Android中實(shí)現(xiàn)多線程操作的幾種方式
前言
多線程一直是一個(gè)老大難的問(wèn)題,首先因?yàn)樗y以理解,其次在實(shí)際工作中我們需要面對(duì)的關(guān)于線程安全問(wèn)題也并不常見(jiàn),今天就來(lái)總結(jié)一下實(shí)現(xiàn)多線程的幾種方式,可能并不全面,還請(qǐng)各位看官多多補(bǔ)充。
最基礎(chǔ)的方式
繼承Thread類并實(shí)現(xiàn)run()方法
class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是子線程");
}
}
new MyThread().start();
匿名內(nèi)部類
public class ManyThread {
public static void main(String[] args) {
new Thread(){
public void run() {
System.out.println("我是子線程");
};
}.start();
}
}
實(shí)現(xiàn)Runnable接口
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("我是子線程");
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();
太簡(jiǎn)單了吧??!別著急,這只是熱身~
callable+FutureTask
Callable接口類似于Runnable,區(qū)別在于使用callable可以拿到結(jié)果。
FutureTask實(shí)現(xiàn)了RunnableFuture,
public class FutureTask<V> implements RunnableFuture<V> {
...
}
RunnableFuture又實(shí)現(xiàn)了Runnable和Future
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
所以FutureTask既可以當(dāng)Runnable使用又可以當(dāng)Future使用get()來(lái)拿到結(jié)果
future的API:get():當(dāng)任務(wù)結(jié)束后,返回一個(gè)結(jié)果,如果調(diào)用時(shí),任務(wù)還沒(méi)有結(jié)束,則會(huì)阻塞線程,直到任務(wù)執(zhí)行完畢。可以設(shè)置等待時(shí)間,超出時(shí)間或者返回結(jié)果為null,則拋出異常cancel():
//自定義CallAble并實(shí)現(xiàn)call()方法
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int a = 1;
for (int i = 0; i <100000; i++) {
a++;
}
Thread.sleep(2000);
return a;
}
}
public static void main(String[] args) {
//新建CallAble
MyCallable callable = new MyCallable();
//新建FutureTask任務(wù)
FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
//新建Thread并與FutureTask關(guān)聯(lián)
Thread thread = new Thread(futureTask, "A");
thread.start();
int sum;
try {
//拿到callable的結(jié)果
sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
線程池
線程池的創(chuàng)建方式有兩種,
手動(dòng)創(chuàng)建線程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
要注意線程池的幾個(gè)參數(shù):
- corePoolSize :核心線程數(shù)
- maximumPoolSize :允許創(chuàng)建的最大線程數(shù)
- keepAliveTime :這是空閑線程保持存活的最大時(shí)間。
- unit : keepAliveTime的時(shí)間單位
- workQueue :任務(wù)隊(duì)列,保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列
- threadFactory :用于設(shè)置創(chuàng)建線程的工廠,可以通過(guò)線程工廠給每個(gè)創(chuàng)建出來(lái)的線程設(shè)置更有含義的名字
- handler :飽和策略,當(dāng)隊(duì)列和線程池都滿了,說(shuō)明線程處于飽和狀態(tài),必須采取一種策略處理提交的新任務(wù)。默認(rèn)情況下是AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常。
向線程池提交任務(wù)execute()和submit():
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
//使用execute提交任務(wù)
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("我是線程池");
}
});
//之前說(shuō)的callable
MyCallable callable = new MyCallable();
//使用submit提交任務(wù) 返回一個(gè)future
Future<Integer> future = threadPoolExecutor.submit(callable);
try {
//通過(guò)future拿到執(zhí)行callable的結(jié)果
int x = future.get();
System.out.println(x);
} catch (InterruptedException e) {
//中斷異常
e.printStackTrace();
} catch (ExecutionException e) {
//無(wú)法執(zhí)行任務(wù)異常
e.printStackTrace();
}finally {
//關(guān)閉線程池
threadPoolExecutor.shutdown();
}
使用自定義線程池要合理的配置各個(gè)參數(shù)。
- 根據(jù)任務(wù)的性質(zhì):CPU密集型還是IO密集型設(shè)置核心線程數(shù)的大小,cpu密集型應(yīng)配置盡可能小的線程,可以配置為 CPU個(gè)數(shù)+1。由于IO密集型任務(wù)線程并不是一直在執(zhí)行任務(wù),應(yīng)配置盡可能多的線程,可以配置為 2*cpu個(gè)數(shù)
- 優(yōu)先級(jí)不同的任務(wù),可以使用優(yōu)先級(jí)隊(duì)列PriorityBlockingQueue來(lái)處理,它可以讓優(yōu)先級(jí)高的任務(wù)先執(zhí)行
- 建議使用有界隊(duì)列,有界隊(duì)列能增加系統(tǒng)的穩(wěn)定性和預(yù)警能力。
使用Executors創(chuàng)建線程池
這種創(chuàng)建方式本質(zhì)上也是對(duì)ThreadPoolExecutor的參數(shù)進(jìn)行不同的配置,每條創(chuàng)建后邊把源碼中不同的配置放到后邊,以便更清晰的展示出來(lái)
//會(huì)根據(jù)需要?jiǎng)?chuàng)建新線程的線程池
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//核心線程數(shù)設(shè)置為0,最大線程數(shù)則是無(wú)界,的也就是說(shuō)某些情況下會(huì)有無(wú)限多的線程被創(chuàng)建,從而導(dǎo)致一些問(wèn)題,空閑線程最大等待時(shí)長(zhǎng)是60秒
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//使用單個(gè)worker線程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
//核心線程數(shù)和最大線程數(shù)都設(shè)置為1,并使用無(wú)界隊(duì)列作為線程池的工作隊(duì)列,其容量為無(wú)限大,某些情況下會(huì)導(dǎo)致隊(duì)列中有太多的任務(wù)而出錯(cuò)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//可重用固定線程數(shù)的線程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
//核心線程數(shù)和最大線程數(shù)都設(shè)置為創(chuàng)建時(shí)傳入的數(shù)量,空閑等待時(shí)長(zhǎng)為0,說(shuō)明空閑線程會(huì)立即終止,而任務(wù)隊(duì)列容量也是無(wú)限大,可能會(huì)導(dǎo)致同樣的問(wèn)題
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//可以用來(lái)在給定延時(shí)后執(zhí)行異步任務(wù)或者周期性執(zhí)行任務(wù)
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
//核心線程的個(gè)數(shù)為指定的數(shù)量,允許的最大線程數(shù)是無(wú)限的
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
Android中特有的實(shí)現(xiàn)多線程
使用HandlerThread
HandleThread本質(zhì)上就是一個(gè)線程,其繼承了Thread。它的內(nèi)部有自己的looper對(duì)象,并在run方法中通過(guò)Looper.prepare()來(lái)創(chuàng)建消息隊(duì)列,并通過(guò)Looper.loop()開(kāi)啟消息循環(huán),這樣在使用中就允許在HandlerThread中創(chuàng)建Handler了
HandlerThread和普通的Thread是有顯著的區(qū)別的,普通的Thread主要在run方法中執(zhí)行一個(gè)耗時(shí)任務(wù),而HandlerThread在內(nèi)部創(chuàng)建了消息隊(duì)列,外界需要通過(guò)Handler的消息方式來(lái)通知HandlerThread執(zhí)行一個(gè)具體的任務(wù),由于handlerThread的run方法是一個(gè)無(wú)限循環(huán),因此當(dāng)明確不需要再使用HandlerThread時(shí),可以通過(guò)其quit或者quitSafely方法來(lái)終止線程的執(zhí)行。
public class HandlerThread extends Thread {
Looper mLooper;
private @Nullable Handler mHandler;
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
...
}
具體使用:
//創(chuàng)建handlerThread,標(biāo)記一個(gè)名字
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
//啟動(dòng)線程
handlerThread.start();
//創(chuàng)建工作線程的handler,關(guān)聯(lián)handlerThread的Looper對(duì)象。
//復(fù)寫handleMessage處理消息
Handler myHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(msg.what + msg.obj.toString());
super.handleMessage(msg);
}
};
//定義消息
Message msg = new Message();
msg.what = 1;
msg.obj = "A";
//發(fā)送消息到其綁定的消息隊(duì)列中
myHandler.sendMessage(msg);
//結(jié)束線程,停止線程的消息循環(huán)
handlerThread.quit();
使用IntentService
本質(zhì)上是一個(gè)Service,可以看做是Service和HandlerThread的結(jié)合體,它繼承了Service并且是一個(gè)抽象類,因此需要?jiǎng)?chuàng)建它的子類才能使用IntentService。可以用于執(zhí)行后臺(tái)耗時(shí)任務(wù),由于是服務(wù),所以它的優(yōu)先級(jí)比單純的線程要高很多,并且不容易被系統(tǒng)殺死。有如下幾個(gè)特點(diǎn)
- IntentService是繼承自service并處理異步請(qǐng)求的一個(gè)類,其內(nèi)部有一個(gè)工作線程處理耗時(shí)請(qǐng)求
- 任務(wù)執(zhí)行完畢,會(huì)自動(dòng)銷毀
- 如果IntentService啟動(dòng)多次,每個(gè)耗時(shí)操作會(huì)以隊(duì)列的方式在IntentService的onHandleIntent方法中依次執(zhí)行,串行執(zhí)行結(jié)束后,會(huì)自動(dòng)銷毀。
首先看一下IntentService的源碼:
//繼承自service
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
@UnsupportedAppUsage
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//處理消息時(shí)調(diào)用onHandleIntent方法 我們需要重寫這個(gè)方法實(shí)現(xiàn)自己的處理邏輯
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
...
@Override
public void onCreate() {
super.onCreate();
//創(chuàng)建handlerThread并啟動(dòng)
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
//初始化handler并綁定handlerThread的looper
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
//通過(guò)handler發(fā)送消息給HandlerThread處理
mServiceHandler.sendMessage(msg);
}
...
當(dāng)IntentService被啟動(dòng)時(shí),它的onCreate方法被調(diào)用,onCreate方法會(huì)創(chuàng)建一個(gè)HandlerThread,然后使用它的Looper來(lái)構(gòu)造一個(gè)Handler對(duì)象,這樣通過(guò)Handler發(fā)送的消息最終會(huì)在HandlerThread中執(zhí)行。每次啟動(dòng)IntentService,它的onStartCommand方法就會(huì)調(diào)用一次,IntentService在onStartCommand中處理每個(gè)后臺(tái)任務(wù)的Intnet。onStartCommond調(diào)用了onStart,在onStart方法中,IntentService通過(guò)handler發(fā)送一個(gè)消息,這個(gè)消息會(huì)在HandlerThread中處理。當(dāng)onHandleIntent方法執(zhí)行結(jié)束后,IntentService會(huì)通過(guò)stopSelf方法嘗試停止服務(wù),onHandlerIntent是一個(gè)抽象方法,需要我們?cè)谧宇愔袑?shí)現(xiàn),它的作用是從Intent參數(shù)中區(qū)分具體的任務(wù)并執(zhí)行這些任務(wù)。
如何使用?
- 新建service類并繼承自IntentService
- 實(shí)現(xiàn)service的構(gòu)造方法
- 在manifast.xml中注冊(cè)服務(wù)
- 在服務(wù)的onHandleIntent方法中實(shí)現(xiàn)業(yè)務(wù)邏輯
自定義一個(gè)IntentService
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if(intent != null){
String action = intent.getAction();
if(action.equals("A")){
doSomeThingA();
}else if(action.equals("B")){
doSomeThingB();
}
}
}
}
JobIntentService/JobScheduler
系統(tǒng)不允許后臺(tái)應(yīng)用創(chuàng)建后臺(tái)服務(wù),因此8.0引入了Context.startForegroundService(),以在前臺(tái)啟動(dòng)新服務(wù),
由于Android O的后臺(tái)限制,創(chuàng)建后臺(tái)服務(wù)需要使用JobScheduler來(lái)由系統(tǒng)進(jìn)行調(diào)度任務(wù)的執(zhí)行,而使用JobScheduler的方式比較繁瑣,8.0及以上提供了JobIntentService幫助開(kāi)發(fā)者更方便的將任務(wù)交給JobScheduler調(diào)度,其本質(zhì)是Service后臺(tái)任務(wù)在他的OnhandleWork()中進(jìn)行,子類重寫該方法即可。使用較簡(jiǎn)單。
public class SimpleJobIntentService extends JobIntentService {
/**
* 這個(gè)Service 唯一的id
*/
static final int JOB_ID = 1000;
/**
* 將工作加入此服務(wù)的方法,使用中調(diào)用這個(gè)方法
*/
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, SimpleJobIntentService.class, JOB_ID, work);
}
//在這里執(zhí)行耗時(shí)任務(wù)
@Override
protected void onHandleWork(Intent intent) {
Log.i("SimpleJobIntentService", "Executing work: " + intent);
String label = intent.getStringExtra("label");
if (label == null) {
label = intent.toString();
}
toast("Executing: " + label);
for (int i = 0; i < 5; i++) {
Log.i("SimpleJobIntentService", "Running service " + (i + 1)
+ "/5 @ " + SystemClock.elapsedRealtime());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
Log.i("SimpleJobIntentService", "Completed service @ " + SystemClock.elapsedRealtime());
}
@Override
public void onDestroy() {
super.onDestroy();
toast("All work complete");
}
final Handler mHandler = new Handler();
// Helper for showing tests
void toast(final CharSequence text) {
mHandler.post(new Runnable() {
@Override public void run() {
Toast.makeText(SimpleJobIntentService.this, text, Toast.LENGTH_SHORT).show();
}
});
}
}
activity中通過(guò)調(diào)用enqueueWork()方法來(lái)啟動(dòng)
Intent workIntent = new Intent();
num++;
Log.d("houson", "onClick: "+num);
workIntent.putExtra("work","work num:"+num);
//調(diào)用enqueueWork方法
MyJobIntentService.enqueueWork(getApplicationContext(),workIntent);
WorkManager
根據(jù)需求的不同,Android為后臺(tái)任務(wù)提供了多種解決方案,如JobScheduler,Loader,Service等。如果這些API沒(méi)有被適當(dāng)?shù)厥褂?,可能?huì)消耗大量的電量。WorkManager的出現(xiàn),為應(yīng)用程序中那些不需要及時(shí)完成的任務(wù),提供統(tǒng)一的解決方案,以便在設(shè)備電量和用戶體驗(yàn)之間達(dá)到一個(gè)比較好的平衡。
WorkManager最低可以兼容API Level 14,在API Level 23+,通過(guò)JobScheduler來(lái)完成任務(wù),23一下的設(shè)備,通過(guò)AlarmManager和BroadCastReceivers組合完成任務(wù)。
WorkManager主要針對(duì)不需要及時(shí)完成的任務(wù),如發(fā)送日志,同步應(yīng)用程序數(shù)據(jù),備份等。并且可以保證任務(wù)一定會(huì)被執(zhí)行,即使應(yīng)用當(dāng)前不在運(yùn)行中,WorkManager有自己的數(shù)據(jù)庫(kù),關(guān)于任務(wù)的所有信息和數(shù)據(jù)都保存在這個(gè)數(shù)據(jù)庫(kù)中。因此只要你的任務(wù)交給了WorkManager,哪怕你的應(yīng)用程序徹底退出,或者設(shè)備重新啟動(dòng),WorkManager依然能夠保證完成你交給的任務(wù)。
如何使用?
添加依賴
dependencies {
def versions = "2.2.0"
implementation "androidx.work:work-runtime:$versions"
}
定義Worker任務(wù):
worker是任務(wù)的執(zhí)行者,是一個(gè)抽象類,需要繼承它實(shí)現(xiàn)要執(zhí)行的任務(wù)。
//繼承worker,自定義worker
class MyWork extends Worker{
public MyWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
//執(zhí)行耗時(shí)任務(wù)
@NonNull
@Override
public Result doWork() {
//拿到傳進(jìn)來(lái)的數(shù)據(jù)
String input_data = getInputData().getString("input_data");
// 任務(wù)執(zhí)行完成后返回?cái)?shù)據(jù)
Data outputData = new Data.Builder().putString("output_data", "Success!").build();
// 執(zhí)行成功返回Result.success()
// 執(zhí)行失敗返回Result.failure()
// 需要重新執(zhí)行返回Result.retry()
return Result.success(outputData);
}
}
使用WorkRequest配置任務(wù)。
WorkRequest指定讓哪個(gè) Woker 執(zhí)行任務(wù),指定執(zhí)行的環(huán)境,執(zhí)行的順序等。要使用它的子類 OneTimeWorkRequest(執(zhí)行一次) 或 PeriodicWorkRequest(周期執(zhí)行)。
通過(guò)WorkRequest配置我們的任務(wù)何時(shí)運(yùn)行以及如何運(yùn)行
//設(shè)置任務(wù)的觸發(fā)條件
Constraints constraints = new Constraints.Builder()
.setRequiresCharging(true) // 在充電時(shí)執(zhí)行
.setRequiresStorageNotLow(true) // 不在存儲(chǔ)容量不足時(shí)執(zhí)行
// 網(wǎng)絡(luò)狀態(tài)
//NOT_REQUIRED--沒(méi)有要求
//CONNECTED--網(wǎng)絡(luò)連接
//UNMETERED--連接無(wú)限流量的網(wǎng)絡(luò)
//METERED--連接按流量計(jì)費(fèi)的網(wǎng)絡(luò)
//NOT_ROAMING--連接非漫游網(wǎng)絡(luò)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresDeviceIdle(true) // 在待機(jī)狀態(tài)下執(zhí)行,需要 API 23
.setRequiresBatteryNotLow(true)// 不在電量不足時(shí)執(zhí)行
.build();
//然后將該Constraints設(shè)置到WorkRequest。WorkRequest是一個(gè)抽象類,
//它有兩種實(shí)現(xiàn)OneTimeWorkRequest和PeriodicWorkRequest,分別對(duì)應(yīng)的是一次性任務(wù)和周期性任務(wù)。
//定義要傳遞的數(shù)據(jù)
Data inputData = new Data.Builder().putString("input_data", "Hello").build();
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(MyWork.class)
.setConstraints(constraints)
.setInputData(inputData)
.build();
WorkManager
將任務(wù)提交給系統(tǒng)。管理任務(wù)請(qǐng)求和任務(wù)隊(duì)列,發(fā)起的 WorkRequest 會(huì)進(jìn)入它的任務(wù)隊(duì)列。WorkManager.enqueue()方法會(huì)將你配置好的WorkRequest交給系統(tǒng)來(lái)執(zhí)行。
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest);
觀察任務(wù)的狀態(tài)
任務(wù)在提交給系統(tǒng)后,通過(guò)WorkInfo獲知任務(wù)的狀態(tài),WorkInfo包含了任務(wù)的id,tag,以及Worker對(duì)象傳遞過(guò)來(lái)的outputData,以及任務(wù)當(dāng)前的狀態(tài)。有三種方式可以得到WorkInfo對(duì)象。

