硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)

Posted 初一十五啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)相关的知识,希望对你有一定的参考价值。

前言

短短几个月,看着越来越多的读者通过我写的文章而关注公众号,我好开心,同时也诚惶诚恐,害怕无法持续输出高质量文章,让读者失望。但我知道,只有写出让读者心动的文章,才能得其心,会好好加油,为大家持续输出精品文章的。

本文分享Framework相关内容,提纲如下:

💡一丶Framework通信

1.1.android Binder

进程隔离:

内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据 为了保证系统的安全,用户空间和内核空间是天然隔离的 每个进程有自己的虚拟内存空间,为了安全,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间

为什么要用Binder

  • Android系统内核是Linux内核

  • Linux内核进程通信有:管道、内存共享、SocketFile

  • 对比:

Binder的一次拷贝发生在用户空间拷贝到内核空间;

用户空间: App进程运行的内存空间;

内核空间: 系统驱动、和硬件相关的代码运行的内存空间,也就是进程ID为0的进程运行的空间;

程序局部性原则: 只加载少量代码;应用没有运行的代码放在磁盘中,运行时高速缓冲区进行加载要运行的代码;默认一次加载一个页(4K),若不够4K就用0补齐;

MMU:内存管理单元;

给CPU提供虚拟地址;

当对变量操作赋值时:

  • CPU拿着虚拟地址和值给到MMU

  • MMU用虚拟地址匹配到物理地址,MMU去物理内存中进行赋值;

物理地址: 物理内存的实际地址,并不是磁盘;

虚拟地址: MMU根据物理内存的实际地址翻译出的虚拟地址;提供给CPU使用;

页命中:CPU读取变量时,MMU在物理内存的页表中找到了这个地址;

页未命中:CPU读取变量时,MMU在物理内存的页表中没有找到了这个地址,此时会触发MMU去磁盘读取变量并存到物理内存中;

普通的二次拷贝:

应用A拷贝到服务端:coay_from_user

从服务端拷贝到应用B:coay_to_user

mmap():

  • 在物理内存中开辟一段固定大小的内存空间

  • 将磁盘文件与物理内存进行映射(理解为绑定)

  • MMU将物理内存地址转换为虚拟地址给到CPU(虚拟地址映射物理内存)

共享内存进程通信:

  • 进程A调用mmap()函数会在内核空间中虚拟地址和一块同样大小的物理内存,将两者进行映射

  • 得到一个虚拟地址

  • 进程B调用mmap()函数,传参和步骤1一样的话,就会得到一个和步骤2相同的虚拟地址

  • 进程A和进程B都可以用同一虚拟地址对同一块映射内存进行操作

  • 进程A和进程B就实现了通信

  • 没有发生拷贝,共享一块内存,不安全

Binder通信原理:

角色:Server端A、Client端B、Binder驱动、内核空间、物理内存

  • Binder驱动在物理内存中开辟一块固定大小(1M-8K)的物理内存w,与内核空间的虚拟地址x进行映射得到

  • A的用户空间的虚拟地址ax和物理内存w进行映射

  • 此时内核空间虚拟地址x和物理内存w已经进行了映射,物理内存w和Server端A的用户空间虚拟地址ax进行了映射:也就是 内核空间的虚拟地址x = 物理内存w = Server端A的用户空间虚拟地址ax

  • B发送请求:将数据按照binder协议进行打包给到Binder驱动,Binder驱动调用coay_from_user()将数据拷贝到内核空间的虚拟地址x

  • 因步骤3中的三块区域进行了映射

  • Server端A就得到了Client端B发送的数据

  • 通过内存映射关系,只发生了一次拷贝

Activity跳转时,最多携带1M-8k(1兆减去8K)的数据量;

真实数据大小为:1M内存-两页的请求头数据=1M-8K;

应用A直接将数据拷贝到应用B的物理内存空间中,数据量不能超过1M-8K;拷贝次数少了一次,少了从服务端拷贝到用户;

IPC通信机制:

  • 服务注册

  • 服务发现

  • 服务调用

以下为简单的主进程和子进程通信:

1、服务注册: 缓存中心中有三张表(暂时理解为三个HashMapBinder用的是native的红黑树):

  • 第一种:放keyString - value:类的Class

  • 第二种:放keyClass的类名 - value:类的方法集合;

  • 第三种:放key :Class的类名 - value:类的对象;

类的方法集合:key-value;

key:方法签名:“方法名” 有参数时用 “方法名-参数类型-参数类型-参数类型…”;

value: 方法本身;

注册后,服务若没被调用则一直处于沉默状态,不会占用内存,这种情况只是指用户进程里自己创建的服务,不适用于AMS这种;

2、服务发现: 当被查询到时,要被初始化;

  • 客户端B通过发送信息到服务端A

  • 服务端解析消息,反序列化

  • 通过反射得到消息里的类名,方法,从注册时的第一种、第二种表里找到Class,若对象没初始化则初始化对象,并将对象添加到第三种的表里;

