onStop delays to be called when restarting activity - java

When restarting an Activity, onStop() from previous activity delays too much to be called.
I am using this code to restart my activity PlayerActivity.java
Intent playerIntent = getIntent();
playerIntent.putExtra(Constants.VIDEO_ID, videoId);
playerIntent.putExtra(Constants.CATEGORY_ID, categoryId);
playerIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(playerIntent);
Let's call PreviousPlayerActivity and NewPlayerActivity, the activity that was before and the new activity. (remembering that they are the same PlayerActivity).
Sequence
When restarting the app follows this flow in the activity-lifecycle.
PreviousPlayerActivity onPause() -->
NewPlayerActivity onCreate() -->
NewPlayerActivity onStart() -->
NewPlayerActivity onResume() -->
NewPlayerActivity performs a heavy operation -->
PreviousPlayerActivity onStop() -->
PreviousPlayerActivity onDestroy()
What I need
I need PreviousPlayerActivity to be completed destroyed before NewPlayerActivity starts. However, onStop() is just called after the heavy operation, so it delays around 10 seconds to be called.
What I tried
If I use recreate() method it does destroy PreviousPlayerActivity before calling NewPreviousActivity, but by calling recreate() I can not putExtras into the new activity instance.
Questions
How to completely destroy PreviousActivity when restarting an activity?
Is there a way to putExtras while using recreate()?

In Activity Lifecycle from Android Developer guide.
Coordinating activities
When one activity starts another, they both experience lifecycle
transitions. The first activity stops operating and enters the Paused
or Stopped state, while the other activity is created. In case these
activities share data saved to disc or elsewhere, it's important to
understand that the first activity is not completely stopped before
the second one is created. Rather, the process of starting the second
one overlaps with the process of stopping the first one.
The order of lifecycle callbacks is well defined, particularly when
the two activities are in the same process (app) and one is starting
the other. Here's the order of operations that occur when Activity A
starts Activity B:
Activity A's onPause() method executes.
Activity B's onCreate(), onStart(), and onResume() methods execute in sequence. (Activity B now has user focus.)
Then, if Activity A is no longer visible on screen, its onStop() method executes.
This predictable sequence of lifecycle callbacks allows you to manage
the transition of information from one activity to another.
So the behavior that you describe is expected or predictable.
Back to your questions.
1.How to completely destroy PreviousActivity when restarting an activity?
Using recreate API, the limitation is it only works from API 11 or above
2.Is there a way to putExtras while using recreate()?
From recreate documentation
recreate
public void recreate ()
Cause this Activity to be recreated with a new instance. This results
in essentially the same flow as when the Activity is created due to a
configuration change -- the current instance will go through its
lifecycle to onDestroy() and a new instance then created after it.
Because the activity will be recreated so onSaveInstanceState and onRestoreInstanceState will be called as well. As you can guess the idea is save data in onSaveInstanceState and retrieve in onRestoreInstanceState or onCreate.
Step 1: Save data in onSaveInstanceState
// The key for saving and retrieving isActivityRecreated field.
private static final String KEY_IS_ACTIVITY_RECREATED = "KEY_IS_ACTIVITY_RECREATED";
/** true if this activity is recreated. */
private boolean isActivityRecreated = false;
// Call this method when you want to recreate this activity.
private void recreateActivity() {
isActivityRecreated = true;
recreate();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_IS_ACTIVITY_RECREATED, isActivityRecreated);
outState.putInt(Constants.VIDEO_ID, videoId);
outState.putInt(Constants.CATEGORY_ID, categoryId);
}
Step 2: Retrieve the data in onRestoreInstanceState or onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
isActivityRecreated = savedInstanceState.getBoolean(KEY_IS_ACTIVITY_RECREATED);
if (isActivityRecreated) {
// This activity has been recreated.
// Reset the flag
isActivityRecreated = false;
// Write your code when this activity recreated.
int videoId = savedInstanceState.getInt(Constants.VIDEO_ID);
int categoryId = savedInstanceState.getInt(Constants.CATEGORY_ID);
...
}
}
}

You can do it simply by setting a noHistory flag as true in AndroidManifest.xml file. I think you don't need to keep that activity state when minimizing according to your requirement.
<activity
android:name=".PlayerActivity"
android:noHistory="true" />

