Who moved my Activity?

Who moved my Activity?

  [[312428]]

Preface

I wonder if you have ever thought about this question: in daily development, the most commonly used method is to call up a new Activity through startActivity(). Who holds the reference to the created Activity object? The newly started Activity object should always be referenced during its life cycle, otherwise it will be recycled during the system GC. So what is the reference relationship?

In order to figure out the whole problem, the author began to search the source code (Android Q). First, I had to figure out how the Activity instance was created.

Creation of Activity object

The launch of an Activity is a cross-process communication process. For the client, the creation of an Activity will call back to the handleLaunchActivity() method in ActivityThread:

  1. @Override
  2. public Activity handleLaunchActivity(ActivityClientRecord r,
  3. PendingTransactionActions pendingActions, Intent customIntent){
  4. ···
  5. final Activity a = performLaunchActivity(r, customIntent);
  6. ···
  7. return a;
  8. }

Then I found the creation of the Acitivity instance in the performLaunchActivity() method:

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2. ···
  3. ContextImpl appContext = createBaseContextForActivity(r);
  4. Activity activity = null ;
  5. try {
  6. // Note 1: Create a new Activity instance through ClassLoader and the class name of the target Activity
  7. java.lang.ClassLoader cl = appContext.getClassLoader();
  8. activity = mInstrumentation.newActivity(
  9. cl, component.getClassName(), r.intent);
  10. ···
  11. } ···
  12. }

The creation of Activity is handled by the Instrumentation class:

  1. public Activity newActivity(ClassLoader cl, String className,
  2. Intent intent
  3. throws InstantiationException, IllegalAccessException,
  4. ClassNotFoundException {
  5. String pkg = intent != null && intent.getComponent() != null  
  6. ? intent.getComponent().getPackageName() : null ;
  7. return getFactory(pkg).instantiateActivity(cl, className, intent);
  8. }

The final creation work is further implemented by the factory class AppComponentFactory:

  1. public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
  2. @Nullable Intent intent)
  3. throws InstantiationException, IllegalAccessException, ClassNotFoundException {
  4. return (Activity) cl.loadClass(className).newInstance();
  5. }

At this point, the process of creating an Activity object is already very clear: get the Class object of the target Activity through the ClassLoader object and the class name, and then call the newInstance() method of the Class object to create an instance.

The graphical relationship is as follows:

Reference relationship of Activity object

After understanding the creation process of the Activity object, let's go back to the performLaunchActivity() method of the ActivityThread at the beginning and then look down:

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2. ···
  3. ContextImpl appContext = createBaseContextForActivity(r);
  4. Activity activity = null ;
  5. ···
  6. try {
  7. Application app = r.packageInfo.makeApplication( false , mInstrumentation);
  8. ···
  9. if (activity != null ) {
  10. ···
  11. activity.attach(appContext, this, getInstrumentation(), r.token,
  12. r.ident, app, r.intent, r.activityInfo, title, r.parent,
  13. r.embeddedID, r.lastNonConfigurationInstances, config,
  14. r.referrer, r.voiceInteractor, window, r.configCallback,
  15. r.assistToken);
  16. ···
  17. // Note 2: The ActivityClientRecord object holds a reference to the Activity instance
  18. r.activity = activity;
  19. }
  20. r.setState(ON_CREATE);
  21.  
  22. // Note 3: Add the ActivityClientRecord object to the mActivities collection
  23. synchronized (mResourcesManager) {
  24. mActivities.put(r.token, r);
  25. }
  26.  
  27. } ···
  28.  
  29. return activity;
  30. }

Here we seem to have found the answer we were looking for:

The newly created Activity object will be held by the passed in ActivityClientRecord object, and then the ActivityClientRecord object will be added to a collection called mActivities.

ActivityClientRecord is a static inner class of ActivityThread, used to record information related to Activity. The object creation process can be found in the LaunchActivityItem class (after API 28):

frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java:

  1. @Override
  2. public void execute (ClientTransactionHandler client, IBinder token,
  3. PendingTransactionActions pendingActions){
  4. Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart" );
  5. ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
  6. mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
  7. mPendingResults, mPendingNewIntents, mIsForward,
  8. mProfilerInfo, client, mAssistToken);
  9. client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
  10. Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
  11. }

Let's take a look at this mActivities collection:

frameworks/base/core/java/android/app/ActivityThread.java:

  1. ···
  2. final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
  3. ···

