Android开发 应用软件更新通用方式--强制/非强制/远程控制/浏览器 更新

一、通知栏自动下载更新

XVersionUpdate是VersionUpdate的升级版,全面优化代码,提高兼容性和稳定性,增强用户体验,帮助我们快速实现版本更新功能。

(1).效果图:

在这里插入图片描述
更新内容:

最近更新内容:
1.修复重复下载和进度条显示异常的bug
2.新增取消下载功能
3.兼容Android8.0
4.修复优化评论中的其他问题。

(2).实现过程:

build. gradle (app)文件中注入以下依赖

dependencies {
   
    ......
    implementation 'com.androidkun:xversionupdate:1.0.6'
}

可以在首次启动界面的Oncreate 方法下调用API检测版本更新,让其有新版本,在启动APP后自动下载更新。

@Override
    protected void onCreate(Bundle savedInstanceState) {
   
        super.onCreate(savedInstanceState);
        VersionUpdateConfig.getInstance()//获取配置实例
                .setContext(LoginActivit这里写界面.this)//设置上下文
                .setDownLoadURL("https://www.armpro.cn/assets/这里是下载地址.apk")//设置文件下载链接
                .setNewVersion("3")//设置即将下载的APK的版本号,避免重复下载
                .setFileSavePath("/storage/emulated/0/")//设置文件保存路径(可不设置)
                .setNotificationIconRes(R.drawable.ic_launcher)//设置通知图标
                .setNotificationSmallIconRes(R.drawable.ic_launcher)//设置通知小图标
                .setNotificationTitle("软件有新版本,正在下载中...")//设置通知标题
                .startDownLoad();//开始下载
    }

接下来就不用再做任何处理了,有新版本时启动APP,就会自动在通知栏显示下载进度,在下载完成后会自动跳到安装页面。

项目源码地址:https://github.com/AndroidKun/XVersionUpdate


二、主界面弹窗选择 更新/强制更新/浏览器更新(通用更新)

这个版本的代码通用性很高,代码也很少,理论适用与任意的安卓系统(Android 10版本已测试可用),最简单的源代码实现的更新服务,检测远程更新代码多种更新方式的选择,代码可修改定制性很强,支持同时多个更新链接配置进行容错处理,应用内直链直接下载时失败时会弹出提示错误弹窗,并可选择浏览器下载。可以简单使用gitee搭建静态网页当后台更新服务(源码见下方↓)。

(1).弹窗效果图:

强制与非强制更新效果
应用内更新图
应用内直链直接下载失败时

(2).实现过程:

1. 搭建json格式网页

这里提供源码和搭建后数据展示情况,具体详细的搭建教程很简单,随便百度就可以找到,这里不再重复搭建过程。

搭建源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>https://yirj.gitee.io/项目名称</title>
</head>
<body>
<pre><code id="json"></code></pre>
</body>
<script type="text/javascript">
let btn = document.querySelector('#json');
let data = {
     "hasUpdate": true,"NoIgnorable": true,"versionCode": 51,"versionName": "2.4.1","updateLog": "\n1、更改保存图片的存储路径。\n2、更改软件更新的提示模式。\n3、调整非强制更新控制方式。\n4、新增存储权限的申请授权位置。","apkUrl": "https://www.yuming.com/assets/a某o130.apk","webUrl": "https://yirj.gitee.io/update111","apkSize": "29.5MB"};
btn.textContent = JSON.stringify(data, null, 2);
</script>
</html>
搭建成的链接
https://yirj.gitee.io/项目名称
访问相关展示
{
   
    hasUpdate: true,  //是否有更新 默认true
    NoIgnorable: true, //不 可忽略更新  强制:true 非强制:false
    versionCode: 51,   //服务端的版本号  
    versionName: "2.4.1", //服务端的版本名
    updateLog: "\n1、更改保存图片的存储路径。\n2、更改软件更新的提示模式。\n3、调整非强制更新控制方式。\n4、新增存储权限的申请授权位置。", //更新提示内容
    apkUrl: "https://www.yuming.com/assets/a某o130.apk",//新版本APK直链下载地址
    webUrl: "https://yirj.gitee.io/update111",//浏览器更新链接,随意放(直链、蓝奏、官网均可)
    apkSize: "29.5MB" //新版本的大小 随意写就好
}

2. 创建file_paths.xml及修改AndroidManifest.xml

创建file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
修改AndroidManifest.xml
    <!-- 拥有完全的网络访问权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 修改或删除您的USB存储设备中的内容 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- 查看网络连接 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
        .....
        android:label="@string/app_name"
        android:requestLegacyExternalStorage="true">

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        .........
</application>

3. 启动主界面加入检测更新代码

在软件的检测更新界面的Oncreate方法下,加入检测更新的代码。
更新弹窗出现条件hasUpdate为true且用户使用的APP的版本号<服务端的版本号,就会提示更新。
强制更新出现条件:有更新后跳转到showDialogUpdate方法,判断 NoIgnorable为true则就是强制更新。

