Android應用程序在新的進程中啓動新的Activity的方法和過程分析

Android應用程序在新的進程中啓動新的Activity的方法和過程分析 - 老羅的Android之旅 - 博客頻道 - CSDN.NEThtml

       前面咱們在分析Activity啓動過程的時候,看到同一個應用程序的Activity通常都是在同一個進程中啓動,事實上,Activity也能夠像Service同樣在新的進程中啓動,這樣,一個應用程序就能夠跨越好幾個進程了,本文就分析一下在新的進程中啓動Activity的方法和過程。java

        在前面Android進程間通訊(IPC)機制Binder簡要介紹和學習計劃一文中,咱們提到,在Android系統中,每個應用程序都是由一些Activity和Service組成的,通常Service運行在獨立的進程中,而Activity有可能運行在同一個進程中,也有可能運行在不一樣的進程中。在前面Android系統在新進程中啓動自定義服務過程(startService)的原理分析一文中,咱們已經介紹了使用Activity.startService接口來在新進程中啓動Service的過程,而後又在前面Android應用程序內部啓動Activity過程(startActivity)的源代碼分析一文中介紹了使用Activity.startActivity接口來在原來的進程中啓動Activity的過程,如今,咱們就來看一下同一個Android應用程序如何在新的進程中啓動新的Activity。android

        老規矩,咱們經過例子來介紹Android應用程序在新的進程中啓動新的Activity的方法以及分析其過程。首先在Android源代碼工程中建立一個Android應用程序工程,名字就稱爲Process吧。關於如何得到Android源代碼工程,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文;關於如何在Android源代碼工程中建立應用程序工程,請參考在Ubuntu上爲Android系統內置Java應用程序測試Application Frameworks層的硬件服務一文。這個應用程序工程定義了一個名爲shy.luo.process的package,這個例子的源代碼主要就是實如今這裏了。下面,將會逐一介紹這個package裏面的文件。shell

        應用程序的默認Activity定義在src/shy/luo/process/MainActivity.java文件中:架構

  1. package shy.luo.process;       
  2.       
  3. import android.app.Activity;      
  4. import android.content.Intent;      
  5. import android.os.Bundle;      
  6. import android.util.Log;      
  7. import android.view.View;      
  8. import android.view.View.OnClickListener;      
  9. import android.widget.Button;      
  10.       
  11. public class MainActivity extends Activity  implements OnClickListener {      
  12.     private final static String LOG_TAG = "shy.luo.process.MainActivity";      
  13.       
  14.     private Button startButton = null;      
  15.       
  16.     @Override      
  17.     public void onCreate(Bundle savedInstanceState) {      
  18.         super.onCreate(savedInstanceState);      
  19.         setContentView(R.layout.main);      
  20.       
  21.         startButton = (Button)findViewById(R.id.button_start);      
  22.         startButton.setOnClickListener(this);      
  23.       
  24.         Log.i(LOG_TAG, "Main Activity Created.");      
  25.     }      
  26.       
  27.     @Override      
  28.     public void onClick(View v) {      
  29.         if(v.equals(startButton)) {      
  30.             Intent intent = new Intent("shy.luo.process.subactivity");      
  31.             startActivity(intent);      
  32.         }      
  33.     }      
  34. }      
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);
        }
    }
}    

        和前面文章的例子同樣,它的實現很簡單,當點擊它上面的一個按鈕的時候,就會啓動另一個名字爲「shy.luo.process.subactivity」的Actvity。
        名字爲「shy.luo.process.subactivity」的Actvity實如今src/shy/luo/process/SubActivity.java文件中:app

  1. package shy.luo.process;      
  2.       
  3. import android.app.Activity;      
  4. import android.os.Bundle;      
  5. import android.util.Log;      
  6. import android.view.View;      
  7. import android.view.View.OnClickListener;      
  8. import android.widget.Button;      
  9.       
  10. public class SubActivity extends Activity implements OnClickListener {      
  11.     private final static String LOG_TAG = "shy.luo.process.SubActivity";      
  12.       
  13.     private Button finishButton = null;      
  14.       
  15.     @Override      
  16.     public void onCreate(Bundle savedInstanceState) {      
  17.         super.onCreate(savedInstanceState);      
  18.         setContentView(R.layout.sub);      
  19.       
  20.         finishButton = (Button)findViewById(R.id.button_finish);      
  21.         finishButton.setOnClickListener(this);      
  22.               
  23.         Log.i(LOG_TAG, "Sub Activity Created.");      
  24.     }      
  25.       
  26.     @Override      
  27.     public void onClick(View v) {      
  28.         if(v.equals(finishButton)) {      
  29.             finish();      
  30.         }      
  31.     }      
  32. }      
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();
        }
    }
}    

        它的實現也很簡單,當點擊上面的一個銨鈕的時候,就結束本身,回到前面一個Activity中去。
        再來重點看一下應用程序的配置文件AndroidManifest.xml:ide

  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"      
  3.     package="shy.luo.task"      
  4.     android:versionCode="1"      
  5.     android:versionName="1.0">      
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name">      
  7.         <activity android:name=".MainActivity"      
  8.                   android:label="@string/app_name">    
  9.                   android:process=":shy.luo.process.main"    
  10.             <intent-filter>      
  11.                 <action android:name="android.intent.action.MAIN" />      
  12.                 <category android:name="android.intent.category.LAUNCHER" />      
  13.             </intent-filter>      
  14.         </activity>      
  15.         <activity android:name=".SubActivity"      
  16.                   android:label="@string/sub_activity"    
  17.                   android:process=":shy.luo.process.sub">      
  18.             <intent-filter>      
  19.                 <action android:name="shy.luo.task.subactivity"/>      
  20.                 <category android:name="android.intent.category.DEFAULT"/>      
  21.             </intent-filter>      
  22.         </activity>      
  23.     </application>      
  24. </manifest>     
