美文网首页Android面试二Android
[读书笔记] - 《Android内核剖析》

[读书笔记] - 《Android内核剖析》

作者: New_X | 来源:发表于2019-02-11 10:34 被阅读74次

    第2章 Java基础

    1.为什么不用import而使用ClassLoader?

    • import引用的类必须存在于本地,编译时必须在现场
    • 使用ClassLoader可以动态装载自定义的Jar包,扩充Framework,对原生Framework最少化修改

    2.ClassLoader装载的顺序?
    双亲委派模型:装载Class文件时,子ClassLoader会先请求父ClassLoader加载该Class文件,只有当父ClassLoader找不到该Class文件时,子ClassLoader才会继续装载该类,这是一种安全机制

    3.为什么要用双亲委派模型?

    • 保证了两个对象属于同一个类型(1.同名的类完成实例化;2.类加载器是同一个)
    • 为了系统类的安全,类似"java.lang.Object"这种核心类,jvm需要保证他们生成的对象都被认定为同一种类型(不然instanceof怎么用?)
    不想打破双亲委派模型,只需重写findClass方法,想打破,需重写整个loadClass方法
    

    4.DexClassLoader是什么?
    class转dex不是简单的压缩,而是完全对Class文件内部的各种函数表、变量表进行优化,因此加载这种特殊的class需要用DexClassLoader

    5.怎么实现ClassLoader插件化架构
    先通过ClassLoader装载类,构造出Method对象,并构造出Method对象所使用的参数对象,才能调用 => 简单优化:定义一个接口,该接口值定义函数的输入和输出,不定义具体实现,在宿主中可以通过强转调用 (协议化,软件系统设计的架构)

    6.android的异步消息处理线程实现?(Handler)

    Handler机制.png

    7.什么是线程局部存储(Thread Local Storage)?
    存储一个变量的副本,目的是为了同一线程引用一个变量,值是相同的;不同线程引用一个变量,这个值应该不同。

    内存优化:通过数据池保存Message对象,从而避免不断的创建和删除对象。
    因此处理完该消息后,需要把Message对象标志为空闲状态,表示可以被重用
    

    8.Looper的作用?

    1. 调用静态函数prepare()创建一个消息队列
    2. 提供静态函数loop(),使调用该函数的线程进行无限循环,并从消息队列中读取消息

    9.MessageQueue是怎么取出消息(next)和添加消息(enquenceMessage)的?

    • next():
    1. nativePollOnece(mPtr,int time):从消息队列中取出一个消息
      MessageQueue并没有保存消息队列,真正的消息队列数据保存在JNI中的C代码中(C环境创建的NativeMessageQueue),该消息队列中没有消息,线程会被挂起(wait),如果有消息,c代码会把消息赋值给Java环境中的mMessages变量
    2. synchronized同步锁判断执行的时间是否到了(读写消息加锁),到了返回改消息,置空mMessages变量,没有到则尝试读取下一个消息
    3. 如果mMessages为空,表示C环境的消息队列没有消息,会回调一个“空闲回调函数”,可以在MessageQueue添加“空闲回调函数”的执行,来做一些空闲代码处理
    • enquenceMessage():
    1. 将参数msg赋值给mMessages
    2. 调用nativeWake,把消息添加到C环境的消息队列中,唤起消息线程

    第5章 Binder

    1.什么是Binder?
    Binder是一种架构,提供了服务端接口、Binder驱动、客户端接口。

    20180827111723.png
    客户端似乎是直接调用远程服务对应的Binder,而事实上则是通过Binder驱动进行了中转,即存在两个Binder对象,一个是服务端的Binder对象,另一个是Binder驱动中的Binder对象,不同的是Binder驱动中的对象不会额外产生一个线程。

    2.ServiceManager怎么管理服务?
    ServiceManager本身也是一个Service,Framework提供了一个系统函数,可以获取该Service的Binder引用。类似公司总机,总机号码公开,通过总机再获得分机号码,这种设计好处是仅暴露一个全局Binder引用,其他服务隐藏起来,有助于系统的拓展,以及调用系统服务的的安全检查。其他系统服务在启动时,首先把自己的Binder对象传递给ServiceManager,即所谓的注册(addService)

    3.Manager和Service类之间的关系?

    20180827152548.png
    ServiceManager所管理的所有Service都是以相应的Manager返回给客户端

    第6章 Framework概述

    1.framework的组成?

    framework的组成.png

    2.APK程序是怎么运行的?

    1. ActivityThread从main()函数执行,调用preparMainLooper()为UI线程创建一个消息队列(MessageQueue)
    2. 创建一个ActivityThread对象,在ActivityThread的初始化代码中创建一个H(Handler)对象和一个ApplicationThread(Binder)对象。
    Binder负责接收AMS的IPC调用,接收到调用后,则通过Handler把消息发送到消息队列,
    UI主线程会异步去除这些消息,接着UI主线程调用 Looper.loop()方法进入消息循环体
    
    1. UI主线程调用Looper.loop()方法进入消息循环体,不断从消息队列中读取并处理
    2. ActivityThread接收到AMS发送start某个Activity后,就会创建指定的Activity对象,Activity又会创建PhoneWindow类 => DecorView类 => 创建相应的View或ViewGroup。创建完成后,Activity需要把创建好的界面显示到屏幕上,于是调用WindowManager类,于是创建一个ViewRoot对象,该对象实际上创建了ViewRoot类和W类,创建ViewRoot对象后,WindowManager再调用WMS提供的远程接口完成添加一个窗口并显示到屏幕上。
    3. 接下来,用户开始在界面上操作,KeyQ线程不断把消息队列存储到QueueEvent队列中,InputDispatcherThread线程逐个取出消息,然后调用WMS中的相应函数处理该消息,当发现WMS发现该消息属于某个窗口时,就会调用相应窗口的W接口。
    W类是一个Binder,负责接收WMS的IPC调用,并把调用消息传递给ViewRoot,
    ViewRoot再把消息传递给UI主线程ActivityThread,ActivityThread解析该消息并做相应的处理。
    

    3.Android APK程序中有哪些线程?
    包含activity的客户端只是包含三个线程
    Activity启动后会创建一个ViewRoot.W对象,同时ActivityThread会创建一个ApplicationThread对象(都继承于Binder),UI线程
    4.自定义Thread和UI线程有什么区别?
    UI线程是从ActivityThread运行的,在该类的main()方法中,已经使用Looper.pepareMainLooper()为该线程添加了Looper对象,即已经为该线程创建了消息队列,所以可以在Activity中定义Handler对象。而自定义Thread是一个裸线程,不能直接从Thread中定义Handler对象

    5.Activity如何传递消息(数据)?
    Activity就是类,其实本质是ActivityThread之间如何传递数据。
    两个类之间传递数据:

    1. 先实例化某个类,获得该类的引用,当其他类需要该对象的内部数据时,之间通过该引用访问该类的内部数据(但是Activity的实例化是由Framework完成的,只能通过startActivity方法告诉Framework去运行哪个Activity)
    2. 在A、B两个类之间,先实例化一个第三方类C,然后两个类都可以把需要传递的数据存入C中,或者从C中取出(接口回调)
    3. Intent
    4. 通过共享数据:SP、文件、数据库,广播、EventBus/RxBus是解耦。

    第7章 理解Context

    1.Context是什么?
    上下文,运行环境,场景

    2.一个应用程序中应该包含多少个Context对象?
    一个Activity/Service就是一个场景Context,Application也是一个,所以是:1+Activity+Service

    3.Context相关类的继承关系?

    Context相关类的继承关系.png

    4.ContextImpl和PackageInfo对象的对应关系?

    ContextImpl和PackageInfo对象的对应关系.png

    Context真正实现的类是ContextImpl,但是其内部变量最终指向的都是PackageInfo。这种设计说明ContextImpl是种轻量级类,而PackageInfo是个重量级类。

    第8章 创建窗口的过程

    1.窗口的事件分发机制?
    从WMS的角度看,一个窗口并不是Window类,而是一个View类,WMS收到消息要把消息派发到窗口,View类本身并不能接收WMS传递过来的消息,真正接收用户消息的是IWindow类,而实现IWindow类的是ViewRoot.W类,每一个W内部都包含了一个View变量。

    2.Framework的窗口类型?
    应用窗口、子窗口、系统窗口,int值越大,代表层的位置越靠上面,即所谓的z-order

    3.Dialog是什么类型的窗口
    Dialog对应的窗口默认类型是应用类窗口,但是Dialog的子类会改变默认窗口类型,使之成为一个"子窗口"

    4.Window怎么创建应用窗口?

    1. 创建Activity
      应用窗口必须对应Activity,AMS通知客户端ActivityThread启动Activity 。反射创建Activity对象
    2. 创建Window
      1. activity.attach()方法设置内部变量
      2. activity.attach()方法创建Window对象
      为什么用户消息能够传递到Activity中?
      PolicyManager.makeNewWindow()创建(通过 
      com.android.internal.policy.impl.Policy配置)创建PhoneWondow,
      赋值给Activity.mWindow设置setCallback() , Window的calback为当前 
      Activity
      
      1. Window.mWindowManager赋值
      每一个Window内部都有一个WindowManager,不会造成浪费, 
      WindowManager只是一个接口,不是一个重量级的类,具体实现类是 
      Window.LocalWindowManager(是一个壳,其具体实现在其内部的 
      WindowManagerImpl中) 与 WindowManagerImpl
      
    3. 创建ViewRoot
      1. 添加View,从performLaunchActivity内部调用callActivityOnCreate开始,最终调用onCreate中的setContentView
      2. setContentView源码 ,分析Theme相关设置
        首先installDecor() 创建标题栏等,把用户界面通过inflate添加到窗口
      3. Activity准备好后通知AMS,AMS最终调用Activity.makeVisible()
      4. 在makeVisible()方法中,构造函数窗口类型是TYPE_APPLICATION,ViewGroup.addView 中“关卡” 判断WindowManager层级
      5. LocalWindowManager.addView(WindowManagerImpl.addView一个应用仅有一个此对象),其中包含3个数组。通过ViewRoot.setView完成关键添加工作
      1. 检查是否添加过了,不允许重复添加
      2. 如果添加的窗口是子窗口类型,找到父窗口
      3. 创建一个ViewRoot(每个窗口对应一个 
          ViewRoot对象)
      
    4. 添加窗口
      1. 数组mViews、mRoots、mParams 各保存View、ViewRoot、WindowManager.LayoutParams
      2. 执行ViewRoot.setView 完成最后的窗口添加工作
      1. mAttachInfo赋值
      2. requestLayout 发出界面重新布局请求
      3. sWindowSession.add 通知WMS添加窗口
      

    5.Dialog能在普通的Thread类中创建?
    Dialog的构造函数要创建一个ListenersHandler,用于在内部发送异步消息,因为Handler对象只能在拥有Looper对象的线程中创建。

    Dialog构造函数创建了内部的Window对象,没有告知WmS添加一个可以显示
    的窗口,因此show()方法向WmS中添加一个真正可以显示的窗口。Dialog.show()不仅仅是显示,
    WindowManager添加mDecor,发送显示的消息
    

    6.Dialog的onShow为什么要设计成异步的?
    Dialog显示完之后会执行((OnShowListener) msg.obj).onShow(mDialog.get()),其回调setOnShowListener需要在显示后的执行结果,回调代码的执行时间比较长

    7.添加窗口的标准过程?

    1. 准备WindowManager.LayoutParams对象
    2. 准备要添加的窗口内容(View/ViewGroup)
    3. 调用addView()完成真正的添加

    第9章 Framework的启动过程

    1.zygote是怎么启动的?

    1. 在init.rc中配置zygote启动参数
    zygote进程是所有apk应用进程的父进程
    
    1. 启动Socket服务端口,用于接收启动新的Dalvik进程的命令
    - 启动入口:ZygoteInit.java的main(),该类也是虚拟机执行的第一个类
    
    1. LocalServerSocket端口准备好后,main()函数中调用runSelectLoopMode()进入非阻塞读操作
    • 加载preload-classes(把preload-classes文件打包到framework.jar)
    • 加载preload-resources(加载drawable和color资源)
    • 使用fork启动新的线程(两个进程共享大量的程序时,可以使用复制进程)

    3.app_process是什么?
    app_process的本质是使用dalvikvm启动ZygoteInit.java,并在启动后加载Framework中的大部分类和资源

    4.SystemServer是什么?
    SystemServer是zygote孵化出的第一个进程,android程序的"神经中枢",WmS、AmS、PmS等都是以一个线程的方式存在于SystemServer进程中,在run()中启动各个线程

    4.AMS是怎么启动的?

    1. 调用main()函数,返回一个Context对象,而不是AMS服务本身。
    2. 调用AMS.setSystemProcesss()
    3. 调用AMS.installProviders()
    4. 调用systemReady(),当AMS执行完systemReady()后,会相继启动相关联服务的systemReady()函数,完成整体初始化

    第10章 AmS内部原理

    1.AmS提供的主要功能?

    1. 统一调度各应用程序
    2. 内存管理
    3. 进程管理

    1.AmS中有哪些重要的系统常量?

    系统常量 说明
    MAX_ACTIVITYS = 20 AmS会记录非执行状态的Activity,如果后台Activity数量超过该常量,则会强制杀死一些优先级较低的Activity
    MAX_RECENT_TASKS = 20 AmS会记录最近启动的Activity,超过该常量后,AmS会舍弃最早记录的Activity
    PAUSE_TIMEOUT = 500 AmS通知应用进程暂停指定的Activity,但是AmS的等待时间只有500毫秒,如果进程在该常量时间内还没有暂停,AmS则会强制暂时关闭该Activity,所以onPause不能做过多的事情
    LAUNCH_TIMEOUT = 10*1000 AmS通知应用进程启动(Launch)某个Activity时,超过10秒,AmS就会放弃
    PROC_START_TIMEOUT AmS启动某个客户端进程后,客户端必须在10秒内报告AmS自己已经启动,否则AmS会认为指定的客户进程不存在

    2.AmS的数据类有哪些?

    系统常量 存储类
    进程(Process) ProcessRecord:记录进程文件信息、进程的内存状态信息和进程中包含的Activity、Service等
    活动(Activity) ActivityRecord:AmS中使用ActivityRecord数据类来保存每个Activity的信息。History中包含一个int task变量,保存该Activity所属哪个任务。可以使用Intent.FLAG_NEW_TASK标识告诉AmS为启动的Activity重新创建一个Task
    任务(Task) TaskRecord

    3.Task的作用?
    Task的作用是确保Activity启动和退出的顺序
    Task分类:
    1. 在Task之间的切换
    2. 在同一个Task中调整Activity的顺序
    3. 为了在Task切换时Task内部自动重排所属的Activity

    launchMode:Activity自己声明的运行方式,AndroidManifest.xml指定
    launchFlags:调用者希望以何种方式运行指定的Activity,startActivity()的参数Intent中指定
    

    4.startActivity()流程?

    startActivity流程.png
    在Android中,Activity调度的基本思路是这样的:各应用进程要启动新的Activity或者停止当前的Activity,都要首先报告给AmS,而不能“擅自处理”。AmS在内部为所有应用进程都做了记录,当AmS接到启动或停止的报告时,首先更新内部记录,然后再通知相应客户进程运行或者停止指定的Activity。由于AmS内部有所有Activity的记录,也就理所当然地能够调度这些Activity,并根据Activity和系统内存的状态自动杀死后台的Activity

    attachApplication():进程B给自己安排(attach)一个具体要执行的Activity
    resumeTopActivity():启动真正的Activity

    5.Activity是一个应用程序吗?
    Activity并不对应一个应用程序,ActivityThread才对应一个应用程序,所以Android允许同时运行多个应用程序,实际是是允许多个ActivityThread同时运行

    相关文章

      网友评论

        本文标题:[读书笔记] - 《Android内核剖析》

        本文链接:https://www.haomeiwen.com/subject/qeofeqtx.html