因为只是个demo测试,用于记录,所以后台使用 spring+springmvc+dbutils

数据库部分:

就这么一个很简单的 tb_version 表 

v_id: 主键

v_code:  版本号(一般是整数数值)

v_name: 版本名称 (如:1.0.1)

v_desc: 版本描述介绍

v_down_url: 下载地址

v_type: 0选择性更新    1代表强制更新(没有取消按钮)

v_date: 版本发布时间

接下来是后台java部分:

@RestController
@RequestMapping("/app")
public class AppVersionController {@AutowiredQueryRunner qr;@RequestMapping("/version")public ResultBean checkVersion() throws Exception{ResultBean rb=new ResultBean();rb.setCode(1);String sql="select *from tb_version order by v_date desc limit 1";VersionBean vBean=qr.query(sql, new BeanHandler<VersionBean>(VersionBean.class));rb.setCode(0);rb.setData(vBean);return rb;}}

很简单就是查询出最新的一条返回给客户端

安卓端代码

布局文件xml:  activity_main.xml

这里就一个文本控件,到时候用来显示当前的版本号 和 版本名称

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_version"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

这里用了okgo请求框架,

    implementation 'com.jakewharton:butterknife:10.2.1'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'implementation 'com.lzy.net:okgo:3.0.4'implementation 'com.google.code.gson:gson:2.8.5'

所以自定义个MyApplication来初始化

package com.ls.myversion;import android.app.Application;import com.lzy.okgo.OkGo;public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();//初始化okgoOkGo.getInstance().init(this);}
}

记得改下清单文件和必要的权限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 可以跳转并安装apk所需权限,不然不能兼容8.0 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/><applicationandroid:name=".MyApplication".../>

编写对应的实体类VersionBean.class

public class VersionBean {private int v_code;private String v_name;private String v_desc;private String v_down_url;private int v_type;private String v_date;//省略get,set,toString()
}

安卓端核心代码:

package com.ls.myversion;import android.Manifest;
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.util.Log;
import android.widget.TextView;
import android.widget.Toast;import com.google.gson.Gson;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.model.Response;import org.json.JSONObject;import java.io.File;import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import butterknife.BindView;
import butterknife.ButterKnife;public class MainActivity extends AppCompatActivity {@BindView(R.id.tv_version)TextView tvVersion;private Integer v_code;private String v_name;AlertDialog dialog=null;private VersionBean vb;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);//读取当前版本的versionCode和versionNamePackageInfo pf=getMyVersion();v_code=pf.versionCode;v_name=pf.versionName;//显示在文本空间上tvVersion.setText(v_code+"=="+v_name);//检测是否需要更新apkcheckVersion();}private void checkVersion() {//向后台服务器发请求,获取最新的apk信息OkGo.<String>get("http://192.168.0.107:8080/xxxxx/app/version").execute(new StringCallback() {@Overridepublic void onSuccess(Response<String> response) {//请求成功时String result=response.body();try {JSONObject jsonObject=new JSONObject(result);int code=jsonObject.getInt("code");if (code==0){Gson gson=new Gson();//把服务器返回的结果转成实体类vb=gson.fromJson(jsonObject.getString("data"),VersionBean.class);//判断服务器返回的本版号是否大于当前的版本号if (vb.getV_code()>v_code){//大于则更新,然后判断是可以取消的选择性更新还是没有取消按钮的强制更新if (vb.getV_type()==0){showSelectDialog(vb);}else {//强制更新showMustDialog(vb);}}//不大于什么都不做...}}catch (Exception e){e.printStackTrace();}}});}//强制更新时的操作(弹出diglog)private void showMustDialog(VersionBean vb) {AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);builder.setCancelable(false);String apkUrl=vb.getV_down_url();builder.setTitle("版本更新").setMessage(vb.getV_desc()).setPositiveButton("更新", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {dialog.dismiss();//动态权限申请if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);} else {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);} else {showDownProgressDialog(vb);}}}});dialog=builder.create();dialog.show();}//下载弹窗private void showDownProgressDialog(VersionBean vb) {ProgressDialog pd=new ProgressDialog(MainActivity.this);pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);pd.setMessage("正在下载安装包,请稍等...");pd.setCancelable(false);pd.show();String path= getSdPath()+"/download";OkGo.<File>get(vb.getV_down_url()).execute(new FileCallback(path,"自己命名的新名字.apk") {@Overridepublic void onSuccess(Response<File> response) {Toast.makeText(MainActivity.this,"下载成功!",Toast.LENGTH_SHORT).show();pd.dismiss();installApk(new File(path+"/自己命名的新名字.apk"));}});}private void installApk(File apk) {Intent intent=new Intent();intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(Intent.ACTION_VIEW);Uri apkFileUri;if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){apkFileUri= FileProvider.getUriForFile(MainActivity.this,BuildConfig.APPLICATION_ID+".provider",apk);}else{apkFileUri=Uri.fromFile(apk);}intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkFileUri,"application/vnd.android.package-archive");Log.i("tag","开始安装");try {MainActivity.this.startActivity(intent);
//            finish();}catch (Exception e){e.printStackTrace();}}private String getSdPath(){File sdDir = null;boolean sdCardExist=Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);if (sdCardExist){sdDir=Environment.getExternalStorageDirectory();}return sdDir.toString();}private void showSelectDialog(VersionBean vb) {AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);builder.setCancelable(false);String apkUrl=vb.getV_down_url();builder.setTitle("版本更新").setMessage(vb.getV_desc()).setPositiveButton("更新", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {dialog.dismiss();//动态权限申请if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);} else {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);} else {showDownProgressDialog(vb);}}}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {dialog.dismiss();}});dialog=builder.create();dialog.show();}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);} else {showDownProgressDialog(vb);}} else {Toast.makeText(this, "你拒绝了无法下载哟!", Toast.LENGTH_SHORT).show();}break;default:}}private PackageInfo getMyVersion() {PackageInfo pf=null;try {PackageManager pm=getPackageManager();pf=pm.getPackageInfo(getPackageName(),0);}catch (Exception e){e.printStackTrace();}return pf;}
}

