likes
comments
collection
share

全网最全,精心整理系列(1)

作者站长头像
站长
· 阅读数 2

1. Activity启动模式,以及常见用法

  • standard标准模式:每次启动一个Activity就会创建一个新的实例
  • singleTop栈顶复用模式:如果新Activity已经位于任务栈的栈顶,就不会重新创建,并回调 onNewIntent(intent) 方法
  • singleTask栈内复用模式:只要该Activity在一个任务栈中存在,都不会重新创建,并回调 onNewIntent(intent) 方法。如果不存在,系统会先寻找是否存在需要的栈,如果不存在该栈,就创建一个任务栈,并把该Activity放进去;如果存在,就会创建到已经存在的栈中
  • singleInstance单实例模式:具有此模式的Activity只能单独位于一个任务栈中,且此任务栈中只有唯一一个实例。

案例:1,2,3三个Activity,2是singleInstance模式,然后1->2,2->3,之后狂点back,在回到Home界面后点击菜单键,发现首先启动的是2Activity。

简单解释一下:1和3是一个task,2是单独的一个task,在我们2->3后,前台的task又从2的回到了1和3的。所以最后退出的task是2的线程,而如果不是重新启动App。上一次最后关闭的Task是2的

2. taskAffinity,allowTaskReparting的用法

taskAffinity用于指定当前Activity(activity1)所关联的Task,allowTaskReparenting用于配置是否允许该activity可以更换从属task,通常情况二者连在一起使用,用于实现把一个应用程序的Activity移到另一个应用程序的Task中。 allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task,默认是继承至application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。

迁移的规则是: 从一个与该Activity TaskAffinity属性不同的任务栈中迁移到与它TaskAffinity相同的任务栈中。

举个例子: 现在有两个应用A和B,A启动了B的一个Activity C,然后按Home键回到桌面,然后再单击B的桌面图标,这个时候不是启动了B的主Activity,而是重新显示了已经被应用A启动的Activity C。我们也可以理解为,C从A的任务栈转移到了B的任务栈中。 可以这么理解,由于A启动了C,这个时候C只能运行在A的任务栈中,但是C属于B应用,正常情况下,它的TaskAffinity值肯定不可能和A的任务栈相同,所以当B启动后,B会创建自己的任务栈,这个时候系统发现C原本想要的任务栈已经创建了,所以就把C从A的任务栈中转移过来了。

3. Activity生命周期

  • A启动B的,然后按back键,执行了哪些方法?按home键呢?

从ActivityA 启动 ActivityB的生命周期: A.onCreate —> A.onStart —> A.onResume —>A.onPause —>B.onCreate —> B.onStart —> B.onResume—> A.onSaveInstanceState —> A.onStop

按下Back键: B.onPause —>A.onRestart—>A.onStart —> A.onResume -->B.onStop ---- >B.onDestroy

按下Home键: B.onPause —> OnSaveInstanceState -->B.onStop ---- >B.onDestroy

  • onSaveInstance方法调用时机

1、当用户按下HOME键时。 2、长按HOME键,选择运行其他的程序时。 3、按下电源按键(关闭屏幕显示)时。 4、从activity A中启动一个新的activity时。 5、屏幕方向切换时,例如从竖屏切换到横屏时。

4. Bitmap内存优化

  • Bitmap内存如何计算

占用内存 = 图片宽度图片高度每个像素所占的内存

图片(BitMap)占用的内存和屏幕密度(Density)无关

  • 如何在不改变图片质量的情况下优化

1:Pre-scaling Bitmaps android中经常会做图片的缩放,所以,预缩放意义很明显,能缩小图片(这里不单单是缩放图片尺寸,而是操作的bitmap),降低内存分配,提升显示性能,api为createScaledBitmap()。

2:inSampleSize 第二种是inSampleSize,作用是对原图降采样,通过设置inJustDecodeBounds = true 在图片不加载进内存的情况下能获取图片宽高,计算合适的压缩比,设置inSampleSize。 inSampleSize具体原理是直接从点阵中隔行抽取最有效率,所以为了兼顾效率, inSampleSize只能是2的整数次幂

  • Bitmap内存复用(Options.inBitmap)

BitmapFactory.Options提供了一个参数options.inBitmap

1.新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小

2.新申请的bitmap与旧的bitmap必须有相同的解码格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444与565格式的bitmap了

  • 超大图加载 BitmapRegionDecoder

1.setInputStream里面去获得图片的真实的宽度和高度,以及初始化我们的mDecoder

2.onMeasure里面为我们的显示区域的rect赋值,大小为view的尺寸

3.onTouchEvent里面我们监听move的手势,在监听的回调里面去改变rect的参数,以及做边界检查,最后invalidate

4.在onDraw里面就是根据rect拿到bitmap,然后draw了

  • 跨进程传递大图 通过 AIDL 使用 Binder 进行 IPC 就不受这个限制
Bundle bundle = new Bundle();
bundle.putBinder("binder"new IRemoteGetBitmap.Stub() {
    @Override
    public Bitmap getBitMap() throws RemoteException {
        return mBitmap;
    }
});
intent.putExtras(bundle);

