## 安卓多任務實現的基本原理
### 一.基本概念
>? 操作一些耗時操作時候,如I/O讀寫大文件,數據庫操作以及網絡下載需要很長時間,為了不阻塞用戶界面,出現ANR(應用程序無響應)的響應提示窗口,這個時候我們考慮使用Thread線程來進行解決.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
### 二.Android中的進程和線程
> 在Android系統中,如果有一個應用程序組件時第一次被啟用,而且這時候,應用程序也沒有其他的組件來運行,則Android系統會為應用程序創建一個linux的**進程**,這個Linux進程包含一個**線程**,稱為**主線程**或者**UI線程**.
> 當一個組件在被啟動時,如果該process已經存在了,那么該組件就直接通過這個process被啟動起來,并且運行在這個process的UI線程中.
#### 1.進程
* 默認情況下,同一個應用程序內的所有組件都是運行在同一個進程中的,大部分應用程序都是按照這種方式運行的;
* 在具體應用中,很多時候需要通過在manifest文件中進行設置,通過修改四大組件在Manifest.xml的代碼塊中的android:proess屬性指定組件運行的進程,使其運行在不同的process中。
* <application>中的元素也支持android:proess屬性,用于指定所有組件的默認進程。
#### 2.進程的重要性層次結構
**進程有5中層次,按其重要程度遞減分為**
> 1.前臺進程(用戶當前操作所必須的進程)
* 擁有一個正在與用戶交互的Activity
* 擁有一個Service,這個Service綁定了某一個正在與用戶交互的Activity
* 擁有一個前臺Service
* 擁有一個Service且它正在執行聲明周期回調方法
* 擁有一個BroadcastReceiver且這個BroadcastReceiver正在執行onRece方法
> 2.可見進程
* 沒有任何前臺組件,但是仍然會影響用戶在屏幕上所見內容的進程
* 擁有一個可見但是不可與用戶交互的Activity
* 擁有一個Service,這個Service綁定了一個可見但是不可與用戶進行交互的Activity
> 3.服務進程
* 由startService()方法啟動的Service進程,雖然不直接和所見內容關聯,但是會執行一些用戶關心的操作,例如后臺播放音樂或者下載數據等等
* 若系統不足以維持前臺進程和可見進程,才會犧牲服務進程的空間
> 4.后臺進程
* 包含用戶不可見的Activity的進程,這些進程對用戶體驗沒有直接影響,可以隨時在任意時間終止它們,以回收內存資源.
* 系統通過LRU(最近最少使用)列表進行多個后臺進程的管理,確保最近使用的Activity最后被終止
> 5.空進程
* 進程不含有任何應用組件,該進程主要作用是緩存,以改善在此進程中運行組件的啟動時間。
* 系統會經常終正此類進程
#### 3.線程
> Android是單線程模型,我們創建的Service、Activity以及Broadcast均是在一個主線程處理,這里我們可以理解為uI線程。(應用程序是一個默認的**單線程單任務程序**)
> Ul Thread中運行著許多重要的邏輯,如系統事件處理,用戶輸入事件處理,ul繪制,Service,Alarm等
> 我們編寫的代碼穿插在主線程的邏輯中,比如對用戶觸摸事件的檢測和響應,對用戶輸入的處理,自定義View的繪制等。如果我們插入的代碼比價耗時,如網絡請求或數據庫讀取,就會阻塞uI線程其他邏輯的執行,從而導致界面卡頓。
> 如果卡頓時間超過5秒,系統就會報ANR錯誤。所以,執行耗時的操作,我們需要另起線程執行。
> 在新線程執行完耗時的邏輯后,往往需要將結果反饋給界面,進行uI更新。Android的ul toolkit不是線程安全的,不能在非uI線程進行uI的更新
> **所有對界面的更新必須在uI線程進行**
> 安卓的單線程模式遵從兩個原則
>
> * 1.不要阻塞UI進程
> * 2.不要在UI線程之外訪問UI組件
> 創建線程:? 基礎操作都在UI線程中運行,耗時操作可以創建新的線程去完成
* 繼承Thread類
* 實現Runnable接口
> **安卓提供的四種常用的操作多線程的方式,分別是:**
* Handle + Thread
* AsyncTask
* ThreadPoolExecutor
* IntentService
————————————————————————————————————————————————————————————————————
### 三、實現多任務
#### 1.多任務的實現原理
* **在Android中,我們把除uI線程外的,其他所有的線程都叫做工作線程,也就是說Android只會存在兩種線程:UI主線程(ul thread)和工作線程(work thread)**
* 我們把耗時的操作放在工作線程中去做。操作完成后,再通知UI主線程做出相應的響應。
* 這就需要掌握線程間通信的方式。在Android中提供了兩種線程間的通信方式:
? * AsyncTask機制
? * Handler機制
##### (1)使用AsyncTask
* AsyncTask是Android框架提供的**異步處理**的輔助類,它可以實現耗時操作仕具他線程執行,而處理結果在Main線程執行
* 它屏蔽掉了多線程和Handler的概念,進行了較高程度的封裝。
* 使用AsyncTask的代碼很容易被理解,因為他們都有一些具有特定職責的方法,如:預處理的方法onPreExecute,后臺執行任務的方法dolnBackground,更新進度的方法publishProgress,返回結果的方法onPostExecute等等
##### (2)Handle機制
* Handler機制是通過消息隊列進行通信的機制,通過使用Handler,LooperMessageQueue,和Message這幾個類協調來完成
? * Handler:在android里負責發送和處理消息,通過它可以實現其他線程與Main線程之間的消息通訊
? * Looper:負責管理線程的消息隊列和消息循環
? * MessageQueue:消息隊列,先進先出,它的作用是保存有待線程處理的消息
#####? ? (3)Handler類
* UI主線程在創建時會被自動創建一個消息隊列和消息循環,主線程的Looper通過創建一個Handler對象,對外界提供了訪問消息隊列的渠道
? * 主線程通過Handler.handleMessage()讀取消息隊列中的消息
? * 工作線程通過方法發送消息到主線程的消息隊列**Handler.sendMessage()? ? Handler.post()**
————————————————————————————————————————————————————————————————
### 四、.Android實現多線程的兩種操作模式
#### 1Android有兩種方式實現多線程操作UI:
* 第一種是創建新線程Thread,用handler負責線程間的通信和消息。
* 第二種方式AsyncTask異步執行任務
#### 2.使用Handler實現多任務
在新的線程中調用主線程中的Handler的postXX和sendmessage方法來實現與主線程進行通信
**使用post方法實現多任務的主要步驟**:
* 創建一個Handler對象;
* 將要執行的操作卸載線程對象的run方法中;
* 使用post方法運行線程對象
* 如果需要循環執行,需要在線程對象的run方法中再次調用post方法
#### 3.Handler機制實現異步任務demo
* xml文件中添加一個TextView
? ```xml
? <?xml version="1.0" encoding="utf-8"?>
? <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? ? xmlns:tools="http://schemas.android.com/tools"
? ? ? android:layout_width="match_parent"
? ? ? android:layout_height="match_parent"
? ? ? tools:context=".AsynchronousTask.HandlerAsyTaskActivity">
? ? ? <TextView
? ? ? ? ? android:id="@+id/textview"
? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? android:text="hello"
? ? ? ? ? android:textAllCaps="false"
? ? ? ? ? tools:ignore="MissingConstraints" />
? </androidx.constraintlayout.widget.ConstraintLayout>
? ```
* java文件 編輯代碼
? ```java
? package com.example.handleractivity.AsynchronousTask;
? import androidx.annotation.NonNull;
? import androidx.appcompat.app.AppCompatActivity;
? import android.os.Bundle;
? import android.os.Handler;
? import android.os.Message;
? import android.widget.TextView;
? import com.example.handleractivity.R;
? public class HandlerAsyTaskActivity extends AppCompatActivity {
? ? ? private final static int TEST = 1;
? ? ? private TextView textView;
? ? ? //這是自己創建的Handler,實現異步任務,這里用來更改UI
? ? ? private Handler mHandler = new Handler(){
? ? ? ? ? @Override
? ? ? ? ? public void handleMessage(@NonNull Message msg) {
? ? ? ? ? ? ? textView.setText("這是工作線程文本");
? ? ? ? ? }
? ? ? };
? ? ? @Override
? ? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? ? setContentView(R.layout.activity_handler_asy_task);
? ? ? ? ? textView = findViewById(R.id.textview);
? //? ? ? ? textView.setText("這是一段文本");
? ? ? ? ? new ActivityThread().start();
? ? ? }
? ? ? /*
? ? ? 這是一個線程不安全的行為
? ? ? */
? ? ? class ActivityThread extends Thread{
? ? ? ? ? @Override
? ? ? ? ? public void run() {
? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? Thread.sleep(3000);
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? }
? //? ? ? ? ? ? textView.setText("這是工作線程文本");
? ? ? ? ? ? ? //使用handler發送消息至消息隊列
? ? ? ? ? ? ? Message message = new Message();
? ? ? ? ? ? ? message.what = TEST;
? ? ? ? ? ? ? mHandler.sendMessage(message);
? ? ? ? ? }
? ? ? }
? }
? ```
> 我們設置一個線程,讓其休眠3秒鐘,然后執行 textView.setText("這是工作線程文本")操作,三秒之后程序會崩潰。
>
> 原因是: 不能在除了UI線程之外的線程里邊進行更改UI的操作。
那怎么才能實現呢?
> 使用Handler實現異步任務,自己新建一個Handler,然后在工作線程中使用handler來傳遞信息。handler里面進行更改UI的操作,就可了
### 五、Handler機制方法調用
#### 1.使用Runnable和Handler實現五秒鐘之后出現彈窗
```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? tools:context=".HandlerPost.HandlerPostActivity">
? ? <Button
? ? ? ? android:id="@+id/btnShowToast"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:text="開啟Toast"/>
? ? <TextView
? ? ? ? android:text="你好"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"/>
</LinearLayout>
```
```java
public class HandlerPostActivity extends AppCompatActivity implements View.OnClickListener{
? ? //寫一個Handler
? ? Handler countHandler = new Handler();
? ? /*線程1:啟動一個Toast顯示線程*/
? ? Runnable mRunToast = new Runnable() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? Toast.makeText(HandlerPostActivity.this,"hello Toast",Toast.LENGTH_SHORT).show();
? ? ? ? }
? ? };
? ? private Button mbtnShowToast;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_handler_post);
? ? ? ? mbtnShowToast = findViewById(R.id.btnShowToast);
? ? ? ? mbtnShowToast.setOnClickListener(this);
? ? }
? ? //添加事件監聽
? ? @Override
? ? public void onClick(View v) {
? ? ? ? switch (v.getId()) {
? ? ? ? ? ? case R.id.btnShowToast:
? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * SystemClock.uptimeMillis()表示開機到當前的一個累計時間
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? countHandler.postAtTime(mRunToast, SystemClock.uptimeMillis()+5*1000);
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? }
}
```
#### 2.開辟一個工作線程配合Handler實現計數器的功能
```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? tools:context=".HandlerPost.HandlerPostActivity"
? ? android:orientation="vertical">
? ? <Button
? ? ? ? android:id="@+id/btnStart"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:text="啟動"/>
? ? <Button
? ? ? ? android:id="@+id/btnStop"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:text="關閉"/>
? ? <Button
? ? ? ? android:id="@+id/btnShowToast"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:text="開啟Toast"/>
? ? <TextView
? ? ? ? android:id="@+id/tvCount"
? ? ? ? android:text="你好"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"/>
</LinearLayout>
```
```java
public class HandlerPostActivity extends AppCompatActivity implements View.OnClickListener {
? ? Handler countHandler = new Handler();
? ? int count = 0;
? ? /*線程1:啟動一個Toast顯示線程*/
? ? Runnable mRunToast = new Runnable() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? Toast.makeText(HandlerPostActivity.this, "hello Toast", Toast.LENGTH_SHORT).show();
? ? ? ? }
? ? };
? ? /*線程2:文本區計數器線程*/
? ? Runnable mRunCount = new Runnable() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? textCount.setText("count: " + String.valueOf(count++));
? ? ? ? ? ? //再調用一次,以此形成一個類似于循環的操作,一秒加一次
? ? ? ? ? ? countHandler.postDelayed(mRunCount, 1000);
? ? ? ? }
? ? };
? ? private Button mbtnShowToast;
? ? private TextView textCount;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_handler_post);
? ? ? ? mbtnShowToast = findViewById(R.id.btnShowToast);
? ? ? ? mbtnShowToast.setOnClickListener(this);
? ? ? ? findViewById(R.id.btnStart).setOnClickListener(this);
? ? ? ? findViewById(R.id.btnStop).setOnClickListener(this);
? ? ? ? textCount = findViewById(R.id.tvCount);
? ? }
? ? //添加事件監聽
? ? @Override
? ? public void onClick(View v) {
? ? ? ? switch (v.getId()) {
? ? ? ? ? ? case R.id.btnShowToast:
? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? * SystemClock.uptimeMillis()表示開機到當前的一個累計時間
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? countHandler.postAtTime(mRunToast, SystemClock.uptimeMillis() + 5 * 1000);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case R.id.btnStart:
? ? ? ? ? ? ? ? //表示推遲一秒執行
? ? ? ? ? ? ? ? countHandler.postDelayed(mRunCount, 1000);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case R.id.btnStop:
? ? ? ? ? ? ? ? //移除回調的過程
? ? ? ? ? ? ? ? countHandler.removeCallbacks(mRunCount);
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? }
}
```
運行效果;