<?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>   

        爲了使MainActivity和SubActivity在不一樣的進程中啓動,咱們分別配置了這兩個Activity的android:process屬性。在官方文檔http://developer.android.com/guide/topics/manifest/activity-element.html中,是這樣介紹Activity的android:process屬性的:函數

        The name of the process in which the activity should 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 across 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, reducing resource usage.
        大意爲,通常狀況下,同一個應用程序的Activity組件都是運行在同一個進程中,可是,若是Activity配置了android:process這個屬性,那麼,它就會運行在本身的進程中。若是android:process屬性的值以":"開頭,則表示這個進程是私有的;若是android:process屬性的值以小寫字母開頭,則表示這是一個全局進程,容許其它應用程序組件也在這個進程中運行。工具

        所以,這裏咱們以":"開頭,表示建立的是私有的進程。事實上,這裏咱們不要前面的":"也是能夠的,可是必須保證這個屬性性字符串內至少有一個"."字符,具體能夠看一下解析AndroidManiefst.xml文件的源代碼,在frameworks/base/core/java/android/content/pm/PackageParser.java文件中:oop

  1. public class PackageParser {  
  2.   
  3.     ......  
  4.   
  5.     private boolean parseApplication(Package owner, Resources res,  
  6.             XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)  
  7.             throws XmlPullParserException, IOException {  
  8.         final ApplicationInfo ai = owner.applicationInfo;  
  9.         final String pkgName = owner.applicationInfo.packageName;  
  10.   
  11.         TypedArray sa = res.obtainAttributes(attrs,  
  12.             com.android.internal.R.styleable.AndroidManifestApplication);  
  13.   
  14.         ......  
  15.   
  16.         if (outError[0] == null) {  
  17.             CharSequence pname;  
  18.             if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {  
  19.                 pname = sa.getNonConfigurationString(  
  20.                     com.android.internal.R.styleable.AndroidManifestApplication_process, 0);  
  21.             } else {  
  22.                 // Some older apps have been seen to use a resource reference  
  23.                 // here that on older builds was ignored (with a warning).  We  
  24.                 // need to continue to do this for them so they don't break.  
  25.                 pname = sa.getNonResourceString(  
  26.                     com.android.internal.R.styleable.AndroidManifestApplication_process);  
  27.             }  
  28.             ai.processName = buildProcessName(ai.packageName, null, pname,  
  29.                 flags, mSeparateProcesses, outError);  
  30.   
  31.             ......  
  32.         }  
  33.   
  34.         ......  
  35.   
  36.     }  
  37.   
  38.     private static String buildProcessName(String pkg, String defProc,  
  39.             CharSequence procSeq, int flags, String[] separateProcesses,  
  40.             String[] outError) {  
  41.         if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {  
  42.             return defProc != null ? defProc : pkg;  
  43.         }  
  44.         if (separateProcesses != null) {  
  45.             for (int i=separateProcesses.length-1; i>=0; i--) {  
  46.                 String sp = separateProcesses[i];  
  47.                 if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {  
  48.                     return pkg;  
  49.                 }  
  50.             }  
  51.         }  
  52.         if (procSeq == null || procSeq.length() <= 0) {  
  53.             return defProc;  
  54.         }  
  55.         return buildCompoundName(pkg, procSeq, "process", outError);  
  56.     }  
  57.   
  58.     private static String buildCompoundName(String pkg,  
  59.             CharSequence procSeq, String type, String[] outError) {  
  60.         String proc = procSeq.toString();  
  61.         char c = proc.charAt(0);  
  62.         if (pkg != null && c == ':') {  
  63.             if (proc.length() < 2) {  
  64.                 outError[0] = "Bad " + type + " name " + proc + " in package " + pkg  
  65.                     + ": must be at least two characters";  
  66.                 return null;  
  67.             }  
  68.             String subName = proc.substring(1);  
  69.             String nameError = validateName(subName, false);  
  70.             if (nameError != null) {  
  71.                 outError[0] = "Invalid " + type + " name " + proc + " in package "  
  72.                     + pkg + ": " + nameError;  
  73.                 return null;  
  74.             }  
  75.             return (pkg + proc).intern();  
  76.         }  
  77.         String nameError = validateName(proc, true);  
  78.         if (nameError != null && !"system".equals(proc)) {  
  79.             outError[0] = "Invalid " + type + " name " + proc + " in package "  
  80.                 + pkg + ": " + nameError;  
  81.             return null;  
  82.         }  
  83.         return proc.intern();  
  84.     }  
  85.   
  86.   
  87.     private static String validateName(String name, boolean requiresSeparator) {  
  88.         final int N = name.length();  
  89.         boolean hasSep = false;  
  90.         boolean front = true;  
  91.         for (int i=0; i<N; i++) {  
  92.             final char c = name.charAt(i);  
  93.             if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {  
  94.                 front = false;  
  95.                 continue;  
  96.             }  
  97.             if (!front) {  
  98.                 if ((c >= '0' && c <= '9') || c == '_') {  
  99.                     continue;  
  100.                 }  
  101.             }  
  102.             if (c == '.') {  
  103.                 hasSep = true;  
  104.                 front = true;  
  105.                 continue;  
  106.             }  
  107.             return "bad character '" + c + "'";  
  108.         }  
  109.   
  110.         return hasSep || !requiresSeparator  
  111.             ? null : "must have at least one '.' separator";  
  112.     }  
  113.   
  114.     ......  
  115.   
  116. }  
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";
	}

	......

}

        從調用parseApplication函數解析application標籤開始,經過調用buildProcessName函數對android:process屬性進解析,接着又會調用buildCompoundName進一步解析,這裏傳進來的參數pkg就爲"shy.luo.process",參數procSeq爲MainActivity的屬性android:process的值":shy.luo.process.main",進一步將這個字符串保存在本地變量proc中。若是proc的第一個字符是":",則只須要調用validateName函數來驗證proc字符串裏面的字符都是合法組成就能夠了,即以大小寫字母或者"."開頭,後面能夠跟數字或者"_"字符;若是proc的第一個字符不是":",除了保證proc字符裏面的字符都是合法組成外,還要求至少有一個"."字符。

        MainActivity和SubActivity的android:process屬性配置就介紹到這裏了,其它更多的信息讀者能夠參考官方文檔http://developer.android.com/guide/topics/manifest/activity-element.html或者源代碼文件frameworks/base/core/java/android/content/pm/PackageParser.java。

        再來看界面配置文件,它們定義在res/layout目錄中,main.xml文件對應MainActivity的界面:  

 

  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      
  3.     android:orientation="vertical"      
  4.     android:layout_width="fill_parent"      
  5.     android:layout_height="fill_parent"       
  6.     android:gravity="center">      
  7.         <Button       
  8.             android:id="@+id/button_start"      
  9.             android:layout_width="wrap_content"      
  10.             android:layout_height="wrap_content"      
  11.             android:gravity="center"      
  12.             android:text="@string/start" >      
  13.         </Button>      
  14. </LinearLayout>      
