FROM:http://blog.csdn.net/hdhd588/article/details/6739281


=============================================================


应用程序包的安装是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 plain copy
  1. // Find base frameworks (resource packages without code).  
  2.            mFrameworkInstallObserver = new AppDirObserver(  
  3.                mFrameworkDir.getPath(), OBSERVER_EVENTS, true);  
  4.            mFrameworkInstallObserver.startWatching();  
  5.            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM  
  6.                    | PackageParser.PARSE_IS_SYSTEM_DIR,  
  7.                    scanMode | SCAN_NO_DEX, 0);  

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

[java]  view plain copy
  1. // Collect all system packages.  
  2.           mSystemAppDir = new File(Environment.getRootDirectory(), "app");  
  3.           mSystemInstallObserver = new AppDirObserver(  
  4.               mSystemAppDir.getPath(), OBSERVER_EVENTS, true);  
  5.           mSystemInstallObserver.startWatching();  
  6.           scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM  
  7.                   | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  

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

[java]  view plain copy
  1. // Collect all vendor packages.  
  2.            mVendorAppDir = new File("/vendor/app");  
  3.            mVendorInstallObserver = new AppDirObserver(  
  4.                mVendorAppDir.getPath(), OBSERVER_EVENTS, true);  
  5.            mVendorInstallObserver.startWatching();  
  6.            scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM  
  7.                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  

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

[java]  view plain copy
  1. scanDirLI(mAppInstallDir, 0, scanMode, 0);  

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

[java]  view plain copy
  1. scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,  
  2.                     scanMode, 0);  

扫描方法的代码清单

[java]  view plain copy
  1. private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {  
  2.         String[] files = dir.list();  
  3.         if (files == null) {  
  4.             Log.d(TAG, "No files in app dir " + dir);  
  5.             return;  
  6.         }  
  7.         if (false) {  
  8.             Log.d(TAG, "Scanning app dir " + dir);  
  9.         }  
  10.         int i;  
  11.         for (i=0; i
  12.             File file = new File(dir, files[i]);  
  13.             if (!isPackageFilename(files[i])) {  
  14.                 // Ignore entries which are not apk's  
  15.                 continue;  
  16.             }  
  17.             PackageParser.Package pkg = scanPackageLI(file,  
  18.                     flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);  
  19.             // Don't mess around with apps in system partition.  
  20.             if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&  
  21.                     mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {  
  22.                 // Delete the apk  
  23.                 Slog.w(TAG, "Cleaning up failed install of " + file);  
  24.                 file.delete();  
  25.             }  
  26.         }  
  27.     }  

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

private PackageParser.Package scanPackageLI(File scanFile,

int parseFlags, int scanMode, long currentTime)

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

[java]  view plain copy
  1. if (mInstaller != null) {  
  2.                     int ret = mInstaller.install(pkgName, useEncryptedFSDir,  pkg.applicationInfo.uid,pkg.applicationInfo.uid);  
  3.                     if(ret < 0) {  
  4.                         // Error from installer  
  5.                         mLastScanError =    PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  6.                         return null;  
  7.                     }  
  8.                 }  

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 plain copy
  1. public void installPackage(  
  2.            final Uri packageURI, final IPackageInstallObserver observer, final int flags,  
  3.            final String installerPackageName) {  
  4.        mContext.enforceCallingOrSelfPermission(  
  5.                android.Manifest.permission.INSTALL_PACKAGES, null);  
  6.        Message msg = mHandler.obtainMessage(INIT_COPY);  
  7.        msg.obj = new InstallParams(packageURI, observer, flags,  
  8.                installerPackageName);  
  9.        mHandler.sendMessage(msg);  
  10.    }  

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

[java]  view plain copy
  1. class PackageHandler extends Handler{  
  2.                    
  3. *****************省略若干********************  
  4.          public void handleMessage(Message msg) {  
  5.             try {  
  6.                 doHandleMessage(msg);  
  7.             } finally {  
  8.                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  9.             }  
  10.         }  
  11.    ******************省略若干**********************  
  12.  }  

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

