1

I have a set of activities that I am navigating between, and because these activities are expensive to initialize, I would like to preserve the existing state if possible. The problem is that I might have the same activity open more than once, with a different state in each, and I didn't see a way that the standard Android flags could help me out with that situation.

Here's a visual example:

Imagine I have three activities:

A, B, C

Each of these activities can be open more than once, each one holding a different state. Not only are these activities expensive to initialize, but I'd also like to maintain the user's current state such as scroll position, selected items, etc...

Let's say that A(1) is an instance of activity A in state 1, and A(2) is an instance of activity A in state 2. A(1) and B(1) are unrelated.

I want to implement a circular navigation stack as follows:

... --> A(1) --> A(2) --> B(1) --> B(2) --> B(3) --> A(3) --> C(1) --> A(1) --> ...

Since the activities are expensive, I'd really like to just reuse the existing instances. However, I might need to keep around 2 or 3 instances of the same activity as you can see above.

Thanks!

Kevin
  • 11
  • 1
  • 3
  • What have you attempted so far, do you have any code that isn't working? – hooked82 Aug 26 '11 at 16:56
  • At the moment I just create a new activity each time and I use a BroadcastReceiver to ensure that duplicate activity/states close themselves. – Kevin Aug 26 '11 at 18:03

4 Answers4

4

Ok, so this actually is possible but you need to use reflection and the Intent.FLAG_ACTIVITY_MULTIPLE_TASK flag.

Here's how I did it:

Controller: starts activities with Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK

Activities: Register themselves with the controller by passing in a unique id to identify their state, and their task id using getTaskId()

Controller: Creates an activity if the task has died (it checks with ActivityManager.getRunningTasks()). If the task is still alive, then it uses the following reflection code to bring a task to the front:

Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");

Method getDefault = ActivityManagerNative.getMethod("getDefault", null);
Object activityManagerNative = getDefault.invoke(ActivityManagerNative, null);

Method[] methods = activityManagerNative.getClass().getMethods();

Method moveTaskToFront;

for (final Method method : methods) { if (method.getName().equals("moveTaskToFront")) { moveTaskToFront = method;}}

...

moveTaskToFront.invoke(activityManagerNative, registeredTaskId);

Animations can be controlled by calling overridePendingTransition from the activity that requested the task change.

Hope this can help someone else out there.

Kevin
  • 49
  • 2
2

moveTaskToFront.invoke is only available after API 11. How to implement the same in a API level 8 application.

Himanshu
  • 31,810
  • 31
  • 111
  • 133
Adilmo
  • 117
  • 1
  • 9
1

In the AndroidManifest.xml you can define for every activity of your app, how the runtime system should launch the specific activity. In your case you should define the launchMode property of your activity with the value "singleTask". Then the runtime system won't create multiple instances of the specific activity. Every time, when the specific activity should be launched, the runtime system will onvoke the onNewIntent() activity. In that method you can handling your different states of one specific activity.

AZ13
  • 14,397
  • 5
  • 35
  • 31
  • The thing with this is that it means the activity would have to reload state. This involves a lot of expensive database queries and network queries as well as resetting UI state which is why I was hoping to just switch to a particular instance that already has the correct state. – Kevin Aug 26 '11 at 18:05
  • You mentioned in a different answer that you want to keep references to data, views and adapters with a singleton class. An activity with the launchMode singleTask does exactly the same. It acts like a singleton. In the onCreate try to get all data you need (database, network, inflate views, configure adapters). In the onNewIntent you have then access to this references. You can assign data to already inflated views or do other ui related logic. – AZ13 Aug 26 '11 at 21:50
  • have you looked at this question ...similar to yours ...http://stackoverflow.com/questions/6905774/how-do-i-avoid-oncreate-being-called-when-starting-an-activity – jsp Aug 26 '11 at 23:37
  • Thanks for the update guys. It seems like I would have to write wrapper code to save & restore state, like the contents of the list adapter, the current scroll position, and what was checked/etc.... which seems like a lot more engineering work than simply calling something like show(A(1)). The downside is also because we are trying to coordinate this across multiple applications and teams. Maybe the real problem is that our activities are heavy, so if we improve the launch speed then it might be easier to preserve things like the checked state. – Kevin Aug 29 '11 at 13:56
0

What may work is this: Don't allow more than 1 instance of a particular activity. You do this when you declare each activity in the manifest by setting the launchMode. Then, keep track of the instance states by making an application wide object containing each activities Bundle object. When opening an activity, pass it the appropriate Bundle.

SBerg413
  • 14,515
  • 6
  • 62
  • 88
  • Hmm... maybe I could use an application-wide singleton that would contain the adapter lists and all other sorts of data? I was hoping to just reuse the same instance so I wouldn't have to reapply all of the state data. The application-wide object might be faster than reloading everything from DB but how much faster I don't know. – Kevin Aug 26 '11 at 18:07
  • The benefit of doing what I suggest would be that the heavy lifting in the onCreate of each activity would only need to be done once. Apply the instance state for each activity would definitely be faster and more efficient than loading everything back up from the db. – SBerg413 Aug 26 '11 at 18:48
  • I was really hoping I could just do a showActivity(A(1)) to return to where I was before. ;) This will work too, it's just the engineering downside of writing code to save scroll state, adapter contents etc... and then reapply this later that has me worried especially since it touches multiple teams and apps. Thanks for letting me know though. – Kevin Aug 29 '11 at 14:22