android apk安装原理简述

公司给的任务,研究了几天apk安装方面的内容,研究的目的是给出apk安装过程的各个阶段

下面各个步骤都已经分析清楚,加步骤的话就在每个阶段的开始和接受加上广播的推送即可。

APKAndroid Package的缩写,即Android安装包。APK是类似Symbian SisSisx的文件格式。通过将APK文件直接传到Android模拟器或Android手机中执行即可安装。

一、Android应用安装有如下四种方式

1.系统应用安装――开机时完成,没有安装界面

2.网络下载应用安装――通过market应用完成,没有安装界面

3.ADB工具安装――没有安装界面。

4.第三方应用安装――通过SD卡里的APK文件安装,有安装界面

二、应用安装的流程及路径
应用安装涉及到如下几个目录:

system/app 系统自带的应用程序,无法删除
data/app 用户程序安装的目录,有删除权限。安装时把apk文件复制到此目录
data/data 存放应用程序的数据
data/dalvik-cache apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)

在这个文档中主要介绍的是第三方应用安装的完整过程安装SD卡里的APK文件。

调用安装接口完成安装

pm.installPackage(mPackageURI,observer, installFlags);

看到这里,我有一个疑问:

PackageManager.java中的installPackage的原型如下:

public abstract void installPackage(

Uri packageURI, IPackageInstallObserver observer, int flags,

String installerPackageName);

然后看到所有Packageanager.java里面的方法基本都是抽象的,经过不断寻找,终于在文件ContextImpl.java/framework/bash/core/java/android/app/ContextImpl.java)中找到了实现的位置首先通过getPackageManager()获取到包管理器的句柄,然后再调用它里边的installPackage进行安装。

public PackageManager getPackageManager() {

if (mPackageManager != null) {

return mPackageManager;

}

IPackageManager pm = ActivityThread.getPackageManager();

if (pm != null) {

// Doesn't matter if we make more than one instance.

return (mPackageManager=newApplicationPackageManager(this,pm));

}

return null;

}

ApplicationPackageManager继承了PackageManager,该类实现了所有的抽象方法

public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,

String installerPackageName) {

try {

mPM.installPackage(packageURI,observer,flags, installerPackageName);

} catch (RemoteException e) {

// Should never happen!

}

}

这样就调用了PackageManagerService.java/framework/base/services/java/com/android/server/PackageManagerService.java)中的installPackage里面。

public void installPackage(

final Uri packageURI, final IPackageInstallObserver observer, final int flags,

final String installerPackageName) {

mContext.enforceCallingOrSelfPermission(

android.Manifest.permission.INSTALL_PACKAGES, null);


Message msg = mHandler.obtainMessage(INIT_COPY);

msg.obj = new InstallParams(packageURI, observer, flags,

installerPackageName);

mHandler.sendMessage(msg);

}

通过sendMessage将消息发送给PackageHandler中的handleMessage(Message msg)接收。

if (!mBound) {

// If this is the only one pending we might

// have to bind to the service again.

if (!connectToService()) {

Slog.e(TAG, "Failed to bind to media container service");

params.serviceError();

return;

} else {

// Once we bind to the service, the first

// pending request will be processed.

mPendingInstalls.add(idx,params);

}

} else {

mPendingInstalls.add(idx,params);

// Already bound to the service. Just make

// sure we trigger off processing the first request.

if (idx == 0) {

mHandler.sendEmptyMessage(MCS_BOUND);

}

}

分析INIT_COPY里面的逻辑代码,可以知道首先检查是否已经和IMediaContainerService服务绑定,如果没有则与该服务尝试连接,负责将接收到的安装包添加到mPendingInstalls变量里边,但是mPendingInstalls这里边的请求在什么时候被处理掉的呢?

public void onServiceConnected(ComponentName name, IBinder service) {

if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");

IMediaContainerService imcs =

IMediaContainerService.Stub.asInterface(service);

mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND,imcs));

}

原来在第一次绑定该服务成功后就发送了MCS_BOUND消息,调用到HandlerParamsstartCopy方法准备安装

else if (mPendingInstalls.size() > 0) {

HandlerParams params = mPendingInstalls.get(0);

if (params != null) {

params.startCopy();

}

}

接下来在该方法中调用mHandler.sendEmptyMessage(MCS_UNBIND)PackageHandler接收到MSC_UNBIND消息以后会进入以下逻辑块

if (mPendingInstalls.size() == 0) {

if (mBound) {

disconnectService();

}

} else {

// There are more pending requests in queue.

// Just post MCS_BOUND message to trigger processing

// of next pending install.

mHandler.sendEmptyMessage(MCS_BOUND);

}

