Android?Activity?View加載與繪制流程深入刨析源碼
1.App的啟動(dòng)流程,從startActivity到Activity被創(chuàng)建。
這個(gè)流程主要是ActivityThread和ActivityManagerService之間通過(guò)binder進(jìn)行通信來(lái)完成。
ActivityThread可以拿到AMS 的BinderProxy。AMS可以拿到ActivityThread的BinderProxy ApplicationThread。這樣雙方就可以互相通訊了。
當(dāng)ApplicationThread 接收到AMS的Binder調(diào)用后,會(huì)通過(guò)handler機(jī)制來(lái)執(zhí)行對(duì)應(yīng)的操作。
可以這樣說(shuō)handler和binder是android framework重要的兩塊基石。
而Activity的創(chuàng)建就是在ActivityThread中調(diào)用handleLaunchActivity方法實(shí)現(xiàn)。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//創(chuàng)建一個(gè)Activity,并調(diào)用生命周期onCreate方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
//如果Activity成功創(chuàng)建,則會(huì)調(diào)用生命周期onResume方法。
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
}2.接下來(lái)看performLaunchActivity。創(chuàng)建一個(gè)Activity。
如果Activity創(chuàng)建成功,先調(diào)用activity.attach(),在這個(gè)方法中,創(chuàng)建了Activity的PhoneWindow實(shí)例。
然后mInstrumentation.callActivityOnCreate,調(diào)用了Activity的onCreate生命周期方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
try {
// Instrumentation mInstrumentation;
//拿到類(lèi)加載器,最后會(huì)通過(guò)反射的方式創(chuàng)建Activity對(duì)象
//(Activity) cl.loadClass(className).newInstance();
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
if(activity!=null){
//調(diào)用Activity.attach,
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
}
//通過(guò)這個(gè)方法調(diào)用了Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
// activity.performCreate(icicle, persistentState);
// onCreate(icicle, persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
//activity.performCreate(icicle);
// onCreate(icicle);
}
return activity;
} final void attach(...){
//初始化了window的子類(lèi) 初始化了PhoneWindow,PhoneWindow中有一個(gè)Decoview對(duì)象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//mWindowManager 是WindowManagerImpl的實(shí)例。
mWindowManager = mWindow.getWindowManager();
}3.setContentView,布局加載流程:
在Activity.onCreate方法中,我們通過(guò)setContentView(R.layout.activity_main);就能在界面顯示,那android是怎么做到的?
1)調(diào)用installDecor(),初始化Decorview和mContentParent。
2)mLayoutInflater.inflate(),將布局文件,解析成android中對(duì)應(yīng)的View。
//getWindow拿到的是PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
}
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//初始化Decorview和mContentParent
installDecor();
}
//加載并解析傳進(jìn)來(lái)的布局文件,并add到mContentParent上。
//這個(gè)操作比較耗時(shí),因?yàn)橐獜膞ml文件中解析,然后再通過(guò)反射的方式生成Java中的View對(duì)象。
//所以在recyclerView和listView中要對(duì)這一步進(jìn)行緩存優(yōu)化,
mLayoutInflater.inflate(layoutResID, mContentParent);
}先看installDecor(),通過(guò)generateDecor() new了一個(gè)DecoView實(shí)例并賦值給了mDecor,
mDecor是PhoneView的一個(gè)變量。
//mDecorView是PhoneWindow的一個(gè)成員變量
private DecorView mDecor;
private void installDecor() {
//創(chuàng)建mDecor
if (mDecor == null) {
mDecor = generateDecor(-1);
//new DecorView(context, featureId, this, getAttributes());
}
//創(chuàng)建mContentParent,將創(chuàng)建的DecorView作為參數(shù)傳遞
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}通過(guò)generateLayout(decor) 加載了一個(gè)系統(tǒng)的layout文件,在android.jar--res--layout目錄下。
在mDecor.onResourcesLoaded方法中加載了這個(gè)布局,并添加到了mDecor中。
DecorView繼承自FrameLayout,是一個(gè)真正的view。
然后通過(guò)findViewbyid,找到了一個(gè)ViewGoup,可以看下面的布局文件。
ID_ANDROID_CONTENT = com.android.internal.R.id.content; ,并把這個(gè)返回出去了。
這個(gè)view 就installDecor()方法中的mContentParent()
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
//這個(gè)布局文件就在android.jar--res--layout目錄下。
layoutResource = R.layout.screen_simple;
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
//這個(gè)R.id.content就是定義在screen_simple中的一個(gè)FrameLayout
// android:id="@android:id/content"
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
....
return contentParent;
}
//將布局R.layout.screen_simple 加載成view,并添加到DecorView中
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
......
final View root = inflater.inflate(layoutResource, null);
......
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}//R.layout.screen_simple
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<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:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>總結(jié):至此 installDecor();已經(jīng)完成。主要是創(chuàng)建了mDecorView,并加載了一個(gè)系統(tǒng)的布局,R.layout.screen_simple,
將加載得到的View添加到了mDecorView中,并findViewById(R.id.content)的到的View賦值給了mParentContent。
回到setContentView中看第二行代碼:
layoutResID 就是傳入的布局文件id,mContentParent就是加載的系統(tǒng)的布局文件中id為“content”的view
mLayoutInflater.inflate(layoutResID, mContentParent);
//加載并解析傳進(jìn)來(lái)的布局文件,并add到mContentParent上。
mLayoutInflater.inflate(layoutResID, mContentParent);
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//通過(guò) Resource 得到了一個(gè)XML解析器。
final XmlResourceParser parser = res.getLayout(resource);
try {
//解析我們自定義的layout,并添加到mParentContent上。
//將xml中定義的ViewGroup和View解析成Java對(duì)象。
//這塊代碼會(huì)單獨(dú)寫(xiě)文章講解
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}至此上面的關(guān)系可以總結(jié)為:
Activity-->PhoneWindow-->mDecorView-->addView(R.layout.screen.simple)-->
R.id.content-->mParentContent-->addView(R.layout.activity.main)
我們自己寫(xiě)的layout已經(jīng)添加到了系統(tǒng)的DecorView中。
4.我們知道View有三個(gè)重要的方法onMeasure,onLayout,onDraw,
那這些方法是在哪里調(diào)用的?我們創(chuàng)建的View是如何添加到屏幕上的呢?
回到handleLaunchActivity方法中,還有一個(gè)handleResumeActivity,通過(guò)performResumeActivity 會(huì)執(zhí)行Activity的onResume生命周期方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//創(chuàng)建一個(gè)Activity,并調(diào)用生命周期onCreate方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
//如果Activity成功創(chuàng)建,則會(huì)調(diào)用生命周期onResume方法。
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
}
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
//執(zhí)行Activity onResume生命周期方法
ActivityClientRecord r = performResumeActivity(token, clearHide);
if(r!=null){
final Activity a = r.activity;
//通過(guò)上面代碼我們知道 window 是PhoneWindow
r.window = r.activity.getWindow();
//拿到DecorView
View decor = r.window.getDecorView();
//wm 是 WindowManagerImpl
ViewManager wm = a.getWindowManager();
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//關(guān)鍵代碼,將decorView 添加到wm中
wm.addView(decor, l);
}
}
}
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
//執(zhí)行Activity onStart onResume方法
ActivityClientRecord r = mActivities.get(token);
r.activity.performResume();
return r;
}//Activity中 onStart onResume 生命周期方法
final void performResume(boolean followedByPause, String reason) {
performRestart(true /* start */, reason);
mInstrumentation.callActivityOnResume(this);
}
final void performRestart(boolean start, String reason) {
if (start) {
performStart(reason);
}
}
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
}執(zhí)行完onResume方法后:
wm.addView(decor, l);將Activity的DecorView添加到了wm中。
ViewManager wm = a.getWindowManager();
ViewManager 是一個(gè)抽象類(lèi),實(shí)例是WindowManagerImpl。
在WindowManagerImpl中通過(guò)單例模式獲取了一個(gè)WindowManagerGlobal對(duì)象。
既然是單例模式獲取的對(duì)象,也就在一個(gè)進(jìn)程,ActivityThread 主進(jìn)程中 只有一個(gè)實(shí)例。
//WindowManagerImpl.java addView 方法
//WindowManagerGlobal 是一個(gè)單例模式,在ActivityThread 主進(jìn)程中 只有一個(gè)實(shí)例。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerGlobal.java
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
ViewRootImpl root;
........
root = new ViewRootImpl(view.getContext(), display);
.........
view.setLayoutParams(wparams);
//將decorView ViewRootImpl 存到集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}在WindowManagerGlobal中創(chuàng)建了一個(gè)ViewRootImpl對(duì)象。這是很重要的一個(gè)對(duì)象。
將傳進(jìn)來(lái)的DecorView設(shè)置在了root中,root.setView(view, wparams, panelParentView);
ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// Schedule the first layout -before- adding to the windowmanager,
//to make sure we do the relayout before receiving
// any other events from the system.
//在添加到windowmanager之前進(jìn)行布局,確保在收到系統(tǒng)的event之前進(jìn)行relayout
// 出發(fā)布局的繪制流程,measure,layout,view 的繪制流程,就是從這來(lái)的。
//這個(gè)方法保證了,在添加的屏幕前已經(jīng)完成了測(cè)量、繪制。
requestLayout();
try {
//通過(guò)binder和WindowManagerService進(jìn)行通信,將view添加到屏幕上。
// mWindow = new W(this);
// static class W extends IWindow.Stub {}
//添加到屏幕的邏輯,稍后寫(xiě)文章詳細(xì)分析。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
}catch (RemoteException e) {
throw new RuntimeException("Adding window failed", e);
}
}在setView()方法中有兩句很重要的代碼。
requestLayout();
res = mWindowSession.addToDisplay()
1) requestLayout()請(qǐng)求布局,調(diào)用者行代碼會(huì)執(zhí)行view的measue,layou,draw方法。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}checkThread這有一個(gè)很重要的知識(shí)點(diǎn),為啥子線(xiàn)程不能修改主線(xiàn)程創(chuàng)建的view?
//mThread是在ViewRootImp初始初始化是所在的線(xiàn)程。
//在requestLayout時(shí),會(huì)獲取當(dāng)前請(qǐng)求布局的線(xiàn)程。
//如果兩個(gè)線(xiàn)程不一致就會(huì)拋異常,只有原始創(chuàng)建的線(xiàn)程,可以修改views
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}在scheduleTraversals方法中。通過(guò)mChoreographer編舞者對(duì)象,最后執(zhí)行了mTraversalRunnable中的方法。這塊代碼在消息屏障文章中,詳細(xì)分解。
//開(kāi)始遍歷
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//發(fā)送一個(gè)同步消息,在handler機(jī)制分析中提過(guò)一下,同步屏障。關(guān)于這個(gè)知識(shí)點(diǎn)還會(huì)做詳細(xì)的分析。
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
//mChoreographer 編舞者類(lèi)。
//通過(guò)編舞者,執(zhí)行 runnable中 doTraversal 方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}在TraversalRunnable中執(zhí)行了doTraversal()方法。在這里調(diào)用了三個(gè)重要的方法
performMeasure(),performLayout(),performDraw()。
這也就是為什么View的繪制流程是先調(diào)用onMeasure,onLayout,后調(diào)用onDraw的原因。
并且這些寫(xiě)方法都是在onResume執(zhí)行才調(diào)用的。所以,這就是我們想拿到View的寬高,在onResume之前拿不到的原因。
private void performTraversals() {
//mView DecorView
final View host = mView;
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
performDraw();
} private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
private void performDraw() {
draw(fullRedrawNeeded);
//
}到此這篇關(guān)于Android Activity View加載與繪制流程深入刨析源碼的文章就介紹到這了,更多相關(guān)Android Activity View內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開(kāi)發(fā)之開(kāi)門(mén)狗在程序鎖中的應(yīng)用實(shí)例
這篇文章主要介紹了Android開(kāi)發(fā)之開(kāi)門(mén)狗在程序鎖中的應(yīng)用,以完整實(shí)例形式分析了程序鎖的使用技巧,需要的朋友可以參考下2016-02-02
Kotlin協(xié)程Job生命周期結(jié)構(gòu)化并發(fā)詳解
這篇文章主要為大家介紹了Kotlin協(xié)程Job生命周期結(jié)構(gòu)化并發(fā)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Android實(shí)用控件自定義逼真相機(jī)光圈View
這篇文章主要為大家詳細(xì)介紹了Android實(shí)用控件自定義逼真相機(jī)光圈,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08
Android shape和selector 結(jié)合使用實(shí)例代碼
本篇文章主要介紹了Android shape和selector 的使用,這里提供了shape 和selector 的詳細(xì)介紹,并附有代碼實(shí)例,有興趣的朋友可以參考下2016-07-07
android開(kāi)發(fā)之蜂鳴提示音和震動(dòng)提示的實(shí)現(xiàn)原理與參考代碼
蜂鳴提示音和震動(dòng)提示此功能在手機(jī)使用中很實(shí)用,最近在讀zxing項(xiàng)目,學(xué)到了不少東西;我們一起來(lái)看看他是怎么做的,感興趣的朋友可以了解下哦2013-01-01
flutter項(xiàng)目引入iconfont阿里巴巴圖標(biāo)
這篇文章主要為大家介紹了flutter項(xiàng)目引入iconfont阿里巴巴圖標(biāo)的過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Android實(shí)現(xiàn)圖片的高斯模糊(兩種方式)
本文給大家分享兩種實(shí)現(xiàn)圖片的高斯模糊效果,非常不錯(cuò),具有參考借鑒價(jià)值,對(duì)android圖片高斯模糊效果感興趣的朋友一起看看吧2017-03-03

