Search code examples
androidbroadcastreceiverandroid-looper

is it possible to quit looper in onReceive method in BroadcastReceiver


Using the following code and when onReceive is fired,am getting the following error

Error receiving broadcast Intent { act=com.sample.service.ReminderActivityService flg=0x10 (has extras) } 

in com.sample.common.UserActivity$1@41c2b4b0

The problem is this statement Looper.myLooper().quit(); How do I terminate my looper after receiving the broadcast in the code below?

public class UserActivity extends Thread implements
    ConnectionCallbacks, OnConnectionFailedListener {

private String TAG;
// Constants that define the activity detection interval
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 30;
public static final int DETECTION_INTERVAL_MILLISECONDS = MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
IntentService is;
onActivityGot mCallback;
Handler mHandler;
Context mContext;
BroadcastReceiver br;
/*
 * Store the PendingIntent used to send activity recognition events
 * back to the app
 */
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;

public UserActivity(UserActivity.onActivityGot ints) {
    is = (IntentService) ints;
    mContext = is.getApplicationContext();
    mHandler = new Handler();
    TAG = this.getClass().getSimpleName();
    // This makes sure that the container service has implemented
    // the callback interface. If not, it throws an exception
    try {
        mCallback = (UserActivity.onActivityGot) ints;
    } catch (ClassCastException e) {
        throw new ClassCastException(ints.toString()
                + " must implement UserActivity.onActivityGot");
    }
    Log.i(TAG, "UserActivity constractor fired in activity");
}

@Override
public void run() {
    if (servicesConnected()) {
        Looper.prepare();
        Log.i(TAG, "servicesConnected fired in activity");
        /*
         * Instantiate a new activity recognition client. Since the
         * parent Activity implements the connection listener and
         * connection failure listener, the constructor uses "this"
         * to specify the values of those parameters.
         */
        mActivityRecognitionClient =
                new ActivityRecognitionClient(mContext, this, this);
        // connect to the service
        mActivityRecognitionClient.connect();

        br = new BroadcastReceiver() {
            @Override
            public void onReceive(Context c, Intent i) {
                //call calback with data
                mCallback.activityKnown(i);
                mActivityRecognitionClient.removeActivityUpdates(mActivityRecognitionPendingIntent);
                mActivityRecognitionClient.disconnect();
                mContext.unregisterReceiver(br);
                Looper.myLooper().quit();
            }
        };
        mContext.registerReceiver(br, new IntentFilter("com.sample.service.ReminderActivityService"));
        Looper.loop();
    }
}

@Override
public void onConnected(Bundle dataBundle) {
    Log.i(TAG, "onConnected fired");
    /*
     * Create the PendingIntent that Location Services uses
     * to send activity recognition updates back to this app.
     */
    Intent intent = new Intent(
            mContext, ReminderActivityService.class);
    /*
     * Return a PendingIntent that starts the IntentService.
     */
    mActivityRecognitionPendingIntent =
            PendingIntent.getService(mContext, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    /*
     * Request activity recognition updates using the preset
     * detection interval and PendingIntent. This call is
     * synchronous.
     */
    mActivityRecognitionClient.requestActivityUpdates(
            DETECTION_INTERVAL_MILLISECONDS,
            mActivityRecognitionPendingIntent);
}

@Override
public void onDisconnected() {
    // Delete the client
    mActivityRecognitionClient = null;
    Looper.myLooper().quit();
    Log.i(TAG, "onDisconnected fired");
}

@Override
public void onConnectionFailed(ConnectionResult cr) {
    mHandler.post(new UiToastCommunicaton(mContext,
            is.getResources().getString(R.string.action_connfailed)));
    mCallback.activityFail();
    Looper.myLooper().quit();
    Log.i(TAG, "onConnectionFailed fired");
}

private boolean servicesConnected() {
    // Check that Google Play services is available
    int resultCode =
            GooglePlayServicesUtil.
            isGooglePlayServicesAvailable(is.getBaseContext());

    if (ConnectionResult.SUCCESS == resultCode) {// If Google Play services is available
        // In debug mode, log the status
        Log.d("Activity Recognition",
                "Google Play services is available.");
        // Continue
        return true;

    } else {// Google Play services was not available for some reason
        mHandler.post(new UiToastCommunicaton(mContext,
                is.getResources().getString(R.string.gpserv_notfound)));
        return false;
    }
}

public interface onActivityGot {

    public void activityKnown(Intent i);

    public void activityFail();
}
}

Solution

  • found a way by storing a handle to the looper in a static variable. view below.

    declare the variable

    public static Handler looperHandle;
    

    set the variable after preparing looper

     Looper.prepare();
        looperHandle = new Handler();
    

    since i had instantiated the class in an object i just called

     object.looperHandle.getLooper().quit();
    

    am not comfortable with this solution because of using a static variable. if someone has a better solution please post it here.