3、服务调用

  • 使用了动态代理

  • 客户端在服务发现时,拿到对象(其实是代理)

  • 客户端调用对象方法

  • 代理发送序列化数据到服务端A

  • 服务端A解析消息,反序列化,得到方法进行处理,得到序列化数据结果

  • 将序列化结果写入到客户端进程的容器中;

  • 回调给客户端

AIDL BpBinder:数据发送角色 BbBinder:数据接收角色

编译器生成的AIDLjava接口.Stub.proxy.transact()为数据发送处;

发送的数据包含:数据+方法code+方法参数等等;

  • 发送时调用了Linux的驱动

  • 调用copy_from_user()拷贝用户发送的数据到内核空间

  • 拷贝成功后又进行了一次请求头的拷贝:copy_from_user()

  • 也就是把一次的数据分为两次拷贝

请求头:包含了目的进程、大小等等参数,这些参数占了8K

编译器生成的AIDLjava接口.Stub.onTransact()为数据接收处;

Binder中的IPC机制:

  • 每个App进程启动时会在内核空间中映射一块1M-8K的内存

  • 服务端A的服务注册到ServiceManager中:服务注册

  • 客户端B想要调用服务端A的服务,就去请求ServiceManager

  • ServiceManager去让服务端A实例化服务:服务发现

  • 返回一个用来发送数据的对象BpBinder给到客户端B

  • 客户端B通过BpBinder发送数据到服务端A的内核的映射区域(传参时客户端会传一个reply序列化对象,在底层会将这个地址一层一层往下传,直至传到回调客户端):这里发生了一次通信copy_from_user:服务调用

  • 服务端A通过BBBinder得到数据并处理数据

  • 服务端唤醒客户端等待的线程;将返回结果写入到客户端发送请求时传的一个reply容器地址中,调用onTransact返回;

  • 客户端在onTransac中得到数据;通信结束;

ServiceManager维持了Binder这套通信框架;

1.2.Android Handler

Handler、Message、MessageQueue、Looper;

以下为零散的记录,最后有总结; 内存泄露的本质:

长生命周期对象持有短生命周期对象,导致短生命周期对象销毁不掉;

持有链:

线程>>Looper>>MessageQueue>>Message>>Handler>>Activity;

Message对象的变量target为发送消息的Handler; MessageQueue 队列里放MessageLooper对象里实例化MessageQueue; 一个线程绑定一个Looper

为什么要有handler? 主要目的是要解决线程切换问题,handler里的Message机制解决了线程间通信;

为什么有队列MessageQueueMessageQueue是一个单向链表,next()调用nativePollOnce->lunxepoll_wait()等待实现阻塞时队列;

  • 在单线程中一次只能执行一句代码

  • 假如发送了一个大消息A

  • 处理这个大的消息A

  • 但是处理的太慢了

  • 从而导致其他后续要发送的消息发不出去

  • 因为单线程阻塞到了第3步处理那个消息A的地方

队列的出现解决了"处理消息"阻塞到"发送消息"的问题;

队列是生产者消费者模式;

而要使用队列需要至少两个线程、和一个死循环;

  • 一个线程负责生产消息;

  • 一个线程消费消息;

  • 死循环需要取出放入队列里的消息;

为什么有Looper

为了循环取出队列里的消息;

一个线程有几个Looper,为什么不会有多个?

一个线程一个Looper,放在ThreadLocalMap中;

假如Looper对象由Handler创建,每创建一个Handler就有一个Looper,那么调用Looper.loop()时开启死循环;在外边调用Looper的地方就会阻塞;

主线程中Looper的死循环为什么没有导致系统卡死?

  • 我们的UI线程主线程其实是ActivityThread线程,而一个线程只会有一个Looper;

  • ActivityThread.javamain函数是一个APP进程的入口,如果不卡死,main函数执行完则整个应用进程就会退出;

  • android是以事件为驱动的操作系统,当有事件来时,就去做对应的处理,没有时就显示静态界面;

获取当前线程:Thread.currentThread();

ThreadLocalMap:类似于HashMap;

每个Thread对象都有一个对应的ThreadLocalMap;

Looper.prepare()时,存入Looper,存LooperThreadLocalMap的key为ThreadLocalvalueLooper

内存抖动根本的解决方式是复用;

handler.obtainMessage();

  • Looper的回收池中取Message

  • Message是一个单向链表,Message不是一个单纯的对象,而是一个链表集合

  • 最大长度固定50个

Linux函数: epoll_create:App注册进红黑树中,拿到一个事件fd的值; epoll_ctl:注册事件类型,监听fd是否改变(Linux中事件都会被写入文件中,如触摸屏幕事件会写入到:dev/input/event0文件中),fd有改变时唤醒epoll_waitepoll_wait:有事件时就分发,没事件就阻塞

