Android "single top" launch mode and onNewIntent method - java

I read in the Android documentation that by setting my Activity's launchMode property to singleTop OR by adding the FLAG_ACTIVITY_SINGLE_TOP flag to my Intent, that calling startActivity(intent) would reuse a single Activity instance and give me the Intent in the onNewIntent callback. I did both of these things, and onNewIntent never fires and onCreate fires every time. The docs also say that this.getIntent() returns the intent that was first passed to the Activity when it was first created. In onCreate I'm calling getIntent and I'm getting a new one every time (I'm creating the intent object in another activity and adding an extra to it...this extra should be the same every time if it was returning me the same intent object). All this leads me to believe that my activity is not acting like a "single top", and I don't understand why.
To add some background in case I'm simply missing a required step, here's my Activity declaration in the manifest and the code I'm using to launch the activity. The Activity itself doesn't do anything worth mentioning in regards to this:
in AndroidManifest.xml:
<activity
android:name=".ArtistActivity"
android:label="Artist"
android:launchMode="singleTop">
</activity>
in my calling Activity:
Intent i = new Intent();
i.putExtra(EXTRA_KEY_ARTIST, id);
i.setClass(this, ArtistActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);

Did you check if onDestroy() was called as well? That's probably why onCreate() gets invoked every time instead of onNewIntent(), which would only be called if the activity is already existing.
For example if you leave your activity via the BACK-button it gets destroyed by default. But if you go up higher on the activity stack into other activities and from there call your ArtistActivity.class again it will skip onCreate() and go directly to onNewIntent(), because the activity has already been created and since you defined it as singleTop Android won't create a new instance of it, but take the one that is already lying around.
What I do to see what's going on I implement dummy functions for all the different states of each activity so I always now what's going on:
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
}
Same for onRestart(), onStart(), onResume(), onPause(), onDestroy()
If the above (BACK-button) wasn't your problem, implementing these dummies will at least help you debugging it a bit better.

The accepted answer is not quite correct. If onDestroy() was called previously, then yes, onCreate() would always be called. However, this statement is wrong:
"If you go up higher on the activity stack into other activities and from there call your ArtistActivity.class again it will skip onCreate() and go directly to onNewIntent()"
The "singleTop" section of http://developer.android.com/guide/components/tasks-and-back-stack.html explains plainly how it works (attention to bold text below; I've also proven this through my own debugging):
"For example, suppose a task's back stack consists of root activity A with activities B, C, and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D. If D has the default "standard"launch mode, a new instance of the class is launched and the stack becomes A-B-C-D-D. However, if D's launch mode is "singleTop", the existing instance of D receives the intent through onNewIntent(), because it's at the top of the stackā€”the stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new instance of B is added to the stack, even if its launch mode is "singleTop"."
In other words, starting an activity through SINGLE_TOP will only reuse the existing activity if it is already at the top of the stack. It won't work if another activity in that same task is at the top (for example, the activity that is executing startActivity(SINGLE_TOP)); a new instance will be created instead.
Here are two ways to fix this so that you get the SINGLE_TOP behavior that you want -- the general purpose of which is to reuse an existing activity, instead of creating a new one...
First way (as described in the comment section of the accepted
answer): You could add a launchMode of "singleTask" to your Activity. This would force onNewIntent() because singleTask means there can only be ONE instance of a particular activity in a given task. This is a somewhat hacky solution though because if your app needs multiple instances of that activity in a particular situation (like I do for my project), you're screwed.
Second way (better):
Instead of FLAG_ACTIVITY_SINGLE_TOP, use FLAG_ACTIVITY_REORDER_TO_FRONT. This will reuse the existing activity instance by moving it to the top of the stack (onNewIntent() will be called as expected).
The main purpose of FLAG_ACTIVITY_SINGLE_TOP is to prevent the creation of multiple instances of an Activity. For instance, when that activity can be launched via an intent that comes from outside of your application's main task. For internal switching between activities in my app, I've found that FLAG_ACTIVITY_REORDER_TO_FRONT is generally what I want instead.

Set this flag to your intent:
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)

Related

How to find out from an activity that another activity got terminated

