感悟
最近自己又去阅读了Android源码,感觉以前有些困惑清晰了不少。
android源码庞大而复杂,一个源文件文件少则都是几千行代码。抓住核心很重要,那么多API的调用链根本记不完,不能一窥全貌,就只能迷失在森林之中,其实重要的是去梳理流程,搞清楚这个模块的设计初衷是为了什么,如果让你自己来你会怎么设计,如何写出高质量的代码。
思维上的提高才是本质上的进步!!!
摘要
APK安装的方式其实有很多种,网上也有很多好的文章,不过学会才是最重要的,做出我们自己的总结。
从2021 年 8 月起 新应用需要使用 Android App Bundle 才能在 Google Play 中发布(Android ABB),在加上国内华为也支持了该ABB形式文件上架应用,后面也兴会成为主流。
关于相关的 Android App Bundle的知识可以点击这里🏷)
APK安装的方式
本章节只讲大致流程,代码流程我们后面再慢慢梳理
- 安装方式
- 1.系统应用和预制应用的安装
- 2.通过商店
- 3.Adb
- 4.通过SD卡
🏷 科普
Android科普
既然是APK安装,有输入点就有输出点,所以就会有对应的目录,每个不同的目录存放这对应的文件。
目录 | 含义 |
---|---|
/system/app | 系统自带的应用程序,获得 root 权限才能删除 |
/data/app | 第三方应用apk文件.安装时把apk文件复制到此目录 |
/data/anr | 存放anr信息(/data/anr/traces.txt用于存放app ANR信息) |
/data/data | 应用程序数据 |
/data/data/${package_name} | 特定应用程序数据目录 |
/data/data/${package_name}/cache | 临时文件,系统会自动清理 |
/data/data/${package_name}/databases | 数据库 |
/data/data/${package_name}/files | 一般文件 |
/data/data/${package_name}/shared_pres | SharedPreference |
/data/data/${package_name}/lib | so文件 |
/data/dalvik-cache | 存放odex文件.将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,ART模式的可执行文件格式为.aot,启动ART时,系统会执行dex文件转换至aot文件) |
/data/system/packages.list | 类似于Window的注册表,该文件是解析apk时由writeLP()创建的。记录了系统的permissons,以及解析apk的AndroidManifest获取的应用name,codePath,flag,ts,version,userid等信息。解析完apk后将更新信息写入这个文件并保存到flash,下次开机的时候直接从里面读取相关信息并添加到内存相关列表中.当有apk升级,安装或删除时会更新这个文件。 |
/data/system/packages.xml | 指定应用的默认存储位置/data/data/com.xx.xx/package.xml中包含了该应用申请的权限,签名和代码所在的位置等信息系,并且两者都有同一个userld. |
/data/user/0 | 软链接,指向/data/data |
/data/user_de/0/${package_name} | 设备存储保护区,在快速启动模式可以访问这个文件夹 |
/proc/cpuinfo | cpu信息 |
/proc/smaps | 内存占用信息 |
/sdcard | 软链接,最终指向/storage/emulated/0【跟Android版本和ROM版本有关】 |
/storage/emulated/0 | 外部存储的根目录 |
/storage/emulated/0/Android/data/${package_name} | 应用的额外数据 |
/system/app | 系统应用apk文件 |
/system/lib | 系统应用so库 |
———————————————— |
在/data/data/包名目录下,每个app都有自己的目录,目录名就是应用程序在AndroidManifest.xml文件中定义的包。每个应用程序的代码,对自己的目录是有绝对的控制权限的。在每个目录下,一般有如下几个子目录(结合上面的表格):
databases : 存放数据库
cache : 存放缓存数据
files : 存放应用程序自己控制的文件
lib : 存放使用的包
Linux科普
为什么这里需要了解Linux的知识点,
Linux进程有两个ID,一个就是用户ID,为每个用户的唯一标识符;
另一个是组ID,为用户组的唯一标识符
当你在手机点击一个APK之后,APK中的AndroidManifest.xml会被解析,在手机root之后,或者用模拟器打开根目录 /data/system/ ,我们获得超级权限之后,可以在查看目前手机在系统已经注册的app,解析的内容会被存储到 /data/system/packages.xml 和 /data/system/packages.list 中。我们打开packages.list和packages.xml,我们已Apk的包名为索引,可以看到对应的内容。
packages.list
1 | com.game.dalan2.s3 10130 1 /data/user/0/com.game.dalan2.s3 default 3003 |
packages.xml
1 | <package name="com.game.dalan2.s3" codePath="/data/app/com.game.dalan2.s3-1" nativeLibraryPath="/data/app/com.game.dalan2.s3-1/lib" primaryCpuAbi="x86" publicFlags="940097350" privateFlags="0" ft="17b8267bd90" it="17b586694ad" ut="17b8267cb67" version="1" userId="10130"> |
packages.list中指名了该应用默认存储的位置
packages.xml中包含了该应用申请的权限、签名和代码所在位置等信息,并且两者都有一个userId为10060。
之所以每个应用都有一个userId,是因为Android在系统设计上把每个应用当作Linux系统上的一个用户对待,这样就可以利用已有的Linux上用户管理机制来设计Android应用,比如应用目录,应用权限,应用进程管理等。
这个解析的过程也可以理解为在APK在系统的注册。
相关知识点可以查看这里 Stack Overflow
Apk安装的流程
这里大致只讲流程,详细的代码我们后面再一一罗列出来:
简单来说分为四步:
1)将APK的信息通过IO流的形式写入到PackageInstaller.Session中。
2)调用PackageInstaller.Session的commit方法,将APK的信息交由PKMS处理。
3)拷贝APK
4)最后进行安装
Android十万个为什么
什么是覆盖安装 ?
无论在国内还是出海国外上架海外游戏App,我们在每次上架新版本的时候,都是需要versionCode+1,用户再通过商店渠道再把 versioncode+1的apk覆盖掉原先的apk,这个过程就是覆盖安装,。
不过还是有前置条件:
1.包名跟旧的一致
2.签名跟原来的一致
3.组件不冲突
更详细的内容看这张表:
正常安装 | 覆盖安装 | DES | MORE |
---|---|---|---|
删除旧版本APK之后重新进行安装 | (不删除之前的旧版本包,直接安装) | ||
SQLite的数据也会被删除 | SQLite的数据不会丢失 | ||
shared_prefs中的数据会被删除 | shared_prefs中的数据不会丢失 | ||
4.4之前的版本不会比较apk的Version_code |
部分手机在APK安装成功后提示清除安装包,要清除安装包的是在哪里?
首先要清除对应的安装包,就得知道安装包的目录是在哪,计算机的世界有入口就有出口。
我们这里在做一些知识的扩展:
现在部分手机的App是具有分享Apk的功能,也就是传输本机上的apk,比如说QQ,还有一些第三方的软件具备之类的功能。手机安装app时,会在根目录和外存储器合生成文件夹。反过来说,你删除app的时候,这些对应生成文件夹也会被系统删除。根目录会放置app的安装包,而且统一命名为base.apk,还有一些重要的app用户数据。(你想查看,前提是开启root)
外存储器就是你能看到的文件夹,里面放置可操作的文件,例如保存的图片,视频。所以即使你安装app后删除安装包,也是删除了外存储器的安装包,QQ传输app安装包就是传输根目录的安装包。
Android目录扫盲
关于存储的目录,我们后面额外再开一张新的文章,android对于目录和权限其实做了不少的修改。
接下来这段是很多Code的误区
1 | Environment.getExternalStorageDirectory() |
实际上是获取的手机自带的sd卡,这个自带的sd卡你在手机的文件管理应用里看,显示的中文名是“内部存储”,这个“内部存储”实际上就是手机自带的sd卡,也就是ExternalStorage