android設備靜默升級

       還是一如既往的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);
        }
    }
}

 配置文件中來一波,靜態註冊

        <receiver
            android:name=".receiver.UpdateReceiver"
            android:enabled="true">
            <intent-filter android:priority="100">
                <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>

 走到這一步,然後程序run一把,然後讓服務器配合一波,基本就可以了。如果遇見問題,可以參照下面的踩坑記載,對了

記得在配置文件添加相關的權限。因爲我這個版本相對來說是6.0,稍微低一些,如果大家在高版本的板子上遇見問題,可以留言

大家一起討論一波,基本上都是適配權限的問題。還有就是系統簽名,一定要系統簽名,否則人家不認你。

    <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"
        tools:ignore="ProtectedPermissions" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

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

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

下面來說一下搞這個時候遇見幾個小坑。
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章