Activity啟動分析(二)--建立Window和View

yangxi_001發表於2014-02-26
一.Activity啟動
Activity由ActivityThread負責啟動。 ActivityThread的分析將在《ActivityThread分析》中,在此,只需要知道建立activity的入口在ActivityThread就行,不影響以下的分析。
    ActivityThread.java
    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }

二。呼叫Activity的attach方法。
    ActivityThread.java
    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ContextImpl appContext = new ContextImpl();
        appContext.init(r.packageInfo, r.token, this);
        appContext.setOuterContext(activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mConfiguration);
        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                + r.activityInfo.name + " with config " + config);
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstance,
                r.lastNonConfigurationChildInstances, config);
    }

三。Activity的attach實現
    1.attach的實現
        Activity.java //Activity implement Window.Callback
        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,
                Object lastNonConfigurationInstance,
                HashMap<String,Object> lastNonConfigurationChildInstances,
                Configuration config) {
            attachBaseContext(context); //ContextThemeWrapper中實現,賦值給mBase

            mWindow = PolicyManager.makeNewWindow(this); //建立window,實際上是一個PhoneWindow物件
            mWindow.setCallback(this); //設定Window.Callback,因為Activity implement Window.Callback    
            mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
            mWindowManager = mWindow.getWindowManager();
        }
    2.PolicyManager.makeNewWindow實現    
        (1).PolicyManager.java
        private static final String POLICY_IMPL_CLASS_NAME =
            "com.android.internal.policy.impl.Policy";
        private static final IPolicy sPolicy;
        static {
            // Pull in the actual implementation of the policy at run-time
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        }
        
        public static Window makeNewWindow(Context context) {
            return sPolicy.makeNewWindow(context); 
        }        
        (2).Policy.java //Policy implements IPolicy
        public PhoneWindow makeNewWindow(Context context) {
            return new PhoneWindow(context);
        }
        
    3.設定WindowManager
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString()); //為Window中的WindowManager賦值
        mWindowManager = mWindow.getWindowManager(); //為Acitivity中的WindowManager賦值
        
        WindowManager只是一個interface,實現類有兩個:Window.LocalWindowManager和WindowManagerImpl。
        (1).Window.LocalWindowManager實現,只是對WindowManagerImpl的封裝呼叫。
        private class LocalWindowManager implements WindowManager {
            private final WindowManager mWindowManager;
            LocalWindowManager(WindowManager wm) {
                mWindowManager = wm;
            }
            public final void addView(View view, ViewGroup.LayoutParams params) {
                //some code to check params here
                mWindowManager.addView(view, params);
            }
            public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
                mWindowManager.updateViewLayout(view, params);
            }
            public final void removeView(View view) {
                mWindowManager.removeView(view);
            }
            public final void removeViewImmediate(View view) {
                mWindowManager.removeViewImmediate(view);
            }
        }
        (2).Window.setWindowManager實現分析
        public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
            if (wm == null) {
                wm = WindowManagerImpl.getDefault();
            }
            mWindowManager = new LocalWindowManager(wm);
        }

四。回撥Activity的onCreate方法。
    (1). ActivityThread.java
    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    (2). Instrumentation.java
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        activity.onCreate(icicle);
    }

五。Activity中setContentView(int layout)分析
    Activity.java
    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID); 
    }
    
    public Window getWindow() {
        return mWindow; //mWindow = PolicyManager.makeNewWindow(this);
    }
    
