Search code examples
javaandroidloggingacra

Where should I place ACRA.init(this);?


I started to use ACRA (https://github.com/ACRA/acra) for crash reporting. While testing, everything was perfect. Nonetheless, when I released the app, I saw in the Google Play Console an error that was new to the version that I released, caused by ACRA.init(this);:

java.lang.RuntimeException: 
at android.app.ActivityThread.handleBindApplication 
(ActivityThread.java:6209)
at android.app.ActivityThread.access$1200 (ActivityThread.java:236)
at android.app.ActivityThread$H.handleMessage 
(ActivityThread.java:1784)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:214)
at android.app.ActivityThread.main (ActivityThread.java:7032)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run 
(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by: java.lang.IllegalStateException: 
at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1666)
at android.app.ContextImpl.startService (ContextImpl.java:1611)
at android.content.ContextWrapper.startService 
(ContextWrapper.java:677)
at org.acra.sender.SenderServiceStarter.startService 
(SenderServiceStarter.java:43)
at org.acra.util.ApplicationStartupProcessor.sendApprovedReports 
(ApplicationStartupProcessor.java:75)
at org.acra.ACRA.init (ACRA.java:230)
at org.acra.ACRA.init (ACRA.java:156)
at org.acra.ACRA.init (ACRA.java:139)
at com.myapplication.MyApplication.onCreate 
(MyApplication.java:132)
at android.app.Instrumentation.callApplicationOnCreate 
(Instrumentation.java:1154)
at android.app.ActivityThread.handleBindApplication 
(ActivityThread.java:6204)

The content of MyApplication.java:132 is:

ACRA.init(this);

This means that initializing ACRA is causing the crash, ironically. To provide some context, this is where I have ACRA.init(this):

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    super.onCreate();
    ACRA.init(this);

I was reading the discussion at https://groups.google.com/forum/#!topic/acra-discuss/XUKJ5dFHBl0 and the I read Malcolm Cooke's proposed solution:

For the benefit of anyone else I discovered what my problem was.

The class MyDBOpenHelper was being triggered from the onCreate method of a ContentProvider, which gets called before the application class's onCreate method. Resolved it for now by moving the acra init method call within the application class as follows

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);

    ACRA.init(this);
    // some of your own operations before content provider will launch
}

Where should I put ACRA.init(this);? I have it in public void onCreate() but it is throwing this java.lang.IllegalStateException to me. So I guess I should try putting it in protected void attachBaseContext(Context base), as Malcolm Cooke suggested. Can anyone confirm what the right place for ACRA.init(this); is? Thank you.

UPDATE 1:

Chapter 3 of Practical Android: 14 Complete Projects on Advanced Techniques and Approaches (https://www.amazon.com/dp/B078SK4W1M/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1) provides an example of a project using ACRA and in their MyApplication.java file, they use this:

package com.wickham.android.crashlog;

import org.acra.annotation.ReportsCrashes;
import org.acra.*;
import android.app.Application;

@ReportsCrashes(
                customReportContent = { ReportField.REPORT_ID,
                                        ReportField.APP_VERSION_CODE,
                                        ReportField.APP_VERSION_NAME,
                                        ReportField.PACKAGE_NAME, 
                                        ReportField.PHONE_MODEL, 
                                        ReportField.ANDROID_VERSION, 
                                        ReportField.STACK_TRACE,
                                        ReportField.TOTAL_MEM_SIZE,
                                        ReportField.AVAILABLE_MEM_SIZE,
                                        ReportField.DISPLAY,
                                        ReportField.USER_APP_START_DATE,
                                        ReportField.USER_CRASH_DATE,
                                        ReportField.LOGCAT,
                                        ReportField.DEVICE_ID,
                                        ReportField.SHARED_PREFERENCES,
                                        ReportField.CUSTOM_DATA },
                //formKey = "",
                formUri = "https://example.com/crashed.php",
                httpMethod = org.acra.sender.HttpSender.Method.POST,
                mode = ReportingInteractionMode.TOAST,
                resToastText = R.string.msg_crash_text)

public class MyApplication extends Application
{
    @Override
    public void onCreate() 
    {
        super.onCreate();
        ACRA.init(this);
    }
}

They are placing ACRA.init(this); in public void onCreate(). It works for me while I test. Nonetheless, when I released the app, I already saw a crash in the Google Play Console that was caused by ACRA.init(this); as I explained in my question. So I guess I could try placing ACRA.init(this); in protected void attachBaseContext(Context base), as Malcolm Cooke suggested. Can anyone clarify this to me?

UPDATE 2:

Reading https://github.com/ACRA/acra/wiki/BasicSetup, I see they have this:

import org.acra.*;
import org.acra.annotation.*;

@AcraCore(buildConfigClass = BuildConfig.class)
public class MyApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        // The following line triggers the initialization of ACRA
        ACRA.init(this);
    }
}

UPDATE 3:

Even when I put it in attachBaseContext, ACRA is making my app to crash:

java.lang.RuntimeException: 
  at android.app.LoadedApk.makeApplication (LoadedApk.java:1164)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6529)
  at android.app.ActivityThread.access$1900 (ActivityThread.java:267)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1963)
  at android.os.Handler.dispatchMessage (Handler.java:109)
  at android.os.Looper.loop (Looper.java:207)
  at android.app.ActivityThread.main (ActivityThread.java:7470)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:524)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:958)
Caused by: java.lang.IllegalStateException: 
  at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1842)
  at android.app.ContextImpl.startService (ContextImpl.java:1797)
  at android.content.ContextWrapper.startService (ContextWrapper.java:664)
  at org.acra.sender.SenderServiceStarter.startService (SenderServiceStarter.java:43)
  at org.acra.util.ApplicationStartupProcessor.sendApprovedReports (ApplicationStartupProcessor.java:75)
  at org.acra.ACRA.init (ACRA.java:230)
  at org.acra.ACRA.init (ACRA.java:156)
  at org.acra.ACRA.init (ACRA.java:139)
  at com.myapp.MyApplication.attachBaseContext (MyApplication.java:126)
  at android.app.Application.attach (Application.java:224)
  at android.app.Instrumentation.newApplication (Instrumentation.java:1128)
  at android.app.LoadedApk.makeApplication (LoadedApk.java:1156)

Solution

  • Based on the information at https://github.com/ACRA/acra/issues/630, my solution was using this:

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        ACRA.init(this, new ConfigurationBuilder(this).build(), false);
        MultiDex.install(this);
    }
    

    In the logs, I can see LOGCAT showing this line:

    08-18 16:31:50.489 I/ACRA    (11890): ACRA is enabled for com.myapp, initializing...
    

    The initialization is successful now.