使用AsyncTask下載+Service+通知連Notification(控制下載暫停或取消)
阿新 • • 發佈:2019-01-03
通過閱讀郭霖《第一行程式碼》第二版 服務最佳實踐啟發,寫的小案例
實現通知欄顯示下載進度,兩個按鈕:暫停下載,取消下載(刪除下載檔案)。
首先在建立工程之後,寫一個介面inControllerDownLoad用來回調暫停,取消,ProgressBar進度,下載成功,下載失敗,如下:
public interface inControllerDownLoad { public void onProgress(int progress); public void onCancle(); public void onPaused(); public void onSuccess(); public voidonFailed(); }
接著需要寫一個執行緒用來實現檔案下載,我們繼承AsysncTask命名為DownLoadAsysncTask
程式碼裡敘述AsysncTask使用,上程式碼:
package com.huida.notificationdemo; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Environment; import java.io.File; import java.io.IOException; importjava.io.InputStream; import java.io.RandomAccessFile; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * Created by Json on 2017/9/19. */ /** * 繼承AsyncTask發現有三個泛型引數 * 第一個String表示在執行此執行緒時候需要傳入一個字串引數 * 第二引數Integer表示用整型資料來作為進度顯示單位 * 第三個引數Integer表示使用整型資料來反饋執行結果 */ public classDownLoadAsysncTask extends AsyncTask<String, Integer, Integer> { private final inControllerDownLoad inController; public static final int SUCCESS = 0; public static final int FAILED = 1; public static final int PAUSED = 2; public static final int CANCELED = 3; private final Context context; private boolean isCancled = false; private boolean isPaused = false; private int lastProgress; private String downloadUrl; private File file; private RandomAccessFile savedFile; public DownLoadAsysncTask(inControllerDownLoad inter, Context context) { this.context = context; this.inController = inter; } /** * 執行緒在此方法中執行 downLoadAsysncTask.execute("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3");這裡開啟執行緒 * 引數可以為多個,這裡只寫了一個引數 * * @param strings * @return */ @Override protected Integer doInBackground(String... strings) { InputStream is = null; long downloadLength = 0;//記錄已經下載檔案長度 downloadUrl = strings[0];//通過第一個引數拿到下載連結 String fileName = Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS).getPath() + "測試下載"; file = new File(fileName); if (file != null) { downloadLength = file.length();//獲取檔案所下載的位元組長度 } long countLength = 0; try { countLength = getContentLength(downloadUrl);//獲取需要下載檔案的總長度 if (downloadLength == countLength) { return SUCCESS;//如果一直 那麼下載成功 } if (countLength == 0) { return FAILED;//如果總長為0 表示請求失敗 } //這裡用到Okhttp 需要在app下的build.gradle 新增依賴 compile 'com.squareup.okhttp3:okhttp:3.8.1' Response response = new OkHttpClient().newCall(new Request.Builder() .addHeader("RANGE", "bytes=" + file.length() + "-").url(downloadUrl).build()) .execute(); //"RANGE","bytes=" + file.length() + "-",表示從伺服器請求file.length() 之後資料,注意格式bytes後有等於號 if (response.body() != null) { //獲取到file.length()位元組之後的流 is = response.body().byteStream(); //通過RandomAccessFile寫入 savedFile = new RandomAccessFile(file, "rw"); //將指標指向file.length() savedFile.seek(file.length()); byte[] buff = new byte[1024]; int len; while ((len = is.read(buff)) != -1) { //在下載向檔案中寫資料時候,如果使用者呼叫了pausedDownLoad(),和cancelDownLoad()方法會監聽到此 //isCancled,isPaused會改變值 以下進行判斷 if (isCancled) { return CANCELED; } else if (isPaused) { return PAUSED; } else { if (file.length() >= countLength) { break; } savedFile.write(buff, 0, len); int progress = (int) ((file.length()) * 100 / countLength); publishProgress(progress);//AsyscTask的方法表示更新Progress會呼叫下方onProgressUpdate()方法 } } response.body().close(); //到此表示流正常寫入 返回成功 return SUCCESS; } } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } if (savedFile != null) { savedFile.close(); } if (isCancled && file != null) { file.delete(); } } catch (Exception e) { e.printStackTrace(); } } return FAILED; } private long getContentLength(String downloadUrl) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(downloadUrl).build(); Response response = client.newCall(request).execute(); if (response.body() != null && response.isSuccessful()) { response.body().close(); return response.body().contentLength(); } return 0; } /** * 表示線上程執行前的準備工作 */ @Override protected void onPreExecute() { super.onPreExecute(); } /** * 這裡執行緒執行完成之後呼叫,此方法中會在主執行緒執行 * 判斷 執行緒執行完的返回值,AsyncTask<String, Integer, Integer>第三個引數對應, * 也是doInBackground(String... strings) 的返回值 * * @param integer */ @Override protected void onPostExecute(Integer integer) { switch (integer) { case SUCCESS: inController.onSuccess(); break; case CANCELED: inController.onCancle(); //取消之後接著刪除該檔案 只有在下載過程中能監聽到 if (file != null) { file.delete(); } isCancled = false; break; case PAUSED: inController.onPaused(); break; case FAILED: inController.onFailed(); break; } } /** * 表示更新操作 也是在主執行緒中執行 * * @param values 可以為多個 我們在上方 publishProgress(progress);只傳入一個 */ @Override protected void onProgressUpdate(Integer... values) { int progress = values[0]; if (progress > lastProgress) { //這裡回撥介面的更新操作 inController.onProgress(progress); lastProgress = progress; } } /** * 暴露次方法用來暫停或繼續下載 * * @param isPaused true表示暫停,false表示繼續下載 */ public void pausedDownLoad(boolean isPaused) { this.isPaused = isPaused; if (!isPaused) { Intent intent = new Intent(context, DownLoadService.class); intent.putExtra("status", "begin"); context.startService(intent); } } /** * 暴露此方法用來取消下載(會刪除該下載檔案) */ public void cancelDownLoad() { isCancled = true; } }
執行緒已經準備好了,這裡我們打算在服務中開啟此執行緒,我們寫一個服務
DownLoadService 程式碼裡都有註釋
ackage com.huida.notificationdemo; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.widget.RemoteViews; import android.widget.Toast; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; /** * Created by Json on 2017/9/19. */ public class DownLoadService extends Service { private static boolean isPause = true;//用來判斷暫停還是繼續下載 private DownLoadAsysncTask downLoadAsysncTask; //實現該介面 實現的方法會在downLoadAsysncTask進行回撥 private inControllerDownLoad inControllerDownLoad = new inControllerDownLoad() { @Override public void onProgress(int progress) { /** * getNotificationManager().notify();開啟通知欄 * 兩個引數 * int id:給我們的Notification 設定id 唯一標識 * Notification:需要我們寫的通知欄 */ getNotificationManager().notify(1, getNotification("Downloading...", progress)); } @Override public void onCancle() { stopForeground(true);//取消 通知關閉 getNotificationManager().notify(1, getNotification("取消", -1)); getNotificationManager().cancel(1); downLoadAsysncTask=null; } @Override public void onPaused() { getNotificationManager().notify(1, getNotification("暫停", -1)); Toast.makeText(DownLoadService.this, "暫停", Toast.LENGTH_SHORT).show(); } @Override public void onSuccess() { getNotificationManager().notify(1, getNotification("下載完成", -1)); Toast.makeText(DownLoadService.this, "DownLoadSuccess", Toast.LENGTH_SHORT).show(); stopForeground(true);//下載成功 通知關閉 getNotificationManager().cancel(1); downLoadAsysncTask=null; } @Override public void onFailed() { stopForeground(false); getNotificationManager().notify(1, getNotification("下載失敗", -1)); Toast.makeText(DownLoadService.this, "DownLoadFailed", Toast.LENGTH_SHORT).show(); } }; /** * * @param s 通知欄顯示的資料 * @param progress 通知欄上progress的進度 * @return */ private Notification getNotification(String s, int progress) { NotificationCompat.Builder builder = new NotificationCompat.Builder(this); //使用RemoteViews進行自定義通知欄 RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.download_contllor); //暫停按鈕的點選事件 Intent pauseIntent = new Intent(this, DownLoadService.class); pauseIntent.putExtra("status", "pause"); /*PendingIntent 相當於Inteng進行封裝了一層 這個通過getService()得到, 也可以通過getBroadcast(),與我們需要的意圖Intent第二引數型別相對應 引數1 上下文 引數2 int requestCode 該PendingIntent的唯一標識,不能重複,否則會判定為同一個PendingIntent 使用者自己定義 引數3 PendingIntent.FLAG_UPDATE_CURRENT 表示 通過requestCode 判斷是否有該PendingIntent 如果有進行更新 */ PendingIntent pausePendingIntent = PendingIntent.getService(this, 1, pauseIntent, PendingIntent.FLAG_UPDATE_CURRENT); //給RemoteViews設定點選事件 表示子控制元件的點選事件 remoteViews.setOnClickPendingIntent(R.id.bt_pause, pausePendingIntent); //取消按鈕的點選事件 Intent goneIntent = new Intent(this, DownLoadService.class); goneIntent.putExtra("status", "gone"); PendingIntent canclePendingIntent = PendingIntent.getService(this, 0, goneIntent, FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.bt_cancle, canclePendingIntent); //此通知的點選事件 隨意跳轉了 到MainActivity中 Intent goMainActivity=new Intent(this,MainActivity.class); remoteViews.setOnClickPendingIntent(R.id.remoteView ,PendingIntent.getActivity(this, 2, goMainActivity, FLAG_UPDATE_CURRENT)); builder.setContent(remoteViews); remoteViews.setProgressBar(R.id.seek, 100, progress, false); remoteViews.setTextViewText(R.id.tv, s); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setWhen(SystemClock.currentThreadTimeMillis()); return builder.build(); } /** * 獲取到系統NotificationManager * @return */ private NotificationManager getNotificationManager() { return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { String status = intent.getStringExtra("status"); switch (status) { case "begin": startDownLoad(); break; case "gone": if (downLoadAsysncTask != null) { downLoadAsysncTask.cancelDownLoad(); } break; case "pause": if (downLoadAsysncTask != null) { downLoadAsysncTask.pausedDownLoad(isPause); isPause = !isPause; } break; } return super.onStartCommand(intent, flags, startId); } /** * 開啟DownLoadAsysncTask傳入我們實現的介面 和上下文 */ private void startDownLoad() { downLoadAsysncTask = new DownLoadAsysncTask(inControllerDownLoad, this); downLoadAsysncTask.execute("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3"); } }
此時基本完事 別忘了去AndroidManifest.xml將服務註冊,在
MainActivity直接呼叫開啟服務
package com.huida.notificationdemo; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //進來我直接呼叫服務進行開啟下載執行緒 findViewById(R.id.bt_start_download).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, DownLoadService.class); intent.putExtra("status","begin"); startService(intent); } }); } }
基本一個簡單的通知欄下載進度完成
思路可以學習一下,我也是自行寫的demo可能還有很多不完善的地方大家見諒