preface
From the previous article (Binder application Layer Summary and Analysis), you saw an overview of interprocess communication, such as the Activity launch process described today, with Binder. The system wraps a lot of work around this process, making it easy to start an Activity. This seemingly simple fact is actually the result of multiple communications between the Activity and ActivityManagerService. Read the article suggested with the source code to eat together, taste better.
Launcher
The mobile desktop is also an App, and the icon of each application is listed on the Launcher. Clicking the icon triggers the onItemClick event. For example, if we want to start the App “Taobao”, we should first define the default Activity information in the manifest file.
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>Copy the code
Then Launcher gets this information and launches the Taobao App
// The package name of the application String PKG = info.activityInfo.packageName;// The application's main activity class Stringcls = info.activityInfo.name;
ComponentName componet = new ComponentName(pkg, cls);
Intent i = new Intent();
i.setComponent(componet);
startActivity(i);Copy the code
Starting an Activity is managed by the Activity manager ActivityManagerService (AMS), whether it is two different activities in the same application, or different applications in different processes. The communication between them needs to use Binder, which communicates with AMS for many times to launch taobao App.
The whole process
Through the study of Android operating system can improve our understanding of operating system in the implementation of technology, which is very helpful to strengthen the internal power of developers. However, since most of the internal implementation of Android is more complex, the study of internal implementation should focus more on the overall process grasp, and can not go into the details of the code.
- The Launcher notifies AMS to start the MainActivity of the Taobao APP, which is the Activity that the manifest file sets to start.
- AMS records information about the Activity to start and tells the Launcher to enter the pause state.
- When the Launcher enters the pause state, AMS is notified that Paused has been executed and Taobao can be started.
- Taobao app has not been opened, so AMS starts a new process and creates an ActivityThread object in the new process, which executes the main function method.
- After the main thread of Taobao APP is started, notify AMS and pass in applicationThread for communication.
- AMS notifies Taobao to bind Application and start MainActivity.
- Taobao launches MainActivitiy, creates and associates the Context, and calls the onCreate method.
startActivityForResult
Let’s start with the Activity’s startActivity method. There are several ways to overload the startActivity method, but they all end up calling the startActivityForResult method.
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if(options ! =null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1); }}Copy the code
Within the startActivityForResult method, the execStartActivity method of the Instrumentation is called.
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
......
Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . }Copy the code
Instrumentation
Instrumentation literally means Instrumentation, specific to the program is a tool class to manage activity, including creating and starting activity, activity lifecycle methods are controlled by Instrumentation this instrument, Only one Instrumentation instance is used in a process.
/ * * * * * @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
* is being started.
* @param token Internal token identifying to the system who is starting
* the activity; may be null.
* @param target Which activity is performing the start (and thus receiving
* any result);
* may be null if this call is no`t being made form an activity.
* @param intent The actual Intent to start.
* @param requestCode Identifier for this request's result; less than zero
* if the caller is not expecting a result.
* @param options Addition options.
*
*/
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, intrequestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; .try {
intresult = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
requestCode, 0.null, options);
// Check the result of starting the Activity (throws exceptions, such as unregistered Activity in the manifest file)
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}Copy the code
We intercept the more critical code fragments to analyze the Instrumentation execStartActivity method, method parameter annotation also has a simple description of several parameters of the method. Let’s examine the two most important parameters, contextThread and Token.
IBinder contextThread
In a method on the incoming to mMainThread getApplicationThread () we can see that this is an IBinder object, shows that its role is the Binder used for interprocess communication object.
MMainThread is actually an ActivityThread object. The ActivityThread, which is the main thread, the UI thread, is created when the App starts, and it represents the App application. What? The ActivityThread represents the App Application. In fact, Application may be very important to us App developers, but in Android system is really not so important, it is a context. Doesn’t an Activity have a Context? The Application is the context for the entire ActivityThread.
We find the ActivityThread file. The getApplicationThread method actually gets the inner class ApplicationThread object and ApplicationThread inherits ApplicationThreadNative
public final class ActivityThread {// No other classes are inherited or implemented.
final ApplicationThread mAppThread = new ApplicationThread();
public ApplicationThread getApplicationThread()
{
return mAppThread;
}
//ActivityThread's inner class ApplicationThread
private class ApplicationThread extends ApplicationThreadNative {. }}Copy the code
We open the ApplicationThreadNative class (available from the SDK source, The path is Sdk\sources\ Android-25 (Sdk version)\ Android \app\ApplicationThreadNative) ApplicationThreadNative is the AIDL communication Stub (server). ApplicationThreadProxy is the AIDL communication Proxy (client). See AIDL II for details. So ApplicationThread is the concrete implementation class for communication. As mentioned above, the Activity is actually started as a result of multiple interprocess communications, which leads us to conclude that ActivityThreads communicate with each other through the internal class ApplicationThread
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread {
static publicIApplicationThread asInterface(IBinder obj) {... } @Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, intflags) throws RemoteException {... }public IBinder asBinder(){return this;}
}
class ApplicationThreadProxy implements IApplicationThread {
private final IBinder mRemote;
publicApplicationThreadProxy(IBinder remote) { mRemote = remote; }... }Copy the code
public interface IApplicationThread extends IInterface {. }Copy the code
IBinder token
Traceback to the parameter origin, this token object is passed in the Attach method of the Activity, which is the Activity information passed in when the Activity is created and associated (as described below). This is also a Binder object that represents the Activity Launcher, which is also sent to AMS through Instrumentation. AMS can query it and know who sent the request to AMS.
//Activity
private IBinder mToken;
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent.String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
mToken = token;
}Copy the code
The contextThread and token parameters are foreshadows that are passed to AMS. When AMS wants to send a message to the Launcher, it can use these two parameters to find the Launcher.
startActivity
In Instrumentation, launch the Activity the real implementation is by ActivityManagerNative getDefault startActivity () method to complete.
int result = ActivityManagerNative.getDefault(a).startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null,
requestCode, 0, null, options);Copy the code
ActivityManagerService (AMS) is derived from ActivityManagerNative (AMN), which is derived from Binder and implements the IActivityManager interface. AMS is a Binder, therefore, it is the concrete implementation IActivityManager, because ActivityManagerNative. GetDefault () is actually a IActivityManager type of Binder object, So its concrete implementation is AMS.
Let’s take a look at the code and go through the above relationships.
public final class ActivityManagerService extends ActivityManagerNative {. }public abstract class ActivityManagerNative extends Binder implements IActivityManager{. }public interface IActivityManager extends IInterface {. }Copy the code
As we know from our AIDL analysis, stubs hold Binder native objects and proxies hold Binder Proxy objects. So system naming is also based on this factor (Native) as the naming norm.
/** * Retrieve the system's default/global activity manager. */
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
returnam; }};/** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */
static public IActivityManager asInterface(IBinder obj) {
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if(in ! =null) {
return in;// Same process, return Stub local object.
}
return new ActivityManagerProxy(obj);// Across processes, return the proxy object.
}Copy the code
It can be found that in AMN, AMS, the Binder object, is provided externally in Singleton mode. Singleton is a Singleton encapsulation class. When its GET method is called for the first time, it initializes AMS through create method. Subsequent calls directly return the previously created object (using the same AMS).
By analyzing the Create method, Binder analysis above shows that in IActivityManager (equivalent to Client), Binder reference 0 in the application can be obtained from SMgr for the Server. AMN uses getDefault to get a Binder reference object for AMS from ServiceManager and convert it into an ActivityManagerProxy object (AMP), which is the AMS proxy object.
Similar to the client binding code in AIDL, we can now communicate with AMS through ActivityManagerProxy (asInterface returns IActivityManager).
//ActivityManagerProxy
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain(a); / / fromParcelIn the pool to getParcelObject (communication carrier) used to write data
Parcel reply = Parcel.obtain(); // If the method has a return value, write the return valuedata.writeInterfaceToken(IActivityManager.descriptor); //BinderA unique identifier// contextThread (contextThread); // contextThread (contextThread);AMSI can connect it toLaunchercommunicationdata.writeStrongBinder(caller! =null ? caller.asBinder(a) : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);//resultTo is the token mentioned aboveLaunchertheActivityinformationdata.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);. // Send type isSTART_ACTIVITY_TRANSACTIONThe request toAMS.datacontainsLauncherAnd taobaoAppThe information of
mRemote.transact(START_ACTIVITY_TRANSACTION.data, reply, 0);
reply.readException();
//InstrumentationResult to check and throw an exception (ActivityInt result = reply.readint (); reply.recycle();data.recycle(a);
return result;
}Copy the code
Amn.getdefault () is actually AMS, so the starting process of the Activity is moved to AMS. Just look at AMS’s startActivity method.
- The Launcher notifies AMS to start the MainActivity of the Taobao APP, which is the Activity that the manifest file sets to start.
AMS
In the previous figure, we first summarized the overall process of Activity startup in an easy-to-understand way, and had a general understanding of the overall framework.
AMS analysis
Again, by looking at the AMS class, we know that AMS is also a Binder and that it is a concrete implementation of IActivityManager.
public final class ActivityManagerService extends ActivityManagerNative {. }public abstract class ActivityManagerNative extends Binder implements IActivityManager{. }public interface IActivityManager extends IInterface {. }Copy the code
Then we continue to analyze the startActivity method of AMS. This stage is quite complicated and tedious, and we will get confused if we are not careful. We understand the general process without going into the details of the code, and we can establish enough understanding of the overall process. (Limited skill, after all)
//AMS
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity".null);
//7.0Acitivty New ActivityStarter(originally ActivityStackSupervisor)
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null.null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null.null, bOptions, false, userId, null.null);
}Copy the code
ActivityStarter
//ActivityStarter
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
Bundle bOptions, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {
` `` `` `
// Select an appropriate activity from the system based on the intent.
// The ResolverActivity pops up to let the user select the appropriate application.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
profileFile, profileFd, userId);
` `` `` `
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, outRecord, container,
inTask);
` `` `` `
return res;
}
// In startActivityLocked, verify the parameters passed in, and create an ActivityRecord object, Call again startActivityUnchecked startActivityUncheckedLocked is before (7.0) method to start the Activity.
final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
` `` `` `
// Create an ActivityRecord object
//ActivityRecord: In AMS, ActivityRecord will be used as the recordkeeper for the Activity. Every Time an ActVityRecord is started, there will be an ActivityRecord object representing a record of the Activity
ActivityRecord r = newActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! =null, mSupervisor, container,
options, sourceRecord);
` `` `` `
err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true, options, inTask);
// This notifies the ActivityStarter that the Task for the Activity has been moved to the foreground
postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
return err;
}Copy the code
In the startActivityLocked method, verify some of the parameters passed in, create an ActivityRecord object, and call the startActivityUnchecked method to start the Activity.
Understanding the startActivityUnchecked method, which is responsible for scheduling ActivityRecords and tasks, is key to understanding the Actvity startup pattern.
The startActivityUnchecked algorithm for scheduling tasks is complex, depending on the current stack rollback, the startup mode of the acitivity to be started, the taskAffinity attribute, and the intent flag that is set when the activity is started. The intent flags can vary, so the algorithm is complex and requires reading the source code and combining it with a specific startup situation to understand.
ActivityRecord is then added to the rollback stack by calling ActivityStack’s startActivityLocked
//ActivityStarter
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
``````
//ActivityStack's startActivityLocked, don't get confused.
// At the same time, call WindowManager to prepare the work related to App switch
mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
if(! mTargetStack.isFocusable() || (topTaskActivity ! =null&& topTaskActivity.mTaskOverlay && mStartActivity ! = topTaskActivity)) {// If the activity is not focusable, we can't resume it, but still would like to
// make sure it becomes visible as it starts (this will also trigger entry
// animation). An example of this are PIP activities.
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
} else {
/ / call ActivityStackSupervisor resumeFocusedStackTopActivityLocked finallymSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); }}Copy the code
ActivityStackSupervisor
//ActivityStackSupervisor
boolean resumeFocusedStackTopActivityLocked() {
return resumeFocusedStackTopActivityLocked(null.null.null);
}
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if(targetStack ! =null && isFocusedStack(targetStack)) {
/ / to start the corresponding Task to the foreground Task Activity, call the Task corresponding ActivityStack resumeTopActivityUncheckedLocked function
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null|| r.state ! = RESUMED) {/ / otherwise you just call the front desk resumeTopActivityUncheckedLocked stack
mFocusedStack.resumeTopActivityUncheckedLocked(null.null);
}
return false;
}Copy the code
ActivityStack
Follow up with ActivityStack
//ActivityStack
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
` `` `` `
result = resumeTopActivityInnerLocked(prev, options);
return result;
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
//mResumedActivity points to the Activity Launcher that was launched last time.
if (mResumedActivity ! = null) {
` `` `` `
// Notify Launcher to enter the pause state
pausing | = startPausingLocked(userLeaving, false.true, dontWaitForPause);
}
if (pausing) {//Launcher has paused
` `` `` `
if (next.app ! = null && next.app.thread ! = null) {
// If the app is already started
// Call the priority of the process where the Taobao (to be started)Activity is located to ensure that it is not killed
mService.updateLruProcessLocked(next.app, true.null); }}` `` `` `
if (next.app ! = null && next.app.thread ! = null) {
// If the Intent is not empty, call the NewIntent method
next.app.thread.scheduleNewIntent(next.newIntents, next.appToken, false);
// Assuming that the Taobao App is already started, click the Home button to go back to the Launcher and start Taobao from the Launcher again (or a third party to start the App already started)
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
mService.isNextTransitionForward(), resumeAnimOptions);
` `` `` `
} else {
` `` `` `
// Create a process and cold start the Activity. Or if the App has been started, restart the Activity
mStackSupervisor.startSpecificActivityLocked(next, true.true);
}
return true;
}
Copy the code
ResumeTopActivityInnerLocked function is cumbersome, but overall, it should be only two of the more critical:
- Check whether an Activity(mResumedActivity) has started (that is, the Launcher that launches Taobao through the Launcher), and pause the Activity if it does
- Determine whether the target Activity needs to be restarted, that is, whether the Activity has already been started. (For example, save in the background, application switch)
2.AMS records the information about the Activity to start and sends a message to the Launcher to enter the pause state. 3.Launcher enters the pause state, and AMS is notified that Paused has been executed and Taobao can be started.
Create a process
The next steps are more important: create the process and start the Activity.
//ActivityStackSupervisor
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
if(app ! =null&& app.thread ! =null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
// Restart the Activity if the target Activity's App is already started (there is an ActivityThread)
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
` `` `` `}}// If the process does not exist, use zygote to create an application process.
mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
}Copy the code
From the above code can learn startSpecificActivityLocked to start the Activity of two kinds of different situations
- For example, to start Taobao cold from Launcher, you need to create a new process and incubate the application process through AMS calling Zygote.
- If taobao App has been started, such as switching from MainActivity to LoginActivity, it passes
realStartActivityLocked
Start.
Since we started with the Launcher taobao example, we went ahead and analyzed the AMS creation process and the Activity binding process. The above analysis to the mService startProcessLocked, here we direct way of starting a thread, in the middle of the process is a little complicated.
4. Taobao App has not been opened, so AMS starts a new process, creates ActivityThread object in the new process, and executes the main function method therein.
//ActivityServiceManager
//Process.javaZygote will spawn a child Process that will call ActivityThread's main function Process via reflection.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);Copy the code
After the Zygote process incubates a new application process, the main method of the ActivityThread class is executed by reflection. In this method, Looper and message queue are prepared, the attach method is called to bind the application process to AMS, and a loop is entered to continuously read messages from the message queue and distribute messages.
//ActivityThread
public static void main(String[] args) {
` `` `` `
// Prepare the main thread Looper, next post analysis Handler,Looper
Looper.prepareMainLooper();
// Create ActivityThread for the current process
ActivityThread thread = new ActivityThread();
// Bind the process to AMS
thread.attach(false);
if (sMainThreadHandler == null) {
// Save the main thread Handler corresponding to the process
sMainThreadHandler = thread.getHandler();
}
` `` `` `
// Enter the main thread message loop
Looper.loop();
` `` `` `
}
As mentioned above, ApplicationThread is the intermediary that ActivityThread uses to communicate with AMS
final ApplicationThread mAppThread = new ApplicationThread();
private void attach(boolean system) {
if(! system) {final IActivityManager mgr = ActivityManagerNative.getDefault();
// Invoke the attachApplication method of AMS to bind the ApplicationThread object to ActivityManagerService
// This allows AMS to control the application process through the ApplicationThread proxy object
mgr.attachApplication(mAppThread);
} else {
` `` `` `}}Copy the code
Now that the process has been created and the main thread is in place, all that is left to do is start the Activity and associate the context with initialization.
5. Notify AMS after the main thread of Taobao APP is started and pass in applicationThread for communication.
AMS Starts Activity summary
The following figure summarizes the general process of starting an Activity in AMS. Each method function is like different parts of a machine, each doing its duty and clearly dividing the work. It’s complicated, but not coupled. For example, the startup mode needs to be optimized by reworking the startActivityUnchecked method function.
Click to see a larger image
Associated with the Activity
At this point, even though you have the app process and the main thread, it’s still an empty shell. There is no activity information and no associated context. At this time, AMS should be asked to conduct the command.
The app at this time — >
Since the main thread sends the ApplicationThread to AMS via the attach method, the ApplicationThread bridge is used to inform the ActivityThread to create/associate and start the Activity.
//AMS
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
// Get the process ID of the applicationThread.
int callingPid = Binder.getCallingPid();
final longorigId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); }}Copy the code
Obtain the process ID of proxy (ApplicationThread) through Binder, that is, obtain the Pid of target (Taobao) process.
//Binder
/** * Return the ID of the process that sent you the current transaction * that is being processed. This pid can be used with higher-level * system services to determine its identity and check permissions. * If the current thread is not currently executing an incoming transaction, * then its own pid is returned. */
public static final native int getCallingPid(a);Copy the code
Next, we’ll focus on attachApplicationLocked methods
//AMS
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
if(pid ! = MY_PID && pid >=0) {
synchronized(mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); }}else {
app = null;
}
// Since the process is started by AMS, there must be ProcessRecord in AMS.
// If ProcessRecord is not available, you need to kill the process and exit
if (app == null) {
` `` `` `
return false;
}
// If this application record is still attached to a previous
// process, clean it up now.
if(app.thread ! =null) {
// If the IApplicationThread obtained from ProcessRecord is not empty, the IApplicationThread needs to be processed
// It is possible that this Pid is reused, the old application process has just been released, and the internal IApplicationThread has not been emptied.
// At the same time, the new process happens to use this Pid
handleAppDiedLocked(app, true.true);
}
// Create dead proxy (notify AMS after process kill)
AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
// The process was successfully registered. The timeout notification was removed
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
` `` `` `
try {
Binding Application / / * * * * * * * * * * * *thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || ! normalMode, app.persistent,new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false.null);
} catch (Exception e) {
` `` `` `
// Restart the process after bindApplication fails
startProcessLocked(app, "bind fail", processName);
return false;
}
try {
//****** Start the Activity(start taobao MainActivity)******
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;//didSomething indicates whether there are four components to start}}catch (Exception e) {
badApp = true;
}
` `` `` `
// Bind the Application of service and Broadcast
if (badApp) {
// If the above components fail to start, the process needs to be killed and the record removed
app.kill("error during init".true);
handleAppDiedLocked(app, false.true);
return false;
}
DidSomething is false if none of the above components are started
if(! didSomething) {// Adjust the oom_adj value of the process. Oom_adj is a kind of priority
// If the application process has no components running, it is the first to be "killed" by the system when it runs out of memory
updateOomAdjLocked();
}
return true;
}Copy the code
There are two of the more important method functions in attachApplicationLocked, which brings us to the end of this article.
- Thread. BindApplication (…). Bind Application to ActivityThread
- MStackSupervisor. AttachApplicationLocked (app) : start the Activity (prior to 7.0 mMainStack. RealStartActivityLocked ())
bindApplication
Activitythreads communicate with AMS through applicationThreads, so thread.bindApplication (…) Method, which should be communicated through ApplicationThread. In the inner class ApplicationThread of ActivityThread, we find the bindApplication method
//ActivityThread
// Inner class ApplicationThread
private class ApplicationThread extends ApplicationThreadNative {
public final void bindApplication(... A bunch of parameters...) { AppBindData data =new AppBindData();
// Set parameters for data...`````` sendMessage(H.BIND_APPLICATION, data); }}private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
Message msg = Message.obtain();
// Set parameters for MSG
``````
mH.sendMessage(msg);
}Copy the code
Sending messages is done through H’s Handler class, which is impressively named thief. PS: I suspect the engineers who wrote this class are running out of words…
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101; ` ` ` ` ` `public static final int RESUME_ACTIVITY = 107;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111; ` ` ` ` ` `public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break; ` ` ` ` ` `/ / binding application
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break; }}}Copy the code
As you can see, this class H acts as the intermediary between ActivityThread and ApplicationThread, that is, the pimp. ActivityThread communicates with AMS through ApplicationThread. ApplicationThread communicates with the ActivityThread via H to process Activity transactions.
If Hand ApplicationThread are in the ActivityThread class, why should ApplicationThread send messages through Handler? In my opinion, it is convenient for centralized management and printing logs. H is the big steward of this.
Without further details, ApplicationThread sends the BIND_APPLICATION flag to H, where handleBindApplication handles application binding transactions.
//ActivityThread
private void handleBindApplication(AppBindData data) {
` `` `` `
// Create a LoadedApk object based on the ApplicationInfo passed
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);/ / get LoadedApk
/** * For apps targetting Honeycomb or later, we don't allow network usage * on the main event loop / UI thread. This is what ultimately throws * {@link NetworkOnMainThreadException}. */
// Disable network operations on the main thread
if (data.appInfo.targetSdkVersion > = Build.VERSION_CODES.HONEYCOMB) {
StrictMode.enableDeathOnNetwork();
}
/** * For apps targetting N or later, we don't allow file:// Uri exposure. * This is what ultimately throws {@link FileUriExposedException}. */
/ / 7.0 introduced Fileprovide
if (data.appInfo.targetSdkVersion > = Build.VERSION_CODES.N) {
StrictMode.enableDeathOnFileUriExposure();
}
` `` `` `
// Create Android ContextImpl for the process
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
if ((InstrumentationInfo)ii ! = null) {
` `` `` `
} else {
// Note that all Activity lifecycle methods are monitored by Instrumentation objects,
// This means that the Activity lifecycle method must call the related method of the Instrumentation object before and after execution
mInstrumentation = new Instrumentation();
}
try {
` `` `` `
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// Load the process corresponding to the ContentProvider contained in the Package
installContentProviders(app, data.providers);
` `` `` `
mInstrumentation.onCreate(data.instrumentationArgs);
try {
// Application's onCreate method is called
// Therefore, the Applcation object's onCreate method is called after the ActivityThread's main method
// But will be called before all activities in the application
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
` `` `` `
}
} finally {
StrictMode.setThreadPolicy(savedPolicy); }}Copy the code
As mentioned above, the purpose of the Handlebinder Application is to integrate a Java process into the Android system. Therefore, the code in this function does the following:
- Set basic process parameters as required by Android, including process name, time zone, resources, and compatibility. Restrictions have also been added, such as the main thread not being able to access the network.
- Create ContextImpl, LoadedApk, Application, etc., load ContentProvider in Application, and initialize Application.
- use
Instrumentation
Monitor the Activity lifecycle. One process for eachInstrumentation
Instance)
When this is done, the new process is finally added to the Android system.
6.AMS notifies Taobao to bind Application and start MainActivity.
Start the Activity
attachApplicationLocked
In the attachApplicationLocked method of AMS in the code above, we say:
There are two of the more important method functions in attachApplicationLocked, which brings us to the end of this article.
- Thread. BindApplication (…). Bind Application to ActivityThread
- MStackSupervisor. AttachApplicationLocked (app) : start the Activity
After binding Application, we can start the Activity(Taobao MainActivity).
//ActivityStackSupervisor
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
/ / ActivityStackSupervisor maintains all ActivityStack in the terminal
// Find the Activity to start at the top of the foreground stack by polling
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if(! isFocusedStack(stack)) {continue;
}
ActivityRecord hr = stack.topRunningActivityLocked();
if(hr ! =null) {
// Start the foreground Activity when it matches the newly created process
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
/ / realStartActivityLocked actually start work
if (realStartActivityLocked(hr, app, true.true)) {
didSomething = true; }}catch (RemoteException e) {
}
}
}
}
}
return didSomething;
}Copy the code
The system engineers probably feel that the startup process is confusing, and the final method is called realStartActivityLocked, which means don’t get confused, this is the method that will finally start the Activity. Finally, we go straight to huanglong, and in the ActivityStackSupervisor method, we find the following code
//ActivityStackSupervisor
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo);Copy the code
AMS uses ApplicationThread to tell ActivityThread to start the Activity. The ApplicationThread informs the ActivityThread to start the Activity. The ApplicationThread informs the ActivityThread to start the Activity. ApplicationThread — > H — > ActivityThread — > the method that finally starts the Activity.
ActivityThread
//ActivityThread
// Inner class ApplicationThread
private class ApplicationThread extends ApplicationThreadNative {
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
// Set parameters` ` ` ` ` `// As you can see from the LAUNCH_ACTIVITY identifier, it is used to start the ActivitysendMessage(H.LAUNCH_ACTIVITY, r); }}private class H extends Handler {` ` ` ` ` `public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
// Use ApplicationInfo and other information to get the corresponding LoadedApk, save to ActivityClientRecord
//ActivityClientRecord contains activity-related information
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break; ` ` ` ` ` `}}}Copy the code
//ActivityThread
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
` `` `` `
Activity a = performLaunchActivity(r, customIntent);
if(a ! =null) {
` `` `` `
handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);` `` `` `
}
` `` `` `
}Copy the code
The handleLaunchActivity method has two important function calls,
- PerformLaunchActivity: invokes the Activity’s onCreate, onStart, onResotreInstanceState method
- HandleResumeActivity: Calls the Activity’s onResume method.
As you can see from the source code above, the performLaunchActivity method finally completes the Activity object creation and launch process, And the ActivityThread calls the onResume lifecycle method of the initiated Activity through the handleResumeActivity method.
performLaunchActivity
This method does several things. [From Android Development Art Exploration]
1. Get the component information for the Activity to be started from the ActivityClientRecord.
//ActivityThread performLaunchActivity method ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent(a);
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity! = null) { component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Copy the code
2, through the newActivity method of Instrumentation using the class loader to create an Activity object.
ActivityThread performLaunchActivity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader(a);
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess(a);
if (r.state! = null) { r.state.setClassLoader(cl);
}
} catch (Exception e) {
``````
}Copy the code
LoadedApk makeApplication creates the Application object.
// The performLaunchActivity method of ActivityThread
Application app = r.packageInfo.makeApplication(false, mInstrumentation);//r.packageInfo is the LoadedApk objectCopy the code
In bindApplication, we explained how to create an Application with LoadedApk, and after that, Call the onCreate method of Application through the Instrumentation callApplicationOnCreate
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
` `` `` `
mInstrumentation.callApplicationOnCreate(app);Copy the code
So the third step is to determine if the Application is empty, and we can see from the makeApplication method that if the Application has already been created, it won’t be created again.
4. Create ContextImpl and initialize some important data using the Attach method of the Activity.
Context appContext = createBaseContextForActivity(r, activity); // Create ContextImpl object
``````
Window window = null;
if (r.mPendingRemoveWindow! = null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);Copy the code
ContextImpl is an important data structure that is the implementation of the Context. Most of the logic in the Context is done by ContextImpl. ContextImpl. ContextImpl is a ContextImpl attached to the Activity using the attach method of the Activity. In addition, in the attach method, the Activity completes the creation of the Window and associates itself with the Window. This allows the Window to pass the event to the Activity when it receives an external input event.
5. Call the Activity’s onCreate method
mInstrumentation.callActivityOnCreate(activity, r.state);Copy the code
Since the Activity’s onCreate has already been called, this also means that the Activity has completed the entire startup process.
6, call the Activity onStart, onResotreInstanceState method
mInstrumentation.callActivityOnCreate(activity, r.state);
` `` `` `
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);Copy the code
conclusion
So far, the whole process of starting an Activity from the Launcher has been described. Through this analysis, we have a general control of the origin and origin of starting an Activity, but there are also several knowledge points that have not been explored clearly. (The more you explore, the more you find out.)
- Looper, How Handler works (Android messaging)
- What exactly is Context
- What about Windows’ internal mechanics
What if taobao MainActivity starts LoginActivity? Matter of fact, broadly in line with a principle and the process in ActivityStackSupervisor startSpecificActivityLocked method, found that the process has started, direct call realStartActivityLocked start-up Activity.
Refer to the “Android Development Art Exploration” Activity startup process series
Start the Activity (top) Start the Activity (Bottom)
General overview of Jianqiang Bao’s startup process (II) (III) (IV)
Diagram the Application startup process