Android 项目必备(二十九)-->App 在线升级与更新

在这里插入图片描述

文章目录

前言

1. 用户使用 App 的时候升级提醒有两种方式获得:

  • 通过应用市场获取;
  • 打开应用之后提醒用户更新升级。

2. 更新操作一般是在用户点击了升级按钮之后开始执行的,这里的升级操作也分为两种形式:

  • 一般升级
  • 强制升级

3. App 升级操作

  • 应用市场的 app 升级
    在 App Store 中升级需要为 App Store 上传新版 App ,我们在新版本完成之后都会上传到 App Store 中,在审核完成之后就相当于完成了这个应用市场的发布了,也就是发布上线了。这时候如果用户安装了这个应用市场,那么就能看到我们的 App 有新版本的升级提醒。

  • 应用内升级
    在应用内升级主要是通过调用服务器端接口获取应用的升级信息,然后通过获取的服务器升级应用信息与本地的 App 版本比对,若服务器下发的最新的 App 版本高于本地的版本号,则说明有新版本发布,那么我们就可以执行更新操作了,否则忽略掉即可。

实战

01.gif

002.gif

1. 权限申请

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    ...
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:networkSecurityConfig="@xml/network_security_config"
        android:theme="@style/Theme.AppUpdate"
        android:name=".MyApp"
        tools:ignore="UnusedAttribute">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.hkt.appupdate.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths"/>
        </provider>
    </application>

2. network_security_config.xml

<?xml version ="1.0" encoding ="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

3. filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external" path="."/>
</paths>

4. 服务端
json 文件:http://59.110.162.30/app_updater_version.json

5. 封装 updatelib, 对外的统一接口
XDown 为程序入口,它提供以下方法:

  • pause() 暂停任务
  • start() 开始任务
  • stopTask() 停止任务 ,如果你的apk更新不是在 activity 使用,建议在app退出的时候,使用该方法,防止内存泄漏
  • stopTaskAndDeleteCache() 停止任务,并删除已文件和数据库,当任务失败时,可以使用
  • isTaskExists() 任务是否存在
  • isRunning() 是否正在下载
  • updateListener() 从后台退回来,如果任务正在下载,直接更新接口就可以了,UI就不会乱了
  • deleteCacheAndStart() 当任务失败时,可以用这个把缓存文件和数据删了
/**
 * Created on 2021/12/29 9:41
 * 对外的统一接口
 * @author Gong Youqiang
 */
public class XDown {
    public static CheckRequest checkWith(Context context){
        return CheckRequest.get(context);
    }

    public static RequestManager with(Context context){
        XDBManager.getInstance().config(context.getApplicationContext());
        return RequestManager.getInstance().with(context);
    }

    /**
     * 暂停
     */
    public static void pause(){
        if (RequestManager.getInstance().mTask != null) {
            RequestManager.getInstance().mTask.pause();
        }
    }

    /**
     * 开始
     */
    public static void start(){
        if (RequestManager.getInstance().mTask != null) {
            RequestManager.getInstance().mTask.start();
        }
    }

    /**
     * 暂停任务,虽然已经监听activity的生命周期了,但是有些特殊情况还需要用户自己去判断
     */
    public static void stopTask(){
        if (RequestManager.getInstance().mTask != null) {
            RequestManager.getInstance().mTask.pause();
            RequestManager.getInstance().mTask = null;
        }
    }

    /**
     * 暂停任务,和删除缓存
     */
    public static void stopTaskAndDeleteCache() {
        if (RequestManager.getInstance().mTask != null) {
            RequestManager.getInstance().mTask.pause();
            RequestManager.getInstance().mTask.deleteCache();
            RequestManager.getInstance().mTask = null;
        }

    }

    /**
     * 是否存在
     * @return
     */
    public static boolean isTaskExists(){
        return RequestManager.getInstance().mTask != null;
    }