### 六、AsyncTask實現多任務
* 使用Handler類來在子線程中更新UI線程總會啟動一些匿名的子線程,太多的子線程給系統帶來了巨大的負擔
* Andoroid提供了一個工具類AdyncTask,來實現異步執行任務
* AsyncTask是抽象類,具有三種泛型:Params、Progress、Result
? * **Params: **表示啟動任務執行的參數,比如HTTP請求的URL
? * **Progress:** 表示后臺任務執行的百分比
? * **Result: **表示后臺執行任務最終返回的結果,比如String,Integer等
* 通過繼承一個AsyncTask類定義一個異步任務類
* Android提供一個讓程序員編寫后臺操作更為容易和透明AsyncTask,使得后臺線程能夠在UI主線程外進行處理
* 使用AsyncTask,不需要自己來寫后臺線程,無需終結后臺線程,只需要創建AsyncTask類,并實現其中的抽象方法以及重寫某些方法
> 下面是一個實例,點擊按鈕,啟動進度條。進度條加載完畢,把“執行完畢”顯示在文本上
#### 1.xml文件
```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:orientation="vertical"
? ? tools:context=".AsynchronousTask.AsyncTaskDemo">
? ? <Button
? ? ? ? android:id="@+id/btn"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:text="Press it"
? ? ? ? android:textAllCaps="false" />
? ? <TextView
? ? ? ? android:id="@+id/txt"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_gravity="center"
? ? ? ? android:textSize="30sp"
? ? ? ? android:text="hello world" />
? ? <ProgressBar
? ? ? ? android:id="@+id/progressbar"
? ? ? ? style="@style/Widget.AppCompat.ProgressBar.Horizontal"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:visibility="invisible"
? ? ? ? android:layout_marginTop="50dp" />
</LinearLayout>
```
#### 2.java文件
```java
public class AsyncTaskDemo extends AppCompatActivity implements View.OnClickListener {
? ? private final static String TAG = "AsyncTaskDemo";
? ? private Button mBtn;
? ? private ProgressBar progressBar;
? ? private TextView mTxt;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_async_task_demo);
? ? ? ? mBtn = findViewById(R.id.btn);
? ? ? ? mBtn.setOnClickListener(this);
? ? ? ? progressBar = findViewById(R.id.progressbar);
? ? ? ? mTxt = findViewById(R.id.txt);
? ? }
? ? @Override
? ? public void onClick(View v) {
? ? ? ? TimeTickLoad timeTickLoad = new TimeTickLoad();
? ? ? ? timeTickLoad.execute(1000);
? ? }
? ? class TimeTickLoad extends AsyncTask<Integer,Integer,String>{
? ? ? ? @Override
? ? ? ? protected void onPreExecute() {
? ? ? ? ? ? Log.i(TAG, "onPreExecute");
? ? ? ? ? ? progressBar.setVisibility(ProgressBar.VISIBLE);
? ? ? ? ? ? super.onPreExecute();
? ? ? ? }
? ? ? ? @Override
? ? ? ? protected void onPostExecute(String s) {
? ? ? ? ? ? Log.i(TAG, "onPostExecute");
? ? ? ? ? ? super.onPostExecute(s);
? ? ? ? ? ? mTxt.setText(s);
? ? ? ? }
? ? ? ? @Override
? ? ? ? protected void onProgressUpdate(Integer... values) {
? ? ? ? ? ? mTxt.setText("onProgressUpdate");
? ? ? ? ? ? Log.i(TAG, "onProgressUpdate");
? ? ? ? ? ? //更新進度條
? ? ? ? ? ? progressBar.setProgress(values[0]);
? ? ? ? ? ? super.onProgressUpdate(values);
? ? ? ? }
? ? ? ? /**
? ? ? ? 這個方法是使用AsyncTask必須進行覆寫的一個方法,耗時的操作在此方法內執行
? ? ? ? */
? ? ? ? @Override
? ? ? ? protected String doInBackground(Integer... integers) {
? ? ? ? ? ? //這里不能撰寫UI相關操作
? ? ? ? ? ? Log.i(TAG, "hello");
//? ? ? ? ? ? mTxt.setText("doInBackground");
? ? ? ? ? ? for (int i = 0; i < 10; i++) {
? ? ? ? ? ? ? ? //執行此操作會自動調用onProgressUpdate()
? ? ? ? ? ? ? ? publishProgress(i*10);
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(integers[0]);
? ? ? ? ? ? ? ? }catch (Exception e){
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return "執行完畢";
? ? ? ? }
? ? }
}
```
> AsyncTask的周期:

**從打印的日志可以看出,首先運行的是onPreExecute()方法,然后運行的是doInBackground(),最后運行的是onPostExecute**
> 運行效果:

#### 3.實現步驟
* 使用execute方法出發異步任務的執行
* 使用onPreExecute()表示執行預處理 : 在本例中實現繪制一個進度條控件
* 使用doInBackground()用于執行較為費時的操作:在本例中就是計算進度
? **這個方法是AsyncTask的關鍵,必須進行覆寫**
* 使用onProgressUpdate()對進度條控件根據進度值做出具體的響應
* 使用onPostExecute()可以對后臺任務的結果做出處理