第一章 四大組件 之 Activity(一)

文章目錄

第一組件 Activity

Activity是Android組件中最基本也是最爲常見用的四大組件(Activity,Service服務,Content Provider內容提供者,BroadcastReceiver廣播接收器)之一 。
Activity是一個應用程序組件,提供一個屏幕,用戶可以用來交互爲了完成某項任務。
Activity中所有操作都與用戶密切相關,是一個負責與用戶交互的組件,可以通過setContentView(View)來顯示指定控件。
在一個android應用中,一個Activity通常就是一個單獨的屏幕,它上面可以顯示一些控件也可以監聽並處理用戶的事件做出響應。Activity之間通過Intent進行通信。

(一)Activity生命週期

1、生命週期五大狀態

(1)啓動狀態:Activity的啓動狀態很短暫,當Activity啓動後便會進入運行狀態。
(2)運行狀態:Activity在此狀態時處於屏幕最前端,它是可見、有焦點的,可以與用戶進行交互。如單擊、長按等事件。即使出現內存不足的情況,Android也會先銷燬棧底的Activity,來確保當前的Activity正常運行。
(3)暫停狀態:在某些情況下,Activity對用戶來說仍然可見,但它無法獲取焦點,用戶對它操作沒有沒有響應,此時它處於暫停狀態。
(4)停止狀態:當Activity完全不可見時,它處於停止狀態,但仍然保留着當前的狀態和成員信息。如系統內存不足,那麼這種狀態下的Activity很容易被銷燬。
(5)銷燬狀態:當Activity處於銷燬狀態時,將被清理出內存。

2、生命週期流程

在這裏插入圖片描述
啓動Activity方法的生命週期調度:

  • 比方說我們點擊跳轉一個新Activity,這個時候Activity會入棧,同時它的生命週期也會從onCreate()到onResume()開始變換,這個過程是在ActivityStack裏完成的,ActivityStack
    是運行在Server進程裏的,這個時候Server進程就通過ApplicationThread的代理對象ApplicationThreadProxy向運行在app進程ApplicationThread發起操作請求。
  • ApplicationThread接收到操作請求後,因爲它是運行在app進程裏的其他線程裏,所以ApplicationThread需要通過Handler向主線程ActivityThread發送操作消息。
  • 主線程接收到ApplicationThread發出的消息後,調用主線程ActivityThread執行響應的操作,並回調Activity相應的週期方法。

3、常見場景的生命週期調用方式

在這裏插入圖片描述

4、具體場景生命週期調用方式

(1)橫豎屏切換對Activiy生命週期影響

橫豎屏切換涉及到的是Activity的android:configChanges屬性:
android:configChanges可以設置的屬性值有:
orientation:消除橫豎屏的影響
keyboardHidden:消除鍵盤的影響
screenSize:消除屏幕大小的影響
【情況1】
1、android:configChanges=orientation
2、android:configChanges=orientation|keyboardHidden
3、不設置android:configChanges
橫豎屏切換Activity生命週期變化:
onPause–>onSaveInstanceState–>onStop–>onDestroy–>onCreate–>onStart–>onRestoreInstanceState–>onResume
在進行橫豎屏切換的時候在調用onStop之前會調用onSaveInstanceState來進行Activity的狀態保存,隨後在重新顯示該Activity的onResume方法之前會調用onRestoreInstanceState來恢復之前由onSaveInstanceState保存的Activity信息
【情況2】
1、android:configChanges=orientation|screenSize
2、android:configChanges=orientation|screenSize|keyboardHidden
橫豎屏切換不會重新加載Activity的各個生命週期
【情況3】
屏蔽橫豎屏切換操作,不會出現切換的過程中Activity生命週期重新加載的情況
方法1:清單文件
android:screenOrientation=“portrait” 始終以豎屏顯示
android:screenOrientation=“landscape” 始終以橫屏顯示
方法2:動態Activity
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);以豎屏顯示
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);以橫屏顯示

(2)啓動Activity A

onCreate–>onStart -->onResume

(3)按back返/在Activity A調用finish()方法

onPause -->onStop–>onDestroy

(4)按home/打開進入另一個Activity B

onPause -->onStop

(5)再次進入Activity A

onReStart–>onStart -->onResume

(6)dialog對話框

(6.1)Android組件Dialog

Activity仍位於前臺,不影響Activity生命週期

(6.2)Theme爲Dialog的Activity

Activity A啓動對話框onPause
Activity A關閉對話框onResume

面試題:兩個Activity之間跳轉時必然會執行的是哪幾個方法?
答:當在A Activity裏面激活B Activity的時候, A會調用onPause()方法,然後B調用onCreate() ,onStart(), onResume()。
這個時候B覆蓋了A的窗體, A會調用onStop()方法。
如果B是個透明的窗口,或者是對話框的樣式, 就不會調用A的onStop()方法。
如果B已經存在於Activity棧中,B就不會調用onCreate()方法。
故一定會執行A的onPause()和B的onStart()與onResume()。

(二)Activity之間傳遞數據(6種方式)

(1)通過Intent傳遞數據(簡單數據類型或可序列化對象)

1.1)傳統Intent

1.1.1)傳遞基本數據類型

發送數據

   Intent intent =new Intent(EX06.this,OtherActivity.class); 
   intent.putExtra("boolean_key", true);
   intent.putExtra("string_key", "string_value");
   startActivity(intent);

獲取數據

   Intent intent=getIntent();
   intent.getBooleanExtra("boolean_key",false);
   intent.getStringExtra("string_key");
1.1.2)傳遞多種數據類型(Bundle)

發送數據

	Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
	Bundle bundle =new Bundle();
	bundle.putBoolean("boolean_key", true);
	bundle.putString("string_key", "string_value");
	intent.putExtra("key", bundle);// 封裝對象
	startActivity(intent);// 啓動新的 Activity

獲取數據

Intent intent =getIntent();
Bundle bundle =intent.getBundleExtra("key");
bundle.getBoolean("boolean_key");
bundle.getString("string_key");
1.1.3)傳遞對象
Serializable

對象序列化

public class Person implements Serializable {

    private String name;
    private int age;

    public Person() {}

    public void setName(String name){
        this.name = name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }

}

發送數據

				Person person = new Person();
                person.setName("chenjy");
                person.setAge(18);

                Bundle bundle = new Bundle();
                bundle.putSerializable("person",person);

                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                intent.putExtras(bundle);
                startActivity(intent);

獲取數據

Person person = (Person)getIntent().getSerializableExtra("person");

這種序列化是通過反射機制從而削弱了性能,這種機制也創建了大量的臨時對象從而引起GC頻繁回收調用資源。

Parcelable

Parcelable是由Android提供的序列化接口,google做了大量的優化
對象序列化

public class Person implements Parcelable {

    private String name;
    private int age;

    public Person() {}

    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public void setName(String name){
        this.name = name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }


    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            Person person = new Person();
            person.name = in.readString();
            person.age = in.readInt();
            return person;
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
     dest.writeString(name);
     dest.writeInt(age);
    }
}

1.2)startActivityForResult + setResult方式

startActivityForResult

public class Activity1 extends Activity
{
    private TextView tetxtview;
    private Button btn;
    private final int REQUESTCODE=1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity1);
        tetxtview=(TextView) findViewById(R.id.tetxtview);
        btn=(Button) findViewById(R.id.btn);
        btn.setText("跳轉到Activity2");
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Activity1.this, Activity2.class);
                intent.putExtra("data", "123");
                startActivityForResult(intent, REQUESTCODE);
            }
        });
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if(resultCode==RESULT_OK)
        {
            if(requestCode==REQUESTCODE)
            {
                tetxtview.setText("收到信息:【"+intent.getStringExtra("data")+"】");
            }
        }
    }
 
}

setResult

