Android點將臺:顏值擔當[-Activity-]

張風捷特烈發表於2019-01-21
溫故而知新,可以為師矣
至美不過回首,往事重重,顧來時,荊棘漫漫,血浸途中。
今銅衣鐵靴,再行來路,任荊棘漫漫,唯落綠葉殘枝。
                             ----張風捷特烈 
複製程式碼

零、前言

1.本文的知識點

1).Acticity生命週期測試與分析
2).Activity間的資料傳遞
3).Activity的四種啟動模式詳細分析
4).Activity的跳轉動畫
5).Acticity的生命週期原始碼分析


2.Activity總覽

Activity.png

類名:Activity      父類:ContextThemeWrapper      修飾:public
包名:android.app   依賴類個數:115
內部類/介面個數:5
原始碼行數:7754       原始碼行數(除註釋):3197
屬性個數:20       方法個數:282       public方法個數:245
複製程式碼

Activity繼承關係.png


一、生命週期測試:

1.開啟與關閉

開啟與關閉.png

1.1.開啟LifeCycleActivity:
2019-01-19 14:06:10.614 : LifeCycleActivity--onCreate: 
2019-01-19 14:06:10.617 : LifeCycleActivity--onStart: 
2019-01-19 14:06:10.634 : LifeCycleActivity--onResume: 
複製程式碼
1.2.返回鍵關閉LifeCycleActivity:
2019-01-19 14:08:52.675 : LifeCycleActivity--onPause: 
2019-01-19 14:08:53.247 : LifeCycleActivity--onStop: 
2019-01-19 14:08:53.249 : LifeCycleActivity--onDestroy: 
複製程式碼

2.跳轉普通Activity時兩個Activity的生命週期

普通跳轉與返回.png

2.1.在LifeCycleActivity中開啟CommonActivity:
2019-01-19 14:15:57.454 : LifeCycleActivity--onPause: 
2019-01-19 14:15:57.495 : CommonActivity--onCreate: 
2019-01-19 14:15:57.497 : CommonActivity--onStart: 
2019-01-19 14:15:57.501 : CommonActivity--onResume: 
2019-01-19 14:15:58.256 : LifeCycleActivity--onStop: 
複製程式碼
2.2.CommonActivity返回鍵到LifeCycleActivity:
2019-01-19 14:19:09.511 : CommonActivity--onPause: 
2019-01-19 14:19:09.527 : LifeCycleActivity--onRestart: 
2019-01-19 14:19:09.528 : LifeCycleActivity--onStart: 
2019-01-19 14:19:09.529 : LifeCycleActivity--onResume: 
2019-01-19 14:19:09.963 : CommonActivity--onStop: 
2019-01-19 14:19:09.963 : CommonActivity--onDestroy: 
複製程式碼

3.跳轉對話方塊Activity時兩個Activity的生命週期

開啟對話方塊Activity.png

3.1.在LifeCycleActivity中開啟DialogActivity:
2019-01-19 14:43:32.842 : LifeCycleActivity--onPause: 
2019-01-19 14:43:32.908 : DialogActivity--onCreate: 
2019-01-19 14:43:32.910 : DialogActivity--onStart: 
2019-01-19 14:43:32.912 : DialogActivity--onResume:
複製程式碼
3.2.DialogActivity返回鍵到LifeCycleActivity:
2019-01-19 14:44:45.771 : DialogActivity--onPause: 
2019-01-19 14:44:45.812 : LifeCycleActivity--onResume: 
2019-01-19 14:44:45.874 : DialogActivity--onStop: 
2019-01-19 14:44:45.874 : DialogActivity--onDestroy: 
複製程式碼

4.LifeCycleActivity旋轉螢幕(相當於關閉再開啟):

橫豎屏切換時.png

2019-01-19 14:46:28.619 : LifeCycleActivity--onPause: 
2019-01-19 14:46:28.639 : LifeCycleActivity--onStop: 
2019-01-19 14:46:28.639 : LifeCycleActivity--onDestroy: 
2019-01-19 14:46:28.743 : LifeCycleActivity--onCreate: 
2019-01-19 14:46:28.744 : LifeCycleActivity--onStart: 
2019-01-19 14:46:28.751 : LifeCycleActivity--onResume: 
複製程式碼

