多瞭解一點Activity
Activity和普通類的重要區別在於其有生命週期的回撥方法,本文意在通過其回撥方法的呼叫,揭開其神祕面紗
1.幾個重要的類
(1)ActivityThread
應用啟動的時候會建立一個獨立的程式,在這個程式裡面會有一個主執行緒,主執行緒首先做的事就是呼叫ActivityThread的main方法,也就是說ActivityThread的main方法是應用程式的入口,相當於java的main方法.
注意:ActivityThread不是執行緒!!!
(2)ApplicationThread
是ActivityThread的私有內部類,繼承自ApplicationThreadNative,ApplicationThreadNative繼承了Binder並實現了IApplicationThread介面,說明了ApplicationThread具有跨程式通訊的能力.
IApplicationThread是一個介面,我們看看它的介紹:
/**
* System private API for communicating with the application. This is given to
* the activity manager by an application when it starts up, for the activity
* manager to tell the application about things it needs to do.
* <p>
* {@hide}
*/
翻譯:這是一個用於和應用通訊的系統私有API,當應用啟動的時候會被應用交給ActivityManager,讓ActivityManager告訴應用要做什麼.
我們看看這個介面裡面的一些方法:
這只是一部分方法,通過上面的部分方法我們能看出這個介面是AMS控制Activity,Service等生命週期的一個介面,ApplicationThread實現了該介面,也就是說AMS通過ApplicationThread實現對Activity,Service等的宣告週期的控制.在程式間通訊中這個時候AMS是Client,ApplicationThread是Server(實際中AMS持有的是ApplicationThread的代理物件ApplicationThreadProxy)
(3)H
H是ActivityThread的內部類,繼承自Handler,簡單說H就是一個處理訊息的類.
我們在上面已經知道AMS通過ApplicationThread來呼叫Activity的生命週期方法,那ApplicationThread又是怎麼呼叫的呢? ApplicationThread是通過H來傳送Message,然後再由H來處理訊息
(4)ActivityClientRecord,ActivityRecord
這兩者都是用來描述Activity的,比如Activity所屬的window,配置資訊等等,二者有不同,但大體上一樣.ActivityClientRecord是在我們應用程式端使用的,ActivityRecord是AMS使用的
(5)Instrumentation
是一個輔助類,用於建立application,開啟Activity,呼叫Activity的各個生命週期的方法.
(6)ViewRoot
ViewRoot可能比較陌生,但是其作用非常重大。所有View的繪製以及事件分發等互動都是通過它來執行或傳遞的。
ViewRoot對應ViewRootImpl類,它是連線WindowManagerService和DecorView的紐帶,View的三大流程(測量(measure),佈局(layout),繪製(draw))均通過ViewRoot來完成。
ViewRoot並不屬於View樹的一份子。從原始碼實現上來看,它既非View的子類,也非View的父類,但是,它實現了ViewParent介面,這讓它可以作為View的名義上的父檢視。RootView繼承了Handler類,可以接收事件並分發,Android的所有觸屏事件、按鍵事件、介面重新整理等事件都是通過ViewRoot進行分發的。同時持有WindowSession通過Binder與WMS通訊,同時持有IWindow作為WSM的回撥介面,用於例如touch事件的回撥。
(7)Window
Window是檢視的承載器,內部持有一個 DecorView,而這個DecorView才是 view 的根佈局。Window是一個抽象類,實際在Activity中持有的是其子類PhoneWindow。PhoneWindow中有個內部類DecorView,通過建立DecorView來載入Activity中設定的佈局R.layout.activity_main。Window 通過WindowManager將DecorView載入其中,並將DecorView交給ViewRoot,進行檢視繪製以及其他互動。
(8)DecorView
DecorView是FrameLayout的子類,它可以被認為是Android檢視樹的根節點檢視。DecorView作為頂級View,一般情況下它內部包含一個豎直方向的LinearLayout,在這個LinearLayout裡面有上下三個部分,上面是個ViewStub,延遲載入的檢視(應該是設定ActionBar,根據Theme設定),中間的是標題欄(根據Theme設定,有的佈局沒有),下面的是內容欄。具體情況和Android版本及主體有關,以其中一個佈局為例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
下面通過程式碼把上面這些類串起來,看看Activity是怎麼顯示出我們設定的佈局來的.
2.程式碼分析
我們只關注過程,不關注細節
(1)從ActivityThread的main方法開始
//ActivityThread.java
public static void main(String[] args) {
......
//就是為主執行緒建立一個looper,用於處理訊息
Looper.prepareMainLooper();
//這裡記住一點,在建立ActivityThread的時候也建立了ApplicationThread
ActivityThread thread = new ActivityThread();
//這裡是重點,主要是與ActivityManagerService互動
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//開始輪詢,處理訊息
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在這個方法裡面涉及到了Handler,具體Handler的原理可以參考:
https://www.jianshu.com/p/d4415033349d
我們這裡著重關注thread.attach(false)方法
private void attach(boolean system) {
...
//AMS執行在一個單獨的程式中,與我們的app程式不一樣,這裡我們通過ActivityManagerNative
//獲得其遠端代理物件
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//mAppThread就是ApplicationThread
//通過上面對ApplicationThread的介紹,我們知道ApplicationThread和AMS關係密切,這裡就是二者的互動
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
(2)第一步中,該關聯的都關聯了.下面該啟動一個Activity了,啟動Activity是由AMS呼叫的,具體反映就是AMS遠端呼叫ApplicationThread的scheduleLaunchActivity方法.然後該方法通過H 的sendMessage方法傳送了一個訊息,我們看看H是怎麼處理這個訊息的.
case LAUNCH_ACTIVITY: {
//這個obj是遠端的AMS傳送過來的,告訴app要啟動哪個Activity
//AMS怎麼知道要啟動哪個Activity呢,這是在啟動該Activity傳入的intent引數中獲得的
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
(3)接著看handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");方法
//ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//先看這個方法做了什麼,看完這個方法的解析再往下看
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//performLaunchActivity看完之後就看這裡
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
performPauseActivityIfNeeded(r, reason);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
performLaunchActivity()方法的主要邏輯(這裡只貼出部分主要程式碼):
①通過反射建立Activity
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//從這裡我們看到Activity的建立用的是Instrumentation輔助類,這裡用到了反射,我們不去細究
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
②建立application物件
//建立application物件,每個應用都有一個application,原來是在這裡建立的.
//具體的建立者也是Instrumentation
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
③建立window物件
//建立Window物件,對Activity內部成員變數進行賦值,把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);
④執行onCreate()方法
//從中我們看出來是用到了Instrumentation來呼叫Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
⑤執行onStart()方法
if (!r.activity.mFinished) {
//這個方法點進去,發現是Instrumentation最終呼叫了Activity的onStart()方法
activity.performStart();
r.stopped = false;
}
我們著重看下第④個方法
這個方法我們點進去看發現就是回撥了我們建立Activity時的onCreate()方法.我們在這個方法裡面通過setContentView()來設定頁面佈局,下面我們看看這個佈局是怎麼顯示出來的
//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
原來是呼叫window的setContentView(),通過本文開頭的解釋,我們知道這個Window就是PhoneWindow.
//PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
//初始化DecorView,這個view前面已經介紹
//在這個方法裡面除了建立decorview,也建立了mContentParent
//mContentParent是我們設定的佈局的父佈局
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//如果有轉場動畫就執行動畫
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//沒有轉場動畫就直接把我們設定的layout新增到mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
注意:執行完PhoneWindow.setContentView()之後,這時view還沒有顯示,只是新增到DecorView.
上面五個主要方法執行完之後,我們再回到handleLaunchActivity()方法,我們接著看handleResumeActivity()方法
(4)handleResumeActivity()
從這個方法的名字可以看出來,這是要呼叫onResume方法了,也就是到了要顯示我們設定的layout的地方了
我們只看這個方法最核心的部分:
顯示view
//ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
//這個時候,Activity.onResume()已經呼叫了,但是現在介面還是不可見的
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//decor對使用者不可見
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//被新增進WindowManager了,但是這個時候,還是不可見的
wm.addView(decor, l);
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
//在這裡,執行了重要的操作,使得DecorView可見
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
看看Activity的makeVisible()方法
//Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
到此DecorView便可見,顯示在螢幕中。但是在這其中,wm.addView(mDecor, getWindow().getAttributes());起到了重要的作用,因為其內部建立了一個ViewRootImpl物件,負責繪製顯示各個子View。
具體來看addView()方法,因為WindowManager是個介面,具體是交給WindowManagerImpl來實現的。由於這裡使用了橋接模式,WindowManagerImpl又交給WindowManagerGlobal 的addView()方法去實現.
//WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......
synchronized (mLock) {
ViewRootImpl root;
//例項化一個ViewRootImpl物件
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
......
try {
//將DecorView交給ViewRootImpl
root.setView(view, wparams, panelParentView);
}catch (RuntimeException e) {
}
}
}
看到其中例項化了ViewRootImpl物件,然後呼叫其setView()方法。其中setView()方法經過一些列折騰,最終呼叫了performTraversals()方法,然後依照下圖流程層層呼叫,完成繪製,最終介面才顯示出來。
至此,結束,長舒一口氣~
相關文章
- 一個Activity顯示多個Activity
- 通過小故事,瞭解多一點何謂 Node 高效能
- 瞭解多型多型
- asio 瞭解的那一點事
- 斷點續傳瞭解一下啊?斷點
- Android Activity生命週期的一點感悟Android
- ? 一文帶你瞭解多檔案混淆加密加密
- 一文帶你瞭解python中的多型Python多型
- Android Activity那點事Android
- 深入瞭解babel(一)Babel
- WebGL基礎(一): 從一個滑鼠畫點開始瞭解原生webGLWeb
- 看過這個,你可能更瞭解指標一點(2)指標
- Range/Content-Range與斷點續傳,瞭解一下?斷點
- 安全技術|一個實驗瞭解多層內網滲透內網
- [譯] android應用開發者,你們真的瞭解Activity的生命週期嗎?Android
- Mybatis【一對多、多對一、多對多】知識要點MyBatis
- java基礎-多執行緒初步瞭解Java執行緒
- Pytest系列(一)初次瞭解
- 微服務之初瞭解(一)微服務
- HTTP 學習瞭解 (一)HTTP
- 瞭解一個React元件React元件
- 瞭解一下Bootstrapboot
- CSS 瞭解一下CSS
- koa,瞭解一下?
- JSX,瞭解一下?JS
- 一文帶你瞭解微服務架構和設計(多圖)微服務架構
- 快速瞭解Java多執行緒,有這一篇就夠了Java執行緒
- 詳解Android中的四大元件之一:Activity詳解Android元件
- 瞭解這一點輕鬆解決Oracle資料庫系統報錯問題Oracle資料庫
- 一文帶你瞭解關鍵點標註 | 資料標註
- 一文帶你瞭解 Flink Forward 柏林站全部重點內容Forward
- 人生苦短,瞭解一下前端必須明白的http知識點前端HTTP
- Python有哪些優缺點,你瞭解嗎?Python
- RecyclerView學習筆記整理(3)解決item中關於跳轉到另一個Activity的問題和判斷多個item進行跳轉到另一個ActivityView筆記
- 探究 | 如何捕獲一個Activity頁面上所有的點選行為
- 一篇瞭解Java反射Java反射
- table 元件瞭解一下?元件
- 一張圖瞭解WiFi 6WiFi
- this.$toast() 瞭解一下?AST