public class Activity2 extends Activity
{
    private TextView tetxtview;
    private Button btn;
    private String data;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity1);
        data=getIntent().getStringExtra("data");
        tetxtview=(TextView) findViewById(R.id.tetxtview);
        tetxtview.setText("收到信息:【"+data+"】");
        btn=(Button) findViewById(R.id.btn);
        btn.setText("回到到Activity1");
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=getIntent();
                intent.putExtra("data","456");
                setResult(RESULT_OK, intent);
                finish();
            }
        });
    }
}

(2)通過靜態變量傳遞數據(不建議)

設置靜態變量

public class OtherActivity extends Activity {
    public static String name;
    public static String age;
    public static String address;  

直接訪問靜態變量

Intent intent = new Intent(MainActivity.this,OtherActivity.class);
//直接訪問OtherActivity的三個靜態變量的值  
OtherActivity.name = "wulianghuan";
OtherActivity.age = "22";
OtherActivity.address = "上海閔行";
startActivity(intent); 

(3)通過全局對象傳遞數據(Application)

如果想使某些數據長時間駐留內存,以便程序隨時調用,建議採用全局對象。Application全局類不需要定義靜態變量只要定義普通成員變量即可,但全局類中必須有一個無參構造方法,編寫完Application類後,還需要在標籤中制定全局類名,否則系統不會自動創建全局對象。
MainApplication

public class MainApplication extends Application {
    private String username; //Application設置應用的全局變量
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

清單文件

<application
    android:name=".MainApplication">

設置&獲取全局對象

public class MainActivity extends Activity {
    application =(MainApplication)getApplication();
       application.setUsername("sunzn");
}
public class OtherActivity extends Activity {
    application = (MainApplication) getApplication();
    username = application.getUsername();
}

(4)EventBus

但是當傳輸的數據量較大的時候Parcelable雖然很便捷,但是會出現異常TransactionTooLargeException。只時候就需要用到插件EventBus。
EventBus 使用的是發佈 訂閱者模型,發佈者通過EventBus發佈事件,訂閱者通過EventBus訂閱事件。當發佈者發佈事件時,訂閱該事件的訂閱者的事件處理方法將被調用。
定義事件

public class MessageEvent {

    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

訂閱事件
使用@Subscribe註解來定義訂閱者方法,方法名可以是任意合法的方法名,參數類型爲訂閱事件的類型

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    ...
}

@Subscribe(threadMode = ThreadMode.MAIN)中使用了ThreadMode.MAIN這個模式,表示該函數在主線程即UI線程中執行
EventBus總共有四種線程模式,分別是:

ThreadMode.MAIN:表示無論事件是在哪個線程發佈出來的,該事件訂閱方法onEvent都會在UI線程中執行,這個在Android中是非常有用的,因爲在Android中只能在UI線程中更新UI,所有在此模式下的方法是不能執行耗時操作的。
ThreadMode.POSTING:表示事件在哪個線程中發佈出來的,事件訂閱函數onEvent就會在這個線程中運行,也就是說發佈事件和接收事件在同一個線程。使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。
ThreadMode.BACKGROUND:表示如果事件在UI線程中發佈出來的,那麼訂閱函數onEvent就會在子線程中運行,如果事件本來就是在子線程中發佈出來的,那麼訂閱函數直接在該子線程中執行。
ThreadMode.AYSNC:使用這個模式的訂閱函數,那麼無論事件在哪個線程發佈,都會創建新的子線程來執行訂閱函數。

訂閱者還需要在總線上註冊,並在不需要時在總線上註銷

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 註冊訂閱者
    EventBus.getDefault().register(this);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 註銷訂閱者
    EventBus.getDefault().unregister(this);
}    

發佈事件

EventBus.getDefault().post(new MessageEvent("Post Event!"));

(5)藉助外部存儲

包括SharedPreference 、Android數據庫SQLite 、File

(6)LocalBroadcast

在同一個應用程序內,進行通信的組件。它通過 LocalBroadcastManager (以下簡稱 LBM),來實現註冊、解注、發送廣播等操作。
它和原有的 BroadcastReceiver (Global)相比而言,好處有:
1、因 LocalBroadcast 只在本應用內,所以完全無需擔心隱私數據被泄露的問題。
2、LocalBroadcast 的發送和接收更可控。
3、比原有的 BroadcastReceiver 更高效。

(三)從Activity返回數據

要從Activity返回數據,需要使用startActivityForResult方法來顯示Activity。
1、 從Activity1跳轉到Activity2:startActivityForResult(intent,requestCode)

Intent intent = new Intent();
 intent = intent.setClass(ActivityIntent.this, AnotherActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("string", et_string.getText().toString());
        intent.putExtras(bundle);
        startActivityForResult(intent,0);

2、 從Activity2返回數據到Activity1:setResult(resultCode,Intent data)

Intent intent = new Intent();
intent = intent.setClass(AnotherActivity.this, ActivityIntent.class);
Bundle bundle = new Bundle();
bundle.putInt("result", "Activity2的處理結果");
intent.putExtras(bundle);
AnotherActivity.this.setResult(RESULT_OK, intent); //RESULT_OK是返回狀態碼
AnotherActivity.this.finish();

3、 在Activity1中重寫onActivityResault方法,接受數據:重寫onActivityResult(requestCode,resultCode)

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch(resultCode) { //根據狀態碼,處理返回結果
        case RESULT_OK:
        Bundle bundle =data.getExtras(); //獲取intent裏面的bundle對象
        String result = bundle.getInt("result");
        break;
default:
        break;
        }

(四)Activity啓動過程

Activity啓動過程,是Activity與ActivityManagerService以Binder爲通訊方式進行多次通訊的結果。

1、整體流程

(1)Launcher通知AMS啓動該APP的MainActivity,也就是清單文件設置啓動的Activity

手機桌面也是一個app,每一個應用的icon都羅列在Launcher上,點擊icon觸發onItemClick事件,下面例如我們要啓動某個app,首先我們要在清單文件定義默認啓動的Activity信息。

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>

然後Launcher獲取該信息後,啓動App

//該應用的包名
            String pkg = info.activityInfo.packageName;
            //應用的主activity類
            String cls = info.activityInfo.name;

            ComponentName componet = new ComponentName(pkg, cls);

            Intent i = new Intent();
            i.setComponent(componet);
            startActivity(i);

啓動Activity由ActivityManagerService(AMS)全權管理,他們之間的通訊需要使用Binder,Activity通過Binder與AMS多次通訊,才能啓動app。
startActivityForResult
我們從Activity的startActivity方法開始分析。
startActivity方法有好幾種重載方式,但它們最終都會調用startActivityForResult方法。

 @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);
        }
    }

在startActivityForResult方法內,會調用Instrumentation的execStartActivity方法

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {

            ......

            Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            ......
}

Instrumentation
Instrumentation從字面上來看是儀器盤的意思,具體到程序中是管理activity的一個工具類,包括創建和啓動Activity,activity的生命週期方法都是由Instrumentation這個儀器來控制,一個進程中只用一個Instrumentation實例。

  /**
     *
     *
     * @param who The Context from which the activity is being started.activity所在上下文
     * @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.啓動的activity標識
     * @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.傳入的Intent
     * @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, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;

        ......

        try {
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

            //檢查啓動Activity的結果(拋出異常,例如清單文件未註冊Activity)
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

下面我們來分析一下比較重要的2個參數,contextThread和token。
IBinder contextThread=mMainThread.getApplicationThread
ActivityThread通過內部類ApplicationThread用於進程間通訊的Binder對象

1、mMainThread實際上是ActivityThread對象。ActivityThread,就是主線程,也就是UI線程,它是在App啓動時創建的,它代表了App應用程序。
2、通過構造方法我們就很清晰的可以得知原來這個ApplicationThreadNative就是相當於AIDL通訊中的Stub,也就是服務端,ApplicationThreadProxy即AIDL通訊中的Proxy,也就是客戶端。詳解見深入淺出AIDL二。所以ApplicationThread是通訊的具體實現類。

public abstract class ApplicationThreadNative extends Binder implements IApplicationThread {

    static public IApplicationThread asInterface(IBinder obj) {...}

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {...}

    public IBinder asBinder(){return this;}

}

class ApplicationThreadProxy implements IApplicationThread {
    private final IBinder mRemote;

    public ApplicationThreadProxy(IBinder remote) {
        mRemote = remote;
    }

    ...
}

IBinder token
token對象,是在Activity的attach方法中傳入的,也就是Activity的創建與關聯時候傳入的Activity信息。 這也是個Binder對象,它代表了Launcher這個Activity,這裏也通過Instrumentation傳給AMS,AMS查詢後,就知道是誰向AMS發起請求了。

contextThread和token這兩個參數用於通訊,contextThread表示Activity上下文所在主線程,token爲啓動的Activity標識。傳遞給AMS,以後AMS想反過來通知該Activity,就能通過這兩個參數,找到Activity。

IActivityManager
在Instrumentation中,啓動Activity真正的實現是由ActivityManagerNative.getDefault()的startActivity方法來完成。

ActivityManagerService(下面簡稱AMS)繼承自ActivityManagerNative(下面簡稱AMN),而AMN繼承自Binder並實現了IActivityManager這個Binder接口,因此AMS也是一個Binder,它是IActivityManager的具體實現.

public final class ActivityManagerService extends ActivityManagerNative {...}
public abstract class ActivityManagerNative extends Binder implements IActivityManager{...}
public interface IActivityManager extends IInterface {...}

分析AIDL

  /**
     * 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);
            return am;
        }
    };

    /**
     * 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;//同一進程,返回Stub本地對象。
        }

        return new ActivityManagerProxy(obj);//跨進程,返回代理對象。
    }

可以發現,在AMN 中,AMS這個Binder對象採用單例模式對外提供,Singleton是一個單例的封裝類,第一次調用它的get方法時它會通過create方法來初始化AMS這個Binder對象,在後續的調用中則直接返回之前創建的對象(使用同一個AMS)。

分析create方法,根據Binder分析,可以得知在IActivityManager內(相當於Client),通過應用程序中的0號引用,可以向SMgr獲取服務端(Server)的Binder引用。
AMN通過getDefault方法,從ServiceManager中獲取AMS中Binder的引用對象,然後將它轉換成ActivityManagerProxy對象(簡稱AMP),AMP就是AMS的代理對象。類似AIDL中客戶端的綁定代碼,此時我們就可以通過ActivityManagerProxy(asInterface返回值爲 IActivityManager),與AMS進行通訊。
在這裏插入圖片描述
從上面的分析可以知道,Activity是由AMN.getDefault()來啓動的,而AMN.getDefault()實際上是AMS,因此Activity的啓動過程又轉移到了AMS中,爲了繼續分析這個過程,只需要查看AMS的startActivity方法即可。
在這裏插入圖片描述

(2)AMS記錄要啓動的Activity信息,並且通知Launcher進入pause狀態。Launcher進入pause狀態後,通知AMS已經paused了,可以啓動app了。

ActivityManagerService
AMS的startActivity方法實際調用mActivityStarter的startActivityMayWait方法

//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啓動管理類新增ActivityStarter(原本是ActivityStackSupervisor處理該過程)
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, null);
    }

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) {

            ``````

            //根據intent在系統中找到合適的應用的activity,如果有多個activity可選擇,
            //則會彈出ResolverActivity讓用戶選擇合適的應用。
            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;
    }



    //在startActivityLocked方法裏,對傳過來的參數做一些校驗,然後創建ActivityRecord對象,再調用startActivityUnchecked(7.0前是startActivityUncheckedLocked)方法啓動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;

        ``````
        //創建ActivityRecord對象
        //ActivityRecord  :  在AMS中,將用ActivityRecord來作爲Activity的記錄者,每次啓動一個Actvity會有一個對應的ActivityRecord對象,表示Activity的一個記錄
        ActivityRecord r = new ActivityRecord(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);


        //此處將通知ActivityStarter, Activity對應的Task被移動到前臺
        postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);

        return err;
    }
//ActivityStarter

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {

            ``````
        //ActivityStack的startActivityLocked,不要搞混了。同時調用WindowManager準備App切換相關的工作
        //將ActivityRecord(當前Activity)加入回退棧
        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 {
                //最終調用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
            }

}

(1)在startActivityLocked方法裏,對傳過來的參數做一些校驗(檢查調用者權限)
(2)然後創建ActivityRecord對象,ActivityRecord代表要開啓的Activity信息,內封裝了很多信息。再調用startActivityUnchecked方法啓動Activity。
(3)startActivityUnchecked方法負責調度ActivityRecord和Task。理解該方法是理解Actvity啓動模式(默認standard:每啓動一次Activity,就會創建一個新的Activity的實例並將該實例置於棧頂)的關鍵。startActivityUnchecked方法調度task的算法非常複雜,和當前回退棧,要啓動的acitivity的啓動模式以及taskAffinity屬性,啓動activity時設置的intent的flag等諸多要素相關。
1、調用ActivityStack的startActivityLocked將ActivityRecord加入回退棧,如果這是首次打開的應用,則會被放入ActivityTask棧頂
2、最終調用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked
ActivityStackSupervisor

//ActivityStackSupervisor

    boolean resumeFocusedStackTopActivityLocked() {
        return resumeFocusedStackTopActivityLocked(null, null, null);
    }

    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        if (targetStack != null && isFocusedStack(targetStack)) {
        //待啓動Activity對應的Task爲前臺Task時,調用該Task對應ActivityStack的resumeTopActivityUncheckedLocked函數
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null || r.state != RESUMED) {
        //否則只是調用當前前臺棧的resumeTopActivityUncheckedLocked
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
        }
        return false;
    }

ActivityStack

//ActivityStack


    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {

        ``````
        result = resumeTopActivityInnerLocked(prev, options);

        return result;
    }


    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {


        //mResumedActivity指向上一次啓動的Activity(Launcher)
        if (mResumedActivity != null) {
            ``````

           //通知Launcher進入pause狀態
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
        }
         if (pausing) {//Launcher已經暫停了
            ``````

            if (next.app != null && next.app.thread != null) {
                //如果app已經啓動過
                //調用淘寶(待啓動)Activity所在進程的優先級,保證其不被kill
                mService.updateLruProcessLocked(next.app, true, null);
            }

        } 
        ``````

        if (next.app != null && next.app.thread != null) {

        //如果Intent不爲空,調用NewIntent方法傳入Intent
        next.app.thread.scheduleNewIntent(next.newIntents, next.appToken, false);

        //假設淘寶App已經啓動,點擊Home鍵返回到Launcher,再次從Launcher啓動淘寶(或者第三方啓動已開啓的App)
        next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                        mService.isNextTransitionForward(), resumeAnimOptions);
        ``````

        } else {
        ``````
           //創建進程,冷啓動Activity。或者已啓動App,重新啓動Activity
           mStackSupervisor.startSpecificActivityLocked(next, true, true);
        }

        return true;
    }

resumeTopActivityInnerLocked主要執行2個功能:
1、找出還沒結束的首個ActivityRecord,如果一個沒結束的Activity都沒有,就開啓Launcher程序,否則先需要暫停當前的Activity。因爲我們是在Lancher中啓動mainActivity,所以當前mResumedActivity!=null,調用startPausingLocked()使得Launcher進入暫停狀態
2、判斷是否需要重新啓動目標Activity,即Activity是否已經啓動過。(例如保存在後臺,應用切換)

(3)app未開啓過,所以AMS啓動新的進程,並且在新進程中創建ActivityThread對象,執行其中的main函數方法。app主線程啓動完畢後通知AMS,並傳入applicationThread以便通訊。

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);
                }
                //目標Activity的App已經啓動(存在ActivityThread),則重啓Activity
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
               ``````
            }
        }

        //如果進程不存在,則通過zygote創建應用進程。
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

ActivityStackSupervisor的startSpecificActivityLocked用於創建新的進程並啓動Activity,分爲2種情況:
(1)如App未啓動,例如從Launcher冷啓動App,則需要創建新進程,通過AMS調用Zygote(受精卵)孵化應用進程。
(2)如果App已經啓動,例如從MainActivity跳轉到LoginActivity,則通過realStartActivityLocked啓動。
ActivityManagerService
我們以Launcher啓動App(App未開啓過),故通過AMS創建新的ActivityThread對象並進行Activity的綁定

//ActivityServiceManager

        //Process.java的start函數,將通過socket發送消息給zygote
        //zygote將派生出一個子進程,子進程將通過反射調用ActivityThread的main函數
        Process.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);

Zygote進程孵化出新的應用進程後,通過反射執行ActivityThread類的main方法。在該方法裏會先準備好Looper和消息隊列,然後調用attach方法將應用進程綁定到AMS,然後進入loop循環,不斷地讀取消息隊列裏的消息,並分發消息。
ActivityThread

//ActivityThread

public static void main(String[] args) {
    ``````
    //準備主線程的Looper,下篇博文分析Handler,Looper
    Looper.prepareMainLooper();

    //創建當前進程的ActivityThread
    ActivityThread thread = new ActivityThread();

    //將該進程綁定到AMS
    thread.attach(false);

    if (sMainThreadHandler == null) {
    //保存進程對應的主線程Handler
        sMainThreadHandler = thread.getHandler();
    }

    ``````
    //進入主線程的消息循環
    Looper.loop();

    ``````
}

//上面說過,ApplicationThread是ActivityThread用來與AMS通訊的中介
final ApplicationThread mAppThread = new ApplicationThread();

private void attach(boolean system) {
    if (!system) {
        final IActivityManager mgr = ActivityManagerNative.getDefault();

        //調用AMS的attachApplication方法,將ApplicationThread對象綁定至ActivityManagerService
        //這樣AMS就可以通過ApplicationThread代理對象控制應用進程

            mgr.attachApplication(mAppThread);
    } else {
        ``````
    }
}

至此,進程創建完畢,擁有了主線程,並通過attach方法綁定到AMS

AMS啓動Activity大致流程
在這裏插入圖片描述

(4)AMS通知app綁定Application。

此時,有了app進程和主線程,但仍是一幅空殼。沒有activity信息,也沒有關聯上下文。
AMS通過傳入的applicationThread通知ActivityThread綁定Application並啓動Activity。
AMS

   @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            //通過Binder獲取proxy(ApplicationThread)方的進程id(也就是淘寶應用進程id)
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
    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;
        }

        //因爲進程由AMS啓動,所以在AMS中一定會有ProcessRecord(進程記錄)
        //如果沒有ProcessRecord,則需要殺死該進程並退出
        if (app == null) {
            ``````
            return false;
        }

        // If this application record is still attached to a previous
        // process, clean it up now.
        if (app.thread != null) {
            //如果從ProcessRecord中獲取的IApplicationThread不爲空,則需要處理該IApplicationThread
            //因爲有可能此Pid爲複用,舊應用進程剛釋放,內部IApplicationThread尚未清空,
            //同時新進程又剛好使用了此Pid
            handleAppDiedLocked(app, true, true);
        }


        //創建死亡代理(進程kill後通知AMS)
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);

        //進程註冊成功,移除超時通知
        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        ``````
        try {
            //******綁定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) {

            ``````
            //bindApplication失敗後,重啓進程
            startProcessLocked(app, "bind fail", processName);
            return false;
        }

        try {
            //******啓動Activity(啓動淘寶MainActivity)******
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;//didSomething表示是否有啓動四大組件
            }
        } catch (Exception e) {
            badApp = true;
        }

        ``````
        //綁定service和Broadcast的Application


        if (badApp) {
            //如果以上組件啓動出錯,則需要殺死進程並移除記錄
            app.kill("error during init", true);
            handleAppDiedLocked(app, false, true);
            return false;
        }

        //如果以上沒有啓動任何組件,那麼didSomething爲false
        if (!didSomething) {
            //調整進程的oom_adj值, oom_adj相當於一種優先級
            //如果應用進程沒有運行任何組件,那麼當內存出現不足時,該進程是最先被系統“殺死”
            updateOomAdjLocked();
        }
        return true;
    }

在在attachApplicationLocked中有兩個比較重要的方法函數:
1、thread.bindApplication(…) : 綁定Application到ActivityThread
2、mStackSupervisor.attachApplicationLocked(app) : 啓動Activity(7.0前爲mMainStack.realStartActivityLocked())
ApplicationThread
ActivityThread通過ApplicationThread與AMS進行通訊,所以上面的thread.bindApplication(…)方法,就應該是通過ApplicationThread進行傳達。即,AMS通過ApplicationThread通知ActivityThread進行綁定Application

private class ApplicationThread extends ApplicationThreadNative {

        public final void bindApplication(...一大堆參數...) {
            AppBindData data = new AppBindData();
            //給data設置參數...
            ``````

            sendMessage(H.BIND_APPLICATION, data);
        }
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        Message msg = Message.obtain();
        //給msg設置參數
        ``````
        mH.sendMessage(msg);
    }

H
發送消息是通過H的Handler類來完成的

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;

                ``````
                //綁定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;

            }

        }

    }

可以看出,這個H類相當於ActivityThread和ApplicationThread的中介人,也就是拉皮條的。ActivityThread通過ApplicationThread與AMS通訊。 ApplicationThread通過H與ActivityThread通訊,處理Activity事務。
上面ApplicationThread給H發送BIND_APPLICATION標識,在H中,通過handleBindApplication處理application的綁定事務。
ActivityThread

 private void handleBindApplication(AppBindData data) {

           ``````
          //根據傳遞過來的ApplicationInfo創建一個對應的LoadedApk對象
          data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//獲取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}.
          */
          //禁止在主線程使用網絡操作
          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引入Fileprovide
          if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
              StrictMode.enableDeathOnFileUriExposure();
          }

          ``````
          //創建進程對應的Android運行環境ContextImpl
          final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);


          if ((InstrumentationInfo)ii != null) {
               ``````
          } else {
               //注意Activity的所有生命週期方法都會被Instrumentation對象所監控,
               //也就說執行Activity的生命週期方法前後一定會調用Instrumentation對象的相關方法
               mInstrumentation = new Instrumentation();
          }

          try {
             ``````
             Application app = data.info.makeApplication(data.restrictedBackupMode, null);
             mInitialApplication = app;


             //加載進程對應Package中攜帶的ContentProvider
             installContentProviders(app, data.providers);

             ``````
             mInstrumentation.onCreate(data.instrumentationArgs);

             try {
                  //這裏會調用Application的onCreate方法
                  //故此Applcation對象的onCreate方法會比ActivityThread的main方法後調用
                  //但是會比這個應用的所有activity先調用
                  mInstrumentation.callApplicationOnCreate(app);
              } catch (Exception e) {
                  ``````
              }
            } finally {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }

handleBindApplication的目的是讓一個Java進程融入到Android體系中。
因此,該函數中的代碼主要進行以下工作:
(1)按照Android的要求,完成對進程基本參數的設置置,包括設置進程名、時區、資源及兼容性配置;
(2)同時也添加了一些限制,例如主線程不能訪問網絡等。
(3)創建進程對應的ContextImpl、LoadedApk、Application等對象,同時加載Application中的ContentProvider,並初始化Application(onCreate())。
(4)使用Instrumentation監控Activity的生命週期。(一個進程對應一個Instrumentation實例)
當完成上述工作後,新建的進程終於加入到了Android體系。

這裏的消息傳遞機制包含4個類:H、ApplicationThread、ActivityThread與AMS:
1、Binder消息傳遞:ActivityThread通過ApplicationThread與AMS通訊。
2、Handler消息傳遞:ApplicationThread通過H與ActivityThread通訊,處理Activity事務。
3、ActivityThread通過ApplicationThread和AMS進行進程間通信,AMS以進程通訊的方式來完成ActivityThread的請求後調用ApplicationThread中的Binder方法,然後ApplicationThread會向H發送消息,H收到消息後會將ApplicationThread中的邏輯切換到ActivityThread中去執行,即切換到主線程中去執行,這個過程就是主線程的消息循環模型。
4、使用H文件是因爲:

  • 便於集中管理,方便打印Log日誌等,H文件是大管家
  • AMS通過ApplicationThread回調到我們的進程,ApplicationThread是一個Binder,回調邏輯是在Binder線程池完成的,所以需要Handler H切換UI線程
    5、注:ActivityThread並不是一個線程Thread,它是final類並且無繼承或者實現其它類,它的作用就是在main方法內消息循環,處理主線程事務。

(5)app啓動MainActivitiy,並且創建和關聯Context,最後調用onCreate方法。

綁定application後,便可啓動Activity
ActivityStackSupervisor

    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;

        //ActivityStackSupervisor維護着終端中所有ActivityStack
        //此處通過輪詢,找出前臺棧頂端的待啓動Activity
        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) {
                    //前臺待啓動的Activity與當前新建的進程一致時,啓動這個Activity
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {

                            //realStartActivityLocked進行實際的啓動工作
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {

                        }
                    }
                }
            }
        }

        return didSomething;
    }

最終通過realStartActivityLocked啓動activity

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);

AMS最後通過ApplicationThread通知ActivityThread啓動Activity,啓動套路如綁定Application
ApplicationThread

//內部類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();
            //設置參數
            ``````

            //從LAUNCH_ACTIVITY這個標識我們就可以知道,它就是用來啓動Activity
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
    }

H

  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;

                    //利用ApplicationInfo等信息得到對應的LoadedApk,保存到ActivityClientRecord
                    //ActivityClientRecord包含Activity相關的信息
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

                ``````
            }
        }
    }

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);

        ``````
    } 
    ``````
}

handleLaunchActivity方法裏有有兩個重要的函數調用,
(1)performLaunchActivity : 會調用Activity的onCreate,onStart,onResotreInstanceState方法。最終完成了Activity對象的創建和啓動過程。
(2)handleResumeActivity : 會調用Activity的onResume方法.
performLaunchActivity
performLaunchActivity主要完成了如下幾件事
(1)從ActivityClientRecord中獲取待啓動的Activity的組件信息。

ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        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);
        }

(2)通過Instrumentation的newActivity方法使用類加載器創建Activity對象。

 Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ``````
        }

(3)通過LoadedApk的makeApplication方法來創建Application對象。

Application app = r.packageInfo.makeApplication(false, mInstrumentation);//r.packageInfo爲LoadedApk對象

其實在我們上面的bindApplication中,我們就有介紹到通過LoadedApk創建Application,並且創建完畢後,通過Instrumentation的callApplicationOnCreate來調用Application的onCreate方法

Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    ``````
 mInstrumentation.callApplicationOnCreate(app);

所以第三步是爲了判斷Application是否爲空,而且我們從makeApplication方法中也能看出如果Application已經被創建過了,那麼就不會再重複創建了。
(4)創建ContextImpl對象,並通過Activity的attach方法來完成一些重要數據的初始化。

              Context appContext = createBaseContextForActivity(r, activity);//創建ContextImpl對象

                ``````
                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);

ContextImpl是一個很重要的數據結構,它是Context的具體實現,Context中的大部分邏輯都是由ContextImpl來完成的。ContextImpl來完成的。ContextImpl是通過Activity的attach方法來和Activity建立關聯的,除此之外,在attach方法中Activity還會完成Window的創建並建立自己和Window的關聯,這樣當Window接收到外部輸入事件後就可以將事件傳遞給Activity。

(5)調用Activity的onCreate方法。

                mInstrumentation.callActivityOnCreate(activity, r.state);

由於Activity的onCreate已經被調用,這也意味着Activity已經完成了整個啓動過程。
(6)調用Activity的onStart,onResotreInstanceState方法。

 		mInstrumentation.callActivityOnCreate(activity, r.state);
        ``````
        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);

2、總結

(1)流程圖
在這裏插入圖片描述
(2)時序圖
在這裏插入圖片描述
(3)總結
Activity的啓動過程,我們可以從Context的startActivity說起,其實現是ContextImpl的startActivity
(1)首先會調用Activity的startActivityForResult,之後通過Instrumentation工具類獲取一些用於通訊的參數(ContextThread、Token)並嘗試啓動Activity,這是一個跨進程過程,它會通過Binder機制調用AMS的startActivity方法
(2)當AMS校驗完Activity的合法性後,會將當前Activity加入回退棧,並根據啓動模式啓動Activity
(3)因爲是第一次啓動Activity,應通過AMS調用Zygote創建新的應用進程,之後反射執行ActivityThread類的main方法,在該方法中準備好Looper和消息隊列,並調用attach方法將applicationThread綁定到AMS用於通訊,然後進入Loop循環。
(4)AMS通過ApplicationThread通知ActivityThread綁定Application並啓動Activity。AMS通過ApplicationThread回調到我們的進程,這也是一次跨進程過程,而applicationThread就是一個binder,回調邏輯是在binder線程池中完成的,所以需要通過Handler H將其切換到ui線程。
(5)綁定Application發送的消息是BIND_APPLICATION,對應的方法是handleBindApplication,該方法中對進程進行了配置,並創建及初始化了Application。
啓動Activity發送的消息是LAUNCH_ACTIVITY,對應的方法handleLaunchActivity,在這個方法裏完成了Activity的創建和啓動,接着,在activity的onResume中,activity的內容將開始渲染到window上,然後開始繪製直到我們看見。

(五)Activity四種啓動模式

決定是生成新的Activity還是重用已存在的Activity。Android 中默認啓動模式爲 standard,我們可以通過在 AndroidManifest.xml 的 activity 標籤下通過 launchMode 屬性指定我們想要設置的啓動模式。

(1)標準Standard

1.1 特點
每啓動一次Activity,就會創建一個新的Activity的實例並將該實例置於棧頂
(1)每開啓一次頁面都會在任務棧中添加一個Activity,而只有任務棧中的Activity全部清除出棧時,任務棧被銷燬,程序纔會退出,這樣就造成了用,戶體驗差, 需要點擊多次返回纔可以把程序退出了。
(2)每開啓一次頁面都會在任務棧中添加一個Activity還會造成數據冗餘, 重複數據太多, 會導致內存溢出的問題(OOM)。

  • Activity默認啓動模式
  • 因ApplicationContext沒有任務棧,故無法採用標準模式啓動Activity,但可通過爲待啓動的Activity(一般爲MainActivity)指定標記位:FLAG_ACTIVITY_NEW_TASK,則啓動時就會該Activity創建一個新的任務棧(實際上採用了singleTask模式)
    1.2 示意圖
    在這裏插入圖片描述
    1.3 實例
    一個任務棧,每創建一個Activity就往棧頂增加一個實例
    創建ActivityA→Task1 Instance 1
    創建ActivityA→Task1 Instance2
    創建ActivityB→Task1 Instance3…
    1.4 場景
    正常的去打開一個新的頁面,這種啓動模式使用最多,最普通 。

(2)棧頂複用SingleTop

1.1 特點
啓動Activity,若創建的Activity位於任務棧棧頂,則Activity的實例不會重建,而是重用棧頂的實例。(調用實例的onNewIntent(),可以通過intent傳值,不調用onCreate()和onStart())
否則就創建該Activity新實例並置於棧頂

protected void onNewIntent(Intent intent) {
  super.onNewIntent(intent);
  setIntent(intent);//must store the new intent unless getIntent() will return the old one
}

1.2 示意圖
在這裏插入圖片描述
1.3 實例
打開步驟:MainActivity->SingleTaskActivity->SingleTaskActivity->OtherActivity->SingleTaskActivity->SingleTaskActivity
第一次啓動SingleTopActivity的時候會執行onCreate()方法新建一個實例,然後再次啓動SingleTopActivity頁面會回調onNewIntent(),說明沒有創建新的實例,而且hashCode值沒有發生改變。此時我們繼續打開另一個Activity,這時OtherActivity處於棧頂,我們繼續啓動SingleTopActivity,這時發現又是執行了onCreate(),說明又重新創建了新的實例,當我們繼續啓動SingleTopActivity,發現回調了onNewIntent(),同樣hashCode值沒有發生改變,證明沒有重新創建實例。
1.4場景
singleTop適合接收通知啓動的內容顯示頁面。例如,某個新聞客戶端的新聞內容頁面
假如一個新聞客戶端,在通知欄收到了3條推送,點擊每一條推送會打開新聞的詳情頁,如果爲默認的啓動模式的話,點擊一次打開一個頁面,會打開三個詳情頁,這肯定是不合理的。如果啓動模式設置爲singleTop,當點擊第一條推送後,新聞詳情頁已經處於棧頂,當我們第二條和第三條推送的時候,只需要通過Intent傳入相應的內容即可,並不會重新打開新的頁面,這樣就可以避免重複打開頁面了。

(3)棧內複用SingleTask

1.1 特點
1.查看Activity所在的任務棧是否存在,若不存在則重建一個任務棧,創建Activity實例並置於棧頂(可通過TaskAffinity屬性指定Activity想要的任務棧)
這個過程還存在一個任務棧的匹配,因爲這個模式啓動時,會在自己需要的任務棧中尋找實例,這個任務棧就是通過taskAffinity屬性指定。如果這個任務棧不存在,則會創建這個任務棧。不設置taskAffinity屬性的話,默認爲應用的包名。
2.若存在任務棧,則查看該Activity是否存在棧中,若不存在,則創建Activity實例並置於棧頂
3.若該Activity存在棧中,在將實例上的所有Activity出棧,使該Activity位於棧頂(回調onNewIntent())
1.2 示意圖
在這裏插入圖片描述
1.3 實例
情況1:
SingleTaskActivity配置

<activity
     android:name=".SingleTaskActivity"
     android:launchMode="singleTask">
</activity>

打開步驟:MainActivity->SingleTaskActivity->SingleTaskActivity->OtherActivity->SingleTaskActivity->SingleTaskActivity
結果:首先,因爲發現它們的taskId值都相同,所以它們同屬於一個任務棧。其次,第一次啓動SingleTaskActivity的時候會執行onCreate()方法新建一個實例,然後再次啓動SingleTaskActivity頁面會回調onNewIntent(),說明沒有創建新的實例,而且hashCode值沒有發生改變。此時我們繼續打開另一個Activity,然後繼續啓動SingleTaskActivity,這時發現仍然只回調onNewIntent(),說明沒有創建新的實例,當我們繼續啓動SingleTaskActivity,仍然只是回調了onNewIntent(),此過程中發現hashCode值始終沒有發生改變,證明引用都是同一個的實例。
情況2:
設置taskAffinity屬性,singleTask所在的Activity與啓動它的Activity處於不同的任務棧中。
SingleTaskActivity配置

<activity
     android:name=".SingleTaskActivity"
     android:launchMode="singleTask"
     android:taskAffinity="${applicationId}.singleTask">
</activity>

因爲MainActivity沒有指定taskAffinity屬性,默認爲包名,與SingleTaskActivity不同,所以在啓動SingleTaskActivity時,發現這個棧不存在,系統首先會創建這個棧然後將SingleTaskActivity壓入棧中。之後我們發現只要棧中存在SingleTaskActivity這個實例,就會直接引用。
1.4場景
SingleTask這種啓動模式最常使用的就是一個APP的首頁,因爲一般爲一個APP的第一個頁面,且長時間保留在棧中,所以最適合設置singleTask啓動模式來複用。

(4)單例SingleInstance

1.1 特點
單實例模式,顧名思義,只有一個實例。該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全局唯一性,即整個系統中就這麼一個實例,由於棧內複用的特性,後續的請求均不會創建新的Activity實例,除非這個特殊的任務棧被銷燬了。以singleInstance模式啓動的Activity在整個系統中是單例的,如果在啓動這樣的Activiyt時,已經存在了一個實例,那麼會把它所在的任務調度到前臺,重用這個實例。
創建一個Activity則新建一個任務棧,並將該Activity實例放入新棧。
一旦該模式的Activity實例已存在某個棧中,任何應用激活該Activity都會重用該棧中的實例並進入該應用中,即 多個應用共享該棧中實例
1.2 示意圖
在這裏插入圖片描述
1.3 實例
通過測試發現,在第一次打開SingleInstanceActivity的時候,由於系統不存在該實例,所以系統會新建一個任務棧來存放該Activity實例,而且只要打開過一次該Activity,後面無論什麼時候再次啓動該Activity,都會直接引用第一次創建的實例,而且會回調該實例的onNewIntent()方法。
1.4場景
singleInstance適合需要與程序分離開的頁面。例如鬧鈴提醒、電話撥號盤界面。

(5)啓動模式總結

在這裏插入圖片描述
(1)standard
標準模式下,只要啓動一次Activity,系統就會在當前任務棧新建一個實例。
(2)singleTop
1、當前棧中已有該Activity的實例並且該實例位於棧頂時,不會創建實例,而是複用棧頂的實例,並且會將Intent對象傳入,回調onNewInten()方法;
2、當前棧中已有該Activity的實例但是該實例不在棧頂時,其行爲和standard啓動模式一樣,依然會創建一個新的實例;
3、當前棧中不存在該Activity的實例時,其行爲同standard啓動模式。
(3)singleTask
在複用的時候,首先會根據taskAffinity去找對應的任務棧:
1、如果不存在指定的任務棧,系統會新建對應的任務棧,並新建Activity實例壓入棧中。
2、如果存在指定的任務棧,則會查找該任務棧中是否存在該Activity實例
a、如果不存在該實例,則會在該任務棧中新建Activity實例。
b、如果存在該實例,則會直接引用,並且回調該實例的onNewIntent()方法。並且任務棧中該實例之上的Activity會被全部銷燬。
(4)singleInstance
啓動該模式Activity的時候,會查找系統中是否存在:
1、不存在,首先會新建一個任務棧,其次創建該Activity實例。
2、存在,則會直接引用該實例,並且回調onNewIntent()方法。
特殊情況:該任務棧或該實例被銷燬,系統會重新創建。

在使用APP過程中,不可避免頁面之間的跳轉,那麼就會涉及到啓動模式。其實在對界面進行跳轉時,Android系統既能在同一個任務中對Activity進行調度,也能以Task(任務棧)爲單位進行整體調度。在啓動模式爲standard或singleTop時,一般是在同一個任務中對Activity進行調度,而在啓動模式爲singleTask或singleInstance是,一般會對Task進行整體調度。

(6)任務棧

(6.1)什麼是任務棧

application中有很多activity,application是通過任務棧的形式對管理這些activity。任務棧有以下幾個特點:

  • 任務棧是app管理activity的一種容器,遵循先進後出原則
  • 一個app默認只有一個任務棧,由系統指定
  • 一個app可以有多個任務棧,需要開發者手動指定
  • 多任務棧出棧(點擊back)規則:出棧時,先將前臺棧清空,再去清空後臺棧

(6.2)taskAffinity

activity的任務棧的相關性。擁有相同affinity的activity在概念上屬於同一個task。一個task的affinity取決於這個task內的根activity的taskaffinity。taskaffinity屬性用於指定當前Activity所關聯的任務棧,它的作用主要有:

  • 通過FLAG_ACTIVITY_NEW_TASK標記給activity指定任務棧
  • 決定Activity重新歸屬的任務(與allowTaskReparenting聯合實現把一個應用程序的activity移到另一個程序的任務棧中)
    默認情況下,application中所有的activity擁有相同的affinity(application包名),可以通過給taskaffinity屬性設置不同的值把他們分組。甚至可以把多個application中的activity放到同一個task中。如果這個屬性沒有被設置,那麼此屬性的值就繼承自application的此屬性的值(查看 application的taskaffinity屬性)。默認的值爲application的包名。
    舉例:
    如果加載某個Activity的intent,Flag被設置成FLAG_ACTIVITY_NEW_TASK時,它會首先檢查是否存在與自己taskAffinity相同的Task,如果存在,那麼它會直接宿主到該Task中,如果不存在則重新創建Task。
    AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="example.ylh.com" >

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".activityDemo.ActivityA"></activity>

        <activity android:name=".activityDemo.ActivityB"
            android:taskAffinity="example.ylh.com_new_task01"></activity>

        <activity android:name=".activityDemo.ActivityC"
            android:taskAffinity="example.ylh.com_new_task02"
            android:launchMode="singleTask"></activity>
    </application>