activity生命週期測試總覽(電腦上檢視原圖效果最佳)

activity生命週期.gif


5、儲存資料:

onStop之前,會呼叫onSaveInstanceState 其中有一個Bundle物件可以用來儲存資料
該物件便是onCreate中的Bundle物件savedInstanceState,下圖旋轉屏時使用onSaveInstanceState

onSaveInstanceState.png

override
fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.ac_lifecycle)
    title = "LifeCycleActivity"
    if (savedInstanceState != null) {
        title = savedInstanceState.getString("name")
    }
}

override fun onSaveInstanceState(outState: Bundle?) {
    super.onSaveInstanceState(outState)
    Log.e(TAG, "LifeCycleActivity--onSaveInstanceState: ")
}
複製程式碼

二、生命週期方法解釋:

之所謂生命週期,就是從生到死有明確的回撥函式
函式在不同狀態下由安卓框架層進行回撥,簡化開發流程。
讓開發者使用時只需要關注Activity狀態,根據狀態構建邏輯,而無需深入底層實現。
Activity主要有7個生命週期回撥函式,如下,是最經典的Activity生命週期圖示:

Activity生命週期

建立:onCreate():
    |---可用於初始化工作,如setContentView介面資源、初始化資料
啟動:onStart():
    |---可見但無法互動
恢復:onResume():
    |---恢復播放動畫、音樂、視訊等
暫停:onPause():
    |---可做資料儲存、停止動畫、音樂、視訊等
停止:onStop():
    |---此時Activity不可見,可做視情況做些重量級回收工作,避免被Killed
銷燬:onDestroy():
    |---回收工作、資源釋放
重現:onRestart():
    |---可做一些恢復工作
複製程式碼

三、Activity間的資料傳遞

傳遞資料.png

1.實體類:Person
/**
 * 作者:張風捷特烈
 * 時間:2018/4/26:12:13
 * 郵箱:1981462002@qq.com
 * 說明:簡單實體Person
 */
class Person(var name: String?, var age: Int) : Serializable {
    override fun toString(): String {
        return "Person{" +
                "name='" + name + '\''.toString() +
                ", age=" + age +
                '}'.toString()
    }
}
複製程式碼

2.傳遞基本資料型別、Serializable物件、Parcelable物件

注意Bundle不能傳遞過大資料

傳遞基本資料型別和Serializable物件,Parcelable物件.png

---->[FromActivity點選]-----------
id_btn_for_result.setOnClickListener {
    val intent = Intent(this, ToActivity::class.java)
    val bundle = Bundle()
    bundle.putSerializable("person", Person("form", 24))
    val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.wall_a)
    bundle.putParcelable("bitmap", bitmap)
    intent.putExtra("from", bundle)
    intent.putExtra("title", "張風捷特烈")
    startActivity(intent)

---->[ToActivity接收使用]-----------
val title = intent.getStringExtra("title")
if (title != null) {
    this.title = title
}
val extra = intent.getBundleExtra("from")
if (extra != null) {
    val from = extra.get("person") as Person
    val icon = extra.get("bitmap") as Bitmap
    id_tv_result.text = from.toString()
    id_iv_icon.setImageBitmap(icon)
}
複製程式碼

3.FromActivity使用startActivityForResult開啟ToActivity接返回值

startActivityForResult.png

startActivityForResult測試.png

---->[FromActivity]-----------
companion object {
    private const val DATA_CODE = 0x0001
}
//點選時
id_btn_for_result.setOnClickListener {
    val intent = Intent(this, ToActivity::class.java)
    startActivityForResult(intent, DATA_CODE)
}
//回撥
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    when (requestCode) {
        DATA_CODE -> if (resultCode == Activity.RESULT_OK) {
            val dataFormTarget = data.getStringExtra("data")
            val personData = data.getBundleExtra("To")
            val person = personData.get("person") as Person
            id_tv_result_back.text = ("dataFormTarget:" + dataFormTarget
                    + "\nperson:" + person.toString())
        }
    }
}

---->[ToActivity傳遞資料給FromActivity]-----------
private fun backWithData() {
    val jt = Person("捷特", 24)
    val intent = Intent()
    intent.putExtra("data", "我是ToActivity的資料")
    val bundle = Bundle()
    bundle.putSerializable("person", jt)
    intent.putExtra("To", bundle)
    setResult(Activity.RESULT_OK, intent)
}
複製程式碼

