该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!另外,本系列文章知识可能需要有一定Android开发基础和项目经验的同学才能更好理解,也就是说该系列文章面向的是Android中高级开发工程师。


第四篇了,,接着上一篇说 (怎么感觉还是没人评论呢)


在上一篇文章中我们主要分析了android.app.ActivityThread的main函数以及setContentView。另外我们还稍微分析了一下我们自己的源码,通过WindowManager添加View。我们知道调用setContentView把我们自己的xml布局添加到了DecorView ID为ID_ANDROID_CONTENT的布局后,最终还是会调用WindowManager.addView把DecorView加入PhoneWindow。到这里呢,我们把流程梳理一下。还是上图:


相信读者根据上图再结合前面所讲的内容应该对Activity的创建和显示有了初步的认识。那么本章我们来继续讲Activity的显示。该注意的是本系列并不意在带领读者去看清每一步具体的源码。在前面的文章中我也很少贴出源码。本系列文章意在让读者对Android系统有个更整体的把握。我所写的每一章知识都有可能在实际工作中用到。就如前面所讲解的Android下的进程问题以及Activity的生命周期以及本章要讲解的View的五大过程的基础ViewRootImpl。而理解View的五大过程(一般文章里都是三大过程)以及View的事件体系是更好的去自定义View的基础。

本章接着第Ⅲ篇的文章来讲解View框架树的动力所在ViewRootImpl


上一章的最后我们讲到了使用WindowManager添加View。这一章我们来具体分析。最近我也发表了三篇文章,可反响度一般,我也是刚写技术博客。肯定有些不足之处。这次稍微改变些风格,在分析的时候贴一些源码上去。


从第一篇文章中我们就知道了使用WindowManager.addView方法添加View。那么看看该类的实现吧。

public interface WindowManager extends ViewManager {
//这里我们只列出了一部分函数,但是并没有addView、updateViewLayout、removeView这三个函数
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
...
}

好吧,果然没有这么简单,WindowManager是个接口,而且在其方法中没有找到addView方法,那么我们只能看看ViewManager了

public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}

还好是找到了,ViewManager没有再继承其他接口了。(要不然真不知道要找到什么时候去。)

既然WindowManager是个接口,那肯定要找它的实现类了。(在这里安利一个比较简单的方法,在Android Studio中)

这里我们很幸运只找到了一个WindowManager的实现类(有的时候可能有很多个,当出现多个的时候,那只有一个个去看了)。这里我们来看WindowManagerImpl(看到这个 类的名字我们心里了然一笑,果然是java的命名规范)。

public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow; private IBinder mDefaultToken; public WindowManagerImpl(Context context) {
this(context, null);
} private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
} public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
} public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
} /**
* Sets the window token to assign when none is specified by the client or
* available from the parent window.
*
* @param token The default token to assign.
*/
public void setDefaultToken(IBinder token) {
mDefaultToken = token;
} @Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
} @Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
} private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
// Only use the default token if we don't have a parent window.
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
} // Only use the default token if we don't already have a token.
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
} @Override
public void removeView(View view) {
mGlobal.removeView(view, false);
} @Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
} @Override
public void requestAppKeyboardShortcuts(
final KeyboardShortcutsReceiver receiver, int deviceId) {
IResultReceiver resultReceiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
List<KeyboardShortcutGroup> result =
resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
receiver.onKeyboardShortcutsReceived(result);
}
};
try {
WindowManagerGlobal.getWindowManagerService()
.requestAppKeyboardShortcuts(resultReceiver, deviceId);
} catch (RemoteException e) {
}
} @Override
public Display getDefaultDisplay() {
return mContext.getDisplay();
}
}

WindowManagerImpl的源码如上所示,我们可以看到WindowManagerImpl的addView方法,WindowManagerImpl把工作交给了WindowManagerGlobal

/**
WindowManagerGlobal 源码比较长,这里我们只列出了一部分
*/
public final class WindowManagerGlobal {
private WindowManagerGlobal() {
} public static void initialize() {
getWindowManagerService();
} public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
} public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
} public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
} public static IWindowSession peekWindowSession() {
synchronized (WindowManagerGlobal.class) {
return sWindowSession;
}
} //addView方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... //参数检查 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//① 如果当前窗口需要被添加为另一个窗口的附属窗口(子窗口),则需要父窗口视自己的情况对当前窗口的布局参数进行调整
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} ViewRootImpl root;
View panelParentView = null; int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
//同一个View不允许被添加2次
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
//② 创建一个ViewRootImpl对象并保存在root变量中
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams);
//③ 保存作为窗口的控件、布局参数以及新建的ViewRootImpl
mViews.add(view);
mRoots.add(root);
mParams.add(wparams); // do this last because it fires off messages to start doing things
try {
// ④ 将作为窗口的控件设置给ViewRootImpl.这个动作将导致ViewRootImpl向WMS添加新的窗口、申请Surface以及托管控件在Surface上的重绘工作。这才是真正意义上完成了窗口的添加工作。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
} public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
} final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams); synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
} public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
} synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
} throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
} }

