还是一如既往的android开发板子,需求是静默升级。静默升级顾名思义,就是后台默默下载,在用户无感知的情况,完成APP的升级,虽说现在有热更新的技术,但是奈何需求与实际不相符合,并且必须得有用户参与,才有很大几率成功。所以还是硬着头皮去研究一波静默升级,好在android是root权限,基本的条件是有了。

       现在基本思路是这样,第一,在应用启动的时候,获取版本号,然后启动下载,下载完成之后保存到指定的文件夹,这个想必对大多数搬砖工友来说,都不难,分享一个小的框架,简单易用。Aria。示例代码如下,

                    //开启下载                    Aria.download(this)                            .load(bean.url)     //读取下载地址                            .setFilePath(Constants.FILE_APK) //设置文件保存的完整路径                            .start();   //启动下载

       第二,在下载完成之后,思路就是如何才能调用命令安装,一般的安装代码肯定是没戏,但是shell命令可以试试,调用这个命令就好比。在应用外触发了一个adb命令一般,命令格式是  pm install -r " + apkPath,有没有一种熟悉的感觉,哈哈。下面分享一个封装好的工具类,可以直接传命令执行即可。

    private static final String COMMAND_SU = "su";    private static final String COMMAND_SH = "sh";    private static final String COMMAND_EXIT = "exit\n";    private static final String COMMAND_LINE_END = "\n";    /**     * 执行shell命令     */    public static CommandResult execCommand(String[] commands, boolean isRoot,                                            boolean isNeedResultMsg) {        int result = -1;        if (commands == null || commands.length == 0) {            return new CommandResult(result, null, null);        }        Process process = null;        BufferedReader successResult = null;        BufferedReader errorResult = null;        StringBuilder successMsg = null;        StringBuilder errorMsg = null;        DataOutputStream os = null;        StringBuilder cmd = new StringBuilder();        try {            process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);            cmd.append(isRoot ? COMMAND_SU : COMMAND_SH);            cmd.append(COMMAND_LINE_END);            os = new DataOutputStream(process.getOutputStream());            for (String command : commands) {                if (command == null) {                    continue;                }                os.writeBytes(command);                os.writeBytes(COMMAND_LINE_END);                cmd.append(command);                cmd.append(COMMAND_LINE_END);                os.flush();            }            os.writeBytes(COMMAND_EXIT);            cmd.append(COMMAND_EXIT);            os.flush();            result = process.waitFor();            // get command result            if (isNeedResultMsg) {                successMsg = new StringBuilder();                errorMsg = new StringBuilder();                successResult = new BufferedReader(new InputStreamReader(                        process.getInputStream()));                errorResult = new BufferedReader(new InputStreamReader(                        process.getErrorStream()));                String s;                while ((s = successResult.readLine()) != null) {                    successMsg.append(s);                }                while ((s = errorResult.readLine()) != null) {                    errorMsg.append(s);                }            }        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (os != null) {                    os.close();                }                if (successResult != null) {                    successResult.close();                }                if (errorResult != null) {                    errorResult.close();                }            } catch (IOException e) {                e.printStackTrace();            }            if (process != null) {                process.destroy();            }        }        /*Log.i(TAG, "execCommand: " + cmd.toString());        Log.i(TAG, "execCommand: result = " + result +                ", successMsg = " + (successMsg == null ? null : successMsg.toString()) +                ", errorMsg = " + (errorMsg == null ? null : errorMsg.toString()));*/        return new CommandResult(result,                successMsg == null ? null : successMsg.toString(),                errorMsg == null ? null : errorMsg.toString());    }/**     * 运行结果     */    public static class CommandResult {        //运行结果        private int result;        //运行成功结果        private String successMsg;        //运行失败结果        private String errorMsg;        public CommandResult(int result) {            this.result = result;        }        public CommandResult(int result, String successMsg, String errorMsg) {            this.result = result;            this.successMsg = successMsg;            this.errorMsg = errorMsg;        }        public int getResult() {            return result;        }        public void setResult(int result) {            this.result = result;        }        public String getSuccessMsg() {            return successMsg;        }        public void setSuccessMsg(String successMsg) {            this.successMsg = successMsg;        }        public String getErrorMsg() {            return errorMsg;        }        public void setErrorMsg(String errorMsg) {            this.errorMsg = errorMsg;        }    }

        代码的大概逻辑就是先判断有没有root权限,大家也知道,没有这个,也没办法调用这个命令,如果有的话,就执行命令

意思为覆盖安装。安装完成之后会有结果,如果我们还想安装完成自启动,记着不是开机自启动,而是,静默升级的程序重启,

这个时候需要找一个系统广播,配合来启动一波。系统在安装包卸载重新安装的时候,会去发送系统广播,。至于想怎么样操

作,可以结合业务逻辑判断action,来操作,同样,这个广播需要静态注册,否则,你懂得

public class UpdateReceiver extends BroadcastReceiver {    private static final String TAG = UpdateReceiver.class.getSimpleName();    @Override    public void onReceive(Context context, Intent intent) {        String packageName = intent.getDataString();        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {//接收升级广播            Logger.e(TAG, "onReceive:升级了一个安装包,重新启动此程序");            if (packageName.equals("package:" + SystemUtil.getPackageName())) {                SystemUtil.restartApp();//升级完自身app,重启自身            }        } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {//接收安装广播            Logger.e(TAG, "onReceive:安装了" + packageName);            if (packageName.equals("package:" + SystemUtil.getPackageName())) {                /*SystemUtil.reBootDevice();*/            }        } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {            //接收卸载广播            Logger.e(TAG, "onReceive:卸载了" + packageName);        }    }}

 配置文件中来一波,静态注册

                                                                                                        

 走到这一步,然后程序run一把,然后让服务器配合一波,基本就可以了。如果遇见问题,可以参照下面的踩坑记载,对了

记得在配置文件添加相关的权限。因为我这个版本相对来说是6.0,稍微低一些,如果大家在高版本的板子上遇见问题,可以留言

大家一起讨论一波,基本上都是适配权限的问题。还有就是系统签名,一定要系统签名,否则人家不认你。

                            

下面来说一下搞这个时候遇见几个小坑。
1.首先设备是root的,这个就不用想了,不root这个功能基本不用想了。
2.第二个得启用系统签名,这样系统才能认为你是自己人,才会让你安装,以及后来的自启动,都离不开这个。

       系统签名有几种方式,其中就是你找到系统的两个文件夹,自己配置一波,然后生成签名文件,配置到debug签名下面

这样你生成的就是系统签名apk,这种查了资料,稍微麻烦一些,当然大家有时间的话,那种也挺方便,在这个推荐一种傻瓜方式。主要是节省时间,一把梭,但是得注意不要弄混了安装包,

      

       签名命令,在当前文件夹执行如下命令,第一个app-debug.apk文件是待签名之前的app,appNew.apk是签名后的app,签名成功保存在当前文件夹下, 示例命令,java -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk appNew.apk。

下面是一个朋友整理的资源,分享一波,里面有签名工具

 链接:https://pan.baidu.com/s/18zQucxlvWHLkPNSVXlXKiw 
 提取码:bo8c

更多相关文章

  1. Android系统权限
  2. android的Log组件和logcat命令
  3. Android逆向之旅---带你爆破一款应用的签名验证问题
  4. 学习笔记(一)Android(安卓)的简介
  5. 编译Android源码致命错误解决方案
  6. android 逆向工程
  7. Service与Android系统实现(1)-- 应用程序里的Service(二)
  8. 手机杂谈
  9. Android(安卓)集成微信支付和支付宝支付工具类

随机推荐

  1. SQL 重复记录问题的处理方法小结
  2. SQL Server 服务由于登录失败而无法启动
  3. sql 取两值之间的数据方法(例:100-200之间
  4. sqlserver下Kill 所有连接到某一数据库的
  5. TRUNCATE 快速删除表中的所有数据
  6. 为数据库生成某个字段充填随机数的存储过
  7. SQL创建的几种存储过程
  8. 将表数据生成Insert脚本 比较好用的生成
  9. 动态SQL语句使用心得
  10. sqlserver 统计sql语句大全收藏