import android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class MainActivity extends Activity {
   
    private String TAG;
    public static JSONObject jSONObject = null;
    private static boolean hasUpdate, NoIgnorable = true; //是否有更新。 不可忽略的更新
    private static int versionCode = 0;
    private static String versionName, updateLog, apkSize, apkUrl, webUrl = "";
    private static String[] upl = new String[]{
   "https://yirj.gitee.io/更新链接1", "https://yirj.gitee.io/更新链接2"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
        super.onCreate(savedInstanceState);

        //忽略证书校验的相关方法,http报证书异常或者不安全链接就启用它
        //handleSSLHandshake();
        //应用内直链更新,要先动态申请获取存储权限
        getReadPermissions();

        //启动子线程--更新功能实现调用
        new Thread() {
   
            @Override
            public void run() {
   
                Looper.prepare();
                try {
   
                    GetServerJson();
                    //hasUpdate为true 且 本程序的版本号小于服务器的版本号,那么提示用户更新
                    if (hasUpdate & getVersionCode() < versionCode) {
   
                        showDialogUpdate();//弹出提示版本更新的对话框
                    } else {
   
                        //Toast.makeText(MainActivity.this, "当前已是最新版本", Toast.LENGTH_SHORT).show();
                    }
                } catch (Exception e) {
   
                    e.printStackTrace();
                }
                Looper.loop();
            }
        }.start();
    }


    /**
     * 获取当前使用的软件包的版本号
     */
    public int getVersionCode() {
   
        try {
   
            //获取packagemanager的实例
            PackageManager packageManager = getPackageManager();
            //getPackageName()是你当前类的包名,0代表是获取版本信息
            PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(), 0);
            Log.e("TAG", "版本号" + packInfo.versionCode);  //更新软件用的是版本号
            return packInfo.versionCode;
        } catch (Exception e) {
   
            e.printStackTrace();
        }
        return 1;
    }

    /**
     * 提示版本更新的对话框
     */
    public void showDialogUpdate() {
   
        //hasUpdate为true且程序版本号<服务端版本号,提示用户更新
        if (NoIgnorable) {
    //NoIgnorable为true 就是强制更新,无“取消”按钮
            // 这里的属性可以一直设置,因为每次设置后返回的是一个builder对象
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setCancelable(false); //开启强制更新,无法触摸外部关闭
            builder.setTitle("是否升级到" + versionName + "版本?").
                    // 设置提示框的图标
                    setIcon(R.drawable.ic_launcher).
                    // 设置要显示的信息
                    setMessage("新版本大小:" + apkSize + "\n" + updateLog).
                    // 设置确定按钮
                    setPositiveButton("更新", new DialogInterface.OnClickListener() {
   
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
   
                            loadNewVersionProgress();//程序内直接下载最新的版本程序
                        }
                    }).setNeutralButton("浏览器下载", new DialogInterface.OnClickListener() {
   //中性按钮 应用内下载失败可用它更新 
                @Override
                public void onClick(DialogInterface dialog, int which) {
   
                    Uri uri = Uri.parse(webUrl);
                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                    startActivity(intent);
                    finish(); //强制更新,点击后销毁应用
                }
            });
            // 显示对话框
            builder.create().show();
        } else {
   
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
//            builder.setCancelable(false); //非强制更新,屏蔽此行,触摸外部或退出键可关闭
            builder.setTitle("是否升级到" + versionName + "版本?").
                    setMessage("新版本大小:" + apkSize + "\n" + updateLog).
                    setPositiveButton("更新", new DialogInterface.OnClickListener() {
   //正按钮
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
   
                            loadNewVersionProgress();//下载最新的版本程序
                        }
                    }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
   //负按钮
                @Override
                public void onClick(DialogInterface dialog, int which) {
   
//                        finish(); //屏蔽销毁,不做任何处理
                }
            }).setNeutralButton("浏览器下载", new DialogInterface.OnClickListener() {
   //中性按钮
                @Override
                public void onClick(DialogInterface dialog, int which) {
   
                    Uri uri = Uri.parse(webUrl);
                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                    startActivity(intent);
                    //finish(); //屏蔽销毁,访问浏览器,程序不会退出
                }
            });
            // 显示对话框
            builder.create().show();
        }
    }

   //轮询验证两个更新链接,返回有效链接
    public static String checkUrl(String[] ltl) {
   
        String resultUrl = null;
        for (String url : ltl) {
   
            resultUrl = url;
            try {
   
               //调用检查链接是否有效的方法
                String result = get(url);
                if (result != null && result.length() != 0) {
   
                    break;
                }
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        }
        return resultUrl;
    }

    //检查更新链接是否有效的方法
    public static String get(String url) {
   
        URL infoUrl = null;
        InputStream inStream = null;
        String line = "";
        try {
   
            infoUrl = new URL(url);
            URLConnection connection = infoUrl.openConnection();
            HttpURLConnection httpConnection = (HttpURLConnection) connection;
            int responseCode = httpConnection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
   
                inStream = httpConnection.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8"));
                StringBuilder strber = new StringBuilder();
                while ((line = reader.readLine()) != null)
                    strber.append
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值