Android APK 程序实现自动更新,java服务命令处理无弹窗,终极解决方案

安卓更新方式,网上五花八门,但是真正实现apk自动更新无痕迹的方式,少之又少,毕竟不要钱的方式,稳定的方式才能让开发者在困难中脱颖而出。

安卓程序如何做到自动更新?安卓程序如何实现无弹框更新?

1,安卓apk自动更新方式?

a,第三方平台更新apk,灰度发布,用友

b,系统更新方式有弹窗contenx,通过窗体上下文方式实现更新。

c,通过安卓程序系统服务实现命令更新,自启等。

d,通过安卓反编译修改安卓源码包实现自动更新。

我们采用安卓系统apk方式实现apk重启更新上图:

整体流程:

1,我们tomcat服务器挂载或者ftp服务器提供apkurl地址。

2,安卓程序定时拉取获取监听下载url地址,下载apk到本地获取新的版本,对比当前的apk版本进行更新。

3,如果当前版本大于运行的apk程序版本进行apk更新。

4,通过我们java程序执行cmd命令 adb 方式去读取命令行执行方式返回信息进行程序更新。

5,通过adb命令把apk推送到系统程序里面,获取apk包名实现程序重启,即可完成安卓程序自动更新。

实现弹框方式:

public boolean installApk(Context context) throws Exception {
        try {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(new File(configPath + "app-release.apk")), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //打开安装应用界面
            context.startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

 这种方式不能实现自动发包,但是可以完成自动更新。

通过cmd命令操作实现apk覆盖自动重启:

1,连接到adb 

adb connect 127.0.0.1:7555

 2,查看连接状态

adb devices 

3,通过adb root 命令权限。

adb root

 4,获取系统app权限

adb remount

 

5,把app上传到系统里面

adb push D:\app-release.apk /system/app/

6,查找包名重启apk

adb shell pm list package -f

 查看启动Activity类名

adb shell 

dumpsys package com.instwall.launch  (进行系统shell里面去查看包名)

 

7,重启程序(复制系统app下面的应用包名)

adb shell am start -n <应用包名>/<应用启动Activity绝对名称>

adb shell am start -n  com.instwall.launch/.MainActivity

 我们可以看到当前程序已经启动。但是命令行的方式还是需要手动去打命令自然不满足我们自动更新的操作处理,至于这里为什么不用第三方平台的问题,原因是因为我们的apk程序要支持离线更新,apk程序本身在专网里面,所以有很多限制。这里我们可以通过java 程序去代替我们执行adb 脚本命令从而实现apk自动更新,只不过步骤要写很多。

贴几个代码执行脚本的代码:

版本更新的核心处理类。

package com.instwall.launch.task;


import static com.instwall.launch.contans.Contans.configPath;
import static com.instwall.launch.utils.FileDirectoryUtils.dropVedio;
import static com.instwall.launch.utils.FileDirectoryUtils.getDownApkVersion;
import static com.instwall.launch.utils.FileDirectoryUtils.readAppInfo;
import static com.instwall.launch.utils.FileDirectoryUtils.writeAppInfo;
import static com.instwall.launch.utils.FileDirectoryUtils.writeLog;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.instwall.launch.contans.Contans;
import com.instwall.launch.utils.CmdTerminalUtils;
import com.instwall.launch.utils.FileDirectoryUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

/**
 * 版本更新
 */
public class UpdateVersionTask implements Runnable {
    private final Context context;

    public UpdateVersionTask(Context context) {
        this.context = context;
    }

    private static void download(String mUrl, String mPath) throws MalformedURLException {
        // 下载网络文件
        int byteread = 0;
        int mDownloadSize = 0;
        URL url = new URL(mUrl);
        URLConnection conn = null;
        InputStream is = null;
        FileOutputStream os = null;
        try {
            conn = url.openConnection();
            is = conn.getInputStream();
            os = new FileOutputStream(mPath);
            byte[] buffer = new byte[1204];
            int length;
            while ((byteread = is.read(buffer)) != -1) {
                mDownloadSize += byteread;
                os.write(buffer, 0, byteread);
            }
            Log.i("文件下载成功-------", mPath);
            writeLog("apk down  file  success .......... " + mPath);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            //  dropFile(mPath);
            writeLog("apk down  file  error .......... " + e.getMessage());

            Log.e("文件下载失败-------", e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            // dropFile(mPath);
            Log.e("文件下载失败---删除当前下载文件----", e.getMessage());
            writeLog("apk down  file  error .......... " + e.getMessage());

        } catch (Exception e) {
            e.printStackTrace();
            //  dropFile(mPath);
            Log.e("文件下载失败---删除当前下载文件----", e.getMessage());
            writeLog("apk down  file  error .......... " + e.getMessage());

        } finally {
            // 关闭输出流
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 关闭输入流
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }


    @Override
    public void run() {
        try {
            download(Contans.getApiDownUrl(), configPath + "app-release.apk");
            //获取下载的版本包
            int verson = getDownApkVersion(context, configPath + "app-release.apk");
            // writeLog("down apk  version .......... " + verson);
            int installVersion = readAppInfo();
            if (installVersion == 0) {
                installVersion = FileDirectoryUtils.getVersionCode(context);
            }
            writeLog("down now  version .......... " + installVersion);
            //如果下载的版本大于当前运行的apk版本直接安装最新并且写入文件
            if (verson > installVersion) {
                writeLog("down now  version .......... " + installVersion);
                //安装apk
                CmdTerminalUtils.startApk();
                //写入当前版本到文件夹
                writeAppInfo(verson);
                //删除没有下载完成的视频
                dropVedio();
            }

        } catch (Exception e) {
            writeLog("ReportHear api  pulish   error .......... " + e.getMessage());
        }

    }

    public boolean installApk(Context context) throws Exception {
        try {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(new File(configPath + "app-release.apk")), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //打开安装应用界面
            context.startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private boolean isHasInstallPermissionWithO(Context context) {
        if (context == null) {
            return false;
        }
        return context.getPackageManager().canRequestPackageInstalls();
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void startInstallPermissionSettingActivity(Context context) {
        if (context == null) {
            return;
        }
        Intent intent = new Intent();
        //获取当前apk包URI,并设置到intent中(这一步设置,可让“未知应用权限设置界面”只显示当前应用的设置项)
        Uri packageURI = Uri.parse("package:" + context.getPackageName());
        intent.setData(packageURI);
        //设置不同版本跳转未知应用的动作
        if (Build.VERSION.SDK_INT >= 26) {
            //intent = new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,packageURI);
            intent.setAction(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        } else {
            intent.setAction(android.provider.Settings.ACTION_SECURITY_SETTINGS);
        }
        context.startActivity(intent);
    }

}

1,建立连接

 /**
     * 建立连接
     *
     * @return
     */
    public static boolean firstCmd() throws IOException {

        Process process = null;
        String command = "D:\\platform-tools\\adb connect 127.0.0.1:7555";   //sdk所在位置
        process = Runtime.getRuntime().exec(command);
        InputStreamReader ir = new InputStreamReader(process.getInputStream());

        try {

            LineNumberReader input = new LineNumberReader(ir);
            String line;
            List<String> list = new ArrayList<>();
            while ((line = input.readLine()) != null) {
                list.add(line);
            }

            input.close();
            for (String result : list) {
                if (result.contains("device") || result.contains("already")) {
                    writeLog("adb: "+command );
                    writeLog("adb first cmd  message: "+result );
                    return true;
                }
                System.out.println("message" + result);
            }

        } catch (IOException e) {

            System.err.println("IOException" + e.getMessage());

        } finally {
            ir.close();
        }
        return false;
    }

2,赋予权限:

 /**
     * 建立连接
     *
     * @return
     */
    public static boolean twoCmd() throws IOException {

        Process process = null;


        String command = "D:\\platform-tools\\adb root";   //sdk所在位置
        process = Runtime.getRuntime().exec(command);

        InputStreamReader ir = new InputStreamReader(process.getInputStream());
        try {


            LineNumberReader input = new LineNumberReader(ir);

            String line;
            List<String> list = new ArrayList<>();
            while ((line = input.readLine()) != null) {
                list.add(line);
            }
            for (String result : list) {

                if (result.equals("adbd is already running as root")) {
                    writeLog("adb: "+command );
                    writeLog("adb tow cmd  message: "+result );
                    return true;
                }
                if (result.contains("device")) {
                    writeLog("adb: "+command );
                    writeLog("adb tow cmd  message: "+result );
                    return true;
                }
                System.out.println("message" + result);
            }
        } catch (IOException e) {

            System.err.println("IOException" + e.getMessage());

        } finally {
            ir.close();
        }
        return false;
    }

3,后面的步骤同上,把不同脚本信息的命令通过cmd执行,获取特定的返回messge信息判断程序命令是否执行成功。

 /**
     * 通过命令启动apk
     */
    public static void startApk() {
        //定义执行成功标识,如果执行不成功一直执行到成功
        boolean flag = true;
        while (flag)
            try {
                 //执行命令
                boolean first = firstCmd();
                if (first) {
                    //执行权限
                    boolean two = twoCmd();
                    if (two) {
                        boolean three = threeCmd();
                        if (three) {
                            fourCmd();
                            fiveCmd();
                            flag = false;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

    }

我们之间通过字符串包含的方式判断命令是否执行完成,即可完成命令的执行,从而实现我们java程序去处理adb命令从而实现apk自动更新。

到这里就结束了。自动更新完成,喜欢记得关注和转发。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杨互联网

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值