App下载、静默安装并自启动

前不久遇到一个需求,说是要实现app的静默安装并且安装完了之后要能够自启动。我上网查了很多资料,方法基本都差不多,但是基本上每种方法我都试过了,能够实现静默安装,但是自启动的时候就遇到了问题。很多类似的博客下面几乎都有人提问说为啥不能够自启动。我当时在操作的时候也发现自启动失败,后来我反复推敲尝试实现了这个功能,大多数博客介绍的方法是没有问题的,问题在于博主没有把过程讲述得很清楚,从而给读者造成了误解,特此发文一篇希望帮助广大受阻的读者。

静默更新这个需求是有一定的前提的,一般这类的需求只会出现在一些厂商定制的pad或特定设备上面,系统要给足了root权限,手机上有一类做法可以在不需要root权限的前提下实现更新,但模式算不得静默更新,当然这不是我们今天要讨论的问题,我也就不在这里多说了。

一、安装包下载

安装包下载相关的文章数不胜收,主要是代码涉及静默安装,我这里就把我的代码拿出来做个参考吧。
public class DownLoadUtils {

    public static void download(final String apkUrl) {
        Thread thread;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                downloadFile(apkUrl);
            }
        });
        thread.start();
    }

    //下载apk
    public static File downloadFile(String apkUrl) {
        final String fileName = "update.apk";
        File tmpFile = new File("/sdcard/update");
        if (!tmpFile.exists()) {
            tmpFile.mkdir();
        }
        final File file = new File("/sdcard/update/" + fileName);
        try {
            URL url = new URL(apkUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            InputStream is = conn.getInputStream();
            FileOutputStream fos = new FileOutputStream(file);
            byte[] buf = new byte[1024];
            conn.connect();
            double count = 0;
            if (conn.getResponseCode() >= 400) {
                //连接超时
            } else {
                while (count <= 100) {
                    Log.e("install", "download" + count + "");
                    if (is != null) {
                        int numRead = is.read(buf);
                        if (numRead <= 0) {
                            break;
                        } else {
                            fos.write(buf, 0, numRead);
                        }
                    } else {
                        break;
                    }
                }
            }
            conn.disconnect();
            fos.close();
            is.close();
            InstallApkUtils.excuteSuCMD(InstallApkUtils.sdPath);//执行静默安装
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }
}

上面的代码中,当安装包下载完成之后,开始执行静默安装方法。

二、静默安装

强调一点,文章中提到的静默安装是在系统开发了root权限的前提之下,如果没有获取root权限,还是采用系统的安装流程吧!
public class InstallApkUtils {
    public static Context mContext = null;
    public static String sdPath = "/sdcard/update/update.apk";//下载sd路径

    //判断是否update目录下有文件
    public static boolean isHasFile() {
        try {
            File f = new File(sdPath);
            if (!f.exists()) {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }


    public static void excuteSuCMD(String currenttempfilepath) {
        if (isHasFile()) {
            Process process = null;
            OutputStream out = null;
            InputStream in = null;
            try {
                //请求root
                process = Runtime.getRuntime().exec("su");
                out = process.getOutputStream();
                //调用安装
                out.write(("pm install -r " + currenttempfilepath + "\n").getBytes());
                in = process.getInputStream();
                int len = 0;
                byte[] bs = new byte[256];
                while (-1 != (len = in.read(bs))) {
                    String state = new String(bs, 0, len);
                    if (state.equals("success\n")) {
                        //安装成功后的操作
                        //静态注册自启动广播
                        Intent intent = new Intent();
                        //与清单文件的receiver的anction对应
                        intent.setAction("android.intent.action.PACKAGE_REPLACED");
                        //发送广播
                        mContext.sendBroadcast(intent);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (out != null) {
                        out.flush();
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            Log.e("install", "apk is not exist");
        }
    }
}
安装完成之后我们会发送一条广播
//静态注册自启动广播
 Intent intent = new Intent();
 //与清单文件的receiver的anction对应
 intent.setAction("android.intent.action.PACKAGE_REPLACED");
 //发送广播
 mContext.sendBroadcast(intent);

三、注册并接收广播

上面的安装中提到了安装结束会发送一个广播,这条广播的作用就是如果发生了安装包的替换就重新启动应用。

注意:很多人说安装完成之后,应用并没有自己启动,问题在于更新后的安装包没有注册并接收上一个版本发出的广播,下面所提到的源代码一定要在新安装的apk包中实现。

public class UpdateReceiver extends BroadcastReceiver {

    public static final String UPDATE_ACTION = "android.intent.action.PACKAGE_REPLACED";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(UPDATE_ACTION)) {
            Intent intent2 = new Intent(context, TestActivity.class);
            intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent2);
        }
        //接收安装广播
        if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {
            String packageName = intent.getDataString();
//            System.out.println("安装了:" + packageName + "包名的程序");

        }
        //接收卸载广播
        if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) {
            String packageName = intent.getDataString();
//            Log.e("install","卸载了:" + packageName + "包名的程序");
        }
    }
}
然后在AndroidManifest.xml文件中注册UpdateReceiver,当然你也可以选择在源代码中动态注册。
<!--静默更新、自启动-->
<receiver android:name=".utils.UpdateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
     <intent-filter>
         <action android:name="android.intent.action.PACKAGE_REPLACED" />
         <action android:name="android.intent.action.PACKAGE_ADDED" />
         <action android:name="android.intent.action.PACKAGE_REMOVED" />
         <data android:scheme="package" />
      </intent-filter>
 </receiver>
  当然需要用到的权限不要忘了声明
<uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

  <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  <uses-permission android:name="android.permission.RESTART_PACKAGES" />
  <uses-permission
        android:name="android.permission.INSTALL_PACKAGES"
        tools:ignore="ProtectedPermissions" />

四、总结

整个过程大概流程如下:

  1. 下载apk到指定的文件夹下(声明相关的权限);
  2. 注册广播,用来监听app的变动情况实现app的自启动,广播的注册和接收处理在所有的app版本里都要存在;
  3. 获取root权限,从文件夹获取安装包,执行安装程序;
  4. 安装完成之后,发送广播重新启动app。

我当时在实现的过程中,发现app没有重新启动,但是退出app再重新进入已经是更新过的版本,后来我反复阅读别人的文章,发现是我自己没有理解透彻整个实现的过程,我放在服务器上的安装包没有实现广播的接收流程,所以导致了app没有重新启动,后来重新上传了安装包测试通过。

猜你喜欢

转载自blog.csdn.net/zhimingshangyan/article/details/79770255
今日推荐