较大的 bitmap 直接通过 Intent 传递容易抛异常是因为 Intent 启动组件时,系统禁掉了文件描述符 fd 机制 , bitmap 无法利用共享内存,只能拷贝到 Binder 映射的缓冲区,导致缓冲区超限, 触发异常; 而通过 putBinder 的方式,避免了 Intent 禁用描述符的影响,bitmap 写 parcel 时的 allowFds 默认是 true , 可以利用共享内存,所以能高效传输图片

  • 资源文件加载规则

APP在查找图片资源的时候遵循先高后低的原则,假设设备的分辨率是xxhdpi,那么查找顺序如下

1.先去drawable-xxhdpi文件夹查找,如果有这张图片就使用,这个时候图片不会缩放

2.如果没有找到,则去更高密度的文件夹下找,例如drawable-xxxhdpi,密度依次递增,如果找到了,图片将会缩小,因为系统认为这些图片都是给高分辨率设备使用的

3.所有高密度文件夹都没有的话,就会去drawable-nodpi文件夹去找,如果找到,不缩放,使用原图

4.还是没有的话,就会去更低密度的文件夹下面找,xhdpi,hdpi等,密度依次递减,如果找到了,图片将会放大,因为系统认为这个图片是给低分辨率设备使用的

5. 跨进程通信

  • Android的进程间通信方式 Android也是基于Linux内核,Linux现有的进程通信手段有以下几种:

1.管道:在创建时分配一个page大小的内存,缓存区大小比较有限;

2.消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

3.共享内存:无须复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

4.套接字:作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通信;

5.信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

既然有现有的IPC方式,为什么重新设计一套Binder机制

1.效率:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝

2.稳定性:上面说到共享内存的性能优于Binder,那为什么不采用共享内存呢,因为共享内存需要处理并发同步问题,容易出现死锁和资源竞争,稳定性较差。Socket虽然是基于C/S架构的,但是它主要是用于网络间的通信且传输效率较低。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。

3.安全性:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID,且在Binder通信时会根据UID/PID进行有效性检测。

  • binder通信的内存大小限制

1.普通的由Zygote孵化而来的用户进程,所映射的Binder内存大小是不到1M的

2.特殊的进程ServiceManager进程,它为自己申请的Binder内核空间是128K,这个同ServiceManager的用途是分不开的,ServcieManager主要面向系统Service,只是简单的提供一些addServcie,getService的功能

  • Binder机制 原理

Binder 通信采用 C/S 架构,从组件视角来说,包含 Client、 Server、 ServiceManager 以及 Binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中

1.注册服务

Server进程通过Binder驱动 向 Service Manager进程 注册服务,创建 一个Binder对象,注册服务后,Binder驱动持有 Server进程创建的Binder实体

2.获取服务

Client进程通过Binder驱动向ServiceManager进程 获取相应的Service信息的Binder代理对象

  1. 使用服务

Client进程根据获取到的 Service信息(Binder代理对象),通过Binder驱动 建立与该Service所在Server进程通信的链路,并开始使用服务

Client进程 将参数(整数a和b)发送到Server进程

// 1. Client进程 将需要传送的数据写入到Parcel对象中
// data = 数据 = 目标方法的参数(Client进程传进来的,此处就是整数a和b) + IInterface接口对象的标识符descriptor
  android.os.Parcel data = android.os.Parcel.obtain();
  data.writeInt(a); 
  data.writeInt(b); 

  data.writeInterfaceToken("add two int");;
  // 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中

  android.os.Parcel reply = android.os.Parcel.obtain();
  // reply:目标方法执行后的结果(此处是相加后的结果)

// 2. 通过 调用代理对象的transact() 将 上述数据发送到Binder驱动
  binderproxy.transact(Stub.add, data, reply, 0)
  // 参数说明:
    // 1. Stub.add:目标方法的标识符(Client进程 和 Server进程 自身约定,可为任意)
    // 2. data :上述的Parcel对象
    // 3. reply:返回结果
    // 0:可不管

// 注:在发送数据后,Client进程的该线程会暂时被挂起
// 所以,若Server进程执行的耗时操作,请不要使用主线程,以防止ANR


// 3. Binder驱动根据 代理对象 找到对应的真身Binder对象所在的Server 进程(系统自动执行)
// 4. Binder驱动把 数据 发送到Server 进程中,并通知Server 进程执行解包(系统自动执行)

Server进程根据Client进要求 调用 目标方法

