在各大APP软件中都含有一个功能,就是在软件中进行检查软件版本号,并进行下载安装的操作。
今天,我也记录一下,我使用OkGo网络协议进行软件版本更新的操作。
一、基础配置
1.如果安卓版本号是安卓9或更高版本,如要在application中添加
android:usesCleartextTraffic=“true” 。这句话。因为在安卓高版本中,谷歌对设备访问网络做了限制,具体原因可以去查询。
2. 在Mainfest.xml中,添加权限。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<!--下面这个权限一定还要添加,否则程序不会自动安装-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
开启未知应用安装权限的入口,并设置允许安装。
3.创建一个空类,继承自FileProvider
//空类
public class MyFileProvider extends FileProvider {
}
4.创建res/xml/fiel_path.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 其中external-path节点表示Environment.getExternalStorageDirectory()路径
path表示apk文件在Environment.getExternalStorageDirectory()路径下的文件夹名称-->
<external-path name="external_storage_root" path="download" />
</paths>
5.清单文件中添加provider
<provider
android:name="com.example.updatesoft.MyFileProvider"
android:authorities="${applicationId}.MyFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
二.在MainActivity的布局文件中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<!--更新按钮-->
<TextView
android:id="@+id/text_iew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="更新软件按钮"
android:layout_gravity="center"
android:layout_marginTop="50dp"
android:background="@drawable/bg_button_pressed"
android:padding="10dp"/>
<!--版本信息-->
<TextView
android:id="@+id/version_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="这是1.0版本"
android:layout_marginTop="50dp"/>
<!--下载进程圆圈-->
<ProgressBar
android:id="@+id/progress_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:visibility="gone"/>
<!--下载进程-->
<TextView
android:id="@+id/tvDownloadSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text=""
android:layout_marginTop="50dp"/>
</LinearLayout>
三.安卓6.0之后需要动态添加权限,需要访问本地文件
//android6.0之后要动态获取权限
private void checkPermission(Activity activity) {
// Storage Permissions
final int REQUEST_EXTERNAL_STORAGE = 1;
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(MainActivity.this,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
四.进行版本号比较
获取本地的版本号与云端版本号。通过比较两个版本号信息,判断时候需要进行版本更新操作。如果本地的版本号小于云端版本号,就可以进行下载并安装的操作。但是本地的版本号与云端版本号相同,就无需进行下载安装,并可以对用户进行提示。
获取本地版本号的方法:
/**
* 获取当前软件版本信息
* 根据包名获取应用程序相关信息
* flags:指定信息的标签,指定了标签就会获取相应的相关信息
* getPackageName():获取应用程序的包名
*/
public static String getPresentVersion(Context context) {
String presentVersion = "";
PackageManager packageManager = context.getPackageManager(); //包的管理者,获取应用程序中清单文件中的信息
try {
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
presentVersion = packageInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace(); //找不到包的异常
}
return presentVersion;
}
版本号比较大小的方法:
/**
* 版本号比较
* 0代表相等,1代表version1大于version2,-1代表version1小于version2
*
* @param version1
* @param version2
* @return
*/
private static int compareVersion(String version1, String version2) {
if (version1.equals(version2)) {
return 0;
}
String[] version1Array = version1.split("\\.");
String[] version2Array = version2.split("\\.");
int index = 0;
// 获取最小长度值
int minLen = Math.min(version1Array.length, version2Array.length);
int diff = 0;
// 循环判断每位的大小
while (index < minLen
&& (diff = Integer.parseInt(version1Array[index])
- Integer.parseInt(version2Array[index])) == 0) {
index++;
}
if (diff == 0) {
// 如果位数不一致,比较多余位数
for (int i = index; i < version1Array.length; i++) {
if (Integer.parseInt(version1Array[i]) > 0) {
return 1;
}
}
for (int i = index; i < version2Array.length; i++) {
if (Integer.parseInt(version2Array[i]) > 0) {
return -1;
}
}
return 0;
} else {
return diff > 0 ? 1 : -1;
}
}
通过OkGo获取云端版本号并进行版本比较的方法
public void upApk(){
//StyledDialog.buildMdLoading("正在获取新版本信息,请稍等").show();
OkGo.<String>post("这里填写获取APK的云端服务器地址")
.tag(this)
.params("appId","1") //向服务器传送请求appId为1的软件信息,这里根据自己的实际情况进行填写。
.execute(new StringCallback() {
@Override
public void onSuccess(Response<String> response) {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(response.body());
String dataStr = jsonObject.getString("data");
JSONObject dataObj = new JSONObject(dataStr);
String cloudVersion = dataObj.getString("version"); //获取云端版本号
String presentVersion = getPresentVersion(MainActivity.this);
String apkPath=dataObj.getString("apk");//获取云端软件的下载地址
if (compareVersion(presentVersion,cloudVersion)==-1) { //比较两个版本号,判断是否需要下载
Toast.makeText(MainActivity.this, "可以更新", Toast.LENGTH_LONG).show();
downloadApk(apkPath);
}else{
Toast.makeText(MainActivity.this, "已是最新版本", Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onError(Response<String> response) {
Toast.makeText(MainActivity.this, "访问出错了", Toast.LENGTH_LONG).show();
super.onError(response);
}
@Override
public void onFinish() {
super.onFinish();
//StyledDialog.dismissLoading(MainActivity.this);
}
});
}
五.下载APK
/**
* @description 下载APK
*/
public void downloadApk(String apkPath){
String headStr = "这里填写获取APK的云端服务器地址";
final String downloadFile = headStr + apkPath;
final String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/download"; //APK的下载目录
final String fileName = "abc.apk"; //下载的APK的新文件名
file = new File(filePath + "/" + fileName);
file = new File(filePath);
if (file.exists())
file.delete();
final Context finalContext = context;
OkGo.<File>post(downloadFile)
.tag(context)
.execute(new FileCallback(filePath,fileName) {
@Override
public void onSuccess(Response<File> response) {
String absolutePath = response.body().getAbsolutePath();
File apkFile = response.body().getAbsoluteFile();
}
//@Override
@SuppressLint("SetTextI18n")
public void downloadProgress(Progress progress) {
super.downloadProgress(progress);
//如何拿到当前的下载进度
progressBar.setVisibility(View.VISIBLE);
//progressBar.setProgress((int)(progress.fraction*100));
//这里回调下载进度(该回调在主线程,可以直接更新ui)
//currentSize totalSize以字节byte为单位
Log.e("currentSize", String.valueOf(progress.currentSize)); //当前下载大小
Log.e("totalSize", String.valueOf(progress.totalSize)); //APK总的大小值
Log.e("speed", String.valueOf(progress.speed)); //下载速度
tvDownloadSize.setText(progress.currentSize + "/" + progress.totalSize);
if (progress.currentSize==progress.totalSize){
progressBar.setVisibility(View.GONE);
installApk(); //下载完后进行安装APK
}
}
});
}
六.安装APK
private void installApk() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
String type = "application/vnd.android.package-archive";
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(MainActivity.this, "com.example.updatesoft.MyFileProvider",getApkFile());
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
uri = Uri.fromFile(getApkFile());
}
intent.setDataAndType(uri, type);
MainActivity.this.startActivity(intent);
}
获取本地APK文件
/**
* 获取本地Apk文件
*/
private static File getApkFile() {
String apkDir = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
+ "download" + File.separator;
String apkName = "abc.apk";
File newApkFile = new File(apkDir, apkName); //File()的第一个参数为文件的父路径,第二个参数是文件名
return newApkFile;
}
整个操作流程到此就完成了。