您的位置:首页 > 其它

APK安装过程及原理详解

2012-12-20 23:52 471 查看
应用程序包的安装是android的特点

APK为AndroidPackage的缩写

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

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

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

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

4.第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由 packageinstaller.apk应用处理安装及卸载过程的界面。

应用安装的流程及路径

应用安装涉及到如下几个目录:

system/app ---------------系统自带的应用程序,获得adb root权限才能删除

data/app ---------------用户程序安装的目录。安装时把 apk文件复制到此目录

data/data ---------------存放应用程序的数据

data/dalvik-cache--------将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)

安装过程:

复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。

卸载过程:

删除安装过程中在上述三个目录下创建的文件及目录。

安装应用的过程解析

一.开机安装

PackageManagerService处理各种应用的安装,卸载,管理等工作,开机时由systemServer启动此服务

(源文件路径:android\frameworks\base\services\java\com\android\server\PackageManagerService.java)

PackageManagerService服务启动的流程:

1.首先扫描安装“system\framework”目录下的jar包

[java] view
plaincopy

// Find base frameworks (resource packages without code).

mFrameworkInstallObserver = new AppDirObserver(

mFrameworkDir.getPath(), OBSERVER_EVENTS, true);

mFrameworkInstallObserver.startWatching();

scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR,

scanMode | SCAN_NO_DEX, 0);

2.扫描安装系统system/app的应用程序

[java] view
plaincopy

// Collect all system packages.

mSystemAppDir = new File(Environment.getRootDirectory(), "app");

mSystemInstallObserver = new AppDirObserver(

mSystemAppDir.getPath(), OBSERVER_EVENTS, true);

mSystemInstallObserver.startWatching();

scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

3.制造商的目录下/vendor/app应用包

[java] view
plaincopy

// Collect all vendor packages.

mVendorAppDir = new File("/vendor/app");

mVendorInstallObserver = new AppDirObserver(

mVendorAppDir.getPath(), OBSERVER_EVENTS, true);

mVendorInstallObserver.startWatching();

scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

4.扫描“data\app”目录,即用户安装的第三方应用

[java] view
plaincopy

scanDirLI(mAppInstallDir, 0, scanMode, 0);

5.扫描" data\app-private"目录,即安装DRM保护的APK文件(一个受保护的歌曲或受保 护的视频是使用 DRM 保护的文件)

[java] view
plaincopy

scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,

scanMode, 0);

扫描方法的代码清单

[java] view
plaincopy

private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {

String[] files = dir.list();

if (files == null) {

Log.d(TAG, "No files in app dir " + dir);

return;

}

if (false) {

Log.d(TAG, "Scanning app dir " + dir);

}

int i;

for (i=0; i<files.length; i++) {

File file = new File(dir, files[i]);

if (!isPackageFilename(files[i])) {

// Ignore entries which are not apk's

continue;

}

PackageParser.Package pkg = scanPackageLI(file,

flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);

// Don't mess around with apps in system partition.

if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&

mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {

// Delete the apk

Slog.w(TAG, "Cleaning up failed install of " + file);

file.delete();

}

}

}

并且从该扫描方法中可以看出调用了scanPackageLI()

private PackageParser.Package scanPackageLI(File scanFile,

int parseFlags, int scanMode, long currentTime)

跟踪scanPackageLI()方法后发现,程序经过很多次的if else 的筛选,最后判定可以安装后调用了 mInstaller.install

[java] view
plaincopy

if (mInstaller != null) {

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

if(ret < 0) {

// Error from installer

mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;

return null;

}

}

mInstaller.install() 通过

LocalSocketAddress address = new LocalSocketAddress(

"installd", LocalSocketAddress.Namespace.RESERVED);

指挥installd在C语言的文件中完成工作

PackageManagerService小节 :1)从apk, xml中载入pacakge信息, 存储到内部成员变量中, 用于后面的查找. 关键的方法是scanPackageLI().

