`
liguanyi11111
  • 浏览: 62509 次
  • 性别: 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  

相关推荐

    国有企业股权转让意向书(备忘录)---律所整理.pdf

    国有企业股权转让意向书(备忘录)---律所整理.pdf

    基于android平台的手机备忘录设计与实现-PPT.ppt

    - 显示备忘录的基本信息,如是否响铃、是否震动、是否重复等。 - 用户可以根据需求调整这些属性。 ##### 3. **列表显示设计与实现** - 使用列表视图组件展示备忘录信息。 - 支持上下滚动查看更多的备忘录条目。...

    app开发-基于gin+grpc+gorm+etcd+mysql的备忘录功能APP实现-付项目源码+流程教程-优质项目实战

    app开发_基于gin+grpc+gorm+etcd+mysql的备忘录功能APP实现_付项目源码+流程教程_优质项目实战

    设计模式专题之(十九)备忘录模式---设计模式备忘录模式示例代码(python--c++)

    备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。这种模式通常用于游戏存档、撤销/重做操作、以及...

    nacos git下载太慢了,留个备忘 2.3.2 2024-4-3版本,2024-7-24日官网下载

    nacos git下载太慢了,留个备忘 2.3.2 2024-4-3版本,2024-7-24日官网下载

    桌面备忘录Memo-自制-含源代码.zip

    因工作需要,自制了一个桌面版备忘录,不能联网,适用于一些研发类的公司,不能连外网的。虽然有360桌面的备忘,但是对于多屏系统,总会出各种问题,所以还是自制吧,简洁,体积小,够用。里面含源代码,基于VC6.0+...

    移动端备忘录

    在移动设备上,备忘录应用程序提供了一种便捷的方式,让用户随时随地记录重要信息,无论是在工作中还是在生活中。这篇内容包含了代码和截图,意味着我们将探讨如何开发一个移动端备忘录小程序,以及可能涉及的技术和...

    备忘录模式-极客学院-java-课件代码

    1. **发起人(Originator)角色**:这是需要保存状态的对象,它可以创建一个备忘录,用来记录它的内部状态。在需要时,发起人可以使用备忘录恢复到之前的状态。 2. **备忘录(Memento)角色**:备忘录存储发起人的...

    微信小程序事件倒计时,备忘录小程序ComingEvent-develop.zip

    综上所述,"ComingEvent-develop.zip"中的小程序项目涉及到了微信小程序的基本开发流程,时间倒计时的编程技巧,以及备忘录应用的设计和实现。通过深入理解和实践这些知识点,开发者可以创建出功能丰富、用户体验...

    Android课程设计--便捷备忘录

    1. **MainActivity.java** - 主Activity文件,包含备忘录列表的显示和事件监听。 2. **EditActivity.java** - 编辑备忘录的Activity,用户可以输入新备忘录或者修改已有的。 3. **DatabaseHelper.java** - SQLite...

    备忘录小程序源码-微信原生开发框架.zip

    备忘录小程序源码,微信原生开发,助力高效生活与商业成功!这款源码不仅功能全面,支持文本、图片、语音等多种记录方式,满足您多样化的需求;而且性能卓越,流畅运行,让您无需担心卡顿问题。同时,我们严格遵守...

    表格模板-工作备忘录1.ett

    表格模板-工作备忘录1.ett

    程序员Linux备忘手册linux-memo-master.zip

    《程序员Linux备忘手册》是一份专为程序员设计的实用指南,旨在帮助开发者在Linux环境中高效工作。这个压缩包“linux-memo-master.zip”包含了丰富的Linux相关知识,涵盖了从基础命令到高级操作的各个方面,是程序员...

    javaMD5加密及登录验证(备忘) - Hibernate - Java - ITeye论坛.mht

    javaMD5加密及登录验证(备忘) - Hibernate - Java - ITeye论坛.mhtjavaMD5加密及登录验证(备忘) - Hibernate - Java - ITeye论坛.mhtjavaMD5加密及登录验证(备忘

    安卓备忘录-课设毕设-源码-可远程

    该备忘录实现,登录,数据库连接,添加,删除,增加,更新,可与手机安装使用,基本里面所有关键点都有着注释,是按照学生的角度来编写的,注释详细,有文档,做出来很久了,但是之前都是在我主页博客没有进行收费,...

    备忘录--C++程序

    2. **命令行界面**:设计一个简单的命令行界面,让用户通过输入指令(如“add”,“edit”,“delete”,“view”)来操作备忘录。 3. **登录功能**:实现用户登录系统,可以使用`std::map`存储用户名和密码。注意...

    创业板信息披露业务备忘录第10号-年度报告相关事项知识.pdf

    该备忘录旨在规范创业板上市公司的年度报告编制、报送和披露流程,提升年报信息的质量,确保上市公司及其相关各方的行为符合法律法规和交易所规定。备忘录主要内容涵盖非经常性损益的披露、每股收益的计算与披露、...

    MB备忘----2

    【标题】"MB备忘----2"所提及的内容可能与某个名为"MB"的项目或者技术有关,这可能是一个软件开发、系统管理或者是其他IT领域的记事或文档集合。"MB"可能是项目缩写,也可能是特定技术或工具的名称。由于描述中提到...

    备忘录---win7使用

    电脑中的必备品,如有兴趣的可以私下交流,备忘相关事宜。

    微信小程序-------备忘录

    微信小程序备忘录,备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录备忘录...

Global site tag (gtag.js) - Google Analytics