具体实现思路:
我们通过downloaderManager来下载apk,并且本地保存downManager.enqueue(request)返回的id值,并且通过这个id获取apk的下载文件路径和下载的状态,并且通过状态来更新通知栏的显示。
第一次下载成功,弹出安装界面
如果用户没有点击安装,而是按了返回键,在某个时候,又再次使用了我们的APP
如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本,如果都满足则直接启动安装程序。
- 通过BroadcastReceiver来监听是否下载完成
文件下载管理的实现,包括创建request和加入队列下载,通过返回的id来获取下载路径和下载状态。
public class FileDownloadManager {
private DownloadManager downloadManager;
private Context context;
private static FileDownloadManager instance;
private FileDownloadManager(Context context) {
downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
this.context = context.getApplicationContext();
}
public static FileDownloadManager getInstance(Context context) {
if (instance == null) {
instance = new FileDownloadManager(context);
}
return instance;
}
/**
* @param uri
* @param title
* @param description
* @return download id
*/
public long startDownload(String uri, String title, String description,String appName) {
DownloadManager.Request req = new DownloadManager.Request(Uri.parse(uri));
。 //设置用于下载时的网络类型,默认任何网络都可以,提供:NETWORK_BLUETOOTH、NETWORL_MOBILE、NETWORK_WIFI
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
//设置漫游状态下是否可以下载
//req.setAllowedOverRoaming(false);
//VISIBILTY_HIDDEN: Notification:将不会显示,如果设置该属性的话,必须要添加权限 。
Android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
//VISIBILITY_VISIBLE: Notification显示,但是只是在下载任务执行的过程中显示,下载完成自动消失。(默认值)
// VISIBILITY_VISIBLE_NOTIFY_COMPLETED : Notification显示,下载进行时,和完成之后都会显示。
//VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION :只有当任务完成时,Notification才会显示。
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//设置文件的保存的位置[三种方式]
//第一种
//file:///storage/emulated/0/Android/data/your-package/files/Download/update.apk
//这个文件是应用专属,软件卸载后,下载的文件也将全部删除
req.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, appName+".apk");
//第二种
//file:///storage/emulated/0/Download/update.apk
//req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk");
//第三种 自定义文件路径
//req.setDestinationUri()
// 设置一些基本显示信息
req.setTitle(title);
req.setDescription(description);
//req.setMimeType("application/vnd.android.package-archive");
return downloadManager.enqueue(req);//异步
//dm.openDownloadedFile()
}
/**
* 获取文件保存的路径
*
* @param downloadId an ID for the download, unique across the system.
* This ID is used to make future calls related to this download.
* @return file path
* @see FileDownloadManager#getDownloadUri(long)
*/
public String getDownloadPath(long downloadId) {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = downloadManager.query(query);
if (c != null) {
try {
if (c.moveToFirst()) {
return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
}
} finally {
c.close();
}
}
return null;
}
/**
* 获取保存文件的地址
*
* @param downloadId an ID for the download, unique across the system.
* This ID is used to make future calls related to this download.
* @see FileDownloadManager#getDownloadPath(long)
*/
public Uri getDownloadUri(long downloadId) {
return downloadManager.getUriForDownloadedFile(downloadId);
}
public DownloadManager getDownloadManager() {
return downloadManager;
}
/**
* 获取下载状态
*
* @param downloadId an ID for the download, unique across the system.
* This ID is used to make future calls related to this download.
* @return int
* @see DownloadManager#STATUS_PENDING
* @see DownloadManager#STATUS_PAUSED
* @see DownloadManager#STATUS_RUNNING
* @see DownloadManager#STATUS_SUCCESSFUL
* @see DownloadManager#STATUS_FAILED
*/
public int getDownloadStatus(long downloadId) {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = downloadManager.query(query);
if (c != null) {
try {
if (c.moveToFirst()) {
return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
}
} finally {
c.close();
}
}
return -1;
}
}
app的检测安装的实现:
public class DownLoadApk {
public static final String TAG = DownLoadApk.class.getSimpleName();
public static void download(Context context, String url, String title,final String appName) {
// 获取存储ID
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long downloadId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L);
if (downloadId != -1L) {
FileDownloadManager fdm = FileDownloadManager.getInstance(context);
int status = fdm.getDownloadStatus(downloadId);
if (status == DownloadManager.STATUS_SUCCESSFUL) {
//启动更新界面
Uri uri = fdm.getDownloadUri(downloadId);
if (uri != null) {
if (compare(getApkInfo(context, uri.getPath()), context)) {
startInstall(context, uri);
return;
} else {
fdm.getDownloadManager().remove(downloadId);
}
}
start(context, url, title,appName);
} else if (status == DownloadManager.STATUS_FAILED) {
start(context, url, title,appName);
} else {
Log.d(TAG, "apk is already downloading");
}
} else {
start(context, url, title,appName);
}
}
private static void start(Context context, String url, String title,String appName) {
long id = FileDownloadManager.getInstance(context).startDownload(url,
title, "下载完成后点击打开",appName);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putLong(DownloadManager.EXTRA_DOWNLOAD_ID,id).commit();
Log.d(TAG, "apk start download " + id);
}
public static void startInstall(Context context, Uri uri) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(uri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
}
/**
* 获取apk程序信息[packageName,versionName...]
*
* @param context Context
* @param path apk path
*/
private static PackageInfo getApkInfo(Context context, String path) {
PackageManager pm = context.getPackageManager();
PackageInfo info = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
if (info != null) {
return info;
}
return null;
}
/**
* 下载的apk和当前程序版本比较
*
* @param apkInfo apk file's packageInfo
* @param context Context
* @return 如果当前应用版本小于apk的版本则返回true
*/
private static boolean compare(PackageInfo apkInfo, Context context) {
if (apkInfo == null) {
return false;
}
String localPackage = context.getPackageName();
if (apkInfo.packageName.equals(localPackage)) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackage, 0);
if (apkInfo.versionCode > packageInfo.versionCode) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
return false;
}
}
上面的代码可知:我们通过获取当前app的信息来比较是否需要下载和是否立即安装。第一次下载把downloadId保存到本地,用户下次进来的时候,取出保存的downloadId,然后通过downloadId来获取下载的状态信息。如果下载失败,则重新下载并且把downloadId存起来。如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本
,如果都满足则直接启动安装程序。
监听app是否安装完成
public class ApkInstallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){
long downloadApkId =intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
installApk(context, downloadApkId);
}
}
/**
* 安装apk
*/
private void installApk(Context context,long downloadApkId) {
// 获取存储ID
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long downId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L);
if(downloadApkId == downId){
DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Uri downloadFileUri = downManager.getUriForDownloadedFile(downloadApkId);
if (downloadFileUri != null) {
Intent install= new Intent(Intent.ACTION_VIEW);
install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
}else{
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}
}
DownloadManager下载完成后会发出一个广播 android.intent.action.DOWNLOAD_COMPLETE
新建一个广播接收者即可:
清单配置:
先添加网络下载的权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
再添加静态广播:
<receiver android:name=".ApkInstallReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>