<?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>    

        而sub.xml對應SubActivity的界面:

  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      
  3.     android:orientation="vertical"      
  4.     android:layout_width="fill_parent"      
  5.     android:layout_height="fill_parent"       
  6.     android:gravity="center">      
  7.         <Button       
  8.             android:id="@+id/button_finish"      
  9.             android:layout_width="wrap_content"      
  10.             android:layout_height="wrap_content"      
  11.             android:gravity="center"      
  12.             android:text="@string/finish" >      
  13.         </Button>      
  14. </LinearLayout>    
<?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>  

        字符串文件位於res/values/strings.xml文件中:

  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <resources>      
  3.     <string name="app_name">Process</string>      
  4.     <string name="sub_activity">Sub Activity</string>      
  5.     <string name="start">Start activity in new process</string>      
  6.     <string name="finish">Finish activity</string>      
  7. </resources>     
<?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>   

        最後,咱們還要在工程目錄下放置一個編譯腳本文件Android.mk:

  1. LOCAL_PATH:= $(call my-dir)      
  2. include $(CLEAR_VARS)      
  3.       
  4. LOCAL_MODULE_TAGS :optional      
  5.       
  6. LOCAL_SRC_FILES := $(call all-subdir-java-files)      
  7.       
  8. LOCAL_PACKAGE_NAME :Process      
  9.       
  10. include $(BUILD_PACKAGE)  
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)

       接下來就要編譯了。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。
       執行如下命令進行編譯和打包:

  1. USER-NAME@MACHINE-NAME:~/Android$Snbsp;mmm packages/experimental/Process        
  2. USER-NAME@MACHINE-NAME:~/Android$Snbsp;make snod     
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Process
USER-NAME@MACHINE-NAME:~/Android$ make snod   

       這樣,打包好的Android系統鏡像文件system.img就包含咱們前面建立的Process應用程序了。
       再接下來,就是運行模擬器來運行咱們的例子了。關於如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。
       執行如下命令啓動模擬器:

  1. USER-NAME@MACHINE-NAME:~/Android$Snbsp;emulator    