4.開啟相簿並設定圖片

開啟相簿選擇圖片.png

//點選圖片
id_iv_icon.setOnClickListener {
    val intent = Intent(Intent.ACTION_PICK)
    intent.type = "image/*";
    startActivityForResult(intent, 0)
}

//處理結果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 0 && resultCode == Activity.RESULT_OK) {//成功
        val selectedImage = data?.data ?: return
        val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
        val cursor = contentResolver.query(// 獲取選擇照片的資料檢視
            selectedImage, filePathColumn, null, null, null
        )
        cursor.moveToFirst()
        // 從資料檢視中獲取已選擇圖片的路徑
        val columnIndex = cursor.getColumnIndex(filePathColumn[0])
        val picturePath = cursor.getString(columnIndex)
        cursor.close()
        id_iv_icon.setImageBitmap(BitmapFactory.decodeFile(picturePath))
    }
}
複製程式碼

三、Activity的四種啟動模式

Activity任務棧:Activity的活動序列

standard:標準棧
singleTop:頂複用棧
singleTask:物件唯一棧
singleInstance:單獨例項棧
複製程式碼

1.standard: 標準棧

當啟動一個Activity,建立該Activity的新例項。入棧處於棧頂
測試:Activity1、2皆為standard

standard.png

standard.gif

依次開啟Activity1、2、2、1、2
 E/TASK_ID: Activity1 Task id is 89
 E/TASK_ID: Activity2 Task id is 89
 E/TASK_ID: Activity2 Task id is 89
 E/TASK_ID: Activity1 Task id is 89
 E/TASK_ID: Activity2 Task id is 89
依次返回
 E/TASK_ID: Activity2 銷燬
 E/TASK_ID: Activity1 銷燬
 E/TASK_ID: Activity2 銷燬
 E/TASK_ID: Activity2 銷燬
 E/TASK_ID: Activity1 銷燬
複製程式碼

2:singleTop模式:頂複用棧

在啟動活動時若棧頂已經是該Activity,則直接使用它,不建立例項
測試:Activity1為standard, Activity2 為singleTop

singleTop.png

singleTop.gif

依次開啟Activity1、2、2、1、2
E/TASK_ID: Activity1 Task id is 82
E/TASK_ID: Activity2 Task id is 82
E/TASK_ID: Activity1 Task id is 82
E/TASK_ID: Activity2 Task id is 82
依次返回
E/TASK_ID: Activity2 銷燬
E/TASK_ID: Activity1 銷燬
E/TASK_ID: Activity2 銷燬
E/TASK_ID: Activity1 銷燬
複製程式碼

3:singleTask模式:物件唯一棧

整個棧中沒有相同的例項,兩次相同例項之間的Activity會被殺死(夠霸道,我喜歡)
測試:Activity1為standard, Activity2 為singleTask

singleTask.png

singleTask.gif

依次開啟Activity1、2、2、1、2
E/TASK_ID: Activity1 Task id is 94
E/TASK_ID: Activity2 Task id is 94
E/TASK_ID: Activity1 Task id is 94
E/TASK_ID: Activity1 銷燬
依次返回
E/TASK_ID: Activity2 銷燬
E/TASK_ID: Activity1 銷燬
複製程式碼

4:singleInstance 單獨例項棧

啟用一個新的活動棧來管理這個活動(夠豪,夠任性)
測試:Activity1為standard, Activity2 singleInstance

singleInstance.png

singleInstance.gif

依次開啟Activity1、2、2、1、2
 E/TASK_ID: Activity1 Task id is 115
 E/TASK_ID: Activity2 Task id is 116
 E/TASK_ID: Activity1 Task id is 115
依次返回
 E/TASK_ID: Activity2 銷燬
 E/TASK_ID: Activity1 銷燬
 E/TASK_ID: Activity1 銷燬
複製程式碼

注意一點:
singleTask模式和singleTop模式時,非第一次啟動,不會呼叫onCreate方法!   
但會走onNewIntent方法
複製程式碼

四、Activity的跳轉動畫

這裡只是簡單的四個平移動畫,需要的更酷炫的效果道理是一樣的
關於動畫的更多知識,這裡不廢話了,可詳見:Android 動畫 Animator 家族使用指南