六。Window中setContentView(int layout)分析
    (1)PhoneWindow.java //因為Window是個abstract class, mWindow實際上是一個PhoneWindow物件
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }
    (2) installDecor()分析 //PhoneWindow.java中
    private void installDecor() {
        if (mDecor == null) { 
            mDecor = generateDecor();
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        }
    }
    mDecor: class DecorView extends FrameLayout
    mContentParent: class ViewGroup
    (3)generateDecor()分析
    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
    DecorView:繼承自FrameLayout,可以理解成視窗修飾,這個視窗修飾可以有各種style,比如標題欄,顯示進度條等。常見的視窗修飾的layout路徑為:frameworks/base/core/res/res/layout,比如R.layout.dialog_title_icons, R.layout.screen_title_icons.
    (4)generateLayout()分析
    protected ViewGroup generateLayout(DecorView decor) {
        // Inflate the window decor.
        int layoutResource;
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_title_icons;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title_icons;
            }
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
            layoutResource = com.android.internal.R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_custom_title;
            } else {
                layoutResource = com.android.internal.R.layout.screen_custom_title;
            }
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_title;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
        } else {
            layoutResource = com.android.internal.R.layout.screen_simple;
        }

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        return contentParent;
    }
    DecorView的樣式的定義:第一種是在Activity中onCreate中呼叫requestFeature()。另一種是在AndroidManifest.xml中配置android:theme="xxx".
    DecorView新增View:generateLayout的前部分程式碼,就是確定DecorView的樣式,然後inflate這個layout檔案,再呼叫DecorView.addView()新增到DecorView上。
    mContentParent賦值:視窗修飾的layout中必須有一個FrameLayout,id為ID_ANDROID_CONTENT(實際上id=content),將這個FrameLayout賦值給mContentParent。
    (5)新增視窗內容到mContentParent。
    mLayoutInflater.inflate(layoutResID, mContentParent);
    將activity傳入的layoutRes新增到視窗裝飾中。
    (6)Window.Callback回撥
    final Callback cb = getCallback(); //Activity實現了Window.Callback介面,並且在建立PhoneWindow後,呼叫mWindow.setCallback(this)
    if (cb != null) {
        cb.onContentChanged();
    }
   
七。通知WmS,顯示DecorView
    Activity準備好後會通知AmS,AmS通過一些條件判斷,回撥Activity的makeVisible().
    (1.) Activity.java
    public void setVisible(boolean visible) {
        if (mVisibleFromClient != visible) {
            mVisibleFromClient = visible;
            if (mVisibleFromServer) {
                if (visible) makeVisible();
                else mDecor.setVisibility(View.INVISIBLE);
            }
        }
    }
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes()); //通過WindowManager將DecorView新增到window。
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
    public Window getWindow() {
        return mWindow;
    }
    (2.) Window.java
    public final WindowManager.LayoutParams getAttributes() {
        return mWindowAttributes;
    }
    private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();
    (3.) WindowManager.LayoutParams
    public LayoutParams() {
        super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        type = TYPE_APPLICATION;
        format = PixelFormat.OPAQUE;
    }
    (4.)Window.LocalWindowManager
    wm.addView(mDecor, getWindow().getAttributes())分析
    這個wm是LocalWindowManager,是對WindowManagerImpl的封裝,目地是對params進行一些校驗後,再呼叫WindowManagerImpl的addView。
    校驗params:
    if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
        wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    } else {
    }
    
