相關資料:
https://www.bilibili.com/video/BV1m4411r73w?p=3https://www.bilibili.com/video/BV1m4411r73w?p=3
https://www.bilibili.com/video/BV1m4411r73w?p=3
1、多執行緒的意義
1.1、為什么要使用多執行緒
為什么要使用多執行緒?
a)提高用戶體驗或避免ANR
在事件處理中需要使用多執行緒,否則會出現ANR,或者因為回應較慢導致用戶體驗很差,
b)異步
應用中有些情況并不一定需要同步阻塞去等待回傳結果,可以通過多執行緒來實作異步,例如你的應用的某個Activity需要從云端
獲取一些圖片,加載圖片比較耗時,這時需要使用異步加載,加載完成一個圖片重繪一個,
c)多任務
例如多執行緒下載
1.2、為什么通過多執行緒可以提高用戶體驗避免ANR
1.2.1、什么是ANR
ANR全程Application Not Responding,意思是程式未回應,如果一個應用無法回應用戶的輸入,系統就會彈出一個ANR對話框,用戶可以自行選擇繼續等待亦或停止當前程式,
1.2.2、深入了解
我們繼續來了解一下Android應用程式的main執行緒,它負責處理UI的繪制,Android系統為了防止應用程式較慢導致系統無法正常運行做了一個處理,一種情況是當用戶輸入事件在5秒內無法得到回應,那么系統會彈出ANR對話框,由用戶決定繼續等待還是強制結束應用程式,(另一種情況是BroadcastReceiver超過10秒沒有執行完也會彈出ANR對話框,見文章 ANR 彈窗的顯示原理)
ANR 的四種場景:
-
Service TimeOut: service 未在規定時間執行完成:前臺服務 20s,后臺 200s
-
BroadCastQueue TimeOut: 未在規定時間內未處理完廣播:前臺廣播 10s 內, 后臺 60s 內
-
ContentProvider TimeOut: publish 在 10s 內沒有完成
-
Input Dispatching timeout: 5s 內未回應鍵盤輸入、觸摸螢屏等事件
ANR 的根本原因是:應用未在規定的時間內處理 AMS 指定的任務才會 ANR,
另外,人眼可以分辨的時間的160毫秒,超過這個時間就可以感到卡頓,所以要控制好這個時間,
1.2.3、事件處理原則
事件處理的原則:所有可能耗時的操作都放到其他執行緒去處理,
Android中的main執行緒的事件處理不能太耗時,否則后續的事件無法在5秒內得到回應,就會彈出ANR對話框,那么哪些方法會在main執行緒執行呢?
1)Activity的生命周期方法,例如:onCreate()、onStart()、onResume()等
2)事件處理方法,例如onClick()、onItemClick()等
通常Android基類中的以on開頭的方法是在main執行緒被回呼的,
提高應用的回應性,可以從這兩方面入手,
一般來說,Activity的onCreate()、onStart()、onResume()方法的執行時間決定了你的應用首頁打開的時間,這里要盡量把不必要的操作放到其他執行緒去處理,如果仍然很耗時,可以使用SplashScreen,使用SplashScreen最好用動態的,這樣用戶知道你的應用沒有死掉,
1.2.4、實際操作
當用戶與你的應用互動時,事件處理方法的執行快慢決定了應用的回應性是否良好,
這里分兩種情況:
1)同步,需要等待回傳結果.例如用戶點擊了注冊按鈕,需要等待服務疏回傳結果,那么需要有一個進度條來提示用戶你的程式正在運行沒有死掉,一般與服務端互動的都要有進度條,例如系統自帶的瀏覽器,URL跳轉時會有進度條.
2)異步,不需要等待回傳結果,例如微博中的收藏功能,點擊完收藏按鈕后是否成功執行完成后告訴我就行了,我不想等它,這里最好實作為異步的. 無論同步異步,事件處理都可能比較耗時,那么需要放到其他執行緒中處理, 等處理完成后,再通知界面重繪,
這里有一點要注意,不是所有的界面重繪行為都需要放到Main執行緒處理, 例如Textview的setText()方法需要在Main執行緒中,否則會拋出 CalledFromWrongThreadException ,而ProgressBar的setProgress()方法 則不需要在Main執行緒中處理,當然你也可以把所有UI組件相關行為都放到Main執行緒中處理,沒有問題,可以減輕你的思考負擔,但你最好了解他們之間的差別,掌握事物之間細微差別的是專家,把事件處理代碼放到其他執行緒中處理,如果處理的結果需要
重繪界面,那么需要執行緒間通訊的方法來實作在其他執行緒中發訊息紿 Main執行緒處理.
1.3、如何實作多執行緒之間的通訊
1.3.1、Handler
Handler、Looper、Message、MessageQueue
1.3.2、AsyncTask
AsyncTask是Android框架提供的異步處理的輔助類,它可以實作耗時操作 在其他執行緒執行,而處理結果在Main執行緒執行,對于開發者而言,它屏蔽掉了多執行緒和后面要即Handler的概念,你不了解怎么處理執行緒間通訊也 沒有關系,AsyncTask體貼的幫你做好了,不過封裝越好施高級的API,對初級程式員反而越不利,就是你不了解它的原理.當你需要面對更加復雜的情況,而高級API無法完成得很好時,你就杯具了.所以,我們也要掌握功 能更強大,更自由的與Main執行緒通訊的方法:Handler的使用.
1.4、利用執行緒池提高性能
這里我們建議使用執行緒池來管理臨時的Thread物件,從而達到提高應用程式 性能的目的.
執行緒池是資源池在執行緒應用中的一個實體.了解執行緒池之前我們首先要了解 一下資源池的概念在JAVA中,創建和銷毀物件是匕陽消耗資源的,我們 如果在應用中需要頻繁創建銷毀某個型別的物件實體,這樣會產生很多臨時 物件,當失去參考的情時物件較多時,虛擬機會進行垃圾回收(GC), CPU在 進行GC時會導致應用程式的運行得不到相應,從而導致應用的回應性降低,
資源池就是用來解決這個問題,當你需要使用物件時,從資源池來獲取.資 源池負責維護物件的生命周期,了解了資源池,就很好理解執行緒池了,執行緒 池就是存放物件型別都是執行緒的資源池.
2、多執行緒的創建
2.1、繼承Thread實作多執行緒
繼承Thread類的方法盡管被我列為一種多執行緒實作方式,但Thread本質上也是實作了Runnable介面的一個實體,它代表一個執行緒的實體,并且啟動執行緒的唯一方法就是通過Thread類的start()實體方法,start()方法是一個native方法,它將啟動一個新執行緒并執行run()方法,這種方式實作多執行緒很簡單,通過自己的類直接extends Thread ,并復寫run()方法,就可以啟動新執行緒并執行自己定義的run()方法,
2.2、實作Runnable介面實作多執行緒
如果自己的類extends另一個類,就無法直接extends Thread ,此時 必須實作一個Runnable介面,同時為了啟動MyThread ,需要首先實體化一個Thread ,用傳入自己的已經實作好Runnable介面的目標物件,
2.3、實作Runnablej介面和繼承Thread的區別
1 ,一個類只能繼承一個父類,存在局限;一個類可以實作多個介面
2 ,在實作Runable介面的時候呼叫Thread(Runnable target)創建行程時 ,使用同一個Runnable實體,則建立的多執行緒的實體變數也是共享的, 但是通過繼承Thread類是不能用一個實體建立多個執行緒,故而實作 Runnable介面適合于資源共享,當然,繼承Thread類也能夠共享變數, 能共享Thread類的static變數;
3 , Runnable介面和Thread之間的聯系:
public class Thread extends Object implements Runnable可以看出Thread類也是Runnable介面的子類;
2.4、代碼實戰
2.4.1、Constant
public class Constant {
public static final String TAG = "Multi_Thread";
}
2.4.2、MyThread
public class MyThread extends Thread {
@Override
public void run() {
Log.i(Constant.TAG, Thread.currentThread().getName() + ".run()");
}
}
2.4.3、MyRunnable
public class MyRunnable implements Runnable{
@Override
public void run() {
Log.i(Constant.TAG, Thread.currentThread().getName() + ".run()");
}
}
2.4.4、SaleTicket
public class SaleTicket implements Runnable {
private int ticket = 20;
@Override
public void run() {
while (true) {
synchronized (this){
if (ticket > 0) {
Log.i(Constant.TAG, Thread.currentThread().getName() + "賣出了第" + (20 - ticket + 1) + "張票");
ticket--;
} else {
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.4.5、MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_test_thread).setOnClickListener(view -> testThread());
findViewById(R.id.btn_test_runnable).setOnClickListener(view -> testRunnable());
findViewById(R.id.btn_test_sale).setOnClickListener(view -> testSale());
}
private void testSale() {
SaleTicket saleTicket = new SaleTicket();
Thread thread1 = new Thread(saleTicket, "A代理");
Thread thread2 = new Thread(saleTicket, "B代理");
Thread thread3 = new Thread(saleTicket, "C代理");
Thread thread4 = new Thread(saleTicket, "D代理");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
private void testRunnable() {
MyRunnable runnable1 = new MyRunnable();
Thread thread1 = new Thread(runnable1);
MyRunnable runnable2 = new MyRunnable();
Thread thread2 = new Thread(runnable1);
thread1.start();
thread2.start();
}
private void testThread() {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
2.4.6、布局檔案
<?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=".MainActivity">
<TextView
android:id="@+id/tv_mark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MainActivity"
android:textSize="30sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_test_thread"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測驗Thread"
android:textAllCaps="false"
android:textSize="26sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_mark" />
<Button
android:id="@+id/btn_test_runnable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測驗Runnable"
android:textAllCaps="false"
android:textSize="26sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_test_thread" />
<Button
android:id="@+id/btn_test_sale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測驗賣票"
android:textAllCaps="false"
android:textSize="26sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_test_runnable" />
</androidx.constraintlayout.widget.ConstraintLayout>
3、執行緒池的應用
4、異步訊息處理機制
5、異步任務
6、設計自己的圖片輪播器
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/309539.html
標籤:其他
