###基本配置
1 | 安装: |
本地仓库及操作
1 | git init (初始化一个仓库) |
####远程仓库
1 | git remote add [远程仓库名] [远程仓库地址] |
####添加上游分支
1 | 给fork配置远程库 |
###基本配置
1 | 安装: |
1 | git init (初始化一个仓库) |
####远程仓库
1 | git remote add [远程仓库名] [远程仓库地址] |
####添加上游分支
1 | 给fork配置远程库 |
源码基于:Android R
Watchdog字面上是“看门狗”的意思,有做过嵌入式低层的朋友应该知道,为了防止嵌入式系统MCU里的程序因为干扰而跑飞,专门在MCU里设计了一个定时器电路,叫做看门狗。当MCU正常工作的,每隔一段时间会输出一个信号给看门狗,也就是所谓的喂狗。如果程序跑飞,MCU在规定的时间内没法喂狗,这时看门狗就会直接触发一个reset信号,让CPU重新启动。
在Android系统的framework中,设计了一个系统服务Watchdog,它类似于一个软件看门狗,用来保护重要的系统服务。它的源代码位于:
frameworks/base/services/core/java/com/android/server/Watchdog.java
public class Watchdog extends Thread { }
想要分析一个功能代码,可能先从本身的源头找起,对于Java 类首先看的就是类的定义以及构造构造函数啦!
从这里看 WatchDog 其实一个Thread,这个Thread 可能比较特殊而已,至于怎么特殊,下面会在SystemServer 分析的时候说明。那对于一个Thread,核心的操作部分就是run() 函数了,这个最重要的部分会放在最后解析。
再来看下WatchDog 的构造函数:
private Watchdog() { }
WatchDog 构造函数是private,对于外界获取对象的接口为:
public static Watchdog getInstance() { if (sWatchdog == null) { sWatchdog = new Watchdog(); } return sWatchdog; }
外界获取WatchDog 就是通过getInstance(),至于这个“外界”后面会补充。
/frameworks/base/services/java/com/android/server/SystemServer.java private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startBootstrapServices"); // Start the watchdog as early as possible so we can crash the system server // if we deadlock during early boot t.traceBegin("StartWatchdog"); final Watchdog watchdog = Watchdog.getInstance(); watchdog.start(); t.traceEnd(); ... t.traceBegin("InitWatchdog"); watchdog.init(mSystemContext, mActivityManagerService); t.traceEnd(); ... }
从第一节得知 WatchDog 实际是一个 thread,而且是以单例的形式存在,则需要确定其启动的地方。
这部分代码主要作用:
由此,WatchDog 线程是在 system_server 进程中启动,专门用来给 system_server 中的其他线程喂狗。
private Watchdog() { //线程名为watchdog super("watchdog"); //将fg thread 单独提出作为主要的checker mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread", DEFAULT_TIMEOUT); mHandlerCheckers.add(mMonitorChecker); //创建主线程的checker mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread", DEFAULT_TIMEOUT)); //创建UI thread 的checker mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", DEFAULT_TIMEOUT)); //创建Io thread 的checker mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", DEFAULT_TIMEOUT)); //创建display thread 的checker mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(), "display thread", DEFAULT_TIMEOUT)); //创建animation thread 的checker mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(), "animation thread", DEFAULT_TIMEOUT)); //创建surface animation thread 的checker mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(), "surface animation thread", DEFAULT_TIMEOUT)); //fg thread的checker 中添加对binder 的checker addMonitor(new BinderThreadMonitor()); // 添加对watchdog 相关目录的监控 mOpenFdMonitor = OpenFdMonitor.create(); mInterestingJavaPids.add(Process.myPid()); // See the notes on DEFAULT_TIMEOUT. assert DB || DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;}
设WatchDog 的线程名为 watchdog
WatchDog 中靠 HandlerChecker 来完成check 工作,每个 HandlerChecker 伴随一个Handler,即一个独立的 Looper 和 Thread。
WatchDog 在创建的时候指定对 FgThread、MainThread、UIThread、IoThread、DisplayThread、AnimationThread、SurfaceAnimationThread 等thread 的监控,当然后期可以通过接口动态添加到 check list(mHandlerCheckers) 中,例如通过接口 addThread(),该函数后面会详细剖析,可以查看第 5.2 节。
MainThread 是在 SystemServer 运行的时候创建的
frameworks/base/services/java/com/android/server/SystemServer.javaprivate void run() { … Looper.prepareMainLooper(); …}
其他的Thread 最终都是继承自HandlerThread(详细看 **Android HandlerThread 详解**)
HandlerChecker 有3 个参数分别是Handler 对象、name、以及触发watchdog 的最大时间间隔,详细的HandlerChecker 看下面第6节。
private static final class BinderThreadMonitor implements Watchdog.Monitor { @Override public void monitor() { Binder.blockUntilThreadAvailable(); } }
monitor() 会调用到 native 层 IPCThreadState:
frameworks/native/libs/binder/IPCThreadState.cpp void IPCThreadState::blockUntilThreadAvailable(){ pthread_mutex_lock(&mProcess->mThreadCountLock); while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) { ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n", static_cast<unsigned long>(mProcess->mExecutingThreadsCount), static_cast<unsigned long>(mProcess->mMaxThreads)); pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock); } pthread_mutex_unlock(&mProcess->mThreadCountLock);}
BinderThreadMonitor 会被添加到 fg thread 中,主要是用于确认binder 是否有出现不够用的情况,例如,假设 binder 的max thread 为15个,超过15后就需要 check 是否存在 binder 阻塞。
public static OpenFdMonitor create() { // Only run the FD monitor on debuggable builds (such as userdebug and eng builds). if (!Build.IS_DEBUGGABLE) { return null; } final StructRlimit rlimit; try { rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE); } catch (ErrnoException errno) { Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno); return null; } final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK)); return new OpenFdMonitor(new File("/data/anr"), fdThreshold);}
主要是触发WatchDog 或者 HALF time 的时候,将这里存的 pid 所对应的堆栈dump 出来。AMS 中每次启动一个进程时,都会调用 handleProcessStartedLocked() 最终调用 WatchDog**.**processStarted() 添加到 WatchDog 中,而process 也是有限定的,如下:
private static boolean isInterestingJavaProcess(String processName) { return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName) || processName.equals("com.android.phone"); } public void processStarted(String processName, int pid) { if (isInterestingJavaProcess(processName)) { Slog.i(TAG, "Interesting Java process " + processName + " started. Pid " + pid); synchronized (this) { mInterestingJavaPids.add(pid); } } }
public void init(Context context, ActivityManagerService activity) { mResolver = context.getContentResolver(); mActivity = activity; context.registerReceiver(new RebootRequestReceiver(), new IntentFilter(Intent.ACTION_REBOOT), android.Manifest.permission.REBOOT, null); }
注册了reboot 的广播,软重启的操作是在这里进行的:
final class RebootRequestReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { if (intent.getIntExtra("nowait", 0) != 0) { rebootSystem("Received ACTION_REBOOT broadcast"); return; } Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent); } } void rebootSystem(String reason) { Slog.i(TAG, "Rebooting system because: " + reason); IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE); try { pms.reboot(false, reason, false); } catch (RemoteException ex) { } }
该软重启的 intent 中需要设定一个参数 nowait,且不为0.
public void addMonitor(Monitor monitor) { synchronized (this) { mMonitorChecker.addMonitorLocked(monitor);//通过此接口会将monitor添加到fg thread中 } }
本函数是将 monitor 添加到 mMonitorChecker 中监控;
public interface Monitor { void monitor(); }
例如AMS 构造函数中:
Watchdog.getInstance().addMonitor(this);
public void addThread(Handler thread) { addThread(thread, DEFAULT_TIMEOUT); } public void addThread(Handler thread, long timeoutMillis) {//通过此接口新建HandlerChecker synchronized (this) { final String name = thread.getLooper().getThread().getName(); mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis)); } }
通过本函数新建 HandlerChecker 并添加到 mHandlerCheckers 中,然后进行 check 该线程是否阻塞,默认timeout 为 60s;
例如 AMS 构造函数中:
Watchdog.getInstance().addThread(mHandler);
在分析WatchDog 核心函数 run 之前,先来分析下核心处理类 HandlerChecker,所有 thread 的监听、处理都是在这个类中。
public final class HandlerChecker implements Runnable {
HandlerChecker(Handler handler, String name, long waitMaxMillis) { mHandler = handler; mName = name; mWaitMax = waitMaxMillis; mCompleted = true;}
构造参数有三个:
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
一个HandlerChecker 中可以添加多个monitor,为了安全考虑,防止在monitor check 过程中添加新的monitor,将过程分两部分:
详细看scheduleCheckLocked() 函数,即第 6.4 节。
public void scheduleCheckLocked() { if (mCompleted) { //step 1 // Safe to update monitors in queue, Handler is not in the middle of work mMonitors.addAll(mMonitorQueue); mMonitorQueue.clear(); } if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) || (mPauseCount > 0)) { //step 2 mCompleted = true; return; } if (!mCompleted) { //step 3 // we already have a check in flight, so no need return; } mCompleted = false; //step 4 mCurrentMonitor = null; mStartTime = SystemClock.uptimeMillis(); mHandler.postAtFrontOfQueue(this);}
逻辑比较简单,见代码中注释,将逻辑分为4个部分:
public void run() { final int size = mMonitors.size(); for (int i = 0 ; i < size ; i++) { synchronized (Watchdog.this) { mCurrentMonitor = mMonitors.get(i); } mCurrentMonitor.monitor(); } synchronized (Watchdog.this) { mCompleted = true; mCurrentMonitor = null; }}
通过一个循环分别调用每一个 monitor 的 monitor() 函数,如果其中一个阻塞了,则无法退出该循环,mCompleted 状态也不会变成 true,如果 mCompleted 为false,WatchDog 只能通过时间来进一步确认状态,在下面一节中会详细说明。
用 AMS 中的 monitor 来举例:
frameworks/base/services/core/java/com/android/server/am/AMS.java public void monitor() { synchronized (this) { } }
AMS 的 monitor 就是为了 check AMS 是否死锁了。
上面的基本函数都大概解析完了,对于一个Thread 那最重要的肯定还是run() 函数,这里也是WatchDog 的监测机制所在,因为代码比较多,这里裁剪分析。
WatchDog 在整个系统运行过程中都需要存在的,除非了进程或是系统重启了,不然WatchDog 需要长期运行。
public void run() { boolean waitedHalf = false; while (true) { ... } }
synchronized (this) { long timeout = CHECK_INTERVAL; for (int i=0; i<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); hc.scheduleCheckLocked(); }
读取所有的 mHandlerCheckers,执行每一个 checker 中的 scheduleCheckLocked() 函数,下面会等待 30 s 的时间,然后在收集这些 checker 的状态。
注意两点:
long start = SystemClock.uptimeMillis(); while (timeout > 0) { if (Debug.isDebuggerConnected()) { debuggerWasConnected = 2; } try { wait(timeout); // Note: mHandlerCheckers and mMonitorChecker may have changed after waiting } catch (InterruptedException e) { Log.wtf(TAG, e); } if (Debug.isDebuggerConnected()) { debuggerWasConnected = 2; } timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); }
这里等待30s,直到时间完全用完。此处调用 wait() 会临时释放 this 这把锁,这样所有的 checker 就会进入下一步处理,即 runnable() 处理。
final int waitState = evaluateCheckerCompletionLocked(); if (waitState == COMPLETED) { // The monitors have returned; reset waitedHalf = false; continue; } else if (waitState == WAITING) { // still waiting but within their configured intervals; back off and recheck continue; } else if (waitState == WAITED_HALF) { if (!waitedHalf) { Slog.i(TAG, "WAITED_HALF"); // We've waited half the deadlock-detection interval. Pull a stack // trace and wait another half. ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids); ActivityManagerService.dumpStackTraces(pids, null, null, getInterestingNativePids(), null); waitedHalf = true; } continue; }
在 30s 时间等待完成后,通过 evaluateCheckerCompletionLocked() 函数来收集所有 checkers 的状态。
状态分:
blockedCheckers = getBlockedCheckersLocked(); subject = describeCheckersLocked(blockedCheckers);
最开始是统计出OVERDUE 的HandlerChecker,存入blockedCheckers 中,并统计这些HandlerChecker 的描述。
private ArrayList<HandlerChecker> getBlockedCheckersLocked() { ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>(); for (int i=0; i<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); if (hc.isOverdueLocked()) { checkers.add(hc); } } return checkers; }
统计所有状态OVERDUE 的HandlerChecker。
private String describeCheckersLocked(List<HandlerChecker> checkers) { StringBuilder builder = new StringBuilder(128); for (int i=0; i<checkers.size(); i++) { if (builder.length() > 0) { builder.append(", "); } builder.append(checkers.get(i).describeBlockedStateLocked()); } return builder.toString(); }
统计所有符合条件的OVERDUE 的HandlerChecker 的描述,以逗号分割。
例如:
11-06 00:05:28.603 2197 2441 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on ActivityManager (ActivityManager)
接着就是保存日志,包括一些运行时的堆栈信息,这些日志是我们解决Watchdog问题的重要依据。如果判断需要杀掉system_server进程,则给当前进程(system_server)发送signal 9,详细信息看 Android 系统中WatchDog 日志分析
Watchdog.getInstance().addMonitor(this); Watchdog.getInstance().addThread(mHandler);
来看下一直说的monitor 是什么东西:
public void monitor() { synchronized (this) { } }
这里其实就是确认AMS 中是否存在死锁,30秒后还是没有放出来这个锁,WatchDog 会在给一次机会,如果还是没有释放的话,会打印堆栈信息并且结束AMS 所在进程。
需要注意的是WatchDog 的监控,除了WatchDog 构造函数中的默认Thread,外界若需要添加监控,需要通过两种途径:
两个接口区别在于参数,一个传入的是monitor,例如WatchDog 内部的 BinderThreadMonitor 或AMS 的monitor,monitor() 的处理是在 fg thread 中;另一个传入的是Handler,通过第 5 节得知,addThread() 会创建一个HandlerChecker 对象单独监控;
那么,结合 HandlerChecker 的 **scheduleCheckLocked() **函数,可以认定,WatchDog 可以监控的形式有两种:
都是通过addMonitor() 添加到 fg thread 的 HandlerChecker 中。
都是通过 Handler 的方式添加到各自的 HandlerChecker 中,有的是在 WatchDog 的构造的时候添加,有的是后期通过 addThread() 方式添加。
第 8 节中列出了AMS 代码中 addMonitor() 和 addThread() 两种方式都使用了,所以在dump trace 的时候会列举 fg thread 的 stack trace 和 ActivityManager stack trace:
11-06 00:05:28.603 2197 2441 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on ActivityManager (ActivityManager)11-06 00:05:28.603 2197 2441 W Watchdog: foreground thread stack trace:11-06 00:05:28.603 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.monitor(ActivityManagerService.java:23862)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.Watchdog$HandlerChecker.run(Watchdog.java:211)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.handleCallback(Handler.java:790)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:99)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)11-06 00:05:28.604 2197 2441 W Watchdog: ActivityManager stack trace:11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.idleUids(ActivityManagerService.java:23305)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService$MainHandler.handleMessage(ActivityManagerService.java:2428)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:106)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)11-06 00:05:28.604 2197 2441 W Watchdog: *** GOODBYE
具体的可以看 Android 系统中WatchDog 日志分析
Android 中的WatchDog 主要是监测系统中重要服务,例如AMS、WMS 等,当注册的monitor 无法通过检测,或者是消息处理超时的时候就会触发WatchDog,最后可能会引起系统的重启。
下一篇博文 Android 系统中WatchDog 日志分析 中会结合实例详解run() 第 4 步保存日志。
###项目经历
重庆捷旭科技有限公司 2021.02-至今 Android工程师
捷宇rk3566-android13_r6 framework定制开发
产品简介:http://www.fjjieyu.com/product/index/133
1.将云法庭apk内置成系统应用并实现不可退出不可卸载的需求;
2.修改开关机动画为云上共享法庭的启动动画;
3.app执行shell命令(自定义服务实现app通过自定义服务与adbd通信来实现shell命令);
4.实现带鱼屏宽屏的分屏场景-双屏动画切换移动功能;
5.冻屏bug处理
6.壁纸切换闪黑bug解决
云上共享法庭 android开发
重庆全市法院数智法庭建设的重点项目,实现法院庭审从排期到闭庭签字的全流程办案,独立负责里面的内网法官/当事人/签字
端/手机端/电视端的开发及适配处理;
信息发布 android开发
类似与广告机的应用,动态实现图片/音视频/文字/定制布局等不同场景的展示及开机自启和自动升级等功能;
案件排期 android开发
一款基层法院窗口信息显示应用,通过与条屏厂家对接实现条屏的适配显示窗口信息等;
傲视取号机 android开发
一款基层法院取号应用,通过与硬件厂家对接实现刷身份证,取号,叫号,切纸等操作;
重庆国狮科技有限公司 2018.02-2019.03 Android组负责人
重庆全心协力科技有限公司 2016.04-2018.02 Android工程师
####个人信息
姓名:唐玉龙 状态:在职
性 别:男 年 龄:34
手 机:18996195388 邮 箱:1104653262@qq.com
学校:重庆交通大学(非全) 本科 专 业:计算机科学与技术
工作年限:7年app&1年framework 岗 位:Android研发工程师
简书:https://www.jianshu.com/u/517b3c07af90 博客:https://cq-tyl.github.io
####专业技能
####工作经历
重庆捷旭科技有限公司 2020.03-至今 Android工程师
重庆国狮科技有限公司 2018.06-2020.02 Android组负责人
重庆全心协力科技有限公司 2016.04-2018.06 Android工程师
####重点项目经历
捷宇M32-rk3566-android13_r6 framework定制开发
云上共享法庭APP开发
出行加司机&乘客 android开发
项目简介:一款专用于出行加网约车平台司机接单,网约车乘客派单的应用,根据司机注册公司及车辆信息,可动态接取不同类型的网约车订单,乘客可选择出租车/专车/顺风车/闪电送/代驾等不同类型的车辆并派单,应用也提供了店铺优惠信息的发布以及领取等各种优惠活动以激励用户的使用,该应用已繁衍出小巷约车应用;
项目职责:独立负责该项目的乘客及司机端的开发及适配任务;
需求&技术:
先来看一下MVC、MVP、MVVM的架构图。
从这些架构图中,可以看到每种架构都有3个模块以及数据流动方向箭头。
模块
在系统架构中,首先要做的就是把系统整体按照一定的原则划分成模块。
数据流动
模块划分之后,模块之间的通信,就是数据的流动。在Android中,流动数据包括两部分,事件和数据。
架构
模块和模块之间的数据通信方式构成不同的架构。在这3种架构中,都是把系统整体划分成了3个模块:视图层,数据层,业务层。 他们之间的区别在于,模块之间的通信方式(数据流动方向)不一致。
视图层:在MVC架构中, Android的xml布局文件和Activity/Fragment文件被划分为View视图层。 因为xml作为视图层功能太弱,只能够实现页面的布局,不能够实现页面数据和事件的处理。需要和Activity一起才能够构成一个完整的视图层。
业务层:大多数的MVC架构开发的安卓项目, 并没有把Controller业务层独立出来,而是将业务层也在Activity/Fragment中实现。这导致了Activity/Fragment的代码非常臃肿,这就是MVC的缺点之一。 在本例中,我们会将业务层独立出来,实现一个标准的MVC架构。
数据层:数据层Model指的是,数据管理模块,这包括了数据的获取,处理。存储等。 MVP、MVVM的架构中的Model也是一样。后面不再赘述。
代码结构
xml代码
1 | <?xml version="1.0" encoding="utf-8"?> |
Activity代码
public class MVCActivity extends AppCompatActivity {
TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
tvResult = findViewById(R.id.tv_result);
TextView tvAccount = findViewById(R.id.tv_account);
TextView tvPwd = findViewById(R.id.tv_pwd);
Button btnLogin = findViewById(R.id.btn_login);
MVCController mvcController = new MVCController();
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mvcController.login(tvAccount.getText().toString(),tvPwd.getText().toString(), MVCActivity.this);
}
});
}
public void loginSuccess(){
tvResult.setText("登录结果:登录成功");
}
public void loginFail(){
tvResult.setText("登录结果:登录失败");
}
}
Controller代码
public class MVCController {
MVCModel mvcModel;
public MVCController() {
mvcModel = new MVCModel();
}
public void login(String account, String pwd, MVCActivity loginActivity) {
mvcModel.login(account, pwd, loginActivity);
}
}
Model代码
public class MVCModel {
public void login(String account, String pwd, MVCActivity loginActivity){
if (account == null || account.length()==0) {
loginActivity.loginFail();
}
if (pwd == null || pwd.length()==0) {
loginActivity.loginFail();
}
if ("user123".equals(account) && "pwd123".equals(pwd)){
loginActivity.loginSuccess();
}
}
}
在Activity中监听登录按钮的事件,接收到事件之后,调用Controller的登录方法处理登录逻辑,在Controller的登录方法中调用Model请求网络数据(这里是模拟)判断是否登录成功,Model拿到登录结果后,调用Activity的方法刷新页面数据,展示登录结果。
优点:通过划分模块的方式,将系统分成了3个模块,视图层,业务层和数据层。 代码开发实现不再是只在一个代码文件中,一定程度便于程序开发。
缺点:但是三个模块之间还存在很强的耦合关系。 不利于业务需求的更变和代码维护工作。
MVP架构是基于MVC的改进,将MVC的中Controller独立出来作为Presenter。 xml和Activity还是作为视图层, 视图层接收到页面数据,调用Presenter进行业务逻辑处理,Presenter调用Model进行数据处理,Model回传数据给Presenter,Presenter回传数据给View。数据的回传通过接口回调的方式来实现。
代码结构
IModel接口代码
public interface IModel {
public boolean login(String account, String pwd);
}
IView接口代码
public interface IView {
public void loginSuccess();
public void loginFail();
}
Activity代码
public class MVPActivity extends AppCompatActivity implements IView {
TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
tvResult = findViewById(R.id.tv_result);
TextView tvAccount = findViewById(R.id.tv_account);
TextView tvPwd = findViewById(R.id.tv_pwd);
Button btnLogin = findViewById(R.id.btn_login);
MVPPresenter presenter = new MVPPresenter();
presenter.setiView(this);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.login(tvAccount.getText().toString(), tvPwd.getText().toString());
}
});
}
public void loginSuccess() {
tvResult.setText("登录结果:登录成功");
}
public void loginFail() {
tvResult.setText("登录结果:登录失败");
}
}
Model代码
public class MVPModel implements IModel {
public boolean login(String account, String pwd) {
if (account == null || account.length() == 0) {
return false;
}
if (pwd == null || pwd.length() == 0) {
return false;
}
if ("user123".equals(account) && "pwd123".equals(pwd)) {
return true;
}
return false;
}
}
Presenter代码
public class MVPPresenter {
MVPModel model;
public MVPPresenter() {
model = new MVPModel();
}
IView iView;
public void setiView(IView iView) {
this.iView = iView;
}
public void login(String account, String pwd) {
boolean loginResult = model.login(account, pwd);
if (loginResult){
iView.loginSuccess();
}else {
iView.loginFail();
}
}
}
定义了两个接口,IView和IModel, Activity和Model分别实现了这两个接口。 在Presenter中持有这两个实例。Presenter调用Model处理数据后,通过Iview的接口方法回调给Activity刷新页面。
从上面的代码可以看到,三个模块之间的通信是通过接口实现的,在实际开发,定义的接口和方法会非常多。 导致很简单的一个页面功能也需要实现多个接口和方法。
优点就是通过Presenter,把MVC中的Controller代码抽出来了,并且Presenter作为View和Model通信的桥梁,完成了Model和View的解耦。
MVVM在MVP的基础上加入了双向绑定,使View能够感知ViewModel中的数据变化,ViewModel能够感知View数据的变化。
代码结构
xml代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.domain.android.study.notes.architecture.mvvm.MVVMViewModel" />
</data>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_gallery_outer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.result}"
android:layout_gravity="center" />
<EditText
android:id="@+id/tv_account"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_height="40dp"
android:hint="输入用户名"
android:gravity="center"
android:text="@={viewModel.account}"
android:layout_gravity="center"
android:layout_marginTop="20dp" />
<EditText
android:id="@+id/tv_pwd"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_height="40dp"
android:hint="输入密码"
android:text="@={viewModel.pwd}"
android:gravity="center"
android:layout_gravity="center" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_height="40dp"
android:text="登录"
android:gravity="center"
android:layout_gravity="center" />
</LinearLayout>
</layout>
Activity代码
public class MVVMActivity extends AppCompatActivity {
MVVMViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
viewModel = ViewModelProviders.of(this).get(MVVMViewModel.class);
binding.setVariable(BR.viewModel, viewModel);
binding.setLifecycleOwner(this);
binding.btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.login();
}
});
}
}
ViewModel代码
public class MVVMViewModel extends ViewModel {
public ObservableField<String> account = new ObservableField<>("");
public ObservableField<String> pwd = new ObservableField<>("");
public ObservableField<String> result = new ObservableField<>("");
MVVMModel mvvmModel;
public MVVMViewModel() {
mvvmModel = new MVVMModel();
}
public void login(){
boolean loginResult = mvvmModel.login(account.get(), pwd.get());
result.set(loginResult ? "登录结果:成功" :"登录结果:失败");
}
}
Model代码
public class MVVMModel {
public boolean login(String account, String pwd) {
if (account == null || account.length() == 0) {
return false;
}
if (pwd == null || pwd.length() == 0) {
return false;
}
if ("user123".equals(account) && "pwd123".equals(pwd)) {
return true;
}
return false;
}
}
注意
在本例MVVM架构实现中,用到了Android提供的data binding这个数据双向绑定框架。需要在APP模块的gralde文件中添加以下配置开启:
android {
...
dataBinding {
enabled true
}
...
}
通过Android提供的数据双向绑定库data binding 将Acitvity/xml视图层与ViewModel绑定。在xml布局文件中,通过@{}
来表示单向绑定或者@={}
来表示双向绑定。Activity接受到视图层的登录点击事件后,调用ViewModel处理登录业务逻辑,ViewModel通过双向数据绑定拿到到视图层输入的账号密码数据,调用Model处理数据,Model处理数据后,回传给ViewModel, ViewModel的数据改变,View感知后刷新页面。
注意
data binding通过观察者模式实现。 内部具体实现也是通过调用notify通知数据变化给观察者,notify调用了观察者实现的接口方法。
优点:经过数据双向绑定之后,我们不在需要想MVP中写那么多接口回调方法区实现视图层和业务层的交互。业务层也不再持有视图层的引用。
缺点:通过这种方式进行数据双向绑定后,xml中会多出一些标签、表达式、甚至和业务有点的简单计算代码。这不利于业务的逻辑的查看。并且由于双向绑定是data binding实现的。在这个过程中, 如果出现bug导致数据没有被感知改变,不方便排错,因为xml不能debug调试。
MVC、MVP、MVVM大体上都是把系统划分成3个模块:视图层、业务层、数据层。 但是他们的通信方式、数据流动方向不一致,形成了不同的架构。 其后面产生的架构都是为了更好的解耦,解决已有架构的不足。每个架构都有自己的优缺点,没有最好的架构,只有最合适的架构。
1 | //性能优化 |
什么是窗口
窗口即是屏幕上的一块用于绘制各种UI元素并可以响应用户输入的一个矩形区域。从原理上讲,窗口的概念是独自占有一个Surface实例的显示区域(我们在屏幕上看到的图形都需要绘制在Surface上)。
Window是个抽象类其实现类为PhoneWindow。
本文以窗口添加的流程为例,讲解窗口添加相关的流程及其涉及的方法。
其他建议:可以先学习层级结构树相关内容,有助于对窗口模块的理解
Android T 窗口层级其一 —— 容器类
Android T 窗口层级其二 —— 层级结构树的构建
Android T 窗口层级其三 —— 层级结构树添加窗口
当Activity.onResume()被调用之后,客户端会与WMS进行通信将我们的布局显示在屏幕上。其中主要涉及以下几个过程:
客户端通知WMS创建一个窗口,并添加到WindowToken。即addToDisplayAsUser阶段。
客户端通知WMS创建Surface,并计算窗口尺寸大小。即relayoutWindow阶段。
客户端获取到WMS计算的窗口大小后,进一步测量该窗口下View的宽度和高度。即performMeasure阶段。
客户端确定该窗口下View的尺寸和位置。即performLayout阶段。
确定好View的尺寸大小位置之后,便对View进行绘制。即performDraw阶段。
通知WMS,客户端已经完成绘制。WMS进行系统窗口的状态刷新以及动画处理,并最终将Surface显示出来。即reportDrawFinished阶段。
这里以Activity.onResume()被调用之后为起点
WindowManager:是一个接口类,负责窗口的管理(增、删、改)。
WindowManagerImpl:WindowManager的实现类,但是他把对于窗口的具体管理操作交给WindowManagerGlobal来处理。
WindowManagerGlobal:是一个单例类,实现了窗口的添加、删除、更新的逻辑,但是
ViewRootImpl:通过IWindowSession与WMS进行通信。其内部类W实现了WMS与ViewRootImpl的通信。
ActivityThread.java
wm.addView(decor, l);
,wm为ViewManager对象,即ViewManager wm = a.getWindowManager();
WindowManagerImpl.java
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());
mGlobal为WindowManagerGlobal对象。WindowManagerGlobal.java
root.setView(view, wparams, panelParentView, userId);
root为ViewRootImpl对象。parentWindow.adjustLayoutParamsForSubWindow(wparams);
parentWindow为Window(Window为抽象类,PhoneWindow继承于Window),即在Window中调用adjustLayoutParamsForSubWindow,用于赋值参数布局的token以及titleViewRootImpl.java
res = mWindowSession.addToDisplayAsUser(mWindow,mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls);
Session表示一个客户端和服务端的交互会话。一般来说不同的应用通过不同的会话来和WindowManagerService交互,但是处于同一个进程的不同应用通过同一个Session来交互。
IWindowSession.aidl
ViewRootImpl中通过此接口调用服务端
1.addToDisplayAsUser
2.relayout
3.finishDrawing
Session.java
IWindowSession的实现在这里,最终调用到WMS中
1.addToDisplayAsUser->addWindow
2.relayout->relayoutWindow
3.finishDrawing->finishDrawingWindow
WindowManagerService:负责为Activity对应的窗口分配Surface,管理Surface的显示顺序以及位置尺寸,控制窗口动画,并且还是输入系统的一个重要中转站。
WindowState:和客户端窗口一一对应,在向WMS添加一个窗口时,WMS会为其创建一个WindowState,来表示窗口的所有属性,WindowState相当于属性窗口管理(比如对外提供操作接口,属于层级结构中最底部的容器),窗口画面相关都剥离给了WindowStateAnimator,WindowState也是WMS中事实上的窗口。
WindowStateAnimator:主要用于管理WindowState相关画面surface,通过mDrawState参数来描述Surface所处的状态。
WindowToken:保存了所有具有同一个token的WindowState,将属于同一个activity的窗口组织在一起,activity在需要更新窗口时,必须向WMS提供WindowToken以表名自己的身份,并且窗口的类型必须与所持有的的WindowToken类型一致。
补充:一个WindowToken可以对应多个WindowState。 WindowToken是一个用于表示窗口层次结构中的窗口的标识符。每个Window具有一个与之关联的WindowToken,它用于帮助系统管理窗口的显示和交互。
一个WindowToken可以有多个WindowState表示与之相关的窗口。这是因为在Android系统中,可能会存在一些特殊情况,例如PopupWindow、Dialog等,它们属于同一个WindowToken,但是显示在不同的窗口上。
因此,一个WindowToken可以与多个WindowState关联,这样可以实现多个窗口的操作和管理。
WindowSurfaceController:用来创建SurfaceControl。
DisplayContent:即代表的是单个屏幕。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每个DisplayContent都对应着一个唯一的id,在添加窗口时可以通过指定这个ID决定将其显示在哪个屏幕中。
WindowSurfacePlacer:整个窗口层次结构刷新的入口。
RootWindowContainer:是窗口容器的顶层容器,其直接管理DisplayContent。
WindowManagerService.java
1.根据客户端传来的token获取WindowToken或创建WindowToken,并将其挂载到对应的层级节点上WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
判断WindowToken是否有父亲,即parentWindow 是否不为空final boolean hasParent = parentWindow != null;
注:前面代码有判断是否是子窗口,是则会给parentWindow 赋值;否则parentWindow仍为初始值,即为空
关于窗口类型,见 窗口常见参数汇总
Activity启动时会在ActivityRecord的构造方法中new Token()。
应用侧直接通过addView的方式添加窗口不会有ActivityRecord,因此不会在ActivityRecord的构造方法中new Token()。
系统侧直接添加的窗口(状态栏、导航栏等),是通过new WindowToken.Builder的方式添加
即主动使用ViewManager.addView来添加一个窗口则不会在ActivityRecord的构造方法中new Token(),否则通过new WindowToken.Builder的方式添加。
attrs.token这个参数一可以在应用端设置,应用没有设置token那么就为空,token为IBinder类型对象,默认值为空public IBinder token = null;
例如:
在应用侧可通过mLayoutParams.token的方式设置值private WindowManager.LayoutParams mLayoutParams;
mLayoutParams.token = null;
后面会继续判断token是否为空,最终会到最后的else中创建token
2.创建WindowStatefinal WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);
3.验证当前窗口是否可以添加到WMSres = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
该方法会对窗口TYPE,FLAG等多方面判断。只有返回ADD_OKAY时表示允许当前窗口的添加,反之则不允许添加该窗口。假如想禁止某些应用做添加窗口操作时,可以在里面通过应用的包名过滤该应用,也可以直接在WindowManagerGlobal.java的addView()方法中直接对应用想要添加的窗口进行过滤。
注:ADD_OKAY
在WindowManagerGlobal中定义,这个类里面还有一些其他的返回值,所有返回给res
的常量最终会在ViewRootImpl的setView方法中判断
4.调用openInputChannel,初始化input相关通路(本文不做讨论)final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) { win.openInputChannel(outInputChannel); }
5.将WindowState加入到WindowTokenwin.mToken.addWindow(win);
WMS窗口添加之后,还没有创建Surface,此时mDrawState状态为NO_SURFACE
1.根据客户端传递过来的IWindow的mWindowMap获取窗口添加阶段创建的WindowStatefinal WindowState win = windowForClientLocked(session, client, false);
2.设置DisplayContent.mLayoutNeeded以及shouldRelayout标志位win.setDisplayLayoutNeeded();
win为WindowState对象,该方法实际操作在DisplayContent中final boolean shouldRelayout = viewVisibility == View.VISIBLE &&(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING || win.mActivityRecord.isClientVisible());
3.创建SurfaceControl
在layoutWindow()调用了createSurfaceControl方法创建SurfaceControlresult = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
该方法的实现仍然在WMS中
这里以createSurfaceControl方法为起点
在createSurfaceControl()中调用WindowStateAnimator执行具体的SurfaceControl的创建 surfaceController = winAnimator.createSurfaceLocked();
创建Surface后,Surface还未进行绘制,此时mDrawState状态为DRAW_PENDING
将创建的SurfaceControl赋值给客户端的outSurfaceControlsurfaceController.getSurfaceControl(outSurfaceControl);
4.窗口尺寸的计算以及Surface状态更新
在layoutWindow()调用了performSurfacePlacementmWindowPlacerLocked.performSurfacePlacement(true /* force */);
mWindowPlacerLocked为WindowSurfacePlacer对象,因此这里以WindowSurfacePlacer的performSurfacePlacement()为起点
处理窗口布局循环
WindowSurfacePlacer.performSurfacePlacementLoop()
处理Surface的状态更变,以及调用LayoutWindowLw的流程
RootWindowContainer.performSurfacePlacementNoTrace()
计算窗口位置大小
DisplayPolicy.layoutWindowLw()
1.WMS接受客户端请求,将mDrawState更新为COMMIT_DRAW_PEDINGwin.finishDrawing(postDrawTransaction, seqId)
,并请求窗口布局mWindowPlacerLocked.requestTraversal();
2.通过mApplySurfaceChangesTransaction的callback,
调用commitFinishDrawingLocked()
改变mDrawState状态将mDrawState更新为READY_TO_SHOW,
最终mDrawState更新为HAS_DRAW后,再次请求窗口布局
3.执行show SurfaceshowSurfaceRobustlyLocked(t)
注:WindowStateAnimator的commitFinishDrawingLocked()方法中,如果是应用通过WindowManager中的addView的方式创建窗口,则不会有ActivityRecord,或者该窗口类型为启动窗口,则直接调用result = mWin.performShowLocked();
,即WindowState的performShowLocked()方法改变窗口状态为HAS_DRAW,否则会从RootWindowContainer的checkAppTransitionReady方法逐步调用到performShowLocked()
WMS为了管理窗口的显示进度,在WindowStateAnimator中定义了mDrawState来描述Surface所处的状态。主要有如下五种状态:
NO_SURFACE:WMS添加窗口,即调用addWindow之后,还没有创建Surface,mDrawState处于该状态。
DRAW_PENDING:app调用relayoutWindow创建Surface后,但是Surface还没有进行绘制,mDrawState处于该状态。
COMMIT_DRAW_PENDING:app完成Surface的绘制,调用finishDrawing,将mDrawState设置为该状态。
READY_TO_SHOW:在performSurfacePlacement过程中会将所有处于COMMIT_DRAW_PENDING状态的mDrawState变更为READY_TO_SHOW。
HAS_DRAW:若准备显示窗口,WMS执行performShowLocked,将mDrawState设置为该状态
窗口显示相关方法
工作内容解释
addWindow
App向WMS请求添加窗口记录,会在WMS里新建WindowState(NO_SURFACE)
relayoutWindow
App向WMS申请surface用于绘制,执行后window拥有了surface(NO_SURFACE->DRAW_PENDING)
finishDrawingWindow
App在surface上完成绘制后,通知WMS(DRAW_PENDING->COMMIT_DRAW_PENDING)
commitFinishDrawingLocked
WMS遍历window,对于完成绘制的window(COMMIT_DRAW_PENDING->READY_TO_SHOW)
performShowLocked
判断系统是否允许窗口显示isReadyForDisplay(READY_TO_SHOW->HAS_DRAWN)
showSurfaceRobustlyLocked
对HAS_DRAWN状态的窗口,用SurfaceControl通知SurfaceFlinger显示出来
窗口移除从App端发起,当Activity执行destroy(),即以handleDestroyActivity()为起点,执行wm.removeViewImmediate()开启;
通过WindowManagerGlobal–>ViewRootImpl–>Session–>WindowManagerService的removeWindow(),调用到WindowState的removeIfPossible()–>removeImmediately(),接着调用到WindowStateAnimator的destroySurfaceLocked()–>destroySurface(),逐步调用改变绘制状态为NO_SURFACE–>WindowSurfaceController的destroy()最终调用到SurfaceControl的remove()来通知SurfaceFlinger来移除layer;
从ActivityThread.handleResumeActivity方法看起
1.调用performResumeActivity,执行onResume。
2.获取WindowManager的实现类WindowManagerImpl的实例。
3.调用WindowManagerImpl.addView传入DecorView和当前布局参数WindowManager.LayoutParams。
代码路径:framework/core/java/android/app/ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
......
// TODO Push resumeArgs into the activity for consideration
// skip below steps for double-resume and r.mFinish = true case.
/*1.执行onResume*/
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
......
//获取Activity实例
final Activity a = r.activity;
......
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
//mStartedActivity在performLaunchActivity和performResumeActivity方法中被置为false
boolean willBeVisible = !a.mStartedActivity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
//获取当前Activity的PhoneWindow
r.window = r.activity.getWindow();
//从PhoneWindow中获取DecorView
View decor = r.window.getDecorView();
//将view的可见性状态设置为INVISIBLE,view不可见但是仍然占用布局空间
decor.setVisibility(View.INVISIBLE);
/*2.获取WindowManager的实现类WindowManagerImpl的实例*/
ViewManager wm = a.getWindowManager();
//获取布局参数
WindowManager.LayoutParams l = r.window.getAttributes();
//将phoneWindow的DecorView赋值给mDecor
a.mDecor = decor;
//设置窗口类型为TYPE_BASE_APPLICATION
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
/*3.传入DecorView和当前布局参数WindowManager.LayoutParams*/
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
}
......
}
wm.addView(decor, l);
WindowManager接口的实现是WindowManagerImpl,即实际调用的是WindowManagerImpl中的addView方法
代码路径:framework/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
//转交给windowManagerGlobal,添加view
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerImpl对窗口的管理交给WindowManagerGlobal,调用WindowManagerGlobal的addView方法
WindowManagerGlobal中对窗口的处理主要如下几个步骤:
1.对WindowManagerImpl传进来的参数进行检查。
2.设置WindowManager.LayoutParams中的token、title等相关属性。查看“【1.2 Token的创建与传递】”。
3.创建ViewRootImpl对象,并获取客户端与WMS通信的Session。查看“【1.3 ViewRootImpl的创建】”。
4.在WindowManagerGlobal中备份DecorView,WindowManager.LayoutParams以及ViewRootImpl。
5.调用ViewRootImpl,与WMS通信添加窗口。查看“【1.4 ViewRootImpl与WMS的通信】”。
代码路径:framework/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
/*1.对WindowManagerImpl传进来的参数进行检查*/
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//此处的ParentWindow即当Activity的PhoneWindow
if (parentWindow != null) {
/*2.为wparams的token进行赋值*/
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
......
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
IWindowSession windowlessSession = null;
......
if (windowlessSession == null) {
/*3.新建ViewRootImpl,在新建时会通过WindowManagerGlobal获取session*/
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
view.setLayoutParams(wparams);
/*4.在WindowManagerGlobal中备份DecorView,WindowManager.LayoutParams以及ViewRootImpl。*/
//当前view加入到view列表中
mViews.add(view);
//将新建的viewRootImpl加入到root列表中
mRoots.add(root);
//将当前布局参数加入到布局参数列表中
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
/*5.调用ViewRootImpl,设置view,panelParentView为null,与WMS通信添加窗口*/
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
parentWindow.adjustLayoutParamsForSubWindow(wparams);
调用Window的adjustLayoutParamsForSubWindow()方法
在adjustLayoutParamsForSubWindow中会分别对WindowManager.LayoutParams中的token以及title进行赋值。
1.首先针对子窗口、系统窗口以及应用窗口做了不同的处理,此处我们只关注应用窗口的处理。
2.其次将当前PhoneWindow.mAppToken赋值给WindowManager.LayoutParams.token。
代码路径:framework/core/java/android/view/Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
//对子窗口的Token以及Title赋值
......
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
//对子窗口的Token以及Title赋值
......
} else {
//对应用窗口的Token以及Title赋值
if (wp.token == null) {
//将当前PhoneWindow的mAppToken赋值给wp.Token
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
//将Title设置为mAppName
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null) {
wp.setTitle(mAppName);
}
}
//设置为packageName
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
......
}
此处的mAppToken便是在Activity启动时,在ATMS端创建的Token。
接下来我们看看Token是如何从ATMS端传递过来,并赋值给PhoneWindow.mAppToken的
1.在ATMS端新建ActivityRecord时,便新建了Token。并赋值给ActivityRecord.token
ActivityRecord继承WindowToken
代码路径:framework/services/core/java/com/android/server/wm/ActivityRecord.java
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
@Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,
TaskDescription _taskDescription, long _createTime) {
//新建Token
super(_service.mWindowManager, new Token(), TYPE_APPLICATION, true,
null /* displayContent */, false /* ownerCanManageAppTokens */);
......
}
2.将ActivityRecord.token封装在clientTransaction中,并将这个传递到客户端
代码路径:framework/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
......
final Task task = r.getTask();
final Task rootTask = task.getRootTask();
......
try {
......
try {
......
// Create activity launch transaction.
/*将ActivityRecord.token封装在clientTransaction中*/
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.token);
final boolean isTransitionForward = r.isTransitionForward();
final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));
......
// Schedule transaction.
/*将clientTransaction传递给客户端*/
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
} catch (RemoteException e) {
......
}
} finally {
......
}
......
return true;
}
final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.token);
在ClientTransaction中调用obtain方法,把ActivityRecord.token存到mActivityToken
代码路径:framework/core/java/android/app/servertransaction/ClientTransaction.java
/** Obtain an instance initialized with provided params. */
public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
if (instance == null) {
//创建ClientTransaction
instance = new ClientTransaction();
}
instance.mClient = client;
/*把ActivityRecord.token存到mActivityToken*/
//private IBinder mActivityToken;
instance.mActivityToken = activityToken;
return instance;
}
3.客户端从ClientTransaction中获取ATMS端传来的Token,并传递到LaunchActivityItem中
代码路径:framework/core/java/android/app/servertransaction/TransactionExecutor.java
/** Cycle through all states requested by callbacks and execute them at proper times. */
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
......
/*从ClientTransaction中获取ATMS端传来的Token*/
final IBinder token = transaction.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
......
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
......
/*将Token传递到LaunchActivityItem中*/
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
......
}
}
4.在LaunchActivityItem中将客户端传过来的Token保存在ActivityClientRecord.token中
代码路径:framework/core/java/android/app/servertransaction/LaunchActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
//将客户端传过来的Token保存在ActivityClientRecord的token中
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
ClientTransactionHandler调用handleLaunchActivity方法,ClientTransactionHandler为抽象类,其子类为ActivityThread,即实际调用的是该类中的handleLaunchActivity(),有从该方法中调用到了performLaunchActivity()
5.客户端ActivityThread将ActivityClientRecord以及其对应的token保存在ActivityThread.mActivities数组中,并调用Activity.attach将Token传给Activity。
代码路径:framework/core/java/android/app/ActivityThread.java
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
try {
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
......
synchronized (mResourcesManager) {
/*将ActivityClientRecord以及其对应的Token保存在mActivities中*/
//mActivities的类型为ArrayMap<IBinder, ActivityClientRecord>
mActivities.put(r.token, r);
}
if (activity != null) {
......
/*将Token赋值给Activity.mToken*/
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
......
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
......
}
return activity;
}
6.在Activity中将客户端传来的Token赋值给Activity.mToken。此外在该方法中还新建了PhoneWindow,并将PhoneWindow.mAppToken也设置为客户端传过来的Token。
代码路径:framework/core/java/android/app/Activity.java
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
/*新建PhoneWindow*/
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
/*将客户端传过来的Token赋值给mToken*/
mToken = token;
......
/*PhoneWindow.mAppToken设置为当前客户端传递过来的Token*/
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
......
}
PhoneWindow继承Window,setWindowManager实际调用的是其父类方法,把mAppToken设置为当前客户端传递过来的mToken
代码路径:framework/core/java/android/view/Window.java
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
//传递客户端的mToken给appToken
setWindowManager(wm, appToken, appName, false);
}
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
/*把appToken赋值给mAppToken*/
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
root = new ViewRootImpl(view.getContext(), display);
之前在【1.1 Activity走到onresume后】的流程中有调用创建ViewRootImpl,这里我们看下ViewRootImpl的构造方法
代码路径:framework/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
this(context, display, session, false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
}
从这个构造方法中我们可以看出,通过WindowManagerGlobal.getWindowSession获取到客户端与WMS沟通的桥梁IWindowSession,并将其赋值给ViewRootImpl.mWindowSession。
下面我们查看WindowManagerGlobal中是如何获取Session的。
1.通过getWindowManagerService获取IWindowManager,而WindowManagerService则实现了这个Binder接口。
2.调用IWindowManager.openSession方法即WMS.openSession,在WMS端便会新建Session。至此客户端与WMS通信的桥梁便已经搭建好了
代码路径:framework/core/java/android/view/WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
/*1.获取Binder*/
IWindowManager windowManager = getWindowManagerService();
/*2.调用WMS的openSession*/
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
从代码中可以看出如果sWindowSession不为空则直接返回,sWindowSession为当前WindowManagerGlobal属性,且WindowManagerGloba又是单例的,所以客户端一个进程中只有一个IWindowSession与WMS通信。如果sWindowSession为空,则会创建IWindowSession。
调用WindowManagerService中的openSession,新建Session
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
/*新建Session*/
return new Session(this, callback);
}
root.setView(view, wparams, panelParentView, userId);
之前在【1.1 Activity走到onresume后】的流程中有调用ViewRootImpl与WMS的通信,继续看看
当前方法是与WMS进行通信添加窗口的入口,在此处我们只关注两点:
1.requestLayout()该方法会调用到doTraversal(),之后调用performTraversals(),最终调用到relayoutWindow()和reportDrawFinished()流程,在通过Session与服务端通信
2.mWindowSession.addToDisplayAsUser,与服务端进行Binder通信,调用Session的addToDisplayAsUser方法。
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
setView(view, attrs, panelParentView, UserHandle.myUserId());
}
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
......
//将布局参数拷贝纸mWindowAttributes
mWindowAttributes.copyFrom(attrs);
//设置包名
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs = mWindowAttributes;
......
// Keep track of the actual window flags supplied by the client.
//获取当前布局的flags
mClientWindowLayoutFlags = attrs.flags;
......
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
/*请求布局,对应服务端layoutWindow流程*/
requestLayout();
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
......
try {
......
/*与服务端进行Binder通信,调用Session的addToDisplayAsUser方法*/
//执行addWindow的相关流程
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
......
} catch (RemoteException e) {
......
} finally {
if (restore) {
attrs.restore();
}
}
......
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
case WindowManagerGlobal.ADD_INVALID_USER:
throw new WindowManager.BadTokenException("Unable to add Window "
+ mWindow + " -- requested userId is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
......
}
}
}
其中关键的添加代码为
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
addToDisplayAsUser()
方法最终会走到WindowManagerService.java的addWindow方法,addWindow方法的返回值最后会返回给res
,之后回看ViewRootImpl的setView方法,返回值如果满足if (res < WindowManagerGlobal.ADD_OKAY)
条件,那么会根据switch (res)
中对应的case抛出异常。
至此,客户端流程结束,后面进入服务端流程。
WMS通过Session接受客户端添加窗口的请求,因此WMS会新建WindowState、将WindowState加入到WindowToken,并更新WindowToken下所有WindowState的z-order。
客户端通过Binder通信调用WMS端的Session.addToDisplayAsUser进入addWindow的流程。
主要做了这三件事:
1.接收客户端请求
2.WindowState初始化
3.WindowState加入到WIndowToken
客户端传递给Session的参数
IWindow window:是WMS与客户端通信的句柄。
WindowManager.LayoutParams arrts:窗口布局参数。
viewVisibility:附着在窗口的rootView的可见性。
displayId:顾名思义,display id表示的是DisplayContent即屏幕的id。
InsetsVisibilities requestedVisibilities:当前对象的mVisibilities记录了insets的可见性。
InputChannel outInputChannel:InputDispatcher接收InputReader读取到的事件,分发给对应窗口,InputDispatcher属于system_server进程和各个应用不在同一进程,它们之间的联系靠的就是InputChannel。
InsetsState outInsetsState:用来保存系统中所有Insets的状态,该对象只是在客户端创建,内部属性需要在WMS端赋值。
InsetsSourceControl[] outActiveControls:InSetsSourceControl数组。该对象也是只在客户端创建,内部属性需要在WMS端赋值。
Session调用WindowManagerService.addWindow 将客户端传入的参数传递给WindowManagerService。
代码路径:framework/services/core/java/com/android/server/wm/Session.java
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
}
添加窗口的主要逻辑均在WMS.addWindow执行,该方法主要实现以下功能:
1.首先进行权限验证以及各种条件判断。
2.根据客户端传来的token获取windowToken。
3.借助客户端传来的参数,创建WindowState实例,并将其加入到WMS. mWindowMap中。
4.将新建的WindowState加入到相应的WindowToken,并为每个窗口赋值一个z-order。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
......
/*1.进行权限验证以及各种条件判断*/
//判断调用者是否有权限add window
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != ADD_OKAY) {
return res;
}
WindowState parentWindow = null;
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
//获取将要添加的窗口类型
final int type = attrs.type;
synchronized (mGlobalLock) {
......
//根据displayId以及客户端传过来的token获取相应的displayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
......
//判断mWindowMap中是否已经存在当前客户端的key,如果有则已经将当前客户端的window添加了,无需重复添加
if (mWindowMap.containsKey(client.asBinder())) {
ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//判断是否是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
//判断当前DisplayContent是否是私有的,只拥有该display或者display已经的应用才可以在其上创建
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
ProtoLog.w(WM_ERROR,
"Attempted to add private presentation window to a non-private display. "
+ "Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
......
ActivityRecord activity = null;
//设置是否有父窗口的标志位
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
/*2.根据客户端传来的token获取windowToken*/
//attrs.token去DisplayContent.mTokenMap中去取WindowToken
//那么WindowToken是什么时候加入到mTokenMap中的呢
//这就要追溯到Activity的启动时,加入到DisplayContent中
//在ActivityStarter.startActivityInner中调用addOrReparentStartingActivity通过addChild一步步调用到WindowContainert中。
//在调用setParent,最终通过onDisplayChanged将ActivityRecord加入到DisplayContent.mTokenMap中
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
final IBinder windowContextToken = attrs.mWindowContextToken;
if (token == null) {
......
}else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
//当前窗口为应用窗口,通过token,获取ActivityRecord
activity = token.asActivityRecord();
......
} else if (token.asActivityRecord() != null) {
ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",
rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.build();
}
/*3.创建WindowState*/
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
//将客户端传过来的Insets可见性赋值给WindowState的requestedVisibilities
win.setRequestedVisibilities(requestedVisibilities);
//验证当前窗口是否可以添加到WMS
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
return res;
}
//调用openInputChannel,初始化input相关通路
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
//创建SufaceSession用于SurfaceFlinger通信
win.attach();
//将客户端与WindowState加入到mWindowMap中
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
......
/*4.将WindowState加入到WindowToken*/
win.mToken.addWindow(win);
......
return res;
}
mWindowMap保存了每个WindowState和客户端窗口的映射关系,客户端应用请求窗口操作时,通过mWindowMap查询到对应的WindowState
token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.build();
这里调用的是其WindowToken自身的build方法创建
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
WindowToken build() {
return new WindowToken(mService, mToken, mType, mPersistOnEmpty, mDisplayContent,
mOwnerCanManageAppTokens, mRoundedCornerOverlay, mFromClientToken, mOptions);
}
protected WindowToken(WindowManagerService service, IBinder _token, int type,
boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
}
dc.addWindowToken(token, this);
在WindowToken构造方法中,调用DisplayContent.addWindowToken将WindowToken添加到以DisplayContent为根节点的WindowContainer层级结构中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
// this moving forward we will either need to merge the WindowTokens some how or have
// the binder map to a list of window tokens.
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
}
if (binder == null) {
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " binder is null");
}
if (token == null) {
throw new IllegalArgumentException("Can't map null token to display="
+ getName() + " binder=" + binder);
}
mTokenMap.put(binder, token);
if (token.asActivityRecord() == null) {
// Set displayContent for non-app token to prevent same token will add twice after
// onDisplayChanged.
// TODO: Check if it's fine that super.onDisplayChanged of WindowToken
// (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
token.mDisplayContent = this;
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
//1.调用DisplayContent.findAreaForToken为当前WindowToken寻找一个合适的父容器,DisplayArea.Tokens对象。
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
//2.将WindowToken添加到父容器中。
da.addChild(token);
}
}
这里我们分两步看
1.final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
调用DisplayContent.findAreaForToken为当前WindowToken寻找一个合适的父容器,DisplayArea.Tokens对象。
/**
* Finds the {@link DisplayArea} for the {@link WindowToken} to attach to.
* <p>
* Note that the differences between this API and
* {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} is that this API finds a
* {@link DisplayArea} in {@link DisplayContent} level, which may find a {@link DisplayArea}
* from multiple {@link RootDisplayArea RootDisplayAreas} under this {@link DisplayContent}'s
* hierarchy, while {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} finds a
* {@link DisplayArea.Tokens} from a {@link DisplayArea.Tokens} list mapped to window layers.
* </p>
*
* @see DisplayContent#findAreaForTokenInLayer(WindowToken)
*/
DisplayArea findAreaForToken(WindowToken windowToken) {
return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
}
为传入的WindowToken找到一个DisplayArea对象来添加进去。
DisplayArea findAreaForWindowType(int windowType, Bundle options,
boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
// TODO(b/159767464): figure out how to find an appropriate TDA.
//1.如果是App窗口,那么返回默认的TaskDisplayArea对象。
if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
return getDefaultTaskDisplayArea();
}
// Return IME container here because it could be in one of sub RootDisplayAreas depending on
// the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
// the server side, but not mSelectRootForWindowFunc customized by OEM.
//2.如果是输入法窗口,那么返回ImeContainer。
if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
return getImeContainer();
}
//3.如果是其他类型,继续寻找。
return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
ownerCanManageAppToken, roundedCornerOverlay);
}
如果是App窗口,那么返回默认的TaskDisplayArea对象。
如果是输入法窗口,那么返回ImeContainer。
如果是其他类型,继续寻找。mDisplayAreaPolicy.findAreaForWindowType(windowType, options, ownerCanManageAppToken, roundedCornerOverlay);
调用的是DisplayAreaPolicy中的findAreaForWindowType方法,DisplayAreaPolicy为抽象类,DisplayAreaPolicyBuilder中的Result继承了该类
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
static class Result extends DisplayAreaPolicy {
......
@Override
public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
ownerCanManageAppTokens, roundedCornerOverlay);
}
......
代码路径:frameworks/base/services/core/java/com/android/server/wm/RootDisplayArea.java
DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay) {
//通过getWindowLayerFromTypeLw方法获取对应的窗口类型
int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
ownerCanManageAppTokens, roundedCornerOverlay);
if (windowLayerFromType == APPLICATION_LAYER) {
throw new IllegalArgumentException(
"There shouldn't be WindowToken on APPLICATION_LAYER");
}
return mAreaForLayer[windowLayerFromType];
}
通过getWindowLayerFromTypeLw方法计算出该窗口的类型对应的层级值windowLayerFromType,然后从mAreaForLayer数组中,找到windowLayerFromType对应的那个DisplayArea.Tokens对象。
2. da.addChild(token);
将WindowToken添加到父容器(叶子节点)中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java
/**
* DisplayArea that contains WindowTokens, and orders them according to their type.
*/
public static class Tokens extends DisplayArea<WindowToken> {
......
void addChild(WindowToken token) {
addChild(token, mWindowComparator);
}
......
addChild(token, mWindowComparator);
最终调用到WindowContainer.addChild方法添加WindowToken到叶子节点
在addWindow中初始化WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
下面我们看一下在WindowState的实例化过程中,都做了什么。
1.根据客户端传过来的参数,对相关属性进行赋值。
2.根据当前窗口的类型获取mBaseLayer,当将WindowState加入到WindowToken时,该值用来确定加入窗口在WindowToken数组中的位置。
3.实例化WindowStateAnimator,该类会跟踪当前WIndowState的动画以及surface操作。
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
/*1.根据客户端传递过来的参数,对相关属性进行赋值*/
//调用WindowState的父类WindowContainer构造方法,将WMS赋值给其父类属性mWmService
super(service);
//获取事务
mTmpTransaction = service.mTransactionFactory.get();
//将Session赋值给mSession
mSession = s;
//将与客户端通信的Binder赋值给mClient
mClient = c;
mAppOp = appOp;
//将当前activity的token赋值给mToken
mToken = token;
//通过token,获取当前窗口对的ActivityRecord
mActivityRecord = mToken.asActivityRecord();
//赋值id
mOwnerUid = ownerId;
mShowUserId = showUserId;
//是否可以添加系统窗口的标志位
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
mWindowId = new WindowId(this);
//布局参数赋值给mAttrs
mAttrs.copyFrom(a);
//将surfaceInsets赋值给mLastSurfaceInsets
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
//将窗口可见性赋值给mViewVisibility
mViewVisibility = viewVisibility;
//将窗口WindowManagerPolicy赋值给mPolicy
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
......
/*2.获取当前窗口的BaseLayer*/
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
......
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
//当前为应用窗口所以mPolicy.getWindowLayerLw(this)获取值为2,即应用层级
//TYPE_LAYER_MULTIPLIER为同一类型的多窗口保留空间
//TYPE_LAYER_OFFSET将同一组窗口移动到同一层中其他窗口的上方或者下方
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
......
/*3.新建windowStateAnimator,该类会跟踪当前WindowState的动画以及surface操作*/
mWinAnimator = new WindowStateAnimator(this);
//将透明度alpha赋值给mAlpha
mWinAnimator.mAlpha = a.alpha;
......
}
在addWindow中将WindowState加入到WindowTokenwin.mToken.addWindow(win);
WindowState加入到WindowToken中的具体过程:
1.将要加入的WindowState.mBaseLayer与WindowToken中现有的WindowState.mBaseLayer相比,按照mBaseLayer有小到大存放到数组中,若mBaseLayer相等,则后加入的WindowState放在数组后面。
代码路径:framework/services/core/java/com/android/server/wm/WindowToken.java
void addWindow(final WindowState win) {
ProtoLog.d(WM_DEBUG_FOCUS,
"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
if (win.isChildWindow()) {
// Child windows are added to their parent windows.
//如果是子窗口直接返回
return;
}
// This token is created from WindowContext and the client requests to addView now, create a
// surface for this token.
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
// Layers could have been assigned before the surface was created, update them again
reassignLayer(getSyncTransaction());
}
if (!mChildren.contains(win)) {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
//调用WindowContainer.addChild方法
addChild(win, mWindowComparator);
mWmService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
}
}
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise.
*/
private final Comparator<WindowState> mWindowComparator =
(WindowState newWindow, WindowState existingWindow) -> {
final WindowToken token = WindowToken.this;
......
//如果新窗口的mBaseLayer 不小于(大于等于)已经存在的WindowState的BaseLayer,则返回1,否则返回-1
return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
};
/**
* Returns true if the new window is considered greater than the existing window in terms of
* z-order.
*/
protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
WindowState existingWindow) {
// New window is considered greater if it has a higher or equal base layer.
//此处可以发现比较的是两个窗口的mBaseLayer
return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
}
我们看看WindowContainer.addChild方法
代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java
/**
* Adds the input window container has a child of this container in order based on the input
* comparator.
* @param child The window container to add as a child of this window container.
* @param comparator Comparator to use in determining the position the child should be added to.
* If null, the child will be added to the top.
*/
@CallSuper
protected void addChild(E child, Comparator<E> comparator) {
......
//记录插入数组的位置,若为-1则将windowState加入到后面
int positionToAdd = -1;
if (comparator != null) {
//判断当前WindowToken中WindowState的数量
//依次比较将要加入的窗口与已经存在的WindowState的BaseLayer
//mChildren越大放到数组最前面WindowToken
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
//比较baseLayer,如果child大于列表中已经存在的,则需要返回1,否则返回-1
//新加入的的child大于mChildren.get(i)则返回1,小于则返回-1
//注:comparator比较器的逻辑见上面代码的mWindowComparator
if (comparator.compare(child, mChildren.get(i)) < 0) {
//记录当前要插入的位置
positionToAdd = i;
break;
}
}
}
//如果新加入的窗口大于现在所有窗口
if (positionToAdd == -1) {
//将该窗口加入到列表最后
mChildren.add(child);
} else {
mChildren.add(positionToAdd, child);
}
// Set the parent after we've actually added a child in case a subclass depends on this.
//此处将child的mParent设置为this
child.setParent(this);
}
2.将WindowState的mParent置为刚才的WindowToken,并更新其Parent的mTreeWeight。mTreeWeight记录了其子节点的数量。
继续查看WindowState的父类WindowContainer.setParent
final protected void setParent(WindowContainer<WindowContainer> parent) {
//将当前WindowState的mParent设置为相应的WindowToken
final WindowContainer oldParent = mParent;
mParent = parent;
if (mParent != null) {
//更新parent中的mTreeWeight属性
//mTreeWeight代表以parent的根节点的子树中的元素的数量
mParent.onChildAdded(this);
} else if (mSurfaceAnimator.hasLeash()) {
mSurfaceAnimator.cancelAnimation();
}
if (!mReparenting) {
onSyncReparent(oldParent, mParent);
if (mParent != null && mParent.mDisplayContent != null
&& mDisplayContent != mParent.mDisplayContent) {
onDisplayChanged(mParent.mDisplayContent);
}
//计算显示layer
onParentChanged(mParent, oldParent);
}
}
3.将WindowState加入到WindowToken之后,调用parent的assignChildLayers方法,调整其所有child的z-order。主要经历以下步骤:
初始化layer=0,代表着z-order。
遍历mChildren数组,判断Children是否需要提高到顶部(判断标志位mNeedsZBoost)。如果不需要则调用Children的assignLayer方法调整其z-order为layer,并将layer++。如果需要则执行下一遍循环。
再次遍历mChildren数组,判断Children是否需要提高到顶部。如果需要则则调用Children的assignLayer方法调整其z-order为layer,并将layer++。如果不需要则执行下一次循环。
注:Z-order也被称为深度顺序(depth order)或Z轴顺序,它用于确定图层(Layers)在屏幕上的堆叠顺序。简单来说,Z-order就是图层在Z轴上的位置,Z轴位置越低,图层越在底层,Z轴位置越高,图层越在顶层。
继续看onParentChanged方法
/**
* Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
* Supposed to be overridden and contain actions that should be executed after parent was set.
*/
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
onParentChanged(newParent, oldParent, null);
}
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent,
PreAssignChildLayersCallback callback) {
......
if (mSurfaceControl == null) {
// If we don't yet have a surface, but we now have a parent, we should
// build a surface.
//创建一个SurfaceControl来调整窗口的z-order
createSurfaceControl(false /*force*/);
} else {
......
}
......
// Either way we need to ask the parent to assign us a Z-order.
//进入WindowToken的父类WindowContainer中,调整窗口的z-order
mParent.assignChildLayers();
scheduleAnimation();
}
void assignChildLayers() {
assignChildLayers(getSyncTransaction());
scheduleAnimation();
}
void assignChildLayers(Transaction t) {
//分配给当前窗口的z-order,初始化为0
int layer = 0;
// We use two passes as a way to promote children which
// need Z-boosting to the end of the list.
//此处会以parent为根节点向下遍历到子节点,再从下到上依次进行处理
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
wc.assignChildLayers(t);
//needsZBoot是用来判断当前窗口是否应该提升到容器的顶部
//若不需要提升到容器的顶部
if (!wc.needsZBoost()) {
//调用WindowState的父类WindowContainer中的assignLayer
wc.assignLayer(t, layer++);
}
}
//处理需要提升到容器顶部的窗口
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
if (wc.needsZBoost()) {
wc.assignLayer(t, layer++);
}
}
if (mOverlayHost != null) {
mOverlayHost.setLayer(t, layer++);
}
}
4.在Children的assignLayer中会首先判断此次要调整的layer与自己上次layer是否相等,不相等则最终会调用nativeSetLayer来调整自己的z-order。
void assignLayer(Transaction t, int layer) {
// Don't assign layers while a transition animation is playing
// TODO(b/173528115): establish robust best-practices around z-order fighting.
//如果正在执行Transaction,则不需要进行assignLayer
if (mTransitionController.isPlaying()) return;
//layer为此次要调整的z-order
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
//如果需要调整
if (mSurfaceControl != null && changed) {
//调用setLayer调整窗口的z-order
setLayer(t, layer);
//将mLastLayer调整为新的z-order
mLastLayer = layer;
mLastRelativeToLayer = null;
}
}
protected void setLayer(Transaction t, int layer) {
if (mSurfaceFreezer.hasLeash()) {
......
} else {
// Route through surface animator to accommodate that our surface control might be
// attached to the leash, and leash is attached to parent container.
//调用SurfaceAnimator中的setLayer
mSurfaceAnimator.setLayer(t, layer);
}
}
代码路径:framework/services/core/java/com/android/server/wm/SurfaceAnimator.java
/**
* Sets the layer of the surface.
* <p>
* When the layer of the surface needs to be adjusted, we need to set it on the leash if the
* surface is reparented to the leash. This method takes care of that.
*/
void setLayer(Transaction t, int layer) {
//调用SurfaceControl中的setlayer方法
t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
}
代码路径:framework/core/java/android/view/SurfaceControl.java
/**
* Set the Z-order for a given SurfaceControl, relative to it's siblings.
* If two siblings share the same Z order the ordering is undefined. Surfaces
* with a negative Z will be placed below the parent surface.
*
* @param sc The SurfaceControl to set the Z order on
* @param z The Z-order
* @return This Transaction.
*/
@NonNull
public Transaction setLayer(@NonNull SurfaceControl sc,
@IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
//调用调整layer
checkPreconditions(sc);
nativeSetLayer(mNativeObject, sc.mNativeObject, z);
return this;
}
当WindowState加入到WindowToken并调整z-order之后,客户端会再次调用WindowManagerService.relayoutWindow执行窗口布局。
主要做了这三件事:
1.接收客户端请求
2.创建SurfaceControl
3.窗口位置和大小计算
与addWindow流程的调用过程类似,WindowManagerService.relayoutWindow也是由客户端通过Session来调用的。
首先我们来看一下客户端给我们传递了哪些参数吧。
window:是WMS与客户端通信的Binder。
attrs:窗口的布局属性,根据attrs提供的属性来布局窗口。
requestWidth、requestHeight:客户端请求的窗口尺寸。
viewFlags:窗口的可见性。包括VISIBLE(0,view可见),INVISIBLE(4,view不可见,但是仍然占用布局空间)GONE(8,view不可见,不占用布局空间)
flags:定义一些布局行为。
outFrames:返回给客户端的,保存了重新布局之后的位置与大小。
mergedConfiguration:相关配置信息。
outSurfaceControl:返回给客户端的surfaceControl。
outInsetsState:用来保存系统中所有Insets的状态。
outActiveControls:InSetsSourceControl数组。
outSyncSeqIdBundle:与布局同步有关。
Session调用WMS.relayoutWindow将客户端传入的参数传递给WMS。
代码路径:framework/services/core/java/com/android/server/wm/Session.java
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSyncSeqIdBundle);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
return res;
}
在WMS.relayoutWindow中主要做了以下事情:
1.根据客户端传过来的IWindow在mWindowMap获取窗口添加阶段创建的WindowState。
2.设置DisplayContent.mLayoutNeeded以及shouldRelayout标志位
3.Surface的创建流程。
4.窗口尺寸的计算以及Surface的状态变更。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
......
synchronized (mGlobalLock) {
/*1.根据客户端传过来的Iwindow从mWindowMap中获取对应的WindowState*/
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
//获取DisplayContent、DisplayPolicy以及WindowStateAnimator
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
//根据客户端请求的窗口大小设置WindowState的requestedWidth, requestedHeight
//并设置WindowState.mLayoutNeeded为true
win.setRequestedSize(requestedWidth, requestedHeight);
}
......
//根据请求的宽带和高度窗口缩放比例
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
......
//获取原来window的可见性,此时为INVISIBLE
final int oldVisibility = win.mViewVisibility;
......
//代表现在没有surface但应该很快就有标志位
win.mRelayoutCalled = true;
win.mInRelayout = true;
//将当前窗口的可见性有原来的INVISIBLE调整为VISIBLE
win.setViewVisibility(viewVisibility);
ProtoLog.i(WM_DEBUG_SCREEN_ON,
"Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
viewVisibility, new RuntimeException().fillInStackTrace());
/*2.1.将displayContent中的布局标志为mLayoutNeeded置为true*/
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
// We should only relayout if the view is visible, it is a starting window, or the
// associated appToken is not hidden.
/*2.2.判断是否允许relayout,此时为true*/
//判断条件:view可见且(activityRecord不为空,或者布局类型为TYPE_APPLICATION_STARTING,或者窗口已经告诉客户端可以显示)
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
......
// Create surfaceControl before surface placement otherwise layout will be skipped
// (because WS.isGoneForLayout() is true when there is no surface.
/*3.surface的创建流程*/
if (shouldRelayout) {
try {
//进入creatSurfaceControl开始创建SurfaceControl
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
......
return 0;
}
}
// We may be deferring layout passes at the moment, but since the client is interested
// in the new out values right now we need to force a layout.
/*4.窗口尺寸的计算以及Surface的状态变更*/
//WindowSurfacePlacer在WMS初始化的时候创建
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
......
//填充计算好的frame返回给客户端,更新mergedConfiguration对象
win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
false /* useLatestConfig */, shouldRelayout);
// Set resize-handled here because the values are sent back to the client.
win.onResizeHandled();
......
}
Binder.restoreCallingIdentity(origId);
//返回result
return result;
}
在relayoutWindow中创建SurfaceControlresult = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
关于SurfaceControl的创建在WMS中主要做两件事:
1.调用WindwoStateAnimator执行具体的SurfaceControl的创建。
2.将创建的SurfaceControl赋值给客户端的outSurfaceControl。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
......
WindowSurfaceController surfaceController;
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
/*
* WindowStateAnimator用来帮助WindowState管理animator和surface基本操作的
* 1.WMS将创建的surfaceContorl的操作交给windowAnimator来处理
*/
surfaceController = winAnimator.createSurfaceLocked();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
/*2.将WMS的SurfaceControl赋值给客户端的outSurfaceControl*/
surfaceController.getSurfaceControl(outSurfaceControl);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
outSurfaceControl.release();
}
return result;
}
在WindowStateAnimator中创建SurfaceControl主要经过以下三个步骤:
1.重置Surface标志位,变更mDrawState状态为DRAW_PENDING。
2.通过实例化WindowSurfaceController来创建SurfaceControl。
3.处理Surface标志位,将其置为true,标志着当前WindowState已经有surface了
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked() {
final WindowState w = mWin;
//首先判断是否存在mSurfaceController
if (mSurfaceController != null) {
return mSurfaceController;
}
/*1.1.设置WindowState的mHasSurface设置为false*/
w.setHasSurface(false);
ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
/*1.2.将WindowStateAnimator中的DrawState设置为DRAW_PENDING*/
resetDrawState();
mService.makeWindowFreezingScreenIfNeededLocked(w);
/*1.3.将surface创建flag设置为hidden*/
int flags = SurfaceControl.HIDDEN;
//获取windowState的布局参数
final WindowManager.LayoutParams attrs = w.mAttrs;
// Set up surface control with initial size.
try {
......
/*2.创建WindowSurfaceController*/
//attrs.getTitle().toString()为当前activity的全路径名
//format为位图格式
//flags为surface创建的标志位(如:HIDDED(0x04,surface创建为隐藏),SKIP_SCREENSHOT(0x040,截屏时跳过此图层将不会包含在非主显示器上),SECURE(0X080,禁止复制表面的内容,屏幕截图和次要的非安全显示将呈现黑色内容而不是surface内容)等)
//attrs.type为窗口类型
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
/*3.将WindowState的hasSurface标志设置为true,标志着道歉WindowState已经有surface了*/
w.setHasSurface(true);
......
} catch (OutOfResourcesException e) {
......
} catch (Exception e) {
......
}
......
return mSurfaceController;
}
SurfaceControl的创建过程为典型的建造者模式
接下来看看WindowSurfaceController的构造方法
代码路径:framework/services/core/java/com/android/server/wm/WindowSurfaceController.java
WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
int windowType) {
//设置WindowStateAnimator
mAnimator = animator;
//窗口名
title = name;
//WMS对象
mService = animator.mService;
//WindowState对象
final WindowState win = animator.mWin;
//窗口类型
mWindowType = windowType;
//IWindowSession对象
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
//makeSurface最终会调用到DisplayContent的makeChildSurface方法,返回SurfaceControl.Builder
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
......
//获取SurfaceControl实例对象
mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
两个关键方法win.makeSurface()和b.build()
1.final SurfaceControl.Builder b = win.makeSurface()
我们先来看看win.makeSurface(),windowState中没有makeSurface()方法,因此调用其父类WindowContainer的makeSurface()方法
代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java
SurfaceControl.Builder makeSurface() {
final WindowContainer p = getParent();
return p.makeChildSurface(this);
}
/**
* @param child The WindowContainer this child surface is for, or null if the Surface
* is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
*/
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
final WindowContainer p = getParent();
// Give the parent a chance to set properties. In hierarchy v1 we rely
// on this to set full-screen dimensions on all our Surface-less Layers.
return p.makeChildSurface(child)
.setParent(mSurfaceControl);
}
最终会调用到DisplayContent的makeChildSurface
代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
//此时child为WindowState
//获取SurfaceSession,SurfaceSession的创建在Session.windowAddedLocked中,其最开始调用在WindowManagerService.addWindow中win.attach()中创建
SurfaceSession s = child != null ? child.getSession() : getSession();
//返回SurfaceControl.Builder
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
if (child == null) {
return b;
}
//设置SurfaceControl.Builder的name以及parent
return b.setName(child.getName())
.setParent(mSurfaceControl);
}
2.mSurfaceControl = b.build();
再来看看b.build(),调用SurfaceControl中的build
代码路径:framework/core/java/android/view/SurfaceControl.java
/**
* Construct a new {@link SurfaceControl} with the set parameters. The builder
* remains valid.
*/
@NonNull
public SurfaceControl build() {
//检查width以及height,初始都应该为0
if (mWidth < 0 || mHeight < 0) {
throw new IllegalStateException(
"width and height must be positive or unset");
}
if ((mWidth > 0 || mHeight > 0) && (isEffectLayer() || isContainerLayer())) {
throw new IllegalStateException(
"Only buffer layers can set a valid buffer size.");
}
if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
setBLASTLayer();
}
//创建SurfaceControl的实例
return new SurfaceControl(
mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
mLocalOwnerView, mCallsite);
}
/**
* @param session The surface session, must not be null.
* @param name The surface name, must not be null.
* @param w The surface initial width.
* @param h The surface initial height.
* @param flags The surface creation flags.
* @param metadata Initial metadata.
* @param callsite String uniquely identifying callsite that created this object. Used for
* leakage tracking.
* @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
*/
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
String callsite)
throws OutOfResourcesException, IllegalArgumentException {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
mName = name;
mWidth = w;
mHeight = h;
mLocalOwnerView = localOwnerView;
//创建Parcel用来传递数据
Parcel metaParcel = Parcel.obtain();
try {
......
//调用native层
mNativeObject = nativeCreate(session, name, w, h, format, flags,
parent != null ? parent.mNativeObject : 0, metaParcel);
} finally {
metaParcel.recycle();
}
if (mNativeObject == 0) {
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
}
mNativeHandle = nativeGetHandle(mNativeObject);
mCloseGuard.openWithCallSite("release", callsite);
}
SurfaceControl的构造方法调用完成后,返回查看前面result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
在relayoutWindow中计算窗口大小位置mWindowPlacerLocked.performSurfacePlacement(true /* force */);
该流程我们分为三部分介绍:
1.该部分处理有关窗口布局循环的逻辑。
2.该部分处理Surface的状态变更,以及调用layoutWindowLw的流程。
3.计算窗口位置大小。
performSurfacePlacement是一个确定所有窗口的Surface的如何摆放,如何显示、显示在什么位置、显示区域多大的一个入口方法。
该方法主要设置了布局的循环条件,当mTraversalScheduled 标志位为true,且loopCount大于0。将会调用performSurfacePlacementLoop执行布局操作。
代码路径:framework/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
//将循环的最大次数设置为6次
int loopCount = 6;
do {
//将该标志为设置为false
mTraversalScheduled = false;
//执行窗口布局操作
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
//只有当mTraversalScheduled为true且循环次数大于0时,才会再次循环执行布局
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
performSurfacePlacementLoop方法主要做两件事:
1.调用RootWindowContainer对所有窗口执行布局操作,
2.处理是否再次进行布局的逻辑。如果DisplayContent.mLayoutNeeded标志位为true且布局循环次数小于6次,则会将mTraversalScheduled标志位置为true,在performSurfacePlacement中会再次调用performSurfacePlacementLoop。
private void performSurfacePlacementLoop() {
//若当前已经进行布局操作,则无需重复调用直接返回
if (mInLayout) {
......
return;
}
......
//将该标志位置为true,表示正在处于布局过程中
mInLayout = true;
......
try {
/*1.调用RootWindowContainer的performSurfacePlacement()方法对所有窗口执行布局操作*/
mService.mRoot.performSurfacePlacement();
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
/*2.若需要布局,且布局次数小于6次,则需要再次请求布局*/
if (++mLayoutRepeatCount < 6) {
//该方法中会将mTraversalScheduled标志位设置位true
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
}
} catch (RuntimeException e) {
mInLayout = false;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
}
mService.mRoot.performSurfacePlacement();
上面说到在RootWindowContainer.performSurfacePlacement()中调用了performSurfaceNoTrace()方法,该方法为实际的处理布局的方法,主要处理以下流程:
1.如果有焦点变化,更新焦点。
2.执行窗口尺寸计算,surface状态变更等操作。
3.将Surface状态变更为HAS_DRAWN,触发App触发动画。该过程在finishdrawing()中再详细分析。
4.如果壁纸有变化,更新壁纸。
5.再次处理焦点变化。
6.如果过程中由size或者位置变化,则通知客户端重新relayout。
7.销毁不可见的窗口
代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
//调用performSurfacePlacementNoTrace()
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// "Something has changed! Let's make it correct now."
// TODO: Super long method that should be broken down...
void performSurfacePlacementNoTrace() {
......
/*1.如果有焦点变化,更新焦点*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
......
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
//开启事务,获取GlobalTransactionWrapper对象
mWmService.openSurfaceTransaction();
try {
/*2.执行窗口尺寸计算,surface状态变更等操作*/
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
//关闭事务
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
......
/*3.将Surface状态变更为HAS_DRAWN,触发App触发动画。该过程在“2.3.3mDrawState变更为HAS_DRAW”流程中再详细分析*/
checkAppTransitionReady(surfacePlacer);
......
/*4.遍历所有DisplayContent,如果壁纸有变化,更新壁纸*/
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
//判断DisplayContent的壁纸是否需要改变
if (displayContent.mWallpaperMayChange) {
ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
displayContent.pendingLayoutChanges);
}
}
}
/*5.在此处理焦点变化*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
......
/*6.如果过程中size或者位置变化,则通知客户端重新relayout*/
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
mOrientationChangeComplete);
}
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
/*7.销毁不可见的窗口*/
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
......
}
在applySurfaceChangesTransaction();
方法中其主要执行:
1.水印、StrictMode警告框以及模拟器显示的布局。
2.遍历所有DisplayContent执行其applySurfaceChangesTransaction
我们一起看看这个方法
private void applySurfaceChangesTransaction() {
mHoldScreenWindow = null;
mObscuringWindow = null;
// TODO(multi-display): Support these features on secondary screens.
/*1.水印、StrictMode警告框以及模拟器显示的布局*/
//获取手机默认DisplayContent的信息
final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
//布局水印
if (mWmService.mWatermark != null) {
mWmService.mWatermark.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
//布局StrictMode警告框
if (mWmService.mStrictModeFlash != null) {
mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
//布局模拟器显示覆盖
if (mWmService.mEmulatorDisplayOverlay != null) {
mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
}
/*2.遍历RootWindowContainer下所有DisplayContent执行其applySurfaceChangesTransaction()*/
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
接下来继续跟踪dc.applySurfaceChangesTransaction();
该方法主要
1.遍历所有窗口,计算窗口的布局大小,具体流程查看performLayoutNoTrace。(主要跟踪点)
2.surface的状态更改。(见“2.3.3mDrawState变更为HAS_DRAW”流程”)
3.处理surface的位置、大小以及显示等。(见“2.3.4 show Surface”流程”)
代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
//获取WindowSurfacePlacer
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
......
// Perform a layout, if needed.
/*1.执行布局,该方法最终会调用performLayoutNoTrace,计算窗口的布局参数*/
performLayout(true /* initial */, false /* updateInputWindows */);
......
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
try {
/*2.遍历所有窗口,主要是改变surface的状态。见“2.3.3mDrawState变更为HAS_DRAW”流程*/
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/*3.处理各个surface的位置、大小以及是否要在屏幕上显示等。后面finishDrawing()流程中再跟踪*/
prepareSurfaces();
......
}
继续跟踪performLayout(true /* initial */, false /* updateInputWindows */);
该方法主要就是调用performLayoutNoTrace()方法,首先判断布局标志位mLayoutNeeded,该标志位在WMS.relayoutWindow中被置为true。
false则直接返回不会进行布局操作。
true则分别遍历父窗口和子窗口进行布局,我们暂且只分析父窗口的布局操作,查看mPerformLayout。
void performLayout(boolean initial, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
try {
//调用performLayoutNoTrace
performLayoutNoTrace(initial, updateInputWindows);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
/*1.判断是否需要布局,不需要则直接返回,即判断布局标志位mLayoutNeeded是否为true*/
if (!isLayoutNeeded()) {
return;
}
//将DisplayContent.mLayoutNeeded属性置为false
clearLayoutNeeded();
......
// First perform layout of any root windows (not attached to another window).
/*2.对所有顶级窗口进行布局*/
//最终会回调mPerformLayout
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// Now perform layout of attached windows, which usually depend on the position of the
// window they are attached to. XXX does not deal with windows that are attached to windows
// that are themselves attached.
/*3.处理子窗口的布局*/
//最终会回调mPerformLayoutAttached
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
......
}
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
当遍历到DisplayContent下的每个窗口时都会执行mPerformLayout,该方法会将WindowState.mLayoutNeeded标志位置false,并将具体的布局操作交给DisplayPolicy进行处理,见“3. 计算窗口位置大小“。
private final Consumer<WindowState> mPerformLayout = w -> {
//如果当前窗口为子窗口则直接返回
if (w.mLayoutAttached) {
return;
}
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
//先判断当前窗口是否会不可见
final boolean gone = w.isGoneForLayout();
......
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
if (!gone || !w.mHaveFrame || w.mLayoutNeeded) {
if (mTmpInitial) {
//设置窗口布局WindowFrames.mContentChanged为false
w.resetContentChanged();
}
//将mSurfacePlacementNeeded标志为置为true
w.mSurfacePlacementNeeded = true;
//将WindowState.mLayoutNeeded标志位置为false
w.mLayoutNeeded = false;
//判断当前窗口是否是第一次布局
final boolean firstLayout = !w.isLaidOut();
//调用DisplayPolicy.layoutWindowLw进行布局,根据DisplayFrames对象对WindowState.mWindowFrames中的各个Rect对象属性进行确定
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
// If this is the first layout, we need to initialize the last frames and inset values,
// as otherwise we'd immediately cause an unnecessary resize.
if (firstLayout) {
// The client may compute its actual requested size according to the first layout,
// so we still request the window to resize if the current frame is empty.
if (!w.getFrame().isEmpty()) {
w.updateLastFrames();
}
w.onResizeHandled();
}
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
+ " mParentFrame=" + w.getParentFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
};
当mLayoutNeeded标志被设置时,表示布局需要更新;mSurfacePlacementNeeded标志被设置时,表示需要更新surface位置的条件之一(WindowState.updateSurfacePosition方法中有判断)。
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
layoutWindowLw主要做了以下三件事
1.首先会获DisplayFrames:DisplayContent新建时创建,内部数据由屏幕提供。
2.其次调用WindowLayout.computeFrames计算窗口布局大小。
3.最后调用WindowState.setFrames将计算的布局参数赋值给当前窗口的windowFrames。
代码路径:framework/services/core/java/com/android/server/wm/DisplayPolicy.java
/**
* Called for each window attached to the window manager as layout is proceeding. The
* implementation of this function must take care of setting the window's frame, either here or
* in finishLayout().
*
* @param win The window being positioned.
* @param attached For sub-windows, the window it is attached to; this
* window will already have had layoutWindow() called on it
* so you can use its Rect. Otherwise null.
* @param displayFrames The display frames.
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
//判断是否需要跳过布局
if (win.skipLayout()) {
return;
}
// This window might be in the simulated environment.
// We invoke this to get the proper DisplayFrames.
/*1.获取DisplayFrames*/
displayFrames = win.getDisplayFrames(displayFrames);
//获取某个方向的窗口布局参数
final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
//null
final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
// If this window has different LayoutParams for rotations, we cannot trust its requested
// size. Because it might have not sent its requested size for the new rotation.
final boolean trustedSize = attrs == win.mAttrs;
final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
/*2.调用WindowLayout.computeFrames计算窗口布局大小*/
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
sTmpClientFrames);
/*3.将计算的布局参数赋值给windowFrames*/
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
先来看看computeFrames,计算窗口布局大小
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
sTmpClientFrames);
调用的是WindowLayout的computeFrames方法
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities,
Rect attachedWindowFrame, float compatScale, ClientWindowFrames outFrames)
入参说明:attrs
:这些是窗口的布局参数。它们定义了窗口的位置、大小、堆叠顺序等属性。state
:这个参数代表了窗口的边距状态。边距是窗口与设备边缘之间的空间,可能会被其他元素(如状态栏或导航栏)占据。displayCutoutSafe
:这是一个矩形,表示在计算窗口大小时可以安全忽略的显示切边区域。这通常是为了防止应用程序内容与设备上的物理切边重叠。windowBounds
:这是窗口的边界矩形,通常表示窗口在屏幕上的位置和大小。windowingMode
:这个参数定义了窗口的窗口模式。例如,它可以是全屏、浮动等模式。requestedWidth
、requestedHeight
:这是应用程序请求的窗口宽度和高度。requestedVisibilities
:请求的可见性。这定义了应用程序请求的边距可见性,例如状态栏或导航栏是否可见。attachedWindowFrame
:附加窗口的边界。如果这个窗口是附加到另一个窗口的,这个参数表示它相对于其父窗口的位置和大小。compatScale
:兼容性比例,一个缩放因子,用于调整窗口内容的显示大小以适应不同的屏幕尺寸或分辨率。outFrames
:用于返回计算后的窗口信息。它包含了窗口的实际边界、边距等信息。
代码路径:frameworks/base/core/java/android/view/WindowLayout.java
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities,
Rect attachedWindowFrame, float compatScale, ClientWindowFrames outFrames) {
//传入的参数attrs中提取出窗口的类型(type)、标志(fl)、私有标志(pfl)和布局是否在屏幕内(layoutInScreen)
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
//定义了用于存储结果的矩形变量,包含:显示边界(outDisplayFrame)、父边界(outParentFrame)和实际边界(outFrame)
final Rect outDisplayFrame = outFrames.displayFrame;
final Rect outParentFrame = outFrames.parentFrame;
final Rect outFrame = outFrames.frame;
// Compute bounds restricted by insets
//计算窗口被Insets限制的边界。Insets是屏幕边缘的空间,用于放置状态栏、导航栏等。
//这一步通过调用state.calculateInsets()方法完成,该方法需要窗口边界和窗口布局参数作为输入。
final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
attrs.isFitInsetsIgnoringVisibility());
//代码根据Insets的边类型(LEFT、TOP、RIGHT、BOTTOM),从计算出的Insets中提取出相应的边距,
//并将它们添加到窗口的原始边界上,得到显示边界。
final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
//代码将计算出的显示边界赋值给outDisplayFrame
outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
windowBounds.right - right, windowBounds.bottom - bottom);
//根据窗口的附加信息和布局属性来确定父边界的位置和大小。
if (attachedWindowFrame == null) {
//将outParentFrame设置为与outDisplayFrame相同,这意味着父边界与显示边界相同
outParentFrame.set(outDisplayFrame);
//检查私有标志PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME是否被设置。
//这个标志可能表示是否需要根据输入法窗口(IME)的位置来调整父边界。
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
//从状态中获取输入法窗口的源(source)
final InsetsSource source = state.peekSource(ITYPE_IME);
if (source != null) {
//如果输入法窗口的source存在,则使用该source来计算父边界的内边距(Insets)。
outParentFrame.inset(source.calculateInsets(
outParentFrame, false /* ignoreVisibility */));//这里忽略source的可见性。
}
}
} else {
//如果layoutInScreen为true,则将outParentFrame设置为与attachedWindowFrame相同。
//这表示父边界是由附加窗口的边界决定的。
//如果layoutInScreen为false,则将outParentFrame设置为与outDisplayFrame相同。
//这表示父边界与显示边界相同。
outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame);
}
// Compute bounds restricted by display cutout
//根据屏幕的显示切边和窗口的布局属性来计算窗口在屏幕上受到限制的位置和大小,确保窗口不会覆盖到显示切边区域
final int cutoutMode = attrs.layoutInDisplayCutoutMode;//切边模式
final DisplayCutout cutout = state.getDisplayCutout();//屏幕上的显示切边区域
//将displayCutoutSafeExceptMaybeBars设置为与displayCutoutSafe相同,
//这是一个临时矩形,用于稍后计算不受某些系统界面元素(如状态栏)影响的显示切边安全区域。
final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
//将outFrames.isParentFrameClippedByDisplayCutout设置为false,表示父边界目前没有被显示切边裁剪
outFrames.isParentFrameClippedByDisplayCutout = false;
//如果layoutInDisplayCutoutMode不是ALWAYS并且显示切边不为空
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
// Ensure that windows with a non-ALWAYS display cutout mode are laid out in
// the cutout safe zone.
//获取屏幕的显示边界(displayFrame)
final Rect displayFrame = state.getDisplayFrame();
//获取状态的Source
final InsetsSource statusBarSource = state.peekSource(ITYPE_STATUS_BAR);
//检查状态栏源(statusBarSource)是否存在,并且如果displayCutoutSafe.top大于屏幕的顶部
if (statusBarSource != null && displayCutoutSafe.top > displayFrame.top) {
// Make sure that the zone we're avoiding for the cutout is at least as tall as the
// status bar; otherwise fullscreen apps will end up cutting halfway into the status
// bar.
//调整displayCutoutSafeExceptMaybeBars.top以确保切边避开的区域至少与状态栏一样高。
displayCutoutSafeExceptMaybeBars.top =
Math.max(statusBarSource.getFrame().bottom, displayCutoutSafe.top);
}
//如果layoutInDisplayCutoutMode是SHORT_EDGES
if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
if (displayFrame.width() < displayFrame.height()) {
//如果屏幕的宽度小于高度,则将displayCutoutSafeExceptMaybeBars的顶部和底部设置为最大和最小整数值,
//这意味着不考虑这些方向上的显示切边。
displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
} else {
//否则,将左侧和右侧设置为最大和最小整数值
//这意味着不考虑这些方向上的显示切边。
displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
}
}
//通过位运算检查attrs.flags中的FLAG_LAYOUT_INSET_DECOR标志是否被设置。如果被设置,则layoutInsetDecor为true
// FLAG_LAYOUT_INSET_DECOR:使窗口的内容布局在DecorView(装饰视图)之内
final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0;
//检查布局是否应在屏幕上进行且是否需要考虑显示切边
//布局在屏幕上、DecorView之内 且 显示切边模式为默认或短边缘模式
if (layoutInScreen && layoutInsetDecor
&& (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
|| cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
//使用给定的displayFrame、系统栏类型和可见性请求来计算系统栏的插入
//系统栏包含: STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, WindowInsets.Type.systemBars(), requestedVisibilities);
//如果系统栏在左侧、顶部、右侧或底部的插入大于0,则调整displayCutoutSafeExceptMaybeBars的相应边界,
//使其尽可能地远离屏幕边缘。这是为了确保窗口不会覆盖到这些系统栏。
if (systemBarsInsets.left > 0) {
displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
}
if (systemBarsInsets.top > 0) {
displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
}
if (systemBarsInsets.right > 0) {
displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
}
if (systemBarsInsets.bottom > 0) {
displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
}
}
//如果窗口类型是输入法(IME)
if (type == TYPE_INPUT_METHOD) {
//获取导航栏的Source
final InsetsSource navSource = state.peekSource(ITYPE_NAVIGATION_BAR);
//如果存在导航栏且其底部插入大于0
if (navSource != null && navSource.calculateInsets(displayFrame, true).bottom > 0) {
// The IME can always extend under the bottom cutout if the navbar is there.
//调整displayCutoutSafeExceptMaybeBars.bottom,允许IME窗口扩展到底部显示切边以下。
//这是为了确保IME可以正常显示在有导航栏的设备上。
displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
}
}
//如果窗口已附加到其父窗口并且不是全屏布局,则attachedInParent为true
final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen;
// TYPE_BASE_APPLICATION windows are never considered floating here because they don't
// get cropped / shifted to the displayFrame in WindowState.
//判断窗口是否为浮窗
//如果窗口不是全屏的、全屏布局的并且不是基础应用程序类型,那么它是一个浮动在屏幕上的窗口,简称浮窗。
final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
&& type != TYPE_BASE_APPLICATION;
// Windows that are attached to a parent and laid out in said parent already avoid
// the cutout according to that parent and don't need to be further constrained.
// Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
//对于非附加到父窗口和非浮动在屏幕上的窗口,需要处理其与显示切边的交集。这是因为这些窗口需要避免与显示切边重叠。
if (!attachedInParent && !floatingInScreenWindow) {
mTempRect.set(outParentFrame); //临时存储父窗口的边界
//将父窗口的边界设置为与displayCutoutSafeExceptMaybeBars交集的边界,没有交集则置为空矩阵的边界
outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
//如果父窗口的边界交集后与原始边界不同,则表示父窗口的边界被切边裁剪了
outFrames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
}
//将输出显示边界设置与displayCutoutSafeExceptMaybeBars交集的边界,没有交集则置为空矩阵的边界
outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
}
//检查attrs.flags中的FLAG_LAYOUT_NO_LIMITS位是否被设置。
//FLAG_LAYOUT_NO_LIMITS表示允许窗口布局到屏幕外侧。
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
//检查当前窗口是否处于多窗口模式
final boolean inMultiWindowMode = WindowConfiguration.inMultiWindowMode(windowingMode);
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
//noLimits是否为true即允许窗口布局到屏幕外)
//type是否不等于TYPE_SYSTEM_ERROR(表示窗口类型不是系统错误)
//inMultiWindowMode是否为false(表示窗口不在多窗口模式下)
if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
//设置输出显示的窗口边界
//[left,top]为左上角左边,[right,bottom]为右上角坐标,两个坐标构成一个矩形
//左上角左边设置为屏幕最小点,右下角坐标设置为屏幕最大点,即窗口将占据整个屏幕的边界
outDisplayFrame.left = MIN_X;
outDisplayFrame.top = MIN_Y;
outDisplayFrame.right = MAX_X;
outDisplayFrame.bottom = MAX_Y;
}
//如果compatScale不等于1,则hasCompatScale为true。这意味着存在一个兼容的缩放因子。
final boolean hasCompatScale = compatScale != 1f;
//父窗口的宽度和高度
final int pw = outParentFrame.width();
final int ph = outParentFrame.height();
//判断窗口的布局尺寸是否因为显示切边而扩展
//某些设备可能具有物理上的切边(如刘海屏、水滴屏等),这些切边区域不能用于显示内容。
//PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT作用就是为了确保应用程序的布局在具有切边的设备上仍然正确显示
//设置这个标志时,窗口的实际尺寸将大于其请求的尺寸,以便在切边区域周围填充空间。
final boolean extendedByCutout =
(attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
//请求的窗口宽度和高度
int rw = requestedWidth;
int rh = requestedHeight;
//窗口的位置坐标
float x, y;
//最终确定的窗口宽度和高度
int w, h;
// If the view hierarchy hasn't been measured, the requested width and height would be
// UNSPECIFIED_LENGTH. This can happen in the first layout of a window or in the simulated
// layout. If extendedByCutout is true, we cannot use the requested lengths. Otherwise,
// the window frame might be extended again because the requested lengths may come from the
// window frame.
//如果请求的窗口宽度或高度,是UNSPECIFIED_LENGTH或者窗口因为显示切边而扩展,
//那么其大于或等于0则使用attrs.width或attrs.height的值,否则使用父窗口的宽度和高度
if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
rw = attrs.width >= 0 ? attrs.width : pw;
}
if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
rh = attrs.height >= 0 ? attrs.height : ph;
}
//如果设置了FLAG_SCALED标志,代码会根据是否应用兼容性缩放来调整窗口的宽度和高度。
if ((attrs.flags & FLAG_SCALED) != 0) {
if (attrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
w = (int) (attrs.width * compatScale + .5f);
} else {
w = attrs.width;
}
if (attrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
h = (int) (attrs.height * compatScale + .5f);
} else {
h = attrs.height;
}
} else {
if (attrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
w = (int) (rw * compatScale + .5f);
} else {
w = rw;
}
if (attrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
h = (int) (rh * compatScale + .5f);
} else {
h = rh;
}
}
//如果存在兼容缩放因子,则调整窗口的x和y位置以考虑缩放。否则,直接使用原始的x和y位置。
if (hasCompatScale) {
x = attrs.x * compatScale;
y = attrs.y * compatScale;
} else {
x = attrs.x;
y = attrs.y;
}
//当前窗口是多窗口且设置PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME
//PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME的作用是窗口应该根据其父窗口的边界来调整自己的大小和位置。
//即控制子窗口在其父窗口内的布局行为,确保子窗口不会超出父窗口的边界。
if (inMultiWindowMode
&& (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) == 0) {
// Make sure window fits in parent frame since it is in a non-fullscreen task as
// required by {@link Gravity#apply} call.
//将窗口的宽度和高度分别设置为它们与父窗口宽度和高度中的较小值。
//这样做的目的是确保子窗口的大小不会超过其父窗口的大小。
w = Math.min(w, pw);
h = Math.min(h, ph);
}
// We need to fit it to the display if either
// a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
// for the taskless windows)
// b) If it's a secondary app window, we also need to fit it to the display unless
// FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
// screen, but SurfaceViews want to be always at a specific location so we don't fit it to
// the display.
//如果窗口不在多窗口模式下,或者窗口类型不是基础应用程序类型并且noLimits标志未设置,那么窗口需要适应显示。
final boolean fitToDisplay = !inMultiWindowMode
|| ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
// Set mFrame
//根据给定的重力属性、宽度、高度、父边界等,计算并设置outFrame。
//这里主要是确定窗口的位置。
Gravity.apply(attrs.gravity, w, h, outParentFrame,
(int) (x + attrs.horizontalMargin * pw),
(int) (y + attrs.verticalMargin * ph), outFrame);
// Now make sure the window fits in the overall display frame.
//如果窗口需要适应显示,那么使用Gravity.applyDisplay方法来调整outFrame(实际边界)的大小和位置在outDisplayFrame(显示边界)之内。
if (fitToDisplay) {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
//确保应用窗口的位置不会与设备的切边冲突。
//如果窗口的布局因为切边的存在而进行了扩展,并且窗口的边界超出了安全区域,那么它会调整窗口的位置,使其位于安全区域内。
if (extendedByCutout && !displayCutoutSafe.contains(outFrame)) {
mTempRect.set(outFrame);
// Move the frame into displayCutoutSafe.
final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL;
Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe,
mTempRect);
if (mTempRect.intersect(outDisplayFrame)) {
outFrame.union(mTempRect);
}
}
if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle()
+ " outFrames=" + outFrames
+ " windowBounds=" + windowBounds.toShortString()
+ " attachedWindowFrame=" + (attachedWindowFrame != null
? attachedWindowFrame.toShortString()
: "null")
+ " requestedWidth=" + requestedWidth
+ " requestedHeight=" + requestedHeight
+ " compatScale=" + compatScale
+ " windowingMode=" + WindowConfiguration.windowingModeToString(windowingMode)
+ " displayCutoutSafe=" + displayCutoutSafe
+ " attrs=" + attrs
+ " state=" + state
+ " requestedVisibilities=" + requestedVisibilities);
}
computeFrame方法是WindowState类中的一个重要方法,用于计算窗口的位置和大小。具体来说,它负责计算窗口的绘制区域,即窗口的内容在屏幕上实际显示的位置和大小。这个计算涉及到考虑窗口的位置、大小、布局参数以及可能的边界限制,确保窗口内容不会超出屏幕边界或被其他窗口遮挡。
在窗口管理器中,computeFrame方法通常会在以下情况被调用:
总之,computeFrame方法在Android窗口管理系统中起到了非常重要的作用,确保应用程序窗口能够正确地在屏幕上显示,并且适应不同的设备和系统事件, 为了计算小窗的位置,以及处理小窗内的View 的边界异常情况,
再来看看setFrames,更新窗口大小位置变化
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
调用的是WindowState的setFrames方法
入参说明:sTmpClientFrames
:在computeFrames方法计算后的值,用于传递的窗口位置大小相关信息requestedWidth
、requestedHeight
:这是应用程序请求的窗口宽度和高度。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void setFrames(ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
final WindowFrames windowFrames = mWindowFrames;
//用mTmpRect存储windowFrames.mParentFrame
mTmpRect.set(windowFrames.mParentFrame);
//LOCAL_LAYOUTd的值取决于配置项persist.debug.local_layout的值
if (LOCAL_LAYOUT) {
//将clientWindowFrames.frame的值设置为windowFrames.mCompatFrame。
windowFrames.mCompatFrame.set(clientWindowFrames.frame);
//将clientWindowFrames.frame的值设置为windowFrames.mFrame。
//将clientWindowFrames.displayFrame的值设置为windowFrames.mDisplayFrame。
//将clientWindowFrames.parentFrame的值设置为windowFrames.mParentFrame。
windowFrames.mFrame.set(clientWindowFrames.frame);
windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
if (hasCompatScale()) {
// The frames sent from the client need to be adjusted to the real coordinate space.
//如果存在兼容比例(通过调用hasCompatScale()方法检查),则对windowFrames中的Frame进行缩放,以适应实际的坐标空间。
//这里使用mGlobalScale作为缩放因子。
windowFrames.mFrame.scale(mGlobalScale);
windowFrames.mDisplayFrame.scale(mGlobalScale);
windowFrames.mParentFrame.scale(mGlobalScale);
}
} else {
//将clientWindowFrames.parentFrame的值设置为windowFrames.mParentFrame。
//将clientWindowFrames.displayFrame的值设置为windowFrames.mDisplayFrame。
//将clientWindowFrames.frame的值设置为windowFrames.mFrame。
windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
windowFrames.mFrame.set(clientWindowFrames.frame);
//将clientWindowFrames.frame的值设置为windowFrames.mCompatFrame。
windowFrames.mCompatFrame.set(windowFrames.mFrame);
if (hasCompatScale()) {
// Also, the scaled frame that we report to the app needs to be adjusted to be in
// its coordinate space.
//仅对windowFrames.mCompatFrame缩放
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
}
//isParentFrameClippedByDisplayCutout是一个Boolean
//如果为true,表示父窗口的边界被显示切边裁剪了;如果为false,表示父窗口的边界没有被显示切边裁剪。
windowFrames.setParentFrameWasClippedByDisplayCutout(
clientWindowFrames.isParentFrameClippedByDisplayCutout);
// Calculate relative frame
//将mRelFrame设置为与mFrame相同的值。这意味着mRelFrame现在存储了与mFrame相同的位置和尺寸信息。
windowFrames.mRelFrame.set(windowFrames.mFrame);
//获取当前窗口的父容器
WindowContainer<?> parent = getParent();
//初始化parentLeft和parentTop为0,表示父容器的左上角坐标
int parentLeft = 0;
int parentTop = 0;
if (mIsChildWindow) {//如果当前窗口是一个子窗口
//从父窗口的状态中获取其边界的位置信息。
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) {//如果当前窗口不是子窗口,并且父容器不为空
//获取父容器的边界位置
final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
//调整mRelFrame的位置,使其相对于父容器的左上角有一个偏移
windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
windowFrames.mFrame.top - parentTop);
//如果请求的宽度、高度或者父框架与上次不同
if (requestedWidth != mLastRequestedWidth || requestedHeight != mLastRequestedHeight
|| !mTmpRect.equals(windowFrames.mParentFrame)) {
//更新最后请求的宽度和高度,并标记内容已更改
mLastRequestedWidth = requestedWidth;
mLastRequestedHeight = requestedHeight;
windowFrames.setContentChanged(true);
}
//如果窗口的类型是TYPE_DOCK_DIVIDER,并且边界的位置发生了变化,mMovedByResize标记为true。
//在分屏的场合,YPE_DOCK_DIVIDER窗口类型用于绘制这个分隔栏,并处理用户的触摸事件以实现大小调整功能。
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
}
}
//如果当前窗口是壁纸,并且框架的宽度或高度发生了变化,更新壁纸的位置。
if (mIsWallpaper) {
final Rect lastFrame = windowFrames.mLastFrame;
final Rect frame = windowFrames.mFrame;
if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
mDisplayContent.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
}
}
//更新windowFrames.mFrame
updateSourceFrame(windowFrames.mFrame);
//如果定义了LOCAL_LAYOUT,并且还没有边界,更新最后边界的位置。
if (LOCAL_LAYOUT) {
if (!mHaveFrame) {
// The first frame should not be considered as moved.
updateLastFrames();
}
}
//如果存在活动记录,并且当前窗口不是子窗口
if (mActivityRecord != null && !mIsChildWindow) {
//调用mActivityRecord.layoutLetterbox(this);来填充空白区域。也就是Letterbox模式
mActivityRecord.layoutLetterbox(this);
}
//设置mSurfacePlacementNeeded为true。
mSurfacePlacementNeeded = true;
//设置mHaveFrame为true,表示已经设置了边界
mHaveFrame = true;
}
其中WindowFrames 是一个表示窗口边框大小和位置的类。
WindowFrames 中有一些重要成员变量,用于描述不同的窗口区域。
mFrame
表示窗口在屏幕上的位置和大小,是窗口管理和界面绘制的基础依据。mVisibleFrame
表示窗口可见区域的位置和大小,即除去状态栏和导航栏等系统 UI 元素后,窗口实际可以显示的区域。mDecorFrame
表示窗口装饰区域的位置和大小,即窗口除去实际内容区域外,包含的标题栏、边框、按钮等 UI 元素所占用的空间。mDisplayFrame
表示整个屏幕的可见区域的位置和大小,也就是说它包含了状态栏和导航栏等系统 UI 元素。
这些成员变量共同描述了窗口在屏幕中的位置和大小,并提供给其他模块使用,比如 WindowManager 和 View 系统。
在 Android Framework 中,WindowManagerService 会在每次窗口大小发生变化时,调用 WindowFrames 的 setFrames() 方法,更新这些成员变量的值。
当应用端执行measure-layout-draw之后,便会调用WindowManagerService.finishDrawingWindow,处理Surface的状态变更并将Surface show出来。
首先还是看一下该阶段的流程图,对整个流程有个初步的了解。
将整个流程分为三部分:
1.WMS接受客户端请求,将mDrawState更新为COMMIT_DRAW_PENDING,并请求窗口布局。
2.mDrawState更新为HAS_DRAW,再次请求窗口布局。
3.执行show Surface。
代码路径:framework/services/core/java/com/android/server/wm/Session.java
@Override
public void finishDrawing(IWindow window,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
//调用WMS中的finishDrawingWindow处理
mService.finishDrawingWindow(this, window, postDrawTransaction, seqId);
}
1.在WMS中根据客户端的Binder在mWindowMap中获取对应的WindowState。
2.调用WindowState.finishDrawing执行mDrawState的状态变更。
3.将WindowState.mLayoutNeeded标志位置为true。
4.请求进行布局刷新。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
void finishDrawingWindow(Session session, IWindow client,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (postDrawTransaction != null) {
postDrawTransaction.sanitize();
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
/*1.根据客户端的Binder在mWindowMap中获取对应的WindowState*/
WindowState win = windowForClientLocked(session, client, false);
ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
/*2.finishDrawing执行mDrawState的状态更变*/
if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
if (win.hasWallpaper()) {
win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
/*3.将当前WindowState.mLayoutNeeded置为true*/
//该标志位是判断是否进行窗口大小尺寸计算的条件之一
win.setDisplayLayoutNeeded();
/*4.请求进行布局刷新*/
mWindowPlacerLocked.requestTraversal();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
在finishDrawingWindow中调用WindowState的finishDrawing方法win.finishDrawing(postDrawTransaction, seqId)
这个方法主要调用了WindowStateAnimator的finishDrawingLocked进行状态更变
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
......
//调用WindowStateAnimator.finishDrawingLocked,会将mDrawState的状态更改为COMMIT_DRAW_PENDING
final boolean layoutNeeded =
mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
mClientWasDrawingForSync = false;
// We always want to force a traversal after a finish draw for blast sync.
return !skipLayout && (hasSyncHandlers || layoutNeeded);
}
我们继续看看WindowStateAnimator中的finishDrawingLocked()方法
首先判断mDrawState的状态是否为DRAW_PENDING,在我们创建SurfaceControl时,会将mDrawState状态更新为DRAW_PENDING。因此接下来将状态调整为COMMIT_DRAW_PENDING。
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
boolean forceApplyNow) {
......
boolean layoutNeeded = false;
if (mDrawState == DRAW_PENDING) {
......
//如果当前状态为DRAW_PENDING,则将mDrawState更变为COMMIT_DRAW_PENDING
mDrawState = COMMIT_DRAW_PENDING;
layoutNeeded = true;
}
......
return layoutNeeded;
}
在finishDrawingWindow中请求布局刷新mWindowPlacerLocked.requestTraversal();
requestTraversal中主要做了两件事:
1.首先将遍历标志为mTraversalSchedule置为true。
2.其次发送handle消息mPerformSurfacePlacement
void requestTraversal() {
//判断遍历标志mTraversalScheduled是否为true
if (mTraversalScheduled) {
return;
}
// Set as scheduled even the request will be deferred because mDeferredRequests is also
// increased, then the end of deferring will perform the request.
//将遍历标志位置为true
mTraversalScheduled = true;
if (mDeferDepth > 0) {
mDeferredRequests++;
if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
return;
}
//发送handle消息,处理消息会调用mPerformSurfacePlacement
mService.mAnimationHandler.post(mPerformSurfacePlacement);
}
mPerformSurfacePlacement会新建一个线程调用performSurfacePlacement。
performSurfacePlacement方法我们在讲relayoutWindow相关流程的时候讲过,这是执行遍历布局的入口。可以回看下【2.2.4 计算窗口大小位置中的“1.处理窗口布局循环”】
private class Traverser implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
//调用执行performSurfacePlacement
performSurfacePlacement();
}
}
}
private final Traverser mPerformSurfacePlacement = new Traverser();
final void performSurfacePlacement(boolean force) {
//当mDeferDepth大于0且force为false时,则将延迟布局请求数+1,并直接返回
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
//将循环的最大次数设置为6次
int loopCount = 6;
do {
//将该标志为设置为false
mTraversalScheduled = false;
//执行窗口布局操作
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
//只有当mTraversalScheduled为true且循环次数大于0时,才会再次循环执行布局
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
//若当前已经进行布局操作,则无需重复调用直接返回
if (mInLayout) {
......
return;
}
......
//将该标志位置为true,表示正在处于布局过程中
mInLayout = true;
......
try {
/*1.调用RootWindowContainer的performSurfacePlacement()方法对所有窗口执行布局操作*/
mService.mRoot.performSurfacePlacement();
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
/*2.若需要布局,且布局次数小于6次,则需要再次请求布局*/
if (++mLayoutRepeatCount < 6) {
//该方法中会将mTraversalScheduled标志位设置位true
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
}
} catch (RuntimeException e) {
mInLayout = false;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
}
代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
//调用performSurfacePlacementNoTrace()
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// "Something has changed! Let's make it correct now."
// TODO: Super long method that should be broken down...
void performSurfacePlacementNoTrace() {
......
/*1.如果有焦点变化,更新焦点*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
......
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
//开启事务,获取GlobalTransactionWrapper对象
mWmService.openSurfaceTransaction();
try {
/*2.执行窗口尺寸计算,surface状态变更等操作*/
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
//关闭事务,把事务提交
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
......
/*3.将Surface状态变更为HAS_DRAWN,触发App触发动画。该过程在“2.3.3mDrawState变更为HAS_DRAW”流程中再详细分析*/
checkAppTransitionReady(surfacePlacer);
......
/*4.遍历所有DisplayContent,如果壁纸有变化,更新壁纸*/
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
//判断DisplayContent的壁纸是否需要改变
if (displayContent.mWallpaperMayChange) {
ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
displayContent.pendingLayoutChanges);
}
}
}
/*5.在此处理焦点变化*/
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
......
/*6.如果过程中size或者位置变化,则通知客户端重新relayout*/
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
mOrientationChangeComplete);
}
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
/*7.销毁不可见的窗口*/
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
......
}
这里我们主要关注applySurfaceChangesTransaction();
和checkAppTransitionReady(surfacePlacer);
可以发现,窗口位置计算流程与窗口状态刷新流程都调用了performSurfacePlacement,两次调用的主要不同点在于:
1.窗口状态刷新流程在DisplayContent.applySurfaceChangesTransaction中调用mApplySurfaceChangesTransaction,处理mDrawState状态。
2.窗口状态刷新流程在RootWindowContainer.performSurfacePlacementNoTrace中调用checkAppTransitionReady,处理mDrawState状态变更为HAS_DRAWN,触发Activity过渡动画。
3.窗口状态刷新流程在WindowSurfacePlacementLoop.performSurfacePlacementLoop中会调用requestTraversal,请求再次布局。
4.窗口状态刷新流程在DisplayContent.applySurfaceChangesTransaction中调用prepareSurfaces()处理处理surface的位置、大小以及显示等。
RootWindowContainer的applySurfaceChangesTransaction()方法最终会调用到DisplayContent中调用的applySurfaceChangesTransaction()方法(【2.2.4 计算窗口大小位置】中讲过流程,不再赘述)
我们接着该方法中的mApplySurfaceChangesTransaction跟踪。forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
如果当前WindowState存在surfaceControl,则进入到WindowStateAnimator进行mDrawState的状态更变。
代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
......
//首先判断当前windowState的是否有surfaceControl
if (w.mHasSurface) {
// Take care of the window being ready to display.
//调用WindowStateAnimator的commitFinishDrawingLocked()方法
final boolean committed = winAnimator.commitFinishDrawingLocked();
......
}
......
};
继续看看WindowStateAnimator的commitFinishDrawingLocked()方法final boolean committed = winAnimator.commitFinishDrawingLocked();
1.对mDrawState的状态进行过滤,非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回。
2.此时我们的mDrawState已经在“【2.3.2 finishDrawingWindow】”将状态更新为COMMIT_DRAW_PENDING,因此此处将其变更为READY_TO_SHOW。
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked() {
//非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
mSurfaceController);
//将状态更变为READY_TO_SHOW
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
//直接进入到WindowState.performShowLocked()流程的三种情况
//1.如果ActivityRecord为空,这种情况可以理解为不依赖Activity的窗口,比如常见的悬浮窗
//2.或者canShowWindows()为true,这个方法大概是说:只有当所有窗口都已绘制完成,并且没有正在进行父级窗口的应用过渡动画,并且没有非默认颜色的窗口存在时,返回true
//3.或者窗口类型为启动窗口,启动窗口就是StartingWindow,应用启动时出现的窗口,常见的就是Splash screen ,许多应用都会定义自己的SplashActivity
//进入performShowLocked()流程后mDrawState更新HAS_DRAWN
//由于非这三种情况最终也会调用到performShowLocked(),因此下面这种情况我们暂不讨论
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
这里我们继续跟踪RootWindowContainer.performSurfacePlacementNoTrace()方法中的checkAppTransitionReady()方法checkAppTransitionReady(surfacePlacer);
该方法会遍历所有DisplayContent,处理activity的过滤动画,此处我们只有跟踪有关mDrawState状态更变的相关代码
代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
// Trace all displays app transition by Z-order for pending layout change.
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent curDisplay = mChildren.get(i);
// If we are ready to perform an app transition, check through all of the app tokens
// to be shown and see if they are ready to go.
//检查所有要显示的app token,是否已经准备就绪
if (curDisplay.mAppTransition.isReady()) {
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
curDisplay.mAppTransitionController.handleAppTransitionReady();
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
curDisplay.pendingLayoutChanges);
}
}
......
}
}
调用AppTransitionController的handleAppTransitionReady()方法,该方法主要做了以下事情
1.处理activity的过渡动画(远程动画)
2.分别调用 handleClosingApps以及handleOpeningApps对要关闭的和要打开的Activity进行可见性更新。
3.由于activity的可见性变更,将DisplayContent.mLayoutNeeded设置为true,该标志位在DisplayContent.performLayoutNoTrace中用来判断是否对当前DisplayContent下的所有窗口进行刷新。
代码路径:framework/services/core/java/com/android/server/wm/AppTransitionController.java
/**
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
......
try {
/*1.1应用app transition动画(远程动画)*/
applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp,
voiceInteraction);
/*1.2处理closing activity可见性*/
handleClosingApps();
/*1.3处理opening actvity可见性*/
handleOpeningApps();
......
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
......
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
/*2.由于activity的可见性变更,将DisplayContent.mLayoutNeeded标志位置为true*/
mDisplayContent.setLayoutNeeded();
......
}
applyAnimations()
基于一组ActivityRecord来应用动画,这些ActivityRecord表示正在进行切换的应用程序。applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp,voiceInteraction);
/**
* Apply an app transition animation based on a set of {@link ActivityRecord}
*
* @param openingApps The list of opening apps to which an app transition animation applies.
* @param closingApps The list of closing apps to which an app transition animation applies.
* @param transit The current transition type.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
LayoutParams animLp, boolean voiceInteraction) {
//方法检查过渡类型是否未设置,或者打开和关闭的应用程序是否都为空。如果是,则方法直接返回,不执行任何动画。
if (transit == WindowManager.TRANSIT_OLD_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
//调用getAnimationTargets方法获取打开和关闭的窗口容器(WindowContainer)
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
//打开和关闭的窗口应用动画。这是通过调重载的applyAnimations方法完成的,传递相应的参数,如动画的目标、过渡类型等。
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
//如果存在最近任务动画控制器(RecentsAnimationController),则发送任务出现任务
final RecentsAnimationController rac = mService.getRecentsAnimationController();
if (rac != null) {
rac.sendTasksAppeared();
}
//遍历打开和关闭的应用程序,并设置mOverrideTaskTransition为false
for (int i = 0; i < openingApps.size(); ++i) {
openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
for (int i = 0; i < closingApps.size(); ++i) {
closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
//如果存在辅助功能控制器(AccessibilityController)且有回调,则调用其onAppWindowTransition方法。
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController.hasCallbacks()) {
accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
入参含义:openingApps
和 closingApps
: 这两个参数是ActivityRecord类型的数组,分别表示正在打开和关闭的应用程序,即这些ActivityRecord表示正在进行切换的应用程序。transit
: 这是一个整型参数,表示过渡类型。例如,WindowManager.TRANSIT_OLD_UNSET表示没有特定的过渡类型。animLp
: 这是一个LayoutParams对象,用于定义窗口的布局参数。voiceInteraction
: 一个布尔值,表示是否为语音交互。
总而言之,该方法主要负责处理应用程序窗口的打开和关闭动画。它确保应用程序在切换时有一个平滑的视觉效果,为用户提供更好的体验。最后还与辅助功能服务交互,确保辅助功能用户也能正确地感知应用程序窗口的切换。
再来说说其中调用的applyAnimations方法部分
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
调用的是重载的applyAnimations方法
/**
* Apply animation to the set of window containers.
*
* @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
* @param apps The list of {@link ActivityRecord}s being transitioning.
* @param transit The current transition type.
* @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
* invisible.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
@TransitionOldType int transit, boolean visible, LayoutParams animLp,
boolean voiceInteraction) {
//获取窗口容器的数量
final int wcsCount = wcs.size();
//遍历每一个窗口容器
for (int i = 0; i < wcsCount; i++) {
final WindowContainer wc = wcs.valueAt(i);
// If app transition animation target is promoted to higher level, SurfaceAnimator
// triggers WC#onAnimationFinished only on the promoted target. So we need to take care
// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
// app transition.
//对于每一个窗口容器,检查正在进行切换的应用程序(apps)中哪些是该窗口容器的后代。
//这些后代应用程序将被添加到一个列表中。
final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
if (app.isDescendantOf(wc)) {
transitioningDescendants.add(app);
}
}
//调用每个窗口容器的applyAnimation方法,传入相应的参数(如动画的布局参数、过渡类型、是否可见等)以及后代应用程序的列表。
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
入参含义:wcs
: 一个WindowContainer对象的集合,这些对象是需要应用动画的窗口容器。apps
: 一个ActivityRecord对象的集合,这些对象表示正在进行切换的应用程序。transit
: 当前的过渡类型,例如淡入淡出、滑动等。visible
: 一个布尔值,表示应用程序是否变为可见。animLp
: 布局参数,定义了动画运行时的布局。voiceInteraction
: 一个布尔值,表示是否有语音交互。
这部分远程动画流程,这里不做重点
handleClosingApps()
该方法中主要的作用就是将所有即将close的activity的mVisible标志设置为false。该标志位在后续prepareSurfaces中是判断是否show surface的条件之一。
private void handleClosingApps() {
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final int appsCount = closingApps.size();
for (int i = 0; i < appsCount; i++) {
final ActivityRecord app = closingApps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
//设置activity的可见性,将mVisible设置为false
app.commitVisibility(false /* visible */, false /* performLayout */);
app.updateReportedVisibilityLocked();
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
//强制将allDrawn设置为true
app.allDrawn = true;
......
}
}
handleOpeningApps()
该方法与handleClosingApps方法类似,主要处理两件事情:
1.将所有即将open的activity的mVisible标志位设置为true.
2.调用ActivityRecord.showAllWindowsLocked(),最终会调用到WindowState.performShowLocked() ,处理mDrawState的状态变更
private void handleOpeningApps() {
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
final int appsCount = openingApps.size();
for (int i = 0; i < appsCount; i++) {
final ActivityRecord app = openingApps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
/*1.设置activity的可见性,将mVisible设置为true*/
app.commitVisibility(true /* visible */, false /* performLayout */);
......
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION handleAppTransitionReady()");
//开启事务
mService.openSurfaceTransaction();
try {
/*2.此方法最终会调用到WindowState.performShowLocked*/
app.showAllWindowsLocked();
} finally {
//关闭事务
mService.closeSurfaceTransaction("handleAppTransitionReady");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION handleAppTransitionReady()");
}
......
}
}
app.showAllWindowsLocked();
先调用到ActivityRecord的showAllWindowsLocked()
代码路径:framework/services/core/java/com/android/server/wm/ActivityRecord.java
/**
* This must be called while inside a transaction.
*/
void showAllWindowsLocked() {
forAllWindows(windowState -> {
if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
windowState.performShowLocked();
}, false /* traverseTopToBottom */);
}
windowState.performShowLocked();
再调用到WindowState的performShowLocked()
将mDrawState的状态由READY_TO_SHOW变更为HAS_DRAW
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
// This must be called while inside a transaction.
boolean performShowLocked() {
......
//获取WindowStateAnimator.mDrawState
final int drawState = mWinAnimator.mDrawState;
//这里判断(drawState 状态为HAS_DRAWN 或者READY_TO_SHOW)且ActivityRecord不为空
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
//窗口类型不为启动窗口
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}
}
//如果当前mDrawState的状态不为READY_TO_SHOW ,则直接返回
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
return false;
}
......
//走入窗口动画流程
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
//设置mDrawState的状态为HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
......
return true;
}
添加窗口时,会调用这个mWinAnimator.applyEnterAnimationLocked();
窗口动画的方法,然后调用到applyAnimationLocked()
;窗口移除时,会直接调用applyAnimationLocked()
显示动画。可参考Android T窗口动画显示和退出流程
回到WindowSurfacePlacer中通过requestTraversals(),再次请求布局,该方法将mTraversalScheduled标志位设置为true的判断条件有两个:
1.遍历所有DisplayContent.mLayoutNeeded标志为是否为true。(由于AppTransitionController.handleAppTransitionReady阶段已经将mLayoutNeeded置为true,因此该条件为真)
2.重复布局的次数不能超过6次,该条件也为真。(因为当前还只是第一次布局)
代码路径:framework/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
private void performSurfacePlacementLoop() {
......
try {
......
/*1.遍历所有DisplayContent.mLayoutNeeded标志位是否为true*/
if (mService.mRoot.isLayoutNeeded()) {
/*2.如果需要布局,且布局次数小于6次,则需要再次请求布局*/
if (++mLayoutRepeatCount < 6) {
//该方法中会将mTraversalScheduled标志位设置位true
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
......
} catch (RuntimeException e) {
......
}
}
接下来进入第二次布局循环,其主要目的是为了show surface
在第二次循环中,我们主要关注DisplayContent中applySurfaceChangesTransaction()方法调用的prepareSurfaces()
该方法最终会调用到根容器WindowContainer,来遍历所有子容器中的prepareSurfaces。
代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java
@Override
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
//获取事务
final Transaction transaction = getPendingTransaction();
//调用其父类方法
super.prepareSurfaces();
// TODO: Once we totally eliminate global transaction we will pass transaction in here
// rather than merging to global.
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
调用其父类方法super.prepareSurfaces();
DisplayContent的父类为WindowContainer
代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java
void prepareSurfaces() {
// If a leash has been set when the transaction was committed, then the leash reparent has
// been committed.
mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
//调用所有子容器中的prepareSurfaces
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
}
}
mChildren.get(i).prepareSurfaces();
在WindowState.prepareSurfaces中,主要做了两方面工作。
1.将mWindowFrames中计算出来的left以及top设置surface位置,并调整窗口比例。
2.控制surface的可见性,查看WindowStateAnimator.prepareSurfaceLocked
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java
void prepareSurfaces() {
mIsDimming = false;
applyDims();
//实际调用的是其父类WindowContainer的方法
/*1.最终调用自身的updateSurfacePosition()(自身有重写该方法)计算surface的位置*/
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
//更新窗口比例
updateScaleIfNeeded();
/*2.控制surface的可见性,调用WindowStateAnimator的prepareSurfaceLocked()方法*/
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
}
@Override
@VisibleForTesting
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
return;
}
//这段代码首先检查布局是否被延迟(通过 isLayoutDeferred() 方法)
//或者应用是否正在进行布局(通过 isGoneForLayout() 方法)。
//如果满足这些条件并且 mSurfacePlacementNeeded 为 false,则方法返回,不执行后续操作。
//这是因为当布局被延迟或应用正在进行布局时,界面的位置可能不是最新的,因此不执行updateSurfacePosition。
if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
&& !mSurfacePlacementNeeded) {
// Since this relies on mWindowFrames, changes made while layout is deferred are
// likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
// up-to-date and thus can't be relied on.
return;
}
//将mSurfacePlacementNeeded设置为false
mSurfacePlacementNeeded = false;
//将mSurfacePosition的left以及top设置mWindowFrames中计算出来的left以及top,并根据parent进行偏移
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
mSurfacePosition);
//根据壁纸的比例对SurfacePosition进行调整
if (mWallpaperScale != 1f) {
final Rect bounds = getLastReportedBounds();
Matrix matrix = mTmpMatrix;
matrix.setTranslate(mXOffset, mYOffset);
matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
bounds.exactCenterY());
matrix.getValues(mTmpMatrixArray);
mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
} else {
mSurfacePosition.offset(mXOffset, mYOffset);
}
......
}
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
调用WindowStateAnimator的prepareSurfaceLocked()方法,该则真正的处理触发surface show的逻辑。主要分为两部分。
1.将计算的alpha应用于当前surface。
2.判断是否调用showSurfaceRobustlyLocked将surface show出来。
代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
//首先判断是否有SurfaceControl
if (!hasSurface()) {
......
return;
}
//设置mShowAlpha
computeShownFrameLocked();
//判断parentWindow是否hidden,或者当前窗口是否on-screen
if (w.isParentWindowHidden() || !w.isOnScreen()) {
......
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
mLastAlpha = mShownAlpha;
ProtoLog.i(WM_SHOW_TRANSACTIONS,
"SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);
/*1.设置surface的alpha*/
boolean prepared =
mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);
//如果当前状态为HAS_DRAWN
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
/*2.触发show surface*/
if (showSurfaceRobustlyLocked(t)) {
mAnimator.requestRemovalOfReplacedWindows(w);
//设置mLastHidden为false
mLastHidden = false;
.......
} else {
w.setOrientationChanging(false);
}
}
}
} else {
if (mWin.isAnimating(TRANSITION | PARENTS)) {
ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this);
}
}
......
}
从上述代码中可以看出触发showSurfaceRobustlyLocked的判断条件有以下几点:
1.w.isParentWindowHidden判断其parent的mHidden是否为true,此时当前窗口没有parent直接返回false
2.w.isOnScreen,判断当前窗口是否在屏幕上,如果该窗口mVisible为true或者在不可见之前正在运行动画,判断为在屏幕上。我们在上次布局的AppTransitionController.handleAppTransitionReady阶段将当前窗口的mVisible置为了true,因此w.isOnScreen返回true。
3.mLastAlpha != mShownAlpha以及mLastHidden满足其一即可,此处我们分析mLastHidden,该标志位在创建SurfaceControl或者hide surface时会被置为true,因为当前窗口才刚刚被创建,因此mLastHidden为true。
经过以上判断可以得出我们顺利触发showSurfaceRobustlyLocked
后面通过WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController
/**
* Have the surface flinger show a surface, robustly dealing with
* error conditions. In particular, if there is not enough memory
* to show the surface, then we will try to get rid of other surfaces
* in order to succeed.
*
* @return Returns true if the surface was successfully shown.
*/
private boolean showSurfaceRobustlyLocked(SurfaceControl.Transaction t) {
//WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController
//调用WindowSurfaceController的showRobustly方法
boolean shown = mSurfaceController.showRobustly(t);
//如果没有成功返回false
if (!shown)
return false;
t.merge(mPostDrawTransaction);
return true;
}
在WindowSurfaceController中,首先判断标志位mSurfaceShown,若为true则直接返回;若为false,则将mSurfaceShown置为true,并调用SurfaceControl.show。至此真正的绘图已经显示出来,但是否真正的被用户看见,还需要看其parent是否被show。
代码路径:framework/services/core/java/com/android/server/wm/WindowSurfaceController.java
boolean showRobustly(SurfaceControl.Transaction t) {
......
//首先判断surface是否已经shown
if (mSurfaceShown) {
return true;
}
//将mSurfaceShown设置为true
setShown(true);
//调用SurfceControl中的show方法,将surface show出来
t.show(mSurfaceControl);
if (mAnimator.mIsWallpaper) {
EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
mAnimator.mWin.getDisplayId(), 1 /* request shown */);
}
return true;
}
从SurfaceControl的创建以及show的流程上看,可以发现WMS是通过WindowSurfaceController对SurfaceControl进行管理的。
最后我们看一下SurfaceControl中的show方法
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* Request that a given surface and it's sub-tree be shown.
*
* @param sc The surface to show.
* @return This transaction.
* @hide
*/
@UnsupportedAppUsage
public Transaction show(SurfaceControl sc) {
checkPreconditions(sc);
nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
return this;
}
在“【2.2 窗口位置计算】”以及“【2.3 窗口状态刷新】”部分均调用了WindowSurfacePlacer.performSurfacePlacement(),实际上任何窗口属性变化都会触发该方法,但我们在performSurfacePlacement中只关注了窗口位置大小计算以及窗口状态变更的相关流程。此处再对该流程进行简单的梳理。
当调用到WindowSurfacePlacer.performSurfacePlacement()时首先会执行“1”更新所有窗口的大小以及状态信息,在执行“2”处理是否在此调用执行performSurfacePlacement。
1.1.1:主要调用computeFrames,计算窗口的尺寸大小。
1.1.2:主要处理mDrawState的状态变更,在commitFinishDrawingLocked中会将处于DRAW_PENDING状态的mDrawState更新为COMMIT_DRAW_PENDING。
1.1.3:主要根据computerFrames中计算出来的窗口大小来设置Surface的位置,并调用SurfaceControl.show()将窗口show出来。
1.2:将处于COMMIT_DRAW_PENDING状态的mDrawState更新为READY_TO_SHOW,并将DisplayContent.mLayoutNeeded设置为true。在“2”中会判断该标志位来处理是否再次调用performSurfacePlacement的操作。
尚未添加窗口的层级结构树,如图
DisplayArea层级结构中的每一个DisplayArea,都包含着一个层级值范围,这个层级值范围表明了这个DisplayArea可以容纳哪些类型的窗口。
每种窗口类型,都可以通过WindowManagerPolicy.getWindowLayerFromTypeLw方法,返回一个相应的层级值。
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @return int An arbitrary integer used to order windows, with lower numbers below higher ones.
*/
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
return getWindowLayerFromTypeLw(type, canAddInternalSystemWindow,
false /* roundedCornerOverlay */);
}
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @param roundedCornerOverlay
如何通过dump中的内容找到对应的代码?
我们dump窗口层级发现会有很多信息,adb shell dumpsys activity containers
这里我们以其中的DefaultTaskDisplayArea为例
在源码的framework目录下查找该字符串,找到对应的代码就可以通过打印堆栈或者搜索代码跟踪的方式找到其调用逻辑
final TaskDisplayArea defaultTaskDisplayArea =
new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
也就是这一句
当然我们上篇文章也讲到了DisplayContent代表的屏幕的DisplayArea层级结构的根节点,我们可以直接从DisplayContent.java的构造方法出发,追踪其流程
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
* Create new {@link DisplayContent} instance, add itself to the root window container and
* initialize direct children.
* @param display May not be null.
* @param root {@link RootWindowContainer}
*/
DisplayContent(Display display, RootWindowContainer root) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
......
final Transaction pendingTransaction = getPendingTransaction();
configureSurfaces(pendingTransaction);
pendingTransaction.apply();
......
}
创建新的DisplayContent实例,将其自身添加到根窗口容器并初始化直接子级
FEATURE_ROOT,对应DisplayContent,一个屏幕上的根DisplayArea,也就是dump中的Display节点。
这里我们主要关注一下configureSurfaces(pendingTransaction);
/**
* Configures the surfaces hierarchy for DisplayContent
* This method always recreates the main surface control but reparents the children
* if they are already created.
* @param transaction as part of which to perform the configuration
*/
private void configureSurfaces(Transaction transaction) {
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
if (mDisplayAreaPolicy == null) {
// Setup the policy and build the display area hierarchy.
// Build the hierarchy only after creating the surface so it is reparented correctly
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
......
}
通过DisplayContent来配置图层结构
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer)
调用DisplayAreaPolicy中的Provider接口instantiate方法,去初始化一个DisplayArea层级结构
记住这边传递的参数,后面代码需要结合起来看
/**
* Provider for {@link DisplayAreaPolicy} instances.
*
* <p>By implementing this interface and overriding the
* {@code config_deviceSpecificDisplayAreaPolicyProvider}, a device-specific implementations
* of {@link DisplayAreaPolicy} can be supplied.
*/
public interface Provider {
/**
* Instantiates a new {@link DisplayAreaPolicy}. It should set up the {@link DisplayArea}
* hierarchy.
*
* @see DisplayAreaPolicy#DisplayAreaPolicy
*/
DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
RootDisplayArea root, DisplayArea.Tokens imeContainer);
用来实例化一个DisplayAreaPolicy对象,这个对象应该建立起DisplayArea层级结构,实际走到的则是DisplayAreaPolicy.Provider的实现类DisplayAreaPolicy.DefaultProvider.instantiate方法
/** Provider for platform-default display area policy. */
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
//1.创建一个名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到List中
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
// Define the features that will be supported under the root of the whole logical
// display. The policy will build the DisplayArea hierarchy based on this.
//2.创建HierarchyBuilder
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
//3.1添加ImeContainer到HierarchyBuilder
//3.2创建并保存默认TaskDisplayArea到HierarchyBuilder
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
// Only trusted display can have system decorations.
//4.为HierarchyBuilder添加Feature
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
}
// Instantiate the policy with the hierarchy defined above. This will create and attach
// all the necessary DisplayAreas to the root.
//5.生成DisplayArea层级结构
return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
我们先了解下DisplayArea.Tokens的构造方法
Tokens(WindowManagerService wms, Type type, String name) {
this(wms, type, name, FEATURE_WINDOW_TOKENS);
}
FEATURE_WINDOW_TOKENS,容纳非activity窗口的DisplayArea,dump中对应着ImeContainer、WindowToken等
这里DefaultProvider实现了这个接口。
这个方法主要干了这几件事情:
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
FEATURE_DEFAULT_TASK_CONTAINER,容纳默认Task容器的DisplayArea,dump中正是以“DefaultTaskDisplayArea”命名
创建一个名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到List中
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
HierarchyBuilder是什么?是用来定义在整个逻辑显示的根里面所需的一些Feature
HierarchyBuilder是在DisplayAreaPolicyBuilder中定义的
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
/**
* Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
* {@link RootDisplayArea}
*/
static class HierarchyBuilder {
private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
private static final int LEAF_TYPE_IME_CONTAINERS = 2;
private static final int LEAF_TYPE_TOKENS = 0;
private final RootDisplayArea mRoot;
private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
@Nullable
private DisplayArea.Tokens mImeContainer;
HierarchyBuilder(RootDisplayArea root) {
mRoot = root;
}
......
}
从代码中我们可以看出,HierarchyBuilder用来构建一个DisplayArea层级结构,该层级结构的根节点
其构造方法HierarchyBuilder(RootDisplayArea root)
传入的是RootDisplayArea的对象。
结合前面的configureSurfaces
方法中我们可以发现传入的是DisplayContent,即HierarchyBuilder以DisplayContent对象为根节点,生成一个DisplayArea层级结构。
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
我们先看setImeContainer(imeContainer)
部分。其中参数imeContainer是DisplayArea.Tokens的对象。
在DisplayContent中DisplayAreaPolicy初始化时,传递了一个mImeWindowsContainer对应我们这里的imeContainer形参,其是在DisplayContent中定义并初始化的
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
// WindowContainer which is omitted from screen magnification, as the IME is never magnified.
// TODO(display-area): is "no magnification" in the comment still true?
private final ImeContainer mImeWindowsContainer = new ImeContainer(mWmService);
ImeContainer就是输入法的容器,其继承在DisplayContent中DisplayArea.Tokens
/**
* Container for IME windows.
*
* This has some special behaviors:
* - layers assignment is ignored except if setNeedsLayer() has been called before (and no
* layer has been assigned since), to facilitate assigning the layer from the IME target, or
* fall back if there is no target.
* - the container doesn't always participate in window traversal, according to
* {@link #skipImeWindowsDuringTraversal()}
*/
private static class ImeContainer extends DisplayArea.Tokens {
HierarchyBuilder的setImeContainer方法
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
private DisplayArea.Tokens mImeContainer;
/** Sets IME container as a child of this hierarchy root. */
HierarchyBuilder setImeContainer(DisplayArea.Tokens imeContainer) {
mImeContainer = imeContainer;
return this;
}
从代码中可以看出,就是将DisplayContent的mImeWindowsContainer保存到了HierarchyBuilder的mImeContainer成员变量中,后续创建DisplayArea层级结构时可以直接拿来使用。
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
这里我们看setTaskDisplayAreas(tdaList)
部分,第一步【1.初始化TaskDisplayArea】的时候,就已经把名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到tdaList中,
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
/**
* Sets {@link TaskDisplayArea} that are children of this hierarchy root.
* {@link DisplayArea} group must have at least one {@link TaskDisplayArea}.
*/
HierarchyBuilder setTaskDisplayAreas(List<TaskDisplayArea> taskDisplayAreas) {
mTaskDisplayAreas.clear();
mTaskDisplayAreas.addAll(taskDisplayAreas);
return this;
}
虽然TaskDisplayArea是支持嵌套的,并且这里也采用了一个ArrayList来管理TaskDisplayArea,但是目前TaskDisplayArea只在这里被创建,即目前一个DisplayContent只有一个名为“DefaultTaskDisplayArea”的TaskDisplayArea。从dumpsys activity containers 中我们也可以看到,整个文件也只有一个“DefaultTaskDisplayArea”
// Only trusted display can have system decorations.
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
configureTrustedHierarchyBuilder
这个方法就在DisplayAreaPolicy.DefaultProvider内部
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
WindowManagerService wmService, DisplayContent content) {
// WindowedMagnification should be on the top so that there is only one surface
// to be magnified.
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
// Make the DA dimmable so that the magnify window also mirrors the dim layer.
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());
if (content.isDefaultDisplay) {
// Only default display can have cutout.
// See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",
FEATURE_HIDE_DISPLAY_CUTOUT)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
TYPE_NOTIFICATION_SHADE)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
TYPE_SECURE_SYSTEM_OVERLAY)
.build());
}
rootHierarchy
.addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
FEATURE_FULLSCREEN_MAGNIFICATION)
.all()
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
FEATURE_IME_PLACEHOLDER)
.and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
.build());
}
}
从代码中可以看到五大的Feature:WindowedMagnification、HideDisplayCutout、OneHanded、FullscreenMagnification、ImePlaceholder,这些Feature其实也就是我们在dumpsys中看到那些,还有一些关键方法all()、and()、except()、upto()、build()等
在我们正式开始聊这个几个Feature添加之前,我们先来看看,Feature是怎么定义的
从HierarchyBuilder的addFeature方法跟踪发现,Feature是在DisplayAreaPolicyBuilder中定义的
HierarchyBuilder addFeature(DisplayAreaPolicyBuilder.Feature feature) {
mFeatures.add(feature);
return this;
}
Feature的定义
代码路径/frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
/**
* A feature that requires {@link DisplayArea DisplayArea(s)}.
*/
static class Feature {
private final String mName;
private final int mId;
private final boolean[] mWindowLayers;
private final NewDisplayAreaSupplier mNewDisplayAreaSupplier;
private Feature(String name, int id, boolean[] windowLayers,
NewDisplayAreaSupplier newDisplayAreaSupplier) {
mName = name;
mId = id;
mWindowLayers = windowLayers;
mNewDisplayAreaSupplier = newDisplayAreaSupplier;
}
......
}
首先Feature代表的是DisplayArea的一个特征,可以根据Feature来对不同的DisplayArea进行划分。
关键是其Feature内部定义Builder类以及其build()方法
static class Builder {
private final WindowManagerPolicy mPolicy;
private final String mName;
private final int mId;
private final boolean[] mLayers;
private NewDisplayAreaSupplier mNewDisplayAreaSupplier = DisplayArea::new;
private boolean mExcludeRoundedCorner = true;
/**
* Builds a new feature that applies to a set of window types as specified by the
* builder methods.
*
* <p>The set of types is updated iteratively in the order of the method invocations.
* For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
* apply to all types except TYPE_STATUS_BAR.
*
* <p>The builder starts out with the feature not applying to any types.
*
* @param name the name of the feature.
* @param id of the feature. {@see Feature#getId}
*/
Builder(WindowManagerPolicy policy, String name, int id) {
mPolicy = policy;
mName = name;
mId = id;
mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
}
......
Feature build() {
if (mExcludeRoundedCorner) {
// Always put the rounded corner layer to the top most layer.
mLayers[mPolicy.getMaxWindowLayer()] = false;
}
return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
}
通过一套适用于具体的窗口类型构建方法来构建新Feature
其中mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
mPolicy.getMaxWindowLayer()返回的是窗口最大层数。
/**
* Returns the max window layer.
* <p>Note that the max window layer should be higher that the maximum value which reported
* by {@link #getWindowLayerFromTypeLw(int, boolean)} to contain rounded corner overlay.</p>
*
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
default int getMaxWindowLayer() {
return 36;
}
1.代码中最大层数是36,这里+1,则也就是mLayers = new boolean[37]
,即窗口层级区间为[0,36]
2.在看看build()
方法中的 mLayers[mPolicy.getMaxWindowLayer()] = false;
,则表示mLayers[36]
= false,即第36层在build时会置为false(注:mExcludeRoundedCorner这个变量的值一直是true,没有改动)
3.return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
其中mLayers对应的就是Feature中的mWindowLayers,即mLayers.clone()
就是把Feature.mWindowLayer的值复制给Feature.Builder.mLayer。
下面我们来说说构建Feature的关键星魂
以下代码均在DisplayAreaPolicyBuilder.Feature.Builder中
/**
* Set that the feature applies to all window types.
*/
Builder all() {
Arrays.fill(mLayers, true);
return this;
}
将mLayers数组中的所有元素都设置为true,表示当前DisplayArea可以包含所有类型的窗口。
简述,all()就是把所有类型窗口置为true(添加)
/**
* Set that the feature applies to the given window types.
*/
Builder and(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, true);
}
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的元素设置为true,表示该DisplayArea可以包含传入的窗口类型对应的窗口。
简述,and就是把你传入的所有参数(窗口类型)置为true(添加)
/**
* Set that the feature does not apply to the given window types.
*/
Builder except(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, false);
}
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的元素设置为false,表示该DisplayArea不再包含传入的窗口类型对应的窗口。
简述,except就是你传入的所有参数(窗口类型)置为false(不添加)
/**
* Set that the feature applies window types that are layerd at or below the layer of
* the given window type.
*/
Builder upTo(int typeInclusive) {
final int max = layerFromType(typeInclusive, false);
for (int i = 0; i < max; i++) {
mLayers[i] = true;
}
set(typeInclusive, true);
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的的元素之前的所有元素(包含该元素)设置为true,表示当前DisplayArea可以包含比传入的窗口类型层级值低的所有窗口。
简述,upTo把就是[0,typeInclusive]区间内的所有类型窗口置为true(添加)。
其中layerFromType方法非常重要,我们一起看看
private int layerFromType(int type, boolean internalWindows) {
return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
}
调用的了WindowManagerPolicy.getWindowLayerFromTypeLw方法
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @return int An arbitrary integer used to order windows, with lower numbers below higher ones.
*/
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
return getWindowLayerFromTypeLw(type, canAddInternalSystemWindow,
false /* roundedCornerOverlay */);
}
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @param roundedCornerOverlay