我们可以看到WindowManagerGlobal的私有构造函数以及getInstance()这个熟悉的静态方法名字。可以看出WindowManagerGlobal是个典型的单例

WindowManagerGlobal 的addView方法并不复杂,其主要的关键点我们已经标注并写了注释。也就是说WindowManagerGlobal的职责如下:

  1. 同意管理整个进程中所有窗口的信息。包括控件、布局参数以及ViewRootImpl这三个元素。(这一点从第③个注释可以看出)
  2. WindowManagerGlobal将窗口的创建、销毁、布局更新等任务交给了ViewRootImpl完成。

本篇总结

本篇文章分析了WindowManager的addView的过程,WindowManager是个接口,它的实现类是WindowManagerImpl类,而WindowManagerImpl又把相关逻辑交给了WindowManagerGlobal处理。WindowManagerGlobal是个单例类,它在进程中只存在一个实例,是它内部的addView方法最终创建了我们的核心类ViewRootImpl。ViewRootImpl实现了ViewParent接口,作为整个控件树的根部,它是控件树正常运作的动力所在,控件的测量、布局、绘制以及输入事件的派发处理窦世友ViewRootImpl出发。它是WindowManagerGlobal的实际工作者。


下篇预告

在下一篇文章中我们将深入介绍ViewRootImpl的工作流程。测量、布局、以及绘制。


此致,敬礼

Android开发之漫漫长途 Ⅳ——Activity的显示之ViewRootImpl初探的更多相关文章

  1. Android开发之漫漫长途 Ⅴ——Activity的显示之ViewRootImpl的PreMeasure、WindowLayout、EndMeasure、Layout、Draw

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  2. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(1)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android开发之漫漫长途 Ⅲ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  5. Android开发之漫漫长途 XI——从I到X的小结

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  6. Android开发之漫漫长途 Ⅵ——图解Android事件分发机制(深入底层源码)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  7. Android开发之漫漫长途 番外篇——自定义View的各种姿势2

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  8. Android开发之漫漫长途 XIV——ListView

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  9. Android开发之漫漫长途 Ⅰ——Android系统的创世之初以及Activity的生命周期

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>中的相关知识,再次表示该书 ...

随机推荐

  1. Uva 3708 Graveyard

    题意:在周长为10000的圆上等距分布着n个雕塑.现在又有m个新雕塑加入(位置可以随意放),希望所有n+m个雕塑在圆周上均匀分布. 这就需要移动其中一些原有的雕塑.要求n个雕塑移动的距离最小. (2& ...

  2. 【技巧】datagrid锁定列后重新加载时出现错位问题的解决

    [问题描述]:有时候datagrid设置了锁定列后,在重新加载datagrid数据时,出现锁定列与非锁定列数据错位的问题,如图: [问题分析]:查看css样式我们发现,锁定的列和非锁定的列属于两个不同 ...

  3. 编译LNMP之nginx+php-fpm

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  4. firewalld 操作实践

    1.firewalld 从名称上看,模仿的是硬件防火墙的概念,zone. 所有的接口都必须属于某个zone . 在zone内配置规则. 2.  常用的方法是 增加对一个tcp或者udp端口号的允许通过 ...

  5. 基于EF Core的Code First模式的DotNetCore快速开发框架

    前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...

  6. 当谈到 GitLab CI 的时候,我们该聊些什么(上篇)

    "微服务"这个概念近两年非常热,正在慢慢改变 DevOps 的思路.微服务架构把一个庞大的业务系统拆解开来,每一个组件变得更加独立自治.松耦合.但是,同时也伴随着部署单元粒度越来越 ...

  7. JAVA提高五:注解Annotation

    今天我们学习JDK5.0中一个非常重要的特性,叫做注解.是现在非常流行的一种方式,可以说因为配置XML 比较麻烦或者比容易查找出错误,现在越来越多的框架开始支持注解方式,比如注明的Spring 框架, ...

  8. Android Stuido 提高开发效率的插件

    好久没有更新博客了,最近搞个listview搞得半死不活的,心累~~ 今天带来的是Android Studio插件的整理,全是我已经安装使用的,写这篇博文的目的也是因为我怕我自己给忘记怎么用(尴尬) ...

  9. 80806汇编(5)——[BX]和Loop指令

    80806汇编(5)--[BX]和Loop指令 已经好久没写点东西了,国庆节就一直想弄个个人网站,这段时间一直在弄那个,虽然有现成的框架(Hexo),但是总想弄出自己的效果来,但是最后还是有些差强人意 ...

  10. dubbo的架构

    dubbo架构图如下所示: 节点角色说明: Provider: 暴露服务的服务提供方. Consumer: 调用远程服务的服务消费方. Registry: 服务注册与发现的注册中心. Monitor: ...