安卓多任務實現的基本原理

## 安卓多任務實現的基本原理

### 一.基本概念

>? 操作一些耗時操作時候,如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;

? ? ? ? }

? ? }

}

```

運行效果;

![在這里插入圖片描述](https://img-blog.csdnimg.cn/20210616152014880.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R3ZW50eV9mb3VyXzc=,size_16,color_FFFFFF,t_70#pic_center)

### 六、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的周期:

![在這里插入圖片描述](https://img-blog.csdnimg.cn/20210616152048240.png#pic_center)

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

> 運行效果:

![在這里插入圖片描述](https://img-blog.csdnimg.cn/20210616152057393.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R3ZW50eV9mb3VyXzc=,size_16,color_FFFFFF,t_70#pic_center)

#### 3.實現步驟

* 使用execute方法出發異步任務的執行

* 使用onPreExecute()表示執行預處理 : 在本例中實現繪制一個進度條控件

* 使用doInBackground()用于執行較為費時的操作:在本例中就是計算進度

? **這個方法是AsyncTask的關鍵,必須進行覆寫**

* 使用onProgressUpdate()對進度條控件根據進度值做出具體的響應

* 使用onPostExecute()可以對后臺任務的結果做出處理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容