学了又忘?通俗易懂 Android视图系统的设计与实现!(1)

1.3 如何优雅的衔接各窗口之间关系?

自定义View可以定制各种视图效果,窗口可以让View有条不紊的显示,一切又美好了起来。但问题又来了,每个App都会有很多个界面(窗口),仅靠窗口/View来控制窗口和视图会面临一个很致命的问题:"不具备跳转和回退功能"

按照我们的惯性思维,界面即可以跳转又可以回退,比如界面A跳转到界面B,按返回键理应退到界面A,这是一个标准的栈数据结构。

但关于界面的启动以及返回键的监听似乎与窗口不太搭嘎,所以Android基于单一设计原则封装了Activity,赋予窗口启动和回退功能,并由AMS统一调度Activity栈和生命周期。 又通过迪米特法则窗口的管理屏蔽在内部并暴露出onCreate、onStart、onResume...等模版方法, 让开发者只专注于视图排版(View)生命周期,无需关心窗口以及栈结构的存在

所以,单纯说通过Activity创建一个界面似乎又不那么准确,一切窗口均源自于WMS,而窗口中内容由View进行填充,Activity只是在内部"间接"通过WMS管理窗口并协调好窗口View的关系,最后再赋予栈、生命周期 等 功能而已。

关于Activity如何管理窗口/View ? 请看第二小节

2. 实现流程


读源码的目的是为了理清设计流程,千万不要因果倒置陷入到代码细节当中,所以要懂得挑重点,讲究点到为止。本文为了提供更好的阅读体验,会将源码中大部分无用信息删掉,只保留精华。

2.1 Activity的由来

Activity从何而来?想追溯到源头,恐怕要到从开天辟地时造就第一个受精卵开始

开天辟地造就的Zygote从何而来

Android系统会在开机时由Native层创建第一个进程init进程,随后init进程会解析一个叫init.rc的本地文件创建出Zygote进程

字如其名,Zygote的职责就是孵化进程。当孵化出的第一个进程SystemServer进程后退居幕后,通过Socket静等创建进程的呼唤,一切应用进程均由Zygote进程孵化

SystemServer进程的职责

SystemServerZygote自动创建的进程,并且会长时间驻留在内存中,该进程内部会注册各种Service 如:

  • ActivityManagerService(AMS):用来创建应用进程(通过socket ipc通知zygote进程)、管理四大组件
  • WindowManagerService(WMS):用来开辟和管理屏幕上的窗口,让视图有条不紊的显示
  • InputManagerService(IMS):用来处理和分发各种事件
  • 等等…

为什么要将这些系统服务放在单独进程?

AMS、WMS、IMS都是用来处理一些系统级别的任务,比如Activity存在任务栈/返回栈的概念,如果在通过Activity进行应用间跳转时,需要协调好任务栈/返回栈的关系,而不同应用又属于不同进程,所以需要一个地方能凌驾于所有应用进程之上,而单独进程是最好的选择。关于WMS、IMS等其他Service同理,就不再赘述

应用进程的创建过程

前面说到AMS可以通知Zygote进程孵化应用进程,那究竟何时通知呢?其实大家应该已经猜到了,通过点击桌面上应用图标可以开启一个应用,所以AMS就是在此时通知Zygote创建应用进程。但桌面又是什么东西它从何而来?其实桌面也是一个Activity,它由AMS自动创建

回归正题,点击应用图标到Activity的启动 这之间经历了什么流程?下面我简单列一下:

  • 当点击一个App图标时,如果对应的应用进程还没有创建则会通过Binder IPC通知到AMS创建应用进程

  • 应用进程启动后会执行我们所熟悉的main方法,而这个main方法则位于ActivityThread这个类中,main方法对应的就是Android主线程

  • ActivityThreadmain方法首先会调用Looper.loop(),用来循环处理主线程Hanlder分发的消息。

  • 接下来的main方法会发送一个BIND_APPLICATION的消息,Looper收到后会通过Binder IPC通知AMS创建App进程对应的Application

  • Application创建后会再次通过Binder IPC通知AMS要创建ActivityAMS验证后会回到App进程

  • 回到App进程后会间接调用ActivityThread#performLaunchActivity()来真正启动创建Activity,并且执行attach()onCreate()

tips

ApplicationActivity并不是通过AMS直接创建的,AMS只是负责管理和验证,真正创建具体对象还得到App进程

Android视图系统是一个很庞大的概念,几乎贯穿了整个Java Framework,由于作者能力以及篇幅的原因,无法一文将Java Framework讲解清楚。所以就描述式的说了下系统进程、应用进程以及Activity的由来,尽可能你更清晰的认识Android视图系统。

