Android App内检测更新新版本APK

弓背霞明剑照霜,秋风走马出咸阳。这篇文章主要讲述Android App内检测更新新版本APK相关的知识,希望能为你提供帮助。
Rayland主板虽然作为一块基于android的工控板,但是很多设备厂商并不想让用户看到Android系统信息。所以APK默认设置为开机启动项、img去除了Android头部和底部菜单。但是随之带来了APK更新的问题,传统的插入u盘,sd卡手动安装新版本APK的方式已经不够用了。所以我们需要点自动的东西。
App内检测更新新版本APK 检测新版本APK
我们使用 四大组件之一的BroadcastReceiver来检测 sd卡或是u盘设备的接入。

public class StorageMountListener extends BroadcastReceiver{@Override public void onReceive(final Context context, Intent intent) { if(intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED)){ // 获取插入设备路径 String path = intent.getData().getPath(); // 检测路径是否有新版本APK ApkUpdateUtils.getInstance().checkLocalUpdateAtBackground(context, path); } }}

ApkUpdateUtils.java
/** * 后台检查指定目录下是否有新版本的APK,有则提示安装 * @param context 上下文 * @param path 需要检查的目录 */ public void checkLocalUpdateAtBackground(final Context context, final String path){ ExecutorService executorService =Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { @Override public void run() { // 检查指定目录下是否存在高版本的更新包,并返回结果 File apkFile = findUpdatePackage(context, path); if(apkFile == null){ return; } File msg = new File(apkFile.getParent(), apkFile.getName().replace(".apk", ".txt")); String description = readStringFile(msg); Intent intent = new Intent(context, UpdateActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 新版本apk 路径 intent.putExtra("apk", apkFile.getAbsolutePath()); // 新版本apk 描述信息 intent.putExtra("description", description); context.startActivity(intent); } }); }/** * 检查指定目录下是否存在高版本的更新包,并返回结果 * @param path检查目录 * @returnAPK文件 */ public File findUpdatePackage(Context context, String path) { File parent = new File(path); if(!parent.exists() || parent.isFile()){ return null; } File[] apks = parent.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().toLowerCase().endsWith(".apk"); } }); if(apks == null || apks.length == 0){ return null; } try {/** *通过 build.gradle 中的 versionCode 来判断 *每次版本更新后 修改versionCode */ PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); File apkFile = null; int versionCode = 0; for(File apk : apks){ PackageInfo apkInfo = packageManager.getPackageArchiveInfo(apk.getAbsolutePath(), 0); if(packageInfo.packageName.equals(apkInfo.packageName) & & packageInfo.versionCode < apkInfo.versionCode){ if(apkFile == null){ apkFile = apk; versionCode = apkInfo.versionCode; }else{ if(versionCode < apkInfo.versionCode){ apkFile = apk; versionCode = apkInfo.versionCode; } } } } return apkFile; } catch (Exception e) { e.printStackTrace(); } return null; }/** * 将文件内容读取成String * @param file 要读取的文件 * @return 文件内容 */ public String readStringFile(File file){ StringBuilder builder = new StringBuilder(); BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK")); String line; while((line = br.readLine())!=null){ builder.append(line); builder.append(" "); } } catch (IOException e) { e.printStackTrace(); }finally { if(br!=null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return builder.toString(); }

新版本更新提示
UpdateActivity.java
/** * 显示更新提示对话框 */ private void showUpdateMsgDialog(final Context context, final String apk, String descrption ){ PackageInfo apkInfo = getPackageManager().getPackageArchiveInfo(apk, 0); AlertDialog updateMsgDialog = new AlertDialog.Builder(context).create(); updateMsgDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { finish(); } }); updateMsgDialog.setTitle("检测到新版本"+apkInfo.versionName); updateMsgDialog.setMessage(descrption); updateMsgDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); updateMsgDialog.setButton(AlertDialog.BUTTON_POSITIVE, "安装", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 启动后台安装服务 `SilentInstallService` Intent intent = new Intent(UpdateActivity.this, SilentInstallService.class); intent.putExtra("apkPath", apk); context.startService(intent); } }); updateMsgDialog.setCanceledOnTouchOutside(false); updateMsgDialog.show(); }

后台安装服务
SilentInstallService.java
public class SilentInstallService extends IntentService { static final String TAG = SilentInstallService.class.getSimpleName(); public SilentInstallService() { super(TAG); }@Override protected void onHandleIntent(Intent intent) { PackageManager pm = getPackageManager(); String apkPath = intent.getStringExtra("apkPath"); PackageInfo info = pm.getPackageArchiveInfo(apkPath,PackageManager.GET_ACTIVITIES); if(install(apkPath) & & info!=null){ startActivity(getPackageManager().getLaunchIntentForPackage(info.packageName)); } }public boolean install(String apkPath){ Process process = null; BufferedReader errorStream = null; try { process = Runtime.getRuntime().exec("pm install -r "+apkPath+" "); process.waitFor(); errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream())); String msg = ""; String line; while((line=errorStream.readLine())!=null){ msg += line; } Log.i(TAG, "silent install msg : "+msg); if(!msg.contains("Failure")){ return true; } } catch (Exception e) { e.printStackTrace(); }finally { if(errorStream!=null){ try { errorStream.close(); } catch (IOException e) { e.printStackTrace(); } } if(process!=null){ process.destroy(); } } return false; } }

AndroidManifest.xml注册 StorageMountListenerSilentInstallService
< receiver android:name=".StorageMountListener"> < intent-filter> < action android:name="android.intent.action.MEDIA_MOUNTED" /> < data android:scheme="file"/> < /intent-filter> < /receiver> < service android:name="cn.rayland.update.SilentInstallService"> < /service>

权限配置
到这个一切看起来尽善尽美了? but it does‘t work.
我们需要系统权限
< manifest xmlns:android="http://schemas.android.com/apk/res/android" ... ... android:sharedUserId="android.uid.system">

但是我们会发现安装失败
error:Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]

网上说这是因为你安装了debug权限签名,再安装系统sign签名就会有这个问题。需要卸载以前的app再安装。
然后我们又遇到了它
error:Failure [INSTALL_FAILED_SHARED_USER_INCOMPATIBLE]

这是因为我们在 AndroidManifest.xml申明了系统签名,然而并没有。
我们需要一顿操作
  • 找到编译目标系统时的签名证书platform.pk8和platform.x509.pem,在android源码目录build argetproductsecurity下
  • 将签名工具(signapk.jar)、签名证书(platform.pk8和platform.x509.pem)及编译出来的apk文件都放到同一目录
然后命令行执行:
java -jar signapk.jar platform.x509.pem platform.pk8 input.apk output.apk

就能把路径下的 input.apk变成签名的output.apk
当然你也可以使用现成的signapk,运行signApk.bat
就可以开心的更新了
Android App内检测更新新版本APK

文章图片

其他方法
【Android App内检测更新新版本APK】你也可以将更新APK服务SilentInstallService编译成一个app烧在img中。每次调用有系统签名的SilentInstallService app即可。

    推荐阅读