// 1. 收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包 & 调用目标方法
  public class Stub extends Binder {

          // 复写onTransact()
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          // code即在transact()中约定的目标方法的标识符

          switch (code) { 
                case Stub.add: { 
                  // a. 解包Parcel中的数据
                       data.enforceInterface("add two int"); 
                        // a1. 解析目标方法对象的标识符

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();
                       // a2. 获得目标方法的参数
                      
                       // b. 根据"add two int"通过queryLocalIInterface()获取相应的IInterface对象(即Server创建的plus)的引用,通过该对象引用调用方法
                       int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
                      
                        // c. 将计算结果写入到reply
                        reply.writeInt(result); 
                        
                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 
      // 2. 将结算结果返回 到Binder驱动

Server进程 将目标方法的结果返回给Client进程

  // 1. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果
  // 2. 通过代理对象 接收结果(之前被挂起的线程被唤醒)

    binderproxy.transact(Stub.ADD, data, reply, 0);
    reply.readException();;
    result = reply.readInt();
  • Application的binder机制是何时启动的

zygote在fork好应用进程后,会给应用启动binder机制

  • binder线程池默认最大数量

每个进程的 Binder 线程池的线程个数上限为 15

  • binder和AIDL

Binder是一个类,它实现了IBinder接口,而IBinder接口定义了与远程对象的交互协议。通常在进行跨进程通信时,不需要实现IBinder接口,直接从Binder派生即可。

除了实现IBinder接口外,Binder中还提供了两个重要的接口。 (1)Transact(),客户端调用,用于发送调用请求 (2)onTransact(),服务端响应,用于接收调用请求

AIDL全称Android接口描述语言,是Android开发工具中提供的一种文件格式,通过在文件中预先定义接口,然后快速生成Binder机制的代码,省去了手动编写Binder机制, AIDL基于Binder,而Messenger基于AIDL

AIDL过程需要注意的地方:

如果aidl文件中使用了自定义的parcelable对象

那么必须要创建一个和它同名的aidl文件,并在其中声明为Parcelable类型。并且aidl中除了基本类型,其他类型的参数必须要标明方向int、out或者inout,in输入型参数,out输出型参数,inout输入输出型参数。

aidl服务端的线程同步

由于aidl服务端方法是在binder线程池中执行的,当多个客户端来访问的时候会存在并发读写问题。比较常见的可以使用CopyOnWriteArrayList来支持并发读写自动实现线程同步,类似的还有ConcurrentHashMap。

aidl服务端耗时可能引起的ANR问题

如果确认服务端方法耗时的话客户端调用的时候放在非ui线程即可。

跨进程解注册监听器RemoteCallbackList

aidl跨进程unRegisterListener解注册监听器的时候,由于跨进程客户端和服务端不是同一个对象所以会解注册失败。可以使用RemoteCallbackList处理,它是系统专门提供的用来删除跨进程listener的接口。它的实现原理:内部有一个map来保存所有的callback回调,map的key值是IBinder类型,value是callback类型。RemoteCallbackList本质上不是一个list,因此不可以有类似list的操作,使用的时候必须beginBroadcast和finishBroadcast配套使用。

  • AIDL oneway、in、out、inout

oneway 关键字用于修改远程调用的行为。

oneway可以用来修饰在interface之前,这样会造成interface内所有的方法都隐式地带上oneway; oneway也可以修饰在interface里的各个方法之前。 被oneway修饰了的方法不可以有返回值,也不可以有带out或inout的参数。

由应用进程到服务进程是通过 binder 驱动进行 IPC 通信的,单向的意思应该是应用进程只向 binder 驱动发送一次数据就结束返回,不再等待回复数据;而不用 oneway 修饰的方法需要等待 binder 驱动与服务端通信完后,再回复数据给应用端。

1.本地调用(同步调用)

如果 oneway 用于本地调用,则不会有任何影响,调用仍是同步调用。

2.远程调用(异步调用)

使用oneway时,远程调用不会阻塞;它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自 Binder 线程池的常规调用进行接收。

带oneway的方法,不会生成局部变量_reply。且Proxy中transact中第四个参数必为android.os.IBinder.FLAG_ONEWAY

不带oneway的方法,会生成局部变量_reply,但当方法返回值为void时,不会生成局部变量_result,这个才是真正的返回值。且Proxy中transact中第四个参数必为0。

in参数使得实参顺利传到服务方,但服务方对实参的任何改变,不会反应回调用方。

out参数使得实参不会真正传到服务方,只是传一个实参的初始值过去(这里实参只是作为返回值来使用的,这样除了return那里的返回值,还可以返回另外的东西),但服务方对实参的任何改变,在调用结束后会反应回调用方。

inout参数则是上面二者的结合,实参会顺利传到服务方,且服务方对实参的任何改变,在调用结束后会反应回调用方。

其实inout,都是相对于服务方。in参数使得实参传到了服务方,所以是in进入了服务方;out参数使得实参在调用结束后从服务方传回给调用方,所以是out从服务方出来。

7. Android中Context的理解?四大组件里面的Context都来源于哪里

  • Context 作用

Context提供了关于应用环境全局信息的接口

  1. 使用 context 调用方法,比如启动 Activity、访问资源、调用系统服务等。
  2. 调用方法时传入 context,比如弹出 Toast、创建 Dialog 等。
  • Context 的数量 Context 数量 = Application 数量 + Activity 数量 + Service 数量

Activity 因为要显示 UI 所以它的 Context 直接继承的是 ContextThemeWrapper

Application 和 Service 的 Context 直接继承的是 ContextWrapper

  • Context 在哪创建的

1.Application

2.Activity

3.Service

ContentProvider 的 context 是 Application

BroadCastReceiver 的 context 是 Activity 或 Service

7. Application启动流程

Application启动可分为三步:

第一步,创建应用进程ActivityThread;

第二步,绑定应用程序和系统程序,也就是通过AIDL跨进程通信让系统进程分配pid和uid等应用信息;

第三步,创建Application,调用onCreate方法。

Android中有一个ActivityThread类,代表应用程序的主线程;Android在打开APP时会首先调用ActivityThread中的main方法,即APP进程的启动点;

public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();//1

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();//2
        thread.attach(false, startSeq);//3

        ...
        Looper.loop();//4

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在main方法中1和4是与启动Handler有关;2处创建了ActivityThread,紧接着3处调用attach方法,意为连接。

  @UnsupportedAppUsage
  private void attach(boolean system, long startSeq) {
        ...
        if (!system) {
            ...
            final IActivityManager mgr = ActivityManager.getService();//1
            try {
                mgr.attachApplication(mAppThread, startSeq);//2
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ...
        } else {
            ...
        }
        ...
    }

mAppThread是ActivityThread 的一个ApplicationThread类型的变量,ApplicationThread是一个BInder对象,可以通过Binder机制远程访问。 在attach方法中,在注释1处,通过AIDL获取了AMS的代理对象IActivityManager ,实现了应用进程和系统进程通信;紧接着在注释2处调用了IActivityManager 的attachApplication方法,即跨进程调用了AMS的attachApplication方法;这里的意思是把Application与AMS连接起来,也就是把APP绑定到系统应用,具体怎么绑定的呢?在ActivityManagerService类中找到attachApplication方法,继续分析;

@Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        if (thread == null) {
            throw new SecurityException("Invalid application interface");
        }
        synchronized (this) {
            int callingPid = Binder.getCallingPid();//1
            final int callingUid = Binder.getCallingUid();//2
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);//3
            Binder.restoreCallingIdentity(origId);
        }
    }

thread其实就是mAppThread对象的代理对象, 在注释1和2处系统分别给应用分配了pid和uid,在注释3处调用了叫attachApplicationLocked的方法,传入了pid和uid,同时也传入了ActivityThread的代理类IApplicationThread,继续往下看看attachApplicationLocked方法中做了什么?

@GuardedBy("this")
    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

        ProcessRecord app;  //1
        long startTime = SystemClock.uptimeMillis();
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);  //2
            }
        } 
        ...
        if (app.instr != null) {  //当前进程是否正在活动
           thread.bindApplication(processName, appInfo, providers, app.instr.mClass,
                    profilerInfo, app.instr.mArguments, app.instr.mWatcher, app.instr.mUiAutomationConnection,
                    testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(getGlobalConfiguration()),
                    app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(),
                    buildSerial, isAutofillCompatEnabled);   //3
        } else {
            //Application 绑定到当前线程
            thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                    null, null, null, testMode, mBinderTransactionTrackingEnabled,
                    enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, isAutofillCompatEnabled);  //4
        }
        ...
        //检测最可见的Activity是否在运行进程中等待,如果再则创建Activity
        if (mStackSupervisor.attachApplicationLocked(app)) {  //5
            didSomething = true;
        }
        ...
    }