I have 2 activities
Activity A has a registered receiver that updates a static generic list.
Activity B accesses the static generic list in Activity A.
The problem is:
If Activity A ended (killed by the system), the registered receiver stops, which makes the data in the static generic list unreliable, as it depends on the registered receiver to be updated.
And the biggest problem that the static generic list doesn't get terminated (null / zeroed) along with the registered receiver, though both of them created by the same activity.
So there's no way for me to know that my static generic list has become unreliable to reload it all over with fresh data when Activity B gets created.
Also there's no way - listener - to listen to the registered receiver termination, where I'd have terminated the generic static list, and check it in Activity B creation, if it's null, I'll reload a fresh data.
And as you know it's not guaranteed that onDestroy will get called when the system terminates the activity, otherwise I'd have terminated the generic static list.
I even added a static Boolean var (mAlive) to Activity A that has a default value (false) and becomes (true) in onCreate, in hope that when Activity A gets destroyed, (mAlive) will become false (the default value in its declaration), which I could check in Activity B to know that the registered receiver is terminated and the data is unreliable and reload it again, but it turns out, that even destroying Activity A, doesn't reset static Boolean var (mAlive) to its default value (false).
So, any suggestions please, I got a brain freeze trying to find a solution in the past 2 weeks.
Thank you
You seem to be greatly misunderstanding the Activity Lifecycle.
Only one Activity at a time can be running. The current one is suspended when a new one starts and you cannot access directly anything contained in one Activity from a different Activity. Static variables can hold simple data but that is not recommended as a way to avoid dealing with the Activity Lifecycle.
To pass data between activities, you use Intents and Extras.
How do I pass data between Activities in Android application?
If you want two Activities to share the same data, you could use a Service that contains the data which can be accessed from both Activities.
You can fire a Broadcast(BroadcastReceiver Example) from your desired activity's onDestroy() method. Afterwards Receive broadcast in your required activity.
You can put your data into a separate object so you don't dipend on your activity A's lifecycle. You can see an example here at Finder section
I think you could make a parent activity extended by both your Activity A and B. In your parent activity you could register your receiver into its onResume method and then unregister it into its onPause method. By this way you should have always your receiver registered to the current activity.
Then if you have read the previous link, if you make a Finder responsible to keep your fresh data and you reference it into your parent Activity (so you have a reference into both Activity A and Acitivity B) you should be sufficiently sure that a variable referenced by the current active activity can't be destroyed by the system.
I'm not sure to have understood what you need, let me know

How to go to a certain previous instance of an activity that calls explicit intents to itself

I have an activity [A] that has an explicit intent to itself
So the navigation goes this way :-
[A] > [A:instance1]> [A:instance2] > [A:instance3] > want to
go back to [A:instance1]
Of course with the regular back button I go from [A:instance3] back to [A:instance2] .I tried overriding that method but didn't find any solution.
The clearest way is calling finish() at some point. This some point can be
the activity itself when it fires the next one, if appropriate
the activity itself when resumed
The second alternative needs a flag in the session memory. You could extend the Application class and add methods like MyApp.setFinishOnResume(data). As a implementation aid, note that you need a flag for each activity that will call finish(), and the flag must be cleared after that call.

invoke a method from activity in another

