Java Question Trying to create ACRA library

warwound

Expert
Licensed User
Longtime User
I'm trying to create a library to use ACRA in B4A.

ACRA is an Application Crash Report for Android tool, when your app crashes ACRA can send a crash report to various destinations.
It can send the report to your Google Docs, to a custom script on your web server or it can email the report to you.

I've successfully used it in a native android java app and thought it'd make a good addition to the B4A libraries so...

I started with a class that extends the Android Application class:

B4X:
package uk.co.martinpearman.b4a.acra4b4a;

import org.acra.ACRA;
import org.acra.annotation.ReportsCrashes;

import android.app.Application;
import android.util.Log;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Permissions;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@Author("Martin Pearman")
@ReportsCrashes(formKey="dHZVX1F4OW9mWjlTSmtSM0VvaFFuN1E6MQ")
@Permissions(values = {"android.permission.INTERNET"})
@ShortName("ACRA4B4A")
@Version(1.00f)
public class ACRAApplication extends Application {
   @Override
   public void onCreate() {
      ACRA.init(this);
      super.onCreate();
      Log.d("B4A", "ACRAApplication onCreate");
   }
}

(The ACRA source code is part of the B4A library project in Eclipse - no need for a jar file and the DependsOn annotation).

Updated my manifest:

B4X:
SetApplicationAttribute(android:name, "uk.co.martinpearman.b4a.acra4b4a.ACRAApplication")
AddPermission(android.permission.INTERNET)

(Even though the INTERNET permission is set in the library i noticed that it was not added to the manifest - probably because the library is not an Activity object?)

And created a simple B4A project to test it:

B4X:
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim ExceptionButton As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub ExceptionButton_Click
   Activity.AddView(ExceptionButton, 0, 0, 100%x, 60dip)
End Sub

The project compiles and runs, the log shows:

Retrieve application default SharedPreferences.

Set OnSharedPreferenceChangeListener.

ACRA is enabled for uk.co.martinpearman.b4a.acrademo, intializing...
Looking for error files in /data/data/uk.co.martinpearman.b4a.acrademo/files

ACRAApplication onCreate

So the Application sub class has successfully been created.
A click on the ExceptionsButton raises an exception:

ACRAApplication onCreate
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
main_exceptionbutton_click (java line: 226)

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:1976)
at android.view.ViewGroup.addView(ViewGroup.java:1871)
at android.view.ViewGroup.addView(ViewGroup.java:1851)
at anywheresoftware.b4a.objects.ActivityWrapper.AddView(ActivityWrapper.java:106)
at uk.co.martinpearman.b4a.acrademo.main._exceptionbutton_click(main.java:226)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:158)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:154)
at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:54)
at android.view.View.performClick(View.java:2506)
at android.view.View$PerformClick.run(View.java:9112)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3835)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

But no crash report is sent to my Google Docs account.
With my java android project the same code would have send a crash report to my Google Docs account and i'd have received an email notification.

I tried different methods to cause the B4A project to crash:

An attempt to divide by zero failed to raise an exception, instead the division produced the result Infinity.

An infinite do while loop eventually force closed the app but again no crash report was sent.

I'm wondering if B4A has it's own exception handling code which sits on top of the native android exception handling and this B4A code is intercepting the exceptions which ACRA would otherwise report?

Martin.
 

warwound

Expert
Licensed User
Longtime User
I'm still trying to get this working and wondered if someone could tell me...

Does B4A call the Thread setDefaultUncaughtExceptionHandler() method at any time in an app's lifecycle?

Looks like ACRA works by creating a sub-class of the Thread.UncaughtExceptionHandler class and calls Thread.setDefaultUncaughtExceptionHandler(instance) with an instance of that sub-class.

I'm wondering if B4A does something similar AFTER my ACRA library has initialized itself - replacing the ACRA UncaughtExceptionHandler with another UncaughtExceptionHandler thus stopping my library from working.

I've looked through the compiled java source of my B4A test project and see no references to Thread.setDefaultUncaughtExceptionHandler() but thought i'd ask anyway.

Thanks.

Martin.
 

warwound

Expert
Licensed User
Longtime User
Or does B4A (internally) effectively wrap everything in a try catch so there are no UncaughtExceptions which ACRA can handle?