由于安卓7.0以后使用FileProvider访问sdk私有文件,所以需要创建个FileProvider

1.在清单文件中这样来写

<applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MyVersion"><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.ls.myversion).provider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths" /></provider></application>

2.在res下创建xml文件夹及文件

文件内容为:

<?xml version="1.0" encoding="utf-8"?>
<paths><!--path:需要临时授权访问的路径(.代表所有路径) name:就是你给这个访问路径起个名字--><external-pathname="external_files"path="." />
</paths>

至此全部搞定

效果图:

此时的code:

当检测到更新时:

强制版的效果图就不展示了,点击更新后

就会开始下载,然后开始安装

本人个人原创,如有雷同,纯属巧合,或者与本人联系,做改动。请转载或者CV组合标明出处,谢谢!(如有疑问或错误欢迎指出,本人QQ:752231513)

Android 自动检测版本更新(包含强制更新)并安装相关推荐

  1. 微信小程序自动检测版本并提示更新新版本

    微信小程序自动检测版本并提示更新新版本 微信小程序开发过程中,我们在版本更新迭代后,微信小程序客户端并不能触发自动更新,需要用户手动清理小程序后重新搜索进入后才能获取到最新的小程序版本,但是这个是用户 ...

  2. C#Winform自动检测版本更新,下载最新版本

    解决思路: 思路1:主程序打开后,先访问服务器上的版本数据接口,检查本地版本是否为最新,如果不是,则打开更新程序,关闭主程序,更新程序下载最新的主程序EXE,替换之前的EXE文件,替换完之后再打开主程 ...

  3. android 自动升级 图,手机系统更新好不好 安卓手机系统更新方法【图文教程】...

    智能手机的功能如此丰富,在于系统的智能化,目前人们所用的手机系统大多是 苹果 的ios系统以及谷歌的安卓系统.除了苹果手机外大部分智能手机使用的都是安卓系统,手机用智能系统后的功能是多了很多,但几乎每 ...

  4. iOS 自动检测版本更新APP

        联系人:石虎 QQ:1224614774   昵称: 嗡嘛呢叭咪哄                          QQ群:807236138   群称: iOS 技术交流学习群 一.实现思 ...

  5. Android 自动检测更新,自动下载apk更新版本

    注意:华为手机需要先上架华为应用市场才可以自动更新apk.其他手机可能也需要先上架 关于安卓8.0不显示下载通知问题:https://blog.csdn.net/meixi_android/artic ...

  6. Android自动检测版本及自动升级

    步骤: 1.检测当前版本的信息AndroidManifest.xml-->manifest-->android:versionName. 2.从服务器获取版本号(版本号存在于xml文件中) ...

  7. 微信小程序自动检测新版本并静默更新,及热启动和冷启动

    举个栗子,小程序发现紧急bug经修改后上线一个新版本,如果新用户还好打开就会是你新上线的最新版本,但是老用户手机上的小程序还是之前的旧版本,到此你就会想,怎样才能让老用户知道小程序版本更新了呢? 1. ...

  8. android 自动检测版本升级

    在我们APP的开发中,往往都会遇到版本的升级,因为不可能有任何一个应用做的完美无缺,所以版本升级对APP应用来说是不可缺少的一部分.像新浪微博等一些应用软件,三天两头提醒我升级.不过这样也很正常,就像 ...

  9. 微信小程序用户自动检测最新版本并更新2020年最新版亲测

    微信小程序新版本发布之后,用户使用依然是旧版本,这是因为在微信中有旧版本的缓存,不会及时更新到新版本.要想让用户更新到新版本,可以通过提示用户在微信中的"发现 - 小程序"入口中将 ...

  10. 安卓实现APP自动检测软件版本并提示更新

    安卓实现APP自动检测软件版本并提示更新 前言 当我们在做安卓项目的时候,客户的需求往往难免会发生变动.但是我们的产品已经上线了,却又需要修改的话我们该怎么办呢?也许很多应用商店已经集成了这个功能,只 ...