[java]  view plain copy
  1.  void doHandleMessage(Message msg) {  
  2.             switch (msg.what) {  
  3.              
  4.                 case INIT_COPY: {  
  5.                     if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");  
  6.                     HandlerParams params = (HandlerParams) msg.obj;  
  7.                     int idx = mPendingInstalls.size();  
  8.                     if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);  
  9.                     // If a bind was already initiated we dont really  
  10.                     // need to do anything. The pending install  
  11.                     // will be processed later on.  
  12.                     if (!mBound) {  
  13.                         // If this is the only one pending we might  
  14.                         // have to bind to the service again.  
  15.                         if (!connectToService()) {  
  16.                             Slog.e(TAG, "Failed to bind to media container service");  
  17.                             params.serviceError();  
  18.                             return;  
  19.                         } else {  
  20.                             // Once we bind to the service, the first  
  21.                             // pending request will be processed.  
  22.                             mPendingInstalls.add(idx, params);  
  23.                         }  
  24.                     } else {  
  25.                         mPendingInstalls.add(idx, params);  
  26.                         // Already bound to the service. Just make  
  27.                         // sure we trigger off processing the first request.  
  28.                         if (idx == 0) {  
  29.                             mHandler.sendEmptyMessage(MCS_BOUND);  
  30.                         }  
  31.                     }  
  32.                     break;  
  33.                 }  
  34.                 case MCS_BOUND: {  
  35.                     if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");  
  36.                     if (msg.obj != null) {  
  37.                         mContainerService = (IMediaContainerService) msg.obj;  
  38.                     }  
  39.                     if (mContainerService == null) {  
  40.                         // Something seriously wrong. Bail out  
  41.                         Slog.e(TAG, "Cannot bind to media container service");  
  42.                         for (HandlerParams params : mPendingInstalls) {  
  43.                             mPendingInstalls.remove(0);  
  44.                             // Indicate service bind error  
  45.                             params.serviceError();  
  46.                         }  
  47.                         mPendingInstalls.clear();  
  48.                     } else if (mPendingInstalls.size() > 0) {  
  49.                         HandlerParams params = mPendingInstalls.get(0);  
  50.                         if (params != null) {  
  51.                             params.startCopy();  
  52.                         }  
  53.                     } else {  
  54.                         // Should never happen ideally.  
  55.                         Slog.w(TAG, "Empty queue");  
  56.                     }  
  57.                     break;  
  58.                 }  
  59.             ****************省略若干**********************  
  60. }  
  61. }               

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 plain copy
  1. @Override  
  2.        void handleReturnCode() {  
  3.            // If mArgs is null, then MCS couldn't be reached. When it  
  4.            // reconnects, it will try again to install. At that point, this  
  5.            // will succeed.  
  6.            if (mArgs != null) {  
  7.                processPendingInstall(mArgs, mRet);  
  8.            }  
  9.        }  

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

其中run()方法如下

[java]  view plain copy
  1. run(){  
  2. synchronized (mInstallLock) {  
  3.                         ************省略*****************  
  4.                         installPackageLI(args, true, res);  
  5.                      
  6.  }  
  7. }  
  8. instaPacakgeLI()args,res参数分析  

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

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

  

[java]  view plain copy
  1. static abstract class InstallArgs {  
  2. *********************************************************************  
  3. 其中定义了flag标志,packageURL,创建文件,拷贝apk,修改包名称,  
  4.                     还有一些删除文件的清理,释放存储函数。  
  5.     *********************************************************************  
  6. }  
  7.   class PackageInstalledInfo {  
  8.         String name;  
  9.         int uid;  
  10.         PackageParser.Package pkg;  
  11.         int returnCode;  
  12.         PackageRemovedInfo removedInfo;  
  13.  }  

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

[java]  view plain copy
  1. private void installPackageLI(InstallArgs args,  
  2.           boolean newInstall, PackageInstalledInfo res) {  
  3.       int pFlags = args.flags;  
  4.       String installerPackageName = args.installerPackageName;  
  5.       File tmpPackageFile = new File(args.getCodePath());  
  6.       boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);  
  7.       boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);  
  8.       boolean replace = false;  
  9.       int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE  
  10.               | (newInstall ? SCAN_NEW_INSTALL : 0);  
  11.       // Result object to be returned  
  12.       res.returnCode = PackageManager.INSTALL_SUCCEEDED;  
  13.       // Retrieve PackageSettings and parse package  
  14.       int parseFlags = PackageParser.PARSE_CHATTY |  
  15.       (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |  
  16.       (onSd ? PackageParser.PARSE_ON_SDCARD : 0);  
  17.       parseFlags |= mDefParseFlags;  
  18.       PackageParser pp = new PackageParser(tmpPackageFile.getPath());  
  19.       pp.setSeparateProcesses(mSeparateProcesses);  
  20.       final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,  
  21.               null, mMetrics, parseFlags);  
  22.       if (pkg == null) {  
  23.           res.returnCode = pp.getParseError();  
  24.           return;  
  25.       }  
  26.       String pkgName = res.name = pkg.packageName;  
  27.       if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {  
  28.           if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {  
  29.               res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;  
  30.               return;  
  31.           }  
  32.       }  
  33.       if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {  
  34.           res.returnCode = pp.getParseError();  
  35.           return;  
  36.       }  
  37.       // Get rid of all references to package scan path via parser.  
  38.       pp = null;  
  39.       String oldCodePath = null;  
  40.       boolean systemApp = false;  
  41.       synchronized (mPackages) {  
  42.           // Check if installing already existing package  
  43.           if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {  
  44.               String oldName = mSettings.mRenamedPackages.get(pkgName);  
  45.               if (pkg.mOriginalPackages != null  
  46.                       && pkg.mOriginalPackages.contains(oldName)  
  47.                       && mPackages.containsKey(oldName)) {  
  48.                   // This package is derived from an original package,  
  49.                   // and this device has been updating from that original  
  50.                   // name.  We must continue using the original name, so  
  51.                   // rename the new package here.  
  52.                   pkg.setPackageName(oldName);  
  53.                   pkgName = pkg.packageName;  
  54.                   replace = true;  
  55.               } else if (mPackages.containsKey(pkgName)) {  
  56.                   // This package, under its official name, already exists  
  57.                   // on the device; we should replace it.  
  58.                   replace = true;  
  59.               }  
  60.           }  
  61.           PackageSetting ps = mSettings.mPackages.get(pkgName);  
  62.           if (ps != null) {  
  63.               oldCodePath = mSettings.mPackages.get(pkgName).codePathString;  
  64.               if (ps.pkg != null && ps.pkg.applicationInfo != null) {  
  65.                   systemApp = (ps.pkg.applicationInfo.flags &  
  66.                           ApplicationInfo.FLAG_SYSTEM) != 0;  
  67.               }  
  68.           }  
  69.       }  
  70.       if (systemApp && onSd) {  
  71.           // Disable updates to system apps on sdcard  
  72.           Slog.w(TAG, "Cannot install updates to system apps on sdcard");  
  73.           res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;  
  74.           return;  
  75.       }  
  76.       if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {  
  77.           res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  78.           return;  
  79.       }  
  80.       // Set application objects path explicitly after the rename  
  81.       setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());  
  82.       pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();  
  83.       if (replace) {  
  84.           replacePackageLI(pkg, parseFlags, scanMode,  
  85.                   installerPackageName, res);  
  86.       } else {  
  87.           installNewPackageLI(pkg, parseFlags, scanMode,  
  88.                   installerPackageName,res);  
  89.       }  
  90.   }  

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

[java]  view plain copy
  1. private void installNewPackageLI(PackageParser.Package pkg,  
  2.             int parseFlags,int scanMode,  
  3.             String installerPackageName, PackageInstalledInfo res) {  
  4.      ***********************省略若干*************************************************  
  5.         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
  6.                System.currentTimeMillis());  
  7.      ***********************省略若干**************************************************    
  8. }  

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

三、从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 plain copy
  1. private static void showUsage() {   
  2.         System.err.println("usage: pm [list|path|install|uninstall]");   
  3.         System.err.println("       pm list packages [-f]");   
  4.         System.err.println("       pm list permission-groups");   
  5.         System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");   
  6.         System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");   
  7.         System.err.println("       pm list features");   
  8.         System.err.println("       pm path PACKAGE");   
  9.         System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");   
  10.         System.err.println("       pm uninstall [-k] PACKAGE");   
  11.         System.err.println("       pm enable PACKAGE_OR_COMPONENT");   
  12.         System.err.println("       pm disable PACKAGE_OR_COMPONENT");   
  13.         System.err.println("       pm setInstallLocation [0/auto] [1/internal] [2/external]");  
  14.       **********************省略**************************  
  15.    }  

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