2.2 PhoneWindow不等价于"Window(窗口)"

我之所以第一小节没有将窗口描述成Window是怕大家将二者混淆,因为应用进程的Window/PhoneWindow和真正的窗口根本就是两个概念,作者也曾在阅读源码时就这个问题困惑了很久。

Android SDK中的Window是一个抽象类,它有一个唯一实现类PhoneWindowPhoneWindow内部会持有一个DecorView(根View),它的职责就是对DecorView做一些标准化的处理,比如标题、背景、导航栏、事件中转等,很显然与我们前面所说的窗口概念不符合

PhoneWindow何时被创建?

2.1小结我提到可以通过ActivityThread#performLaunchActivity()创建Activity,来看下其代码:

#ActivityThread

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

Activity activity = null;

//注释1

activity = mInstrumentation.newActivity(

cl, component.getClassName(), r.intent);

if (activity != null) {

//注释2.

activity.attach(…);

//注释3.

if (r.isPersistable()) {

mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

} else {

mInstrumentation.callActivityOnCreate(activity, r.state);

}

}

return activity;

}

复制代码

首先通过注释1处创建一个Activity对象,然后在注释2处执行其attach(..)方法,最后在通过callActivityOnCreate()执行ActivityonCreate()方法

先来看attach做了什么事情:

#Activity

final void attach(…){

mWindow = new PhoneWindow(this, window, activityConfigCallback);

mWindow.setWindowManager(…);

mWindowManager = mWindow.getWindowManager();

}

复制代码

Activity会在attach()方法中创建一个PhoneWindow对象并复制给成员变量mWindow,随后执行WindowManagersetter、getter。来重点看一下setter方法:

#Window

public void setWindowManager(…) {

if (wm == null) {

//注释1

wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

}

//注释2

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

}

复制代码

注释1处会通过系统服务获取一个WindowManager类型对象,用来管理Window

注释2会通过WindowManager创建一个WindowManagerImpl对象,实际上WindowManager是一个接口,它继承自ViewManager接口,而WindowManagerImpl是它的一个实现类

绕来绕去原来是通过WindowManager创建了另一个WindowManager,看起来多此一举,那Android为什么要这样设计呢?

首先WindowManager具备两个职责,管理Window创建WindowManager。系统服务获取的WindowManager具备创建Window功能,但此时并未与任何Window关联。而通过createLocalWindowManager创建的WindowManager会与对应的Window一对一绑定。所以前者用于创建WindowManager,后者用于与Window一对一绑定,二者职责明确,但让作者费解的是为什么不基于单一设计原则创建过程抽取至另一个类?如果有知道的同学可以评论区留言,事先谢过~

关于WindowManagerImpl如何管理Window先暂且不提,下面文章会说到

PhoneWindow已经创建完毕,但还没有跟Activity/View做任何关联。扒一扒PhoneWindow的源码你会发现,它内部只是设置了标题、背景以及事件的中转等工作,与窗口完全不搭嘎,所以切勿将二者混淆

2.3 DecorView的创建时机

通过2.2可知 Activityattach()运行完毕后会执行onCreate(),通常我们需要在onCreate()中执行stContentView()才能显示的XML Layout。关于stContentView() 顾名思义就是设置我们的Content View嘛,内部代码如下:

#Activity

public void setContentView(@LayoutRes int layoutResID) {

getWindow().setContentView(layoutResID);

}

public Window getWindow() {

return mWindow;

}

复制代码

首先通过getWindow()获取到attach()阶段创建的PhoneWindow,随后将layoutResID(XML Layout)传递进去,继续跟:

#PhoneWindow

ViewGroup mContentParent;

public void setContentView(int layoutResID) {

//注释1

if (mContentParent == null) {

installDecor();

} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

mContentParent.removeAllViews();

}

if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

} else {

//注释2

mLayoutInflater.inflate(layoutResID, mContentParent);

}

}

复制代码

注释1处会判断mContentParent是否为空,如果为空会通过installDecor()对其实例化,否则移除所有子View。

注释2处会将layoutResID对应的XML加载到mContentParent。到此为止唯一的疑问是mContentParent如何被创建的,跟一下installDecor()

#PhoneWindow

private void installDecor() {

if (mDecor == null) {

mDecor = generateDecor(-1);

} else {

mDecor.setWindow(this);

}

if (mContentParent == null) {

mContentParent = generateLayout(mDecor);

}

}

复制代码

首先创建DecorView类型对象并赋值给引用mDecor。那什么是DecorView

DecorView继承自FrameLayout,内部有一个垂直布局的LinearLayout用来摆放状态栏、TitleBar、ContentView、导航栏,其中ContentView就是用来存放由Activity#setContentView传入的Layout。之所以设计出DecorView是因为状态栏、导航栏等需要做到系统统一,并将其管控操作屏蔽在内部,只暴露出ContentView由开发者填充,符合迪米特法则