mActivities is a map collection, which is a member variable of the ActivityThread object. Since it is a collection, you can naturally find the operation of removing elements in the collection in the Activity destroy method callback:

  1. /** Core implementation of activity destroy call. */
  2. ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
  3. int configChanges, boolean getNonConfigInstance, String reason){
  4. ActivityClientRecord r = mActivities.get(token);
  5. ···
  6. synchronized (mResourcesManager) {
  7. mActivities.remove(token);
  8. }
  9. StrictMode.decrementExpectedActivityCount(activityClass);
  10. return r;
  11. }

The graphical relationship is represented as follows:

Since the Activity object is indirectly referenced by the ActivityThread object, the ActivityThread object should exist as a singleton. So how is the singleton ActivityThread object created and held?

Creation of ActivityThread object

When a new application process is created, the static main method main() of ActivityThread is called. Here, we found the answer:

frameworks/base/core/java/android/app/ActivityThread.java:

  1. ···
  2. // Annotation 4: Static ActivityThread member variable, used to implement singleton
  3. private static volatile ActivityThread sCurrentActivityThread;
  4. ···
  5.  
  6. // Note 5: ActivityThread's main method entry, called by RuntimeInit
  7. public   static void main(String[] args) {
  8. ···
  9. Looper.prepareMainLooper();
  10. ···
  11. // Annotation 6: Create a new ActivityThread object
  12. ActivityThread thread = new ActivityThread();
  13. thread.attach( false , startSeq);
  14. ···
  15. Looper.loop();
  16.  
  17. throw new RuntimeException( "Main thread loop unexpectedly exited" );
  18. }
  19. ···
  20.  
  21. private void attach(boolean system, long startSeq) {
  22. // Note 7: The ActivityThread object is referenced by a static member variable
  23. sCurrentActivityThread = this;
  24. mSystemThread = system;
  25. if (!system) {
  26. android.ddm.DdmHandleAppName.setAppName( "<pre-initialized>" ,
  27. UserHandle.myUserId());
  28. RuntimeInit.setApplicationObject(mAppThread.asBinder());
  29. final IActivityManager mgr = ActivityManager.getService();
  30. try {
  31. mgr.attachApplication(mAppThread, startSeq);
  32. } catch (RemoteException ex) {
  33. throw ex.rethrowFromSystemServer();
  34. }
  35. ···
  36. } ···
  37. }

From the above code, we can see that when a new application process is created, a new ActivityThread object is created in the main() method and assigned to a static member variable sCurrentActivityThread of the ActivityThread class, thus forming a relationship in which one application process corresponds to one ActivityThread object (singleton).

Summarize

Each newly started Activity, after its object instance is created by the newInstance method of the Class class, is wrapped in an ActivityClientRecord object and then added to the member variable mActivitys of the process's only ActivityThread object. In other words, the holding and release of Activity objects are managed by ActivityThread.

Finally, I would like to reiterate two additional points:

In the source code, the Activity object has a transfer relationship in multiple methods, which is quite complicated. The author is not very knowledgeable and may have missed some other important reference relationships without analysis. Everyone is welcome to correct me.

The framework source code above uses the latest Android Q version before the deadline. The relevant source code of this part will be modified for different Android system versions. I cannot compare and analyze them in detail one by one. I hope you can forgive me.

<<:  What happens from URL input to page display?

>>:  From a technical perspective, the development of 5G industrialization will become clear after reading this article

Recommend

I have seven solutions for implementing real-time web messaging

I have a friend~ I have built a small website, an...

Slow Internet speed? These 8 methods can completely solve it

Here are 8 ways to fix a slow Internet connection...

80VPS May Promotion: 800 yuan/month-E3-1230/32GB/1TB/8C (232 IPs) cluster server

80VPS is offering a promotion for some cluster se...

Network Performance Monitoring and Diagnostics Market Guide (2020 Edition)

With the acceleration of cloud migration and the ...

Difference between web scraping and web crawling

People sometimes mistakenly use the terms “web sc...

Network upgrades you should consider in 2021

As 2020 winds down and the new year dawns, it pro...

PoE Troubleshooting Guide: Common Problems and Solutions

Power over Ethernet (PoE) is a revolutionary tech...

Comprehensive network monitoring tools to watch in 2020

Network monitoring is one of the most important n...

Is the future of the new WIFI standard 802.11ad reliable?

Now there is a new WIFI standard that can increas...

"Online Documents" crashes in real time?

[[420464]] There is a magical phenomenon in this ...

This may be the correct way to open 5G

I wonder what you think 5G should look like? Fast...

Working principle of HTTP protocol: a must-read for front-end

Why does the front-end need to understand the HTT...

Why is optical fiber cheaper than noodles?

[[182802]] According to a report released by mark...