Android應用程序的Activity啓動過程簡要介紹和學習計劃

在Android系統中,Activity和Service是應用程序的核心組件,它們以鬆藕合的方式組合在一塊兒構成了一個完整的應用程序,這得益於應用程序框架層提供了一套完整的機制來協助應用程序啓動這些Activity和Service,以及提供Binder機制幫助它們相互間進行通訊。在前面的文章Android進程間通訊(IPC)機制Binder簡要介紹和學習計劃Android系統在新進程中啓動自定義服務過程(startService)的原理分析中,咱們已經系統地介紹了Binder機制和Service的啓動過程了,在本文中,簡要介紹Activity的啓動過程以及後續學習計劃。 html

        在Android系統中,有兩種操做會引起Activity的啓動,一種用戶點擊應用程序圖標時,Launcher會爲咱們啓動應用程序的主Activity;應用程序的默認Activity啓動起來後,它又能夠在內部經過調用startActvity接口啓動新的Activity,依此類推,每個Activity均可以在內部啓動新的Activity。經過這種連鎖反應,按需啓動Activity,從而完成應用程序的功能。 java

        這裏,咱們經過一個具體的例子來講明如何啓動Android應用程序的Activity。Activity的啓動方式有兩種,一種是顯式的,一種是隱式的,隱式啓動可使得Activity之間的藕合性更加鬆散,所以,這裏只關注隱式啓動Activity的方法。 android

        首先在Android源代碼工程的packages/experimental目錄下建立一個應用程序工程目錄Activity。關於如何得到Android源代碼工程,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文;關於如何在Android源代碼工程中建立應用程序工程,請參考在Ubuntu上爲Android系統內置Java應用程序測試Application Frameworks層的硬件服務一文。這裏,工程名稱就是Activity了,它定義了一個路徑爲shy.luo.activity的package,這個例子的源代碼主要就是實如今這裏了。下面,將會逐一介紹這個package裏面的文件。 架構

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

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

import shy.luo.activity.R;

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.activity.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.activity.subactivity");
			startActivity(intent);
		}
	}
}
        它的實現很簡單,當點擊它上面的一個按鈕的時候,就會啓動另一個名字爲「shy.luo.activity.subactivity」的Actvity。

        名字爲「shy.luo.activity.subactivity」的Actvity實如今src/shy/luo/activity/SubActivity.java文件中: 框架

  1. package shy.luo.activity;  
  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.activity.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.activity;

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.activity.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中去。

        這裏咱們能夠看到,Android應用程序架構中很是核心的一點:MainActivity不須要知道SubActivity的存在,即它不直接擁有SubActivity的接口,可是它能夠經過一個字符串來告訴應用程序框架層,它要啓動的Activity的名稱是什麼,其它的事情就交給應用程序框架層來作,固然,應用程序框架層會根據這個字符串來找到其對應的Activity,而後把它啓動起來。這樣,就使得Android應用程序中的Activity藕合性很鬆散,從而使得Android應用程序的模塊性程度很高,而且有利於之後程序的維護和更新,對於大型的客戶端軟件來講,這一點是很是重要的。 ide

        固然,應用程序框架可以根據名字來找到相應的Activity,是須要應用程序自己來配合的,這就是要經過應用程序的配置文件AndroidManifest.xml來實現了: 學習

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="shy.luo.activity"  
  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.             <intent-filter>  
  10.                 <action android:name="android.intent.action.MAIN" />  
  11.                 <category android:name="android.intent.category.LAUNCHER" />  
  12.             </intent-filter>  
  13.         </activity>  
  14.         <activity android:name=".SubActivity"  
  15.                   android:label="@string/sub_activity">  
  16.             <intent-filter>  
  17.                 <action android:name="shy.luo.activity.subactivity"/>  
  18.                 <category android:name="android.intent.category.DEFAULT"/>  
  19.             </intent-filter>  
  20.         </activity>  
  21.     </application>  
  22. </manifest>  
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="shy.luo.activity"
	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">
			<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">
			<intent-filter>
				<action android:name="shy.luo.activity.subactivity"/>
				<category android:name="android.intent.category.DEFAULT"/>
			</intent-filter>
		</activity>
	</application>
</manifest>
        從這個配置文件中,咱們能夠看到,MainActivity被配置成了應用程序的默認Activity,即用戶在手機屏幕上點擊Activity應用程序圖標時,Launcher就會默認啓動MainActivity這個Activity:
  1. <activity android:name=".MainActivity"  
  2.       android:label="@string/app_name">  
  3.        <intent-filter>  
  4.         <action android:name="android.intent.action.MAIN" />  
  5.         <category android:name="android.intent.category.LAUNCHER" />  
  6.     </intent-filter>  
  7. </activity>  
<activity android:name=".MainActivity"
	  android:label="@string/app_name">
       <intent-filter>
		<action android:name="android.intent.action.MAIN" />
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
        這個配置文件也將名字「shy.luo.activity.subactivity」和SubActivity關聯了起來,所以,應用程序框架層可以根據名字來找到它:
  1. <activity android:name=".SubActivity"  
  2.       android:label="@string/sub_activity">  
  3.     <intent-filter>  
  4.         <action android:name="shy.luo.activity.subactivity"/>  
  5.         <category android:name="android.intent.category.DEFAULT"/>  
  6.     </intent-filter>  
  7. </activity>  