</manifest>

通過FLAG_ACTIVITY_NEW_TASK標記給activity指定任務棧
ActivityA

 tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(ActivityA.this,ActivityB.class);
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//添加FLAG_ACTIVITY_NEW_TASK標識
                startActivity(i);
            }
        });

ActivityB

 tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(ActivityB.this,ActivityC.class);
                startActivity(i);//ActivityC爲singleTask模式。singleTask模式啓動時會給intent添加 FLAG_ACTIVITY_NEW_TASK 標記
            }
        });

可以清楚的看到,其中包含了三個任務棧:
第一個:棧名:example.ylh.com,棧id:8904,包含MainActivity和ActivityA。
第二個:棧名:example.ylh.com_task01,棧id:8905,包含ActivityB。
第三個:棧名:example.ylh.com_task02,棧id:8906,包含ActivityC。 結果符合我們的預期。到此,我們可以得出結論,使用taskAffinity和FLAG_ACTIVITY_NEW_TASK(或singleTask),可以做到給我們的activity指定相應的任務棧。

(6.2)allowTaskReparenting屬性

當某個擁有相同 affinity 的任務即將返回前臺時,Activity 是否能從啓動時的任務轉移至此任務中去 —“true”表示可以移動,“false”表示它必須留在啓動時的任務中。
通常在啓動時,Activity 與啓動時的任務相關聯,並在整個生命週期都位於此任務中。 利用本屬性可以強行讓 Activity 在當前任務不再顯示時歸屬於另一個與其 affinity 相同的任務。 典型應用是讓一個應用程序的 Activity 轉移到另一個應用程序關聯的主任務中去。
實例
如果該Activity的allowTaskReparenting設置爲true,它進入後臺,當一個和它有相同affinity的Task進入前臺時,它會重新宿主,進入到該前臺的task中。
應用A的activityA

public class ActivityA extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_test);

        TextView tv = (TextView) findViewById(R.id.textview);
        tv.setText("A");
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent();
                i.setClassName("ylh.bsdf.com","ylh.bsdf.com.ActivityC");
                startActivity(i);
            }
        });
    }
}

應用A的AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ylh.bsdf.com">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".ActivityA">
        <activity android:name="ylh.bsdf.com.ActivityC"
         android:taskAffinity="ylh.bsdf.com"
        >
        </activity>
    </application>

</manifest>

應用B的activityC

public class ActivityC extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.tv);
        tv.setText("app B activityC");
    }
}

應用B的AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ylh.bsdf.com">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".ActivityC"
            android:allowTaskReparenting="true">
        </activity>
    </application>

</manifest>

首先,我們啓動appA,加載ActivityC,然後按Home鍵,使該task(假設爲task1)進入後臺。然後啓動appB,默認加載MainActivity。但是我們卻看到了ActivityC。實際上MainActivity也被加載了,只是ActivityC重新宿主,所以看到了ActivityC。
應用
如果某條 e-mail 信息包含了一個 Web 頁的鏈接,點擊此鏈接將啓動一個 Activity 顯示此 Web 頁。 這個 Activity 是由瀏覽器程序定義的,但卻作爲 e-mail 任務的一部分被啓動。 如果它重新歸屬於瀏覽器的任務,那麼在下次瀏覽器進入前臺時就會顯示出來,並且會在 e-mail 任務再次回到前臺時消失。

(7)Android退出整個應用解決方案

1、利用SingTask

將主Activity設爲SingTask模式,然後在要退出的Activity中轉到主Activity,然後重寫主Activity的onNewIntent函數,並在函數中加上一句finish

2、利用歷史棧和SingleTop

我們知道Android的窗口類提供了歷史棧,我們可以通過stack的原理來巧妙的實現,這裏我們在D窗口打開A窗口時在Intent中直接加入標誌Intent.FLAG_ACTIVITY_CLEAR_TOP,再次開啓A時將會清除該進程空間的所有Activity。
在D中使用下面的代碼

Intent intent = new Intent(); intent.setClass(D.this, A.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  //注意本行的FLAG設置 
startActivity(intent); finish();關掉自己 

在A中加入代碼

Override
protected void onNewIntent(Intent intent) { // TODO Auto-generated method stub
super.onNewIntent(intent);
//退出
        if ((Intent.FLAG_ACTIVITY_CLEAR_TOP & intent.getFlags()) != 0) {
               finish();
        }
}

A的Manifest.xml配置成

android:launchMode="singleTop"

原理總結: 一般A是程序的入口點,從D起一個A的activity,加入標識Intent.FLAG_ACTIVITY_CLEAR_TOP這個過程中會把棧中B,C,都清理掉。因爲A是android:launchMode=”singleTop” 不會調用oncreate(),而是響應onNewIntent()這時候判斷Intent.FLAG_ACTIVITY_CLEAR_TOP,然後把A finish()掉。 棧中A,B,C,D全部被清理。所以整個程序退出了。

3、通過廣播來完成退出功能

具體實現過程是這樣的:在每個Activity創建時(onCreate時)給Activity註冊一個廣播接收器,當退出時發送該廣播即可。大概的代碼如下:

@Override

protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       IntentFilter filter = new IntentFilter();
       filter.addAction("finish");
       registerReceiver(mFinishReceiver, filter)
       ……
}

private BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
           if("finish".equals(intent.getAction())) {
              Log.e("#########", "I am " + getLocalClassName()
                     + ",now finishing myself...");
              finish();
       }
    }
};

相信聰明的大家會把上面的代碼寫在一個基類裏面,因爲如果你的項目中Activity很多的話,寫起來很麻煩,而且也不符合代碼規範。
在退出時執行以下代碼即可關閉所有界面完全退出程序:

getApplicationContext().sendBroadcast(new Intent("finish"));

4、使用退出類

現下最流行的方式是定義一個棧,寫一個自定義的MyApplication類,利用單例模式去單獨對Activity進行管理,在每個Activity的onCreate()方法中調用MyApplication.getInstance().addActivity(this)將當前的Activity添加到棧中統一管理,如果需要退出應用程序時再調用MyApplication.getInstance().exit()方法直接就完全退出了應用程序。
實現方案:統一管理的好處就是如果需要退出時,直接將add進棧的Activity進行同意finish就行。exit方法的實現原理就是將棧中所有的Activity實例循環然後finish的。
方式1:

public class CloseActivity
{
    private static LinkedList<Activity> acys = new LinkedList<Activity>();

    public static Activity curActivity;

    public static void add(Activity acy)
    {
        acys.add(acy);
    }

    public static void remove(Activity acy) {
        acys.remove(acy);
    }

