做移动软件开发,必然要涉及软件版本升级。版本检测什么的我就不多说了,网上一大堆,这里主要是在获取新版本APK地址后的下载操作。
第一步:判断任务是否已经存在如果存在,先清除原任务
if (downloadId != 0) { //根据任务ID判断是否存在相同的下载任务,如果有则清除
clearCurrentTask(mContext, downloadId);
}
downloadId = downLoadApk(mContext, url, describeStr);
第二步:开启下载任务
public static long downLoadApk(Context context, String url, String describeStr) {
// 得到系统的下载管理
DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
// 以下两行代码可以让下载的apk文件被直接安装而不用使用Fileprovider,系统7.0或者以上才启动。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(localBuilder.build());
}
DownloadManager.Request requestApk = new DownloadManager.Request(uri);
// 设置在什么网络下下载
requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 下载中和下载完后都显示通知栏
requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (saveFile.exists()) { //判断文件是否存在,存在的话先删除
saveFile.delete();
}
requestApk.setDestinationUri(Uri.fromFile(saveFile));
// 表示允许MediaScanner扫描到这个文件,默认不允许。
requestApk.allowScanningByMediaScanner();
// 设置下载中通知栏的提示消息
requestApk.setTitle("XXXX更新下载");
// 设置设置下载中通知栏提示的介绍
requestApk.setDescription(describeStr);
// 7.0以上的系统适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestApk.setRequiresDeviceIdle(false);
requestApk.setRequiresCharging(false);
}
// 启动下载,该方法返回系统为当前下载请求分配的一个唯一的ID
long downLoadId = manager.enqueue(requestApk);
return downLoadId;
}
开启下载任务后返回了任务ID,这是任务的唯一标识。
第三步:下载完成后调用广播,通知系统去执行安装操作点击任务栏时判断任务是否下载完成
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
DownloadApkUtils.installApk(context);
} else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
// 如果还未完成下载,用户点击Notification ,跳转到下载中心
Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(viewDownloadIntent);
}
第四部:检测和安装APK
public static void installApk(Context context) {
downloadId = 0;
Intent intent = new Intent(Intent.ACTION_VIEW);
try {
String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
} catch (Exception ignored) {
ignored.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
下面给出关键类的代码:
(1)DownloadApkUtils:
public class DownloadApkUtils {
private static File saveFile;
private static long downloadId = 0;
public static void startDownload(Context mContext, String url, String describeStr) {
initFile();
if (downloadId != 0) { //根据任务ID判断是否存在相同的下载任务,如果有则清除
clearCurrentTask(mContext, downloadId);
}
downloadId = downLoadApk(mContext, url, describeStr);
}
private static void initFile() {
if (saveFile == null)
saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "XXXX.apk");
}
public static long downLoadApk(Context context, String url, String describeStr) {
// 得到系统的下载管理
DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
// 以下两行代码可以让下载的apk文件被直接安装而不用使用Fileprovider,系统7.0或者以上才启动。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(localBuilder.build());
}
DownloadManager.Request requestApk = new DownloadManager.Request(uri);
// 设置在什么网络下下载
requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 下载中和下载完后都显示通知栏
requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (saveFile.exists()) { //判断文件是否存在,存在的话先删除
saveFile.delete();
}
requestApk.setDestinationUri(Uri.fromFile(saveFile));
// 表示允许MediaScanner扫描到这个文件,默认不允许。
requestApk.allowScanningByMediaScanner();
// 设置下载中通知栏的提示消息
requestApk.setTitle("XXXX更新下载");
// 设置设置下载中通知栏提示的介绍
requestApk.setDescription(describeStr);
// 7.0以上的系统适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestApk.setRequiresDeviceIdle(false);
requestApk.setRequiresCharging(false);
}
// 启动下载,该方法返回系统为当前下载请求分配的一个唯一的ID
long downLoadId = manager.enqueue(requestApk);
return downLoadId;
}
/**
* 下载前先移除前一个任务,防止重复下载
*
* @param downloadId
*/
public static void clearCurrentTask(Context mContext, long downloadId) {
DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
try {
dm.remove(downloadId);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
public static void installApk(Context context) {
downloadId = 0;
Intent intent = new Intent(Intent.ACTION_VIEW);
try {
String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
} catch (Exception ignored) {
ignored.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}
要想安装要有权限,当然还要有读写权限以及网络请求权限,这里就不列出来了
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
(2)下载的广播接收者
public class DownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
DownloadApkUtils.installApk(context);
} else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
// 如果还未完成下载,用户点击Notification ,跳转到下载中心
Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(viewDownloadIntent);
}
}
}
既然用到了广播接收这,记得在配置文件中注册;
<receiver android:name=".receiver.DownloadReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_SERVICE"/>
</intent-filter>
</receiver>
执行下载操作的Activity中注册广播
private boolean isRegisterReceiver = false;
/**
* 注册下载成功的广播监听
*/
private void setReceiver() {
if (!isRegisterReceiver) {
DownloadReceiver receiver = new DownloadReceiver();
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
this.registerReceiver(receiver, intentFilter);
isRegisterReceiver = true;
}
}
都知道android的适配是很烦人的,但是不得不适配。下载过程中涉及了文件的读写,那就必须要适配7.0 使用Provider。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.retrofitdemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
资源文件夹下创建xml文件夹,创建file_paths。内容如下
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
最后就是调用了,由于前面准备工作做得很好,这一步就非常简单了,只要一行代码
DownloadApkUtils.startDownload(MainActivity.this,"下载地址","下载任务描述");
到此版本更新时APK的下载及自动安装操作完成。如果对你有帮助,点个赞再走吧。(手动滑稽)