<activity android:name=".SubActivity"
	  android:label="@string/sub_activity">
	<intent-filter>
		<action android:name="shy.luo.activity.subactivity"/>
		<category android:name="android.intent.category.DEFAULT"/>
	</intent-filter>
</activity>
        下面再列出這個應用程序的界面配置文件和字符串文件。

        界面配置文件在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的界面: this

  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">Activity</string>  
  4.     <string name="sub_activity">Sub Activity</string>  
  5.     <string name="start">Start sub-activity</string>  
  6.     <string name="finish">Finish activity</string>  
  7. </resources>  
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Activity</string>
    <string name="sub_activity">Sub Activity</string>
    <string name="start">Start sub-activity</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 :Activity  
  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 := Activity

include $(BUILD_PACKAGE)
        這樣,整個例子的源代碼實現就介紹完了,接下來就要編譯了。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考 如何單獨編譯Android源代碼中的模塊一文。
        執行如下命令進行編譯和打包:
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Activity    
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod   
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Activity  
USER-NAME@MACHINE-NAME:~/Android$ make snod
       這樣,打包好的Android系統鏡像文件system.img就包含咱們前面建立的Activity應用程序了。
       再接下來,就是運行模擬器來運行咱們的例子了。關於如何在Android源代碼工程中運行模擬器,請參考 在Ubuntu上下載、編譯和安裝Android最新源代碼一文。
       執行如下命令啓動模擬器:
  1. USER-NAME@MACHINE-NAME:~/Android$ emulator    
USER-NAME@MACHINE-NAME:~/Android$ emulator
       模擬器啓動起,就能夠在屏幕上看到Activity應用程序圖標了:

         點擊Activity這個應用程序圖標後,Launcher就會把MainActivity啓動起來:

        點擊上面的Start sub-activity銨鈕,MainActivity內部就會經過startActivity接口來啓動SubActivity:

  1. Intent intent = new Intent("shy.luo.activity.subactivity");  
  2. startActivity(intent);  
Intent intent = new Intent("shy.luo.activity.subactivity");
startActivity(intent);
        以下圖所示:

        不管是經過點擊應用程序圖標來啓動Activity,仍是經過Activity內部調用startActivity接口來啓動新的Activity,都要藉助於應用程序框架層的ActivityManagerService服務進程。在前面一篇文章Android系統在新進程中啓動自定義服務過程(startService)的原理分析中,咱們已經看到,Service也是由ActivityManagerService進程來啓動的。在Android應用程序框架層中,ActivityManagerService是一個很是重要的接口,它不但負責啓動Activity和Service,還負責管理Activity和Service。

        Android應用程序框架層中的ActivityManagerService啓動Activity的過程大體以下圖所示:

         在這個圖中,ActivityManagerService和ActivityStack位於同一個進程中,而ApplicationThread和ActivityThread位於另外一個進程中。其中,ActivityManagerService是負責管理Activity的生命週期的,ActivityManagerService還藉助ActivityStack是來把全部的Activity按照後進先出的順序放在一個堆棧中;對於每個應用程序來講,都有一個ActivityThread來表示應用程序的主進程,而每個ActivityThread都包含有一個ApplicationThread實例,它是一個Binder對象,負責和其它進程進行通訊。

        下面簡要介紹一下啓動的過程:

        Step 1. 不管是經過Launcher來啓動Activity,仍是經過Activity內部調用startActivity接口來啓動新的Activity,都經過Binder進程間通訊進入到ActivityManagerService進程中,而且調用ActivityManagerService.startActivity接口; 

        Step 2. ActivityManagerService調用ActivityStack.startActivityMayWait來作準備要啓動的Activity的相關信息;

        Step 3. ActivityStack通知ApplicationThread要進行Activity啓動調度了,這裏的ApplicationThread表明的是調用ActivityManagerService.startActivity接口的進程,對於經過點擊應用程序圖標的情景來講,這個進程就是Launcher了,而對於經過在Activity內部調用startActivity的情景來講,這個進程就是這個Activity所在的進程了;

        Step 4. ApplicationThread不執行真正的啓動操做,它經過調用ActivityManagerService.activityPaused接口進入到ActivityManagerService進程中,看看是否須要建立新的進程來啓動Activity;

        Step 5. 對於經過點擊應用程序圖標來啓動Activity的情景來講,ActivityManagerService在這一步中,會調用startProcessLocked來建立一個新的進程,而對於經過在Activity內部調用startActivity來啓動新的Activity來講,這一步是不須要執行的,由於新的Activity就在原來的Activity所在的進程中進行啓動;

        Step 6. ActivityManagerServic調用ApplicationThread.scheduleLaunchActivity接口,通知相應的進程執行啓動Activity的操做;

        Step 7. ApplicationThread把這個啓動Activity的操做轉發給ActivityThread,ActivityThread經過ClassLoader導入相應的Activity類,而後把它啓動起來。

        這樣,Android應用程序的Activity啓動過程就簡要介紹到這裏了,在接下來的兩篇文章中,咱們將根據Activity的這兩種啓動情景,深刻到應用程序框架層的源代碼裏面去,一步一步地分析它們的啓動過程:

        1. Android應用程序啓動過程的源代碼分析;

        2. Android應用程序內部啓動Activity過程(startActivity)的源代碼分析。

相關文章
相關標籤/搜索