本篇重點介紹使用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));
截圖如下: