apk安装包介绍(下载安装,存储的位置,路径,可以对里面的文件进行修改吗)
本片文章的主要内容如下:
- 1、概述
- 2、Android应用程序的几种安装方式
- 3、应用安装涉及到的目录
- 4、安装流程概述
- 5、PackageInstaller.apk与PackageManger
- 6、普通的APK安装方式的界面
- 7、PackageInstallerActivity类的安装流程
- 8、InstallAppProgress类的安装流程
- 9、InstallAppProgress中涉及到PackageManager的三个方法
众所周知,Android应用最终是打包成.apk格式(其实就是一个压缩包),然后安装至手机并运行的。其中APK是Android Package的缩写。
Android系统在启动的过程中,会启动一个引用程序管理服务PackageManagerService,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,以.apk为后缀的文件,然后对这些文件进行解析,得到引用程序的相关信息,完成应用程序的安装过程。应用程序管理服务PackageManagerService安装应用程序的过程,其实就是解析应用程序配置文件的AndroidManifest.xml的过程,并从里面得到应用程序的相关信息,例如得到引用程序的组件Activity、Service、Receiver和Content Provider等信息,有了这些信息后,通过ActivityManagerService这个服务,我们就可以在系统中正常地使用这些应用程序了。
Android上应用安装可以分为以下几种方式:
- 1、系统安装:开机的时候,没有安装界面
- 2、adb 命令安装:通过abd命令行安装,没有安装界面
- 3、应用市场安装,这个要视应用的权限,有系统的权限无安装界面(例如MUI的小米应用商店)
- 4、第三方安装,有安装界面,通过packageinstaller.apk来处理安装及卸载的过程的界面
- /system/app:系统自带的应用程序,获得adb root 权限才能删除
- /data/app:用户程序安装的目录。安装时把apk文件复制到此目录
- /data/data:存放应用程序的数据
- /data/dalvik-cache:将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,当然,ART-Android Runtime的可执行文件格式为.oat,启动ART时,系统会执行dex文件转换至oat文件)
- /data/system:该目录下的packages.xml文件。类似于Window的注册表,这个文件是解析apk时由writeLP()创建的,里面记录了系统的permissons,以及每个apk的name,codePath,flag,ts,version,userid等信息,这些信息主要通过apk的AndroidManifest解析获取,解析完apk后将更新信息写入这个文件并保存到flash,下次开机的时候直接从里面读取相关信息并添加到内存相关列表中。当有apk升级,安装或删除时会更新这个文件。 -/data/system/package.xml与/data/system/package.list:packages.list指定了应用的默认存储位置/data/data/com.xxx.xxx;package.xml中包含了该应用申请的权限、签名和代码所在的位置等信息系,并且两者都有同一个userld。之所以每个应用都要一个userId,是因为Android在系统设计上把每个应用当做Linux系统上的一个用户对待,这样就可以利用已有的Linux用户管理机制来设计Android应用,比如应用目录,应用权限,应用进程管理等。
apk的大体流程如下:
- 第一步:拷贝文件到指定的目录: 在Android系统中,apk安装文件是会被保存起来的,默认情况下,用户安装的apk首先会被拷贝到/data/app目录下,/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统出场的apk文件则被放到了/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,该分区只有ROOT权限的用户才能访问,这也就是为什么在没有Root手机之前,我们没法删除系统出场的app的原因了。
- 第二步:解压缩apk,宝贝文件,创建应用的数据目录 为了加快app的启动速度,apk在安装的时候,会首先将app的可执行文件dex拷贝到/data/dalvik-cache目录,缓存起来。然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放在应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等。
- 第三步:解析apk的AndroidManifest.xml文件
Android系统中,也有一个类似注册表的东西,用来记录当前所有安装的应用的基本信息,每次系统安装或者卸载了任何apk文件,都会更新这个文件。这个文件位于如下目录:/data/system/packages.xml。系统在安装这个apk的过程中,会解析apk的AndroidManifest.xml文件,提取出这个apk的重要信息写入到packages.xml文件中,这些信息包括:权限、应用包名、APK的安装位置、版本、userID等等。由此,我们就知道了为什么一些应用市场和软件管理类的app能够很清楚地知道当前手机所安装的所有app,以及这些app的详细信息了。另外一件事就是Linux的用户Id和用户组Id,以便他们可以获得合适的运行权限。以上都是由PackageServcieManager完成的,后面我们会重点介绍PackageServiceManager。
- 第四步:显示快捷方式 如果这些应用程序在PackageManagerService服务注册好了,如果我们想要在Android桌米上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。在Android系统中,负责把系统中已经安装的应用程序在桌面中展现出来的Home应用就是Launcher了。
PackageInstaller.apk地址
PackageInstaller/AndroidManifest.xml.png
(一)、PackageInstaller概述
PackagInstaller是安卓上默认的应用程序,用它来安装普通文件。PackageInstaller提供了用户界面来管理应用或者包文件。PackageInstaller调用一个叫做InstallAppProgress的activity来获取用户发出的指令。InstallAppProgress会请求Package Manager服务,然后通过installed来安装包文件。
installed这个守护进程的首要角色就是获取来自Package Manager服务的请求,而该请求是通过Linux套接字/dev/socket/installed获得的。installed使用管理员权限执行一系列步骤来安装APK。
(二)、PackageInstaller内容解析
PackageInstaller的结构如下:
PackageInstaller结构1.png
PackageInstaller结构2.png
这里面重点介绍以下两个类
- PackageInstallerActivity:主要是检查各种权限,展示被安装应用的向相关信息,最后跳转到实际安装应用的InstallAppProgress
- InstallAppProgress:也是进行了一系列的操作,最终把安装交给了PackageManager.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null);
下面我们就来看看
普通的APK安装方式 一般是经过下面的两个界面的
image.png
上面的两个界面分别是PackageInstallerActivity和InstallAppProgress
(一)、PackageInstallerActivity类
public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
...
...
}
我们是知道PackageInstallerActivity是一个Activity并且实现了OnCancelListener和OnClickListener接口,下面我们来看一下注释。
当通过渠道安装一个应用程序的时候,会启动这个Activity。如果在首次解析这个安装包的时候出现解析错误,会通过对话框的形式告诉用户。如果首次解析安装包的时候,成功解析了,则会通知用户去打开"安装未知应用程序设置"。在启动Activity的时候会进行内存检查,如果内存不足会通知用户。如果这个应用程序已经在这个设备安装过了,则会向用户弹出一个对话框询问用户是否"替换现有应用程序的安装包"。基于用户的回应,然后通过InstallAppConfirm的子Activity来安装应用程序。在这Activity中处理所有状态的转换。
大家平时写Activity一般都是先在onCreate方法里面做一些初始化的操作,那我们来看下PackageInstallerActivity的onCreate里面都做了什么?
(二)、PackageInstallerActivity类的onCreate()方法
代码在PackageInstallerActivity.java 439行
PackageManager mPm;
UserManager mUserManager;
PackageInstaller mInstaller;
PackageInfo mPkgInfo;
ApplicationInfo mSourceInfo;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
//第一步
// 一个PackageManager对象,具体用来执行安装操作
mPm =getPackageManager();
// PackageInstaller对象,在该对象中包含了安装APK的基本信息
mInstaller =mPm.getPackageInstaller();
mUserManager =(UserManager) getSystemService(Context.USER_SERVICE);
// 第二步
final Intent intent =getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId =intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info =mInstaller.getSessionInfo(sessionId);
if (info ==null || !info.sealed || info.resolvedBaseCodePath ==null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId =sessionId;
mPackageURI =Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI =null;
mReferrerURI =null;
} else {
mSessionId =-1;
mPackageURI =intent.getData();
mOriginatingURI =intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI =intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
// 第三步
final boolean unknownSourcesAllowedByAdmin =isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser =isUnknownSourcesEnabled();
boolean requestFromUnknownSource =isInstallRequestFromUnknownSource(intent);
//第四步
mInstallFlowAnalytics =new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
&& unknownSourcesAllowedByUser);
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
//第五步
final String scheme =mPackageURI.getScheme();
if (scheme !=null && !"file".equals(scheme) && !"package".equals