android apk安装原理分析
参考了网上的资料和源码,肤浅分析了下,不够深入。
AndroidAPK安装原理分析
一、概述
APK是AndroidPackage的缩写,即Android安装包。
APK安装可以通过以下四种方式:
1.系统应用安装,开机时完成系统应用的检查,没安装就安装,安装就跳过,无安装界面。
2.网络下载应用安装,通过market应用完成,无安装界面。
3.ADB工具安装,无安装界面
4.通过SD卡来安装apk,有安装界面,由packageinstaller.apk应用处理安装及卸载过程的界面。
应用安装涉及到如下几个目录:
system/app系统自带的应用程序,无法删除
data/app用户程序安装的目录,有删除权限
data/data存放应用程序的数据
Data/dalvik-cache将apk中的dex文件安装到dalvik-cache目录下
(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)
二、系统应用安装
PackageManagerService处理各种应用的安装,卸载,管理等工作,系统启动时由systemServer开启此服务。
@frameworks/base/services/java/com/android/server/SystemServer.java
...
Slog.i(TAG,"PackageManager");
pm=PackageManagerService.main(context,factoryTest!=SystemServer.FACTORY_TEST_OFF);
...
@frameworks/base/services/java/com/android/server/PackageManagerService.java
publicstaticfinalIPackageManagermain(Contextcontext,booleanfactoryTest){
PackageManagerServicem=newPackageManagerService(context,factoryTest);
ServiceManager.addService("package",m);//将PackageManagerService添加到ServiceManager中。
returnm;
}
上面new一个PackageManagerService出来,当然首先调用的函数是他的构造函数了,那么系统应用都是在这个函数中完成安装的。
publicPackageManagerService(Contextcontext,booleanfactoryTest){
...
addBootEvent(newString("Android:PackageManagerService_Start"));
mNoDexOpt="eng".equals(SystemProperties.get("ro.build.type"));
...
Installerinstaller=newInstaller();
//这个Installer类其实是利用socket和installd(c语言)通讯来实现安装,关于installd的实现另外分析。
if(installer.ping()&&Process.supportsProcesses()){
mInstaller=installer;
}else{
mInstaller=null;
}
...
FiledataDir=Environment.getDataDirectory();
mAppDataDir=newFile(dataDir,"data");///data/data
mSecureAppDataDir=newFile(dataDir,"secure/data");///data/secure/data
mDrmAppPrivateInstallDir=newFile(dataDir,"app-private");///data/app-private
...
longstartTime=SystemClock.uptimeMillis();
addBootEvent(newString("Android:PMS_scan_START"));
...
intscanMode=SCAN_MONITOR|SCAN_NO_PATHS;
if(mNoDexOpt){
Slog.w(TAG,"RunningENGbuild:nopre-dexopt!");
scanMode|=SCAN_NO_DEX;
}
...
mFrameworkDir=newFile(Environment.getRootDirectory(),"framework");///system/framework
mDalvikCacheDir=newFile(dataDir,"dalvik-cache");///data/dalvik-cache
//对apk中的DEX文件进行安装时优化,优化之后的ODEX文件存放在/data/dalvik-cache。
...
//类PackageManagerService的内部类AppDirObserver实现了监听某个目录的功能,当把某个apk或者jar包放入被监听的目录,
将会被自动安装。
mFrameworkInstallObserver=newAppDirObserver(mFrameworkDir.getPath(),OBSERVER_EVENTS,true);
//新建一个目录监听的对象
mFrameworkInstallObserver.startWatching();//开始监听这个目录
scanDirLI(mFrameworkDir,PackageParser.PARSE_IS_SYSTEM|PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode|SCAN_NO_DEX,0);//扫描安装“system\framework”目录下的jar包。
addBootEvent(newString("Android:PMS_scan_done:"+mFrameworkDir.getPath().toString()));
//扫描安装“system\app”目录下的各个系统应用。
mSystemAppDir=newFile(Environment.getRootDirectory(),"app");
mSystemInstallObserver=newAppDirObserver(mSystemAppDir.getPath(),OBSERVER_EVENTS,true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir,PackageParser.PARSE_IS_SYSTEM
|PackageParser.PARSE_IS_SYSTEM_DIR,scanMode,0);
addBootEvent(newString("Android:PMS_scan_done:"+mSystemAppDir.getPath().toString()));
//扫描安装"/vendor/app"下的各应用,源码和上面类似
...
addBootEvent(newString("Android:PMS_scan_done:"+mVendorAppDir.getPath().toString()));
//Prune删除anysystempackagesthatnolongerexist.
...
mAppInstallDir=newFile(dataDir,"app");
addBootEvent(newString("Android:PMS_scan_data_start"));
mAppInstallObserver=newAppDirObserver(mAppInstallDir.getPath(),OBSERVER_EVENTS,false);
mAppInstallObserver.startWatching();
scanDirLI(mAppInstallDir,0,scanMode,0);//扫描安装/data/app下面的应用。
addBootEvent(newString("Android:PMS_scan_data_done:"+mAppInstallDir.getPath().toString()));
//扫描安装"data\app-private"目录,即安装DRM保护的APK文件。
mDrmAppInstallObserver=newAppDirObserver(mDrmAppPrivateInstallDir.getPath(),OBSERVER_EVENTS,false);
mDrmAppInstallObserver.startWatching();
scanDirLI(mDrmAppPrivateInstallDir,PackageParser.PARSE_FORWARD_LOCK,scanMode,0);
addBootEvent(newString("Android:PMS_scan_data_done:"+mDrmAppPrivateInstallDir.getPath().toString()));
...
addBootEvent(newString("Android:PMS_scan_END"));
Slog.i(TAG,"Timetoscanpackages:"+((SystemClock.uptimeMillis()-startTime)/1000f)+"seconds");
...
}
以上可以看出,主要的工作是由函数scanDirLI()来完成的。
privatevoidscanDirLI(Filedir,intflags,intscanMode,longcurrentTime){
String[]files=dir.list();
if(files==null){
Log.d(TAG,"Nofilesinappdir"+dir);
return;
}
if(false){
Log.d(TAG,"Scanningappdir"+dir);
}
inti;
for(i=0;i<files.length;i++){
Filefile=newFile(dir,files[i]);//构造apk的全路径名字
if(!isPackageFilename(files[i])){//检查是否是以.apk结尾的文件,不是就跳过。
//Ignoreentrieswhicharenotapk's
continue;
}
//安装apk文件,安装包解析器
PackageParser.Packagepkg=scanPackageLI(file,flags|PackageParser.PARSE_MUST_BE_APK,scanMode,currentTime);
//Don'tmessaroundwithappsinsystempartition.
if(pkg==null&&(flags&PackageParser.PARSE_IS_SYSTEM)==0&&
mLastScanError==PackageManager.INSTALL_FAILED_INVALID_APK){
//Deletetheapk
Slog.w(TAG,"Cleaningupfailedinstallof"+file);
file.delete();
}
}
}
scanPackageLI函数扫描一个package,同时返回一个新解析出来的包。
privatePackageParser.PackagescanPackageLI(FilescanFile,
intparseFlags,intscanMode,longcurrentTime){
...
finalPackageParser.Packagepkg=pp.parsePackage(scanFile,
scanPath,mMetrics,parseFlags);
...
codePath=pkg.mScanPath;
//Setapplicationobjectspathexplicitly.
setApplicationInfoPaths(pkg,codePath,resPath);
//通过解析安装包parsePackage获取到安装包的信息结构。
returnscanPackageLI(pkg,parseFlags,scanMode|SCAN_UPDATE_SIGNATURE,currentTime);
}
privatestaticvoidsetApplicationInfoPaths(PackageParser.Packagepkg,StringdestCodePath,
StringdestResPath){
pkg.mPath=pkg.mScanPath=destCodePath;
pkg.applicationInfo.sourceDir=destCodePath;
pkg.applicationInfo.publicSourceDir=destResPath;
}
privatePackageParser.PackagescanPackageLI(PackageParser.Packagepkg,
intparseFlags,intscanMode,longcurrentTime){
FilescanFile=newFile(pkg.mScanPath);
//ProtectforGMS2.2NetworkLocation.apkwillcausesystemcan'tbootnormally
//onAndroid2.3above
if(pkg.packageName.equalsIgnoreCase("com.google.android.location")
&&(mSdkVersion>=9)&&(pkg.applicationInfo.targetSdkVersion<=){
Slog.w(TAG,"TargetSDKversion:"+mSdkVersion);
Slog.w(TAG,"ApplicationSDKversion:"+pkg.applicationInfo.targetSdkVersion);
Slog.w(TAG,"AppSDKversionisincompatiblewithTargetSDKversion,installfailed.");
returnnull;
}
...
//Initializepackagesourceandresourcedirectories
...
//Checkallsharedlibrariesandmaptotheiractualfilepath.
...
//Checkifwearerenamingfromanoriginalpackagename.
...
...
...
//最后会通过下面这个函数来实现真正的安装。
FiledataPath;
if(mPlatformPackage==pkg){
...
}else{
//Thisisanormalpackage,needtomakeitsdatadirectory.
booleanuseEncryptedFSDir=useEncryptedFilesystemForPackage(pkg);
dataPath=getDataPathForPackage(pkg);
if(dataPath.exists()){
...
mInstaller.install(pkgName,useEncryptedFSDir,pkg.applicationInfo.uid,pkg.applicationInfo.uid);
...
}else{
...
intret=mInstaller.install(pkgName,useEncryptedFSDir,pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
...
}
...
}
Installer类的实现位于文件:frameworks/base/services/java/com/android/server/Installer.java
该类实现的只是一个接口,使用socket来和c代码实现的installd进行通信,实际真正完成安装任务的installd服务
其源码位于:frameworks/base/cmds/installd
这部分在另外一篇文章中分析。
三、从Market上下载应用
GoogleMarket应用需要使用gmail账户登录才可以使用,选择某一应用后,开始下载安装包,此过程中,在手机通知栏有进度条提示,下载完成后,会自动调用Packagemanager的接口安装,调用接口如下:
publicvoidinstallPackage(finalUripackageURI,finalIPackageInstallObserverobserver,finalintflags)
finalUripackageURI:文件下载完成后保存的路径
finalIPackageInstallObserverobserver:返回的安装结果
finalintflags:安装的参数,从market上下载的应用,安装参数为-r(replace)
@frameworks/base/services/java/com/android/server/PackageManagerService.java
/*Calledwhenadownloadedpackageinstallationhasbeenconfirmedbytheuser*/
publicvoidinstallPackage(finalUripackageURI,finalIPackageInstallObserverobserver,finalintflags){
installPackage(packageURI,observer,flags,null);
}
/*Calledwhenadownloadedpackageinstallationhasbeenconfirmedbytheuser*/
publicvoidinstallPackage(finalUripackageURI,finalIPackageInstallObserverobserver,finalintflags,
finalStringinstallerPackageName){
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,null);
Messagemsg=mHandler.obtainMessage(INIT_COPY);
msg.obj=newInstallParams(packageURI,observer,flags,
installerPackageName);
mHandler.sendMessage(msg);//安装过程非常耗时,所以使用了android的handler消息机制。
}
首先,参数封装成INIT_COPYmessage,发到handlerThread,handlerThread收到message后,将参数排队到mPendingInstalls中.
随后,MCS_BOUND流程将会处理这个队列,MCS_BOUND的整个安装流程借助了几个InstallParams和InstallArgs完成其中的参数和安装结果的传递,最终会调用processPendingInstall(),进而调用到install过程的核心installPackageLI()。
handleMessage()
-->doHandleMessage()
-->params.startCopy()
-->handleReturnCode()
-->processPendingInstall(mArgs,mRet)
-->installPackageLI(args,true,res);考虑了是否是新安装还是覆盖安装的情况。
privatevoidinstallPackageLI(InstallArgsargs,
booleannewInstall,PackageInstalledInfores){
...
booleanonSd=((pFlags&PackageManager.INSTALL_EXTERNAL)!=0);
booleanreplace=false;
...
res.returnCode=PackageManager.INSTALL_SUCCEEDED;
...
//RetrievePackageSettingsandparsepackage
...
//Getridofallreferencestopackagescanpathviaparser.
...
if(replace){
replacePackageLI(pkg,parseFlags,scanMode,
installerPackageName,res);//覆盖安装
}else{
installNewPackageLI(pkg,parseFlags,scanMode,
installerPackageName,res);//新安装
}
...
}
不管是新安装还是覆盖安装都会调用到函数scanPackageLI()中去,这个之后的流程就和系统应用一样了。
三、ADB安装apk
可以用3种方式来使用adb安装apk,直接push,adbinstall,abdshellpm。
直接push到system或者data的app目录,系统有实现自动监测这些app目录,只要有apk拷贝进来,会自动安装。
而后两种实现基本相同,adbinstll的函数入口是frameworks/base/cmds/pm/src/com/android/commands/pm/pm.java,
adbshellpm实际上执行的一个linux命令,该命令没有源码,只有ELF文件位于目录frameworks/base/cmds/pm,编译的时候拷贝到system/bin目录下。见Android.mk文件:
include$(CLEAR_VARS)
ALL_PREBUILT+=$(TARGET_OUT)/bin/pm
$(TARGET_OUT)/bin/pm:$(LOCAL_PATH)/pm|$(ACP)
$(transform-prebuilt-to-target)
分析pm.java的源码可以看出,pm.java中的各个函数的功能都是请求服务PackageManagerService中的接口来完成的。
publicvoidrun(String[]args){
mPm=IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
...
if("install".equals(op)){
runInstall();
return;
}
...
}
privatevoidrunInstall(){
intinstallFlags=0;
StringinstallerPackageName=null;
Stringopt;
while((opt=nextOption())!=null){
if(opt.equals("-l")){
installFlags|=PackageManager.INSTALL_FORWARD_LOCK;
}elseif(opt.equals("-r")){//指定覆盖安装
installFlags|=PackageManager.INSTALL_REPLACE_EXISTING;
}elseif(opt.equals("-i")){
installerPackageName=nextOptionData();
if(installerPackageName==null){
System.err.println("Error:novaluespecifiedfor-i");
showUsage();
return;
}
}elseif(opt.equals("-t")){
installFlags|=PackageManager.INSTALL_ALLOW_TEST;
}elseif(opt.equals("-s")){
//指定安装至外部存储中,SD卡
installFlags|=PackageManager.INSTALL_EXTERNAL;
}elseif(opt.equals("-f")){
//指定安装到内部flash
installFlags|=PackageManager.INSTALL_INTERNAL;
}else{
System.err.println("Error:Unknownoption:"+opt);
showUsage();
return;
}
}
StringapkFilePath=nextArg();
System.err.println("\tpkg:"+apkFilePath);
if(apkFilePath==null){
System.err.println("Error:nopackagespecified");
showUsage();
return;
}
PackageInstallObserverobs=newPackageInstallObserver();
try{
mPm.installPackage(Uri.fromFile(newFile(apkFilePath)),obs,installFlags,installerPackageName);
//调用了和从Market下载的应用安装相同的接口
...
}catch(RemoteExceptione){
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
}
}
另外除了在pm后面跟上-s或者-f来指定apk安装位置之外,还可以使用pm的另外一个选项来指定:
adbshellpmsetInstallLocation0/1/2
0[auto]:Letsystemdecidethebestlocation
1[internal]:Installoninternaldevicestorage
2[external]:Installonexternalmedia
而adbshellpmgetInstallLocation可以得到当前的apk安装位置。
这是在命令行这样设置,然后再用adbinstall安装,或者直接在adbshellpm-r-sinstall来安装。
其实上面的设置如果要在代码中使用的话,可以在pm.java中找到可用接口runSetInstallLocation(),他实际上是调用了服务
PackageManagerService的接口:
publicbooleansetInstallLocation(intloc){
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS,null);
if(getInstallLocation()==loc){
returntrue;
}
if(loc==PackageHelper.APP_INSTALL_AUTO||
loc==PackageHelper.APP_INSTALL_INTERNAL||
loc==PackageHelper.APP_INSTALL_EXTERNAL){
android.provider.Settings.System.putInt(mContext.getContentResolver(),
android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION,loc);
returntrue;
}
returnfalse;
}
为了使apk可以使用app2sd工具来转移,那么在写代码的时候,需要有下面信息存在:
1.首先让你的程序支持SD卡上安装必须具备设置APILevel至少为8,即androidmanifest.xml的中android:minSdkVersion至少为8这样你的APK最终运行时兼容的固件只有2.2了,同时在androidmanifest.xml文件的根节点中必须加入android:installLocation这个属性,类似代码如下:
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
...>
2.android:installLocation的值主要有preferExternal、auto和internalOnly这三个选项,通常我们设置为preferExternal可以优先推荐应用安装到SD卡上,当然最终用户可以选择为内部的ROM存储上,如果外部存储已满,Android内部也会安装到内部存储上,auto将会根据存储空间自适应,当然还有一些应用可能会有特殊的目的,他们一般必须安装在内部存储才能可靠运行,设置为internalOnly比较合适,主要体现在:
Services服务
AlarmServices闹铃提醒服务
InputMethodEngines输入法引擎
LiveWallpapers活动壁纸
LiveFolders活动文件夹
AppWidgetsWidget
AccountManagers账户管理
SyncAdapters同步适配器
DeviceAdministrators设备管理器
那么哪些应用适合安装在SD卡中呢?Android开发网建议一些占用资源比较大的游戏,比如大于3MB的单个文件,不需要长期驻留内存的应用,不具备提醒和实时监控的应用一般放到SD卡上比较合适,不过目前想让你的应用装到SD卡上,必须设置APILevel至少为8以上,同时显示注明android:installLocation。
四、安装sd卡上的apk
把APK安装包保存在SD卡中,从手机里访问SD卡中的APK安装包,点击就可以启动安装界面,系统应用Packageinstaller.apk处理这种方式下的安装及卸载。
PackageInstallerActivity负责解析包,判断是否是可用的Apk文件
创建临时安装文件/data/data/com.android.packageinstaller/files/ApiDemos.apk
并启动安装确认界面startInstallConfirm,列出解析得到的该应用基本信息。如果手机上已安装有同名应用,则需要用户确认是否要替换安装。
确认安装后,启动InstallAppProgress,调用安装接口完成安装。
pm.installPackage(mPackageURI,observer,installFlags);
五、其他
data/system/packages.xml文件中,保存了手机上所有已安装应用的基本信息,如安装路径,申请的permission等信息。
参考网址:
http://topic.csdn.net/u/20110410/23/43571cfa-87b2-4e36-880c-1fa499ba32b0.html
PackageInstaller原理简述
http://www.dayoo.com/roll/201009/25/10000307_103589200.htm
在Android2.2上设置程序默认安装SD卡
http://www.cnblogs.com/wisekingokok/archive/2011/08/26/2154505.html
让Android应用程序支持安装到SD卡(APP2SD)
http://www.61ic.com/Mobile/Android/201106/35012.html
AndroidAPK安装到SD卡
http://www.ylmf.net/android/tips/2011012421766.html
Android软件从手机内存转移到存储卡
http://www.cnblogs.com/youxilua/archive/2011/11/25/2263825.html
androidHandler机制研究学习笔记
filelist:
1Pm.java(frameworks\base\cmds\pm\src\com\android\commands\pm):2
2PackageManagerService.java(frameworks\base\services\java\com\android\server)
3Pm.java(frameworks\base\cmds\pm\src\com\android\commands\pm)
4PackageManager.java(frameworks\base\core\java\android\content\pm)
5SearchResults
6Handler.java(frameworks\base\core\java\android\os)
7MountService.java(frameworks\base\services\java\com\android\server)
8Message.java(frameworks\base\core\java\android\os)
9HandlerThread.java(frameworks\base\core\java\android\os)
0ServiceConnection.java(frameworks\base\core\java\android\content)
AProcess.java(frameworks\base\core\java\android\os)
BContext.java(frameworks\base\core\java\android\content)
CInstallAppProgress.java(packages\apps\packageinstaller\src\com\android\packageinstaller)
DInstaller.java(frameworks\base\services\java\com\android\server)
EInstaller.java(frameworks\base\services\java\com\android\server):2
FLocalSocket.java(frameworks\base\core\java\android\net)
GLocalSocketImpl.java(frameworks\base\core\java\android\net)
HLocalSocketAddress.java(frameworks\base\core\java\android\net)
IPackageParser.java(frameworks\base\core\java\android\content\pm)
JSystem.java(libcore\luni\src\main\java\java\lang)
KEnvironment.java(frameworks\base\core\java\android\os)
LFile.java(libcore\luni\src\main\java\java\io)
MServiceManager.java(frameworks\base\core\java\android\os)
NServiceManager.java(frameworks\base\tools\layoutlib\bridge\src\android\os)
OSystemServer.java(frameworks\base\services\java\com\android\server)
PActivityManagerService.java(frameworks\base\services\java\com\android\server\am)
QPackageHelper.java(frameworks\base\core\java\com\android\internal\content)
RPackageManagerTests.java(frameworks\base\core\tests\coretests\src\android\content\pm)
SSettings.java(frameworks\base\core\java\android\provider)
TApplicationSettings.java(packages\apps\settings\src\com\android\settings)
UDatabaseHelper.java(frameworks\base\packages\settingsprovider\src\com\android\providers\settings)
VPackageManagerHostTestUtils.java(frameworks\base\core\tests\hosttests\src\android\content\pm)
WFileObserver.java(frameworks\base\core\java\android\os)
相关推荐
adb shell cd system/app rm *.apk21. 获取管理员权限: adb root22. 启动Activity: adb shell am start -n 包名/包名+类名。