总结: handler如何做的线程切换的? 首先Handler的使用步骤:

  • 调用Looper.prepare();

  • 创建Handler对象;

  • 调用Looper.Loop()方法。

  • 线程中发送消息。

在第一步时,创建一个Looper,并放到当前线程的变量threadLocals中;threadLocals是一个map,key为ThreadLocal对象本身,value为Looper;在Looper.loop()时取出;

第二步,用户在当前线程(可能是子线程)创建Handler对象;

第三步,Looper.loop()一直在死循环,Looper.loop()这句代码下面的代码是不会被调用的,调用Looper.loop()函数时,先从当前线程的map变量中取出Looper,再从Looper中拿到队列MessageQueue,for循环中不断从队列中取出消息;

第四步,在其他线程调用handelr发送消息时,Message里有个target,就是发送消息的handler;

Looper.loop()时,队列中取到消息时,调用msg.target.dispatchMessage(msg);其实就是handler对象.dispatchMessage(msg);

所以不论在哪个线程调用发送消息,都会调用到handler自己分发消息;而handler所处的线程是创建时的“当前线程”,所以处理时也就回到了“当前线程”;实现了线程切换,和线程通信;

Looper的死循环为什么不会让主线程卡死(或ANR)? 简单版:

  • 我们的UI线程主线程其实是ActivityThread所在的线程,而一个线程只会有一个Looper;

  • ActivityThread.javamain函数是一个APP进程的入口,如果不一直循环,则在main函数执行完最后一行代码后整个应用进程就会退出;

  • android是以事件为驱动的操作系统,当有事件来时,就去做对应的处理,没有时就显示静态界面;

  • ANR发生条件是: Activity:5 秒。应用在 5 秒内未响应用户的输入事件(如按键或者触摸) BroadCastReceiver :10 秒。BroadcastReceiver 未在 10 秒内完成相关的处理 Service:20 秒(均为前台)。Service 在20 秒内无法处理完成

  • 如果Handler收到以上三个相应事件在规定时间内完成了,则移除消息,不会ANR;若没完成则会超时处理,弹出ANR对话框;

详细:

  1. App进程的入口为ActivityThread.java的main()函数,注意ActivityThread不是一个线程;

  2. 应用的ui主线程实际是调用ActivityThread.java的main()函数执行时所在的线程,而这个线程对我们不可见,但是这就是主线程;参考:

  3. ActivityThread.javamain()函数中,会调用Looper.prepareMainLooper();

  4. Looper.prepareMainLooper()会创建一个Looper并放到当前线程(主线程)的变量threadLocals中进行绑定,threadLocals是一个ThreadLocal.ThreadLocalMap;

  5. ActivityThread.javamain()函数结尾,开启Looper.loop()进行死循环,不让main函数结束,从而让App进程不会结束;

  6. Android系统是以事件作为驱动的操作系统,当有事件来时,就去做对应处理,没有事件时,就显示当前界面,不做其他多余操作(浪费资源);

  7. Looper.loop()的死循环中,不仅要取用户发的事件,还要取系统内核发的事件(如屏幕亮度改变等等);

  8. 在调用Looper.loop()时,从MessageQueue.next()中获取事件,若没有则阻塞,有则分发;

  9. MessageQueue其实不是一个队列,用epoll机制实现了阻塞;

  10. Looper.prepareMainLooper()时,调用c++函数epoll_create()会将App注册进epoll机制的红黑树中得到fd的值,epoll_ctl()给每个App注册事件类型并监听fd值是否改变,fd有改变时唤醒epoll_wait;

  11. epoll_wait()有事件时就分发,没事件就阻塞

子线程的Looper和子线程Looper有什么不同?

子线程Looper是可以退出的,主线程不行;

1.3.Jetpack之LiveData订阅与数据分发

使用:

public class LiveDataActivity extends AppCompatActivity 
 
    MutableLiveData liveData= new MutableLiveData<String>();
 
    void liveDataTest()
        //  任何线程都可以发送数据
        liveData.postValue("postValue");
        // 只有主线程可以发送数据
        // liveData.setValue("setValue")
    
 
    void observeTest()
        //订阅
        liveData.observe(this, new Observer<String>() 
            @Override
            public void onChanged(String data) 
                //收到数据data
            
        );
        //可以有多个订阅
        liveData.observe(this, new Observer<String>() 
            @Override
            public void onChanged(String data) 
                //收到数据data
            
        );
    

阶段一:

postValue:

protected void postValue(T value) 
        boolean postTask;
        synchronized (mDataLock) 
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        
        if (!postTask) 
            return;
        
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    
  1. 将用户发送的数据给到一个 mPendingData 的变量;
  2. 切换到主线程
  3. 执行了一个mPostValueRunnable;

mPostValueRunnable:

volatile Object mPendingData = NOT_SET;
    private int mVersion;
 
    private final Runnable mPostValueRunnable = new Runnable() 
        @SuppressWarnings("unchecked")
        @Override
        public void run() 
            Object newValue;
            synchronized (mDataLock) 
                newValue = mPendingData;
                mPendingData = NOT_SET;
            
            setValue((T) newValue);
        
    ;
  1. mPostValueRunnable中将mPendingData给到了新的临时变量newValue;
  2. mPendingData的值置为空;
  3. 调用setValue(newValue);

setValue:

private volatile Object mData;
 
    private int mVersion;    
 
    @MainThread
    protected void setValue(T value) 
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    
  1. 将数据版本mVersion +1;
  2. 将发送的数据给到了mData;
  3. 调用分发数据dispatchingValue

看到这里发现,数据其实最后存到了mData中;若想发送订阅消息,肯定得添加订阅者;

阶段二:

添加订阅者,observe():

        //用户使用:
        //liveData.observe(this@LiveDataActivity,
        //    
        //)
 
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
            new SafeIterableMap<>();
    
 
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) 
        assertMainThread("observe");
        //如果被观察者的生命周期是DESTROYED,就不添加订阅者
        if (owner.getLifecycle().getCurrentState() == DESTROYED) 
            // ignore
            return;
        
        
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
       
        ...
 
        owner.getLifecycle().addObserver(wrapper);
    
  1. 将有生命周期的Activity和订阅者Observer传了进来;
  2. 判断Activity的生命周期;
  3. ActivityObserver封装为一个LifecycleBoundObserver对象;
  4. LifecycleBoundObserver放到了mObservers这个Map集合中;
  5. mapkey为观察者,value为封装了activity和观察者Observer的对象LifecycleBoundObserver;

LifecycleBoundObserver及它的父类ObserverWrapper

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver 
        @NonNull
        final LifecycleOwner mOwner;
 
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) 
            super(observer);
            mOwner = owner;
        
 
        ...
 
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) 
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            if (currentState == DESTROYED) 
                removeObserver(mObserver);
                return;
            
            Lifecycle.State prevState = null;
            while (prevState != currentState) 
                prevState = currentState;
                activeStateChanged(shouldBeActive());
                currentState = mOwner.getLifecycle().getCurrentState();
            
        
 
        ...
 
        @Override
        void detachObserver() 
            mOwner.getLifecycle().removeObserver(this);
        
    
 
 
 
 
    //ObserverWrapper
 
    private abstract class ObserverWrapper 
        //传进来的观察者放这里了
        final Observer<? super T> mObserver;
 
        boolean mActive;
        int mLastVersion = START_VERSION;
 
        ObserverWrapper(Observer<? super T> observer) 
            mObserver = observer;
        
        
        ...
 
        void activeStateChanged(boolean newActive) 
            if (newActive == mActive) 
                return;
            
 
            mActive = newActive;
            changeActiveCounter(mActive ? 1 : -1);
            if (mActive) 
                dispatchingValue(this);
            
        
   
  1. 将有生命周期的Activity给到了LifecycleBoundObserver的mOwner
  2. 将观察者给到了LifecycleBoundObserver的父类ObserverWrappermObserver
  3. 其实LifecycleBoundObserver就可以获取到ActivitymObserver
  4. 上一步,看到将封装后的LifecycleBoundObserver放到了mObservers这个map中;
  5. mapkey为观察者,value为封装了activity和观察者Observer的对象LifecycleBoundObserver

阶段三:

分发:

在阶段一setValue()时,调用了dispatchingValue(null);

    void dispatchingValue(@Nullable ObserverWrapper initiator) 
        if (mDispatchingValue) 
            mDispatchInvalidated = true;
            return;
        
        mDispatchingValue = true;
        do 
            mDispatchInvalidated = false;
            if (initiator != null) 
                considerNotify(initiator);
                initiator = null;
             else 
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) 
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) 
                        break;
                    
                
            
         while (mDispatchInvalidated);
 
        mDispatchingValue = false;
    
 
 
 
 
    private void considerNotify(ObserverWrapper observer) 
        if (!observer.mActive) 
            return;
        
        ...
 
        if (observer.mLastVersion >= mVersion) 
            return;
        
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    
  1. 当为null时,走到了for循环
  2. 循环取出map中的value,而value就是LifecycleBoundObserver;
  3. LifecycleBoundObserver中有mObservermObserver就是用户传的观察者;
  4. mObserver.onChanged((T) mData);
  5. 完成了“主动”分发;

💡二丶Framework底层服务

2.1.Android Framework源码-AMS

APP的启动到绘制过程中,AMS源码核心应用方案
AMS(ActivityManagerService)SystemServer的进程中,是SystemServer中的一个对象;

作用:

  • 管理activity的生命周期

  • 启动activity

  • PMS进行交互

    Activity->AMS:

  • 调用activity.startActivity()

  • 通过ActivityManage.getService("activity")得到AMSBpBinder

  • 通过BpBinder发送请求,调用AMS的startActivity()

