`
liguanyi11111
  • 浏览: 61561 次
  • 性别: Icon_minigender_1
  • 来自: 黑龙江
社区版块
存档分类
最新评论

备忘(View显示流程1-View draw的准备工作)

阅读更多

      这是一篇写给自己用于备忘的文字。所以内容上比较跳跃,不建议作为参考。使用代码版本Android4.4.

      首先Activity的加载不归我们管辖,所以View的展示可以说是从Activity的setContentView()开始的,这个方法最终会走到PhoneWindow(继承与Window)类中的setContentView()方法。

public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
       ....
    }

       这里涉及了一个重要的对象,mContentParent对象,这是个ViewGroup类型对象。也就是我们activity的内容部分(用于放置我们加进去的View)的根View,这是由installDecor()方法创建的。这个方法首先需要创建整个Activity的根DecorView对象,这是个FrameLayout的子类。是整个的Activity的框架(包含标题栏与ActionBar)。然后在通过generateLayout()方法创建mContentParent对象,与其说创建不如说获取,因为这个对象时从DecorView中实例化xml布局文件中根据ID得到的。

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);
        .......
        return contentParent;
}

 最后,回到PhoneWindow中,mContentParent对象使用addView()方法,加载我们要加入的view。这样我们要展示的View就完全准备好了。但是这里并没有开始绘制,只是单纯的准备工作完成。

       之后,由于Activity自己加载过程中onCreate()方法执行之后即上述加载完View结束后,随着Activity的加载,ActivityManagerService(AMS)开始调用ActivtyThread的handleResumeActivity()的方法,并把mDecorView设置为可见(setVisibility(View.VISIBLE))。

 

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
            boolean reallyResume) {
       .......
       ActivityClientRecord r = performResumeActivity(token, clearHide);
       .......
       final Activity a = r.activity;   //获取activity对象
       .......
       decor.setVisibility(View.INVISIBLE);    //显示decor
       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);     //加载了我们的View
        }
        .......
}

 这里最重要的代码就是wm.addView(decor, l);但是这里要先说一下wm,看上述代码可知只是一个ViewManager类型,实际上是一个WindowManager(实现了ViewManager接口)。他通过activity的getWindowManager()获得,获得的WindowManager对象最初来自于Window类的getWindowManager()方法,由setWindowManager()创建。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
       .......
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

 这里可以看到,首先我们通过mContext对象获取系统的WindowManagerService。然后通过createLocalWindowManager方法复制了一份。所以说对于WindowManagerService对象,每一个Activity程序都会有一个自己的mWindowManager。下面就是通过这个WindowManager将我们的View加载并显示出来。回到addView()方法,这个方法的实现在WindowManagerImpl里面,发现他只是一个代理,这个方法最终交给了WindowManagerGlobal类的一个单例来执行。

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();   //单例对象
...............
public void addView(View view, ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

 单例的addView()方法如下:

 public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
         .........
        ViewRootImpl root;
         .........
         root = new ViewRootImpl(view.getContext(), display);
         view.setLayoutParams(wparams);
         mViews.add(view);
         mRoots.add(root);
         .........
         root.setView(view, wparams, panelParentView);
         .........
}

 这里我们首次看到了ViewRoot,这是非常重要的类。在ViewRoot的构造函数中可见,

 public ViewRootImpl(Context context, Display display) {
        .......
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ......
}

 这里取得了一个WindowSession的对象,这是一个aidl的接口,用于跨进程取得Session对象。流程如下:

public static IWindowSession getWindowSession() {
        .....
        sWindowSession = windowManager.openSession(
                  imm.getClient(), imm.getInputContext());
        .....
        return sWindowSession;
}

 这里调用了类WindowManagerService里实现的openSession方法。

public IWindowSession openSession(IInputMethodClient client,
            IInputContext inputContext) {
        ......
        Session session = new Session(this, client, inputContext);
        return session;
}

 用此可见,Session是在WMS中直接创建的,如名字一样,这是一个WMS与ViewRoot的一个会话,由此ViewRoot的功能就非常清楚:是View与WMS通信的桥梁,在ViewRoot中使用WMS。我们了解了ViewRoot的功能,让我们看看他是如何执行的。我们回到WindowManagerImpl的addView()。ViewRoot使用setView方法将我们需要显示View放入ViewRoot内进行操作。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ..........
                requestLayout();
                ..........
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);
                .........
}

     首先是requestLayout()方法,经过scheduleTraversals()方法的传递,使用Choreographer的postCallback()方法异步执行了一个Runnable。这里的Choreographer就是一个消息处理器,里面包含一个handler来处理几种信号,这个与主题关系不大暂时不做介绍。这个Runnable中调用了一个doTraversal()方法,最终指向performTraversals()方法。这个方法相信有些人非常了解,因为我们所用View的树结构就是在这里被绘制展开的,包含一系列的measure,Layout等测量大小计算位置等方法。但是现在先不介绍他。因为怎么画还不急着去研究,我们先要去拿到我们的画布。虽然绘制代码逻辑在前,但是别忘了他是个消息队列形式执行的方法,在我们的activity没启动完毕时,这个绘制过程是不会被触发的。  这时我们回到setView来看Session的addToDisplay方法。

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }

 这里代理调用了mService的addWindow()方法,其中传入的参数window是ViewRoot内的一个内部类通过binder机制放入到WMS中用于WMS反向调用ViewRoot,即与Session相反。这样ViewRoot与WMS建立了双向链接。这时,让我们进入WMS看看addWindow()方法。

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
            ..............
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            .............
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            .............            
}

 这里实例化了一个WindowState对象,里面保存了了一个SurfaceSession对象。这个SurfaceSession的实例化是通过win的attach()方法,最终调用到Session中的windowAddedLocked()方法。

    void windowAddedLocked() {
       ........
       mSurfaceSession = new SurfaceSession();
       ........
       mService.mSessions.add(this);
    }

 最终SurfaceSession被放入了Service的HashSet<Session>集合里面,对于Surface的实例化这里简单说一下,他调用了自己的native方法,与SurfaceFlinger建立了一个链接,SurfaceFlinger是用来绘制surface的一个服务,SurfaceSession就是WMS与SurfaceFlinger的通信桥梁。至此Activity的启动消息被执行完成,但是我们还是没有获取到画布,所以我们继续进入到performTraversals()方法。先无视掉View绘制的代码,我们直接看我们需要的代码 :relayoutWindow()方法(在performTraversals()直接调用

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
           boolean insetsPending) throws RemoteException {
           .........
           int relayoutResult = mWindowSession.relayout(,......, mSurface);
           .........
}

 看着有点乱,就是讲mSurface传入了Session的方法中。mSurface是ViewRoot自己建立的一个Surface对象,但是是个空构造函数没什么意义。这里传入Session就是要利用WMS充实这个Surface对象,这个Surface就是我们要绘制的屏幕。在Session中,他直接把Surface对象传入了mService即WMS对象中。

 

    public int relayout(IWindow window, ..., Surface outSurface) {
        ......
        int res = mService.relayoutWindow(this, window, ..., outSurface);
       ....
    }

 

这里的outSurface就是从客户端ViewRoot中一步步传入WMS内的Surface对象了。下面来开WMS对Surface对象的处理过程

    public int relayoutWindow(Session session, IWindow client, ......, Surface outSurface) {
    WindowState win = windowForClientLocked(session, client, false);
     .......
    WindowStateAnimator winAnimator = win.mWinAnimator;
      ........
        SurfaceControl surfaceControl =  winAnimator.createSurfaceLocked();
        if (surfaceControl != null) {
                outSurface.copyFrom(surfaceControl);
        }
}

 win就是通过我们上文中被放入map中的WindowState对象。这里首先通过win获取到WindowStateAnimator对象,再通过这个对象通过createSurfaceLocked()方法创建一个SurfaceControl对象(在早期的版本要简单些,直接就通过win创建出surface了)。

SurfaceControl createSurfaceLocked() {
              ........
              mSurfaceControl = new SurfaceControl(
                        mSession.mSurfaceSession,
                        attrs.getTitle().toString(),
                        w, h, format, flags);
               ........
}

 在SurfaceControl的构造函数中传入了SurfaceSession对象。然后回到上面,通过surface的copyFrom方法,从SurfaceControl对象中取得Surface。这里获取的方式仍然是native方法。到此,其实我们已经在java层面看到了surface的创建于传递的过程。其实surface还远没有结束,现在关于它我们只了解了三个部分,ViewRoot的创建(只是简单实例化一个对象,没有意义),SurfaceFlinger创建真正的Surface,WMS获得它。至于Surface怎样传回ViewRoot,还有其他对它的一系列处理,其中大量的代码是在native函数中进行的,这里先不研究native的C++函数。我们优先搞定上层处理。所以我们回到ViewRoot中继续看View的创建于绘制流程。

 

performTraversals() {
       .....
       performDraw();     //开始绘制了
       .....
}

 再继续看performDraw()

 

private void performDraw() {
      ......
      draw(fullRedrawNeeded);
      ......
}

 继续。

private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;    //我们千辛万苦获得的surface
        ........
        if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
               return;
        }
        .......
}

 这里的drawSoftware()方法就是绘制了

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
            boolean scalingRequired, Rect dirty) {
       .......
       canvas = mSurface.lockCanvas(dirty);    //在指定区域拿到canvas画笔
       .......
       mView.draw(canvas);  //开始绘制我们的View
       .......
       surface.unlockCanvasAndPost(canvas);    //回收surface
       .......
}

 终于,我们看到了mView.draw(canvas);利用画笔开始调用View的draw方法。具体的View的绘制,我们以后再看。

分享到:
评论
2 楼 麦田的设计者 2017-01-06  
“所以我们继续进入到performTraversals()方法。先无视掉View绘制的代码,我们直接看我们需要的代码 :relayoutWindow()方法(在performTraversals()直接调用),”你是从哪个function进入到这里的
1 楼 yefengxue 2016-01-07  

相关推荐

Global site tag (gtag.js) - Google Analytics