i know that this question was asked before but i had tried all the solution and get Error
i have two activity on android studio ...
the first called 'MainActivity' and contain a method ' deleteFromArrayList() '
the secound on called 'DeletButtonActivity' and contain a method ' delete(View v) '
i want to invoke 'deleteFromArrayList ()' wihtout creating another class or make the method static .... becouse i have an ArrayList inside deleteFromArrayList()
note : i send value of index i want to delete from array list using Intent ..the code in DeleteButtonActivity is
public void delete(View v) {
try {
Intent i = new Intent(DeleteButton.this, MainActivity.class);
i.putExtra("index", (int) spinner2.getSelectedItemId());
(new MainActivity()).DeletButtonActivity();
Toast.makeText(getApplicationContext(), "it was deleted", Toast.LENGTH_SHORT).show();
}
catch(Exception e){
Toast.makeText(getApplicationContext(), e+"", Toast.LENGTH_SHORT).show();
}
}
and the code in MainActivity
public void deleteFromArrayList (){
this.arrayList.remove(getIntent().getIntExtra("index",-1));
}
when i run the app i got an Error NullPointerException,,,
can anyone help me ..please
hope that i describe the problem very well
Activities in Android are no just a simple class but they also have a Lifecycle:
An activity has essentially four states:
If an activity is in the foreground of the screen (at the top of the
stack), it is active or running. 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.
If an activity is completely
obscured by another activity, it is stopped. It still retains all
state and member information, however, it is no longer visible to the
user so its window is hidden and it will often be killed by the system
when memory is needed elsewhere.
If an activity is paused or stopped,
the system can drop the activity from memory by either asking it to
finish, or simply killing its process. When it is displayed again to
the user, it must be completely restarted and restored to its previous
state.
So the problem with your code is that when you want to access the array in a stopped activity, the instance you have been using before might not be alive anymore.
How to solve your problem
A very simple approach is to use parameter passing before you do the transition from one activity to the other, for this you'd pass your Array as an intent extra and then you "get the result back" when you finish the second activity by using onActivityResult() callback.
A second approach could be to use a Service that is something similar to an Activity but it has no UI and it has its own lifecycle. Being able to be alive even when you app it is not. Using a Service, you'll keep the Array inside the service and you'll communicate with the array to do the usual operations.
A third approach could be to use an EventBus. A very simple communication mechanism between Activities, Fragments, Threads, Services. There's a great talk titled Android Application Architecture on Android Dev Summit 2015 that uses EventBus as a communication mechanism and to implement a MVC architecture pattern on a REST Android App.
Back to your question. If you just need to 'share' an array between two activities, use the first approach. The second and third are just examples of different alternatives for the case you need a lot more than that.
You must not do this. There are mechanism to communicate between activities or fragments.
On can be, using startActivityForResult, this is Activity A calls Activity B, then in B you do something, and communicate the result back to Activity A.
You can have another workaround to what you want. If you can access the data in both of your activities, you can modified in ether one of them, when the activity starts, it will show the updated data.
Please first read well about an Activity here, and also provides more context of your question.

Is the method onSaveInstanceState(Bundle) called after onPause()?

I'm new to android and I read a book for beginners which said that onSaveInstanceState(Bundle) is ensured to be called before the system reclaims your Activity.
I tried it on some test codes and found it incorrect. I found that onSaveInstanceState(Bundle) was called every time after onPause() was called. And it has nothing to do with system reclaimation.
I'm not very sure about it, so that's the question: when is onSaveInstanceState(Bundle) called actually?
I don't agree with a previous answer.
According to the Documentation:
If called, this method will occur before onStop(). There are no
guarantees about whether it will occur before or after onPause().
According to Android Documentation:
In addition, the method onSaveInstanceState(Bundle) is called before placing the activity in such a background state, allowing you to save away any dynamic instance state in your activity into the given Bundle, to be later received in onCreate(Bundle) if the activity needs to be re-created. See the Process Lifecycle section for more information on how the lifecycle of a process is tied to the activities it is hosting. Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
Yes onPause() is called before onSaveInstanceState(Bundle). But onPause() is guaranteed to be called as its a part of activity life cycle
Usually when your activity is re-created for example when you change device orientation then onSaveInstanceState(Bundle) is called if you have not specified the android:configChanges tag in your manifest.xml file.

Resume previous activity from broadcast receiver

here's my problem:
I'm in activity A
Activity A starts a new activity B to call a number (Intent.ACTION_CALL)
How is it possible to resume activity A - I don't want to finish activity B, I simply want to bring activity A to the front
At the moment I have a broadcastReceiver which is able to start a new instance of activity A when the call takes place. If I start activity A from there with "FLAG_ACTIVITY_SINGLE_TOP", I get the error
Calling startActivity() from outside of an Activity
context requires the FLAG_ACTIVITY_NEW_TASK flag.
Is this really what you want?
But I don't want a new task, I want to RESUME activity A
Thanks a lot!
EDIT:
Perhaps I could simply simulate a click on the BACK-button?
Considering your use case - BroadcastReceiver that doesn't have a tasks in which to start the activity - singleTask in the manifest should do what you want.
"singleTask"
The system creates a new task and instantiates the activity at the root of
the new task. However, if an instance of the activity already exists in a
separate task, the system routes the intent to the existing instance through
a call to its onNewIntent() method, rather than creating a new instance.
Only one instance of the activity can exist at a time.
Note: Although the activity starts in a new task, the Back
button still returns the user to the previous activity.
Taken from: http://developer.android.com/guide/components/tasks-and-back-stack.html
Have you tried using FLAG_ACTIVITY_REORDER_TO_FRONT ?
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_REORDER_TO_FRONT
It seems to do exactly what you need.

Categories

Resources