注释1处的ProcessRecord是个进程记录类,小伙伴们可以把这个类理解成为一个javabean,用来保存当前进程相关的一些信息(如pid、uip、ApplicationInfo等),在注释2中,根据pid获取到了这个类。 注释3和注释4都是绑定Application,通过AIDL调用了ActivityThread中的bindApplication方法,在这里可以看到,在AMS中系统只是给Application提供了基本信息,并没有创建Application,由此可以猜测Application创建是在ActivityThread中完成的,具体是不是这样呢,我们看看ActivityThread类的bindApplication方法

@Override
        public final void bindApplication(String processName, ApplicationInfo appInfo,
                ProviderInfoList providerList, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, AutofillOptions autofillOptions,
                ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
          
            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providerList.getList();
            data.instrumentationName = instrumentationName;
            ...

            sendMessage(H.BIND_APPLICATION, data);
        }

bindApplication方法中把携带的参数封装成一个AppBindData对象data,最后用handler发送消息,并携带AppBindData数据;H是ActivityThread中的内部类,继承Handler。继续找到Handler处理消息的地方

 public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);//1
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
            }
}

注释1处handleBindApplication方法

##ActivityThread 
private void handleBindApplication(AppBindData data) {
       
        ...
        //创建Application
        app = data.info.makeApplication(data.restrictedBackupMode, null); 
        
        ...
}

handleBindApplication方法中,调用了LoadedApk中的makeApplication方法。我们再来看看makeApplication的具体创建过程

@UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            //获取加载器
            final java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }

           ...
            //获取上下文
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//1
            
            //反射获取Application
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);//2
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);//3
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        return app;
    }