Related

Android firebase onStart and onStop explanation

I've just started with firebase in android and i can't understand some things in onStart and in onStop.
Why is it necessary to have this code on stop method? Why do we need to remove listener ?
#Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop: ");
if(mAuthStateListener != null)
mAuth.removeAuthStateListener(mAuthStateListener);
}
And one more question what is the advantage of setting up mAuth listener in onStart method instead of onCreate ?
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
mAuth.addAuthStateListener(mAuthStateListener);
}
This is how they shoved in Firebase -> Authentication demo.
There is the need to remove listeners because the mAuth will keep of keeping track of all the listeners you added, in order to notify you when something happens.
When the activity stops you remove the listener from the list because well, the activity has stopped anyway, there is no need to listen for auth events when the activity is stopped, is there?
Why add the listener at onStart then?
Because according to the activity life cycle:
onStart and onStop correspond to each other, while onCreate and onDestroy correspond to each other.
If you add the listener in onCreate and remove at onStop, the listener will not be added back when the activity restarts, since onCreate is not called on restart. onStart is.

Android Studio - Using Service to call final() vs. FLAG_ACTIVITY_NEW_TASK

I have been scouting around for a while, but cannot locate any information for calling final() using started services... or rather, when not using bound services. There is tons of info for bound services, but I already have two pretty large "started services" without binding, so I didn't want to modify the existing services more than absolutely necessary.
My app works by reading bluetooth data every 10 seconds, and depending on the data read, the Service will change to a new activity. However, I cannot call final() from my services, so I fear that I might be endlessly stacking activities while the application/services are running.
To change activities, I had to add Intent.FLAG_ACTIVITY_NEW_TASK. Considering the below image/definition from the developer's page, this flag looks like it might already handle my stacking issue? I do NOT allow for users to use the back button on their phones as everything is handled via confirm/cancel buttons and the services. My app MUST be this way for a few reasons. Thus, keeping the stack order isn't important to my application.
Key Points -
I want to ensure i'm not stacking up activities endlessly when starting new activities
Flagging "new task" when starting activities via my services
Stack order is not important to my app
Below is a very small cut of my code with comments to explain what i'm trying to do. Please make sure to look to the onDestroy() method of this service.
public class AlertService extends Service {
final class Threader implements Runnable{
// Scans bluetooth advertisement packets every 10 seconds
// Thread Runs until interrupted
// Stops service via service ID
stopSelf(this.serviceID);
}
#Override
public void onCreate(){
super.onCreate();
}
// Runs a thread until alert is found.
// Alert calls thread.interrupt()
#Override
public int onStartCommand(Intent intent, int flags, int startID){
enableBluetooth();
// Start Thread
thread = new Thread(new Threader(startID));
thread.start();
return START_STICKY;
}
#Override
public void onDestroy(){
thread.interrupt();
Intent alertActivity = new Intent(this, AlertActivity.class)
alertActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(alertActivity);
}
// Unused Method - We will not be binding
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
EDIT via recommendation to use android:taskAffinity -
Using android:taskAffinity won't help me in this situation. By default, all activities in an application have the same affinity. When I create a new task by setting Intent.FLAG_ACTIVITY_NEW_TASK in the intent flags, the new activity will STILL be started in the same task if the activity has the same taskAffinity of the root activity in the task. Since I am just using the default affinity, everything should have the normal stack flow. I just cannot call finish(), which means that I am stacking up tons of activities.
To answer my own question, each new activity called via Intent.FLAG_ACTIVITY_NEW_TASK, was creating a new instance of each activity and putting it on the stack. This is true. However, each activity is NOT making a new Task, which was one of my fears as well.
When I create a new task by setting Intent.FLAG_ACTIVITY_NEW_TASK in the intent flags, the new activity will STILL be started in the same task (not in a new task) if the new activity has the same taskAffinity of the root activity in the task. Since I am just using the default affinity, every activity I create is being put into the same task. This means that nothing is acting any differently than the normal flow of creating activities and such.
Though, since I have disable the back button for my application, these activities created by flagging a new task are not finished, destroyed, or removed from the stack. To solve this, I will use FLAG_ACTIVITY_CLEAR_TOP, which finds a running instance of an activity in the stack (if there is one) and closes all of the activities above it.
Since my application always starts with the home screen, then ends with the home screen, flagging "clear top" will always close all activities above my home screen. So, upon return to the home screen, the only item on the stack will be the home screen.
I will have to test this, but it seems that I will not call finish() from my home activity to achieve this result - Otherwise, upon returning to the home activity, not all of the stack will be cleared.

How do I process broadcasts that were sent while my activity was stopped?

My activity starts a service which runs a CountDownTimer. The timer sends broadcasts back to the activity as it counts down. The activity processes the broadcasts in the onReceive method of a BroadcastReceiver. All of this works fine.
My problem comes when the following events happen in this order:
App is stopped (via onPause())
Timer finishes
App is resumed (via onResume())
When the app is resumed the service is no longer sending broadcasts, so the activity does not know how much time is left on the timer or if it's finished. This prevents the activity from updating the UI.
I've tried a dozen ways of dealing with this, and read through many Stack Overflow questions and answers, but I've yet to find a solution. I would think that there's a way to pick up a broadcast that was sent while the activity was not active, but I've yet to find a way.
For the record, here is my relevant Activity and Service code:
activity.java
// Start service
timerIntent.putExtra("totalLength", totalLength);
this.startService(timerIntent);
// ...
// BroadcastReceiver
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null && inSession) {
session.setRemaining(intent.getExtras().getLong("millisUntilFinished"));
updateProgress();
}
}
};
// ...
// onResume
#Override
public void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter(TimerService.COUNTDOWN_TS));
}
service.java
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
long length = intent.getExtras().getLong("totalLength");
countDownTimer = new CountDownTimer(length, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timerServiceIntent.putExtra("millisUntilFinished", millisUntilFinished);
sendBroadcast(timerServiceIntent);
}
#Override
public void onFinish() {
}
};
countDownTimer.start();
return super.onStartCommand(intent, flags, startId);
}
What's the best way to process the broadcasts that the service sent while the activity was stopped?
Use the BroadcastReceiver to store the last request (SharedPreferences perhaps) it received and check it when the Activity starts.
Alternatively, instead of processing a countdown using broadcasts, just store the time that the countdown would end. The Activity can then handle the countdown all by itself as it knows when it should end. Using a service and broadcasts seem to be a little over-engineered for such a simple task.
Update:
From the way you have described your task, I see you needing to handle 2 scenarios. This is how I would likely do it.
Assuming that "XYZ" is the service\intent\whatever starting the countdown and "ABC" is the Activity displaying the progress. (ABC and XYZ could be the same activity if that is what you wanted)
Requirements:
When the countdown starts, I would make XYZ store the time that the countdown should end in SharedPreferences.
ABC is already running when the countdown starts. As Commonsware said, the Eventbus model is excellent for handling this scenario so long as XYZ and ABC are running in the same process. Just fire an event to read the preference value and count down to the specified time. If the user closes ABC and reopens it, Scenario 2 will kick in.
ABC is not running. Check in OnResume whether the countdown time has elapsed. If not, set up ABC to display the countdown again. If there is no countdown active, do something else.
If you also need to do something when the countdown has elapsed regardless of whether you have a UI active, then again Commonsware's suggestion of AlarmManager is perfect.
Let's pretend for a moment that using a Service with a CountDownTimer to track some passage of time for the purposes of updating an Activity actually is a good idea. It's not out of the question, assuming that the Service is actually doing something for real and this timing thing is some by-product.
An activity does not receive broadcasts while stopped, mostly for performance/battery reasons. Instead, the activity needs to pull in the current status when it starts, then use events (e.g., your current broadcasts) to be informed of changes in the data while it is started.
This would be simplified by using something like greenrobot's EventBus and their sticky events, as the activity would automatically get the last event when it subscribes to get events. Using greenrobot's EventBus for this purpose would also reduce the security and performance issues that you are introducing by your use of system broadcasts to talk between two Java classes in the same process.
Also, please stick with lifecycle pairs. onResume() is not the counterpart to onStop(). onStart() is the counterpart to onStop(); onResume() is the counterpart to onPause(). Initializing something in one pair (e.g., onResume()) and cleaning it up in the other pair (e.g., onStop()) runs the risk of double-initialization or double-cleanup errors.
What's the best way to process the broadcasts that the service sent
while the activity was stopped?
Using sticky broadcast intents from the service and then retrieving them from the activity would be a way to process the broadcasts that the service sent while the activity was stopped. I can only offer that as a possible solution rather than claiming it is the "best way".
http://developer.android.com/reference/android/content/Context.html#sendStickyBroadcast(android.content.Intent)
They have however, been deprecated since API level 21 due to security concerns.
Instead of using Normal broadcast you can use Ordered broadcast (sent with Context.sendOrderedBroadcast). For this along with defining a BroadcastReceiver in your activity you required to define BroadcastReceiver in your manifest with same intentfilter. Only change is while registering BroadcastReceiver in your activity you need to set priority to high, so that when your activity is running and activity's BroadcastReceiver is registered it gets called first, and inside onReceive of this BroadcastReceiver you can use abortBroadcast for getting the BroadcastReceiver called which is defined in your android manifest. Now when your activity is not running the BroadcastReceiver defined in your android manifest will get called. So this way you can have the status and if you wish you can display updates to user by notification even if your activity is not running.