AMS->PMS:

  • AMSPMS都在SystemServer进程中,都是SystemServer中一个对象

  • 通过包名和PMS里的缓存mPackage查询到App对应的Package

  • 使用activity的类名通过PMS里的内部类PackageManagerInternalImpl查询到activity对应的包装类ResolveInfo; ps:ResolveInfo这个javabean里有activityInfo、ServiceInfo等变量,查询啥就给哪个变量赋值,再返回ResolveInfo;

  • 得到ResolveInfo里的activityInfo;

  • activityInfo返回给App进程的ActivityThread;`

  • ActivityThread中发送事件

  • ActivityThread中的Handler对象mH收到159事件,处理

  • 通过反射创建Activity对象

  • Activity对象放到activtes启动记录中

ActivityThread

  • 每个应用有一个ActivityThread;是应用的入口;

  • APP进程中

  • AMS的缓存中心

  • ActivityThread中的List<ActivityRecord> activtes放了activity的启动记录

ActivityThread中重要的对象:

  • ApplicationThreadAMS回调给ActivityThread数据的桥梁

  • mInstrumentation:管理ApplicationActivity的生命周期(及创建)

  • mH:Handler,处理ApplicationThread里各种回调函数发送的各种消息

点击桌面App图标发生了什么?

  1. 点击的APP图标是在单独的Luancher进程,是一个系统App进程

  2. Luancher进程请求SystemServer进程中的AMS去创建应用的根ActivityAndroidMnifest.xmliniten-fifterLuancheactivity

  3. AMS通过包名让PMS查询到相关应用信息,得到应用的Package

  4. AMS创建activity栈,根据Package拿到根activity的配置节点信息,放到栈中,此时栈中只有一个根activity的配置节点信息,也就是在栈顶;(此处的栈不是应用层的栈,这个栈只是用来放activity节点信息的)

  5. AMS请求zygote进程创建App进程;zygote进程比较特殊, 使用Socket通信,而不是binderzygote是所有应用的孵化器,zygote进程挂掉时,手机会自动重启;

  6. zygote进程去forkApp进程;

  7. APP进程中的主线程调用ActivityThread.main()静态函数,main中创建 ActivityThread对象

  8. 接着在ActivityThread.attch()中创建了一个ApplicationThread对象,作为和AMS通信时,返回结果的桥梁;

  9. App进程通过AMSbinder调用attachApplication(thread)请求AMS获取应用对应的Applaction和栈顶中activity节点信息(步骤4),此时给AMS传过去了一个thread,这个thread就是ApplicationThread

  10. AMS将从PMS查到的application节点数据序列化后,调用thread.bindApplaction(data数据…)传给ActivityThread; (此时代码还会继续往下执行,去获取栈顶activity的节点信息)

  11. ActivityThread调用sendMessage发送消息BIND_APPLICATION(110)HandlerHandler调用handleBindApplication(data)

  12. 通过反射实例化Instrumentation对象:负责applicationactivity的生命周期的管理

  13. 通过Instrumentation对象反射实例化new Applaction对象app

  14. 调用Instrumentation.callApplactionOnCreate(app)

  15. 执行Applaction.onCreate()

  16. 步骤10中AMS继续向下执行查找activityAMS将查到的栈顶根ActivityLaunchActivity )信息封装到一个事务ClientTransaction中,提交事务并执行,在执行中,调用thread.scheduleTransaction(事务数据);(threadActivityThread中的ApplicationThread

  17. ApplicationThread回调scheduleTransaction函数中,发送`EXECUTE_TRANSACTION(159)消息

  18. Handler处理EXECUTE_TRANSACTION消息,从事务数据中取出LaunchActivity信息,并调用hanldeLaunchActivity(activity数据)

  19. 通过Instrumentation对象反射实例化newActivity()出对象activity

  20. 执行activity.attach(),在attach中创建WMS的桥接代理类;(绘制流程会用到)

  21. 通过Instrumentation调用callActivityOnCreate(activity)

  22. 执行Activty.onCreate();

  23. 至此启动页根Activity启动完成;

下图中4-5中少了上面7-23的步骤:

7-15创建并启动了Application;

16-22创建并启动了Activity

应用内activityactivity的跳转是跨进程通信,还是同一个进程内通信?

是跨进程通信;

跳转流程参考上面的:省去了application的创建过程;

步骤3 +步骤16-23;

2.2.Android Framework源码-PMS

Framework面试必问三:Zygote,AMS,PMS连环炮
SystemServer: Android一切服务的启动者;

  1. 开机时,板子引导芯片启动引导程序

  2. 引导程序启动PID为0的linux内核进程

  3. linux系统启动init脚本,启动PID永远为1的init进程

  4. init进程启动SystemManager进程;

  5. SystemManager进程启动完后;

  6. init进程启动zygote进程(native进程)

  7. zygote调用SystemServer.javamain函数,frokSystemServer进程(java进程)

  8. SystemServer.javamain函数里执行SystemServerrun方法,main函数里只有一句代码:new SystemServer().run();

  9. run方法中启动服务进程,AMSPMS

