前段時間做了一個批量安裝卸載應用程序的小應用,由于安裝卸載應用程序的部分API是隱藏的,所以必須在ubuntu下下載Android系統源碼,并編譯之后使用MM命令編譯生成APK文件,其實也難。思路是這樣的,在XX/packages/apps目錄下有一個PackageInstaller的應用程序,Android機器中安裝卸載都是由這個應用程序完成的。但是它沒有批量安裝和卸載的功能,如果要在自己的應用程序中添加批量安裝和卸載的功能,其實很簡單,只需要參考PakcageInstaller里面的安裝卸載代碼加個循環就可以了。但值得注意的是在編譯的過程中必須復制PackageInstaller里面的Android.mk文件,修改文件為工程目錄名。好了,廢話不再多說,下面是關鍵代碼
? ?1、 Android.mk文件
LOCAL_PATH:= $(call my- dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS : = optional LOCAL_SRC_FILES : = $(call all-subdir-java- files) LOCAL_PACKAGE_NAME : = PackageInstaller LOCAL_CERTIFICATE : = platform include $(BUILD_PACKAGE)
?
?
重點是LOCAL_CERTIFICATE := platform要對
?
?
? ?2、PakcageInstaller.java文件(關鍵代碼)
package cn.ceadic.apkmgr; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.util.Log; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageDeleteObserver; import android.os.FileUtils; public class PackageInstaller { private File mTmpFile; private final String TMP_FILE_NAME = "tmpCopy.apk" ; private final static String TAG = "PackInstaller" ; private Context mContext; public PackageInstaller(Context context) { mContext = context; } public void install(String path,String packageName){ Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile( new File(path)), "application/vnd.android.package-archive" ); mContext.startActivity(intent); } public void instatllBatch(String path, String packageName) { Log.i(TAG, "path=" + path); int installFlags = 0 ; PackageManager pm = mContext.getPackageManager(); try { PackageInfo pi = pm.getPackageInfo(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:" + packageName); } // Create temp file before invoking install api mTmpFile = createTempPackageFile(path); if (mTmpFile == null ) { // Message msg = mHandler.obtainMessage(INSTALL_COMPLETE); // msg.arg1 = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; // mHandler.sendMessage(msg); return ; } Uri mPackageURI = Uri.parse("file://" + mTmpFile.getPath()); String installerPackageName = mContext.getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); PackageInstallObserver observer = new PackageInstallObserver(); pm.installPackage(mPackageURI, observer, installFlags, installerPackageName); } private File createTempPackageFile(String filePath) { File tmpPackageFile = mContext.getFileStreamPath(TMP_FILE_NAME); if (tmpPackageFile == null ) { Log.w(TAG, "Failed to create temp file" ); return null ; } if (tmpPackageFile.exists()) { tmpPackageFile.delete(); } // Open file to make it world readable FileOutputStream fos; try { fos = openFileOutput(TMP_FILE_NAME, MODE_WORLD_READABLE); } catch (FileNotFoundException e1) { Log.e(TAG, "Error opening file " + TMP_FILE_NAME); return null ; } try { fos.close(); } catch (IOException e) { Log.e(TAG, "Error opening file " + TMP_FILE_NAME); return null ; } File srcPackageFile = new File(filePath); if (! FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { Log.w(TAG, "Failed to make copy of file: " + srcPackageFile); return null ; } return tmpPackageFile; } private class PackageInstallObserver extends IPackageInstallObserver.Stub { public void packageInstalled(String packageName, int returnCode) { // Message msg = mHandler.obtainMessage(INSTALL_COMPLETE); // msg.arg1 = returnCode; // mHandler.sendMessage(msg); Log.i(TAG, "====INSTALL_COMPLETE" ); } } private class PackageDeleteObserver extends IPackageDeleteObserver.Stub { public void packageDeleted( boolean succeeded) { // Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE); // msg.arg1 = succeeded?SUCCEEDED:FAILED; // mHandler.sendMessage(msg); Log.i(TAG, "====UNINSTALL_COMPLETE" ); } } public void uninstall(String packageName){ Uri packageURI = Uri.parse("package:" + packageName); Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI); mContext.startActivity(uninstallIntent); } public void uninstallBatch(String packageName) { PackageDeleteObserver observer = new PackageDeleteObserver(); mContext.getPackageManager().deletePackage(packageName, observer, 0 ); } }
?
? ?3、別忘記添加權限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
?
以上代碼在Android2.1的SDK中編譯通過,并正確批量安裝卸載應用程序
?
轉自: http://blog.csdn.net/tangcheng_ok/article/details/6681453
另外關鍵的是:你要有linux環境或其他可以mmm交叉編譯的環境,因為這個用到的是隱藏api,SDK中沒有的,ecilipse里面會提示包不存在,但交叉編譯可以,如果你不知道mmm編譯,請return。
調用uinstall會彈出卸載界面,而調用uninstallBatch不會彈出卸載界面。
在Windows下可下載模擬的unix環境cygwin
另外還可參考:
?
?android靜默安裝步驟: http://download.csdn.net/download/lyj286326189/4049858
?
android靜默安裝探討
?
1)在網上搜尋該問題的解決方法,且查閱android開發文檔,沒有發現可以實現該功能的顯示API調用,網絡上很多人請教同樣的問題,但都沒有能夠實現解答;說是android為了用戶的安全,已屏蔽該實現該方法的功能,第三方法應用是無法實現靜默安裝的。
(2)然后自己試圖去看看android實現普通安裝程序的源碼文件,能否找到解決的辦法,打算繞過普通安裝時的提示框,直接調用通過確認后調用的函數進行安裝;在查看android應用程序的普通安裝過程后,發現應用程序安裝過程的方法調用過程為:首先進入到com/android/packageinstaller/PackageInstallerActivity.java這個Activity中,在這個Activity中首先檢查所欲安裝的程序是否是正確的安裝文件,以及當前系統中是否已安裝了此應用程序,提示用戶是否重復安裝,另外還獲取所欲安裝的程序所講用到的權限,然后將這些信息通過一個對話框提示給用戶,當用戶確定安裝時,啟動com.android.packageinstaller.InstallAppProgress.java這個Activity,在這個Activity中,調用
android.content.pm.PackageManager.installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName)進行安裝應用程序,在InstallAppProgress中得到的PackageManager是通過PackageManager pm = getPackageManager()得到的,得到的對象是一個android.app.ContextImpl.ApplicationPackageManager對象,而
ApplicationPackageManager對象經過封裝,
ApplicationPackageManager(ContextImpl context,
? ?? ?? ?? ?? ? IPackageManager pm) {
? ?? ?? ?? ?mContext = context;
? ?? ?? ?? ?mPM = pm;
? ?? ???}
其installPackage方法為
??@Override
? ?? ???public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
? ?? ?? ?? ?? ? String installerPackageName) {
? ?? ?? ?? ?try {
? ?? ?? ?? ?? ? mPM.installPackage(packageURI, observer, flags, installerPackageName);
? ?? ?? ?? ?} catch (RemoteException e) {
? ?? ?? ?? ?? ? // Should never happen!
? ?? ?? ?? ?}
? ?? ???}
可見調用的installPackage方法為 IPackageManager.installPackage(packageURI, observer, flags, installerPackageName);
在ContextImpl中,由IPackageManager pm = ActivityThread.getPackageManager()獲得IPackageManager實例對象;在ActivityThread.getPackageManager()方法中,
static IPackageManager sPackageManager;
public static IPackageManager getPackageManager() {
? ?? ???if (sPackageManager != null) {
? ?? ?? ?? ?return sPackageManager;
? ?? ???}
? ?? ???IBinder b = ServiceManager.getService("package");
? ?? ???sPackageManager = IPackageManager.Stub.asInterface(b);
? ?? ???return sPackageManager;
? ? }
最終得到的installPackage確實是IPackageManager.installPackage方法;
因為class PackageManagerService extends IPackageManager.Stub所以IPackageManager.installPackage調用的是:PackageManagerService.java (frameworks\base\services\java\com\android\server)文件中的
/* Called when a downloaded package installation has been confirmed by the user */
? ? public void installPackage(
? ?? ?? ?? ?final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
? ?? ???installPackage(packageURI, observer, flags, null);
? ? }
(這里不明白為何IPackageManager.installPackage方法調用的是PackageManagerService.java,只是在網上的一篇文章中它給出了上面的原因,因為class PackageManagerService extends IPackageManager.Stub,我不明白,但也找不到其他的函數,通過PackageManagerService.java的源碼,可以看出它確實是進行應用程序安裝的,所以應該可以確定最終調用的方法就是
PackageManagerService.installPackage(final Uri packageURI, final IPackageInstallObserver observer, final int flags))
于是考慮如何得到PackageManagerService.installPackage(),考慮通過反射的方法得到installPackage(),但其中難以得到的是其參數中的IPackageInstallObserver類型,IPackageInstallObserver是由aidl文件定義的,通過aidl文件的特性,將IPackageInstallObserver.aidl文件拷到本地程序中,可以得到類IPackageInstallObserver.calss,通過它反射出installPackage()方法,但在invoke該方法時,卻無法得到IPackageInstallObserver的實例對象,IPackageInstallObserver的實例對象必須通過
IPackageInstallObserver.Stub.asInterface(IBinder binder)方式得到,無法得到與其綁定的IBinder對象,因而無法執行反射出來的方法;另外PackageManagerService.installPackage()似乎是不能被第三方應用程序執行的,有權限的限制,這從下面的實例中似乎可以得到證實。
(3)在程序中執行Runtime.getRuntime().exec("pm install -r " + new File(Environment.getExternalStorageDirectory(),
"download/Shuffle-1.6.3.apk")); 進行安裝,這個命令的執行在 com.android.commands.pm.Pm中,直接調用IPackageManager.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,installerPackageName)方法,在此方法中,
IPackageManager mPm;
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
且class PackageManagerService extends IPackageManager.Stub
所以IPackageManager.installPackage調用的是:PackageManagerService.java (frameworks\base\services\java\com\android\server)文件中的
/* Called when a downloaded package installation has been confirmed by the user */
? ? public void installPackage(
? ?? ?? ?? ?final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
? ?? ???installPackage(packageURI, observer, flags, null);
? ? }
在此方法執行中會出現 Not granting permission android.permission.DELETE_PACKAGES錯誤,這應該是該權限不能授給第三方應用,因而在程序中不能執行,與android中普通安裝應用程序最終調用的方法是相同的,但是卻對第三方應用是沒有權限執行的。。
(4) 另外解決思路:
1> 使用android:sharedUserId="android.uid.system"屬性來使應用程序獲得系統權限,看看是否能夠執行行Runtime.getRuntime().exec("pm install -r ... ")方法。
2> 閱讀android實現應用程序安裝更底層的代碼,看看能否可以調用的底層方法進行安裝或者自己實現一個安裝程序的代碼,但這可能性不大,因為這涉及到android更底層的調用,
肯定會有一定的權限限制。
3> 在網上看到一個文件管理程序,據說是可以實現批量寂寞安裝應用程序,但說明運行時需要用戶確定得到手機的root權限,所以沒有太大意義。
4> 定制自己的android系統,可以解決。
?
說了這么多,最后總結的是:修改android。mk文件并在linux下和系統源代碼一起編譯及簽名為目標系統簽名來生成apk,然后參照packegeinstaller源代碼調用方法即可
確保或者獲取手機root權限,然后執行
Runtime.getRuntime().exec("pm install -r " + filePath);
?
轉自? http://hcq0618.blog.163.com/blog/static/178090351201222552215372/
歡迎各位同學加入 android 技術群?155595043
個人微博:? http://weibo.com/338226333
彈窗安裝
Intent it = new Intent(Intent.ACTION_VIEW); it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); it.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive" ); context.startActivity(it);
?
?相關鏈接: http://blog.csdn.net/fenyush/article/details/6052914
? ? ? ? ? ? ?? http://blog.csdn.net/sodino/article/details/6238818
? ? ? ? ? ? ? ? http://blog.sina.com.cn/s/blog_44e6424c0100zbhp.html
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