    public static void close()
    {
        Activity acy;
        while (acys.size() != 0)
        {
            acy = acys.poll();
            if (!acy.isFinishing())
            {
                acy.finish();
            }
        }
//        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

方式2:
將下面SysApplication這個類複製到工程裏面,然後在每個Acitivity的oncreate方法裏面通過SysApplication.getInstance().addActivity(this); 添加當前Acitivity到ancivitylist裏面去,最後在想退出的時候調用SysApplication.getInstance().exit();可直接關閉所有的Acitivity並退出應用程序。
附代碼:

public class SysApplication extends Application { 
    private List<Activity> mList = new LinkedList<Activity>(); 
    private static SysApplication instance; 

    private SysApplication() {   
    } 
    public synchronized static SysApplication getInstance() { 
        if (null == instance) { 
            instance = new SysApplication(); 
        } 
        return instance; 
    } 
    // add Activity  
    public void addActivity(Activity activity) { 
        mList.add(activity); 
    } 

    public void exit() { 
        try { 
            for (Activity activity : mList) { 
                if (activity != null) 
                    activity.finish(); 
            } 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { 
            System.exit(0); 
        } 
    } 
    public void onLowMemory() { 
        super.onLowMemory();     
        System.gc(); 
    }  
}

在應用程序裏面 的activity的oncreate裏面添加SysApplication.getInstance().addActivity(this)如:

public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SysApplication.getInstance().addActivity(this); 
}

總結:在程序內維護一個activity list,在響應應用程序退出的時候,遍歷該list,調用每一個Activity的finish()方法

Android.os.Process.killProcess(android.os.Process.myPid())  

或者

System.exit(0)  

即可完全退出程序(進程也同時被殺死)。

(六)Activity現場保存狀態

(1)場景

一般來說, 調用onPause()和onStop()方法後的activity實例仍然存在於內存中, activity的所有信息和狀態數據不會消失, 當activity重新回到前臺之後, 所有的改變都會得到保留.
有些內存不足、設備配置、意外操作可能會在運行時發生變化(例如屏幕方向、鍵盤可用性及語言設定切換而不是正常的應用程序行爲)。 發生這種變化時,Android系統會破壞正在運行的 Activity。但系統會使用一組存儲在Bundle對象中的鍵值對的集合來保存該Activity當前狀態。這樣如果用戶導航回它,系統會創建一個新的Activity實例並使用一組保存的數據描述Activity被銷燬的狀態。從而回復之前"實例狀態"。
要妥善處理重啓行爲,Activity 必須通過常規的Activity 生命週期恢復其以前的狀態,在 Activity 生命週期中,Android 會在停止 Activity 之前調用 onSaveInstanceState(),以便您保存有關應用狀態的數據。 然後,您可以在 onCreate()onRestoreInstanceState() 期間恢復 Activity 狀態。

onSaveInstanceState()調用時刻
當某個activity變得“容易”被系統銷燬時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷燬的,例如當用戶按BACK鍵的時候。
注意上面的雙引號,何爲“容易”?言下之意就是該activity還沒有被銷燬,而僅僅是一種可能性。包括如下場景:

  • 鎖屏
  • 點擊home鍵
  • 其他app進入前臺(接聽電話)
  • 啓動新Activity
  • 屏幕方向改變(在屏幕切換之前,系統會銷燬activity A,在屏幕切換之後系統又會自動地創建activity A,所以onSaveInstanceState一定會被執行)
  • App被殺死(內存不足)

這些情況下Activity優先級較低,系統隨時都有可能"未經你的允許"毀滅Activity。則onSaveInstanceState會被系統調用,這是系統的責任,因爲它必須要提供一個機會讓你保存你的數據。
onRestoreInstanceState()調用時刻
至於onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被調用的,onRestoreInstanceState被調用的前提是,activity A“確實”被系統銷燬了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示activity A的時候,用戶按下HOME鍵回到主界面,然後用戶緊接着又返回到activity A,這種情況下activity A一般不會因爲內存的原因被系統銷燬,故activity A的onRestoreInstanceState方法不會被執行

另外,onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數據還原。

(2)原理

狀態改變時,當前Activity被銷燬(onDestroy)後重啓一個全新的Activity(onCreate)(命令行輸出hashcode不一致)導致之前Activity視圖數據丟失。
默認情況下,系統使用Bundle實例狀態來保存有關View中Activity佈局每個對象的信息(例如輸入到EditText對象中的文本值)。因此,如果Activity實例被異常銷燬並重新創建,則佈局狀態會自動恢復到之前的狀態。(注:只有有id的組件自動保存狀態)
但是,Activity可能包含更多要恢復的狀態信息,例如跟蹤Activity中用戶進度的成員變量。此時可以在activity被銷燬之前調用保存每個實例的狀態,開發者可以覆寫onSaveInstanceState()方法(保存當前activity數據)和onRestoreInstanceState()(獲取之前保存的數據在新activity恢復)添加需要額外保存的Activity信息。
onSaveInstanceState()方法接受一個Bundle類型的參數, 開發者可以將狀態數據存儲到這個Bundle對象(鍵值對形式,用於數據的保存和讀取)中,以保證該狀態在onCreate(Bundle)或者onRestoreInstanceState(Bundle)(傳入的Bundle參數是由onSaveInstanceState封裝好的)中恢復。
若向數據庫中插入記錄等,保存持久化數據的操作應該放在onPause()/onStop()中. onSaveInstanceState()方法只適合保存瞬態數據, 比如UI控件的狀態, 成員變量的值等.

(3)生命週期

在這裏插入圖片描述

(4)源碼

4.1)保存Activity狀態

當您的Activity開始停止時,系統會調用,onSaveInstanceState()以便您的Activity可以使用一組鍵值對來保存狀態信息。此方法的默認實現保存有關Activity視圖層次結構狀態的信息,例如EditText小部件中的文本或ListView的滾動位置。
爲了保存Activity的附加狀態信息,您必須實現onSaveInstanceState()並向對象添加鍵值對Bundle。例如:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // 保存用戶自定義的狀態
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
    
    // 調用父類交給系統處理,這樣系統能保存視圖層次結構狀態
    super.onSaveInstanceState(savedInstanceState);
}

4.2)恢復Activity狀態

當您的Activity在之前被破壞後重新創建時,您可以從Bundle系統通過您的Activity中恢復您的保存狀態。這兩個方法onCreate()和onRestoreInstanceState()回調方法都會收到Bundle包含實例狀態信息的相同方法。

4.2.1)在onCreate()方法中恢復Activity狀態

因爲onCreate()調用該方法是否系統正在創建一個新的Activity實例或重新創建一個以前的實例,所以必須Bundle在嘗試讀取之前檢查該狀態是否爲空。如果它爲空,那麼系統正在創建一個Activity的新實例,而不是恢復之前被銷燬的實例。
例如,下面是如何恢復一些狀態數據onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // 記得總是調用父類
   
    // 檢查是否正在重新創建一個以前銷燬的實例
    if (savedInstanceState != null) {
        // 從已保存狀態恢復成員的值
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // 可能初始化一個新實例的默認值的成員
    }
    ...
}
4.2.2)在onRestoreInstanceState()方法中恢復Activity狀態

onRestoreInstanceState()只有在存在保存狀態的情況下才會恢復,因此不需要檢查是否Bundle爲空:

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // 總是調用超類,以便它可以恢復視圖層次超級
    super.onRestoreInstanceState(savedInstanceState);
   
    // 從已保存的實例中恢復狀態成員
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

注:onRestoreInstanceState只有在activity被系統回收,重新創建activity的情況下才會被調用。
調用的例子
比如上期文章提到的onSaveInstanceState的第5種情況橫豎屏切換:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
在這裏onRestoreInstanceState被調用,是因爲屏幕切換時原來的activity被系統回收了,又重新創建了一個新的activity。
不會被調用的例子
而按HOME鍵返回桌面,又馬上點擊應用圖標回到原來頁面時,activity生命週期如下:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
因爲activity沒有被系統回收,因此onRestoreInstanceState沒有被調用。
如果onRestoreInstanceState被調用了,則頁面必然被回收過,則onSaveInstanceState必然被調用過。
由於Activity未被銷燬,Activity實例仍然存在於內存中,Activity的所有信息和狀態數據不會消失, 當Activity重新回到前臺之後, 所有的改變都會得到保留.
onCreate()裏也有Bundle參數,可以用來恢復數據,它和onRestoreInstanceState有什麼區別?
因爲onSaveInstanceState 不一定會被調用,所以onCreate()裏的Bundle參數可能爲空,如果使用onCreate()來恢復數據,一定要做非空判斷。
而onRestoreInstanceState的Bundle參數一定不會是空值,因爲它只有在上次activity被回收了纔會調用。

(5)補充

5.1)如何避免配置改變時Activity重啓?

在清單文件下每個activity註冊時寫上

android:configChanges=“XXX”

比如橫豎屏切換:android:configChanges=“orientation”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章