ps:SystemManager: 是SystemServer的叔叔,SystemServer把所有服务都交给了SystemManager管理;

  1. AMSPMS自身创建后,自身对象会添加到SystemManager中,addService("key",AMS/PMS)

  2. getService()时,取的是个binder

PMS(PackageManagerService):SystemServer的进程中,是SystemServer中的一个对象;

有一个缓存中心:mPackages;是一个Map,key为应用的包名,value为每个应用的Package

在手机启动的时候,做了三件事,且只做一次:

  1. 遍历所有app文件

  2. 解压每个apk文件

  3. dom解析AndroidMnifest.xml,并缓存;

作用:只解析每个Apk中的AndroidMnifest.xml中的信息,而不是去解析节点中每个xxxActivity.java文件;解析到的信息缓存到mPackages中,相当于“注册表”,方便之后AMS快速定位到相应的APP

  1. PackageManagerService.java中会去两个目录做扫描scanDirTracedLI:用户安装的所有APP目录sAppInstallDir:data/app/;和系统应用所有APP的目录systemAppDir:System/app/

  2. 6.0-8.0都是单线程扫描,9.0和10.0是用线程池进行扫描,扫描到的apk文件信息,new PackageParse(),赋值给包解析工具类PackageParse;

  3. 解压Apk文件,9.0和10.0解析时会去判断缓存中是否有,有则用缓存,6.0-8.0没有使用缓存;

  4. 使用工具类PackageParse解析AndroidMnifest.xmlxml解析完会返回Package对象,每个APK对应一个Package对象,得到这个Package对象后,缓存到PackageManagerServicemPackages这个ArrayMap里;key为应用的包名,value为应用的Package

  5. Package对象中有解析出的对应App中的四大组件标签、权限标签等等,放入各自的List中,如:activitesservicesrevicersproviders、权限list等等;activites这些list存的只是一个javabean,而不是存的具体的应用层的Activity

解析AndroidMnifest.xml流程:

  1. 打开AndroidMnifest.xml

  2. 获取版本号、版本名称

  3. 判断tagname=="applacation"

  4. 判断tagname=="activity","reciver","service","provide"等等

  5. 走到对应的解析parseActivity,parseActivityreciveractivity的结构一样,就用同样的javabean接收),parseService,parseProvide

  6. 解析完添加到Package的对应的list中;

2.3.Android Framework源码-IMS

Linux事件机制:

事件都是储存在文件中;

如触摸屏幕事件:存储在dev/input/event0的文件中,每次触摸都会以16进制数据储存;

INotify:监听文件状态,有变化则产生FD值

epoll机制:

epoll_create:注册监听事件类型

epoll_ctl:监听FD值,FD改变则唤醒epoll_wait()

epoll_wait:没事件则阻塞,有事件则分发;

INotifyepoll封装为一个对象EventHub

SystemServer进程启动时,创建了InputManagerService服务,这个IMSnative层创建了InputManager对象;

InputManager里有一个对象EventHub

同时InputManager里面又开启了两个线程:

InputReaderThread:不断去读取EventHub里的事件;有事件时把数据封装后添加到队列,立马从队列里读取交给InputDispatcher进行分发;

InputDispatcherThread:InputDispatcher里面保存了wms中所有的window信息(wms会将window信息实时更新到InputDispatcher中),InputDispatcher就可以将事件分发给对应合适的window

App进程中的ViewRootImplSystemServer中的IMS通过socketpair通信,因为事件产生的非常快且非常多使用binder通信不适合

ViewRootImpl中setView后new了一个监听FD文件的回调,new WindowInputEventReceiver();

在回调中,底层使用epoll_ctl()函数监听FD是否变化,有变化则会回调至java层,dispatchInputEvent();

这里就是Activity-》Viewgroup->View的事件分发前置;

