简体   繁体   中英

When using the up button onRestoreInstanceState isn't called

I'm fairly new to both Android development and StackOverflow and I hope I'm not asking a stupid question that has been asked before but I couldn't find anything on it.

I am making a app that starts a (initially unbound) service when connecting to a Bluetooth device and that should only stop when I tell it to disconnect or when it looses the connection. After the service is started the main activity bounds to it and will unbound when onDestroy() is called. Also it rebinds when onStart is called and mIsBound is true. The mIsBound Boolean is stored with onRestoreInstanceState().

*mIsConnected might be a better name that mIsBound but you get the idea

When I'm reopening the app normally, from the multitasking menu or through the service icon mIsBound is still set to the correct value. Screen orientation isn't a problem and when opening the secondary activity and returning to the main activity via the back button all still goes well. But when I use the up button in the secondary activity the mIsBound value is lost and onRestoreInstanceState() isn't called.

I need this to determine if the service is already running because if it isn't and I call bindService() it will start without needing it and I will get errors when it stops itself but is still bound.

service:

public class BluetoothService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return mBinder;

}

public class LocalBinder extends Binder {
    BluetoothService getService() {
        return BluetoothService.this;
    }
}


private void connectionLost() {
Log.e(TAG, "connection lost");
disconnect();
}


public synchronized void disconnect() {
    Log.d(TAG, "disconnect");
    stopSelf();
}


private void showNotification(String s) {

    // Set the icon, scrolling text and timestamp
    Notification notification = new Notification(R.drawable.ic_launcher, s,
            System.currentTimeMillis());

    // The PendingIntent to launch our activity if the user selects this notification
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
            new Intent(this, LedAndIrControlActivity.class), 0);

    // Set the info for the views that show in the notification panel.
    notification.setLatestEventInfo(this, s, s, contentIntent);

    // Send the notification.
    startForeground(NOTIFICATION, notification);
}

mainActivity:

public class MainActivity extends Activity {   

private static boolean mIsBound = false;
private BluetoothService Com;

@Override
public void onStart() {
    super.onStart();
    if(mIsBound){
        doBindService();
 }

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.i(TAG, "onSaveInstanceState");
    outState.putBoolean("mIsBound", mIsBound);
}

public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);          
    Log.i(TAG, "onRestoreInstanceState");
    mIsBound = savedInstanceState.getBoolean("mIsBound");
    if(mIsBound){
        doBindService();
    }
}

protected void onDestroy() {
    super.onDestroy();
    Log.i(TAG, "onDestroy");
    doUnbindService();
}

public boolean onOptionsItemSelected(MenuItem item) {
    Intent serverIntent = null;
    switch (item.getItemId()) {
    case android.R.id.home:
        return true;
    case R.id.item1:
         // Launch the DeviceListActivity to see devices and do scan
         serverIntent = new Intent(this, DeviceListActivity.class);
         startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
         startService(new Intent(this, BluetoothService.class));
         doBindService();
         return true;
     case R.id.item2:
         // Disconnect device
        Com.disconnect();
        doUnbindService();
         return true;
     case R.id.item3:
          serverIntent = new Intent(this, SecondaryActivity.class);
          //serverIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          startActivity(serverIntent);
        return true;    
    default:
        return super.onOptionsItemSelected(item);
     }      
 } 

 void doBindService() {
     Log.d(TAG, "doBindService");
     // Establish a connection with the service.  We use an explicit
     // class name because we want a specific service implementation that
     // we know will be running in our own process (and thus won't be
     // supporting component replacement by other applications).
     bindService(new Intent(getBaseContext(), //Binding.this
             BluetoothService.class), mConnection, Context.BIND_AUTO_CREATE);
     mIsBound = true;
 }

 void doUnbindService() {
     if (mIsBound) {
         Log.d(TAG, "doUnbindService");
         // Detach our existing connection.
         unbindService(mConnection);
         mIsBound = false;
     }
     Com = null;
 }


 private ServiceConnection mConnection = new ServiceConnection() {

     public void onServiceConnected(ComponentName className, IBinder service) {
         // This is called when the connection with the service has been
         // established, giving us the service object we can use to
         // interact with the service.  Because we have bound to a explicit
         // service that we know is running in our own process, we can
         // cast its IBinder to a concrete class and directly access it.
         Com = ((BluetoothService.LocalBinder)service).getService();
         invalidateOptionsMenu();
         Log.i(TAG, "Service connected");

         // Tell the user about this for our demo.
         Toast.makeText(getBaseContext(), "local_service_connected",//Binding.this
                 Toast.LENGTH_SHORT).show();
     }

     public void onServiceDisconnected(ComponentName className) {
         // This is called when the connection with the service has been
         // unexpectedly disconnected -- that is, its process crashed.
         // Because it is running in our same process, we should never
         // see this happen.
         Com = null;
         Toast.makeText(getBaseContext(), "local service_disconected",//Binding.this
                 Toast.LENGTH_SHORT).show();
     }
 };

secondaryActivity:

public class SecondaryActivity extends Activity {      


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Intent serverIntent = null;
    switch (item.getItemId()) {
    case android.R.id.home:
        // app icon in action bar clicked; go home
        serverIntent = new Intent(this, MainActivity.class);
        serverIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(serverIntent);
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }   
}

My question is how can I make sure that the mIsBound value is always restored? I found that using SharedPreferences is an option but that doesn't seem like the correct way to do this.

Any other suggestions on how to bind to a service, if it is already running, correctly is also appreciated!

Thanks!

UPDATE:

I thought of an other way to solve the problem and that is to simply check if the service is already running and then (re)bind if true. The way of doing this is explained here: How to check if a service is running on Android?

I'm still not quit convinced that this is the correct or best way to do this but it will do for now. If you got a comment on this it is appreciated.

I had the same issue with the subject of your post ( When using the up button onRestoreInstanceState isn't called ), though for different reasons than those you describe in the body. Since I haven't found any other post answering it, I will post my solution to this particular question here.

Q: When using the up button onRestoreInstanceState isn't called

A: Pressing the home (Navigate Up) button on the action bar indeed does not behave the same as pressing the back button. The former does not call the onRestoreInstanceState() of the parent activity and as pressing the back button would do.

If you want to accomplish the same behaviour as the back button, you can do it by overriding the onNavigateUp() method in the child activity and calling the onBackPressed() from it.

In the child activity:

@Override
public boolean onNavigateUp() {
    onBackPressed();
    return true;
}

I am not sure about the return value, true seems to be working fine and satisfies the documentation: "true if Up navigation completed successfully and this Activity was finished, false otherwise" .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM