浅谈android关于APK的安装

由于项目要求,在应用中做了一个升级功能,涉及到了APK的安装,所以在这里总结一下。如果有错误还请大神们指出

非静默安装

用户看到安装过程,能够进行干涉,能取消。目前很少用到

	public void commonInstall(ApkInfo apkInfo) throws IOException{
		LogUtils.i(TAG, "commonInstall");
		File file = new File(apkInfo.getDownloadFilePath());
		if(!file.exists()){
			throw new FileNotFoundException();
		}
		Uri uri = Uri.fromFile(file);
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.setDataAndType(uri, "application/vnd.android.package-archive");
		context.startActivity(intent);
	}
静默安装

用户无法看见安装过程,无法干涉及取消。是目前的主流,分以下两种情况

1.可以获取到/system/xbin/su文件

package com.example.androidinstalldemo.executor;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.util.Log;

import com.example.androidinstalldemo.entity.ApkInfo;
import com.example.androidinstalldemo.utils.LogUtils;
import com.example.androidinstalldemo.utils.PackageManagerUtils;

/**
 * 
 * @author alex_shaw
 *
 */
public class SilencePushExecutor {

	public static final String TAG = "SilencePushExecutor";
	public Context context;
	public static SilencePushExecutor executor;
	
	private SilencePushExecutor(Context context) {
		this.context = context;
	}
	
	public synchronized static SilencePushExecutor getInstance(Context context){
		if(null == executor){
			executor = new SilencePushExecutor(context);
		}
		return executor;
	}
	
	/**
	 * @author alex_shaw
	 * 
	 * @param info 所下载的APK的bean
	 */
	public void execute(ApkInfo info){
		int oldVersionCode = 0;//原始版本的versionCode
		int newVersionCode = info.getVersionCode();
		File file = new File(info.getDownloadFilePath());//需要进行安装的APK路径
		if(!file.exists()) 
				return;
		LogUtils.i(TAG, file.getAbsoluteFile());
		try{
			/********获取原APK的versionCode和versionName************************/
			ApplicationInfo appInfo = PackageManagerUtils.getApplicationInfo(context, info.getPackageName());
			if(appInfo!=null){
				PackageInfo packageInfo = PackageManagerUtils.getPackageInfo(context, appInfo.sourceDir);
				oldVersionCode = packageInfo.versionCode;
			}
			if(newVersionCode > oldVersionCode){
				cmdTask(file, appInfo);
			}
			
		}catch(IOException e){
			LogUtils.e(TAG, e.getMessage());
		}catch (InterruptedException e) {
			LogUtils.e(TAG, e.getMessage());
		}
	}
	/**
	 * 
	 * @author alex_shaw
	 * 
	 * @param file 
	 * @param appInfo 
	 * @throws IOException
	 * @throws InterruptedException
	 */
	private void cmdTask(File file, ApplicationInfo appInfo)
			throws IOException, InterruptedException {
		Process p = Runtime.getRuntime().exec("/system/xbin/su");
		DataOutputStream os = new DataOutputStream(p.getOutputStream());
		//os.writeBytes("mount -o rw,remount /system \n");
		os.writeBytes("adb remount \n");
		os.writeBytes("busybox mv " +file.getAbsolutePath()+  " /system/app \n");
		os.writeBytes("busybox chmod 777 /system/app/"+file.getName()+" \n");
		if(appInfo !=null){
			os.writeBytes("busybox rm "+appInfo.sourceDir+" \n");
			os.writeBytes("pm uninstall "+appInfo.packageName+" \n");
			os.writeBytes("busybox rm -r "+appInfo.dataDir+" \n");
		}
		os.writeBytes("sync \n");
		os.writeBytes("exit \n");
		int code = p.waitFor();
		Log.i("msg","code = " + code);
	}

}

2.无法获取su文件,但有root权限

	public void packageManagerInstallInReflect(ApkInfo apkInfo) throws FileNotFoundException{
		File file = new File(apkInfo.getDownloadFilePath());
		if(!file.exists()){
			throw new FileNotFoundException();
		}
		Uri uri = Uri.fromFile(file);
		try {
			PackageManager pm = context.getPackageManager();
				//此处无法通过所注释的代码获取改方法,提示NoSuchMethodException,应该是IPackageInstallObserver.Stub.class这个类的原因,这是系统的类,在这里用可能有问题
				//Method method = pm.getClass().getMethod("installPackage", Uri.class,IPackageInstallObserver.Stub.class,Integer.class,String.class);
				Method[] methods = pm.getClass().getMethods();
				for(Method m : methods){
					if("installPackage".equals(m.getName())){
						m.invoke(pm, uri,new MyInstallObserver(),INSTALL_REPLACE_EXISTING,apkInfo.getPackageName());
						break;
					}
				}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	private class MyInstallObserver extends IPackageInstallObserver.Stub{
		
		/**
		 * 安装完成之后的回调,自定义逻辑
		 * @param packageName 安装的包名
		 * @param returnCode 安装结果
		 */
		@Override
		public void packageInstalled(String packageName, int returnCode)
				throws RemoteException {
			LogUtils.i(TAG, packageName + "Install Success,the code = " + returnCode);
		}
		
	}

说一下具体的步骤

1.需要一个IPackageInstallObserver.aidl 的文件,从源码里可以找到。

2.将这个aidl文件加入到项目中,但是不要改变它的包名。

3.在我们写IPackageInstallObserver.Stub的实现类的时候,eclipse可能提示我们找不到该包或类,这时候就需要我们收到的import了。

4.需要我们在清单文件中添加INSTALL_PACKAGES的权限

5.打包生成的APK只有放到system/app下才能运行。


附加上我所遇到的所有关于升级失败的原因,

非静默升级,提示解析失败,我遇到原因有两个,

1,APK在下载的时候出现数据丢失的情况,建议在下载完成之后做一次MD5校验,确保其完整性。

2,APK下载完成之后,MD5值正确,但是没有权限,建议将APK以及APK所在的目录做一次chmod 777 操作。

非静默升级,提示签名冲突,这是因为系统里面的APK的签名和你要进行升级安装的APK的签名不一致导致。

静默升级,遇到的一个非常奇怪的问题,在通过mv命令移动APK至system/app目录下时,移动前和移动后的APK的MD5会不一致,我只在朝歌的盒子上发现过这个问题,应该是rom的问题。

另外一个问题,如果说你的APK已经存在于system/app,这个时候你adb install 一个低版本的APK,是可以安装上的,也可以运行,但是重启之后又会被高版本的APK覆盖掉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值