让我们自己的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;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值