I have an android app that uses push. If the app receives a push message from the server it creates a push notification (with headline, message, icon) and presents it to the user.
normal case
The click on the notification opens up an Activity (not the launcher) and the activity opens a fragment that shows content based on the notification. No magic here and everything works as expected.
the special/bad case
If the device is inactive for at least 2h and you pick it up and click a notification that was pushed before, then it opens the Activity like in the normal case but no fragment comes up. Instead the launcher will be called.
While debugging this is time costly and frustrating I found out the following things. Maybe someone has an idea:
The Activity usually starts with an animation that I load from resources. I load and run it in onCreate(). If the Activity gets invoked by the push then the animation will be loaded but never runs. I bypassed this with a Handler that waits 2 seconds and checks if the animation listener was called. If not (bad case) then the handler calls the code to open the fragment.
opening the fragment caused a crash:
IllegalStateException: Cannot perform this after onSaveInstanceState. Similar like here Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState So I replaced commitAllowingStateLoss(). Now there is no crash anymore but the fragment does not appear. Only Fragment.newInstance() will be called but no onCreate() or any other lifecycle method.
Question:
What crazy state is it that seems to break functions in my code after longer inactivity?
Device:
I'm currently testing with a Nexus 5 with Android 6.0.1
I found out that if my device is in this crazy sleep state that my activity gets startet twice if I then click on a push notification. At first the push triggers the activity with its data in the intent. Then, after it, the launcher activity gets invoked (with an empty intent) and starts the main screen.
I fixed it with like here -> Activity opened twice
To sum it up:
1) I changed my code so that all my deeplinks, push notifications, etc. will invoke the lauchner activity. The activity then decides what to do. That means we have a single entry point into the app. That makes everything easy and the code clearer.
(with this fix the same activity will be invoked twice. we have to do a little more to fix it)
2) I marked the activity in the manifest with android:launchMode="singleTask". This means that if there is an instance of the activity already, it will be reused and not created a second time.
3) The flag from 2) triggers a callback method. Instead of invoking the activity a second time, the system reuses the already running activity and calls the method onNewIntent(intent). That means if the activity is freshly started, then onCreate() will be called. If the activity is reused (because it was triggered by a push notification, deeplink, etc.) then onNewIntent(intent) will be called. In both, onCreate() and onNewIntent() I call a method to decide what screen/fragment/activity is next and handover the intent from getIntent(). Done.
Related
I m facing this issue of creating multiple instance of same activity when app comes in foreground from background. Every time app goes in background then comes in foreground after sometime, new activity created without destroying old so the count increases by 1 every time.
while debugging with android studio, it is found that instance of application shows dead after remaining for sometime in background, after this dead state when app comes in background this issue happens.
Launch Mode is SingleTop with ClearTop
Logs in screenshots are stated as :
in activity oncreate ----> in oncreate function of activity
in oncreate ----> in oncreate function of fragment attached to activity
in onViewCreated ----> in onViewCreated function of fragment
For screenshots click on links below.
app open first tym and moved to background
Dead state is showing instead of app running in background, when it brought to foreground, new Activity created. and look at screenshot below
after bringing in foreground
Here's the scenario I'm struggling with:
The user starts my app and presses the Download button. The app starts a download on its own (with an AsyncTask, not using the System DownloadManager) and shows progress in the notifications. Now the user presses the back button, which finishes the activity. Except the AsyncTask and notification keep going, even with their "parent" activity finished.
Now the user goes back into the app by clicking on the notification. Because the last activity was finished and onDestroy() was called, a new activity gets created. The problem is this new activity has no idea that an AsyncTask from a previous instance of itself is still going, and so it shows the Download button again. Clicking this creates a duplicate AsyncTask and notification. Not good.
How can the new activity get a handle on this "lost" AsyncTask so that it can get information from it?
Ideas I'm not looking for:
Blocking/overwriting onBackPressed
Constantly sending info through SharedPreferences
I have 2 activities which are using the same Android task - i.e. they will use the same back stack.
Let's call A the first activity in the back stack, and B the second activity in the back stack.
case 1: A is notified that it goes in the background when A was in the foreground and then B comes in the foreground (for any reason, like because it received an external intent).
case 2: A is notified that it goes in the background when A was in the foreground but another application was started in the foreground.
Question: How does my app differentiate between those 2 cases? Is there a way to know if the transition A in foreground -> A in background keeps the current task displayed on the screen?
Basically, I need to get an event when the Android task becomes visible or invisible.
If I understand you correctly, your Activity A would do something different if Activity B doesn't get activated after A was paused.
First of all, it's not a good idea to let an activity do something when it's not visible anymore. As we see in the Android lifecycle, it could get stopped:
I would put your background code into a service. This service gets notified whenever Activity A or B gets paused or resumed. There you could add a little timeout which has to pass till you start your logic.
If you do that, that would happen:
Case 1:
Activity A goes into background and sends a message in the onPause handler to your service. Service starts its timeout timer. Activity B gets started and sends a message in it's onResume handler to the service.
Now the services knows it's still the same activity and acts accordingly.
Case 2:
Activity A goes into background and sends the message. The service starts its timeout timer. It timeouts (I think 200ms would be enough) and now your service knows the task isn't active anymore and acts accordingly.
Use log or Toast in activity life cycle to display message
Keep a static flag in Activity B. set it on resume and unset it on pause.
Override onPostResume
#Override
protected void onPostResume() {
//Your code here
super.onPostResume();
}
I have an activity that sends a BC and waits 5 seconds for the response using AlarmManager.
Once i get the broadcast i:
1. remove the FragmentDialog using mDialog.dismiss();
2. start a new activity using getActivity().startActivity(myIntent);
I'm inside a Fragment (using TabFragment from the support library and MyTabActivity which i created).
The thing is i constantly getting balck screen and ANR, if i remove the line startActivity()
I'm not getting the ANR, the fragment does gets dismissed, but my activity doesn't show, i get the black screen and if i press it, ANR.
What i thought about is that the fragment manager started out working on the activity and then i started a new one that fucks up android, since i'm basing myself on fragment i really don't know how to get out of this mess, thought of using handler in the activity to post my activity start in 300 millis, this is a very very ugly solution.
Any nice ideas ?
the problem seems to be that i unbding a service twice in onStop of the activity due to the fact that once i call unbind ServiceDisconnected is NOT called and changes the state of my activity to unbonded (state i follow).
since onStop is never eding the original activity gets the UIthrehad and make the whole app stuck...
I have the following flow in my code:
Activity1 : startActivity(Activity2) --->
Activity2: startActivity(Activity3) --->
Activity3: startService(MyService) --->
MyService: startActivity(Activity4)
Each Activity above shows a single view and represents a step in a 4-step setup. The final Activity - Activity 4 - is started after some setup work is done inside MyService, which basically tells the user,
"The service has started, you can close the application by pressing Back or Home button"
When the user presses Back or Home, I want to destroy Activities 1-4 , and only have MyService running. Also, after stopping the Application as above, when the user navigates back to the Application via the menu and starts it, I'll be checking if MyService is already running. If it is already running, I don't want to show Activities 1-3, I want to show another Control Panel View (Another Activity), which says,
"Dude, the service is already running, do you want to Stop or Restart it?"
This view will have a Stop and Restart button, to do the appropriate tasks.
My Questions:
How do I stop Activities 1-4 from inside Activity 4 when Back or Home is pressed,safely? My thought was to add a static stopActivity() method to each Activity, and calling Activity[1-3].stopActivity() from onBackPressed() or onPause() of Activity4. Then inside each stopActivity(), I'll call finish(), thus ending each Activity. But is it safe and efficient to do it this way?
The flow I have illustrated above, is it the optimal way of doing things, or is there a cleaner way? I have BroadcastReceivers registered in these Activities, so I need to perform clean exits for each Activity, without leaked receivers, or worse, crashing the App or affect the User's phone due to unclean exit strategies.
Thanks for your suggestions.
You don't need to stop activities, Android will do it for you. Start your activities using intents with the flag FLAG_ACTIVITY_NO_HISTORY so they won't appear when the user presses back. Those activities will be stopped as soon as the user leaves them.
In the onStop method of each of your activities, write any code you want to deallocate memory if there is something you want to deallocate manually, although that wouldn't be necessary because Android will deallocate it for yourself when the device is short on memory. In those onStop methods unregister any BroadcastReceiverpreviously registered.