最新文章

  1. 【12c新特性】12c中如何自动启动PDB Pluggable Database
  2. C++ sizeof与strlen
  3. Atitit.atiJsBridge 新特性v7q329
  4. 构建之法 阅读笔记01
  5. php抓取页面400错误
  6. ICCV Neural Architects Workshop - 对话深度学习大咖
  7. python装好了怎么启动车_【填空题】Python安装好后,可以直接在CMD命令行下输入( )命令, 可启动交互式编程,提示窗口如下:...
  8. 第60课 比特超市 《小学生C++趣味编程》
  9. Python学习笔记之类(四)
  10. VMware 虚拟机优化十招
  11. savefiledialog对话框的取消和确定按钮分别返回一个什么值?_确定按钮该放在左边还是右边?...
  12. phpFreeChat 2.0.0 发布,Web 聊天室
  13. 大数据架构方案总结-ljt(转载)
  14. c语言二级编程题库,计算机二级C语言编程题库(100题)
  15. Spring 专业认证考试学习指南
  16. DuckDuckGo
  17. html制作晚安,晚安计划.html
  18. matlab读取wav文件
  19. 来自微信团队的 6 个开源项目
  20. 2021程序员必看面试指南-进大厂年薪百万需要付出多少努力?你看看你们配吗......

热门文章

  1. Ubuntu压缩文件的分卷压缩和解压
  2. xynu 2917: 整数凑和问题(搜索)
  3. NR的SSB子载波间隔讨论——为何无60kHz
  4. html如何调用flash插件,htmlflash播放器插件如何播放 网页播放器flash插件怎么解决...
  5. 中国农业大学2020计算机院线,2020年中国农业大学考研分数线公布
  6. 这几个图片格式转换工具可以收藏
  7. TZOJ--5447: Irrational Division (博弈)
  8. 什么是搜索引擎???搜索引擎的介绍
  9. 免费在excel密码破解--超好用
  10. Microsoft 提供的 USB 驱动程序