Android源码解析Android知识Android技术知识

Android addView过程

2017-04-05  本文已影响510人  javalong

以前就知道有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,然后还是跟前面的流程一样了。

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

上一篇下一篇

猜你喜欢

热点阅读