I would like to finish() a paused activity that is underneath a transparent activity.
I have an activity, called activity A. Two things can happen while activity A is active;
we can launch (a transparent) Activity B
we can receive an asynchronous callback to finish activity A.
These two actions happen very close to each other. The code looks like this
public class ActivityA extends Activity
{
public class DataHandler implements ContentLoader.OnDataListener
{
@Override
public void onData(Cursor data)
{
_binder.bind(data);
}
}
//If this callback is executed while Activity A is paused, it will not go into onStop until it the activity above it is finished
private class LoaderCallbacks extends ContentLoader.LoaderCallbacks
{
public LoaderCallbacks(ContentLoader loader)
{
super(loader);
}
@Override
public void onLoadFinished(
Loader<Cursor> loader,
Cursor cursor)
{
if (cursor == null || cursor.getCount() <= 0)
{
Log.d("Eric", "* ON FINISH *");
finish();
finishagain();
return;
}
super.onLoadFinished(loader, cursor);
}
}
}
Inside of a listfragment shown by this activity there is a mechanism for launching Activity B
public class FragmentA extends ListFragment
{
//Some fragment functions here...
@Override
public void onListItemClick(
ListView list,
View view,
int position,
long id)
{
Intent intent = new Intent();
intent.setAction(Intent.LAUNCH_ACTIVITY_B);
getActivity().sendBroadcast(intent)
}
}
My problem is when the callback to finish activity A is called AFTER activity B is launched, then Activity A is not being finished immediately. It remains in the paused state until Activity B is finished, and then both finish. This is a race condition, and I've confirmed this by trying to finish again, while in the paused state, using a simple waiting thread. All the finish calls are performed on the main thread, as expected.
private void finishagain()
{
Handler handler = new Handler();
int LOCK_HOME_DELAY = 5000;
handler.postDelayed(new Runnable()
{
public void run()
{
if (notfinished){
Log.d("Eric", "*************** FINISH AGAIN ****************");
finish(); //Does nothing while the activity is paused
}
else{
Log.d("Eric", "* Times up do nothing *");
}
}
}, LOCK_HOME_DELAY);
}
Here are my logs (some package names may be redacted)
10-10 18:23:05.168 74-98/system_process I/ActivityManager: Displayed somepackage/com.eric.activity.A: +894ms
10-10 18:23:07.135 74-98/system_process I/ActivityManager: Displayed somepackage/com.eric.activity.B: +343ms
10-10 18:23:07.102 547-547/somepackage D/Eric: * Times up do nothign *
10-10 18:23:07.231 547-547/somepackage D/Eric: * ON FINISH *
10-10 18:23:08.220 547-547/com.eric.Status D/Eric: * Times up do nothign *
10-10 18:23:08.305 547-547/com.eric.Status D/Eric: * Times up do nothign *
10-10 18:23:12.305 547-547/com.eric.Status D/Eric: *************** FINISH AGAIN ****************
10-10 18:23:12.305 74-668/system_process W/ActivityManager: Finishing task with all activities already finished
10-10 18:23:12.305 74-668/system_process W/ActivityManager: Duplicate finish request for ActivityRecord{3627639c u0 somepackage/com.eric.activity.A t2292 f}
(Notice the timestamps - I call finish at :07 seconds, and it doesnt finish. finishAgain() calls finish again at :12 seconds, and it appears to close here, but I've seen it finish later too. Also note the "duplicate finish request" - to me it looks like the finish was queued or something).
How can I get Activity A to finish when it is paused underneath the transparent Activity B?
TO be honest, I'm surprised this is an issue; I thought activities on the backstack should be readily killeable, but perhaps not those in the onPause state? I haven't been able to find documentation on this, perhaps someone knows the relevant doc/code?
EDIT see my answer
Resume Your Activity When the user resumes your activity from the Paused state, the system calls the onResume() method. Be aware that the system calls this method every time your activity comes into the foreground, including when it's created for the first time.
paused: In this state, the activity is partially obscured by another activity—the other activity that's in the foreground is semi-transparent or doesn't cover the entire screen. The paused activity does not receive user input and cannot execute any code.
An Android activity goes through six major lifecycle stages or callbacks. These are: onCreate() , onStart() , onResume() , onPause() , onStop() , and onDestroy() . The system invokes each of these callbacks as an activity enters a new state.
The onDestroy() method runs immediately before the activity is destroyed. The onDestroy() method enables you to perform any final clean up such as freeing up resources. After the onDestroy() method has run, the activity is destroyed.
Hmmm this question is trickier than it looks, the transparent Activity causes problems. You explained it well, but people dont normally answer the question, they spill-out something they are familiar with.
"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations." see android doc.
you say:
//If this callback is executed while Activity A is paused, it will not go into onStop until it the activity above it is finished
You cannot go into stopped state while Activity A is visible.Android destroying activities, killing processes
This may help:
View topLevelLayout = findViewById(R.id.top_layout);
topLevelLayout.setVisibility(View.INVISIBLE);
In my answer the default case is asynchronous (it's a broadcast).
Register each Activity (that you want to later kill, ActivityA
) for local broadcast when it gets created.
When it goes to the background (paused state),
(i.e., a new activity comes to the top of the stack),
its onPause()
{onStop()
} will get called, but it can still receive broadcasts.
You just need to make sure you call unregisterReceiver() in onDestroy()
rather than in onPause()
{onStop()
}.
Here'a some sample code to give you the idea:
/** your "kill" method (ActivityA) **/
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.package.ACTION_KILL");//some string
// sendBroadcast(broadcastIntent);//global broadcast
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
/** The receiver (ActivityA) **/
BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.package.ACTION_KILL");//some string
LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("onReceive","KILL in progress");
//At this point you should do your stuff ;O)
finish();//kill it
//System.exit(0)// to clear static variables
//android.os.Process.killProcess(android.os.Process.myPid());//"cave man" kill }
}, intentFilter);
}
protected void onDestroy(
{
LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadcastReceiver );
super.onDestroy();
}
https://developer.android.com/training/basics/activity-lifecycle/index.html https://developer.android.com/reference/android/app/Activity.html https://developer.android.com/reference/android/content/BroadcastReceiver.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With