Question

I've developed a small Android application which uses an AlarmManager to call a Handler Thread that synchronizes some information from the mobile device with a remote server. This process happens every 15 minutes.

When I use the HandlerThread without the aid of the AlarmManager, everything ALWAYS works fine. But when I try to put these two to work together, SOMETIMES it works and SOMETIMES I get the following error :

W/System.err(10557): java.lang.NullPointerException
W/System.err(10557):at Synchronizer.queueSyncEverything(Synchronizer.java:109)
W/System.err(10557): at SyncService.onHandleIntent(SyncService.java:33)
W/System.err(10557): at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
W/System.err(10557): at android.os.Handler.dispatchMessage(Handler.java:99)<BR/>
W/System.err(10557): at android.os.Looper.loop(Looper.java:137)
W/System.err(10557): at android.os.HandlerThread.run(HandlerThread.java:60)

The code is really simple as you can see in the following snippets:

//Method from SyncService, a class which extends IntentService    
protected void onHandleIntent(Intent intent) {
    ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    if(cm.getActiveNetworkInfo() == null){
        return;
    }
    if(mSynchronizer == null){
        mSynchronizer = new Synchronizer(getApplicationContext(), this);
        mSynchronizer.start();
            mSynchronizer.getLooper();
    }       
    mSynchronizer.queueSyncEverything();

}

//Method from Synchronizer, a class which extends HandlerThread
public void queueSyncEverything(){
    try{
        mHandler.obtainMessage(MESSAGE_SYNC_EVERYTHING).sendToTarget();
    }
    catch(Exception e){
        mListener.onError();
        e.printStackTrace();
    }
}

I already checked if the Handler or the Looper are null, but they're all fine.

EDIT: As @DavidWasser suggested, I did some more testing and discovered that sometimes mHandler is null. This variable is set inside the method onLooperPrepared from Synchronizer (which extends from HandlerThread) as you can see here:

@Override
protected void onLooperPrepared(){
    mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg){

            if(msg.what == MESSAGE_SYNC_EVERYTHING){
                handleSyncEverything();
                mListener.onFinished();
            }
        }
    };
}

The constructor of this class, as @DavidWasser asked, is just:

public Synchronizer(Synchronizer.Listener listener){
    super(TAG);
    mListener = listener;       
}

mListener is just an delegate-like object who receives events sent by the Synchronizer class, like when the Synchronization is finished. TAG is just a name to make it easier to debug. Maybe my main difficulty is to understand how the class HandlerThread works and when is it the right time to set its Handler object.

P.S.: This way of setting up the HandlerThread I got by reading the book "Android Programming: The Big Nerd Ranch Guide".

Était-ce utile?

La solution

Don't initialize mHandler in onLooperPrepared() as this method could be called after the call to getLooper() returns. This is a small timing window that could bite you.

Instead create a method in Synchronizer like this:

public void createHandler() {
    mHander = new Handler(getLooper()) {
        @Override
        public void handleMessage(Message msg){
            if(msg.what == MESSAGE_SYNC_EVERYTHING){
                handleSyncEverything();
                mListener.onFinished();
            }
        }
    };
}

and call this method in onHandleIntent() instead of calling mSynchronizer.getLooper().

This will ensure that mHandler is initialized before you call queueSyncEverything().

NOTE: You don't need to override onLooperPrepared() at all.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top