注释1创建了Application的上下文,也就是getApplicationContext()获取到的上下文,获取的是一个ContextImpl对象,ContextImpl 是Context的子类。注释2就是创建Application了,在makeApplication中通过类加载器和反射创建了Application;注释3调用了Application的onCreate方法。

  • AMS是如何确认Application启动完成的? 关键条件是什么?

zygote返给AMS的pid;应用的ActivityThread#main方法中会向AMS上报Application的binder对象

8.startActivity的具体过程

www.jianshu.com/p/e654d2116…

9. Activity#setContentView的具体过程

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

通过上面的方法我们可以看到三个方法,分别是getWindow()、getWindow().setContentView(layoutResID)、还有initWindowDecorActionBar()。我们一个一个看,在getWindow()方法中

/**
 * Retrieve the current {@link android.view.Window} for the activity.
 * This can be used to directly access parts of the Window API that
 * are not available through Activity/Screen.
 *
 * @return Window The current window, or null if the activity is not
 *         visual.
 */
public Window getWindow() {
    return mWindow;
}

getWindow()方法中返回了一个Window对象,但是在这个方法中没有体现Window对象实在什么时候创建的。我们通过搜索Activity中的mWindow对象可以看到在Activity的attach方法中实例化了mWindow对象。

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) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }

拿到mWindow对象之后调用了mWindow.setContentView的方法,点进去我们看到的是Window这个抽象类,并没有看到具体的实现内容,在attach方法中我们看到的是mWindow = new PhoneWindow(this, window),其实PhoneWindow是Window类的实现类。我们到PhoneWindow中去查看setContentView方法。

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

首先mContentParent是一个ViewGroup,我们先对mContentParent进行判空处理,如果为空那么我们需要获取DecorView,如果mContentParent不为空,那么移除当前ViewGroup的所有子View。然后添加我们指定的XML文件,通过Callback回调来更新视图。上面还有三处可能大家不是很清楚。第一个是mLayoutInflater从哪里来的,第二是Callback回调到哪里去了,第三个是 installDecor()方法做了什么。

    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

mLayoutInflater再PhoneWindow实例化的时候被赋值,getCallback()方法对应的setCallback()方法是在Activity的attach方法中设置的。看到这里可以看出回调的位置为Activity。

installDecor()方法做了些什么

if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
          mContentParent = generateLayout(mDecor);
        }
}

    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
    
    
        protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
      ........
    //加载不同的styleable
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

      .................
        // Inflate the window decor.

        int layoutResource;
      ............
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        .......
        mDecor.finishChanging();

        return contentParent;
    }

mDecor是一个DecorView的对象,我们先去判断mDecor是否为空,如果为空,那么回去创建一个mDecor对象,不为空则添加在Window中。

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);这个方法调用是给DecorView来填充内容的具体看看里面做了什么

   void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        ............

        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

上方法中就是将我们的下面布局填充添加到DecorView中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

们在Activity 中onCreate()方法中设置的布局将要天加到中。 再回到PhoneWindow setContentView()方法中来看可以看到mLayoutInflater.inflate(layoutResID, mContentParent); 将布局加到R.id.content中。

注意:setContentView()执行完之后,View此时还是不可见的,要等DecorView添加至window中,然后触发ViewRootImpl#performTraversals方法开始View的绘制,测量等工作

  • Activity的布局是何时显示出来的?ViewRootImpl是何时初始化的?它的作用是什么

activitythreadhandleResumeActivity方法中,会通过WindowManager的addview方法,将前面已经初始化完成了的decorview添加到窗口中,然后完成其布局的测量、布局、绘制流程,然后将其设置为可见

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            ......
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //将decorview添加到窗口中
                    wm.addView(decor, l);
                } else {
                    ......
                }
            }
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            ......
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
       ......
    }

这里的wm就是WindowManagerGlobal:

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
       ......

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
           ......
            int index = findViewLocked(view, false);
            ......
            //初始化ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            try {
                //关联ViewRootImpl与decorview,并且开始布局
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

ViewRootImpl中的setView方法完成了将decorview添加到窗口,进行布局,完成测量布局绘制流程等一些重要工作,点击事件接收器也是在这里初始化的,所以点击事件最先是在传递到decorview中的

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......
                //调用布局流程
                requestLayout();
                ......
                try {
                    ......
                    //添加到window
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
                   ......
                } 
                ......
                //将viewrootimpl设置为decorview的parent
                view.assignParent(this);
                ......
        }
}

requestLayout调用后会依次调用scheduleTraversals->doTraversal->performTraversals, 在performTraversals中会将viewrootimpl中初始化的mattachInfo全部赋值给子view:

    private void performTraversals() {
        ......
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        ......
    }

Decorview下的所有子view都完成了测量、布局、绘制流程,并且全部子view的mattachInfo变量也都已经设置了,ViewRootImpl中的变量mView就是DecorView,然后每个字view中的mattachInfo变量都是持有的ViewRootImpl中的实例引用.

