前段时间做了一个批量安装卸载应用程序的小应用,由于安装卸载应用程序的部分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、别忘记添加权限
以上代码在Android2.1的SDK中编译通过,并正确批量安装卸载应用程序
转自:http://blog.csdn.net/tangcheng_ok/article/details/6681453
另外关键的是:你要有linux环境或其他可以mmm交叉编译的环境,因为这个用到的是隐藏api,SDK中没有的,ecilipse里面会提示包不存在,但交叉编译可以,如果你不知道mmm编译,请return。
调用uinstall会弹出卸载界面,而调用uninstallBatch不会弹出卸载界面。
在Windows下可下载模拟的unix环境cygwin
另外还可参考:
实现静默安装APK的两种方法
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
http://www.fanfengle.com