預設 修改
Android點將臺:顏值擔當[-Activity-]
Android點將臺:顏值擔當[-Activity-]

1.程式碼實現Activity跳轉

Activity跳轉動畫.png

/**
 * 作者:張風捷特烈<br></br>
 * 時間:2019/1/20/020:18:25<br></br>
 * 郵箱:1981462002@qq.com<br></br>
 * 說明:紅色Activity
 */
class RedActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view = View(this)
        view.setBackgroundColor(Color.RED)
        title = "RedActivity"
        view.setOnClickListener { v ->
            startActivity(Intent(this, BlueActivity::class.java))
            overridePendingTransition(R.anim.open_enter, R.anim.open_exit);
        }
        setContentView(view)
    }
    override fun onBackPressed() {
        super.onBackPressed()
        overridePendingTransition(R.anim.open_enter, R.anim.open_exit);
    }
}

/**
 * 作者:張風捷特烈<br></br>
 * 時間:2019/1/20/020:18:25<br></br>
 * 郵箱:1981462002@qq.com<br></br>
 * 說明:綠色Activity
 */
class BlueActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view = View(this)
        view.setBackgroundColor(Color.BLUE)
        title = "BlueActivity"
        view.setOnClickListener { v ->
            startActivity(Intent(this, RedActivity::class.java))
            overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
        }
        setContentView(view)
    }
    override fun onBackPressed() {
        super.onBackPressed()//右移入---右移出
                overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
    }
}
複製程式碼

2.跳轉動畫
---->[open_enter.xml]----------------------
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator">
    <!--左移入-->
    <translate
            android:duration="500"
            android:fromXDelta="100%p"
            android:toXDelta="0%p"/>
</set>

---->[open_exit.xml]----------------------
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator">
    <!--左移出-->
    <translate
            android:duration="500"
            android:fromXDelta="0%p"
            android:toXDelta="-100%p"/>
</set>