底层事件信号传递总结:

  1. 事件信号都是用物理文件存储数据的,位置在dev/input 文件夹下;touch事件存储在dev/input/event0的文件中;

  2. Linux有提供相关的文件监控apiinotify()epoll机制

  3. android创建了一个封装了 inotify()epoll机制的对象EventHub,来监控dev/input文件夹下面的事件信号文件;

  4. android自己启动两个线程来处理dev/input文件夹下面的事件信号文件:InputReaderThreadInputDispatherThread;`

  5. InputReaderThread中开启循环,对EventHub对象进行getEvent();

  6. getEvent()中有epoll_wait;相当于wait-notif机制;唤醒的触发点时dev/input下的文件被改变;

  7. InputReaderThreaddev/input文件夹下面的事件信号文件数据进行 提取、封装,然后交给InputDispatherThread;

  8. InputDispatherThread最终选择到对应的ViewRootImpl(window)进行分发数据;

  9. 这里App进程和SystemServer两个进程通过Socketpair进行通信;两个进程一边一组socketpair;

  10. ViewRootImpl中对于Channel连接的文件进行监控(epoll_ctr),从而是上层接收到touch信号;

2.4.Android WMS及绘制流程

主角:ViewRootImplChoreographerSurfaceflinfer

WMS扮演了什么角色?

作为协调者,协调view布局,绘制;

  1. ActivityThread中创建Actiivty后,调用activity.attach()时,创建一个窗体对象PhoneWindow

  2. PhoneWindow创建了一个WMS的代理桥接类WindowManagerImpl对象,作为WMSapp中的代表;

  3. WindowManagerImpl对象中的(mGlobal)WindowManagerGlobal专门和WMS通信,在mGlobal里面获取了到了WMSBindergetWindowSession()->WMS::openSession();

setContentView()

  1. 调用PhoneWindow.setContentView(resouseID)

  2. PhoneWindow中:创建mDector:窗体上的整个View:里面有官方的主题布局+用户自己的布局;

  3. PhoneWindow中:创建mContentParent:官方主题布局中提供给用户装载布局的容器:id为content

  4. 调用mLayoutInflater.inflater(resouseID,mContentParent):

  5. 解析用户的布局xml

  6. 递归调用:解析根布局,通过反射创建根布局;解析子view,通过反射创建view

  7. 最后PhoneWindow中的mContentParent加载用户的根布局;

  8. view数据

ps:这里递归调用,若嵌套层级太多,会导致栈溢出;因为递归调用不会释放栈;

ViewRootImpl 单例,管理所有View的绘制策略;

注意onCreate.setContentView后view数据已解析并实例化了;

  1. 在状态机为Resume时:

  2. 调用WindowManagerImpl中的mGlobal.addView(view)

  3. addView中创建ViewRootImpl root=new ViewRootImpl()

  4. root.setView(view);

  5. setView总调用requestLayout()

  6. requestLayout()请求绘制,编舞者出场

帧速率: CPU/GPU出图速率;

刷新率: 屏幕刷新速率;

  1. 帧速率>刷新率时,出现丢帧(出图好多张了,但是只显示了开头和结尾两张,中间的丢了)

  2. 帧速率<刷新率,出现卡顿(屏幕刷新好多次了,但是还是显示的第一帧)

Vsync: 垂直同步绘制信号; 因可能硬件帧速率和刷新率不一致,用来同步刷新的问题;

Choreographer编舞者: 负责管理帧率节奏;

  1. 在内部维护了个HandlerLooper,保证绘制发生在UI主线程:Looper.myLooper==mLooper判断是否是主线程,是的话去调同步绘制信号,不是的话发送消息,走主线程去调同步绘制信号

  2. native层请求垂直同步信号,实际是找底层驱动要上次绘制的时间

  3. 请求到垂直同步信号后回调onVsync

  4. doFrame去逻辑管控, 判断当前时间离上次绘制的时间大于了1帧的时间(16.66毫秒) 就跳帧(卡顿优化有用到),若小于16.66毫秒就再次请求垂直同步信号,防止重叠

  5. 执行callback,让ViewRootImpl去真正绘制,调用ViewRootImpl.performTraversals()

真正的绘制: ViewRootImpl.performTraversals()

  1. 调用relayoutWindow()

  2. 创建用户java层的surface:只有用户提供的画面数据;

  3. 创建native层的surface:包含用户提供的画面数据(java层的surface)+系统的画面数据(状态栏,电池、wifi等等);

  4. 创建完surface后:依次调用:performMeasure(对应viewonMeasure)、performLayout(onLayout)performDraw(onDraw);

performDraw()中:

  1. view的数据传至native层的surface

  2. surface中的canvas记录数据

  3. 生成bitmap图像数据(此时数据是在surface中)

  4. surface放入队列中;生产者消费者模式;

  5. 通知surfaceflinfer进程去队列中取surface数据

  6. surfaceflinfer拿到不同的surface,进行融合,生成bitmap数据

  7. bitmap数据放入framebuffer中,进行展示

简单版总结: Activity.setContentView(R.layout.resId):

解析xml并实例化;

  1. 调用phoneWindow.setContentView(resId)

  2. setContentView中调用installDector():根据不同的主题,找到系统默认的xml,初始化出mDectormContentParent(反射实例化出对应的ViewGroup

  3. 初始化完成后,调用mLayoutInflater.inflate(resId,mContentParent):

  4. 解析resIdxml文件,将解析的view反射实例化;递归添加到各节点的viewgroup中;最后将自己定义的xml根布局view添加到mContentParent;

绘制发生时间:AMS回调ActivityThread中的handleResumeActivity时,也就是Resume时,而不是onCreate()

  1. 获取PhoneWindow

  2. 获取PhoneWindow中的mDector布局视图view

  3. mDector布局视图view传给ViewRootImpl

  4. ViewRootImpl中调用requestLayout()

  5. requestLayout()中依次调用:performMeasure()、performLayout()、performDraw()

💡三丶Framework事件机制

3.1.Framework事件机制—Android事件处理的三种方法

3.1.1.背景

Android的事件处理的三种方法:

1、基于监听的事件处理机制

setOnClickListenersetOnLongClickListenersetOnTouchListener

注意:如果onTouchEvent方法return true,则单击事件和长摁事件不再执行;若onLongClick方法返回true,则单击事件不再处理。

2、基于回调的事件处理机制

需要定义继承组件的类,重写回调方法Touch方法执行时,先被Activity捕获,DispatchTouchEvent方法处理。return false,交给上层的onTouchEvent方法处理;return super.dispatchTouchEvent(ev),则传递给最外层的View

ViewDispatch方法处理,return false,由上层的onTouchEvent方法处理。如果返回super.dispatchTouchEvent(ev),则本层的onInterceptTouchEvent拦截,如果拦截true,则拦截,false不拦截,传递给子ViewDispatchTouchEvent处理。

常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(轨迹球事件)监听和回调同时存在时,先调用监听。

3.1.2.Android基于监听

基于监听的时间处理机制模型

流程模型图

监听三要素:

Event source 事件源 Event 事件 Event Listener 事件监听器 下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:

  • 点击时间( 由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个 )

  • 触摸事件

归纳:

事件监听机制是一种委派式的事件处理机制,事件源(组件)事件处理委托给事件监听器 当事件源发生指定事件时,就通知指定事件监听器,执行相应的操作

常⽤监听接⼝

View.OnClickListener 单击事件监听器必须实现的接⼝ View.OnCreateContextMenuListener 创建上下⽂菜单事件 View.OnFocusChangeListener 焦点改变事件 View.OnKeyListener 按键事件监听器 View.OnLongClickListener 长按事件监听器 View.OnTouchListener 触摸屏事件监听器

  • 基于监听的事件处理机制

⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。

事件监听器处理流程:

  1. 为事件源(例如:button)设置⼀个监听器,⽤于监听⽤户的操作(点击操作等)

  2. ⽤户做出的操作触发事件源的监听器

  3. ⾃动⽣成对应的事件对象

  4. 将事件源对象作为参数传给事件监听器

  5. 事件监听器对事件对象进⾏判断,执⾏对应的事件处理器(处理⽅法)在此以OnClickListener单击事件为例使用intent来实现页面的跳转

内部类形式实现监听

<TextView
//id值
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="内部类"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="#fff"/>

public class MainActivity extends AppCompatActivity

//定义一个TextView对象
 private TextView textView2;

 @Override
 protected void onCreate(Bundle savedInstanceState) 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
//获得事件源
  textView = findViewById(R.id.test2);
//确定事件为点击事件,绑定监听器到事件源
  textView.setOnClickListener(new myListener());
 
 //内部类实现页面跳转
 private class myListener implements View.OnClickListener 
  @Override
  public void onClick(View v) 
 //采用显示Intent启动第二个页面
startActivity(new Intent(MainActivity.this,internalActivity.class));
  
 

匿名内部类实现

<TextView
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="匿名内部类"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="#fff"/>

public class MainActivity extends AppCompatActivity 

 private TextView textView1;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
//获得事件源
  textView1 = findViewById(R.id.test3);
  //匿名内部类实现跳转 (实现监听器,绑定监听器到事件源要同步进行)
  textView1.setOnClickListener(new View.OnClickListener() 
@Override
public void onClick(View v) 
 startActivity(new Intent(MainActivity.this,anonymousActivity.class));

  );
 

类本身实现监听器

<TextView
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="类本身打开浏览器"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="@color/colorWhite"/>

public class MainActivity extends AppCompatActivity implements View.OnClickListener

 private TextView textView2;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //获得事件源
  textView2 = findViewById(R.id.test4);
  //绑定监听器到事件源
  textView2.setOnClickListener(this);
 
//类本身实现 浏览器跳转
 @Override
 public void onClick(View v) 
  switch (v.getId())
case R.id.test4:
//采用隐式intent
 Intent intent = new Intent();
 intent.setAction(Intent.ACTION_VIEW);
 intent.setData(Uri.parse("http://www.baidu.com"));
 startActivity(intent);
 break;
  
 

本节给大

以上是关于硬不硬核,你说了算,怒肝Android Framework学习路线一条龙!(万字长文+视频资源)的主要内容,如果未能解决你的问题,请参考以下文章

知识

如何在不硬刷新的情况下升级新的 React 应用程序版本?

怒肝2W长文 !带你进入数据仓库Hive的世界理论+实践

怒肝Framework通信篇合集;Handler+Binder+LiveData事件机制

端午在家怒肝了一份6月HadoopHDFS面试题,让你面试无忧(干货满满,建议收藏!)

超硬核!16000 字 Redis 面试知识点总结,这还不赶紧收藏?