在Android的app來說,他的外表示靠Activity來建立的,因此我們就從Activity開始來看。當一個 app要被啟動是利用zygote去fork一個process來啟動。最先會跑到 main()@ActivityThread.java 然後直到handleLaunchActivity()@ActivityThread.java 來建立Activity。
handleLaunchActivity()@ActivityThread.java
//利用performLaunchActivity()回傳一個activity Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward); ... } ...
我們可以發現藉由performLaunchActivity()來建立一個activity之後呼叫handleResumeActivity() 先了解一下performLaunchActivity好了
performLaunchActivity()@ActivityThread.java
... //launch一個activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... activity.mCalled = false; //在這裡呼叫activity裡頭的onCreate() mInstrumentation.callActivityOnCreate(activity, r.state); if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; ... return activity; }
我們繼續看一下mInstrumentation.newActivity() 的部份。
newActivity()@Instrumentation.java
public Activity newActivity(Class clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException { Activity activity = (Activity)clazz.newInstance(); ActivityThread aThread = null; //設定一些activity的東西 activity.attach(context, aThread, this, token, application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, new Configuration()); return activity; }我們看一下activity.attach()
attach()@Activity.java
... //利用PolicyManager.makeNewWindow來產生一個window mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); ...那PolicyManager.makeNewWindow()是什麼呢?
makeNewWindow()@PolicyManager.java
... static { // Pull in the actual implementation of the policy at run-time try { //在run time的時候讀取 //frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java //一個PhonePolicy Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME); sPolicy = (IPolicy)policyClass.newInstance(); } catch (ClassNotFoundException ex) { ... } } // The static methods to spawn new policy-specific objects public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); }如果我們去看Policy.java就可以發現真正的makeNewWindow了
makeNewWindow()@Policy.java
public Window makeNewWindow(Context context) { return new PhoneWindow(context); }回到performLaunchActivity(),他會去呼叫Activity裡面的onCreate()而在這裡面我們會利用setContentView()來設定我們的UI,接著看一下handleResumeActivity()
handleResumeActivity()@ActivityThread.java
... if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); //取得decorView View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); //取得WindowManager ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; //將取得的 decor加入取得的WindowManager wm.addView(decor, l); } } ...這裡有兩個地方可以看的。第一個這個decorView是什麼呢?第二個,這個WindowManager又是哪來的呢? 第一部份,r.window.getDecorView()的前面r.window是什麼呢?剛剛我們追過就是phoneWindow所以getDecorView()我們就直接看
PhoneWindow.getDecorView()
getDecorView()@PhoneWindow.java
@Override public final View getDecorView() { if (mDecor == null) { installDecor(); } return mDecor; } private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { //generateLayout input是mDecor return 一個viewgroup mContentParent = generateLayout(mDecor); ... } protected ViewGroup generateLayout(DecorView decor) { ... //取得title 並加入decor中 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //findViewById實際上是return getDecorView().findViewById(id); //他會去尋找{@link android.app.Activity#onCreate}所定義的xml ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } ... return contentParent; }
因此我們可以發現,最剛開始windows manager裡面的view是一個PhoneWindow而PhoneWindow裡面是一個decorView裡面又包含了programmer定義的view 這是一個Decorator的design pattern 接著分析完了handleResumeActivity中的view, viewmanager之後我們繼續看 wm.addView(decor,l)
addView()@Window.java
public final void addView(View view, ViewGroup.LayoutParams params) { // Let this throw an exception on a bad params. WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params; CharSequence curTitle = wp.getTitle(); ... super.addView(view, params); }而LocalWindowManager 他是extends WindowManagerImpl.CompatModeWrapper, 因此我們去看一下WindowManagerImpl 的CompatModeWrapper才知道addView是寫些什麼
addView()@WindowManagerImpl.java
public void addView(View view, ViewGroup.LayoutParams params) { addView(view, params, null, false); } private void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih, boolean nest) { ... //ViewRootImpl是我們接下來的重點 ViewRootImpl root; View panelParentView = null; synchronized (this) { ... root = new ViewRootImpl(view.getContext()); root.mAddNesting = 1; ... mViews[index] = view; mRoots[index] = root; mParams[index] = wparams; } // do this last because it fires off messages to start doing things //這裡的view是我們一開始addView(mDecor,l)中的mDecor root.setView(view, wparams, panelParentView); }我們進入ViewRootImpl了解一下
ViewRootImpl.java
//ViewRootImpl繼承了Handler public final class ViewRootImpl extends Handler implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { //這裡建立一個surface 的 object private final Surface mSurface = new Surface(); //還有一個W 的object, W這個class 是繼承IWindow.stub final W mWindow; .... public ViewRootImpl(Context context) { .... //初始化對 getWindowSession(context.getMainLooper()); .... mWindow = new W(this); .... } .... } public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { InputMethodManager imm = InputMethodManager.getInstance(mainLooper); //open session透過Binder與WindowManagerService做溝通做溝通 sWindowSession = Display.getWindowManager().openSession( imm.getClient(), imm.getInputContext()); mInitialized = true; } catch (RemoteException e) { } } return sWindowSession; } }ok看完了ViewRootImpl的一些global variable及constructure之後我們回到剛剛的root.setView() setView()@ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout();//實際上就是scheduleTraversals() ... try { mOrigWindowType = mWindowAttributes.type; res = sWindowSession.add(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } ... } public void requestLayout() { checkThread(); mLayoutRequested = true; scheduleTraversals(); } public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; //noinspection ConstantConditions if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { final long now = System.nanoTime(); Log.d(TAG, "Latency: Scheduled traversal, it has been " + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f) + "ms since the last traversal finished."); } sendEmptyMessage(DO_TRAVERSAL); } } public void handleMessage(Message msg) { switch (msg.what) { ... case DO_TRAVERSAL: ... performTraversals(); ... } private void performTraversals() { .... //取得一個surface relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); .... //從surface裡面取得一個canvas然後畫在上面再把他show到螢幕 draw(fullRedrawNeeded); .... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { .... //透過IWindowSession與WindowManagerService溝通取得一個surface int relayoutResult = sWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); //Log.d(TAG, "<<<<<< BACK FROM relayout"); .... return relayoutResult; } private void draw(boolean fullRedrawNeeded) { //mSurface是剛剛ViewRootImpl的global variable Surface surface = mSurface; ... if (!dirty.isEmpty() || mIsAnimating) { Canvas canvas; try { int left = dirty.left; int top = dirty.top; int right = dirty.right; int bottom = dirty.bottom; .... //lock一塊canvas canvas = surface.lockCanvas(dirty); .... try { .... //把東西畫到canvas上 mView.draw(canvas); } finally { .... } .... } } finally { //把canvas show到螢幕上 surface.unlockCanvasAndPost(canvas); } } .... if (animating) { mFullRedrawNeeded = true; scheduleTraversals(); } }總結一下,在這一篇我們說了
- 建立一個Activity之後會有一個PhoneWindow 這個PhoneWindow裡面包含了DecorView他是一個framelayout 我們的app所畫得ui會包含在Decorview裡頭
- 建立一個Activity之後也會產生一個LocalWindowManager主要實做在WindowManagerImpl而他包含了ViewRootImpl
- ViewRootImpl包含了一個view指向DecorView及透過Binder從WindowServiceManager取得的surface
- 要畫出ui首先透過mSurface lock一塊canvas 然後透過mView.draw畫在上面 然後unlock 之後把他show在螢幕上
此篇是依照鄧凡平的Android系統原理深入解析 去trace 而成