文章目录

  • 前言
  • 实战

前言

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

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

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

  • 一般升级
  • 强制升级

3. App 升级操作

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

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

实战

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"/>...<applicationandroid: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><providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.hkt.appupdate.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid: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>() {@Overridepublic void onCheck(CheckBean data) {checkAndDown(data);}@Overridepublic 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() {@Overridepublic void onClick(View v) {v.setEnabled(false);XDown.with(mContext).url(bean.getUrl()).threadCount(3).reFreshTime(500).filePath(mPath).listener(new TaskListener() {@Overridepublic void onSuccess(String filePath, String md5Msg) {installApk(mContext,filePath);dialog.dismiss();}@Overridepublic void onDownloading(XBean bean) {int progress = (int) bean.progress;updateBtn.setVisibility(View.GONE);progressBar.setVisibility(View.VISIBLE);progressBar.setProgress(progress);}@Overridepublic 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

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

  1. Android 项目必备(十六)--> 手机号 验证码 密码

    文章目录 手机号 验证码 密码 在我们的项目中,获取验证码.手机号验证以及密码输入是很常见的小功能. 手机号 1. 布局文件 <EditTextandroid:id="@+id/et_ ...

  2. android游戏性能测试,评测 篇二十九:用游戏数据来彰显实力,华为nova5i Pro游戏性能篇...

    评测 篇二十九:用游戏数据来彰显实力,华为nova5i Pro游戏性能篇 2020-01-02 00:20:00 4点赞 1收藏 2评论 笔者带着大家对华为nova5i Pro做了开箱测试,了解外观与 ...

  3. 【Microsoft Azure 的1024种玩法】二十九.基于Azure VM快速实现网络入侵检测 (IDS) 及网络安全监视 (NSM)

    [简介] 数据包捕获是一个重要组件,可以实施网络入侵检测系统 (IDS) 并执行网络安全监视 (NSM). 我们可以借助开源 IDS 工具来处理数据包捕获,并检查潜在网络入侵和恶意活动的签名. 使用网 ...

  4. Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)二十二(下单和微信支付)

    Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)二十(下单) 0.学习目标 会调用订单系统接口 实现订单结算功能 实现微信支付功能 1.订单 ...

  5. 微信小程序从入坑到放弃二十九:一个小场景搞懂冒泡事件bindtap和catchtap的区别

    摘要: 在微信小程序中,bindtap事件会产生冒泡,若不加以拦截,会一直冒泡到顶端.在某些情况下,一次点击会触发若干点击事件.为了防止冒泡,使用catchtap即可解决问题.在有全屏半透明背景的弹出 ...

  6. SAP UI5 应用开发教程之二十九 - SAP UI5 的路由和导航功能介绍试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  7. 2021年大数据Hadoop(二十九):​​​​​​​关于YARN常用参数设置

    全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 关于yarn常用参数设置 设置container分配最小内 ...

  8. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十九:LCD模块

    实验二十九:LCD模块 据说Alinx 301支持 7"TFT,好奇的朋友一定疑惑道,它们3.2"TFT以及7"TFT等两者之间究竟有何区别呢?答案很简单,前者自带控制器 ...

  9. Bootstrap入门(二十九)JS插件6:弹出框

    Bootstrap入门(二十九)JS插件6:弹出框 加入小覆盖的内容,像在iPad上,用于存放非主要信息 弹出框是依赖于工具提示插件的,那它也和工具提示是一样的,是需要初始化才能够使用的 首先我们引入 ...

最新文章

  1. 机器人编程语言python-机器人行业,10大流行编程语言对比
  2. Python机器学习基础教程-第2章-监督学习之K近邻
  3. Xmpp实现简单聊天系列 --- ②用户注册和登陆
  4. puppet完全攻略(一)puppet应用原理及安装部署
  5. 【数据挖掘】决策树算法简介 ( 决策树模型 | 模型示例 | 决策树算法性能要求 | 递归创建决策树 | 树根属性选择 )
  6. JDBC的CRUD操作之PreparedStatement的查询操作
  7. 交叉编译器arm-linux-gcc
  8. 正则只能出现特定字符_python正则表达式的简单使用总结
  9. 云服务器 存放 文件夹,云服务器 存放 文件夹
  10. S5PV210 芯片降价
  11. 计算机毕业设计中用Java编写泛型与集合框架
  12. 跨考武汉大学计算机专业,武汉大学计算机专业考研心得_跨考网
  13. 【转】javascript弹出固定大小的窗口页面
  14. SSM框架整合思想及步骤
  15. rest_framework学习之序列化(Serializer)
  16. JLINK V9项目启动【jlink接口定义】【开启VCOM(虚拟串口)功能】
  17. 计算机桌面怎样创建文件,怎么在电脑桌面新建一个透明的文件夹?
  18. hl7 java_HL7 2.X解析(JAVA版)
  19. 为什么魂斗罗只有128KB却能实现那么长的剧情有答案了
  20. Python基础(3)——北京市地铁买票问题(思维练习题)

热门文章

  1. 关于主从复制的超详细解析(全)
  2. 06-02 标签管理
  3. 文科生也能看懂,什么是熵
  4. 随机变量的函数的分布
  5. MeSH 医学主题词数据库
  6. 字节流和字符流的应用
  7. STC 纯硬件自动下载电路 - 三极管负脉冲电路
  8. FFmpeg 安装与使用
  9. EJB初探(二)为什么需要EJB
  10. int argc,char*argv[ ]的简洁解释