I am not able to find best solution to the situation where child activity changes data of parent activity.
I have activity A containing a list of items. Activity A starts activity B to show details to the user. User can run action and create activity C. Activity C creates new elements for the list in activity A.
The data are held in database so there is no problem with passing the data. I'm interested only in notification.
What is best solution to notify activity A that the data have been changed?
Currently I am aware of 2 solutions:
1) Return result by activity B and C with startActivityForResult(..) and Extras. The result would contain message "datachanged" -> true / false.
I don't like this one because I am not able to send the message directly
2) Always refresh data in activity A on resume.
Isn't that waste of processing?
3) Send Intent from activity C to activity A (broadcasts)
The solution I have found which are almost certainly wrong:
4) Save in some global state
There is no global state (am I right?). We shouldn't do that as there is an extra abstraction layer (intents).
5) use getParent() to use parent activity
The parent activity may be destroyed and recreated on return
Isn't there some other more light-weighy messanger system beetween activities? Something like Handler and Messanger in Activity-Service communiction. Maybe there shouldn't be one because it's against design?
Another option is to use broadcasts or localbroadcasts in order to send notification / event from C to A.
It will look something like:
1. Activity A loads the data in onCreate.
Activity A registers a broadcast receiver to some custom action "com.mypackage.datachanged".
Activity C sends broadcast intent with the same action once it changes the data.
This way - if Activity A is still kept in memory - it's receiver will catch the event and will reload your list when needed. If activity A is killed - it will auto refresh the data automatically when it is recreated.
Some notes:
1. Do not forget to un-register the receiver in Activity A's onDestroy.
2. You can use local broadcasts instead of broadcasts. The advantage:better performance and security (your intent will never leave your app).
Sending the message through B and C with startActivityForResult is the best way to do this.
Related
So, I have 2 activities, the first one(lets call this A) passes data to an activity with a list, lets call this B. B then uses the data from A and makes a list out of it.A service S is then called by B. The service then calls another activity lets say C, which in turn starts B. Should B still work with all the data?BTW all activities are ActionBarActivities.
In the general case, each time an activity in invoked using startActivity, a new instance is create.
This behavior can be modified by the use of the launchMode. singleTask will always be the same instance, but your activity must be at the root of the task
An existing activity can also be called back to the front of the stack using the flag FLAG_ACTIVITY_REORDER_TO_FRONT. It is unclear in the documentation how the new data is passed, or whether the previous data in the activity is kept.
I have following activities. 1) Main activity 2) Activity 1 3) Activity 2
This is the way Activities is connected together
Main Activity -> Activity 1 -> Activity 2(Create Background Async Task) So "Activity 1" is child activity and "Activity 2" is a subchild activity. And creating Background Async task from "Activity 2"
So Let's say I am currently on "Activity 2" screen and Backround Async task is running. Now after some failure event on "Background Async task", I want go back to "Main activity". What can be good way to do this?
Few way I am thinking is, 1) To restart the app completely 2) To create new Intent of Main activity from Background Async task just like we created "Activity 1 & 2".
I am not sure which one from this is good to use. Is there any other way to do it?
I am kind of newbie to android. I am currently using BroadcastReceiver(on Activity 1 & 2)to finish current activity when Background Async task broadcast failure event but sometimes it jump to "Main activity" and sometimes jump to "Activity 1". So not working as I am looking for.
Will you please suggest a good way to do this?
[Sorry, I had to ask this question once again. Because old question was deleted]
#Rohit, I tried way you suggested to finish Activity 1 before going to Activity2. That also works but It create one another problem.
After doing this, when I press back button it directly go to Main activity rather than Activity 1 since it no longer exist.
Is there any solution which can get this both thing working? I want to jump to Activity 1 when back button is pressed. And I want to jump to Main activity when Background Async task created from Activity 2 find problem.
[Edit]
Let me describe more specific case.
Main Activity list open Wifi access point based hardware.
When clicked on particular listed access point, It opens Login activity and ask for user id & password for hardware access. Login activity onCreate function also create TcpAsyncTask background task which will communicate with hardware(which has WiFi accesspoint feature) for authentication and data passing.
When user enter login detail and click on login button, It will create momentary background task LoginDeviceAsyncTask. This task will communicate to hardware via TcpAsyncTask created earlier to verify authentication. If authentication is successful, it will create Navigation activity using startActivityForResult.
In the Navigation activity, It list options for opening specific window e.g. Dashboard. History. When user select Dashboard from options, it creates Dashboard activity using startActivity. When user select History from options, it creates History activity using startActivity.
So if Dashboard activity is opened, it display some data periodically received from hardware using TcpAsyncTask(created earlier in Login activity)
Similarly if History activity is opened, it display some data periodically received from hardware using TcpAsyncTask(created earlier in Login activity)
Dashboard/History activity has options to move to History/Dashboard activity directly. For this it creates selected activity using startActivity and finish current activity.
Now my problem is,
Let's say I am in Dashboard activity and it is displaying data received from hardware using TcpAsyncTask. Now somehow TcpAsyncTask fails to communicate with hardware and at this point I want to move to Main activity directly.
In TcpAsyncTask, I don't have context of activity like Navigation/Dashboard/History which was created after TcpAsyncTask. May be I can get this context in TcpAsyncTask using some MainActivity variables which is updated as per create/finish. Am I correct?
I am current broadcasting to close from TcpAsyncTask and receiving broadcast message in Navigation/Dashboard/History activity using BroadcastReceiver and finish particular activity which received broadcast close message. But sometimes it jump to Main activity and sometimes it jump to Navigation activity when sendBroadcast from TcpAsyncTask to close.
Pass Context in your AsyncTask and through that you can kill your Activity 1 as well as Activity 2
((Activity)context).finish();
You can do this for both activities by passing context of both.
One possible solution is to start Activity 2 with startActivityForResult() then during its processing, you can call setResult() to indicate success or failure. In Activity 1, override onActivityResult() and detect the result of Activity 2. If Activity 2 indicates a failure, simply call finish() on Activity 1 to return to the main activity.
Firstly, remember that code being run in an inactive activity when it is not in the foreground cannot be relied upon. So you should not rely on background async task. Instead, use a Service which does the background processing; this is precisely what Services are meant to encompass (among other persistent processing). While it is quite a lot for a beginner to learn, it will save you a LOT of trouble long term, as its lifecycle is far easier to manage than complex webs of activities and their relationships and lifecycles.
You should read up on android "Back Stacks". If you finish an activity, the previous activity on the back stack will be displayed. These are like the history browsers maintain that determine how browser back-buttons to operate.
So what you need is the main activity spawning a new instance of the sub-activity. Then when the user navigates the back button, the previous activity in the back stack (your main activity) will be resumed. This will not need intents.
However, for an activity to be triggered by a background processing task, I strongly recommend using a Service and intents to co-ordinate it all.
How can i transfer data between two activities, without actually move to that activity? The scenario is as follows:
Activity A is a splash screen. While the splash screen is showing, some data is being generated in the background of type ArrayList. After This data is loaded, i need to start Activity B (lets say a Login screen) and just before that i need to transfer the data to Activity C. I know i can use PutExtras for transferring data, but wont this run the target Activity instead only sending the data?
Thanks
The full flow of the app is as follows:
Activity A (splash screen) --> Activity B (Login screen) --> Activity D (some user interface and buttons) --> Activity C (the activity which should be able to load the data generated in the splash screen upon certain Button press).
I think You don't fully understand life cycle of Activity and application.
Rule of thumb, there is always just one activity, (it is the worst scenario when each activity has to be recreated when You go back to it) the one visible at the moment. You can't do things like start activity but don't show it yet or anything like this.
If You have expensive task to do like downloading data do it in AsyncTask
if this task has to be start and finish when You enter and leave Activity A use fragments
if it has to finish once You start whether Activity A is still existing or it was already destroyed use services
Places You can store this loaded data so it may be easily accessed later form any Activity is database with help of Loaders(loader is optional but really nice) or SharedPreferences.
If you want to pass data to an activity that is running and not start a new instance of that activity you just need to set flags on your intent. For example:
Intent i = new Intent()
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
will not re-launch an activity if it is already running at the top of the stack. If all you want to do is get data to Activity C which is running without starting it again you could try this or one of the other flags. There's a bunch of them. Just look look through the list and see if one fits your needs.
I am using an Intent Service that performs an action and needs to pass back to the activity that started it the results of the action.
I've searched through dozens of similar posts but as far as i can tell, all solutions i found have a problem. They don't handle well screen rotation. Suppose an activity starts the Intent Service, the service takes 10 seconds to perform the action, and during those 10 secs, the screen gets rotated. The activity gets destroyed and a new one is created.
Use Receiver : It creates a memory leak , as the receiver is bound to the activity that must be destroyed, so the activity never gets destroyed.
Use Broadcast : You have to register a listener, and unregistered the listener before the activity gets destroyed. If the broadcast message arrives after the listener is unregistered, and before the new activity's listener is registered, the message will never be received.
Use Messaging : Same as receiver.
Use Shared Preferences/database with listener : Same as Broadcast.
The solution i came up with, is having the service save the result in a preference file, and the activity checking regularly (lets say every 200ms) for a change in the preference file. Thus, when the screen rotates, the activity stops checking, and starts again when recreated. If the result was delivered in between, it still gets to the (recreated) activity. However, it seems as though this consumes cpu and performs unnecessary reads from the SD card.
Another solution would be to have the service save the result in preference file/database and set a global variable to the time it saved it. The activity has a listener to the preference file/database. Before registering the listener, it checks the global variable to see if the result was put during the screen rotation (global var < currentTimeMillies()) and if true, gets the result, if not, registers the listener. Since the result might be put between the check and the registration, this has to be done inside a block in which the activity holds a lock that the service must acquire to put the result. This would also work, but it is way too complicated.
Is there a simpler and more elegant way of doing it, surviving a screen rotation?
Have a look at my answer to this question:
How to handle IPC between a service and an activity (and its subactivity)?
Perhaps that will give you an idea.
EDIT (Add following suggestion):
Another approach would be to use a Receiver which you create in the Activity. On a screen rotation, the OS will call onRetainNonConfigurationInstance() where you can return the Receiver instance and it will get handed off to the new Activity (see getLastNonConfigurationInstance()). NOTE: These methods have been deprecated in 4.0 and you can use a Fragment and setRetainInstance() to achieve similar behaviour.
In my application I start a Register activity from the Main Activity with a button
startActivity( new Intent(getBaseContext(),Register.class));
This Register activity creates records that can be viewed in a ListActivity.
If I go to the ListActivity from the Main Activity (with another button) and decide that I want to send the data of a record from the ListActivity to the Register activity in order to edit it, there are two possibilities:
1- the Register activity has never been called
2- the Register activity is opened in the background
I have two questions:
-Calling startActivity Register.class once from Main and once from ListActivity will it make two copies of Register or one?
-When I call Register activity from ListActivity and try to pass data with a handler, the handler message arrives before the activity is opened and the data do not show in the Register screen? How can I do that? Is there a way to know from the ListActivity when the view of Register is opened?
Thanks.
Charles.
The first question has a complex answer, but shortly you can control instantiation of activities, only it has implications in flow of user interaction: see Activities and Tasks, particularly Launch modes.
For the second, I don't know what kind of data you're going to pass, but consider putting it in the intent with some Intent.putExtra() method (there are many for many types of data).