第一步:創建一個處理下載結果的接口
public interface DownloadListener {// 更新進度條 void onPRegress(int progress);// 下載成功 void onSuccess();// 下載失敗 void onFaild();// 暫停下載 void onPaused();// 取消下載 void onCanceled();}第二步:創建下載的異步任務,引用了okhttp
/** * AsyncTask三個參數 * 參數1:在執行AsyncTask時需要傳入的參數。可用于后臺任務中 * 參數2.后臺任務執行時,如需顯示進度,則使用這里指定的泛型作為進度單位 * 參數3.但任務執行完畢后,如果需要返回結果,則這里指定的泛型作為返回值類型 */public class DownLoadTask extends AsyncTask<String, Integer, Integer> { public static final int TYPE_SUCCESS = 0; public static final int TYPE_FAIL = 2; public static final int TYPE_PAUSED = 3; public static final int TYPE_CANCLE = 4; private DownloadListener listener; private boolean isCanceled = false; private boolean isPaused = false; private int lastProgress; public DownLoadTask(DownloadListener downloadListener) { listener = downloadListener; } /** * 在后臺任務執行之前調用,用于進行界面上的初始化操作,比如顯示一個進度對話框 */ @Override protected void onPreExecute() { super.onPreExecute(); } /** * 在這里處理所有的耗時任務,任務一旦完成通過return返回 * 這里不能對UI進行操作,因為此處代碼都運行在子線程中。 * * @param params * @return */ @Override protected Integer doInBackground(String... params) { InputStream inputStream = null; RandomaccessFile saveFile = null; File file = null; try {// 記錄已下載文件的長度 long downLoadLenght = 0; String downloadUrl = params[0]; String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));// SD卡的download目錄 String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); file = new File(directory + fileName); if (file.exists()) { downLoadLenght = file.length(); }// 獲取文件的總長度 long contentLenght = getContentLenght(downloadUrl); if (contentLenght == 0) { return TYPE_FAIL; } else if (contentLenght == downLoadLenght) {// 已下載的字節和文件總字節相等,說明下載已經完成 return TYPE_SUCCESS; } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder()// 斷點下載,指定從哪一個節點開始下載 .addHeader("RANGE", "bytes=" + downLoadLenght + "-") .url(downloadUrl) .build(); Response response = client.newCall(request).execute(); if (response != null) { inputStream = response.body().byteStream(); saveFile = new RandomAccessFile(file, "rw"); saveFile.seek(downLoadLenght);//跳過已下載的字節 } byte[] b = new byte[1024]; int total = 0; int len; while ((len = inputStream.read(b)) != -1) { if (isCanceled) { return TYPE_CANCLE; } else if (isPaused) { return TYPE_PAUSED; } else { total += len; saveFile.write(b, 0, len); int progress = (int) ((total + downLoadLenght) * 100 / contentLenght);// 通知進度條更新 publishProgress(progress); } } response.body().close(); return TYPE_SUCCESS; } catch (Exception e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (saveFile != null) { saveFile.close(); } if (isCanceled && file != null) { file.delete(); } } catch (Exception e) { } } return TYPE_FAIL; } /** * 獲取文件的總長度 * @param downloadUrl * @return * @throws IOException */ private long getContentLenght(String downloadUrl) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(downloadUrl) .build(); Response response = client.newCall(request).execute(); if (response != null && response.isSuccessful()) { long contentLenght = response.body().contentLength(); response.close(); return contentLenght; } return 0; } /** * 這里可以更新UI了 * * @param values */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values);// 從參數中獲取到當前的下載進度,和上一次下載進度對比,有變化則通知進度條更新 int progress = values[0]; if (progress > lastProgress) { listener.onPregress(progress); lastProgress = progress; } } /** * 但后臺任務完成后return后這個方法馬上會執行 * 并且rentur的值可以在參數中獲得。這里做收尾工作 */ @Override protected void onPostExecute(Integer integer) { switch (integer) { case TYPE_CANCLE: listener.onCanceled(); break; case TYPE_SUCCESS: listener.onSuccess(); break; case TYPE_PAUSED: listener.onPaused(); break; case TYPE_FAIL: listener.onFaild(); break; } } public void pauseDownload(){ isPaused =true; } public void cancleDownload(){ isCanceled =true; } /** * 執行這個異步任務通過這個異步類的對象.execute() */ }第三步:創建一個服務,并通過通知通知下載進度
//保證DownLoadTask可以一直在后臺運行,我們需要創建一個下載的服務//通過new -> service 創建一個service 這樣就可以自動去清單文件注冊了public class DownloadService extends Service { private DownLoadTask downLoadTask; private String downloadUrl; private DownloadListener downloadListener = new DownloadListener() { @Override public void onPregress(int progress) { getNotificationManager().notify(1, getNotification("Dowing...", progress)); } @Override public void onSuccess() { downLoadTask = null;// 下載成功時將前臺服務通知關閉,并創建一個下載成功的通知 stopForeground(true); getNotificationManager().notify(1, getNotification("Download Success", -1)); Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_SHORT).show(); } @Override public void onFaild() { downLoadTask = null;// 下載失敗時通知將前臺服務關閉,并創建一個下載失敗的通知 stopForeground(true); getNotificationManager().notify(1, getNotification("Download Fail", -1)); Toast.makeText(DownloadService.this, "Download Fail", Toast.LENGTH_SHORT).show(); } @Override public void onPaused() { downLoadTask = null; Toast.makeText(DownloadService.this, "Download Paused", Toast.LENGTH_SHORT).show(); } @Override public void onCanceled() { downLoadTask = null; stopForeground(true); Toast.makeText(DownloadService.this, "Download Cancle", Toast.LENGTH_SHORT).show(); } }; private DownloadBinder binder = new DownloadBinder(); /** * */ class DownloadBinder extends Binder { public void startDownload(String url) { if (downLoadTask == null) { downloadUrl = url; downLoadTask = new DownLoadTask(downloadListener);// 在這里開啟下載 downLoadTask.execute(downloadUrl);// 讓這個服務成為一個前臺服務。這樣就可以在系統狀態欄中創建一個持續運行的通知了 startForeground(1, getNotification("Downloading...", 0)); Toast.makeText(DownloadService.this, "Downing...", Toast.LENGTH_SHORT); } } public void pauseDownload() { if (downLoadTask != null) { downLoadTask.pauseDownload(); } } public void cancelDownload() { if (downLoadTask != null) { downLoadTask.cancleDownload(); } else { if (downloadUrl != null) {// 取消下載時需將文件刪除,并將通知關閉 String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/")); String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); File file = new File(directory + fileName); if (file.exists()) { file.delete(); } getNotificationManager().cancel(1); stopForeground(true); Toast.makeText(DownloadService.this, "Cancled", Toast.LENGTH_SHORT).show(); } } } } private NotificationManager getNotificationManager() { return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } /** * 用于顯示下載進度的通知 * @param title * @param progress * @return */ private Notification getNotification(String title, int progress) { Intent intent = new Intent(this, DownloadActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.a8)); builder.setContentTitle(title); builder.setContentIntent(pendingIntent); if (progress > 0) {// 當progress大于或等于0時才需要顯示下載進度 builder.setContentText(progress + "%");// 參數1:通知的做大進度。 2.通知的當前進度。 3.是否使用模糊進度條 builder.setProgress(100, progress, false); } return builder.build(); } public DownloadService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service.// throw new UnsupportedOperationException("Not yet implemented"); return binder; }}第四步:使用
public class DownloadActivity extends AppCompatActivity implements View.OnClickListener { private DownloadService.DownloadBinder downloadBinder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { downloadBinder = (DownloadService.DownloadBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_download); initView(); } private void initView() { findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.pause).setOnClickListener(this); findViewById(R.id.cancle).setOnClickListener(this); Intent intent = new Intent(this, DownloadService.class); startService(intent); //啟動服務 bindService(intent,connection,BIND_AUTO_CREATE); //綁定服務 if (ContextCompat.checkSelfPermission(DownloadActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(DownloadActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); } } @Override public void onClick(View v) { if (downloadBinder == null){ return; } switch (v.getId()) { case R.id.start: String url = "http://218.201.198.108:8081/apk/oil1.apk"; downloadBinder.startDownload(url); break; case R.id.pause: downloadBinder.pauseDownload(); break; case R.id.cancle: downloadBinder.cancelDownload(); break; } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if (grantResults.length>0 &&grantResults[0]!= PackageManager.PERMISSION_GRANTED){ Toast.makeText(this,"權限被拒絕了呦",Toast.LENGTH_SHORT).show(); finish(); } break; } } @Override protected void onDestroy() { super.onDestroy();// 要記得解綁,不然可能會造成內存溢出 unbindService(connection); }}布局文件<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_download" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.lvyequeen.test.day06.DownloadActivity"> <Button android:text="start download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_marginLeft="23dp" android:layout_marginTop="31dp" android:id="@+id/start" /> <Button android:text="pause download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/start" android:layout_alignRight="@+id/start" android:layout_marginTop="36dp" android:id="@+id/pause" /> <Button android:text="cancle download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/pause" android:layout_alignLeft="@+id/pause" android:layout_marginTop="36dp" android:id="@+id/cancle" /></RelativeLayout>
新聞熱點
疑難解答