USER-NAME@MACHINE-NAME:~/Android$ emulator  

       模擬器啓動起,就能夠App Launcher中找到Process應用程序圖標,接着把它啓動起來:

      點擊中間的按鈕,就會在新的進程中啓動SubActivity:

        如今,咱們如何來確認SubActivity是否是在新的進程中啓動呢?Android源代碼工程爲咱們準備了adb工具,能夠查看模擬器上系統運行的情況,執行下面的命令查看:

  1. USER-NAME@MACHINE-NAME:~/Android$Snbsp;adb shell dumpsys activity  
USER-NAME@MACHINE-NAME:~/Android$ adb shell dumpsys activity

       這個命令輸出的內容比較多,這裏咱們只關心繫統中的任務和進程部分:

  1. ......  
  2.   
  3. Running activities (most recent first):  
  4.     TaskRecord{40770440 #3 A shy.luo.process}  
  5.       Run #2: HistoryRecord{406d4b20 shy.luo.process/.SubActivity}  
  6.       Run #1: HistoryRecord{40662bd8 shy.luo.process/.MainActivity}  
  7.     TaskRecord{40679eb8 #2 A com.android.launcher}  
  8.       Run #0: HistoryRecord{40677570 com.android.launcher/com.android.launcher2.Launcher}  
  9.   
  10. ......  
  11.   
  12. PID mappings:  
  13.     ......  
  14.   
  15.     PID #416: ProcessRecord{4064b720 416:shy.luo.process:shy.luo.process.main/10037}  
  16.     PID #425: ProcessRecord{406ddc30 425:shy.luo.process:shy.luo.process.sub/10037}  
  17.   
  18. ......  
......

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}

......

        這裏咱們看到,雖然MainActivity和SubActivity都是在同一個應用程序而且運行在同一個任務中,然而,它們倒是運行在兩個不一樣的進程中,這就能夠看到Android系統中任務這個概念的強大之處了,它使得咱們在開發應用程序的時候,能夠把相對獨立的模塊放在獨立的進程中運行,以下降模塊之間的耦合性,同時,咱們又沒必要去考慮一個應用程序在兩個進程中運行的細節的問題,Android系統中的任務會爲咱們打點好一切。

        在啓動Activity的時候,系統是如何作到在新的進程中來啓動這個Activity的呢?在前面兩篇文章Android應用程序啓動過程源代碼分析Android應用程序內部啓動Activity過程(startActivity)的源代碼分析中,分別在Step 22和Step 21中分析了Activity在啓動過程當中與進程相關的函數ActivityStack.startSpecificActivityLocked函數中,它定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

  1. public class ActivityStack {    
  2.     
  3.     ......    
  4.     
  5.     private final void startSpecificActivityLocked(ActivityRecord r,    
  6.             boolean andResume, boolean checkConfig) {    
  7.         // Is this activity's application already running?    
  8.         ProcessRecord app = mService.getProcessRecordLocked(r.processName,    
  9.             r.info.applicationInfo.uid);    
  10.     
  11.         ......    
  12.     
  13.         if (app != null && app.thread != null) {    
  14.             try {    
  15.                 realStartActivityLocked(r, app, andResume, checkConfig);    
  16.                 return;    
  17.             } catch (RemoteException e) {    
  18.                 ......    
  19.             }    
  20.         }    
  21.     
  22.         mService.startProcessLocked(r.processName, r.info.applicationInfo, true0,    
  23.             "activity", r.intent.getComponent(), false);    
  24.     }    
  25.     
  26.     
  27.     ......    
  28.     
  29. }    
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);
    }  

    ......  

}  

        從這個函數能夠看出,決定一個Activity是在新的進程中啓動仍是在原有的進程中啓動的因素有兩個,一個是看這個Activity的process屬性的值,另外一個是這個Activity所在的應用程序的uid。應用程序的UID是由系統分配的,而Activity的process屬性值,如前所述,是能夠在AndroidManifest.xml文件中進行配置的,若是沒有配置,它默認就爲application標籤的process屬性值,若是application標籤的process屬性值也沒有配置,那麼,它們就默認爲應用程序的package名。這裏就是根據processName和uid在系統查找是否已有相應的進程存在,若是已經有了,就會調用realStartActivityLocked來直接啓動Activity,不然的話,就要經過調用ActivityManagerService.startProcessLocked函數來建立一個新的進程,而後在新進程中啓動這個Activity了。對於前者,能夠參考Android應用程序內部啓動Activity過程(startActivity)的源代碼分析一文,然後者,能夠參考Android應用程序啓動過程源代碼分析一文。

        至此,Android應用程序在新的進程中啓動新的Activity的方法和過程分析就結束了。在實際開發中,一個應用程序通常不多會在一個新的進程中啓動另一個Activity,若是真的須要這樣作,還要考慮如何與應用程序中其它進程的Activity進行通訊,這時候不妨考慮使用Binder進程間通訊機制。寫這篇文章的目的,更可能是讓咱們去了解Android應用程序的架構,這種架構可使得應用程序組件以鬆耦合的方式組合在一塊兒,便於後續的擴展和維護,這是很是值得咱們學習的。

相關文章
相關標籤/搜索