如果mPendingInstalls还有未处理的请求继续重复上述步骤,否则断开与IMediaContainerService建立的服务。


抽象类HandlerParams中实现了方法startCopy(),而handleStartCopy()handleReturnCode()两个方法的实现则是在HandlerParams的子类InstallParams里边。

final void startCopy() {

try {

if (DEBUG_SD_INSTALL) Log.i(TAG, "startCopy");

retry++;

if (retry > MAX_RETRIES) {

Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");

mHandler.sendEmptyMessage(MCS_GIVE_UP);

handleServiceError();

return;

} else {

handleStartCopy();

if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_UNBIND");

mHandler.sendEmptyMessage(MCS_UNBIND);

}

} catch (RemoteException e) {

if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT");

mHandler.sendEmptyMessage(MCS_RECONNECT);

}

handleReturnCode();

}


InstallParams里边handleStartCopy()主要实现的功能是获取安装位置信息以及复制apk到指定位置。

if (ret == PackageManager.INSTALL_SUCCEEDED) {

// Create copy only if we are not in an erroneous state.

// Remote call to initiate copy using temporary file

ret=mArgs.copyApk(mContainerService,true);

}

抽象类InstallArgs中的copyApk负责复制APK文件,具体实现在子类FileInstallArgsSdInstallArgs里面。

下面我们看一下FileInstallArgs中实现该方法的关键部分

if (imcs.copyResource(packageURI,out)) {

ret = PackageManager.INSTALL_SUCCEEDED;

}

通过IMediaContainerService服务将APK复制到指定目录(data/app/vmdl26489.tmp)

SdInstallArgs中该方法实现的原理差不多,就不再赘述。

我们再回到InstallParams里面的handleReturnCode()中来,该方法调用processPendingInstall方法处理安装。

@Override

void handleReturnCode() {

processPendingInstall(mArgs,mRet);

}

private void processPendingInstall(final InstallArgs args, final int currentStatus) {

// Queue up an async operation since the package installation may take a little while.

mHandler.post(new Runnable() {

public void run() {

/** 省略若干 **/


if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

args.doPreInstall(res.returnCode);

synchronized (mInstallLock) {

installPackageLI(args,true,res);

}

args.doPostInstall(res.returnCode);

}


/** 省略若干 **/

if (!doRestore) {

// No restore possible, or the Backup Manager was mysteriously not

// available -- just fire the post-install work request directly.

if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);

Messagemsg= mHandler.obtainMessage(POST_INSTALL,token,0);

mHandler.sendMessage(msg);

}

}

});

}

方法processPendingInstall中关键的操作已经用红色标出,主要的安装流程都在这个方法里面。安装过程放在一个线程里面,因为安装需要一定的时间(这里有个问题,Handler中有一个MessageQueue处理所有的Message队列,那么通过post进来一个Runnable,这个Runnable是在什么时候处理的?)。处理流程是预安装-安装-安装收尾-发送POST_INSTALL消息

预安装:下面这个doPreInstall的实现是来自InstallArgs的直接子类SdInstallArgs,所做的工作是检查当前安装包的状态以及确保SDCARD的挂载,并返回状态信息。从中我们可以看出来预安装功能就是在安装前确保安装环境的可靠。

int doPreInstall(int status) {

if (status != PackageManager.INSTALL_SUCCEEDED) {

// Destroy container

PackageHelper.destroySdDir(cid);

} else {

boolean mounted = PackageHelper.isContainerMounted(cid);

if (!mounted) {

cachePath = PackageHelper.mountSdDir(cid, getEncryptKey(), Process.SYSTEM_UID);

if (cachePath == null) {

return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;

}

}

}

return status;

}

安装:对mHistallLock加了锁,表明同时只能有一个安装包进行安装,installPackageLI稍候介绍。

synchronized (mInstallLock) {

installPackageLI(args,true,res);

}

安装收尾:我们依然查看来自InstallArgs的直接子类SdInstallArgs中该方法的实现。代码很简单,检查状态,如果安装未成功,删除掉SDCARD上的目录文件,如果没有挂载上,继续尝试挂载。

int doPostInstall(int status) {

if (status != PackageManager.INSTALL_SUCCEEDED) {

cleanUp();

} else {

boolean mounted = PackageHelper.isContainerMounted(cid);

if (!mounted) {

PackageHelper.mountSdDir(cid,

getEncryptKey(), Process.myUid());

}

}

return status;

}

最后一个阶段就是发送POST_INSTALL消息,该消息由PackageHandler接收,负责处理安装完毕的广播发送工作(安装和替换的两个广播Intent.ACTION_PACKAGE_ADDEDIntent.ACTION_PACKAGE_REPLACED),以及回调函数的调用等,这个回调函数是IPackageInstallObserver里面的packageInstalled方法(利用该方法可以让我们在安装完毕后做一些操作)

