1. 程式人生 > >《第一行程式碼 Android 第二版》 第10章 後臺默默的勞動者——探究服務

《第一行程式碼 Android 第二版》 第10章 後臺默默的勞動者——探究服務

本章主要學習了

  1. 動態申請許可權
  2. 服務的用法(前臺服務、後臺服務)
  3. Android中多執行緒的應用 (AsyncTask 類)
  4. 最後通過一個綜合例子實現下載功能。基本搞懂這個例子本章就學完了。

先介紹下幾個主要的類

/**
 * 主活動(UI執行緒)工作如下:
 * 1、初始化 UI 介面,繫結按鈕事件。
 * 2、例項化 ServiceConnection 通過他的 onServiceConnected 回撥函式獲取 service 的控制權
 * 3、動態申請許可權
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener{...}

/**
 * 下載服務(注意服務還是在主執行緒,DownloadTask中的 doInBackground 才在子執行緒裡跑)
 */
public class DownloadService extends Service {
	 /**
     *  onBind(Intent) 會將此內部類的例項傳給活動 MainActivity
     *  此類中定義了 【開始下載】【暫停下載】【取消下載】三個方法
     *  這樣在活動中就可以通過不同的按鈕控制服務幹活了。
     */
    class DownloadBinder extends Binder {...}
}

/**
 *  下載任務類,繼承了非同步任務 AsyncTask
 *  真正下載這幹活的程式碼都在這裡面
 */
public class DownloadTask extends AsyncTask<String, Integer, Integer> {
	public DownloadTask(DownloadListener listener) {
		this.listener = listener;
	}
}

/**
 * DownloadService 中匿名實現 DownloadListener,通過建構函式傳給 DownloadTask
 * 這樣當下載進行到哪一步,就可以呼叫相應的方法進行處理了
 * 比如進行(移除事務、停止服務、推送通知、顯示提示)等操作
 */
public interface DownloadListener {
    void onProgress(int progress);
    void onSuccess();
    void onFailed();
    void onPaused();
    void onCanceled();
}

說了半天還是上張圖直觀點:
開啟前臺服務,非同步執行下載任務

知識點

1、動態申請許可權 (MainActivity 中)

    /**
     * 1、初始化UI介面
     * 2、繫結按鈕事件
     * 3、啟動服務 + 繫結服務
     * 4、動態申請許可權
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	// ...省略部分程式碼...
        // ----------- 動態申請許可權 -----------
        // 如果有許可權則返回PackageManager.PERMISSION_GRANTED,否則返回PackageManager.PERMISSION_DENIED
        int permissionCheck = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        // 判斷如果沒有授權,則動態申請
        if ( permissionCheck != PackageManager.PERMISSION_GRANTED) {
            // 申請獲取許可權,彈出確認框等使用者操作,然後會回撥 onRequestPermissionsResult
            // 引數1:目標活動,MainActivity 彈框詢問自然就傳 MainActivity
            // 引數2:要申請的許可權,放到數組裡(就是說可以同時申請多個許可權啦)
            // 引數3:請求碼,就是暗號,不然回撥怎麼知道是哪個請求的結果返回來了(自己定,別和其他請求重複就行)
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission. WRITE_EXTERNAL_STORAGE }, 1);
        }
    }
    
    /**
     * 使用者選擇授權與否,會回撥這裡
     * @param requestCode 這就是上面那個“請求碼”
     * @param permissions 許可權陣列
     * @param grantResults 結果陣列
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                // 只申請了一個就簡單寫了
                if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "拒絕許可權將無法使用程式", Toast.LENGTH_SHORT).show();
                    finish();// 不授權,恕我無能為力,回見。
                }
                break;
            default:
        }
    }

2、主執行緒與子執行緒通訊

在子執行緒裡跑的貌似只有 DownloadTask.doInBackground(String… params)
在服務 DownloadService 中實現介面 DownloadListener 並例項化,當例項化 DownloadTask 時作為構造引數傳進去。這樣 DownloadTask 裡的子執行緒就能利用 DownloadListener 例項與主執行緒通訊了。(在很多地方安卓都是這麼幹的)
我們來捋一捋。

主活動類 MainActivity 中主要做了什麼:

1、初始化UI介面
2、繫結按鈕事件
3、啟動服務 + 繫結服務
4、動態申請許可權
5、實現 ServiceConnection 介面,MainActivity 能通過它拿到 DownloadService.onBind 傳過來的 IBinder 進而呼叫服務中的方法。(按鈕點選就是呼叫的它的方法。當然需要執行什麼功能,定義什麼方法,是我們按需求來定的)
6、活動銷燬時解綁服務 unbindService(connection);

服務類 DownloadService 中主要做了什麼:

1、匿名實現 DownloadListener 介面,用於處理下載任務的五種狀態。 按具體情況執行(移除事務、停止服務、推送通知、顯示提示)
2、定義內部類 class DownloadBinder extends Binder 通過 onBind 將例項傳給 MainActivity
3、DownloadBinder 的【開始下載】startDownload(String url) 中 new DownloadTask(listener);

非同步事務 DownloadTask 中主要做了什麼:

1、下載前,做點準備,建立進度條啥的
2、開始下載,下載的過程中要判斷下載狀態。DownloadTask提供了兩個方法用來設定狀態,UI上的按鈕可以調動。
3、下載過程中,更新進度。
4、下載完成,收尾。

   /**
     * 這個方法是在執行非同步任務之前的時候執行,並且是在UI Thread當中執行的。
     * 通常我們在這個方法裡做一些UI控制元件的初始化的操作,例如彈出要給ProgressDialog
     */
    @Override
    protected void onPreExecute(){...}
    
    /**
     * 在onPreExecute()方法執行完之後,會馬上執行這個方法,這個方法就是來處理非同步任務的方法,
     * Android作業系統會在後臺的執行緒池當中開啟一個worker thread來執行我們的這個方法,
     * 所以這個方法是在worker thread當中執行的,
     * 這個方法執行完之後就可以將我們的執行結果傳送給我們的最後一個 onPostExecute 方法,
     * 在這個方法裡,我們可以從網路當中獲取資料等一些耗時的操作
     * @param params
     * @return
     */
    @Override
    protected Integer doInBackground(String... params) {...}

    /**
     * 這個方法也是在UI Thread當中執行的,我們在非同步任務執行的時候,
     * 有時候需要將執行的進度返回給我們的UI介面,
     * 例如下載一張網路圖片,我們需要時刻顯示其下載的進度,就可以使用這個方法來更新我們的進度。
     * 這個方法在呼叫之前,我們需要在 doInBackground 方法中呼叫 publishProgress(Progress) 方法
     * 來將我們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {...}
    
    /**
     * 當我們的非同步任務執行完之後,就會將結果返回給這個方法,這個方法也是在UI Thread當中呼叫的。
     * 我們可以將返回的結果顯示在UI控制元件上
     * @param status
     */
    @Override
    protected void onPostExecute(Integer status) {...}