Question: How can I get the current activity? It has been asked dozens of times on Stackoverflow and other sites, and there are many suggested approaches. However, they all have flaws in one form or another.
In this post, I assume that there is no solution in the Android API, for example: Application.getTask().getRootActivity()
.
It would be nice if :-)?
So, to be clear, I am not asking for an answer to the question How can I get the current activity?
Instead, I request that such an opportunity not be provided. Given that each running application has a task (provided that the task has not been emptied), and each such task has root activity, it would be easy to provide access to this root action.
The fact that such access is not provided when it is so clearly desirable implies that there is something fundamental in the Android architecture that I don’t understand.
What am I missing? Why is this information not provided by the Android API?
For the background, here is a section that outlines some of the proposed approaches. I found the following two links, especially informative (each of the approaches below is presented on one or both links).
References
The approaches
- Static hook
- Reflection
- ActivityManager
- Other (Instrumentation, AccessibilityService, UsageStatsManager) `
ActivityManager
In the ActivityManager approach, only the name of the Activity class is specified, not the current instance of the Activity. For example, for an instance of Context c:
c.getSystemService().getActivityManager() .getAppTasks().get(0).getTaskInfo() .topActivity().getClassName()
Reflection
My favorite is the reflection suggested by _AZ , but this approach is fragile given that it relies on the insides. What I would like to see on Android is the approach provided by the standard API, which developers could then use safely.
Static hook
The most common approach is to use a static hook to maintain a link to the current activity. The hook can be either for activity, or for each application. The elimination of memory leaks can be avoided by saving / destroying the value of the hook (for example, in onCreate () / onDestroy (), onStart () / onStop (), onPause () / onResume ()). However, problems can arise when several actions are involved (for example, due to overlapping life cycles - see below).
I applied the static hook method, which does the following (to be completely transparent, I have not implemented # 1 yet - I am currently using the per-Activity static hook, which is a mistake).
- Provides a class that extends the application to provide it. The hook contains a stack; each node in the stack is a simple ActivityInfo class that contains a reference to an Activity instance, as well as the state of this instance (CREATED, STARTED, RESUMED).
- Provides an ActivityTracker class that extends Activity. Then I expand each of my activities with ActivityTracker. ActivityTracker uses its lifecycle callbacks to push / pop up / from the stack and update its state - my other actions should not do anything.
In theory, this would allow me to always know the full state of the returned task stack — a complete set of actions, including root activity, as well as their current state. In practice, however, there is a twist - when one action triggers another action, their life cycles overlap. During this period, peeking at the stack stack may lead to an unexpected instance of Activity.
From: https://developer.android.com/guide/components/activities/activity-lifecycle.html#soafa , "Coordination Actions":
Here is the order of operations that occur when starting Activity A Acivity B:
- Operation A onPause () is performed.
- The actions B onCreate (), onStart (), and onResume () are executed sequentially. (Activity B now has user focus.)
- Then, if action A is no longer displayed on the screen, its onStop () method does
Of course, this could also be controlled. The bottom line is that we have a global context that is accessible for storing information (application), and we have complete information about the transitions of the activity life cycle, so with sufficient effort I believe that this static approach based on stacks is probably a proof .
But at the end
But in the end it seems to me that I'm just rewriting code that probably already exists inside to control the activity stack, so I ask (if you forgot):
Why is there no Android API to get current activity?
UPDATE
In this update, I will summarize what I learned from this topic and my own experiments and research. We hope that this summary will be useful to others.
Definitions
I am going to use the following definitions for “Activity Visibility States” based on the definitions of activity states in https://developer.android.com/guide/components/activities/activity-lifecycle.html .
Questions
The very definition of “Current Activities” is muddy. When I use this, I mean one action in a full-sized state. At any given time, such an activity may or may not be. In particular, when Activity A starts Activity B, A onPause () is called, then B onCreate (), onStart (), and onResume (), followed by A onStop (). There is a stretch between A onPause () and B onResume (), where none of them are in the Fully Visible state, so there is no current activity (as I define it). Of course, there are situations when the background thread may want to access the current action and may or may not be an activity at all, and even more so the current activity.
I also realized that I might not always need the current ("fully visible") activity. In many cases, I may just need a link to an existing event, regardless of whether it is visible or not. In addition, this link can only apply to any Activity (for situations when I need to pass a general action reference for some API method), or it can be a specific instance of the Activity subclass (so that I can initiate some code specific to this Action subclass )
Finally, you need to understand when activity lifecycle callbacks invoke the main UI looper and how events such as configuration changes are handled. For example, if I create a DialogFragment using an Activity Activity that is currently in the "Not Visible" state, will it ever be displayed, and if so, when? Similarly, it turns out that the onDestroy () and onCreate () methods, caused by the configuration change, are contained in the same message in the message queue of the user interface (see Message queue for accessing the Android user interface, the sending order ), therefore, between these two callbacks ( during configuration changes) no other messages will be processed. Understanding this level of processing seems to be crucial, but the documentation for it is sorely lacking, if not completely enough.
The approaches
Here is a set of approaches that can be used to solve most of the above situations.
Background
- For discussion, suppose Activity A and Activity B, where A creates B.
- Generally speaking, you can create a global variable, "public static" for almost any class. Expanding the Application class conceptually and adding it to the extended class will be fine, but if this is too much work, it can be included (for an instance) in one of the Activity classes.
General work guide
- Useful whenever a general operation is required.
- Create a global variable. In both A and B, onCreate () set it to "this", and onDestroy () set it to null.
Best job reference
- Useful whenever you want to access your current visible activity.
- Create a global variable. In both A and B, onResume () sets the value to "this". This approach works fine if all the actions are not completed, in which case you may need to create a separate flag to indicate this situation. (This flag may be the above referenced link implementation).
Case Reference
- Useful whenever you need to process a specific instance of an Activity subclass.
- In A and B: create a global variable in the Activity subclass itself. Have onCreate () set it to "this and onDestroy () set it to null.
Application context
- Useful whenever you need a context covering the life cycle of the entire application, or when you do not need to use a specific activity context (for example, to create a toast from a background thread).
- You can get this from Activity getApplication () and save it on a static hook.
Handling Configuration Changes
There may be times when you want to stop / start a background thread only through an activity session where I define a “session” to include a series of Activity instances that can be created and destroyed due to configuration changes. In my particular case, I have a Bluetooth Chat activity and an associated background thread to handle a network connection. I do not want the connection to be destroyed and created every time the user rotates the device, so I need to create it only when it does not exist and destroy it only if the configuration change is not performed. The key point here is understanding when onDestroy () is called due to a configuration change. This can be done with or without fragments. As often happens, I prefer a non-fragmented approach, since the fragmented approach does not seem to me unnecessary complexity.
Approach 1: No Fragments
In onCreate (), create a background thread if it does not already exist. In onDestroy (), destroy the background thread only if isFinally () returns false.
Approach 2: with fragments
This works well because the FragmentManager will store fragment instances in configuration changes if setRetainInstance (true) is used. For an excellent example, see http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html . An example for AsyncTasks, but can also be used to control the background thread (just create a thread instead of AsyncTask in the onCreate () fragment, and then destroy the stream in the onDestroy () fragment).
Closing
A thorough understanding of these issues requires a deep understanding of how the user interface looper handles the message queue - when Activity callbacks are called, how other messages alternate with them, when display updates appear, etc. For example, if a DialogFragment created using an instance of an invisible Activity, will it be displayed at all, and if so, when? Perhaps someday, Android will provide a deeper API for tasks and related backlinks, as well as documentation describing user interface message processing and related mechanisms. Until then, more "source code and / or ... empirical analysis" :-).
Thanks,
Barry