八。WindowManagerImpl.addView分析
    方法:addView(View view, ViewGroup.LayoutParams params, boolean nest);
    class WindowManagerImpl extends WindowManager, interface WindowManager extends ViewManager
    3個成員變數:
    private View[] mViews;//每個view物件都將成為WmS所認為的一個視窗
    private ViewRoot[] mRoots;//每個view對應一個ViewRoot
    private WindowManager.LayoutParams[] mParams;//對應mViews的每個view的param
    (1.)檢查view是否已經新增過,不允許重複新增
        int index = findViewLocked(view, false);
        if (index >= 0) {
            if (!nest) {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
            root = mRoots[index];
            root.mAddNesting++;
            // Update layout parameters.
            view.setLayoutParams(wparams);
            root.setLayoutParams(wparams, true);
            return;
        }
    (2.)檢查視窗型別是否為sub window。如果是,則找到它的父視窗,並儲存在臨時變數panelParentView中,為下面呼叫ViewRoot的setView使用。
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews != null ? mViews.length : 0;
            for (int i=0; i<count; i++) {
                if (mRoots[i].mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews[i];
                }
            }
        }
    (3.)建立一個新的ViewRoot,上文說過每個view都對應一個ViewRoot。
        root = new ViewRoot(view.getContext());
        root.mAddNesting = 1;
        ViewRoot的構造方法:
        public ViewRoot(Context context) {
            super();

            // Initialize the statics when this class is first instantiated. This is
            // done here instead of in the static block because Zygote does not
            // allow the spawning of threads.
            getWindowSession(context.getMainLooper()); //init static IWindowSession sWindowSession;
            
            mThread = Thread.currentThread();
            mLocation = new WindowLeaked(null);
            mLocation.fillInStackTrace();
            mWidth = -1;
            mHeight = -1;
            mDirty = new Rect();
            mTempRect = new Rect();
            mVisRect = new Rect();
            mWinFrame = new Rect();
            mWindow = new W(this, context); // class W extends IWindow.Stub
            mInputMethodCallback = new InputMethodCallback(this);
            mViewVisibility = View.GONE;
            mTransparentRegion = new Region();
            mPreviousTransparentRegion = new Region();
            mFirst = true; // true for the first time the view is added
            mAdded = false;
            mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
            mViewConfiguration = ViewConfiguration.get(context);
            mDensity = context.getResources().getDisplayMetrics().densityDpi;
        }
    (4.)將view,root和param新增到上面的3個陣列中。
        if (mViews == null) {
            index = 1;
            mViews = new View[1];
            mRoots = new ViewRoot[1];
            mParams = new WindowManager.LayoutParams[1];
        } else {
            index = mViews.length + 1;
            Object[] old = mViews; //儲存原來的陣列
            mViews = new View[index]; //新建一個陣列,長度+1
            System.arraycopy(old, 0, mViews, 0, index-1); //將原來的陣列copy到新建的陣列中
            old = mRoots;
            mRoots = new ViewRoot[index];
            System.arraycopy(old, 0, mRoots, 0, index-1);
            old = mParams;
            mParams = new WindowManager.LayoutParams[index];
            System.arraycopy(old, 0, mParams, 0, index-1);
        }
        index--;
        mViews[index] = view;
        mRoots[index] = root;
        mParams[index] = wparams;
    (5.)呼叫ViewRoot的setView方法,完成最後的新增工作。

九。ViewRoot的setView分析
    引數意義:
    view: WindowManagerImpl中mViews陣列中的元素,也就是新建的視窗介面
    attrs:視窗引數,描述視窗的風格,大小,位置。attrs中的token變數指明瞭該視窗和activity的關係。
    panelParentView:view的父視窗
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;//給成員變數賦值。
                mWindowAttributes.copyFrom(attrs);//給成員變數賦值。
                attrs = mWindowAttributes;//給成員變數賦值。
                ………………………………
                mSoftInputMode = attrs.softInputMode;//給成員變數賦值。
                mWindowAttributesChanged = true;//給成員變數賦值。
                mAttachInfo.mRootView = view;//給成員變數賦值。
                if (panelParentView != null) {
                    mAttachInfo.mPanelParentWindowToken
                            = panelParentView.getApplicationWindowToken();
                }
                ………………………………
                requestLayout(); //發出重繪請求,使該 在相應訊息前變的可見
                ………………………………
                try {
                    //通知WmS,新增視窗
                    res = sWindowSession.add(mWindow, mWindowAttributes,
                            getHostVisibility(), mAttachInfo.mContentInsets,
                            mInputChannel);
                } catch (RemoteException e) {
                } 
            }
        }
    }
    
十。sWindowSession.add分析
    (1)IWindowSession是一個aidl介面,實現類是在WmS中:class Session extends IWindowSession.Stub
    (2)sWindowSession的初始化: //static IWindowSession sWindowSession;
        public static IWindowSession getWindowSession(Looper mainLooper) {
            synchronized (mStaticInit) {
                if (!mInitialized) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                        sWindowSession = IWindowManager.Stub.asInterface(
                                ServiceManager.getService("window")) //先獲得WmS
                                .openSession(imm.getClient(), imm.getInputContext()); //再通過WmS獲取分配的IWindowSession
                        mInitialized = true;
                    } catch (RemoteException e) {
                    }
                }
                return sWindowSession;
            }
        }
    (3)sWindowSession為static,WmS為每個程式只分配1個。呼叫sWindowSession.add是app請求WmS新增視窗的唯一入口。
    
        
總結:至此,從客戶端的角度講,已經完成了視窗建立的全部工作。

相關文章