常见的apk安装方式有三种:
-
系统自带的应用和厂商预装的应用. 没错, 系统自带的应用其实也是apk, 其安装是在系统首次启动时完成的. 这也就是为什么root后可以卸载系统自带应用.没有安装界面.
-
通过存储介质安装. 最常见的就是通过sd卡放置apk或者网上下载apk方式安装.通过 packageinstaller.apk来处理安装及卸载的过程的界面
-
adb命令安装. 这应该是开发者最熟悉的安装方式了, 包括
adb install
和
adb pm install
.没有安装界面.
三种安装方式, 在安装apk时最终都是同一套流程, 即处理apk文件的流程. 安装过程可以归结为以下几个步骤:
-
将apk文件拷贝到指定目录下. 系统应用是在
/system/app
, 第三方应用在
/data/app
下.
-
解压apk, 拷贝文件. 创建UID, 创建
/data/data/${package_name}
目录, 设置权限. 这个就是应用的数据目录.
-
从apk中提取dex, 放到
/data/dalvik-cache
目录.
-
解析
AndroidManifest.xml
文件, 提取信息添加到
PMS
中,
更新PMS中相应的数据结构. 具体是, 将提取到的包信息更新到
/data/system/packages.list
和
/data/system/packages.xml
.
-
发送广播
Intent.ACTION_PACKAGE_ADDED
或者
Intent.ACTION_PACKAGE_REPLACED
. 从名字可以判断分别对应全新安装和覆盖安装.
Android中每个app都要一个
userId(UID)
的原因:Android在系统设计上把每个应用当做Linux系统上的一个用户对待,这样就可以利用已有的Linux用户管理机制来设计Android应用,比如应用目录/应用权限/应用进程管理等.
-
系统自带的以及厂商预装的app, 在手机首次启动时, 会通过扫描
/system/app
/system/framework
/vendor/app
等目录下面的APK文件, 完成安装. 原生系统没有
vendor
(供应商)目录.
-
至于通过
adb push
的方式, 如果root过, 完全可以推送到系统目录, 以系统应用的方式进行安装. 系统应用的好处是系统在启动的时候就会将apk进行解压复制, 坏处是没办法热更新等. 推送到一般目录, 则可以使用系统命令
/bin/pm
安装apk文件.
pm
就是一个可执行文件版的
PackageManager
. 最终调用
PackageManager.installPackage() -> PackageManagerService.installPacakge()
进行安装. 顺便一提, 具有
INSTALL_PACKAGES
权限就可以自己调用这个方法进行apk安装.
扫描apk安装, 主要是
PackageManagerService
进行安装包扫描和解析工作. 信息解析完毕后存在特定数据结构中(
PackageParser.Package
), 此后需要进行信息同步工作. 这是因为, 扫描到的APK可能是已经更名的包/disable的包/需要升级的包/已经安装但签名冲突的包/替换了系统包的非系统包等情况, 需要处理这些情况, 保证最终信息正确.
如果包需要进行
Rename
或者
Update
, 则需要签名比较.
而使用
adb shell pm
安装略有不同, 是由
com.android.commands.pm.Pm
中的
runInstall
来安装的(
adb install
最终也是调用的
shell pm
).
系统最初调用
installPackageAsUser
检查是否有安装权限, 安装APK的整个过程在
PackageHandler
中进行, 主要分为 拷贝APK(检查是否有足够空间)->扫描APK->安装后处理(主要是发送广播信息), 其中最关键的一步就是扫描APK, 由函数
scanPackageLI
完成, 该函数里面调用
scanPackageDirtyLI
.
scanPackageDirtyLI()
源码位于
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
中(基于7.0源码):
参考http://blog.hjhjw1991.com/android/2018/01/02/Apk%E5%AE%89%E8%A3%85%E5%8D%B8%E8%BD%BD%E5%8E%9F%E7%90%86/
目录
|
含义
|
/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 : 存放使用的包
在第三方app安装后,会创建的目录、文件以及记录的信息:
-
拷贝的apk文件,位于
/data/app
下
-
创建的app数据目录
/data/data/${package_name}
-
从apk中提取的dex文件,位于
/data/dalvik-cache
目录下
-
/data/system/packages.list
和
/data/system/packages.xml
中关于app的记录信息
另外,app在运行期间有可能会使用到外部存储目录
/storage/emulated/0/Android/data/${package_name}
,该目录只有在app运行时调用相关函数时才创建,app安装后不会创建的。
卸载是安装的逆过程,
删除在创建过程中三个路径下产生的文件夹
,以及有可能后面创建的外部存储目录
/storage/emulated/0/Android/data/${package_name}
。
通过系统来卸载App通常是,点击卸载后,就会发送一个
Intent
给
UninstallerActivity
,在
UninstallerActivity
最后会启动
UninstallAppProgress
的
initView
方法,调用到
ApplicantPackageManger.java
的
deletePackage
方法,通过
Binder
绑定,其实是调用
PMS
中的
deletePackageAsUser
方法, 同样位于
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
。
源码参考:http://blog.hjhjw1991.com/android/2018/01/02/Apk%E5%AE%89%E8%A3%85%E5%8D%B8%E8%BD%BD%E5%8E%9F%E7%90%86/
卸载第三方app最后由
deleteInstalledPackageLI()
方法来完成,分两步走:
-
第一步删除
/data/data
下面的数据目录,并从PMS的内部数据结构上清除当前卸载的package信息
-
deleteInstalledPackageLI()
–>(表示调用,下同)
removePackageDataLI()
。
removePackageDataLI()
–>
removePackageLI()
–>mPackages的
remove()
:删除apk。
removePackageLI()
–>
cleanPackageDataStructuresLILPw()
:将package的
providers
、
services
、
receivers
、
activities
等信息去PMS的全局数据结构上移除。
-
removePackageDataLI()
–>
removeDataDirsLI()
–>installd的
remove()
:删除目录
/data/data/${package_name}
-
removePackageDataLI()
–>
schedulePackageCleaning()
:安排清理动作。向
PackageHandler
发送
START_CLEANING_PACKAGE
消息,PMS会调用
ContainService
的函数去删除
/storage/sdcard0/Android/data
和
/storage/sdcard0/Android/media
下面与
package
相关的文件
-
removePackageDataLI()
–>Settings的
removePackageLPw()
:首先从
mPackages
这个
map
中删除
PackageSettings
信息,即删除对应的
Package UID
信息
-
removePackageDataLI()
–>
updatePermissionsLPw()
:检查
mPermissionTrees
和
mPermissions
两个数组中的权限是否是被删除的
Package
提供,如果有,则删除。
-
removePackageDataLI()
–>Settings的
updateSharedUserPermsLPw()
:清除
sharedUser
不用的
gid
信息,防止权限泄露。
-
removePackageDataLI()
–>Settings的
writeLPr()
:将修改的信息写到
Package.xml
中
-
第二步就删除code和resource文件
-
分别构造
FileInstallArgs
和
AsecInstallArgs
来完成code和resource资源的清除
-
FileInstallArgs
的
doPostDeleteLI()
–>
cleanUpResourcesLI()
–>
cleanUp()
:删除code、resource以及library文件
-
cleanUpResourcesLI()
–>installd的
rmdex()
:删除存在
/data/dalvik-cache
文件
参考:
https://blog.csdn.net/xinsong1989/article/details/78527439
http://blog.hjhjw1991.com/android/2018/01/02/Apk%E5%AE%89%E8%A3%85%E5%8D%B8%E8%BD%BD%E5%8E%9F%E7%90%86/
https://blog.csdn.net/hanfengzqh/article/details/82790896
apk本质上就是一个zip包1. Apk安装常见的apk安装方式有三种:系统自带的应用和厂商预装的应用. 没错, 系统自带的应用其实也是apk, 其安装是在系统首次启动时完成的. 这也就是为什么root后可以卸载系统自带应用.没有安装界面.通过存储介质安装. 最常见的就是通过sd卡放置apk或者网上下载apk方式安装.通过 packageinstaller.apk来处理安装及卸载的过程的...
Android
与IOS一个比较大的不同就是
文件
夹部分,IOS在
卸
载
应用的时候会同时
删除
应用所
创建
的所有
文件
及
文件
夹,
Android
不会。
以下是执行这个操作的方法。
首先写一个广播接收器即BroadcastReceiver
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(
前段时间由于项目的需要,研究了下
Android
App
的
卸
载
与
安装
,这段时间整理了下,写成博客,方便以后查看。
一个手机
App
,经下
载
、
安装
、更新、
卸
载
。
下
载
就不说了,本篇博客主要研究
安装
、更新和
卸
载
。
一个apk经过下
载
下来,有几种
安装
方式?
1.内置
APP
随着系统启动PMS而
安装
。(刷机后一般第一步操作就是这个)
2.使用adb install命令
安装
。(开发人员常用方式)
应用程序在运行的
过程
中如果需要向手机上保存数据,一般是把数据保存在SDcard中的。
大部分应用是直接在SDCard的根
目录
下
创建
一个
文件
夹,然后把数据保存在该
文件
夹中。
这样当该应用被
卸
载
后,这些数据还保留在SDCard中,留下了垃圾数据。
如果你想让你的应用被
卸
载
后,与该应用相关的数据也清除掉,该怎么办呢?
通过Context.getExternalFilesDir()方法
首先我们要清楚总体可以分成如下几种
安装
的情况
* 系统开机的应用
安装
,
安装
的是系统级别的应用,用户在没有获取到root权限的情况下无法
卸
载
的应用
* adb
安装
的应用,没有
安装
界面
* 第三方市场下
载
的应用,此处要分情况,部分是通过电脑的客户端
安装
的没有
安装
的界面,部分是手机上的市场
安装
的,会有
安装
的界面那么我们就对几种情况一一分析它的
安装
流程
### 开机
安装
1. 首先在开机
作者venshine,源码
App
Uninstall,方案监听系统
卸
载
广播:只能监听到其他应用的
卸
载
广播,无法监听到自己是否被
卸
载
。读取系统 log:第三方软件
卸
载
无法得知。静默
安装
另一个程序,监听自己是否被
卸
载
:需要 root 权限。Java 线程轮询,监听/data/data/{package-name}
目录
是否存在:
卸
载
app
,进程退出,线程也被销毁。C 进程轮询,监听/data/data/{package-name}
目录
是否存在:目前业界普遍采用的方案。原理从前四种方案可以看到,单纯的 Java 层代码是无法监听自身
卸
载
的。既然 Java 层无法实现,我们试着使用 C 语言在底层实现。借助 Linux 进程 fork 出来的 C 进程在应用被
卸
载
后不会被销毁,监听/data/data/{package-name}
目录
是否存在,如果不存在,就证明应用被
卸
载
了。本程序采用第 5 种解决方案,对其进行优化,通过 linux 中的inotify机制来监听应用的
卸
载
。实现fork()子进程
创建
监听
文件
初始化 inotify 实例注册监听事件调用 read 函数开始监听
卸
载
反馈统计场景正常
卸
载
断网
卸
载
清除数据(5.0 以上不支持)kill 进程(5.0 以上不支持)插拔 USB 线覆盖
安装
内部存储移到 SD 卡开机监听(官方不推荐)打开浏览器(5.0 以上部分机型无法开启)
已
安装
应用
目录
结构在PMS启动初始化时,会扫描一下
目录
,并
安装
对应的应用。每个
目录
下是已
安装
的应用,每个应用的对应
目录
包括apk
文件
,lib库so
文件
,以及oat
文件
/system/
app
/data/
app
/data/
app
-asec
/data/
app
-privatepackages.xml
文件
在/data/system
目录
下,packages.xml保存了系统
安装
的应用相
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/
app
/packagename-2/lib/arm/libxxxxxx.so" is 32-bit instead of 64-bit
问题出现后检查libaudiooperate.so的确是32位的so,然后查...
解决Could not load dynamic library 'libcudart.so.10.0'; dlerror: libcudart.so.10.0: cannot open ...
41521