6-系统源码

SurfaceView

SurfaceView 的绘制原理

Android应用程序窗口是通过SurfaceFlinger服务来绘制自己的UI。一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个 Surface,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的 Surface,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。

unknown_filename.2

在 WMS 中会为这个 Window 分配 Surface,并确定显示层级,可见负责显示界面的是画布 Surface,而不是窗口本身,WMS 将他管理的 Surface 交由 SurfaceFlinger 处理,SurfaceFlinger 将这些 Surface 合并后放入到 buffer 中,屏幕会定时从 buffer 中获取显示数据,显示到屏幕上。

刷新率

取的的时间
[[垂直同步vsync]]

SurfaceFlinger

  • 在 App 进程中创建 PhoneWindow 后会创建 ViewRoot。ViewRoot 的创建会创建一个 Surface。
  • 图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到显示部分。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface
  • HWComposer 是基于硬件来产生 VSync 信号的,来通知 SurfaceFlinger 重绘控制显示的帧率。
  • 图像流生产方可以是生成图形缓冲区以供消耗的任何内容。例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。

Dialog不能用Application的Context

我说不能,解释的是以前在崩溃日志中看到Activity不存在,但Dialog 还存在,然后造成崩溃,后来使用DialogFragment ,这样可以管理弹窗的生命周期,不再存在Dialog 的崩溃。但不知道为什么 Dialog不能使用Application的Context。查了资料总结一下。

用Application的上下文来创建Dialog,在调用它的show方法时程序会Crash,LogCat的异常信息如下:

Caused by: android.view.WindowManager$BadTokenException: Unable to add window – token null is not for an application  

Token:这里提到的Token主是指窗口令牌(Window Token),是一种特殊的Binder令牌,Wms用它唯一标识系统中的一个窗口。 必须在一个窗口上


PMS

总结

  • 应用安装的时候,通过 PackageManagerService 解析 apk 的 AndroidManifest.xml 文件,提取出这个 apk 的信息写入到 system/packages.xml 文件中,这些信息包括:权限、应用包名、icon、apk 的安装位置、版本、userID 等等。packages.xml 文件位于系统目录下/data/system/packages.xml。
  • 同时桌面 Launcher 会为安装过的应用生成不同的应用入口,对应桌面上的应用图标

PackageManagerService

APK安装过程 完全解析
PackageManagerService 通过名字 你可能会想起 和四大组件紧密相关的 ActivityManagerService(AMS),它俩都是运行在SystemServer进程,App端和AMS、PMS进行交互都是通过Binder跨进程完成。AMS负责Activity等四大组件的管理,而PMS则负责 包package 的管理,APK的全称是Android Package,即PMS的作用就是管理APK。

1
2
3
4
5
6
7
8
9
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
final IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}

PMS的初始化

因为 所有App运行都需要 这些系统服务,所以是 系统开机的时候 完成 PMS、AMS这些的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//SystemServer.java
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}

private void run() {
...
// Here we go!
Slog.i(TAG, "Entered the Android system server!");
...
// Prepare the main looper thread (this thread).
Looper.prepareMainLooper();
...
// Start services.
startBootstrapServices(t); //系统开启中 具有复杂相互依赖性的 关键服务
startCoreServices(t); //没有复杂依赖的核心服务
startOtherServices(t); //其他服务
...
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

在开机系统启动时,zygote 进程会fork一个 SystemServer 进程然后执行上面main方法,这里进行大量的初始化,其中就包括 启动各种服务。 这里我们看 startBootstrapServices():

1
2
3
4
5
6
7
8
9
10
11
12
13
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// Installer
Installer installer = mSystemServiceManager.startService(Installer.class);
...
//AMS
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
mActivityManagerService.setInstaller(installer);
...
//PMS
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
...
}

发现这里开启了 AMS、PMS,并且都使用了Installer。Installer 看名字像是安装器,后面再做介绍。 PMS的main方法内用构造方法创建了PMS实例,而它的构造方法内容特别多,我们关注其中一句调用scanDirTracedLI():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** data/app,已安装app的目录 */
private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app");

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest){
...
//读取packages.xml中 上次的包信息,解析后将数据存放到mSettings中
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false));
...
//扫描 data/app
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,packageParser, executorService);
...
//把新的 mSettings 内容更新到 packages.xml
mSettings.writeLPr();
...
}

packages.xml:记录系统中 所有安装的应用信息,包括基本信息、签名和权限。
mSettings:用来保存和PMS相关的一些设置,它保存的内容在解析应用时会用到。

先读取packages.xml文件,解析后将数据存放到mSettings中,代表上次启动时的应用包信息。然后扫描所有APK目录并解析APK,最后更新packages.xml文件。 而 packages.xml文件 是在 Settings 构造方法中创建。

data/app 是用户已安装App所在的目录,另外还有system/app存放 系统App。PMS构造方法中会对 这两个目录在内的多个目录进行扫描,我们这里可以猜想到这是开机时对所有已安装App的初始化。

总结
系统启动后创建并启动了PMS,并且PMS完成了对所有存在APK的目录进行了扫描,解析所有APK的AndroidManifest.xml,然后进一步扫描APK 最后提交包扫描结果到 PMS 的属性中。