ViewRootImpl其实并不是一个View,它只是一个DecorView与窗口管理之间的一个媒介,负责界面的布局相关的管理。mAttachInfo因其持有ViewRootImpl的引用,所以它的一个重要作用就是协调view的布局重绘相关的功能。

10. Choreographer机制流程

  • ViewRootImpl的requestLayout开启绘制流程
@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
        void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

这里主要关注两点:

  1. postSyncBarrier : Handler 的同步屏障。它的作用是可以拦截 Looper 对同步消息的获取和分发,加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态。也就是说,对View绘制渲染的处理操作可以优先处理(设置为异步消息)。
  2. Choreographer: 编舞者。统一动画、输入和绘制时机。也是这章需要重点分析的内容。

Choreographer 扮演 Android 渲染链路中承上启下的角色

承上:负责接收和处理 App 的各种更新消息和回调,等到 Vsync 到来的时候统一处理。比如集中处理 Input(主要是 Input 事件的处理) 、Animation(动画相关)、Traversal(包括 measure、layout、draw 等操作) ,判断卡顿掉帧情况,记录 CallBack 耗时等

启下:负责请求和接收 Vsync 信号。接收 Vsync 事件回调(通过 FrameDisplayEventReceiver.onVsync );请求 Vsync(FrameDisplayEventReceiver.scheduleVsync) .

  • Choreographer 的工作流程
  1. Choreographer 初始化

  2. 初始化 FrameHandler ,绑定 Looper

  3. 初始化 FrameDisplayEventReceiver ,与 SurfaceFlinger 建立通信用于接收和请求 Vsync

  4. 初始化 CallBackQueues

  5. SurfaceFlinger 的 appEventThread 唤醒发送 Vsync ,Choreographer 回调 FrameDisplayEventReceiver.onVsync , 进入 SurfaceFlinger 的主处理函数 doFrame

  6. Choreographer.doFrame 计算掉帧逻辑

  7. Choreographer.doFrame 处理 Choreographer 的第一个 callback : input

  8. Choreographer.doFrame 处理 Choreographer 的第二个 callback : animation

  9. Choreographer.doFrame 处理 Choreographer 的第三个 callback : insets animation

  10. Choreographer.doFrame 处理 Choreographer 的第四个 callback : traversal

  11. traversal-draw 中 UIThread 与 RenderThread 同步数据

  12. Choreographer.doFrame 处理 Choreographer 的第五个 callback : commit ?

  13. RenderThread 处理绘制数据,真正进行渲染

  14. 将渲染好的 Buffer swap 给 SurfaceFlinger 进行合成

  • Choreographer 的初始化
  1. Choreographer 的单例初始化
    public static Choreographer getInstance() {
            return sThreadInstance.get();
        }
    
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            return choreographer;
        }
    };
  1. Choreographer 的构造函数
private Choreographer(Looper looper, int vsyncSource) {
    mLooper = looper;
    // 1. 初始化 FrameHandler
    mHandler = new FrameHandler(looper);
    // 2. 初始化 DisplayEventReceiver
    mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
    mLastFrameTimeNanos = Long.MIN_VALUE;
    mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    //3. 初始化 CallbacksQueues
    mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
    for (int i = 0; i <= CALLBACK_LAST; i++) {
        mCallbackQueues[i] = new CallbackQueue();
    }
    ......
}

Choreographer类中有一个Looper和一个FrameHandler变量。变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,如果系统使用了Vsync同步机制,则创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件,最后Choreographer创建了一个大小为4的CallbackQueue队列数组,每一个元素作为头指针,引出对应类型的链表,4种事件就是通过这4个链表来维护的。

不同类型的Callback包括如下4种:

    public static final int CALLBACK_INPUT = 0;
    public static final int CALLBACK_ANIMATION = 1;
    public static final int CALLBACK_TRAVERSAL = 2;
    public static final int CALLBACK_COMMIT = 3;
  1. FrameHandler
private final class FrameHandler extends Handler {
    ......
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_DO_FRAME://开始渲染下一帧的操作
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC://请求 Vsync 
                doScheduleVsync();
                break;
            case MSG_DO_SCHEDULE_CALLBACK://处理 Callback
                doScheduleCallback(msg.arg1);
                break;
        }
    }
}
  1. Choreographer 初始化链

在 Activity 启动过程,执行完 onResume 后,会调用 Activity.makeVisible(),然后再调用到 addView(), 层层调用会进入如下方法

ActivityThread.handleResumeActivity(IBinder, boolean, boolean, String) (android.app) 
-->WindowManagerImpl.addView(View, LayoutParams) (android.view) 
  -->WindowManagerGlobal.addView(View, LayoutParams, Display, Window) (android.view) 
    -->ViewRootImpl.ViewRootImpl(Context, Display) (android.view) 
    public ViewRootImpl(Context context, Display display) {
        ......
        mChoreographer = Choreographer.getInstance();
        ......
    }
  1. FrameDisplayEventReceiver

