Android addView过程

作者: javalong | 来源:发表于2017-04-05 16:31 被阅读510次

    以前就知道有WindowManagerService这个东西,知道addView最终是跟WindowManagerService进行交互的,但是一直没有深入去看,去理解。

    然后到网上搜搜资料。
    看到老罗的文章后
    http://blog.csdn.net/luoshengyang/article/details/8462738

    我发现我更不懂了。还是试着自己看源码,自己尝试着去理解,虽然可能没有他理解的那么深刻,还有结构图,时序图,但是整个流程至少自己可以走通。

    下面就贴上代码,把整个流程跑一遍。

    注意:Android源码 2.3.1
    

    这里我就从new Activity开始。

    frameworks/base/core/java/android/app/ActivityThread

     private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         ...
         ...
        activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
        ...
        activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstance,
                            r.lastNonConfigurationChildInstances, config);
        ...     
    }
    

    接收到AMS发来的消息后,开始创建Activity。

    frameworks/base/core/java/android/app/Activity

     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);
    
            mWindow = PolicyManager.makeNewWindow(this);
            mWindow.setCallback(this);
      ...
            mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
      ...
        }
    

    本文就不过多的介绍中间跳转的过程了,因为直接会有很方法重载,我就直接贴出关键的调用代码,希望大家能自己跟一遍,对理解,记忆都非常有好处。

    这里最重要的3行代码

    1. PolicyManager.makeNewWindow创建了PhoneWindow
    2. 给PhoneWindow设置callback,也就是Activity本身。
    3. setWindowManager

    frameworks/base/core/java/android/view/Window

     public void setWindowManager(WindowManager wm,
                IBinder appToken, String appName) {
            mAppToken = appToken;
            mAppName = appName;
            if (wm == null) {
                wm = WindowManagerImpl.getDefault();
            }
            mWindowManager = new LocalWindowManager(wm);
        }
    }
    public WindowManager getWindowManager() {
            return mWindowManager;
     }
    

    虽然getWindowManager得到的是LocalWindowManager,但是其实
    LocalWindowManager可以算个代理类,里面很多方法的具体实现还是在WindowManagerImpl。

    其实我们当前分析的addView,以及与WMS交互流程会比前面AMS交互要难,因为AMS交互大部分的过程都是连续的,可以一步走到底,可是我们现在分析的就很难一步走到底。当然这是我的个人感觉。

    现在newActivity创建了一些变量,走不下去,我们就继续回过头看看。其实这个时候触发了Activity的onCreate方法了。

    frameworks/base/core/java/android/app/ActivityThread

    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ...
    mInstrumentation.callActivityOnCreate(activity, r.state);
      ...
    }
    

    回调我们写的onCreate。而这时候我们一般会setContentView去设置布局。

    frameworks/base/core/java/android/app/Activity

    public void setContentView(int layoutResID) {
            getWindow().setContentView(layoutResID);
        }
    ...
    public Window getWindow() {
            return mWindow;
        }
    ...
    

    getWindow其实获取到的就是刚才初始化Activity中的mWindow,也就是PhoneWindw.
    frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow

     @Override
        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();
            }
        }
    
    ...
    
     private void installDecor() {
            if (mDecor == null) {
                mDecor = generateDecor();
                ...
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);
                ...
            }
        }
    
    protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
        }
    ...
     protected ViewGroup generateLayout(DecorView decor) {
    ...
     View in = mLayoutInflater.inflate(layoutResource, null);
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    }
    ...
    

    这里代码 稍微有点多,但是逻辑上很简单,基本上都能看得懂。首先是前面调用了Activity的setContentView,其实最终是调用了PhoneWindow的setContentView。

    在PhoneWindow中会判断是否创建了DecorView。如果没有创建的话,就会new DecorView 然后再根据用户设置的主题还有属性,获取到对应的layout布局,然后inflater之后(这里姑且把inflater之后的布局叫做RootView)add到DecorView之中。RootView其实是一个LinearLayout,分为三大块,状态栏,标题栏,用户的内容栏(其实就是一个FrameLayout)。

    最后其实是调用了mLayoutInflater.inflate(layoutResID, mContentParent);
    把setContentView设置的layout,inflater之后add到用户的内容栏中。

    到了这一步之后,发现又无路可走了,再往前看看,performLaunchActivity也已经执行完了。然后确实是不知道下一步到底怎么走了,然后我搜了搜,其实这个时候触发了handleResumeActivity
    frameworks/base/core/java/android/app/ActivityThread

     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        ...
          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;
             wm.addView(decor, l);
           }
        ...
    }
    

    这几句代码很关键,因为我们知道其实整个界面最底层的其实是PhoneWindow,然后是DecorView,然后是RootView,接下来才是我们自己的View.前面流程只讲了创建DecorView然后add RootView 然后add 我们自定义的layout。

    而这几句代码才把DecorView add到PhoneWindow中。
    上面的getWindowManager()其实是LocalWindowManager,不过
    LocalWindowManager其实只能算是一个代理类,具体的实现在WindowManagerImpl中。
    frameworks/base/core/java/android/View/WindowManagerImpl

     private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
        {
           ...
          ViewRoot root;
          ...
          root = new ViewRoot(view.getContext());
          ...
          root.setView(view, wparams, panelParentView);  
    }
    

    ViewRoot在添加View的过程中是很重要的一个角色,现在终于出现了,我当前android源码是2.3.1,后面的版本中,ViewRoot被替换成了ViewRootImpl,但是这并没有什么关系,我们能把 这里的源码看懂了,新版中虽然有改动,但是还是能很快的理解的。

    frameworks/base/core/java/android/View/ViewRoot

    public static IWindowSession getWindowSession(Looper mainLooper) {
            synchronized (mStaticInit) {
                if (!mInitialized) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                        sWindowSession = IWindowManager.Stub.asInterface(
                                ServiceManager.getService("window"))
                                .openSession(imm.getClient(), imm.getInputContext());
                        mInitialized = true;
                    } catch (RemoteException e) {
                    }
                }
                return sWindowSession;
            }
        }
    
    public ViewRoot(Context context) {
            super();
            ...
            getWindowSession(context.getMainLooper());
            ...
    }
    

    看到getWindowSession这个方法其实会感到非常的熟悉,因为在前面AMS中也出现过相同的代码,就是创建一个代理类与ActivityManagerService利用Binder机制进行交互。这里也是一样的。是与WindowManagerService建立连接。

    虽然代码看过去挺像的,但又有点不太一样,这里会比ActivityManagerService那里会稍微复杂点。下面我来具体分析下。
    out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager

    
    ...
    ...
    case TRANSACTION_openSession:
    {
    data.enforceInterface(DESCRIPTOR);
    com.android.internal.view.IInputMethodClient _arg0;
    _arg0 = com.android.internal.view.IInputMethodClient.Stub.asInterface(data.readStrongBinder());
    com.android.internal.view.IInputContext _arg1;
    _arg1 = com.android.internal.view.IInputContext.Stub.asInterface(data.readStrongBinder());
    android.view.IWindowSession _result = this.openSession(_arg0, _arg1);
    reply.writeNoException();
    reply.writeStrongBinder((((_result!=null))?(_result.asBinder()):(null)));
    return true;
    }
    
    ...
    public android.view.IWindowSession openSession(com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    android.view.IWindowSession _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((client!=null))?(client.asBinder()):(null)));
    _data.writeStrongBinder((((inputContext!=null))?(inputContext.asBinder()):(null)));
    mRemote.transact(Stub.TRANSACTION_openSession, _data, _reply, 0);
    _reply.readException();
    _result = android.view.IWindowSession.Stub.asInterface(_reply.readStrongBinder());
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    ...
    

    使用Binder机制与WindowManagerService交互,调用WindowManagerService的openSession方法

    frameworks/base/services/java/com/android/server/WindowManagerService

    ...
     public IWindowSession openSession(IInputMethodClient client,
                IInputContext inputContext) {
            if (client == null) throw new IllegalArgumentException("null client");
            if (inputContext == null) throw new IllegalArgumentException("null inputContext");
            Session session = new Session(client, inputContext);
            return session;
        }
    ...
    private final class Session extends IWindowSession.Stub
                implements IBinder.DeathRecipient {
    ...
    }
    

    这个过程虽然有点复杂,但是还算连续,有了前面阅读过AMS源码经验,这里其实也是可以接受的。

    最终的结果就是,调用WindowManagerService的openSession创建一个Session对象,这个Session对象其实也是IWindowSession.Stub的子类。然后把session这个对象重新write回来。然后调用

      _result = android.view.IWindowSession.Stub.asInterface(_reply.readStrongBinder());
    

    在App进程中,读取到这个Session后,使用Binder机制,在本地创建IWindowSession.Stub.Proxy对象与WindowManagerService那边的Session对象建立连接。

    虽然只是一句简单的代码

    sWindowSession = IWindowManager.Stub.asInterface(
                                ServiceManager.getService("window"))
                                .openSession(imm.getClient(), imm.getInputContext());
    

    但是里面涉及到了好多。
    最终返回的sWindowSession,其实是一个IWindowSession.Stub.Proxy对象,用来与WindowManagerService的Session建立连接。

    其实ViewRoot初始化的时候还有一个重要的变量。

     public ViewRoot(Context context) {
    ...
     mWindow = new W(this, context);
    ...
    }
    

    在前面介绍AMS的时候,我们都知道,App进程使用ActivityManagerProxy与ActivityManagerService进行交互,而ActivityManagerService则是使用ApplicationThreadProxy与App进程中的ApplicationThread进行交互。

    大同小异,WindowManagerService想与App端进行交互,那么也需要一个类似ApplicationThread这样的Binder来接收WindowManagerService传递过来的信息。它就是W这个类。

    那么我们继续往下看。

    还是回到刚才的WindowManagerImpl,创建好了ViewRoot之后。
    frameworks/base/core/java/android/view/WindowManagerImpl

     private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
        {
          private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
        {
           ...
          ViewRoot root;
          ...
          root = new ViewRoot(view.getContext());
          ...
          root.setView(view, wparams, panelParentView);  
    }
    

    继续是调用ViewRoot的setView

    public void setView(View view, WindowManager.LayoutParams attrs,
                View panelParentView) {
          ...
          ...
        res = sWindowSession.add(mWindow, mWindowAttributes,
                                getHostVisibility(), mAttachInfo.mContentInsets,
                                mInputChannel);
        ...
    }
    

    调用刚才获取到的Session代理的add方法
    out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowSession

    
    ...
    ...
    case TRANSACTION_add:
    {
    data.enforceInterface(DESCRIPTOR);
    android.view.IWindow _arg0;
    _arg0 = android.view.IWindow.Stub.asInterface(data.readStrongBinder());
    android.view.WindowManager.LayoutParams _arg1;
    if ((0!=data.readInt())) {
    _arg1 = android.view.WindowManager.LayoutParams.CREATOR.createFromParcel(data);
    }
    else {
    _arg1 = null;
    }
    int _arg2;
    _arg2 = data.readInt();
    android.graphics.Rect _arg3;
    _arg3 = new android.graphics.Rect();
    android.view.InputChannel _arg4;
    _arg4 = new android.view.InputChannel();
    int _result = this.add(_arg0, _arg1, _arg2, _arg3, _arg4);
    reply.writeNoException();
    reply.writeInt(_result);
    if ((_arg3!=null)) {
    reply.writeInt(1);
    _arg3.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    }
    else {
    reply.writeInt(0);
    }
    if ((_arg4!=null)) {
    reply.writeInt(1);
    _arg4.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    }
    else {
    reply.writeInt(0);
    }
    return true;
    }
    ...
    public int add(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs, int viewVisibility, android.graphics.Rect outContentInsets, android.view.InputChannel outInputChannel) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));
    if ((attrs!=null)) {
    _data.writeInt(1);
    attrs.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    _data.writeInt(viewVisibility);
    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readInt();
    if ((0!=_reply.readInt())) {
    outContentInsets.readFromParcel(_reply);
    }
    if ((0!=_reply.readInt())) {
    outInputChannel.readFromParcel(_reply);
    }
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    ...
    

    还是使用Binder机制,调用了WindowManagerService下的Session对象的add方法。

    其中值得关注的是,这里把我刚才提到的mWindow也就是W对象write给了WindowManagerService的Session,其实跟AMS的交互是一样的。Session那边可以利用W对象与App进程进行交互。

    ...
     public int addWindow(Session session, IWindow client,
                WindowManager.LayoutParams attrs, int viewVisibility,
                Rect outContentInsets, InputChannel outInputChannel) {
        
    }
    ...
     private final class Session extends IWindowSession.Stub
                implements IBinder.DeathRecipient {
    ...
    ...
     public int add(IWindow window, WindowManager.LayoutParams attrs,
                    int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
                return addWindow(this, window, attrs, viewVisibility, outContentInsets,
                        outInputChannel);
            }
    ...
    }
    

    Session其实是WindowManagerService的一个内部类,最后还是调用了WindowManagerService的add方法。

    我发现到了这里之后我又走不下去了,又往前找。
    frameworks/base/core/java/android/app/ActivityThread

     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
      ...
       ViewManager wm = a.getWindowManager();
                            View decor = r.window.getDecorView();
                            wm.updateViewLayout(decor, l);
      ...
    }
    

    addView之后,调用WindowManagerImpl的updateViewLayout。
    frameworks/base/core/java/android/view/WindowManagerImpl

    ...
     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams
                    = (WindowManager.LayoutParams)params;
            
            view.setLayoutParams(wparams);
    
            synchronized (this) {
                int index = findViewLocked(view, true);
                ViewRoot root = mRoots[index];
                mParams[index] = wparams;
                root.setLayoutParams(wparams, false);
            }
        }
    ...
    

    最后一行调用ViewRoot setLayoutParams
    frameworks/base/core/java/android/view/ViewRoot

    ...
    case DO_TRAVERSAL:
             ...
    
                performTraversals();
    
              ...
                break;
    ...
    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
                ...
                scheduleTraversals();
            }
        }
    ...
      public void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                sendEmptyMessage(DO_TRAVERSAL);
            }
        }
    ...
    

    终于最后调用了,performTraversals。由于这个方法很重要,所以这里单独提取出来。

    private void performTraversals() {
        final View host = mView;
        ...
        ...
       host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    
      ...
       host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
      ...
      ...
      draw(fullRedrawNeeded);
      ...
    }
    

    mView 其实就是刚才我们调用ViewRoote setView设置进来的DecorView。

    多的我也就不说了,最熟悉的3个方法出现了。接下来其实就是遍历测量,遍历布局,遍历绘制。

    总的来说,这里整个添加View的过程比前面的AMS管理生命周期代码跟踪起来要难。

    这里我再简单的说下,界面显示后的,动态代码addView的过程。
    frameworks/base/core/java/android/view/ViewGroup

     public void addView(View child, int index, LayoutParams params) {
            if (DBG) {
                System.out.println(this + " addView");
            }
    
            // addViewInner() will call child.requestLayout() when setting the new LayoutParams
            // therefore, we call requestLayout() on ourselves before, so that the child's request
            // will be blocked at our level
            requestLayout();
            invalidate();
            addViewInner(child, index, params, false);
        }
    

    requestLayout其实是View里的方法
    frameworks/base/core/java/android/view/View

     public void requestLayout() {
            if (ViewDebug.TRACE_HIERARCHY) {
                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
            }
    
            mPrivateFlags |= FORCE_LAYOUT;
    
            if (mParent != null && !mParent.isLayoutRequested()) {
                mParent.requestLayout();
            }
        }
    
    

    最后其实是调用mParent的方法,mParent其实是当前View的父控件,所以一步步往上调用,最后的话是调用DecorView的parent。

    这就尴尬了,DecorView的 parent是谁呢?

    前面其实已经介绍到了,我们最终是调用了WindowManagerImpl的addView,然后调用了ViewRoot的setView,让我们再来看一下setView.
    frameworks/base/core/java/android/view/ViewRoot

    public void setView(View view, WindowManager.LayoutParams attrs,
                View panelParentView) {
        ...
        view.assignParent(this);
        ...
    }
    

    这里其实就看出来了,ViewRoot直接把自己设置为了DecorView的parent。

    所以requestLayout其实还是调用了ViewRoot的requestLayout.

    public void requestLayout() {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    

    最后还是调用了scheduleTraversals,然后还是跟前面的流程一样了。

    好,过程很复杂,当时跟踪一遍了之后,还是收获很多的。

    相关文章

      网友评论

      • 高丕基:关于addView我有个疑惑,就是当我直接调用addView()方法,此时还会有Window与之关联吗?就类似于在屏幕上直接创建一个Button。毕竟从addView传入的参数是一个View而不是Window。如果有的话 是什么时候View和Window关联上的呢?
      • ZZombiee:前排围观大神

      本文标题:Android addView过程

      本文链接:https://www.haomeiwen.com/subject/fejtattx.html