APK的安装过程

  • APK用写入Session且包信息和APK安装操作 都提交到了PMS;
  • PMS中先把APK拷贝到 /data/app,然后使用PackageParser2解析APK 获取 四大组件、搜集签名、PackageSetting等信息,并进行校验确保安装成功;
  • 接着提交信息包更新系统状态及PMS的内存数据;
  • 然后使用 Installer 准备用户目录/data/user、进行 dexOpt;
  • 最后发送安装结果通知UI层。

crash流程

unknown_filename.17


IntentSerVice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 @Override
public void onCreate() {

super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
// -------
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}

Context

  • Context指:一个应用程序环境的信息,即上下文。Context 是一个接口,ContextImpl 是子类,ContextWapper 是具体实现。

unknown_filename.9

Activity 因为有了 一层 Theme ,所以中间有个 ContextThemeWrapper ,相当于它是Service和Application 的侄子
Context Wrapper 是一个包装类 ,没 任何具体的实现,真正的逻辑都在 Contextlmpl里面

  • 可以看出每次创建 Application 和 Acitvity 以及 Service 时就会有一个 ContextImpl 实例,ContentProvider 和B roadcastReceiver 的 Context 是其他地方传入的。
  • 所以 Context 数量 = Application 数量 + Activity 数量 + Service 数量,单进程情况下 Application 数量就是 1。
  • Application 的 Context 生命周期与应用程序完全相同。Activity 或者 Service 的 Context 与他们各自类生命周期相同。
  • Application 通常作为工具类来使用的,Application 中在 onCreate ()方法里去初始化各种全局的变量数据是一种比较推荐的做法,但是如果你想把初始化的时间点提前到极致,也可以去重写 attachBaseContext ()方法。不能写在构造函数里。

注意

虽然 Context 神通广大,但是并不是随便拿到一个 Context 实例就可以为所欲为,还是有一些限制的。在绝大多数场景下,Activity、Service 和 Application 这三种类型的 Context 都是通用的,不过也有几种场景比较特殊,比如启动 Activity、弹出 Dialog。Android 是不允许 Activity 或 Dialog 凭空出现的,一个 Activity 的启动必须建立在另外一个 Activity 的基础之上,也就以此形成任务栈。而 Dialog 则必须在一个 Activity 的上面弹出(除非是 System Alert 类型的 Dialog),因此在这种场景下,我们只能使用 Activity 类的 Context。

  1. 使用 ApplicationContext 去启动一个 LaunchMode 为 standard 的 Activity。

会报错,因为非 Activity 类型的 Context 没有所谓的任务栈。

  1. getApplication、getApplicationContext 区别

在绝大多数场景下,getApplication 和 getApplicationContext 这两个方法完全一致,返回值也相同。区别在于 getApplication 只存在于 Activity 和 Service 对象,对于 BroadcastReceiver 和 ContentProvider 只能使用 getApplicationContext。

功能

  • 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  • 确定生命周期的、获取各种资源
  • 文件、SharedPreference、数据库相关
  • 获取系统 / 应用资源,包括 AssetManager、PackageManager、Resources、SystemService 以及 color、string、drawable 等
    unknown_filename.6

原文链接

Service 工作原理

众所周知, Service 有两套流程,一套是启动流程,另一套是绑定流程
unknown_filename.10

假设要启动的 Service 是在一个新的进程中,启动过程可分为5个阶段
unknown_filename.26

  1. AMS 检查启动 Service 的进程是否存在,如果不存在,先把 Service 信息存下来,然后新建一个新的进程
    AMS 检查 Service 是否在 AndroidManifest 声明了 ,若没声明 则会直接报错 AMS检查启动 Service 的进程是否存在,如果不存在,先把 Seic 信息存下来,然后创建一个新的进程,在AMs 中,每个 rice ,都使用 SeviceRecord 对象来保存

unknown_filename.27

unknown_filename.11

你会发现,这段代码和前面介绍的 handleLaunchActivity 差不多,都是 PMS 中取出包的信息 packagelnfo ,这是一个LoadedApk 对象 ,然后获取它的 classload 反射出来一个类的对象,在这里反射的是 Service.
四大组件的逻辑都是如此,所以我们要做插件化,可以在这里做文章,换成插件的classLoader ,加载插件中的四大组件
unknown_filename.12

广播原理
BroadcastManager静态注册是通过 PMS (即:PackageManagerService完成整个注册过程的,除此之外四大组件也是通过PMS完成注册)。
acitivityManagerService通知packmanagerService去查询静态广播,查到后将广播放到BroadcastQueue里,然后用handler机制去动态注册广播
unknown_filename.5

内容提供者原理
原理:packmanagerservice里注册,在使用ContentResolver来进行查询操作时,query 方法层层调用到 ActivityThread 的 acquireExistingProvider 方法,根据URI字符串当中的授权host(即 authority )和当前所在用户的 userId 来获取对应的Provider实例。


6-系统源码
http://peiniwan.github.io/2024/04/cdc258b3cfd7.html
作者
六月的雨
发布于
2024年4月6日
许可协议