Vsync 的注册和回调通过 FrameDisplayEventReceiver 这个类 FrameDisplayEventReceiver 继承 DisplayEventReceiver , 有三个比较重要的方法

  1. onVsync -- Vsync 信号回调
  2. run -- 执行 doFrame
  3. scheduleVsync -- 请求 Vsync 信号
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
    ......
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        ......
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    @Override
    public void run() {
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
    
    public void scheduleVsync() {
        ......  
        nativeScheduleVsync(mReceiverPtr);
        ......
    }
}

可见onVsync()过程是通过FrameHandler向主线程Looper发送了一个自带callback的消息 callback为FrameDisplayEventReceiver。 当主线程Looper执行到该消息时,则调用FrameDisplayEventReceiver.run()方法,紧接着便是调用doFrame。

void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }
            //原本计划的绘帧时间点
            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            //由于Vsync事件处理采用的是异步方式,因此这里计算消息发送与函数调用开始之间所花费的时间
            final long jitterNanos = startNanos - frameTimeNanos;
            //如果线程处理该消息的时间超过了屏幕刷新周期
            if (jitterNanos >= mFrameIntervalNanos) {
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                frameTimeNanos = startNanos - lastFrameOffset;
            }
            //如果frameTimeNanos小于一个屏幕刷新周期,则重新请求VSync信号
            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();
                return;
            }

            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
            //分别回调CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL事件
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

当Vsync事件到来时,顺序执行4种事件对应CallbackQueue队列中注册的回调。

void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            //从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;

            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                if (jitterNanos >= 2 * mFrameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                            + mFrameIntervalNanos;
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            //由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecord
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                if (DEBUG_FRAMES) {
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

接开篇讲的

void scheduleTraversals() {
    ...             
    mChoreographer.postCallback(
        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}

mTraversalRunnable对应:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal()
        } 
}

run方法被执行,所以doTraversal()被执行,开启View的绘制流程。

11. 消息是如何存储的?延时消息一定准时么?是如何保证延时时间的?

  • 消息如何发送的

Handler::sendMessage

public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

Handler:: sendMessageDelayed

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

Handler:: sendMessageAtTime

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
  • 消息是如何存储

MessageQueue::enqueueMessage

boolean enqueueMessage(Message msg, long when) {
       // 每一个普通Message必须有一个target
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) { //检查信息是否有使用过
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {  //正在退出时,回收msg,加入到消息池
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                 //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
               //消息队头存在阻塞,并且同时Message是队列中最早的异步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
            //去唤醒消息队列所在的线程
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。 当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。

  • Handler发送消息的 Delay 可靠吗?

答案是不靠谱的,引起不靠谱的原因有如下

  1. 发送的消息太多,Looper负载越高,任务越容易积压,进而导致卡顿
  2. 消息队列有一些消息处理非常耗时,导致后面的消息延时处理
  3. 大于Handler Looper的周期时基本可靠(例如主线程>50ms)
  4. 对于时间精确度要求较高,不要用handler的delay作为即时的依据

如何优化保证可靠性

消息精简,从数量上处理

  1. 队列优化,重复消息过滤
  2. 互斥消息取消
  3. 复用消息
  • Handler#dispatchMessage
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //当Message存在回调方法,回调msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //当Handler存在Callback成员变量时,回调方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回调方法handleMessage()
        handleMessage(msg);
    }
}

分发消息流程:

  1. 当Message的回调方法不为空时,则回调方法msg.callback.run(),其中callBack数据类型为Runnable,否则进入步骤2;
  2. 当Handler的mCallback成员变量不为空时,则回调方法mCallback.handleMessage(msg),否则进入步骤3;
  3. 调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
  • 消息是如何获取的

MessageQueue::next()

    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) { //当消息循环已经退出,则直接返回
            return null;
        }

        int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
           //阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
            nativePollOnce(ptr, nextPollTimeoutMillis);
             //如果阻塞操作结束,则去获取消息 
            synchronized (this) {
                // 去获取下一条消息
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                 //当消息的Handler为空时,则查询异步消息
                if (msg != null && msg.target == null) {
                    // 查找队列中的下一个异步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                          // 获取一条消息,并返回
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //设置消息的使用状态,即flags |= FLAG_IN_USE
                        msg.markInUse();
                        return msg;  //成功地获取MessageQueue中的下一条即将要执行的消息
                    }
                } else {
                    //没有消息
                    nextPollTimeoutMillis = -1;
                }

               // 现在,所有挂起的消息都已处理完毕,请处理退出消息
                if (mQuitting) {
                    dispose();
                    return null;
                }
               //当消息队列为空,或者是消息队列的第一个消息时
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    //没有idle handlers 需要运行,则循环并等待。
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            //只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; //去掉handler的引用

                boolean keep = false;
                try {
                    keep = idler.queueIdle();//idle时执行的方法
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

               //重置idle handler个数为0,以保证不会再次重复运行
            pendingIdleHandlerCount = 0;

            //当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
            nextPollTimeoutMillis = 0;
        }
    }

nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。

当处于空闲时,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息 我们先看下native层的nativePollOnce是如何实现