    /**
     * 是否正在下载
     * @return
     */
    public static boolean isRunning(){
        if (RequestManager.getInstance().mTask != null) {
            return RequestManager.getInstance().mTask.isRunning();
        }
        return false;
    }

    /**
     * 当存在时,可以直接跟新接口就行了
     * @param listener
     */
    public static void updateListener(BaseListener listener){
        if (RequestManager.getInstance().mTask != null) {
            RequestManager.getInstance().mTask.updateListener(listener);
        }
    }

    /**
     * 当存在错误下载不了,尝试把缓存文件和数据库删除了使用
     */
    public static void deleteCacheAndStart(){
        stopTaskAndDeleteCache();
        start();
    }
}

6. 使用

/**
 * Created on 2022/2/11 10:24
 *
 * @author Gong Youqiang
 */
public class UpdateApk {
    private static final String TAG = "UpdateApk";
    private Activity mContext;
    private String mPath;

    public UpdateApk(Activity context) {
        mContext = context;
        mPath = Environment.getExternalStorageDirectory().getAbsolutePath();
        MyApp.HANDLER.postDelayed(() -> {
            if (NetUtils.isNetworkConnected()) {
                XDown.checkWith(context)
                        .url("http://59.110.162.30/app_updater_version.json")
                        .get()
                        .listener(new CheckListener<CheckBean>() {
                            @Override
                            public void onCheck(CheckBean data) {
                                checkAndDown(data);
                            }

                            @Override
                            public void onFail(String errorMsg) {
                                Log.d(TAG, "onFail: " + errorMsg);
                            }
                        }).check();
            }
        },500);
    }

    private void checkAndDown(CheckBean bean) {
        if (bean != null) {
            if (Integer.parseInt(bean.getVersionCode()) > PackageUtils.getVersionCode(mContext) ) {
                final CusDialog dialog = new CusDialog.Builder()
                        .setContext(mContext)
                        .setLayoutId(R.layout.update_layout)
                        .showAlphaBg(true)
                        .setWidth(480)
                        .setHeight(620)
                        .builder();
                String[] split = bean.getContent().split("\\|");
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < split.length; i++) {
                    sb.append(split[i]);
                    sb.append("\n");
                }
                dialog.setTextView(R.id.update_info, sb.toString());
                dialog.setTextView(R.id.update_version, mContext.getString(R.string.update_version, bean.getTitle()));
                dialog.setDismissByid(R.id.update_dismiss);
                final TextView updateBtn = dialog.getViewbyId(R.id.tv_update);
                final NumberProgressBar progressBar = dialog.getViewbyId(R.id.number_progress_bar);


                dialog.setOnClickListener(R.id.tv_update, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        v.setEnabled(false);
                        XDown.with(mContext)
                                .url(bean.getUrl())
                                .threadCount(3)
                                .reFreshTime(500)
                                .filePath(mPath)
                                .listener(new TaskListener() {
                                    @Override
                                    public void onSuccess(String filePath, String md5Msg) {
                                        installApk(mContext,filePath);
                                        dialog.dismiss();
                                    }

                                    @Override
                                    public void onDownloading(XBean bean) {
                                        int progress = (int) bean.progress;
                                        updateBtn.setVisibility(View.GONE);
                                        progressBar.setVisibility(View.VISIBLE);
                                        progressBar.setProgress(progress);
                                    }

                                    @Override
                                    public void onFail(String errorMsg) {
                                        Log.d(TAG, "onFail: " + errorMsg);
                                        dialog.dismiss();
                                    }
                                }).down();
                    }
                });

            }
        }
    }

    public static void installApk(Activity context, String filePath){
        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(Intent.ACTION_VIEW);
        File apkFile = new File(filePath);
        Uri uri = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile);
        } else {
            uri = Uri.fromFile(apkFile);
        }
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        context.startActivity(intent);
    }
}

demo 传送门:AppUpdate.rar

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kevin-Dev

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

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

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

打赏作者

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

抵扣说明:

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

余额充值