android开发艺术探索(八)

理解Window和WindowManager

Window和WindowManager

Window
Window是一个抽象类,它的具体实现是PhoneWindow。我们可以通过WindowManager创建一个Window。WidowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。
Android中的所有视图都是通过Window来呈现的,不管是Activity、Dialog还会Toast,他们的视图实际上都是附加在Window上的,因此Window实际上是View的直接管理者。

我们来使用WindowManger在向布局中添加一个按钮:具体实现如下

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WindowManager manager=getWindowManager();
        Button bt=new Button(this);
        bt.setText("button");
        WindowManager.LayoutParams params=
        new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,0,0,
                PixelFormat.TRANSPARENT
        );
        params.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                |WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
        params.gravity= Gravity.LEFT|Gravity.TOP;
        params.x=100;
        params.y=300;
        manager.addView(bt,params);
    }

其中flags和type这两个参数比较重要。
flags

Flags表示Window的属性,它有多选项,通过这些选项可以控制Window的显示特性。

FLAG_NOT_FOCUSABLE

表示Window不需要获取焦点,也不需要接受各种输入时间,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终时间会直接传递给下层的具有焦点的Window。

FLAG_NOT_TOUCH_MODAL

在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件。

FLAG_SHOW_WHEN_LOCKED

开启此模式可以让Window显示在锁屏的界面上。

type

type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window和系统Window。
应用类Window对应着一个Activity。
子类不能单独存在,他需要负数在特定的父Window中,比如常见的一些Dialog就是一个子Window。
系统Window是需要声明权限才能创建的Widow,比如Toast和系统状态栏都是系统Window。

此外,系统Window使用需要在AndroidMainfest中使用相应的权限。

WindowManager
WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager;

public interface ViewManager {
    void addView(View var1, LayoutParams var2);
    void updateViewLayout(View var1, LayoutParams var2);
    void removeView(View var1);
}

我们还使用上面那个例子:我们来实现一个可以拖动的Window,我们来给按钮加一个监听:

  bt.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent ev) {
                int rawX= (int) ev.getRawX();
                int rawY= (int) ev.getRawY();

                switch (ev.getAction()){
                    case MotionEvent.ACTION_MOVE:
                        params.x=rawX;
                        params.y=rawY;
                        /**在这里不断更新按钮的位置*/
                        manager.updateViewLayout(bt,params);
                        break;
                }
                return false;
            }
        });

Window的内部机制

Window是一个抽象的概念,是以View的形式存在。通过WindowManager来操作View。

Window的添加过程
Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口。它的实现类是WindowManagerImpl类。WindowManagerImpl并没有直接实现Window的三大操作,而是通过WindowManagerGloba来处理。

a. WindowManagerGlobal中的addView
b. 检查参数是否合法;
c. 如果子Window还需要调节布局参数;
d. 创建ViewRootImpl并将View添加到列表中;
e. 通过ViewRootImpl的setView来更新界面并完成Window的添加过程:
requestLayout中的scheduleTraversals是View绘制的入口,最终通过WindowSession来完成Window的添加过程,注意其实这里是个IPC过程,最终会通过WindowManagerService的addWindow方法来实现Window的添加。

Window的删除过程
Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGloba来实现的。

a. WinodwManagerGlobal中的removeView;
b. findViewLocked来查找待删除待View的索引,再调用removeViewLocked来做进一步删除;
c. removeViewLocked通过ViewRootImpl的die方法来完成删除操作,包括同步和异步两种方式,同步方式可能会导致意外的错误,不推荐,一般使用异步的方式,其实就是通过handler发送了一个删除请求,将View添加到mDyingViews中;
d. die方法本质调用了doDie方法,真正删除View的逻辑在该方法的dispatchDetachedFromWindow方法中,主要做了四件事:垃圾回收,通过Session的remove方法删除Window,调用View的dispatchDetachedFromWindow方法同时会回调View的onDetachedFromWindow以及onDetachedFromWindowInternal,调用WindowManagerGlobal的doRemoveView刷新数据。

具体需要研究源码。

Window的更新过程
Window的更新还是通过WindowManagerGloba的updateViewLayout方法。

a. WindowManagerGlobal的updateViewLayout;
b. 更新View的LayoutParams;
c. 更新ViewImple的LayoutParams,实现对View的重新测量,布局,重绘;
d. 通过WindowSession更新Window的视图,WindowManagerService.relayoutWindow()。

Window的创建过程

Activity的Window创建过程
当我们启动一个新的Activity时,相当于重新创建一个Activity。我们来具体分析下:

1、在Activity的attach中,系统会创建Activity所属的Window并为其设置回调(CallBack)。

2、Window是通过PolicyManager工厂类中的makeNewWindw()方法创建

3、makeNewWindw()方法中,通过new PhoneWindow()返回Window对象,Window的具体实现类是PhoneWindow。

4、Window创建好之后,通过PhoneWindow的setContentView将Activity与Window进行关联,这个方法大致步骤:

(1)创建DecorView(是Activity中的顶级View),由installDecor方法来完成,在方法内部会通过generateDecor方法直接创建DecorView。它的id是android.R.id.content。就是一个空白的FrameLayout

(2)将View添加到DecorView的mContentParent中,通过LayoutInflate.inflate()来添加。

(3)回调Activity的onContentChange方法通知Activity视图已经发生变化

(4) Activity onResume的时候会调用Activity的makeVisible方法真正完成DecorView的添加和显示。

Dialog的Window创建过程
Dialog的Window的创建过程和Activity类似,有以下步骤:
1.创建Window

通过PolicyManager的makeNewWindow方法创建Window;
2、初始化DecorView并将Dialog的视图添加到DecorView中
通过setContentView添加视图
3、将DecorView添加到Window中并显示
在Dialog的show方法中,会通过WindowManager将DecorView添加到Window中。

当Dialog关闭时,会通过WindowManager来移除DecorView

mWindowManager.removeViewImmediate(mDecor);
这里需要注意的是:普通的Dialog必须采用Activity的Context,如果采用Application的Context,会报错。(没有应用token的错误,token一般只有Activity拥有)

Toast的Window创建过程

Toast和Dialog不同,但也是基于Window来实现的,但是Toast有定时取消功能,所以系统采用了Handler。
在Toast的内部有两类IPC过程,

第一类是Toast访问NotifycationManagerService(NMS)
第二类是NofitycationManager回调Toast里的TN接口。

Toast属于系统Window,它内部的视图由两种方式指定:

1、系统默认样式,内部视图mNextView一种为系统默认样式
2、通过setView方法指定一个自定义View

Toast的显示和隐藏:

Toast的显示和隐藏都需要通过NMS来实现,由于NMS运行在系统的进程中,所以只能通过远程调用的方式来显示和隐藏Toast。

TN是一个Binder类,当NMS处理Toast的显示和隐藏请求时会跨进程回调TN中的方法,由于NMS运行在Binder线程池中,所以需要通过Handler将其切换到当前线程中。注意::由于这里使用了Handler,所以这意味着Toast无法再没有Looper的线程中弹出,这是因为Handler需要使用Looper才能完成切换线程的功能。

Toast显示时,调用了NMS中的enqueueToast方法。它内部首先将Toast请求封装为ToastRecord对象并将其添加到一个名为mToastQueue的队列中。(非系统应用最多塞50个)

当ToastRecord被添加到mToastQueue中后,NMS就会通过showNextToastLocked的callback来完成的,Toast显示由ToastRecord的callback方法中的show方法完成。这个callback实际上就是Toast中的TN对象的远程Binder所以最终调用的是TN中的方法,并运行在发起Toast请求应用的Binder线程池中。

Toast显示以后,NMS还会通过scheduleTimeoutLocked方法来发送一个延时消息(具体的延时取决于Toast的时长),延时后NMS会通过cancelToastLocked方法来隐藏Toast并将其从mToastQueue中移除。隐藏是通过ToastRecord的callback中的hide方法来实现的。

callback回调TN的show和hide方法后,会通过handler发送两个Runnable,里面的handleShow和handleHide方法是真正完成显示和隐藏Toast的地方。handleShow方法中将Toast的视图添加到Window中,handleHide方法将Toast视图从Window中移除。

阅读更多
文章标签: android
个人分类: 读书笔记
想对作者说点什么? 我来说一句

Android开发艺术探索

2018年04月17日 6.64MB 下载

android开发艺术探索书中源码

2015年11月24日 18.65MB 下载

Android开发艺术探索 完整版 pdf

2017年02月14日 31B 下载

android 开发艺术探索 源码

2017年10月29日 18.06MB 下载

Android开发艺术探索.pdf

2018年02月07日 207B 下载

Android开发艺术探索.mobi

2017年12月05日 2.77MB 下载

Android开发艺术探索之高清版本

2018年02月02日 5.78MB 下载

没有更多推荐了,返回首页

不良信息举报

android开发艺术探索(八)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