---->[close_enter.xml----------------------
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator">
    <!--右移入-->
    <translate
            android:duration="500"
            android:fromXDelta="-100%p"
            android:toXDelta="0%p"/>
</set>

---->[close_exit.xml]----------------------
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator">
    <!--右移出-->
    <translate
            android:duration="500"
            android:fromXDelta="0%p"
            android:toXDelta="100%p"/>
</set>
複製程式碼

這樣就可以了


3.另外還可以配置動畫的style

用起來比在程式碼裡方便些

<!--配置Activity進出動畫-->
<style name="TranAnim_Activity" parent="@android:style/Animation.Activity">
    <item name="android:activityOpenEnterAnimation">@anim/open_enter</item>
    <item name="android:activityOpenExitAnimation">@anim/open_exit</item>
    <item name="android:activityCloseEnterAnimation">@anim/close_enter</item>
    <item name="android:activityCloseExitAnimation">@anim/close_exit</item>
</style>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowAnimationStyle">@style/TranAnim_Activity</item>
</style>
複製程式碼

五、Acticity的啟動原始碼分析

一直想總結一下Activity的啟動流程(),這裡從Activity的生命週期入手
本文所講述的啟動流程主要是ActivityThread的H在接收到訊息之後,即handleMessage
至於訊息如何傳遞過來的將在跨程式通訊篇講述

Activity繼承關係.png


1.誰是幕後黑手?
翻一下原始碼可以看出Context只是一個抽象類,定義了很多抽象方法 
而ContextWrapper作為實現類將所有的工作甩手給了一個mBase的Context成員變數
ContextThemeWrapper寥寥百行程式碼,也不會是幕後黑手,現在臺面上只有mBase
複製程式碼

ContextWrapper.png


2.Activity是如何建立的?

相信應該沒有人去new Activity(),framework 層是如何建立Activity的例項呢?

Activity例項化.png

 ---->[ActivityThread]-------
 final H mH = new H();
 
 ---->[ActivityThread$H#handleMessage]-------
public void handleMessage(Message msg) {
 switch (msg.what) {
      case LAUNCH_ACTIVITY: {//啟動Activity
          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
          //r:記錄Activity的一些描述資訊
          final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
          //通過r來獲取包資訊
          r.packageInfo = getPackageInfoNoCheck(
                  r.activityInfo.applicationInfo, r.compatInfo);
          //開啟的核心方法(劃重點)
          handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

 ---->[ActivityThread#handleLaunchActivity]-------
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    //在這裡返回來Activity的物件
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        //略...
}

 ---->[ActivityThread#performLaunchActivity]-------
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 //略...
   ComponentName component = r.intent.getComponent();
   Activity activity = null;
   try {
       java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
       //此處可見是mInstrumentation建立的Activity
       activity = mInstrumentation.newActivity(
               cl, component.getClassName(), r.intent);
       StrictMode.incrementExpectedActivityCount(activity.getClass());
 //略...
    return activity;
    }
    
    
 ---->[Instrumentation#newActivity]-------
 public Activity newActivity(ClassLoader cl, String className,
         Intent intent)
         throws InstantiationException, IllegalAccessException,
         ClassNotFoundException {
    //通過類載入器生成Activity例項
     return (Activity)cl.loadClass(className).newInstance();
 }
 
複製程式碼

3.Application例項化及onCreate()方法呼叫

實現移到剛才建立Activity的performLaunchActivity方法

Application例項化.png

 ---->[ActivityThread#performLaunchActivity]-------
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 //略...
   ComponentName component = r.intent.getComponent();
   Activity activity = null;
   try {
       java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
       activity = mInstrumentation.newActivity(
               cl, component.getClassName(), r.intent);
       StrictMode.incrementExpectedActivityCount(activity.getClass());
 //略...
    try {
    //建立Activity之後通過ActivityClientRecord的packageInfo物件的makeApplication
    //來建立Application,packageInfo是一個LoadedApk類的物件
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//略...
    }

 ---->[LoadedApk#makeApplication]-------
public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        Application app = null;
//略...

        try {
            java.lang.ClassLoader cl = getClassLoader();
//略...
            //這裡ContextImpl出場了
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //這裡通過mInstrumentation的newApplication方法建立Application物件
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //將建立的Application設定到appContext上
            appContext.setOuterContext(app);
        }
        //略...
        //mActivityThread將當前app加入mAllApplications列表中
        mActivityThread.mAllApplications.add(app);
        mApplication = app;
        if (instrumentation != null) {
            try {
            //這時呼叫application的OnCreate方法
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }
        return app;
    }

 ---->[Instrumentation#newApplication]-------
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
        //也是通過反射獲取Application例項
    return newApplication(cl.loadClass(className), context);
}

 ---->[Instrumentation#callApplicationOnCreate]-------
 public void callApplicationOnCreate(Application app) {
     app.onCreate();//直接呼叫onCreate onCreate
 }
    

複製程式碼

4.Activity的Context的建立及onCreate()方法的呼叫

Activity的oncreate.png

 ---->[ActivityThread#performLaunchActivity]-------
 if (activity != null) {
    Context appContext = createBaseContextForActivity(r, activity);
    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    //Activity的一些配置資訊
    Configuration config = new Configuration(mCompatConfiguration);
    if (r.overrideConfig != null) {
        config.updateFrom(r.overrideConfig);
    }
    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
            + r.activityInfo.name + " with config " + config);
    Window window = null;
    if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
        window = r.mPendingRemoveWindow;
        r.mPendingRemoveWindow = null;
        r.mPendingRemoveWindowManager = null;
    }
    //將Activity和window繫結
    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);

    if (customIntent != null) {
        activity.mIntent = customIntent;
    }
    r.lastNonConfigurationInstances = null;
    activity.mStartedActivity = false;
    int theme = r.activityInfo.getThemeResource();
    if (theme != 0) {
        activity.setTheme(theme);
    }

    activity.mCalled = false;
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }


---->[ActivityThread#createBaseContextForActivity]-------
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
//略...
    //看這裡appContext是ContextImpl類物件,Activity的Context幕後黑手出現了
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.token, displayId, r.overrideConfig);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;

    final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
    // For debugging purposes, if the activity's package name contains the value of
    // the "debug.use-second-display" system property as a substring, then show
    // its content on a secondary display if there is one.
    String pkgName = SystemProperties.get("debug.second-display.pkg");
    if (pkgName != null && !pkgName.isEmpty()
            && r.packageInfo.mPackageName.contains(pkgName)) {
        for (int id : dm.getDisplayIds()) {
            if (id != Display.DEFAULT_DISPLAY) {
                Display display =
                        dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
                baseContext = appContext.createDisplayContext(display);
                break;
            }
        }
    }
    return baseContext;
}


---->[ContextImpl#createActivityContext]-------
static ContextImpl createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration) {
    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
    return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
            null, overrideConfiguration, displayId);
}

---->[Instrumentation#callActivityOnCreate]-------
public void callActivityOnCreate(Activity activity, Bundle icicle,
        PersistableBundle persistentState) {
    prePerformCreate(activity);
    activity.performCreate(icicle, persistentState);
    postPerformCreate(activity);
}

---->[Activity#performCreate]-------
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

---->[Activity#attach]-----------------
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
    //這裡的Window實現類是PhoneWindow
    mWindow = new PhoneWindow(this, window);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();
    
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                    Looper.myLooper());
        }
    }

    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
 }
複製程式碼

5.Activity的佈局載入

setContentView我們再熟悉不過了,看一下Activity原始碼是如何載入的

新增布局.png

---->[Activity#setContentView]-----------------
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

//可見是通過Window的setContentView來載入佈局的,
//通過attach方法知道這個window物件是PhoneWindow類

---->[PhoneWindow#setContentView]-----------------
 @Override
 public void setContentView(View view, ViewGroup.LayoutParams params) {
     if (mContentParent == null) {
         installDecor();//初始化DecorView
     } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
         mContentParent.removeAllViews();
     }
     if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
         view.setLayoutParams(params);
         final Scene newScene = new Scene(mContentParent, view);
         transitionTo(newScene);
     } else {
         mContentParent.addView(view, params);
     }
     mContentParent.requestApplyInsets();
     final Callback cb = getCallback();
     if (cb != null && !isDestroyed()) {
         cb.onContentChanged();
     }
 }

---->[PhoneWindow#installDecor]-----------------
 private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            //通過DecorView來建立mContentParent
            mContentParent = generateLayout(mDecor);
         //對mDecor進行處理,略...
         
---->[PhoneWindow#generateDecor]-----------------
    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

>DecorView何許人也?
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
可見是一個幀佈局FrameLayout,最頂層的檢視
複製程式碼

DecorView.png


6.Activity的onResume和onRestart方法的回撥

onCreate分析了,onResume基本是差不多,還是在H類中的
handleMessage中處理資訊,當標識為RESUME_ACTIVITY時,呼叫handleResumeActivity

onResume.png

---->[ActivityThread#handleMessage]-------
case RESUME_ACTIVITY:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
    SomeArgs args = (SomeArgs) msg.obj;
    handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
            args.argi3, "RESUME_ACTIVITY");
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

---->[ActivityThread#handleResumeActivity]-------
 final void handleResumeActivity(IBinder token,
         boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
     ActivityClientRecord r = mActivities.get(token);
     if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
         return;
     }

     // If we are getting ready to gc after going to the background, well
     // we are back active so skip it.
     unscheduleGcIdler();
     mSomeActivitiesChanged = true;

     // TODO Push resumeArgs into the activity for consideration
     r = performResumeActivity(token, clearHide, reason);

---->[ActivityThread#performResumeActivity]-------
public final ActivityClientRecord performResumeActivity(IBinder token,
        boolean clearHide, String reason) {
    ActivityClientRecord r = mActivities.get(token);
            //略...
            r.activity.performResume();
            
---->[Activity#performResume]-------
final void performResume() {
    performRestart();//可見是先呼叫了Restart方法
    mFragments.execPendingActions();
    mLastNonConfigurationInstances = null;
    mCalled = false;
    // mResumed is set by the instrumentation
    mInstrumentation.callActivityOnResume(this);
    if (!mCalled) {

---->[Activity#performRestart]-------
final void performRestart() {
        //略...
        mInstrumentation.callActivityOnRestart(this);
//又看到老朋友mInstrumentation了,
//可以看到Activity的生命週期由mInstrumentation全權負責  
//就連呼叫本類的一個onRestart方法都要mInstrumentation來轉手

---->[Instrumentation#callActivityOnRestart]-------
public void callActivityOnRestart(Activity activity) {
    activity.onRestart();
}

---->[Instrumentation#callActivityOnResume]-------
public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();
    
    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i<N; i++) {
                final ActivityMonitor am = mActivityMonitors.get(i);
                am.match(activity, activity, activity.getIntent());
            }
        }
    }
}

---->[ActivityThread#handleResumeActivity]-------
//回撥onResume後進行window介面顯示
 if (r.window == null && !a.mFinished && willBeVisible) {
     r.window = r.activity.getWindow();
     View decor = r.window.getDecorView();
     decor.setVisibility(View.INVISIBLE);
     ViewManager wm = a.getWindowManager();
     WindowManager.LayoutParams l = r.window.getAttributes();
     a.mDecor = decor;
     l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
     l.softInputMode |= forwardBit;
     if (r.mPreserveWindow) {
         a.mWindowAdded = true;
         r.mPreserveWindow = false;
         // Normally the ViewRoot sets up callbacks with the Activity
         // in addView->ViewRootImpl#setView. If we are instead reusing
         // the decor view we have to notify the view root that the
         // callbacks may have changed.
         ViewRootImpl impl = decor.getViewRootImpl();
         if (impl != null) {
             impl.notifyChildRebuilt();
         }
     }
     if (a.mVisibleFromClient && !a.mWindowAdded) {
         a.mWindowAdded = true;
         wm.addView(decor, l);
     }

複製程式碼

7.Activity的onStop方法的回撥

onstop.png

---->[ActivityThread#handleMessage]-------
case STOP_ACTIVITY_SHOW: {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
    SomeArgs args = (SomeArgs) msg.obj;
    handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;


---->[ActivityThread#handleStopActivity]-------
private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
    ActivityClientRecord r = mActivities.get(token);
    StopInfo info = new StopInfo();
    performStopActivityInner(r, info, show, true, "handleStopActivity");
//略...
    updateVisibility(r, show);
//略...
}

---->[ActivityThread#performStopActivityInner]-------
private void performStopActivityInner(ActivityClientRecord r,
        StopInfo info, boolean keepShown, boolean saveState, String reason) {
//略...
    // One must first be paused before stopped...
    performPauseActivityIfNeeded(r, reason);//暫停邏輯
//略...
    if (!keepShown) {
    try {
        // Now we are idle.
        r.activity.performStop(false /*preserveWindow*/);//停止
    
---->[ActivityThread#performPauseActivityIfNeeded]-------
private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
//略...
        mInstrumentation.callActivityOnPause(r.activity);

---->[Instrumentation#callActivityOnPause]-------
public void callActivityOnPause(Activity activity) {
    activity.performPause();
}

---->[Activity#callActivityOnPause]-------
final void performPause() {
    mDoReportFullyDrawn = false;
    mFragments.dispatchPause();
    mCalled = false;
    onPause();
    //略...
}
複製程式碼

8.Activity的onDestroy方法的回撥

ondestory.png

---->[ActivityThread#handleMessage]-------
case DESTROY_ACTIVITY:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
    handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
            msg.arg2, false);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

---->[ActivityThread#performDestroyActivity]-------
private void handleDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance) {
    ActivityClientRecord r = performDestroyActivity(token, finishing,
            configChanges, getNonConfigInstance);


---->[ActivityThread#performDestroyActivity]-------
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        ActivityClientRecord r = mActivities.get(token);
             //略...
            performPauseActivityIfNeeded(r, "destroy");//暫停
            if (!r.stopped) {//如果沒stop,先stop
                try {
                    r.activity.performStop(r.mPreserveWindow);
            //略...
            try {
                r.activity.mCalled = false;
                mInstrumentation.callActivityOnDestroy(r.activity);
複製程式碼

後記:捷文規範

1.本文成長記錄及勘誤表
專案原始碼 日期 附錄
V0.1--無 2018-1-22

釋出名:Android點將臺:顏值擔當[-Activity-]
捷文連結:juejin.im/post/5c4584…

2.更多關於我
筆名 QQ 微信
張風捷特烈 1981462002 zdl1994328

我的github:github.com/toly1994328
我的簡書:www.jianshu.com/u/e4e52c116…
我的簡書:www.jianshu.com/u/e4e52c116…
個人網站:www.toly1994.com

3.宣告

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援

icon_wx_200.png

相關文章