本篇重点介绍使用webview遇到下载文件时,不跳转到浏览器,自己用DownLoadManager来下载。说下思路:
1.跳转到浏览器去下载文件的前提是webview有个DownLoadListener里监听到要下载,这时候我们用代码告诉它跳转到浏览器去下载这个文件,但这样用户体验不好,因为已经跳出了APP应用程序,至于怎么实现在以前两篇博客里已经贴过代码,这里不再多说。
2.在本地下载也是顺着这个思路,当监听到有文件要下载的时候这个时候我们弹出一个对话框提示用户下载还是取消下载---->当用户点击下载之后------>用DownLoadManger去下载------->下载完成后用BroadCastReceiver接收消息后进行自动安装。
代码如下:
1.
/**
* Created by xutingting on 2017/8/17.
* 这个类的作用是监听到有需要下载的文件时弹出下载对话框,点击下载后调用下载的类的downloadApk。
*/
public class MyDownLoadListener implements DownloadListener {
private Context context;
private String name;
public MyDownLoadListener(Context context) {
this.context = context;
}
@Override
public void onDownloadStart(final String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
//截取url的最后/后面的字符串作为每个下载app的名字
String[] split = url.split("/");
if (split.length > 0) {
name = split[split.length - 1];
}
/**
* 弹出下载提示框
*/
final AlertDialog dialog = new AlertDialog.Builder(context, R.style.Theme_Light_Dialog).create();
View inflate = LayoutInflater.from(context).inflate(R.layout.xia_zai_dialog, null);
Window window = dialog.getWindow();
//设置dialog在屏幕底部
window.setGravity(Gravity.BOTTOM);
window.getDecorView().setPadding(0, 0, 0, 0);
//获得window窗口的属性
android.view.WindowManager.LayoutParams lp = window.getAttributes();
//设置窗口宽度为充满全屏
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
//设置窗口高度为包裹内容
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
dialog.setView(inflate);
dialog.show();
//将设置好的属性set回去
window.setAttributes(lp);
RelativeLayout mGroundLayout = (RelativeLayout) inflate.findViewById(R.id.bei_jing_layout);
TextView mCancleTextView = (TextView) inflate.findViewById(R.id.qu_xiao_textview); //取消按钮
TextView mSureTextView = (TextView) inflate.findViewById(R.id.xia_zai_textView); //下载按钮
mCancleTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
mSureTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
// Looper.prepare();
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//检查权限,有去下载,没有去请求权限。
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
} else {
Downloader downloader = new Downloader(context); //下载类
Toast.makeText(context, "成功创建下载任务,正在下载", Toast.LENGTH_SHORT).show();
dialog.dismiss();
if (name.length() > 0) {
downloader.downloadAPK(url, name);//DownLoader 需要在oncreate 中初始化
} else {
downloader.downloadAPK(url, "***.apk");//DownLoader 需要在oncreate 中初始化
}
}
}
});
}
}.start();
}
});
}
}
2.这个是Downloader 类,主要是用DownLoadManager去下载(downloadApk)
/**
* Created by xutingting on 2018/2/28.
* 用DownLoadManager去下载
*/
public class Downloader {
//下载器
private DownloadManager downloadManager;
//上下文
private Context mContext;
//下载的ID
private long downloadId;
public Downloader(Context context) {
this.mContext = context;
}
//下载apk
public void downloadAPK(String url, String name) {
//创建下载任务
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(false);
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
request.setTitle(name);
request.setDescription("正在下载...");
request.setVisibleInDownloadsUi(true);
//设置下载的路径
// request.setDestinationInExternalPublicDir(Environment.getExternalStorageDirectory().getAbsolutePath(), name);
request.setDestinationInExternalPublicDir("xianjinfenqixt", name);
//获取DownloadManager
downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
downloadId = downloadManager.enqueue(request);
}
}
3.用BroadCastReceiver接收到下载完成的监听后,进行自动安装。这里DownLoadManager会自己一直发送自己下载进度的广播,不需要我们担心,我们只需要注册一个静态的广播来接收就可以,这里没有用动态方式去注册广播是因为用动态的方式去注册广播的缺点是广播会受到生命周期的影响,当生命周期结束后广播也随机停止,会导致下载完成后没有自动安装。
/**
* Created by xutingting on 2018/4/13.
* 静态注册的MyBroadCastReceiver,收到下载完成的消息后进行安装
*/
public class MyBroadCastReceiver extends BroadcastReceiver {
private long downloadId;
private DownloadManager downloadManager;
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "下载完成", Toast.LENGTH_SHORT).show();
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
//检查下载状态
checkStatus(context);
}
}
/**
* 检查下载状态
* 这里的contxt 直接用onReceive里的context,MyBroadCastReceiver不能有构造方法这里需要注意,需要参数的时候从onReceiver里的context和intent获得
* @param context
*/
private void checkStatus(Context context) {
DownloadManager.Query query = new DownloadManager.Query();
//通过下载的id查找
query.setFilterById(downloadId);
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
//下载暂停
case DownloadManager.STATUS_PAUSED:
break;
//下载延迟
case DownloadManager.STATUS_PENDING:
break;
//正在下载
case DownloadManager.STATUS_RUNNING:
break;
//下载完成
case DownloadManager.STATUS_SUCCESSFUL:
//下载完成安装APK
installAPK(context);
break;
//下载失败
case DownloadManager.STATUS_FAILED:
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
break;
}
}
}
/**
* 下载到本地后执行安装
* @param context
*/
private void installAPK(Context context) {
Intent intent = new Intent();
File apkFile = queryDownloadedApk();
String packageName = context.getPackageName();
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//7.0启动姿势<pre name="code" class="html"> //com.xxx.xxx.fileprovider为上述manifest中provider所配置相同;apkFile为问题1中的外部存储apk文件</pre>
uri = FileProvider.getUriForFile(context, packageName + ".fileprovider", apkFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//7.0以后,系统要求授予临时uri读取权限,安装完毕以后,系统会自动收回权限,次过程没有用户交互
intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
} else {
//7.0以下启动姿势
uri = Uri.fromFile(apkFile);
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intent);
}
public File queryDownloadedApk() {
File targetApkFile = null;
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloadManager.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!uriString.isEmpty()) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
}
4.在清单文件里静态注册广播在</application>的标签里
<!--静态注册广播接受者-->
<receiver android:name=".view.MyBroadCastReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
5.最后一步就是设置给webview将MyDownLoadListener
mWebView.setDownloadListener(new MyDownLoadListener(this));
截图如下: