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
Related
I start Activity B from Activity A. Activity B does something to my Data and at some point I call finish();, however Activity B is still doing something with my Database in the background and when its finished I want to get notified in Activity A, that this process is finished.
Because there is no way to my knowledge to call a method from another activity I tried to solve this problem with startActivityForResult but because Im calling finish(); before actually setting a Result this does not seem to work either.
Any Idea on how to solve this problem?
Don't do task in Activity that may live beyond Activity lifecycle. In that case do your task in Service and notify the Result to Activity. There are some ways to communicate between Activity and Service using BroadcastReceiver , Messenger, Handler, Bound Service . You can also use EventBus library for this communication.
Check this and this thread for communicating between Activity and Service
Try to close your database, and after it's been closed successfully, 'notify' your activity A about this result and finish activity. Quite an imprecise question, frankly :P
And if you want to invoke some method from another class, pass the activity's Context.
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.
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.
I'm passing a Serializable object using an Intent from Activity A to Activity B, but when I print the object.toString() method before putting the object into the intent in A and after retrieving it in B, I see that they are actually different instances.
Here's the problem. I have the fields of that object initialized in a different class and different thread, and by the time I put the object into an intent in Activity A, those fields aren't ready yet, but by the time I retrieve them they are ready. However, when I attempted to retrieve those fields in Activity B, I saw that they were empty because the retrieved object was a new instance.
How do I solve this problem? I don't know how to make the main thread wait until the object-initializing task is over (already tried thread.join()). I basically just have to send two ArrayLists from one activity to another, except when I'm sending them (i.e. putting them into an intent) they are empty but by the time they are being retrieved they are already populated.
When serializing an object it's translated into a form that can later be used to create a semantically identical clone of the original object. This will of course be a different instance of the original object.
There are two scenarios I could think of:
Your Activity B can run without having the information you're trying to send via Intent. In this case Activity B could register a BroadcastReceiver and your thread, once finished, would broadcast the data via LocalBroadcastManager
Your Activity B can not run without that data and Activity A runs the code that generates that data e.g. in an AsyncTask and starts Activity B once the task has finished (that would be in the onPostExecute() method).
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)