case POST_INSTALL: {

/** 省略若干 **/

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

res.removedInfo.sendBroadcast(false, true);

Bundle extras = new Bundle(1);

extras.putInt(Intent.EXTRA_UID, res.uid);

final boolean update = res.removedInfo.removedPackage != null;

if (update) {

extras.putBoolean(Intent.EXTRA_REPLACING, true);

}

sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,

res.pkg.applicationInfo.packageName,

extras,null);

if (update) {

sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,

res.pkg.applicationInfo.packageName,

extras,null);

}

if (res.removedInfo.args != null) {

// Remove the replaced package's older resources safely now

deleteOld = true;

}

}

// Force a gc to clear up things

Runtime.getRuntime().gc();

// We delete after a gc for applications on sdcard.

if (deleteOld) {

synchronized (mInstallLock) {

res.removedInfo.args.doPostDeleteLI(true);

}

}

if (args.observer != null) {

try {

args.observer.packageInstalled(res.name,res.returnCode);

} catch (RemoteException e) {

Slog.i(TAG, "Observer no longer exists.");

}

}

} else {

Slog.e(TAG, "Bogus post-install token " + msg.arg1);

}

} break;

上面介绍了几个安装过程的具体实现(预安装-安装-安装收尾-发送POST_INSTALL消息),除了安装过程,下面我们就对其单独叙述。

我们看到installPackageLi里面主要做了两件事情:解析安装包、替换\新安装安装包。

private void installPackageLI(InstallArgs args,

boolean newInstall, PackageInstalledInfo res) {

/** 省略若干 **/

PackageParserpp=newPackageParser(tmpPackageFile.getPath());

pp.setSeparateProcesses(mSeparateProcesses);

finalPackageParser.Packagepkg= pp.parsePackage(tmpPackageFile,

null,mMetrics,parseFlags);

/** 省略若干 省略部分主要工作是设置标志位、扫描模式、解析模式等等校验工作**/

// Set application objects path explicitly after the rename

if (replace) {

replacePackageLI(pkg,parseFlags,scanMode,

installerPackageName,res);

} else {

installNewPackageLI(pkg,parseFlags,scanMode,installerPackageName,res);

}

parsePackage 是一个重定义函数,它主要做了三件事,一个是解析apk中的asset

, res

parsePackage(Resources res, XmlResourceParser parser, int flags, String[] outError)apk

AndroidManifest.xml 进行解析,将每个标签对应的信息添加到每个包的相关列表中,如将标

applicationactivity pkg.activities.add(a)package activities ,

service 添加到 owner.services.add(s)


public Package parsePackage(File sourceFile, String destCodePath,

DisplayMetrics metrics, int flags) {

/** 省略若干 **/

XmlResourceParser parser = null;

AssetManager assmgr = null;

//解析/asset下的文件

intcookie= assmgr.addAssetPath(mArchiveSourcePath);

parser=assmgr.openXmlResourceParser(cookie,"AndroidManifest.xml");

/** 省略若干 **/

//解析/res下的文件,通过parsePackage函数解析AndroidManifest.xml文件

pkg=parsePackage(res,parser,flags, errorText);


/** 省略若干 **/

// Set code and resource paths

pkg.mPath=destCodePath;

pkg.mScanPath=mArchiveSourcePath;

return pkg;

}

Package parsePackage(Resources res, XmlResourceParser parser, int flags, String[] outError)

{

解析AndroidManifest.xml里的各个标签,并对pkgmVersionCode,mSharedUserId,

mSharedUserLabel,installLocation application , permission-

group , permission , permission-tree , uses-permission , uses-configuration , uses-

feature , uses-sdk , supports-screens , protected-broadcast , instrumentation , original-

package,adopt-permissions,eat-comment 等标签调用各自解析函数进行处理。

解析出每个标签下的子标签的信息,然后将这些信息添加到每个package的对应列表中,如将application下的activity通过pkg.activities.add(a)添加到packageactivities列表。

//pkg返回

return pkg;

}

解析完apk包以后接下来通过replace这个变量来判断目前安装的apk是替换掉原有的apkreplacePackageLI)还是安装一个全新的apkinstallNewPackageLI)。

首先我们介绍installNewPackageLI

private void installNewPackageLI(PackageParser.Package pkg,

int parseFlags,

int scanMode,

String installerPackageName, PackageInstalledInfo res) {

// Remember this for later, in case we need to rollback this install

String pkgName = pkg.packageName;

/** 省略若干 **/

PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,

System.currentTimeMillis());