Messagenext()从队列中获取并返回下一个消息. 如果队列为空, 则该方法将调用native void nativePollOnce(long, int), 该方法将一直阻塞直到添加新消息为止,当将 Message 添加到队列时, 框架调用 enqueueMessage 方法, 该方法不仅将消息插入队列, 而且还会调用native static void nativeWake(long). nativePollOncenativeWake 的核心魔术发生在 native 代码中. native MessageQueue 利用名为 epoll 的 Linux 系统调用, 该系统调用可以监视文件描述符中的 IO 事件. nativePollOnce 在某个文件描述符上调用 epoll_wait, 而 nativeWake 写入一个 IO 操作到描述符, epoll_wait 等待. 然后, 内核从等待状态中取出 epoll 等待线程, 并且该线程继续处理新消息.

结论:

nativePollOnce. 它只是表明所有消息的处理已完成, 线程正在等待下一个消息.

  • Looper#loop方法为何不会导致ANR

在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作

  • Handler中的IdleHandler了解么?合适调用?

IdleHandler是一个回调接口,可以通过MessageQueueaddIdleHandler添加实现类。当MessageQueue中的任务暂时处理完了(没有新任务或者下一个任务延时在之后)会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调。

合适场景可以从下面几点出发:

  • 消息队列相关
  • 主线程能干的事情
  • 返回true和false带来不同结果

场景:

1.Activity启动优化:onCreateonStartonResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间

2.想要一个View绘制完成之后添加其他依赖于这个View的View,当然这个View#post()也能实现,区别就是前者会在消息队列空闲时执行。

3.发生一个返回true的IdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作。

4.一些第三方库中使用,比如LeakCanaryGlide

  • Handler Message 种类

Handler的Message种类分为3种:

普通消息

屏障消息

异步消息

屏障消息如何插入消息队列?

同步屏障是通过MessageQueue的postSyncBarrier方法插入到消息队列

如何区分普通消息和异步消息

屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。

  • Message对象池

通过obtain方法获取Message对象使得Message到了重复的利用,减少了每次获取Message时去申请空间的时间

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

最大限制容量

    // 设置了对象池中的Message的最大数量,也就是链表的最大长度
    private static final int MAX_POOL_SIZE = 50;

如何实现给Handler发送一个Runnable,不通过Handler#post(Runnable run)这个API

  1. 利用 public static Message obtain(Handler h, Runnable callback)

  2. 反射设置Message#callback属性

12.ThreadLocal和ThreadLocalMap分析

  • ThreadLocal set() 方法
public void set(T value) {
    // 获取当前线程
    Thread t = Thread.currentThread();

    // 获取线程所对应的ThreadLocalMap,从这可以看出每个线程都是独立的
    ThreadLocalMap map = getMap(t);

    // 如果map不为空,则k-v赋值,看出k是this,也就是当前ThreaLocal对象
    if (map != null)
        map.set(this, value);

    // 如果获取的map为空,则创建一个并保存k-v关系
    else
        createMap(t, value);
}

1、获取当前线程,根据当前线程获取对应的ThreadLocalMap

2、如果对应的ThreadLocalMap不为null,则调用其的set方法保存对应关系

3、如果map为null,就最终调用ThreadLocalMap的构造方法创建一个ThreadLocalMap并保存对应关系

  • ThreadLocal get() 方法
/**     
 * 获取当前线程本地变量的值
 */
public T get() {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程对应的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // 如果map不为空
    if (map != null) {
        // 如果当前ThreadLocal对象对应的Entry还存在
        ThreadLocalMap.Entry e = map.getEntry(this);
        // 并且Entry不为null,返回对应的值,否则都执行setInitialValue方法
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果该线程对应的ThreadLocalMap还不存在,则执行初始化方法
    return setInitialValue();
}


ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}


private T setInitialValue() {
    // 获取初始值,一般是子类重写
    T value = initialValue();
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程对应的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // 如果map不为null
    if (map != null)
        // 调用ThreadLocalMap的set方法进行赋值
        map.set(this, value);
    else
        // 否则创建个ThreadLocalMap进行赋值
        createMap(t, value);
    return value;
}


/**
 * 构造参数创建一个ThreadLocalMap代码
 * ThreadLocal为key,我们的泛型为value
 */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    // 初始化table的大小为16
    table = new Entry[INITIAL_CAPACITY];
    // 通过hashcode & (长度-1)的位运算,确定键值对的位置
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

    // 创建一个新节点保存在table当中
    table[i] = new Entry(firstKey, firstValue);

    // 设置table内元素为1
    size = 1;

    // 设置扩容阈值
    setThreshold(INITIAL_CAPACITY);
}

1、获取当前线程,根据当前线程获取对应的ThreadLocalMap

2、在ThreadLocalMap当中获取该ThreadLocal对象对应的Entry节点,并且返回对应的值

3、如果获取到的ThreadLocalMap为null,则证明还没有初始化,就调用setInitialValue方法

1)在调用setInitialValue方法的时候,会双重保证,再进行获取一次ThreadLocalMap

2)如果依然为null,就最终调用ThreadLocalMap的构造方法