系统下载管理器DownloadManager下载,安装apk
首先学习下FileProvider
看这里 https://www.jianshu.com/p/e05f35fbb569
整理下方便用到的时候直接copy,先这样,以后有空再改。
安装的代码
权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
fun installCheck(context: Context,file: File) {
if (!file.exists()) {
return
}
//下边这部分不一定需要
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (context.getPackageManager().canRequestPackageInstalls()) {
} else {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + context.getPackageName()))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
return
}
}
installApk(context, file)
}
//安装apk,兼容7.0
private fun installApk(context: Context, file: File) {
if (!file.exists()) {
return
}
val intent = Intent(Intent.ACTION_VIEW)
// 没有在Activity环境下启动Activity,设置下面的标签
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
var uri: Uri
//版本在7.0以上是不能直接通过uri访问的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//参数1 上下文, 参数2 Provider主机地址和清单文件中保持一致 参数3 共享的文件
uri = FileProvider.getUriForFile(context, "com.charlie.fileProvider", file)
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
uri = Uri.fromFile(file)
}
intent.setDataAndType(uri, "application/vnd.android.package-archive")
// intent.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive");
context.startActivity(intent)
}
FileProvider的配置
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.charlie.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
file_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--Context.getFilesDir().-->
<files-path
name="filepath"
path="filepath"/>
<!-- getCacheDir().-->
<cache-path
name="cachepath"
path="cachepath"/>
<!-- Environment.getExternalStorageDirectory().-->
<external-path
name="externalpath"
path="/"/>
<!-- Context.getExternalCacheDir(). -->
<external-cache-path
name="externalcachepath"
path="externalcachepath"/>
<!--Context#getExternalFilesDir(String) Context.getExternalFilesDir(null). -->
<external-files-path
name="externalfilespath"
path="externalfilespath"/>
</paths>
使用系统下载
工具类
import java.io.File;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
public abstract class FileDownManager {
private DownloadManager downloadManager;
private String fileName;
private String saveDir;
public static String DOWNLOAD_SP_NAME = "download_sp_name";
public Context mContext;
public abstract Context getContext();
public SharedPreferences getSp() {
return getContext().getSharedPreferences(DOWNLOAD_SP_NAME, Context.MODE_PRIVATE);
}
public abstract String savedFileName(String url, String newVersion);
public abstract String savedFileDir();
public File getSavedFile() {
return new File(Environment.getExternalStorageDirectory(),saveDir+"/"+fileName);
}
public static String downloadKey = "downID";
public static String downloadVersionKey = "version";
public long getDownloadID() {
return getSp().getLong(downloadKey, -1);
}
public String getDownloadVersion() {
return getSp().getString(downloadVersionKey, "0");
}
public void downOrSuccessAction(String url, String newVersion) {
saveDir = savedFileDir();
fileName = savedFileName(url, newVersion);
downloadManager = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
String downVersion = getSp().getString(downloadVersionKey, null);
if (TextUtils.equals(newVersion, downVersion)) {
long id = getSp().getLong(downloadKey, -1);
if (id == -1) {
down(url, newVersion);
} else {
query(url, id, newVersion);
}
} else {
getSp().edit().putString(downloadVersionKey, newVersion).commit();
down(url, newVersion);
}
}
private void down(String url, String newVersion) {
Uri resource = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(resource);
request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
request.setAllowedOverRoaming(false);
// set file mime type
// MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
// String mimeString =
// mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
// request.setMimeType(mimeString);
// show notification on the state bar
request.setVisibleInDownloadsUi(true);
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// set the saving file's directory and name
request.setDestinationInExternalPublicDir(saveDir, fileName);
requestCustom(request);
try {
long id = downloadManager.enqueue(request);
getSp().edit().putLong(downloadKey, id).commit();
} catch (Exception e) {
e.printStackTrace();
enableDownloadManager(getContext());
}
}
public void requestCustom(DownloadManager.Request request) {
}
private void query(String url, long id, String newVersion) {
System.out.println("query========="+url+"==="+id+"==="+newVersion);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor c = downloadManager.query(query);
if(c==null) {
System.out.println("cursor null============");
redownload(url, id, newVersion);//download record history was cleared
}
else if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
case DownloadManager.STATUS_PAUSED:
Log.v("down", "STATUS_PAUSED");
case DownloadManager.STATUS_PENDING:
Log.v("down", "STATUS_PENDING");
case DownloadManager.STATUS_RUNNING:
Log.v("down", "STATUS_RUNNING");
break;
case DownloadManager.STATUS_SUCCESSFUL:
Log.v("down", "STATUS_SUCCESSFUL");
String name=c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String uri=c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
String uri_d=c.getString(c.getColumnIndex(DownloadManager.COLUMN_URI));//the http url address
//name=/storage/emulated/0/justdelete/apkdown/appname1.2.apk
//file:///storage/emulated/0/justdelete/apkdown/appname1.2.apk
File savedFile=getSavedFile();
System.out.println("name="+name+"=="+uri+"==="+uri_d+"====="+savedFile.getAbsolutePath());
if (savedFile.exists()) {
downloadSuccess(savedFile);
} else {
System.out.println("file not exist");
redownload(url, id, newVersion);
}
break;
case DownloadManager.STATUS_FAILED:
Log.v("down", "STATUS_FAILED");
redownload(url, id, newVersion);
break;
default:
Log.v("down", "STATUS_====="+status);
break;
}
}else {
System.out.println("===================cursor size 0");
redownload(url, id, newVersion);
}
}
private void redownload(String url, long id, String newVersion) {
// if file not exist or download failed,redownload.
downloadManager.remove(id);
getSp().edit().remove(downloadKey).commit();
down(url, newVersion);
}
protected void downloadSuccess(File downloadFile) {
System.out.println("downloadfile size======"+downloadFile.length());
}
public static void enableDownloadManager(Context context) {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:com.android.providers.downloads"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
}
然后对于不同的下载类型继承工具类,修改key值即可
如下举了2种例子,一种下载apk,一种下载一个zip文件
apk类型下载
import java.io.File;
import android.content.Context;
public class APKDownloadManager extends FileDownManager{
public static APKDownloadManager apkDownloadManager;
public static APKDownloadManager getInstance(Context context) {
if(apkDownloadManager==null) {
synchronized (APKDownloadManager.class) {
if(apkDownloadManager==null) {
apkDownloadManager=new APKDownloadManager();
}
}
}
apkDownloadManager.mContext=context;
return apkDownloadManager;
}
public APKDownloadManager() {
downloadKey="apk_down_id";
downloadVersionKey="apk_version";
}
@Override
public Context getContext() {
if(mContext==null) {
mContext=MyApplication.application;
}
return mContext;
}
@Override
public String savedFileDir() {
return "justdelete/apkdown";
}
@Override
public String savedFileName(String url, String newVersion) {
return "appname"+newVersion+".apk";
}
@Override
protected void downloadSuccess(File downloadFile) {
super.downloadSuccess(downloadFile);
//安裝apk,7.0以上需要权限,并且uri获取也需要fileprovider
System.out.println("apk download success================="+downloadFile.getAbsolutePath());
}
}
zip文件类型下载
import java.io.File;
import android.content.Context;
public class MapDownloadManager extends FileDownManager{
public static MapDownloadManager mapDownloadManager;
public static MapDownloadManager getInstance(Context context) {
if(mapDownloadManager==null) {
synchronized (MapDownloadManager .class) {
if(mapDownloadManager==null) {
mapDownloadManager=new MapDownloadManager();
}
}
}
mapDownloadManager.mContext=context;
return mapDownloadManager;
}
public MapDownloadManager() {
downloadKey="map_down_id";
downloadVersionKey="map_version";
}
@Override
public Context getContext() {
if(mContext==null) {
mContext=MyApplication.application;
}
return mContext;
}
@Override
public String savedFileName(String url, String newVersion) {
return "map"+newVersion+".zip";
}
@Override
public String savedFileDir() {
return "justdelete/savedMap";
}
@Override
protected void downloadSuccess(File downloadFile) {
//map download success
System.out.println("map download success=============="+downloadFile.getAbsolutePath());
}
}
然后是广播
import java.io.File;
import java.util.Arrays;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.text.TextUtils;
public class FileDownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("intent======action====" + intent.getAction());
if (TextUtils.equals(intent.getAction(), DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (reference == -1) {
return;
}
String filePath = getFilePath(context, reference);
File file = new File(filePath);
if (file.exists() && file.length() > 0) {
APKDownloadManager apkDownloadManager = APKDownloadManager.getInstance(context);
if (reference == apkDownloadManager.getDownloadID()) {
apkDownloadManager.downloadSuccess(file);
return;
}
MapDownloadManager mapDownloadManager = MapDownloadManager.getInstance(context);
if (reference == mapDownloadManager.getDownloadID()) {
mapDownloadManager.downloadSuccess(file);
return;
}
}
} else if (TextUtils.equals(intent.getAction(), DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
long[] ids=intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
System.out.println("ids=============="+Arrays.toString(ids));
}
}
private String getFilePath(Context context, long id) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor c = ((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).query(query);
if (c != null && c.moveToFirst()) {
String name = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
return name;
}
return "";
}
}
注册广播,权限添加,click那个action测试用的,可以不要
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<receiver android:name=".FileDownloadReceiver">
<intent-filter >
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
</intent-filter>
</receiver>
下载代码,判断是否需要下载逻辑就看实际情况了
APKDownloadManager.getInstance(null).downOrSuccessAction("http://d2.eoemarket.com/app0/38
/38199/apk/1957269.apk?channel_id=426", "1.4");
代码说明
这个是设置下载的文件保存的文件夹,以及文件的名字的,需要注意第一个参数是sdcard根目录下的目录的名字。你可以随便起个"AAA/BBB/CCC"都可以。
request.setDestinationInExternalPublicDir(saveDir, fileName);
点到代码里看下,系统建议是用它自带的那些目录,都分门别类了,比如Environment.DIRECTORY_DOWNLOADS就是Download目录了。不过上边也说了,你随便写个目录也可以
public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
File file = Environment.getExternalStoragePublicDirectory(dirType);
//goon
* @param type The type of storage directory to return. Should be one of
* {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
* {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
* {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
* {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
* {@link #DIRECTORY_DCIM}. May not be null.
*
* @return Returns the File path for the directory. Note that this
* directory may not yet exist, so you must make sure it exists before
* using it such as with {@link File#mkdirs File.mkdirs()}.
*/
public static File getExternalStoragePublicDirectory(String type) {
throwIfUserRequired();
return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
}
打开下载管理器应用界面
downloadManager.enqueue(request)
上边的代码进行了异常处理,因为如果下载管理器没有启动的话,会抛出异常,我们在这里就调用下边的代码打开下载管理器页面,让用户手动启动.当然中间你可以添加一个弹框,告诉用户点击启动按钮。
image.png
/**
* Start activity to Settings to enable DownloadManager.
*/
public static void enableDownloadManager(Context context) {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:com.android.providers.downloads"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
系统下载通知的一些参数说明
还可以监听通知栏的点击事件,添加如下的action即可,这个是文件没下完的时候点击事件,下载完再点击通知栏,系统会自己处理相应的文件的。
public final static String ACTION_NOTIFICATION_CLICKED = "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
下载完成的监听
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
通知栏可见性参数
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE);
4种:下载中可见,始终不可见,下载中和完成都可见,只有完成的时候可见
/**
* This download is visible but only shows in the notifications
* while it's in progress.
*/
public static final int VISIBILITY_VISIBLE = 0;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
/**
* This download doesn't show in the UI or in the notifications.
*/
public static final int VISIBILITY_HIDDEN = 2;
/**
* This download shows in the notifications after completion ONLY.
* It is usuable only with
* {@link DownloadManager#addCompletedDownload(String, String,
* boolean, String, String, long, boolean)}.
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3;
需要注意,设置不可见的时候需要权限
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
题外话
下边的方法可以跳转到下载历史记录页面
Intent intent=new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
startActivity(intent);
image.png
下载异常
测试的时候到jira上找个apk地址,结果下载完一直提示我解析失败,我浏览器可以正常下载啊。
后来换了个浏览器,才发现这地址需要登陆的,我默认的浏览器是登陆状态,所以可以正常下。手机端因为没有登陆,其实下了个html文件回来。