讓我們自己的Android程序可以自動後臺升級的實現(需Root權限)

首先分析如何才能讓我們的應用程序才能後臺安裝APK——獲取Root權限使用命令安裝,這個問題解決了只能說實現了一般,那麼怎樣才能實現自己安裝自己,最開始的想法是在在下載升級程序包後自己調用命令安裝,但是發現一整忙碌後,這種方式根本不行,原因也很簡單,就是在執行安裝命令到一半的時候自己的APK就已經退出了,那該怎麼辦呢?
觀察發現別人的應用管理程序都是安裝其他的應用包,受到這個啓發,我就自己寫一個簡單的APK安裝程序,放在自己的資源目錄下,當自己的應用被安裝後就將這個文件釋放出來,並進行後臺安裝這個APK安裝程序,代碼如下

    /**
     * 釋放Server程序
     */
    private void installServer() {
        Intent intent = new Intent();
        try {
            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
            intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
            intent.putExtra("tag", 1);
            startActivity(intent);
            Log.e(TAG, "釋放Server程序: 啓動成功");
        } catch (Exception e) {
            e.printStackTrace();
            //安裝Server程序
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    File file = new File(ConsValue.apkFilePath + File.separator + "server.apk");
                    if (!file.exists()) {
                        Log.e(TAG, "釋放Server程序: 安裝的Server.apk文件不存在!");
                        return;
                    }
                    String cmd_install = "pm install -r "; //靜默安裝命令
                    cmd_install = cmd_install + file.getAbsolutePath();
                    Log.e(TAG, "root權限,本地版本低可升級: 升級命令:" + cmd_install);
                    int suCMD = Utils.excuteSuCMD(cmd_install);
                    Log.e(TAG, "釋放Server程序: 安裝結果:suCMD=" + suCMD);
                    if (suCMD != -1) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Intent intent = new Intent();
                                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                    intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
                                    intent.putExtra("tag", 1);
                                    startActivity(intent);
                                    mSpreferences.edit().putBoolean(ConsValue.spIsOnce, false).commit();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    Log.e(TAG, "釋放Server程序: 那個Server程序確實使用不了");
                                }
                            }
                        });
                    }
                }
            }.start();
        }
    }

可以執行Root命令的方法我也給出來

    /**
     * 執行shell命令
     */
    public static int excuteSuCMD(String cmd) {
        try {
            Process process = Runtime.getRuntime().exec("su");
            DataOutputStream dos = new DataOutputStream(
                    (OutputStream) process.getOutputStream());
            // 部分手機Root之後Library path 丟失,導入library path可解決該問題
            dos.writeBytes((String) "export LD_LIBRARY_PATH=/vendor/lib:/system/lib\n");
            cmd = String.valueOf(cmd);
            dos.writeBytes((String) (cmd + "\n"));
            dos.flush();
            dos.writeBytes("exit\n");
            dos.flush();
            process.waitFor();
            int result = process.exitValue();
            return (Integer) result;
        } catch (Exception localException) {
            localException.printStackTrace();
            return -1;
        }
    }

安裝好以後,爲了不給用戶發現我們其實安裝了兩個APK程序,我們需要將那個APK管理程序的桌面圖標進行隱藏,網上給出了三種
第一種:在代碼中實現,可以隱藏,但是隱藏後的程序,就無法被啓動起來了,故此處不行!

        PackageManager p = getPackageManager();
        p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

第二種:更改AndroidManifest文件中的啓動Activity的intent-filter,要麼將註釋掉,要麼替換爲,但是都是無法允許我們的程序的

第三種:增加AndroidManifest文件中的啓動Activity的intent-filter,

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <data
                    android:host="AuthActivity"
                    android:scheme="com.android.example" />
            </intent-filter>
        </activity>

這個MainActivity可以響應Uri爲com.android.example://AuthActivity的特定 Intent,但是爲什麼加入這個之後app就不顯示圖標了呢?因爲我們把app的入口Activity申明爲由接收隱士的Intent來啓動,這樣自然也就不會顯示圖標了。要啓動就需要在外部來啓動了,恰好符合我們的需求,
APK安裝程序的完整的代碼如下:

public class MainActivity extends AppCompatActivity {
    public static final int start = 1;
    public static final int installDef = 2; //安裝默認路徑下的apk文件
    public static final int installPath = 3; //安裝指定路徑下的apk文件
    public static final String DefFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/APK/Client.apk";
    String cmd_install = "pm install -r "; //靜默安裝命令
    final String serverApkPackage = "com.org.ccbygqd";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = getIntent();
        boolean result = false;
        if (intent != null) {
            int tag = intent.getIntExtra("tag", -1);
            switch (tag) {
                case start:
                    break;
                case installDef:
                    result = excuteSuCMD(cmd_install + DefFile);
                    if (result) {
                        Toast.makeText(this, "程序安裝成功", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(this, "程序安裝失敗", Toast.LENGTH_LONG).show();
                    }
                    break;
                case installPath:
                    String path = intent.getStringExtra("path");
                    result = excuteSuCMD(cmd_install + path);
                    if (result) {
                        Toast.makeText(this, "程序安裝成功", Toast.LENGTH_LONG).show();
                        Intent intentOpen = new Intent();
                        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                        intentOpen.setComponent(new ComponentName(serverApkPackage, serverApkPackage + ".MainActivity"));
                        startActivity(intentOpen);
                    } else {
                        Toast.makeText(this, "程序安裝失敗", Toast.LENGTH_LONG).show();
                    }
                    break;
                default:
                    Toast.makeText(this, "安裝服務程序打開成功!", Toast.LENGTH_LONG).show();
                    break;
            }
        }
        finish();
    }
}

我們自己的程序當下載後的安裝代碼如下:

 /**
     * 需要root權限的版本升級,會比較包名的信息
     *
     * @param data
     */
    private void rootUpgrade(String data) {
        datas = Utils.splitCMD(cmdStr);
        String cmd = datas[1];
        byte[] cmdBytes = cmd.getBytes();
        if (cmdBytes[0] == 0X02) { // 同步文件開始
            Log.e(TAG, "硬件版本升級(APK版本升級): 開始");
            Message msg = handler.obtainMessage();
            msg.what = 888;
            msg.arg2 = HIDFlag; //表示是HID的通信
            msg.sendToTarget();
        } else if (cmdBytes[0] == 0X03) { // 同步文件結束
            Log.e(TAG, "硬件版本升級(APK版本升級): 結束");
            Message msg = handler.obtainMessage();
            msg.what = 777;
            msg.arg2 = HIDFlag; //表示是HID的通信
            msg.sendToTarget();
            String serverName = datas[2];
            //獲取到本地的版本信息
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = null;
            try {
                pi = pm.getPackageInfo(context.getPackageName(), 0);
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
            String localName = pi.versionName;
            String[] localSplit = localName.split("\\.");
            String serverCode = serverName.substring(0, serverName.length() - 4);//將 字符串“.apk”去掉
            //獲取到服務器的版本信息
            String[] serverSplit = serverCode.split("_");
            if (serverSplit == null || serverSplit.length < 2) {
                UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0002));
                isLooping = true;
                return;
            }
            File file = new File(ConsValue.apkFilePath + File.separator + serverName);
            if (file.exists() && file.isFile()) {
                int type = 0; //參數表示:0:版本相同無法升級,1:本地版本低可升級,-1:本地版本高無法升級
                try {
                    //比較版本信息
                    int len = (localSplit.length < serverSplit.length) ? localSplit.length : serverSplit.length;
                    for (int i = 0; i < len; i++) {
                        int local = Integer.parseInt(localSplit[i]);
                        int server = Integer.parseInt(serverSplit[i]);
                        if (local > server) {
                            type = -1;
                            break;
                        } else if (local == server) {
                            type = 0;
                        } else {
                            type = 1;
                            break;
                        }
                        Log.e(TAG, "需要root權限的版本升級: 第" + i + "次比較,local=" + local + "  ,server=" + server + "  ,type=" + type);
                    }
                    if (type == 0 && (localSplit.length < serverSplit.length)) {
                        type = 1;
                    } else if (type == 0 && (localSplit.length > serverSplit.length)) {
                        type = -1;
                    }
                    //判斷是否可以升級
                    switch (type) {
                        case -1: //本地版本高無法升級
                            UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0003));
                            isLooping = true;
                            return;
                        case 0:// 版本相同無法升級
                            UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0004));
                            isLooping = true;
                            return;
                        case 1:  // 本地版本低可升級
                            UsbHid.WriteCmd(Utils.resToChars(true, data, true, ErrorDictionary.UPDHHQ0000));
                            HIDThreadFlag = false;
                            isLooping = true;
                            Intent intent = new Intent();
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                            intent.setComponent(new ComponentName(ConsValue.serverApkPackage, ConsValue.serverApkPackage + ".MainActivity"));
                            intent.putExtra("tag", 3);
                            intent.putExtra("path", file.getAbsolutePath());
                            context.startActivity(intent);
                            Log.e(TAG, "需要root權限的版本升級: 開啓Install程序安裝升級!");
                            break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e(TAG, "版本升級: 升級錯誤" + e.toString());
                    UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.UPDHHQ0001));
                }
            } else {
                UsbHid.WriteCmd(Utils.resToChars(true, data, false, ErrorDictionary.TLCHHQ000E));
            }
        }
        isLooping = true;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章