if (newPackage == null) {

if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {

res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;

}

} else {

updateSettingsLI(newPackage,

installerPackageName,

res);

if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {

deletePackageLI(

pkgName,false,

dataDirExists?PackageManager.DONT_DELETE_DATA:0,

res.removedInfo,true);

}

}

由上面代码中红色部分标注的,主要做了三件事情,scanPackageLi(pkg)updateSettingsLi-deletePackageLi(如果安装失败的话)。

scanPackageLi有两个重载的方法,两个参数稍有不同,下面对两个函数简称为scanPackageLi(file)scanPackageLi(pkg),两个函数的逻辑都比较复杂,代码量也很多,根据方法的名字,很多人都认为是扫描包信息的一个方法,其实安装过程也在这里面完成。

private PackageParser.Package scanPackageLI(pkg) {

/** 省略若干 **/

if (pkg.packageName.equals("android")) {

// todo 系统级应用需要特殊处理

}

/** 省略若干 **/

//检查是否有重复的包名

if (mPackages.containsKey(pkg.packageName)

|| mSharedLibraries.containsKey(pkg.packageName)) {

mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;

return null;

}

/** 接下来主要做了几件事情

(1)Check all shared libraries and map to their actual file path.

(2)Check pkg.reqFeatures in mAvailableFeatures

(3)Check and note if we are renaming from an original package name

(4)Check if we are renaming from an original package name.

**/

//检查新的包中的Content Providers没有和已经存在的Content Providers

重复。这个操作在安装前完成,系统优先已经安装的包。

/** 省略若干 **/

mLastScanError=PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;

//接下来创建安装包的数据目录

//如果包名以android开头的,则将应用的dataDir/data/system

if (mPlatformPackage == pkg) {

// The system package is special.

dataPath = new File (Environment.getDataDirectory(), "system");

pkg.applicationInfo.dataDir = dataPath.getPath();

} else {

dataPath=getDataPathForPackage(pkg);

if (dataPath.exists()) {

/** 省略若干 **/

if (mInstaller != null) {

int ret = mInstaller.remove(pkgName, useEncryptedFSDir);

if (ret >= 0) {

ret=mInstaller.install(pkgName,useEncryptedFSDir,pkg.applicationInfo.uid,

pkg.applicationInfo.uid);

}

}

else{

if (mInstaller != null) {

intret= mInstaller.install(pkgName,useEncryptedFSDir,pkg.applicationInfo.uid,

pkg.applicationInfo.uid);

if(ret < 0) {

// Error from installer

mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;

return null;

}

} else {

dataPath.mkdirs();

if (dataPath.exists()) {

FileUtils.setPermissions(

dataPath.toString(),

FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,

pkg.applicationInfo.uid, pkg.applicationInfo.uid);

}

}

if (dataPath.exists()) {

pkg.applicationInfo.dataDir=dataPath.getPath();

} else {

Slog.w(TAG, "Unable to create data directory: " + dataPath);

pkg.applicationInfo.dataDir = null;

}

}


// Request the ActivityManager to kill the process(only for existing packages)

// so that we do not end up in a confused state while the user is still using the older

// version of the application while the new one gets installed.

if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {

killApplication(pkg.applicationInfo.packageName,

pkg.applicationInfo.uid);

}


以下将每个包的provider,service,activity等信息添加到全局列表中

mServices.addService(s);

mReceivers.addActivity(a, "receiver");

mActivities.addActivity(a, "activity");

mPermissionGroups.put(pg.info.name, pg);

permissionMap.put(p.info.name, bp);

mInstrumentation.put(a.getComponentName(), a);

mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));

//最后再盖上一个时间戳

pkgSetting.setTimeStamp(scanFileTime);

安装完毕后调用updateSettingsLI更新列表信息,主要工作是将pkg信息写入到package.xmlpackage.list里面。packages.xml保存了每个已经安apk 的详尽的信息。如果安装过程失败则会调用deletePackageLi将已经创建或者已经保存的包信息删除。每次包信息有变化都会更新package.xmlpackage.list两个文件。


上面讲述的是安装一个以前不存在的包,如果重新安装已经存在的包则会调用到replacePackageLI这个方法,这里面也分为系统级app和一般app,这里面过程就不再多叙述,大体也就是更新一下所有本地信息,将变化信息写入package.xmlpackage.list等。

private void replacePackageLI(PackageParser.Package pkg,

int parseFlags,

int scanMode,

String installerPackageName, PackageInstalledInfo res) {

/** 省略若干 **/

if (sysPkg) {

replaceSystemPackageLI(oldPackage,pkg,parseFlags,scanMode, installerPackageName,res);

} else {

replaceNonSystemPackageLI(oldPackage,pkg,parseFlags,scanMode, installerPackageName,res);

}


以上介绍了一个新apksdcard上的安装过程。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值