再回到mDecor的创建过程,跟一下generateDecor(-1)代码:

#PhoneWindow

protected DecorView generateDecor(int featureId) {

return new DecorView(context, featureId, this, getAttributes());

}

复制代码

直接new出来了一个DecorView。再回到我们最初的疑问,mContentParent从何而来?installDecor()创建出DecorView会通过generateLayout(mDecor)创建mContentParentgenerateLayout(mDecor)代码很长就不贴了,内部会通过mDecor获取到mContentParent并为其设置主题、背景等

到此阶段DecorView创建完毕并与XML Layout建立了关联,但此时根View(DecorView)还未与窗口建立关联,所以是看不到的。

为什么要在onCreate执行setContentView?

通过setContentView可以创建DecorView,而一个Activity通常只有一个DecorView(撇去Dialog等),如若将setContentView放在start、resume可能会创建多个DecorView,进而会造成浪费。所以onCreate是创建DecorView的最佳时机

2.4 ViewRootImpl如何协调View和Window的关系?

Activity启动后会在不同时机通过ActivityThread调用对应的生命周期方法onResume是一个特殊的时机它通过ActivityThread#handleResumeActivity被调用,代码如下:

#PhoneWindow

public void handleResumeActivity(…) {

//注释1

final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

final Activity a = r.activity;

//注释2

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

ViewManager wm = a.getWindowManager();

WindowManager.LayoutParams l = r.window.getAttributes();

//注释3

wm.addView(decor, l);

}

复制代码

  • 注释1处 会间接调用ActivityonResume方法

  • 注释2处 通过Activity获取PhoneWindow、DecorView、WindowManager,它们的创建时机前面小结有写,忘记的可以回翻阅读。

  • 注释3处 调用了WindowManageraddView方法,顾名思义就是将DecorView添加至Window当中,这一步非常关键

关于WindowManager的概念2.2小结提到过,它是一个接口有一个实现类WindowManagerImp,跟一下其addView()方法

#WindowManagerImp

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

public void addView(…) {

mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId());

}

复制代码

内部调用了mGlobaladdView()方法,其实不光addView几乎所有WindowManager方法都是通过委托mGlobal去实现,这种写法看似很奇怪,但实际上这种设计不仅不奇怪而且还很精妙,具体精妙在何处?我列出以下三点:

  • WindowManager提供的功能全局通用不会与某个View/Window单独绑定,为了节省内存理应设计出一个单例

  • WindowManagerImp具备多个职责如Token管理、WindowManager功能等,所以通过单一设计原则WindowManager功能拆分到另一个类中即WindowManagerGlobal,并将其定义为单例。

  • 为了不违背迪米特法则又通过组合模式将WindowManagerGlobal屏蔽在内部。

回归正题,来看mGlobaladdView()方法:

#WindowManagerGlobal

/**

  • 用来存储所有的DecorView

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-qyK6DoPm-1715894539011)]

[外链图片转存中…(img-p4M5mJde-1715894539013)]

[外链图片转存中…(img-5bPc1rYA-1715894539015)]

[外链图片转存中…(img-R7vmoeHD-1715894539016)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当我们谈论分布式系统时,我们指的是由多个独立计算机或节点组成的系统,这些节点通过网络进行通信和协调,共同完成一些任务。其原理可以简单地解释为以下几个关键点: 1. 分布和复制:分布式系统将数据和任务分布到多个节点上。数据可以被复制到不同的节点上,以提高数据的可靠性和性能。 2. 通信和协调:分布式系统中的节点通过网络进行通信,交换信息和协调工作。它们使用消息传递、远程过程调用等机制进行通信,并使用协议来确保可靠性和一致性。 3. 容错和容灾:分布式系统应对节点故障和网络问题时具备容错和容灾能力。它们使用冗余副本和备份来保护数据,以及使用故障检测和恢复机制来处理节点故障。 4. 一致性和同步:分布式系统需要保证数据的一致性。为此,它们使用一致性协议(如Paxos、Raft等)来确保在不同节点上的数据副本之间达成一致。 5. 扩展性和负载均衡:分布式系统可以通过添加更多的节点来扩展其容量和性能。负载均衡机制可以将请求分配给合适的节点,以平衡负载并提高系统的吞吐量。 总的来说,分布式系统的原理是将任务和数据分布到多个节点上,并通过通信、协调、容错、一致性和扩展性等机制来实现分布式计算和存储。它们旨在提高系统的可靠性、性能和可扩展性,以满足大规模和高并发的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值