Kill app whenever it goes to background

I am creating an app. From MainActivity -> Fragment -> See Details Activity.
It is working fine.
But when I am going to background on See Details Activity, it is still running.
Why this activity or app is not getting destroyed. I want to kill the app whenever I will go to background.
Add this:
#Override public void onPause() { super.onPause(); // Always call the superclass method first
Finish();
}
Invoke finish() method before start Fragment

How to cancel notification in onDestroy function

i want to make a notification like BBM connected notification (for android)...
so when i open my app, the notification will appear, and it cant be canceled...
so i use this code
nm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification=new Notification(android.R.drawable.stat_notify_more, "this is important", System.currentTimeMillis());
notification.flags = notification.FLAG_NO_CLEAR; //notification never dissapear
Context context=MainActivity.this;
CharSequence title="hello there";
CharSequence detail="this is demo";
Intent intent=new Intent(context,MainActivity.class);
PendingIntent pending=PendingIntent.getActivity(context, 0, intent, 0);
notification.setLatestEventInfo(context, title, detail, pending);
nm.notify(0, notification);
i put that code in public void onCreate(Bundle savedInstanceState)...
so, the notification cant cancel when i click or slide.
and when i close my app, the notification still there.
then i have an idea to use this code :
#Override
protected void onDestroy() {
nm.cancelAll();
}
but, this function never called???
how can i cancel the notification when i close the app?
It might be because onDestroy will not be called immediately when you navigate away with the home button.
Android has callback methods available that work well with notifications etc.
These are called onUserLeaveHint() and onUserInteraction().
The JavaDoc for onUserLeaveHint() states:
Called as part of the activity lifecycle when an activity is about to
go into the background as the result of user choice. For example, when
the user presses the Home key, onUserLeaveHint() will be called, but
when an incoming phone call causes the in-call Activity to be
automatically brought to the foreground, onUserLeaveHint() will not be
called on the activity being interrupted. In cases when it is invoked,
this method is called right before the activity's onPause() callback.
This callback and onUserInteraction() are intended to help activities
manage status bar notifications intelligently; specifically, for
helping activities determine the proper time to cancel a notfication.
I'd suppose you will want to override either one of these; especially onUserLeaveHint() seems a good choice for your purpose.
#Override
protected void onUserLeaveHint()
{
nm.cancelAll();
super.onUserLeaveHint();
}
You can't intercept the event of 'force closing' the application process. Android doesn't support this. When the user does this, the system calls Process.killProcess(int pid) and all resources will be freed. It's just not possible to catch this event and perform tasks before the process is really killed. Maybe a workaround is possible, but it wouldn't be how Android wants it.
There is no guarantee that the method will called. If application will not have enough memory, it can be killed by OS.
There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
You missed the super.onDestroy
#Override
protected void onDestroy() {
nm.cancelAll();
super.onDestroy();
}

Categories