2)各种查询操作, 包括query Intent操作.

3)install package和delete package的操作. 还有后面的关键方法是installPackageLI().

二、从网络上下载应用:

下载完成后,会自动调用Packagemanager的安装方法installPackage()

/* Called when a downloaded package installation has been confirmed by the user */

由英文注释可见PackageManagerService类的installPackage()函数为安装程序入口。

[java] view
plaincopy

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);

}

其中是通过PackageHandler的实例mhandler.sendMessage(msg)把信息发给继承Handler的类HandleMessage()方法

[java] view
plaincopy

class PackageHandler extends Handler{

*****************省略若干********************

public void handleMessage(Message msg) {

try {

doHandleMessage(msg);

} finally {

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

}

}

******************省略若干**********************

}

把信息发给doHandleMessage()方法,方法中用switch()语句进行判定传来Message

[java] view
plaincopy

void doHandleMessage(Message msg) {

switch (msg.what) {

case INIT_COPY: {

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

HandlerParams params = (HandlerParams) msg.obj;

int idx = mPendingInstalls.size();

if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);

// If a bind was already initiated we dont really

// need to do anything. The pending install

// will be processed later on.

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);

}

}

break;

}

case MCS_BOUND: {

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

if (msg.obj != null) {

mContainerService = (IMediaContainerService) msg.obj;

}

if (mContainerService == null) {

// Something seriously wrong. Bail out

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

for (HandlerParams params : mPendingInstalls) {

mPendingInstalls.remove(0);

// Indicate service bind error

params.serviceError();

}

mPendingInstalls.clear();

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

HandlerParams params = mPendingInstalls.get(0);

if (params != null) {

params.startCopy();

}

} else {

// Should never happen ideally.

Slog.w(TAG, "Empty queue");

}

break;

}

****************省略若干**********************

}

}

public final boolean sendMessage (Message msg)

public final boolean sendEmptyMessage (int what)

两者参数有别。

然后调用抽象类HandlerParams中的一个startCopy()方法

abstract class HandlerParams {

final void startCopy() {

***************若干if语句判定否这打回handler消息*******

handleReturnCode();

}

}

handleReturnCode()复写了两次其中有一次是删除时要调用的,只列出安装调用的一个方法

[java] view
plaincopy

@Override

void handleReturnCode() {

// If mArgs is null, then MCS couldn't be reached. When it

// reconnects, it will try again to install. At that point, this

// will succeed.

if (mArgs != null) {

processPendingInstall(mArgs, mRet);

}

}

这时可以清楚的看见 processPendingInstall()被调用。

其中run()方法如下

[java] view
plaincopy

run(){

synchronized (mInstallLock) {

************省略*****************

installPackageLI(args, true, res);

}

}

instaPacakgeLI()args,res参数分析

-----------------------------------------------------------------------------------------

//InstallArgs 是在PackageService定义的static abstract class InstallArgs 静态抽象类。

[java] view
plaincopy

static abstract class InstallArgs {

*********************************************************************

其中定义了flag标志,packageURL,创建文件,拷贝apk,修改包名称,

还有一些删除文件的清理,释放存储函数。

*********************************************************************

}

class PackageInstalledInfo {

String name;

int uid;

PackageParser.Package pkg;

int returnCode;

PackageRemovedInfo removedInfo;

}

-----------------------------------------------------------------------------------------

[java] view
plaincopy

private void installPackageLI(InstallArgs args,

boolean newInstall, PackageInstalledInfo res) {

int pFlags = args.flags;

String installerPackageName = args.installerPackageName;

File tmpPackageFile = new File(args.getCodePath());

boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);

boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);

boolean replace = false;

int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE

| (newInstall ? SCAN_NEW_INSTALL : 0);

// Result object to be returned

res.returnCode = PackageManager.INSTALL_SUCCEEDED;

// Retrieve PackageSettings and parse package

int parseFlags = PackageParser.PARSE_CHATTY |

(forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |

(onSd ? PackageParser.PARSE_ON_SDCARD : 0);

parseFlags |= mDefParseFlags;

PackageParser pp = new PackageParser(tmpPackageFile.getPath());

pp.setSeparateProcesses(mSeparateProcesses);

final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,

null, mMetrics, parseFlags);

if (pkg == null) {

res.returnCode = pp.getParseError();

return;

}

String pkgName = res.name = pkg.packageName;

if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {

if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {

res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;

return;

}

}

if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {

res.returnCode = pp.getParseError();

return;

}

// Get rid of all references to package scan path via parser.

pp = null;

String oldCodePath = null;

boolean systemApp = false;

synchronized (mPackages) {

// Check if installing already existing package

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

String oldName = mSettings.mRenamedPackages.get(pkgName);

if (pkg.mOriginalPackages != null

&& pkg.mOriginalPackages.contains(oldName)

&& mPackages.containsKey(oldName)) {

// This package is derived from an original package,

// and this device has been updating from that original

// name. We must continue using the original name, so

// rename the new package here.

pkg.setPackageName(oldName);

pkgName = pkg.packageName;

replace = true;

} else if (mPackages.containsKey(pkgName)) {

// This package, under its official name, already exists

// on the device; we should replace it.

replace = true;

}

}

PackageSetting ps = mSettings.mPackages.get(pkgName);

if (ps != null) {

oldCodePath = mSettings.mPackages.get(pkgName).codePathString;

if (ps.pkg != null && ps.pkg.applicationInfo != null) {

systemApp = (ps.pkg.applicationInfo.flags &

ApplicationInfo.FLAG_SYSTEM) != 0;

}

}

}

if (systemApp && onSd) {

// Disable updates to system apps on sdcard

Slog.w(TAG, "Cannot install updates to system apps on sdcard");

res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;

return;

}

if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {

res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;

return;

}

// Set application objects path explicitly after the rename

setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());

pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();

if (replace) {

replacePackageLI(pkg, parseFlags, scanMode,

installerPackageName, res);

} else {

installNewPackageLI(pkg, parseFlags, scanMode,

installerPackageName,res);

}

}

最后判断如果以前不存在那么调用installNewPackageLI()

[java] view
plaincopy

private void installNewPackageLI(PackageParser.Package pkg,

int parseFlags,int scanMode,

String installerPackageName, PackageInstalledInfo res) {

***********************省略若干*************************************************

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

System.currentTimeMillis());

***********************省略若干**************************************************

}

最后终于回到了和开机安装一样的地方.与开机方式安装调用统一方法。

三、从ADB工具安装

其入口函数源文件为pm.java

(源文件路径:android\frameworks\base\cmds\pm\src\com\android\commands\pm\pm.java)

其中\system\framework\pm.jar 包管理库

包管理脚本 \system\bin\pm 解析

showUsage就是使用方法

[java] view
plaincopy

private static void showUsage() {

System.err.println("usage: pm [list|path|install|uninstall]");

System.err.println(" pm list packages [-f]");

System.err.println(" pm list permission-groups");

System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]");

System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");

System.err.println(" pm list features");

System.err.println(" pm path PACKAGE");

System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");

System.err.println(" pm uninstall [-k] PACKAGE");

System.err.println(" pm enable PACKAGE_OR_COMPONENT");

System.err.println(" pm disable PACKAGE_OR_COMPONENT");

System.err.println(" pm setInstallLocation [0/auto] [1/internal] [2/external]");

**********************省略**************************

}

安装时候会调用 runInstall()方法

[java] view
plaincopy

