<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
1.App的啟動流程,從startActivity到Activity被建立。
這個流程主要是ActivityThread和ActivityManagerService之間通過binder進行通訊來完成。
ActivityThread可以拿到AMS 的BinderProxy。AMS可以拿到ActivityThread的BinderProxy ApplicationThread。這樣雙方就可以互相通訊了。
當ApplicationThread 接收到AMS的Binder呼叫後,會通過handler機制來執行對應的操作。
可以這樣說handler和binder是android framework重要的兩塊基石。
而Activity的建立就是在ActivityThread中呼叫handleLaunchActivity方法實現。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //建立一個Activity,並呼叫生命週期onCreate方法 Activity a = performLaunchActivity(r, customIntent); if (a != null) { //如果Activity成功建立,則會呼叫生命週期onResume方法。 handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); } }
2.接下來看performLaunchActivity。建立一個Activity。
如果Activity建立成功,先呼叫activity.attach(),在這個方法中,建立了Activity的PhoneWindow範例。
然後mInstrumentation.callActivityOnCreate,呼叫了Activity的onCreate生命週期方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { Activity activity = null; try { // Instrumentation mInstrumentation; //拿到類載入器,最後會通過反射的方式建立Activity物件 //(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){ //呼叫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); } //通過這個方法呼叫了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的子類 初始化了PhoneWindow,PhoneWindow中有一個Decoview物件 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的範例。 mWindowManager = mWindow.getWindowManager(); }
3.setContentView,佈局載入流程:
在Activity.onCreate方法中,我們通過setContentView(R.layout.activity_main);就能在介面顯示,那android是怎麼做到的?
1)呼叫installDecor(),初始化Decorview和mContentParent。
2)mLayoutInflater.inflate(),將佈局檔案,解析成android中對應的View。
//getWindow拿到的是PhoneWindow public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); } @Override public void setContentView(int layoutResID) { if (mContentParent == null) { //初始化Decorview和mContentParent installDecor(); } //載入並解析傳進來的佈局檔案,並add到mContentParent上。 //這個操作比較耗時,因為要從xml檔案中解析,然後再通過反射的方式生成Java中的View物件。 //所以在recyclerView和listView中要對這一步進行快取優化, mLayoutInflater.inflate(layoutResID, mContentParent); }
先看installDecor(),通過generateDecor() new了一個DecoView範例並賦值給了mDecor,
mDecor是PhoneView的一個變數。
//mDecorView是PhoneWindow的一個成員變數 private DecorView mDecor; private void installDecor() { //建立mDecor if (mDecor == null) { mDecor = generateDecor(-1); //new DecorView(context, featureId, this, getAttributes()); } //建立mContentParent,將建立的DecorView作為引數傳遞 if (mContentParent == null) { mContentParent = generateLayout(mDecor); } }
通過generateLayout(decor) 載入了一個系統的layout檔案,在android.jar--res--layout目錄下。
在mDecor.onResourcesLoaded方法中載入了這個佈局,並新增到了mDecor中。
DecorView繼承自FrameLayout,是一個真正的view。
然後通過findViewbyid,找到了一個ViewGoup,可以看下面的佈局檔案。
ID_ANDROID_CONTENT = com.android.internal.R.id.content; ,並把這個返回出去了。
這個view 就installDecor()方法中的mContentParent()
protected ViewGroup generateLayout(DecorView decor) { int layoutResource; //這個佈局檔案就在android.jar--res--layout目錄下。 layoutResource = R.layout.screen_simple; mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); // int ID_ANDROID_CONTENT = com.android.internal.R.id.content; //這個R.id.content就是定義在screen_simple中的一個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>
總結:至此 installDecor();已經完成。主要是建立了mDecorView,並載入了一個系統的佈局,R.layout.screen_simple,
將載入得到的View新增到了mDecorView中,並findViewById(R.id.content)的到的View賦值給了mParentContent。
回到setContentView中看第二行程式碼:
layoutResID 就是傳入的佈局檔案id,mContentParent就是載入的系統的佈局檔案中id為“content”的view
mLayoutInflater.inflate(layoutResID, mContentParent);
//載入並解析傳進來的佈局檔案,並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(); //通過 Resource 得到了一個XML解析器。 final XmlResourceParser parser = res.getLayout(resource); try { //解析我們自定義的layout,並新增到mParentContent上。 //將xml中定義的ViewGroup和View解析成Java物件。 //這塊程式碼會單獨寫文章講解 return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
至此上面的關係可以總結為:
Activity-->PhoneWindow-->mDecorView-->addView(R.layout.screen.simple)-->
R.id.content-->mParentContent-->addView(R.layout.activity.main)
我們自己寫的layout已經新增到了系統的DecorView中。
4.我們知道View有三個重要的方法onMeasure,onLayout,onDraw,
那這些方法是在哪裡呼叫的?我們建立的View是如何新增到螢幕上的呢?
回到handleLaunchActivity方法中,還有一個handleResumeActivity,通過performResumeActivity 會執行Activity的onResume生命週期方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //建立一個Activity,並呼叫生命週期onCreate方法 Activity a = performLaunchActivity(r, customIntent); if (a != null) { //如果Activity成功建立,則會呼叫生命週期onResume方法。 handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); } } 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; //通過上面程式碼我們知道 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; //關鍵程式碼,將decorView 新增到wm中 wm.addView(decor, l); } } } public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) { //執行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(); }
執行完onResume方法後:
wm.addView(decor, l);
將Activity的DecorView新增到了wm中。
ViewManager wm = a.getWindowManager();
ViewManager 是一個抽象類,範例是WindowManagerImpl。
在WindowManagerImpl中通過單例模式獲取了一個WindowManagerGlobal物件。
既然是單例模式獲取的物件,也就在一個程序,ActivityThread 主程序中 只有一個範例。
//WindowManagerImpl.java addView 方法 //WindowManagerGlobal 是一個單例模式,在ActivityThread 主程序中 只有一個範例。 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中建立了一個ViewRootImpl物件。這是很重要的一個物件。
將傳進來的DecorView設定在了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之前進行佈局,確保在收到系統的event之前進行relayout // 出發佈局的繪製流程,measure,layout,view 的繪製流程,就是從這來的。 //這個方法保證了,在新增的螢幕前已經完成了測量、繪製。 requestLayout(); try { //通過binder和WindowManagerService進行通訊,將view新增到螢幕上。 // mWindow = new W(this); // static class W extends IWindow.Stub {} //新增到螢幕的邏輯,稍後寫文章詳細分析。 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()請求佈局,呼叫者行程式碼會執行view的measue,layou,draw方法。
public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
checkThread這有一個很重要的知識點,為啥子執行緒不能修改主執行緒建立的view?
//mThread是在ViewRootImp初始初始化是所在的執行緒。 //在requestLayout時,會獲取當前請求佈局的執行緒。 //如果兩個執行緒不一致就會拋異常,只有原始建立的執行緒,可以修改views void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
在scheduleTraversals方法中。通過mChoreographer編舞者物件,最後執行了mTraversalRunnable中的方法。這塊程式碼在訊息屏障文章中,詳細分解。
//開始遍歷 void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; //傳送一個同步訊息,在handler機制分析中提過一下,同步屏障。關於這個知識點還會做詳細的分析。 mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); //mChoreographer 編舞者類。 //通過編舞者,執行 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中執行了doTraversal()方法。在這裡呼叫了三個重要的方法
performMeasure(),performLayout(),performDraw()。
這也就是為什麼View的繪製流程是先呼叫onMeasure,onLayout,後呼叫onDraw的原因。
並且這些寫方法都是在onResume執行才呼叫的。所以,這就是我們想拿到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); // }
到此這篇關於Android Activity View載入與繪製流程深入刨析原始碼的文章就介紹到這了,更多相關Android Activity View內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45