通過(guò)LiveData,我們便可以在任務(wù)狀態(tài)發(fā)生變化的時(shí)候,收到通知。同時(shí)通過(guò)LiveData的WorkInfo.getOutputData(),得到從Worker傳遞過(guò)來(lái)的數(shù)據(jù)。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.getId()).observe(
MainActivity.this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
Log.d("onChanged()->", "workInfo:"+workInfo);
if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
String outputData = workInfo.getOutputData().getString("output_data");
Log.d("onChanged()->", "outputData:"+outputData);
}
}
}
);
取消任務(wù)
WorkManager.getInstance(MainActivity.this).cancelAllWork();
以上就是WorkManager的一些基本使用,更詳細(xì)的可以參照官方文檔或者其他文章,這里就不仔細(xì)講解了。工作中,我們經(jīng)常需要處理后臺(tái)任務(wù),如果處理后臺(tái)任務(wù)所采用的API沒(méi)有被正確使用,那么很可能會(huì)消耗大量設(shè)備的電量。Android出于設(shè)備電量的考慮,為開(kāi)發(fā)者提供了WorkManager,旨在將一些不需要及時(shí)完成的任務(wù)交給它來(lái)完成。
使用協(xié)程
協(xié)程是一種并發(fā)設(shè)計(jì)模式,您可以在 Android 平臺(tái)上使用它來(lái)簡(jiǎn)化異步執(zhí)行的代碼,協(xié)程是輕量級(jí)的線程,為什么是輕量的?因?yàn)樗诰€程池API。協(xié)程可以使用阻塞式的方式寫出非阻塞式的代碼,解決回調(diào)地獄的問(wèn)題。
協(xié)程有如下的特點(diǎn):
- 輕量:您可以在單個(gè)線程上運(yùn)行多個(gè)協(xié)程,因?yàn)閰f(xié)程支持掛起,不會(huì)使正在運(yùn)行協(xié)程的線程阻塞。掛起比阻塞節(jié)省內(nèi)存,且支持多個(gè)并行操作。
- 內(nèi)存泄漏更少:使用結(jié)構(gòu)化并發(fā)機(jī)制在一個(gè)作用域內(nèi)執(zhí)行多項(xiàng)操作。
- 內(nèi)置取消支持:取消操作會(huì)自動(dòng)在運(yùn)行中的整個(gè)協(xié)程層次結(jié)構(gòu)內(nèi)傳播。
- Jetpack 集成:許多 Jetpack 庫(kù)都包含提供全面協(xié)程支持的擴(kuò)展。某些庫(kù)還提供自己的協(xié)程作用域,可供您用于結(jié)構(gòu)化并發(fā)。
引入?yún)f(xié)程
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}
使用
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
協(xié)程的使用當(dāng)然不是這么簡(jiǎn)單,有很多的API,這里限于篇幅,就不深入講解了。
AsyncTask
是一個(gè)輕量級(jí)的異步任務(wù)類,在高版本中已經(jīng)過(guò)時(shí)了。但研究一下還是很有意義的。他可以在線程池中執(zhí)行后臺(tái)任務(wù),然后把執(zhí)行的進(jìn)度和最終結(jié)果傳遞給主線程并在主線程中更新ui。AsyncTask封裝了Thread和Handler,通過(guò)AsyncTask可以更加方便的執(zhí)行后臺(tái)任務(wù)以及在主線程中訪問(wèn)ui。
AsyncTask提供了四個(gè)核心方法:
- onPreExecute:在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之前,此方法會(huì)被調(diào)用,一般用于一些準(zhǔn)備工作
- doInBackground:在線程池中執(zhí)行,此方法用于執(zhí)行異步任務(wù),params參數(shù)表示異步任務(wù)的輸入?yún)?shù),此方法中可以通過(guò)publishProgress來(lái)更新任務(wù)的進(jìn)度。
- onProgressUpdata(Progress...values):在主線程中執(zhí)行,當(dāng)后臺(tái)任務(wù)的執(zhí)行進(jìn)度發(fā)生改變時(shí)調(diào)用此方法
- onPostExecute:在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之后,此方法會(huì)被調(diào)用,result參數(shù)是后臺(tái)任務(wù)的返回值,即doInbackground的返回值
AsyncTask在使用過(guò)程中有一些限制條件:
- AsyncTask必須在主線程中加載,也就是第一次訪問(wèn)AsyncTask必須發(fā)生在主線程
- AsyncTask的對(duì)象須在主線程中創(chuàng)建
- execute方法必須在ui線程調(diào)用
- 不要在程序中直接調(diào)用onPreExecute、onPostExecute、doInBackground、onProgressUpdate方法
- 一個(gè)AsyncTask對(duì)象只能執(zhí)行一次,也就是只能調(diào)用一次execute方法,否則會(huì)報(bào)異常
原理分析:
從execute方法開(kāi)始分析,調(diào)用executeOnExecutor方法,sDefaultExecutor是一個(gè)串行的線程池如下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
//線程池開(kāi)始執(zhí)行 封裝為Futuretask交個(gè)線程池處理
exec.execute(mFuture);
return this;
}
sDefaultExecutor是一個(gè)串行線程池,一個(gè)進(jìn)程中所有的AsyncTask任務(wù)都在這個(gè)線程池中排隊(duì)執(zhí)行。首先系統(tǒng)會(huì)把AsyncTask的Params參數(shù)封裝為FutureTask對(duì)象,這里FutureTask充當(dāng)Runnable,接著這個(gè)FutureTask會(huì)交給SerialExecutor的execute方法處理,SerialExecute的execute方法首先把FutureTask對(duì)象插入到任務(wù)隊(duì)列中,接著調(diào)用scheduleNext方法執(zhí)行FutureTask任務(wù),從隊(duì)列中取出任務(wù)交給THREAD_POOL_EXECUTOR線程池去真正執(zhí)行任務(wù)。InternalHandler用于將執(zhí)行環(huán)境從線程池切換到主線程。
//串行線程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//線程池用于真正執(zhí)行任務(wù)
public static final Executor THREAD_POOL_EXECUTOR;
//用于將執(zhí)行環(huán)境從線程池切換到主線程
private static InternalHandler sHandler;
//初始化執(zhí)行任務(wù)的線程池
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
//用于調(diào)度任務(wù)的串行線程池
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//從隊(duì)列中取出任務(wù)交個(gè)THREAD_POOL_EXECUTOR線程池去真正執(zhí)行任務(wù)
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
再看一下任務(wù)future,call方法中調(diào)用postResult方法,通過(guò)handler發(fā)送消息,最終在handleMessage方法中進(jìn)行處理。
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
結(jié)語(yǔ)
以上介紹了幾種實(shí)現(xiàn)多線程操作的方式,有些并沒(méi)有深入分析,當(dāng)然了,實(shí)現(xiàn)多線程的方式不止以上的幾種,還請(qǐng)各位看官多多補(bǔ)充,如有錯(cuò)誤的地方還請(qǐng)指出。
到此這篇關(guān)于Android中實(shí)現(xiàn)多線程操作的幾種方式的文章就介紹到這了,更多相關(guān)Android多線程操作 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 代碼寫控件代替XML簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android 代碼寫控件代替XML簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android模擬實(shí)現(xiàn)支付寶螞蟻森林效果
這篇文章主要為大家詳細(xì)介紹了如何利用Android模擬實(shí)現(xiàn)支付寶中螞蟻森林的動(dòng)畫效果,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-09-09
Android實(shí)現(xiàn)可拖拽列表和多選功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)可拖拽列表和多選功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
Android穩(wěn)定性:可遠(yuǎn)程配置化的Looper兜底框架
這篇文章主要為大家介紹了Android穩(wěn)定性可遠(yuǎn)程配置化的Looper兜底框架實(shí)例實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
android模擬器開(kāi)發(fā)和測(cè)試nfc應(yīng)用實(shí)例詳解
本文介紹android模擬器開(kāi)發(fā)nfc應(yīng)用詳解,大家參考使用吧2013-12-12
android項(xiàng)目手機(jī)衛(wèi)士來(lái)電顯示號(hào)碼歸屬地
由于詐騙電話越來(lái)越猖狂,號(hào)碼歸屬地顯示越來(lái)越重要,本篇文章主要介紹了android手機(jī)衛(wèi)士來(lái)電顯示號(hào)碼歸屬地,有要的朋友可以了解一下。2016-10-10