Any ideas?

Martin.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
You will not be able to extend Application (and you do not really need to).

You should create a regular object and call ACRA.init from it.
BA class does set its own UncaughtThreadException handler. Though it can be changed.

Basic4android does trap most of the errors. When an error is trapped you see a msgbox asking you whether you want to continue.
 

warwound

Expert
Licensed User
Longtime User
You will not be able to extend Application

Well it looks like i have successfully done this - my sub-class of Application overrides the Application onCreate method and in that onCreate a have a log statement that definitely gets executed.

You should create a regular object and call ACRA.init from it.
BA class does set its own UncaughtThreadException handler. Though it can be changed.

I did some tests earlier.
When my test project starts up no longer initializing the ACRA class.
I added a new method to the Application sub-class to initialize the ACRA class and called that new method on a Button click.
The idea was that if B4A had set it's own UncaughtThreadException then a click on my Button would set the ACRA UncaughtThreadException.
Still the library failed to work and no crash report was sent.

I shall experiment more tomorrow and see what i can get to work.

Martin.
 

warwound

Expert
Licensed User
Longtime User
I forgot to ask one thing...

Whenabouts in the application lifecycle does the B4A UncaughtThreadException handler get set?

Can i find it in the compiled B4A project's .java file or is it hardcoded into Core or B4AShared or elsewhere?

If i know when B4A sets it's own handler i know when i can then set the ACRA handler!

Martin.
 

warwound

Expert
Licensed User
Longtime User
I've found the problem (i think)!

It seems as if setting the DefaultUncaughtExceptionHandler sets that new DefaultUncaughtExceptionHandler for any Thread created afterwards.

I added a couple of methods to my library:

B4X:
   public int DivideByZero() {
      int i = 10, j = 0;
      return i / j;
   }

   public void StartNewThread() {
      Runnable runnable = new Runnable() {
         @Override
         public void run() {
            DivideByZero();
         }
      };
      Thread thread = new Thread(runnable);
      thread.start();
   }

Calling StartNewThread on a Button click caused a force close and ACRA offered to email a crash report.

Calling DivideByZero from a Button click does not cause ACRA to offer to email a crash report.

The library is current a Process object but does not sub-class Application.
I shall revert to sub-classing Application and see if i can get it to work.

Martin.
 

warwound

Expert
Licensed User
Longtime User
Time to give up on this one i think!

I was thinking that ACRA would catch any exception but it seems like an exception in the main UI thread is not caught.

I'd previously got ACRA working in a native Android java app successfully catching an exception.
It's only now that i've thought about it that i realised that the exception in my java app was an exception in a new thread and not an exception in the UI thread.

So there's not much point in continuing development of this library - i wanted to be able to catch exceptions in all threads - user created threads and the main UI thread.

It's odd though that ACRA doesn't catch all exceptions...

When the BA constructor is executed and calls Thread.setDefaultUncaughtExceptionHandler, it first gets a reference to any existing UncaughtExceptionHandler.
It then adds it's own UncaughtExceptionHandler and in it's own UncaughtExceptionHandler it calls the original UncaughtExceptionHandler after it has done it's B4A related stuff.

ACRA does exactly the same - gets a reference to any original UncaughtExceptionHandler and calls that original UncaughtExceptionHandler after it has done it's ACRA stuff.

So whether the BA constructor executes before ACRA is initialized or after it should in theory all work.

Anyway - time for a break.
If anyone is interested in continuing development i can let you have a copy of my library source code.

Martin.
 

INALAMBRIK

Member
Licensed User
Longtime User
Hi Martin,

I've been using BugSense (www.bugsense.com) with success on my android native apps. Reading your post, I was wondering if it's possible to buid as a B4A library.

Regards,
 

warwound

Expert
Licensed User
Longtime User
Well i just had a brief look at the bugsense docs and basic tutorial and can't say whether or not it'd work within b4a.
If bugsense works in a similar way to ACRA then you may find the same problems as i found - possibly a conflict with the b4a UncaughtExceptionHandler.

You'll only know for sure if you wrap the basic functionality of bugsense in a library and give it a try in b4a.

Martin.
 

frapel

Active Member
Licensed User
Longtime User
Top