Android開發(fā)中使用多線程的原因
- 避免ANR(Application is not responding)
- 實現(xiàn)異步,比如從云端獲取圖片比較費時,不應(yīng)該使用同步阻塞獲取結(jié)果,使用異步加載完成一個刷新一個
- 多任務(wù),比如多線程下載
Android事件處理機制
在Android中事件處理的原則是:耗時的操作放到其他線程中處理,Main線程中處理不太耗時的操作,否則事件在5秒內(nèi)無法得到響應(yīng)就會出現(xiàn)ANR現(xiàn)象,在Main線程中一般是處理UI,處理Activity事件(OnCreat()等)和事件處理方法(OnClick()等)。
同步和異步的理解
有些事件必須使用同步比如用戶的注冊,需要得到結(jié)果后才能進行下面的操作,有些事件需要異步,比如微博的點贊,只需要點贊完成后提示我一下就行了,沒必要在與服務(wù)器溝通的過程中采用同步處理不可以干其他的事情。
Java中多線程的創(chuàng)建
通過繼承Thread類
Thread類的本質(zhì)是一個實現(xiàn)了Runnable接口的類,繼承Thread類,復(fù)寫run()方法,最后在通過.start()調(diào)用。
通過接口Runnable
Runnable的好處是接口比繼承更自由,新建對象后實例一個Thread對象將Runnable傳入并調(diào)用.start(),如果多個Thread對象傳入了同一個Runnable它們將共享數(shù)據(jù),比如這個賣火車票的例子。
package info.wujiajun.threadlearning;
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);
}
public void btnOnClickByThread(View view){
ThreadText text1 = new ThreadText();
ThreadText text2 = new ThreadText();
text1.start();
text2.start();
}
public void btnOnClickByRunnable(View view){
RunnableText text1 = new RunnableText();
RunnableText text2 = new RunnableText();
Thread t1 = new Thread(text1);
Thread t2 = new Thread(text2);
t1.start();
t2.start();
}
public void saleTicket(View view){
SaleTicket text = new SaleTicket();
Thread t1 = new Thread(text);
Thread t2 = new Thread(text);
t1.start();
t2.start();
}
SaleTicket.java
package info.wujiajun.threadlearning;
import android.util.Log;
/**
* Email:amojury@outlook.com
* Github:https://github.com/amojury
* Created by Jun on 2017/7/27.
*/
public class SaleTicket implements Runnable {
private int ticket = 20;
@Override
public void run() {
while (true){
if(ticket > 0){
Log.d("TAG",Thread.currentThread().getName()+"賣出了第"+(20-ticket+1)+"張票");
ticket--;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else
break;
}
}
}
線程池的應(yīng)用
new Thread的弊端
- 性能差
- 缺乏統(tǒng)一管理,無序競爭
- 無法定時定期執(zhí)行
線程池
Java通過Executors提供了四種線程池
- newCachedThreadPool 可緩存線程池,回收閑置線程
- newFixedThreadPool 定長線程池,有最大并發(fā)數(shù)
- newScheduledThreadPool 可定時定長線程池
- newSingleThreadExecutor 制定順序單線程池
實現(xiàn)多線程之間的通訊
Android線程通訊原理
Handle(send方法)
實現(xiàn)兩秒后改變button上的文字
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btnHelloWorld)
Button btnHelloWorld;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) { //回調(diào)方法,復(fù)寫handleMessage,處理要放在主線程的事情
if(msg.what == 1)
btnHelloWorld.setText("Not HelloWorld");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btnHelloWorld)
public void onViewClicked() {
new Thread(new Runnable() { //開啟新線程
@Override
public void run() {
try {
Thread.sleep(2000); //模擬網(wǎng)絡(luò)請求
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.sendEmptyMessage(1); //使用send方法發(fā)送一個空消息,標(biāo)識是1
}
}).start();
}
}
send的一些方法
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message,long)
- sendMessageDelayer(Message,long)
Handle(post方法)
post方法允許你排列一個Runnable對象在主線程中
- post(Runnable)
- postAtTime(Runnable,long)
- postDelayer(Runnabld)
三種更新UI的方法
- post
- View.post()
- RunOnUiThread()
AsyncTask類(可以在子線程和UI線程中跳轉(zhuǎn))
解決使用Handle較為繁瑣
AsyncTask是一個抽象類,我們需要一個子類繼承它并提供三個泛型參數(shù)。
- Params 參數(shù)
- Progress 進度
- Result 返回結(jié)果
class DownloadTask extend AsyncTask< Void,Integer,Boolean>
Void表示不需要傳參數(shù)給后臺,Integer表示用整形的數(shù)來表示過程,Boolean表示用布爾值表示結(jié)果。
重寫方法
OnPreExecute() 在后臺任務(wù)開始之前調(diào)用,用于初始化UI界面等
doInBackground(Params) 所有代碼都在子線程用于耗時操作,完成后返回結(jié)果,如果第三個參數(shù)是Void,不用返回,如果需要反饋任務(wù)進度調(diào)用publishProgress()方法
OnProgressUpdate(Progress),當(dāng)執(zhí)行publishProgress()方法后被調(diào)用用于界面更新(不一定會被調(diào)用)
-
OnPostEcecute(Result) 后臺任務(wù)執(zhí)行完畢返回參數(shù)。
使用AsyncTask實現(xiàn)下載器
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn)
Button btn;
@BindView(R.id.pb)
ProgressBar pb;
private int progress = 0;private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick(R.id.btn) public void onViewClicked() { new DownloadTask().execute(); } class DownloadTask extends AsyncTask<Void,Integer,Boolean>{ @Override protected void onPreExecute() { KLog.d("開始下載"); pb.setVisibility(View.VISIBLE); } @Override protected Boolean doInBackground(Void... voids) { KLog.d("開始下載"); while (true){ try { Thread.sleep(1000); progress += 10; publishProgress(progress); if(progress >= 100) break; } catch (InterruptedException e) { e.printStackTrace(); return false; } } return true; } @Override protected void onProgressUpdate(Integer... values) { KLog.d("正在下載"+values[0]); pb.setProgress(values[0]); } @Override protected void onPostExecute(Boolean aBoolean) { if(aBoolean){ KLog.d("下載成功"); pb.setVisibility(View.GONE); }else{ KLog.d("下載失敗"); } } }}
AsyncTask優(yōu)點:簡單,快捷
缺點:當(dāng)有多個異步操作時并需要變更ui復(fù)雜