[java]  view plain copy
  1. private void runInstall() {  
  2.       int installFlags = 0;  
  3.       String installerPackageName = null;  
  4.       String opt;  
  5.       while ((opt=nextOption()) != null) {  
  6.           if (opt.equals("-l")) {  
  7.               installFlags |= PackageManager.INSTALL_FORWARD_LOCK;  
  8.           } else if (opt.equals("-r")) {  
  9.               installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;  
  10.           } else if (opt.equals("-i")) {  
  11.               installerPackageName = nextOptionData();  
  12.               if (installerPackageName == null) {  
  13.                   System.err.println("Error: no value specified for -i");  
  14.                   showUsage();  
  15.                   return;  
  16.               }  
  17.           } else if (opt.equals("-t")) {  
  18.               installFlags |= PackageManager.INSTALL_ALLOW_TEST;  
  19.           } else if (opt.equals("-s")) {  
  20.               // Override if -s option is specified.  
  21.               installFlags |= PackageManager.INSTALL_EXTERNAL;  
  22.           } else if (opt.equals("-f")) {  
  23.               // Override if -s option is specified.  
  24.               installFlags |= PackageManager.INSTALL_INTERNAL;  
  25.           } else {  
  26.               System.err.println("Error: Unknown option: " + opt);  
  27.               showUsage();  
  28.               return;  
  29.           }  
  30.       }  
  31.       String apkFilePath = nextArg();  
  32.       System.err.println("\tpkg: " + apkFilePath);  
  33.       if (apkFilePath == null) {  
  34.           System.err.println("Error: no package specified");  
  35.           showUsage();  
  36.           return;  
  37.       }  
  38.       PackageInstallObserver obs = new PackageInstallObserver();  
  39.       try {  
  40.           mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,  
  41.                   installerPackageName);  
  42.           synchronized (obs) {  
  43.               while (!obs.finished) {  
  44.                   try {  
  45.                       obs.wait();  
  46.                   } catch (InterruptedException e) {  
  47.                   }  
  48.               }  
  49.               if (obs.result == PackageManager.INSTALL_SUCCEEDED) {  
  50.                   System.out.println("Success");  
  51.               } else {  
  52.                   System.err.println("Failure ["  
  53.                           + installFailureToString(obs.result)  
  54.                           + "]");  
  55.               }  
  56.           }  
  57.       } catch (RemoteException e) {  
  58.           System.err.println(e.toString());  
  59.           System.err.println(PM_NOT_RUNNING_ERR);  
  60.       }  
  61.   }  

其中的   

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 plain copy
  1. public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener  
  2.    public void onCreate(Bundle icicle) {  
  3.         super.onCreate(icicle);  
  4.         Intent intent = getIntent();  
  5.         mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);  
  6.         mPackageURI = intent.getData();  
  7.         initView();  
  8.     }  

方法中调用了initView()方法 

[java]  view plain copy
  1. public void initView() {  
  2.        requestWindowFeature(Window.FEATURE_NO_TITLE);  
  3.        setContentView(R.layout.op_progress);  
  4.        int installFlags = 0;  
  5.        PackageManager pm = getPackageManager();  
  6.        try {  
  7.            PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,   
  8.                    PackageManager.GET_UNINSTALLED_PACKAGES);  
  9.            if(pi != null) {  
  10.                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;  
  11.            }  
  12.        } catch (NameNotFoundException e) {  
  13.        }  
  14.        if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {  
  15.            Log.w(TAG, "Replacing package:" + mAppInfo.packageName);  
  16.        }  
  17.        PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, mAppInfo,  
  18.                mPackageURI);  
  19.        mLabel = as.label;  
  20.        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);  
  21.        mStatusTextView = (TextView)findViewById(R.id.center_text);  
  22.        mStatusTextView.setText(R.string.installing);  
  23.        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);  
  24.        mProgressBar.setIndeterminate(true);  
  25.        // Hide button till progress is being displayed  
  26.        mOkPanel = (View)findViewById(R.id.buttons_panel);  
  27.        mDoneButton = (Button)findViewById(R.id.done_button);  
  28.        mLaunchButton = (Button)findViewById(R.id.launch_button);  
  29.        mOkPanel.setVisibility(View.INVISIBLE);  
  30.        String installerPackageName = getIntent().getStringExtra(  
  31.                Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
  32.        PackageInstallObserver observer = new PackageInstallObserver();  
  33.        pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);  
  34.    }  


方法最后我们可以看到再次调用安装接口installPackage()完成安装。

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Pycharm安装PyQt5的详细教程
  3. Python list sort方法的具体使用
  4. python list.sort()根据多个关键字排序的方法实现
  5. 四十七、实现调用Android手机的拍照功能
  6. GooglePlay 分包 安装obb
  7. Android(安卓)自定义View引用xml布局
  8. android一些认识
  9. android中调用金山词霸

随机推荐

  1. Android adb 常用技巧
  2. android EditText控件自动获取焦点弹出键
  3. android中和activity生命周期相关的那些
  4. 离线安装 Android 4.0 SDK
  5. Android materialDesign 风格阴影 改变阴
  6. Android 软键盘弹出,界面整体上移
  7. 第七章 Android 动画机制与使用技巧
  8. react-nativeAndroid打包报错:Daemon: AAP
  9. html5写链接打开ios和android本地应用
  10. 【转】.Android-sharedUserId数据权限