Analysis of how Android applications start new activities in new processes

Source: Internet
Author: User

When analyzing the activity startup process, we can see that the activity of the same application is generally started in the same process. In fact, activity can also be started in a new process like a service, so that an application can span several processes, this article analyzes the methods and processes for starting an activity in a new process.

In the previous article about binder of the android inter-process communication (IPC) mechanism and learning plan, we mentioned that in the Android system, every application is composed of activities and services, generally, the service runs in an independent process, and the activity may run in the same process or in different processes. In the previous article about how the Android system starts the Custom Service Process (startservice) in the new process, we have introduced the use of activity. startservice interface to start the service in the new process, and then introduces the use of activity in the source code analysis of the previously started activity process (startactivity) in the Android Application. startactivity interface to start the activity in the original process. Now, let's take a look at how the same Android Application Starts the activity in the new process.

Old Rules: We use examples to introduce how an android application starts a new activity in a new process and analyze the process. First, create an Android Application project in the android source code project. The project name is process. For more information about how to obtain the android source code project, see download, compile, and install the latest Android source code on Ubuntu. For more information about how to create an application project in the android source code project, for details, refer to the article on Testing hardware services at the application frameworks layer for Android built-in Java applications on Ubuntu. This application project defines a package named shy. Luo. process. The source code of this example is mainly implemented here. Next, we will introduce the files in the package one by one.

The default activity of the application is defined in the src/shy/LUO/process/mainactivity. Java file:

package shy.luo.process;         import android.app.Activity;    import android.content.Intent;    import android.os.Bundle;    import android.util.Log;    import android.view.View;    import android.view.View.OnClickListener;    import android.widget.Button;        public class MainActivity extends Activity  implements OnClickListener {        private final static String LOG_TAG = "shy.luo.process.MainActivity";            private Button startButton = null;            @Override        public void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.main);                startButton = (Button)findViewById(R.id.button_start);            startButton.setOnClickListener(this);                Log.i(LOG_TAG, "Main Activity Created.");        }            @Override        public void onClick(View v) {            if(v.equals(startButton)) {                Intent intent = new Intent("shy.luo.process.subactivity");                startActivity(intent);            }        }    }    

Like the example in the previous article, its implementation is very simple. When you click a button above it, another name named "Shy. luo. process. subactivity.
The actvity named "Shy. Luo. process. subactivity" is implemented in the src/shy/LUO/process/subactivity. Java file:

package shy.luo.process;        import android.app.Activity;    import android.os.Bundle;    import android.util.Log;    import android.view.View;    import android.view.View.OnClickListener;    import android.widget.Button;        public class SubActivity extends Activity implements OnClickListener {        private final static String LOG_TAG = "shy.luo.process.SubActivity";            private Button finishButton = null;            @Override        public void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.sub);                finishButton = (Button)findViewById(R.id.button_finish);            finishButton.setOnClickListener(this);                        Log.i(LOG_TAG, "Sub Activity Created.");        }            @Override        public void onClick(View v) {            if(v.equals(finishButton)) {                finish();            }        }    }    

Its implementation is also very simple. When you click the above button, you end yourself and return to the previous activity.
Next, let's take a look at the configuration file androidmanifest. xml of the application:

<?xml version="1.0" encoding="utf-8"?>    <manifest xmlns:android="http://schemas.android.com/apk/res/android"        package="shy.luo.task"        android:versionCode="1"        android:versionName="1.0">        <application android:icon="@drawable/icon" android:label="@string/app_name">            <activity android:name=".MainActivity"                      android:label="@string/app_name">                    android:process=":shy.luo.process.main"              <intent-filter>                    <action android:name="android.intent.action.MAIN" />                    <category android:name="android.intent.category.LAUNCHER" />                </intent-filter>            </activity>            <activity android:name=".SubActivity"                      android:label="@string/sub_activity"                    android:process=":shy.luo.process.sub">                <intent-filter>                    <action android:name="shy.luo.task.subactivity"/>                    <category android:name="android.intent.category.DEFAULT"/>                </intent-filter>            </activity>        </application>    </manifest>   

To enable mainactivity and subactivity to start in different processes, we have configured the Android: process attribute for these two activities respectively. In the official documentation, refer:

The name of the process in which the activity shoshould run. normally, all components of an application run in the default process created for the application. it has the same name as the application package. the <Application> element's process attribute can set a different default for all components. but each component can override the default, allowing you to spread your application processing SS multiple processes.
If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the activity runs in that process. if the process name begins with a lowercase character, the activity will run in a global process of that name, provided that it has permission to do so. this allows components in different applications to share a process, cing resource usage.
Generally, the activity components of the same application run in the same process. However, if the Android: process attribute is configured for the activity, it runs in its own process. If the value of the Android: process attribute starts with ":", it indicates that the process is private. If the value of the Android: process attribute starts with a lowercase letter, it indicates that the process is a global process, allow other application components to run in this process.

Therefore, we start with ":" to create a private process. In fact, we do not need the preceding ":" This is also acceptable, but we must ensure that at least one attribute string exists ". "Character. For details, refer to parsing androidmaniefst. source code of the XML file, in frameworks/base/CORE/Java/Android/content/PM/packageparser. in the Java file:

public class PackageParser {......private boolean parseApplication(Package owner, Resources res,XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)throws XmlPullParserException, IOException {final ApplicationInfo ai = owner.applicationInfo;final String pkgName = owner.applicationInfo.packageName;TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifestApplication);......if (outError[0] == null) {CharSequence pname;if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {pname = sa.getNonConfigurationString(com.android.internal.R.styleable.AndroidManifestApplication_process, 0);} else {// Some older apps have been seen to use a resource reference// here that on older builds was ignored (with a warning).  We// need to continue to do this for them so they don't break.pname = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestApplication_process);}ai.processName = buildProcessName(ai.packageName, null, pname,flags, mSeparateProcesses, outError);......}......}private static String buildProcessName(String pkg, String defProc,CharSequence procSeq, int flags, String[] separateProcesses,String[] outError) {if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {return defProc != null ? defProc : pkg;}if (separateProcesses != null) {for (int i=separateProcesses.length-1; i>=0; i--) {String sp = separateProcesses[i];if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {return pkg;}}}if (procSeq == null || procSeq.length() <= 0) {return defProc;}return buildCompoundName(pkg, procSeq, "process", outError);}private static String buildCompoundName(String pkg,CharSequence procSeq, String type, String[] outError) {String proc = procSeq.toString();char c = proc.charAt(0);if (pkg != null && c == ':') {if (proc.length() < 2) {outError[0] = "Bad " + type + " name " + proc + " in package " + pkg+ ": must be at least two characters";return null;}String subName = proc.substring(1);String nameError = validateName(subName, false);if (nameError != null) {outError[0] = "Invalid " + type + " name " + proc + " in package "+ pkg + ": " + nameError;return null;}return (pkg + proc).intern();}String nameError = validateName(proc, true);if (nameError != null && !"system".equals(proc)) {outError[0] = "Invalid " + type + " name " + proc + " in package "+ pkg + ": " + nameError;return null;}return proc.intern();}private static String validateName(String name, boolean requiresSeparator) {final int N = name.length();boolean hasSep = false;boolean front = true;for (int i=0; i<N; i++) {final char c = name.charAt(i);if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {front = false;continue;}if (!front) {if ((c >= '0' && c <= '9') || c == '_') {continue;}}if (c == '.') {hasSep = true;front = true;continue;}return "bad character '" + c + "'";}return hasSep || !requiresSeparator? null : "must have at least one '.' separator";}......}

Starting from calling the parseapplication function to parse the application tag, call the buildprocessname function to parse the Android: process attribute, and then call buildcompoundname for further parsing. The PKG parameter passed in here is "Shy. luo. process ", the procseq parameter is the value of the mainactivity attribute Android: Process": shy. luo. process. main ", further save this string in the local variable Proc. If the first character of Proc is ":", you only need to call the validatename function to verify that the characters in the proc string are valid, that is, uppercase and lowercase letters or ". "starts with", followed by a number or "_". If the first character of Proc is not ":", except that the characters in Proc are valid, at least one ". "character.

Android: process attribute configuration of mainactivity and subactivity is introduced here. For more information, refer to the official documentation for details.

Let's take a look at the interface configuration files, which are defined in the Res/layout directory. The main. xml file corresponds to the mainactivity interface:

<?xml version="1.0" encoding="utf-8"?>    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"        android:orientation="vertical"        android:layout_width="fill_parent"        android:layout_height="fill_parent"         android:gravity="center">            <Button                 android:id="@+id/button_start"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:gravity="center"                android:text="@string/start" >            </Button>    </LinearLayout>    

The sub. xml corresponds to the subactivity interface:

<?xml version="1.0" encoding="utf-8"?>    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"        android:orientation="vertical"        android:layout_width="fill_parent"        android:layout_height="fill_parent"         android:gravity="center">            <Button                 android:id="@+id/button_finish"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:gravity="center"                android:text="@string/finish" >            </Button>    </LinearLayout>  

The string file is located in the Res/values/strings. xml file:

<?xml version="1.0" encoding="utf-8"?>    <resources>        <string name="app_name">Process</string>        <string name="sub_activity">Sub Activity</string>        <string name="start">Start activity in new process</string>        <string name="finish">Finish activity</string>    </resources>   

Finally, we need to place a compilation script file Android. mk in the project directory:

LOCAL_PATH:= $(call my-dir)    include $(CLEAR_VARS)        LOCAL_MODULE_TAGS := optional        LOCAL_SRC_FILES := $(call all-subdir-java-files)        LOCAL_PACKAGE_NAME := Process        include $(BUILD_PACKAGE)

Next we will compile it. For details about how to separately compile the android source code project module and how to package system. IMG, refer to the article on how to separately compile the android source code module.
Run the following command to compile and package:

USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Process      USER-NAME@MACHINE-NAME:~/Android$ make snod   

In this way, the packaged Android system image file system. IMG contains the process application we created earlier.
Next, we will run the simulator to run our example. For details about how to run the simulator in the android source code project, refer to download, compile, and install the latest Android source code on Ubuntu.
Run the following command to start the simulator:

USER-NAME@MACHINE-NAME:~/Android$ emulator  

When the simulator starts, you can find the process application icon in the app launcher and start it up:

Click the button in the middle to start subactivity in the new process:

How can we determine whether subactivity is started in a new process? The android source code project has prepared an ADB tool for us to view the running status of the system on the simulator, and execute the following command to view it:

USER-NAME@MACHINE-NAME:~/Android$ adb shell dumpsys activity

This command outputs a lot of content. Here we only care about the tasks and processes in the system:

......Running activities (most recent first):    TaskRecord{40770440 #3 A shy.luo.process}      Run #2: HistoryRecord{406d4b20 shy.luo.process/.SubActivity}      Run #1: HistoryRecord{40662bd8 shy.luo.process/.MainActivity}    TaskRecord{40679eb8 #2 A com.android.launcher}      Run #0: HistoryRecord{40677570 com.android.launcher/com.android.launcher2.Launcher}......PID mappings:    ......    PID #416: ProcessRecord{4064b720 416:shy.luo.process:shy.luo.process.main/10037}    PID #425: ProcessRecord{406ddc30 425:shy.luo.process:shy.luo.process.sub/10037}......

Here we can see that although both mainactivity and subactivity are in the same application and run in the same task, they are running in two different processes, this shows the strength of the task concept in the Android system. It enables us to run relatively independent modules in an independent process when developing applications, in order to reduce the coupling between modules, at the same time, we do not have to consider the details of an application running in two processes, the tasks in the Android system will do everything for us.

When starting an activity, how does the system start the activity in a new process? In the previous two articles, the source code analysis of the android application startup process and the source code analysis of the android application startup activity process (startactivity, in step 22 and step 21, the activitystack function related to the process during activity startup is analyzed. startspecificactivitylocked is defined in frameworks/base/services/Java/COM/Android/Server/AM/activitystack. in the Java file:

public class ActivityStack {        ......        private final 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);            ......            if (app != null && app.thread != null) {              try {                  realStartActivityLocked(r, app, andResume, checkConfig);                  return;              } catch (RemoteException e) {                  ......              }          }            mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,              "activity", r.intent.getComponent(), false);      }          ......    }  

From this function, we can see that there are two factors that determine whether an activity is started in a new process or in the original process. One is to check the value of the activity's process attribute, the other is the UID of the application where the activity is located. The UID of the application is allocated by the system, and the process attribute value of the activity, as described above, can be found in androidmanifest. if no process attribute value is configured in the XML file, it is the process attribute value of the application tag by default. If the process attribute value of the application tag is not configured, they are the package name of the application by default. Here, you can check whether a corresponding process exists in the system based on processname and uid. If yes, you can call realstartactivitylocked to directly start the activity. Otherwise, you must call activitymanagerservice. the startprocesslocked function creates a new process and starts the activity in the new process. For the former, you can refer to the source code analysis article of the startactivity process in the Android Application. For the latter, you can refer to the source code analysis article of the android application startup process.

So far, the method and process analysis for the Android Application to start a new activity in a new process is over. In actual development, an application rarely starts another activity in a new process, we also need to consider how to communicate with the activity of other processes in the application. In this case, we may consider using the inter-process communication mechanism of the binder. The purpose of this article is to learn more about the architecture of Android applications. This architecture allows application components to be loosely coupled, it is worth learning to facilitate subsequent expansion and maintenance.

Lao Luo's Sina Weibo: http://weibo.com/shengyangluo. welcome to the attention!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.