Search code examples
androidservicenullpointerexceptionjava-native-interfacetoast

Android - Null pointer on Toast call


I have a service which calls a C function through JNI. This function then spawns a pthread and attaches it to the JVM. I call a Java method from this pthread which should post a Toast notification. Unfortunately, as soon as the Toast notification is called, I get a null pointer exception.

Here is the method that handles the Toast call in my service class:

public void showToast(final String msg) {
    final Context MyContext = this;
    Handler h = new Handler(MyContext.getMainLooper());
    h.post(new Runnable() {
        @Override
        public void run() {
            Toast myToast = Toast.makeText(MyContext, msg, Toast.LENGTH_SHORT);
            myToast.show();
        }
    });
}

What could be causing the null pointer exception, and how can I fix it?

Is it trying unsuccessfully to take the context from the C function?

Here is the error message

W/dalvikvm( 4010): JNI WARNING: JNI method called with exception pending
W/dalvikvm( 4010):              in Ldalvik/system/NativeStart;.run:()V (CallVoidMethod)
W/dalvikvm( 4010): Pending exception is:
I/dalvikvm( 4010): java.lang.NullPointerException:
I/dalvikvm( 4010):  at android.content.ContextWrapper.getMainLooper(ContextWrapper.java:104)

Using getApplicationContext:

I/dalvikvm( 6242): java.lang.NullPointerException:
I/dalvikvm( 6242):  at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:109)

Solution

  • The problem is that, when called from a C thread through JNI, we cannot get the context. We must get the context from the Service thread itself. In order to do so:

    public class MyService extends Service{
        private static Context MyContext; /* get context to use when displaying toast from JNI*/
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        public void onDestroy() {
        }
    
        @Override
        public int onStartCommand(Intent intent,int flags, int startid){
            MyContext = getApplicationContext();
            // do what you need to
            return START_STICKY;
        }
    
        public void showToast(final String msg) {
            Handler h = new Handler(MyContext.getMainLooper());
            h.post(new Runnable() {
                @Override
                public void run() {
                Toast myToast = Toast.makeText(MyContext, msg, Toast.LENGTH_SHORT);
                myToast.show();
                }
            });
        }
    }
    

    This way, we have a static reference to the proper context that we can access with our call from JNI