private void runInstall() {

int installFlags = 0;

String installerPackageName = null;

String opt;

while ((opt=nextOption()) != null) {

if (opt.equals("-l")) {

installFlags |= PackageManager.INSTALL_FORWARD_LOCK;

} else if (opt.equals("-r")) {

installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;

} else if (opt.equals("-i")) {

installerPackageName = nextOptionData();

if (installerPackageName == null) {

System.err.println("Error: no value specified for -i");

showUsage();

return;

}

} else if (opt.equals("-t")) {

installFlags |= PackageManager.INSTALL_ALLOW_TEST;

} else if (opt.equals("-s")) {

// Override if -s option is specified.

installFlags |= PackageManager.INSTALL_EXTERNAL;

} else if (opt.equals("-f")) {

// Override if -s option is specified.

installFlags |= PackageManager.INSTALL_INTERNAL;

} else {

System.err.println("Error: Unknown option: " + opt);

showUsage();

return;

}

}

String apkFilePath = nextArg();

System.err.println("\tpkg: " + apkFilePath);

if (apkFilePath == null) {

System.err.println("Error: no package specified");

showUsage();

return;

}

PackageInstallObserver obs = new PackageInstallObserver();

try {

mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,

installerPackageName);

synchronized (obs) {

while (!obs.finished) {

try {

obs.wait();

} catch (InterruptedException e) {

}

}

if (obs.result == PackageManager.INSTALL_SUCCEEDED) {

System.out.println("Success");

} else {

System.err.println("Failure ["

+ installFailureToString(obs.result)

+ "]");

}

}

} catch (RemoteException e) {

System.err.println(e.toString());

System.err.println(PM_NOT_RUNNING_ERR);

}

}

其中的

PackageInstallObserver obs = new PackageInstallObserver();

mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,

installerPackageName);

如果安装成功

obs.result == PackageManager.INSTALL_SUCCEEDED)

又因为有

IPackageManage mPm;

mPm = IpackageManager.Stub.asInterface(ServiceManager.getService("package"));

Stub是接口IPackageManage的静态抽象类,asInterface是返回IPackageManager代理的静态方法。

因为class PackageManagerService extends IPackageManager.Stub

所以mPm.installPackage 调用

/* Called when a downloaded package installation has been confirmed by the user */

public void installPackage(

final Uri packageURI, final IPackageInstallObserver observer, final int flags,final String installerPackageName)

这样就是从网络下载安装的入口了。

四,从SD卡安装

系统调用PackageInstallerActivity.java(/home/zhongda/androidSRC/vortex-8inch-for-hoperun/packages/apps/PackageInstaller/src/com/android/packageinstaller)

进入这个Activity会判断信息是否有错,然后调用

private void initiateInstall()判断是否曾经有过同名包的安装,或者包已经安装

通过后执行private void startInstallConfirm() 点击OK按钮后经过一系列的安装信息的判断Intent跳转到

[java] view
plaincopy

public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

Intent intent = getIntent();

mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);

mPackageURI = intent.getData();

initView();

}

方法中调用了initView()方法

[java] view
plaincopy

public void initView() {

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.op_progress);

int installFlags = 0;

PackageManager pm = getPackageManager();

try {

PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,

PackageManager.GET_UNINSTALLED_PACKAGES);

if(pi != null) {

installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;

}

} catch (NameNotFoundException e) {

}

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

Log.w(TAG, "Replacing package:" + mAppInfo.packageName);

}

PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, mAppInfo,

mPackageURI);

mLabel = as.label;

PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);

mStatusTextView = (TextView)findViewById(R.id.center_text);

mStatusTextView.setText(R.string.installing);

mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);

mProgressBar.setIndeterminate(true);

// Hide button till progress is being displayed

mOkPanel = (View)findViewById(R.id.buttons_panel);

mDoneButton = (Button)findViewById(R.id.done_button);

mLaunchButton = (Button)findViewById(R.id.launch_button);

mOkPanel.setVisibility(View.INVISIBLE);

String installerPackageName = getIntent().getStringExtra(

Intent.EXTRA_INSTALLER_PACKAGE_NAME);

PackageInstallObserver observer = new PackageInstallObserver();

pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);

}

方法最后我们可以看到再次调用安装接口完成安装。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: