前言:
基本上Android app都会有版本检测及更新的功能,以前都是集成的第三方SDK来实现应用的升级。今天给大家介绍一下DownloadManager是如何实现版本检测与更新的,并且它也是谷歌比较推崇的一个管理下载的类。
DownloadManager简单介绍:
从Android]2.3(API level 9)开始Android用系统服务(Service)的方式提供了Download Manager来优化处理长时间的下载操作。下载管理器将在后台进行下载,监控连接中的状态变化以及系统重启来确保每一个下载任务顺利完成。想要使用DownloadManager,首先必须通过getSystemService来获取系统的DOWNLOAD_SERVICE服务。
那么接下来就让我们来实现App的下载及更新:
一、当App检测要服务器上有新版本时,开启一个下载的Service,让它在后台进行,这个时候把apk的下载地址传过去,代码如下:
Intent intentService = new Intent(MainActivity.this,DownLoadService.class);
intentService.putExtra("url",url);
startService(intentService);
二、然后在DownLoadService类的onStartCommand方法中去做下载操作,如下所示:
//实例化DownloadManager对象
downloadManager = (DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE);
//apk下载地址
String url = intent.getStringExtra("url");
//请求一个下载
DownloadManager.Request request = new DownloadManager.Request(uri);
//设置下载中通知栏显示标题
request.setTitle("版本更新");
//设置下载中通知栏提示的介绍
request.setDescription("版本更新");
//表示下载完成后通知栏是否显示
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//表示允许下载的网络类型,默认在任何网络下都允许下载
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(true);
下载apk文件所存储的路径有两种(选择其一):
//第一种
//storage/emulated/0/Android/data/your-package/files/Download/update.apk 此目录为内部存储目录,除当前应用外,其它app无法访问它
request.setDestinationInExternalFilesDir(getApplicationContext(), Environment.DIRECTORY_DOWNLOADS, "updata.apk");
//第二种
//storage/emulated/0/Download/update.apk 外部存储目录
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "updata.apk");
最后调用此方法:
//downloadId作为此次下载任务的标记
downloadId = downloadManager.enqueue(request);
这样系统就会默认在后台进行下载,通知栏会显示当前下载的进度。
三、当apk下载完成后系统会发一条DownloadManager.ACTION_DOWNLOAD_COMPLETE广播,并传递downloadId作为参数,这个时候我们就可以通过监听广播来完成下载后的操作:
1、在onStartCommand方法中注册广播
//注册下载完成广播
downLoadBroadCastReceiver = new DownLoadBroadCastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(downLoadBroadCastReceiver, intentFilter);
2、在广播接收器中去实现自动安装的操作:
private class DownLoadBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (downloadId == id) {
//下载完成
unregisterReceiver(downLoadBroadCastReceiver);
//跳到安装界面
installApk(getDownloadUri(id));
}
}
}
private void installApk(Uri uri) {
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(installIntent);
//跳到安装界面后要停止此服务
stopSelf();
}
现在有一个问题:
就是在下载完新的apk文件,然后自动跳到安装界面,这个时候用户没有点击安装,而是直接按了返回键或者退出了应用,等下次再进来是不是又得重写下载一边新的安装包?考虑到用户的体验,其实是不需要重新下载的,针对这个问题我是这样处理的:
1、在调用downloadManager.enqueue(...)这个方法时,将返回的id存入sp,默认值为-1。
2、在执行onStartCommand方法之前先去判断缓存中的id是否为-1,如果是,就去执行下载任务;否则就拿到此Id的下载状态,这里用到了DownloadManager.Query()这个静态类,然后根据它的setFilterById(...)方法去过滤此id,最后再调用query(...)方法将查询到的结果返回,如下所示:
private int getDownloadStatus(long downloadApkId) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadApkId);
Cursor c = downloadManager.query(query);
if (c != null) {
try {
if (c.moveToFirst()) {
//获取当前id的下载状态
returnc.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
c.close();
}
}
return -1;
}
3、判断此id的下载状态是否为DownloadManager.STATUS_SUCCESSFUL,如果是,根据此id去获取上次下载的apk文件存储的Uri地址,这里用到了DownloadManage的一个方法,返回值为Uri:
Uri uri = downloadManager.getUriForDownloadedFile(downloadApkId);
4、最后将下载完的apk和本地应用的版本做对比,如果低于当前版本,则先调用downloadManager.remove(...)移除此下载任务,需要传入此下载任务的id:
downloadManager.remove(downloadApkId);
然后再去调用enqueue(...)去下载最新的安装包;否则就直接跳到安装界面。
Demo已经传到了github上,大家可以下载看看。(版本更新)