后臺(tái)任務(wù)隊(duì)列管理神器 Android-Priority-Job-Queue

有人說(shuō)“Android的開(kāi)發(fā),玩的就是多線(xiàn)程”饲帅。從某個(gè)角度來(lái)說(shuō)的確如此复凳,現(xiàn)在的App被設(shè)計(jì)的越來(lái)越復(fù)雜瘤泪,相信很多開(kāi)發(fā)人員都因大量而又復(fù)雜的后臺(tái)任務(wù)(background work)而焦頭爛額:Async-Task和Activity的生命周期太過(guò)于耦合灶泵,雖然實(shí)現(xiàn)簡(jiǎn)單但是對(duì)于重要的后臺(tái)任務(wù)還是不靠譜;Loaders雖然可以用于異步從磁盤(pán)列讀取數(shù)據(jù)对途,但是對(duì)于異步的網(wǎng)絡(luò)請(qǐng)求就無(wú)能為力了赦邻;相對(duì)給力點(diǎn)的方案是后臺(tái)服務(wù)中開(kāi)辟進(jìn)程池(Thread Pool),使用ThreadPoolExecutor來(lái)幫助管理線(xiàn)程实檀,但是app越復(fù)雜后臺(tái)操作越多惶洲,需要處理的多線(xiàn)程的問(wèn)題越多,想一想就頭大.....
但是各位讀者不要沮喪膳犹,今天就是向大家介紹一個(gè)后臺(tái)任務(wù)隊(duì)列管理庫(kù)Android-Priority-Job-Queue恬吕,它將提供一個(gè)優(yōu)雅的架構(gòu)來(lái)解決以上所有的問(wèn)題!

1. 簡(jiǎn)介

用官方的話(huà)來(lái)說(shuō)须床,Android-Priority-Job-Queue是一款專(zhuān)門(mén)為Android平臺(tái)編寫(xiě)的铐料,實(shí)現(xiàn)了Job Queue的后臺(tái)任務(wù)隊(duì)列類(lèi)庫(kù),能夠輕松的在后臺(tái)執(zhí)行定時(shí)任務(wù)豺旬,并且提高了用戶(hù)體驗(yàn)和應(yīng)用的穩(wěn)定性钠惩。其設(shè)計(jì)理念以靈活性和功能性為主,并且一直在更新族阅。

“Priority Job Queue is an implementation of a Job Queue specifically written for Android to easily schedule jobs (tasks) that run in the background, improving UX and application stability.”
It is written primarily with flexibility & functionality in mind. This is an ongoing project, which we will continue to add stability and performance improvements.

github : https://github.com/yigit/android-priority-jobqueue
在這里可以了解到更多更全面的介紹篓跛。

其使用框架也很簡(jiǎn)便直接:

  1. 構(gòu)造一個(gè)任務(wù)管理器JobManager,為我們管理任務(wù)坦刀;
  2. 自定義Job類(lèi)愧沟,來(lái)作為任務(wù)的載體;
  3. 在需要時(shí)鲤遥,將自定義的Job類(lèi)實(shí)例加入到JobManager中沐寺;

這樣就OK了,JobManager會(huì)根據(jù)優(yōu)先級(jí)渴频、持久性芽丹、負(fù)載平衡、延遲卜朗,網(wǎng)絡(luò)控制拔第、分組等因素來(lái)管理任務(wù)的執(zhí)行咕村。由于是獨(dú)立于各個(gè)Activity,JobManager為Job的執(zhí)行提供了一個(gè)很好的生命周期蚊俺,用戶(hù)體驗(yàn)更為棒懈涛。是不是很驚喜!閑話(huà)少敘泳猬,我們來(lái)看使用范例吧批钠!

2. 使用實(shí)例

2.1 添加方法

在Android Studio添加,如下引用:

dependencies {
    compile 'com.birbit:android-priority-jobqueue:2.0.1'
}

如果你很懷舊得封,還在堅(jiān)持使用Eclipse埋心,那就在maven這樣配置:

<dependency>
    <groupId>com.birbit</groupId>
    <artifactId>android-priority-jobqueue</artifactId>
    <version>2.0.1</version>
</dependency>

2.2 配置JobManager

JobManager是整個(gè)框架的核心。作為一個(gè)重型的對(duì)象忙上,建議Application只構(gòu)建一個(gè)JobManager實(shí)例供全局使用拷呆。另一方面,為了讓任務(wù)的執(zhí)行有一個(gè)更好的生命周期疫粥,建議將JobManager放在Application類(lèi)茬斧,而不是一個(gè)具體的Activity。以下是示例代碼:

import android.app.Application;
import android.util.Log;
import com.path.android.jobqueue.JobManager;
import com.path.android.jobqueue.config.Configuration;
import com.path.android.jobqueue.log.CustomLogger;

public class JobQueueApplication extends Application {
    private JobManager jobManager;
    private static JobQueueApplication instance;
    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;//1. Application的實(shí)例
        configureJobManager();//2. 配置JobMananger
    }

   //私有構(gòu)造器
   private JobQueueApplication(){
        instance=this;
    }
    public JobManager getJobManager() {
        return jobManager;
    }

    public static JobQueueApplication getInstance() {
        return instance;
    }

    private void configureJobManager() {
        //3. JobManager的配置器梗逮,利用Builder模式
        Configuration configuration = new Configuration.Builder(this)
                .customLogger(new CustomLogger() {
                    private static final String TAG = "JOBS";
                    @Override
                    public boolean isDebugEnabled() {
                        return true;
                    }

                    @Override
                    public void d(String text, Object... args) {
                        Log.d(TAG, String.format(text, args));
                    }

                    @Override
                    public void e(Throwable t, String text, Object... args) {
                        Log.e(TAG, String.format(text, args), t);
                    }

                    @Override
                    public void e(String text, Object... args) {
                        Log.e(TAG, String.format(text, args));
                    }
                })
                .minConsumerCount(1)//always keep at least one consumer alive
                .maxConsumerCount(3)//up to 3 consumers at a time
                .loadFactor(3)//3 jobs per consumer
                .consumerKeepAlive(120)//wait 2 minute
                .build();
        jobManager = new JobManager(this, configuration);
    }
}

以上是整個(gè)自定義Application類(lèi)的代碼项秉,其邏輯很清晰。首先為Application類(lèi)設(shè)置單例模式慷彤,并保存私有變量jobManager娄蔼;然后在onCreate()中調(diào)用configureJobManager方法來(lái)完成jobManager的初始化。我們來(lái)看下在其初始化參數(shù)Configuration實(shí)例中都配置了哪些內(nèi)容:

  • CustomLogger:日志設(shè)置瞬欧,便于用戶(hù)查看任務(wù)隊(duì)列的工作信息贷屎,在調(diào)試的過(guò)程中很有用,后面分析JobManager的任務(wù)調(diào)度時(shí)就會(huì)用到艘虎;
  • minConsumerCount&maxConsumerCount: 最少消費(fèi)者和最多消費(fèi)者數(shù)量唉侄,所謂的消費(fèi)者就是開(kāi)啟的線(xiàn)程,用來(lái)執(zhí)行任務(wù)野建。任務(wù)隊(duì)列實(shí)際上就是一個(gè)生產(chǎn)者和消費(fèi)者問(wèn)題属划,用戶(hù)是生產(chǎn)者,提交任務(wù)(Job)候生,開(kāi)啟的線(xiàn)程就是消費(fèi)者來(lái)執(zhí)行任務(wù)同眯,任務(wù)被執(zhí)行就是“消費(fèi)”。這里所謂的最少和最大將會(huì)下面具體解釋?zhuān)?/li>
  • loadFactor(int): 其意義是設(shè)置多少個(gè)任務(wù)為一組被分配個(gè)一個(gè)消費(fèi)者(Thread)唯鸭,也就是一個(gè)Thread最多要“承包”幾個(gè)任務(wù)來(lái)執(zhí)行须蜗;
  • consumerKeepAlive :設(shè)置消費(fèi)者在沒(méi)有任務(wù)的情況下保持存活的時(shí)長(zhǎng),以秒為單位,如果過(guò)了這個(gè)時(shí)長(zhǎng)還沒(méi)有任務(wù)明肮,消費(fèi)者線(xiàn)程就會(huì)被回收菱农;

Configuration以建筑者模式來(lái)鏈?zhǔn)脚渲茫瑢?duì)此不是很熟悉的讀者可以參考如何構(gòu)建含有大量參數(shù)的構(gòu)造器:淺談Builder Pattern的使用和鏈?zhǔn)脚渲?/a>.
有了JobManger自然還需要Job柿估,下面就來(lái)看看如何設(shè)置Job.

2.3 Job

自定義的Job類(lèi)需要繼承Android-Priority-Job-Queue提供的Job類(lèi)循未,下面就是是一個(gè)簡(jiǎn)單的范例,這個(gè)任務(wù)的內(nèi)容就是睡眠5秒秫舌。

import android.util.Log;
import com.path.android.jobqueue.Job;
import com.path.android.jobqueue.Params;
import com.path.android.jobqueue.RetryConstraint;

public class MyJob extends Job{
    public static final int PRIORITY = 1;
    private String text;
    String TAG = "Myjob";
    int sleepTime;
    public MyJob(String text) {
        // A job should be persisted in case the application exits 
        // before job is completed.
        super(new Params(PRIORITY).persist());
        this.text = text;
        sleepTime = 5;
        Log.i(TAG, text+"  goin");
    }
    @Override
    public void onAdded() {
        // Job has been saved to disk.
        // This is a good place to dispatch a UI event 
        // to indicate the job will eventually run.
        Log.i(TAG, text+"  Onadded");
    }
    @Override
    public void onRun() throws Throwable {
        // Job logic goes here. 
        // All work done here should be synchronous, 
        // a job is removed from the queue once onRun() finishes.
        Thread.sleep(sleepTime*1000);
        Log.i(TAG, text+"  onRun");
    }
    @Override
    protected RetryConstraint shouldReRunOnThrowable(Throwable throwable, int runCount,
                                                     int maxRunCount) {
        // An error occurred in onRun.
        // Return value determines whether this job should retry or cancel. You can further
        // specify a backoff strategy or change the job's priority. You can also apply the
        // delay to the whole group to preserve jobs' running order.
        return RetryConstraint.createExponentialBackoff(runCount, 10);
    }
    @Override
    protected void onCancel() {

    }
}

Job類(lèi)的模塊很清晰的妖,我們只需要按照要求覆蓋以下方法即可:

  1. 在構(gòu)造器,利用Params類(lèi)中配置參數(shù)足陨;
  2. onAdded(): 任務(wù)加入隊(duì)列并被保存在硬盤(pán)上嫂粟,定義此時(shí)要處理的邏輯;
  3. onRun(): 任務(wù)開(kāi)始執(zhí)執(zhí)行钠右,在此定義任務(wù)的主題邏輯赋元,當(dāng)執(zhí)行完畢后,任務(wù)將被從任務(wù)隊(duì)列中刪除飒房;
  4. onCancel():任務(wù)取消的時(shí)候要執(zhí)行的邏輯;
  5. shouldReRunOnThrowable():當(dāng)onRun()方法中拋出異常時(shí)媚值,就會(huì)調(diào)用該函數(shù)狠毯,該函數(shù)返回Job類(lèi)在執(zhí)行發(fā)生異常時(shí)的應(yīng)對(duì)策略,是重新執(zhí)行還是取消褥芒,或者是一定時(shí)間之后再?lài)L試嚼松。

在這里特別說(shuō)明下Params類(lèi),通過(guò)該類(lèi)可以配置Job類(lèi)的各種信息锰扶,同樣也采用類(lèi)Builder Pattern的鏈?zhǔn)脚渲茫?/p>

  1. 默認(rèn)構(gòu)造器傳入的是int參數(shù)是該任務(wù)的優(yōu)先級(jí)献酗,優(yōu)先級(jí)越高,越優(yōu)先執(zhí)行坷牛。
public Params(int priority) {
        this.priority = priority;
    }
  1. requireNetwork(): 設(shè)置該任務(wù)要求訪(fǎng)問(wèn)網(wǎng)絡(luò)罕偎;
  2. groupBy(String groupId):設(shè)置組ID,被設(shè)置相同組ID的任務(wù)京闰,將會(huì)按照順序執(zhí)行颜及;
  3. persist():設(shè)置任務(wù)為可持久化的,持久化要求Job類(lèi)為序列化的蹂楣,這一點(diǎn)并不意外俏站,因?yàn)橐粋€(gè)類(lèi)的內(nèi)容只有序列化之后才能變成字節(jié)模式保存在硬盤(pán)上;
  4. delayInMs(long delayMs):設(shè)置延遲時(shí)間痊土,ms為單位肄扎,在該時(shí)間之后再放入任務(wù)隊(duì)列中。

2.4 執(zhí)行任務(wù)

Job的執(zhí)行很簡(jiǎn)單,就把任務(wù)類(lèi)加入到任務(wù)隊(duì)列中即可以犯祠。


public class MainActivity extends AppCompatActivity {
    private JobManager jobManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_start = (Button)findViewById(R.id.start_job_button);
        //JobManager對(duì)象
        jobManager = JobQueueApplication.getInstance().getJobManager();       
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for(int i=0; i<9 ;i++) {
                    //將任務(wù)加入后臺(tái)隊(duì)列中
                    jobManager.addJobInBackground(new MyJob(“"+i));
                } 
            }
        });
    }
}

在一個(gè)Activity中點(diǎn)擊Button,就會(huì)把9個(gè)MyJob實(shí)例加入到后臺(tái)隊(duì)列中萌丈。其運(yùn)行效果如下:

03-25 21:44:47.207 I/Myjob: 0 goin
03-25 21:44:47.208 I/Myjob: 1 goin
03-25 21:44:47.208 I/Myjob: 2 goin
03-25 21:44:47.208 I/Myjob: 3 goin
03-25 21:44:47.208 I/Myjob: 4 goin
03-25 21:44:47.208 I/Myjob: 5 goin
03-25 21:44:47.208 I/Myjob: 6 goin
03-25 21:44:47.208 I/Myjob: 7 goin
03-25 21:44:47.208 I/Myjob: 8 goin
03-25 21:44:47.218 I/Myjob: 0 Onadded
03-25 21:44:47.228 I/Myjob: 1 Onadded
03-25 21:44:47.241 I/Myjob: 2 Onadded
03-25 21:44:47.251 I/Myjob: 3 Onadded
03-25 21:44:47.260 I/Myjob: 4 Onadded
03-25 21:44:47.274 I/Myjob: 5 Onadded
03-25 21:44:47.280 I/Myjob: 6 Onadded
03-25 21:44:47.291 I/Myjob: 7 Onadded
03-25 21:44:47.307 I/Myjob: 8 Onadded
03-25 21:44:52.235 I/Myjob: 1 onRun
03-25 21:44:52.267 I/Myjob: 4 onRun
03-25 21:44:52.297 I/Myjob: 7 onRun
03-25 21:44:57.250 I/Myjob: 8 onRun
03-25 21:44:57.282 I/Myjob: 6 onRun
03-25 21:44:57.310 I/Myjob: 5 onRun
03-25 21:45:02.264 I/Myjob: 3 onRun
03-25 21:45:02.299 I/Myjob: 2 onRun
03-25 21:45:02.324 I/Myjob: 0 onRun

為了便于查看,這里輸出的日志信息只保留最主要的內(nèi)容,我們可以看到任務(wù)0-8依次啟動(dòng)并加入到任務(wù)隊(duì)列中雷则,然后再被執(zhí)行辆雾,JobManager幫你管理了一切,完美月劈!
有的讀者可能已經(jīng)發(fā)行了度迂,任務(wù)0-8的執(zhí)行順序是混亂的,這就涉及到Android-Priority-JobQueue任務(wù)的調(diào)度問(wèn)題猜揪,也是本人最好興趣的內(nèi)容惭墓。

3. 任務(wù)調(diào)度分析

回顧一下,我們對(duì)JobManager的設(shè)置:至少一個(gè)消費(fèi)者線(xiàn)程而姐,至多三個(gè)腊凶;每個(gè)消費(fèi)者線(xiàn)程最多處理三個(gè)任務(wù)。這些設(shè)置就是在影響任務(wù)調(diào)度拴念。
當(dāng)一個(gè)任務(wù)要加入任務(wù)隊(duì)列時(shí)钧萍,會(huì)做出如下的判斷:

  1. 首先計(jì)算當(dāng)前能處理的任務(wù)數(shù)的最大值(CurrentTaskCapacity)= 已啟動(dòng)的消費(fèi)線(xiàn)程數(shù)(CurrentThreadNum) * 每個(gè)線(xiàn)程處理任務(wù)的容量(PerThreadTaskCapacity);
CurrentTaskCapacity = CurrentThreadNum * PerThreadTaskCapacity
  1. 如果當(dāng)前任務(wù)數(shù)加上現(xiàn)在要加入任務(wù)政鼠,小于或等于CurrentTaskCapacity风瘦,則將該任務(wù)加入到任務(wù)隊(duì)列中;
  2. 如果當(dāng)前任務(wù)數(shù)加上現(xiàn)在要加入任務(wù)公般,大于CurrentTaskCapacity万搔,則判斷CurrentThreadNum是不是已經(jīng)達(dá)到設(shè)置的最大值,如果沒(méi)有就開(kāi)辟新的消費(fèi)者線(xiàn)程來(lái)承擔(dān)任務(wù)官帘;
  3. 如果CurrentTaskCapacityCurrentThreadNum都達(dá)到上限瞬雹,那就對(duì)不起了,該任務(wù)就不能加入隊(duì)列刽虹,需要等到有任務(wù)執(zhí)行完畢酗捌,任務(wù)容量又有了空余時(shí)才能進(jìn)入隊(duì)列等待執(zhí)行。

如何驗(yàn)證上面結(jié)論呢状婶?還記得我們?cè)贏pplication類(lèi)中配置JobManager時(shí)設(shè)置的日志信息嗎? 這個(gè)時(shí)候它就發(fā)揮作用了意敛,下面是任務(wù)0-8加入到任務(wù)隊(duì)列中時(shí)輸出的日志,內(nèi)容很多膛虫,請(qǐng)注意的表黑的部分:

03-25 21:44:47.218 D/JOBS: added job id: 84 class: MyJob priority: 0 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.218 I/Myjob: 0 Onadded
03-25 21:44:47.222 D/JOBS: pool-1-thread-1: load factor check. true = (0 < 1)|| (0 * 3 < 1 + 0). consumer thread: false
03-25 21:44:47.222 D/JOBS: adding another consumer
03-25 21:44:47.223 D/JOBS:** starting consumer Thread-177**
03-25 21:44:47.224 D/JOBS: looking for next job
03-25 21:44:47.224 D/JOBS: running groups
03-25 21:44:47.224 D/JOBS: non persistent result null
03-25 21:44:47.228 D/JOBS: added job id: 85 class: MyJob priority: 1 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.228 I/Myjob: 1 Onadded
03-25 21:44:47.234 D/JOBS: persistent result com.path.android.jobqueue.JobHolder@55
03-25 21:44:47.234 D/JOBS: running job MyJob
03-25 21:44:47.237 D/JOBS: pool-1-thread-1: load factor check. false = (1 < 1)|| (1 * 3 < 1 + 1). consumer thread: false
03-25 21:44:47.241 D/JOBS: added job id: 86 class: MyJob priority: 2 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.241 I/Myjob: 2 Onadded
03-25 21:44:47.245 D/JOBS: pool-1-thread-1: load factor check. false = (1 < 1)|| (1 * 3 < 2 + 1). consumer thread: false
03-25 21:44:47.251 D/JOBS: added job id: 87 class: MyJob priority: 3 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.251 I/Myjob: 3 Onadded
03-25 21:44:47.255 D/JOBS: pool-1-thread-1: load factor check. true=(1 < 1) || (1 * 3 < 3 + 1). consumer thread: false
03-25 21:44:47.255 D/JOBS: adding another consumer
03-25 21:44:47.256 D/JOBS: starting consumer Thread-178
03-25 21:44:47.257 D/JOBS: looking for next job
03-25 21:44:47.257 D/JOBS: running groups
03-25 21:44:47.257 D/JOBS: non persistent result null
03-25 21:44:47.260 D/JOBS: added job id: 88 class: MyJob priority: 4 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.260 I/Myjob: 4 Onadded
03-25 21:44:47.264 D/JOBS: persistent result com.path.android.jobqueue.JobHolder@58
03-25 21:44:47.264 D/JOBS: running job MyJob
03-25 21:44:47.270 D/JOBS: pool-1-thread-1: load factor check. false = (2 < 1)|| (2 * 3 < 3 + 2). consumer thread: false
03-25 21:44:47.274 D/JOBS: added job id: 89 class: MyJob priority: 5 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.274 I/Myjob: 5 Onadded
03-25 21:44:47.277 D/JOBS: pool-1-thread-1: load factor check. false = (2 < 1)|| (2 * 3 < 4 + 2). consumer thread: false
03-25 21:44:47.280 D/JOBS: added job id: 90 class: MyJob priority: 6 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.280 I/Myjob: 6 Onadded
03-25 21:44:47.283 D/JOBS: pool-1-thread-1: load factor check. true = (2 < 1)|| (2 * 3 < 5 + 2). consumer thread: false
03-25 21:44:47.283 D/JOBS: adding another consumer
03-25 21:44:47.287 D/JOBS: starting consumer Thread-179
03-25 21:44:47.288 D/JOBS: looking for next job
03-25 21:44:47.288 D/JOBS: running groups
03-25 21:44:47.288 D/JOBS: non persistent result null
03-25 21:44:47.291 D/JOBS: added job id: 91 class: MyJob priority: 7 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.291 I/Myjob: 7 Onadded
03-25 21:44:47.295 D/JOBS: persistent result com.path.android.jobqueue.JobHolder@5b
03-25 21:44:47.295 D/JOBS: running job MyJob
03-25 21:44:47.302 D/JOBS: pool-1-thread-1: load factor check. false = (3 < 1)|| (3 * 3 < 5 + 3). consumer thread: false
03-25 21:44:47.306 D/JOBS: added job id: 92 class: MyJob priority: 8 delay: 0 group : null persistent: true requires network: false
03-25 21:44:47.307 I/Myjob: 8 Onadded
03-25 21:44:47.310 D/JOBS: pool-1-thread-1: load factor check. false = (3 < 1)|| (3 * 3 < 6 + 3). consumer thread: false

日志中這樣的load factor check. true = (0 < 1)|| (0 * 3 < 1 + 0)計(jì)算表達(dá)式草姻,就是在進(jìn)行線(xiàn)程調(diào)度的判定,當(dāng)計(jì)算表達(dá)式為true時(shí)稍刀,就意味著要啟動(dòng)新的消費(fèi)者進(jìn)程撩独。 ||右邊括號(hào)內(nèi)的表達(dá)式就是在比較當(dāng)前任務(wù)數(shù)和當(dāng)前任務(wù)容量敞曹。

通過(guò)日志我們可以看到,面對(duì)任務(wù)0-8综膀,JobManager依次啟動(dòng)了三個(gè)消費(fèi)者進(jìn)程澳迫,并將這9個(gè)任務(wù)分配給他們:

  • Thread-177:任務(wù)0、1剧劝、2橄登;
  • Thread-178:任務(wù)3、4讥此、5拢锹;
  • Thread-179:任務(wù)6、7萄喳、8卒稳;

三個(gè)消費(fèi)者線(xiàn)程并發(fā)執(zhí)行,由于所有任務(wù)的優(yōu)先級(jí)都是一樣的他巨,消費(fèi)者線(xiàn)程們就會(huì)隨機(jī)執(zhí)行任務(wù)充坑。

消費(fèi)者線(xiàn)程被創(chuàng)建,自然也會(huì)被回收染突,這才是完整的生命周期捻爷。以下是當(dāng)任務(wù)執(zhí)行完成時(shí)日志的輸出:

03-25 21:47:02.280 D/JOBS: Thread-177: load factor check. false = (2 < 1)|| (2 * 3 < 0 + 0). consumer thread: true
03-25 21:47:02.280 D/JOBS: finishing consumer Thread-177
03-25 21:47:02.310 D/JOBS: Thread-178: load factor check. false = (1 < 1)|| (1 * 3 < 0 + 0). consumer thread: true
03-25 21:47:02.310 D/JOBS: finishing consumer Thread-178
03-25 21:47:02.337 D/JOBS: Thread-179: load factor check. true = (0 < 1)|| (0 * 3 < 0 + 0). consumer thread: true
03-25 21:47:02.337 D/JOBS: didn't allow me to die, re-running Thread-179
03-25 21:47:02.337 D/JOBS: re-running consumer Thread-179

我們可以看到,當(dāng)任務(wù)執(zhí)行完畢之后Thread-177和Thread-178都被回收了觉痛,但是由于我們?cè)O(shè)置了最小消費(fèi)者線(xiàn)程數(shù)為1役衡,所以Thread-179被留下“堅(jiān)守崗位”,等待下一個(gè)任務(wù)的到來(lái)薪棒,直到超時(shí)。

由此可知榕莺,JobMananger的任務(wù)調(diào)度機(jī)制還是十分復(fù)雜和完備的俐芯,真慶幸已經(jīng)有人幫我們實(shí)現(xiàn)了。

4. RetryConstraint

最后要說(shuō)的就是RetryConstraint钉鸯,即任務(wù)在執(zhí)行中發(fā)生異常之后要執(zhí)行的策略吧史。用戶(hù)可以根據(jù)自己的使用情況來(lái)設(shè)置:

  1. RETRY: RetryConstraint的自帶策略,立刻重新嘗試執(zhí)行策略唠雕,直到執(zhí)行成功或者嘗試次數(shù)達(dá)到最大(18次)贸营;
  2. CANCEL:RetryConstraint的自帶策略,取消當(dāng)前任務(wù)的執(zhí)行岩睁;
  3. *createExponentialBackoff(int runCount, long initialBackOffInMs) *:定期延遲嘗試執(zhí)行任務(wù)钞脂,如果任務(wù)執(zhí)行失敗,下次執(zhí)行的延遲時(shí)間會(huì)以指數(shù)形式增長(zhǎng),最大嘗試次數(shù)為20次捕儒;
public static RetryConstraint createExponentialBackoff(int runCount, long initialBackOffInMs) {
        RetryConstraint constraint = new RetryConstraint(true);
        constraint.setNewDelayInMs(initialBackOffInMs *
                (long) Math.pow(2, Math.max(0, runCount - 1)));
        return constraint;
    }

在官方給出的示例PostTweet中冰啃,是這樣定義* RetryConstraint*的:

    @Override
    protected RetryConstraint shouldReRunOnThrowable(Throwable throwable, int runCount,
            int maxRunCount) {
        if(throwable instanceof TwitterException) {
            //if it is a 4xx error, stop
            TwitterException twitterException = (TwitterException) throwable;
            int errorCode = twitterException.getErrorCode();
            return errorCode < 400 || errorCode > 499 ? RetryConstraint.RETRY : RetryConstraint.CANCEL;
        }
        return RetryConstraint.RETRY;
    }

如果是4XX的錯(cuò)誤(服務(wù)器錯(cuò)誤)就取消訪(fǎng)問(wèn)邓夕,其他異常就離開(kāi)重新嘗試請(qǐng)求。

關(guān)于Android-Priority-JobQueue阎毅,今天先介紹到這里焚刚,內(nèi)容已經(jīng)夠多了∩鹊鳎總之矿咕,這是一個(gè)很優(yōu)秀的任務(wù)隊(duì)列管理庫(kù),很值得使用和研究狼钮。歡迎大家嘗試碳柱,以及給我留言指教。

閱讀參考:

  1. Android Priority Job Queue 入門(mén)
  2. 如何構(gòu)建含有大量參數(shù)的構(gòu)造器:淺談Builder Pattern的使用和鏈?zhǔn)脚渲?/a>
  3. Java序列化心得(一):序列化設(shè)計(jì)和默認(rèn)序列化格式的問(wèn)題
  4. Java序列化心得(二):自定義序列化
  5. Android Priority Job Queue (Job Manager):線(xiàn)程任務(wù)的容錯(cuò)重啟機(jī)制(二)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末燃领,一起剝皮案震驚了整個(gè)濱河市士聪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猛蔽,老刑警劉巖剥悟,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異曼库,居然都是意外死亡区岗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)毁枯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慈缔,“玉大人,你說(shuō)我怎么就攤上這事种玛∶旰祝” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵赂韵,是天一觀的道長(zhǎng)娱节。 經(jīng)常有香客問(wèn)我,道長(zhǎng)祭示,這世上最難降的妖魔是什么肄满? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮质涛,結(jié)果婚禮上稠歉,老公的妹妹穿的比我還像新娘。我一直安慰自己汇陆,他們只是感情好怒炸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瞬测,像睡著了一般横媚。 火紅的嫁衣襯著肌膚如雪纠炮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天灯蝴,我揣著相機(jī)與錄音恢口,去河邊找鬼。 笑死穷躁,一個(gè)胖子當(dāng)著我的面吹牛耕肩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播问潭,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼猿诸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了狡忙?” 一聲冷哼從身側(cè)響起梳虽,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灾茁,沒(méi)想到半個(gè)月后窜觉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡北专,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年禀挫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓颓。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡语婴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驶睦,到底是詐尸還是另有隱情砰左,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布场航,位于F島的核電站菜职,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旗闽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一蜜另、第九天 我趴在偏房一處隱蔽的房頂上張望适室。 院中可真熱鬧,春花似錦举瑰、人聲如沸捣辆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汽畴。三九已至旧巾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忍些,已是汗流浹背鲁猩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罢坝,地道東北人廓握。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嘁酿,于是被迫代替她去往敵國(guó)和親隙券。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,491評(píng)論 0 23
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理闹司,服務(wù)發(fā)現(xiàn)娱仔,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 導(dǎo)語(yǔ): 在情竇初開(kāi)的年少時(shí)代, 我們每人都有一個(gè)關(guān)于情感的幸福夢(mèng)想众弓。 或許是一個(gè)眼神恩溅, 或許是一個(gè)轉(zhuǎn)身, 或許是一...
    摯若初見(jiàn)閱讀 359評(píng)論 0 0
  • 每天給自己定的計(jì)劃總是感覺(jué)沒(méi)有認(rèn)真的對(duì)待谓娃,感覺(jué)都是在敷衍了事脚乡。以后每天早上給自己做計(jì)劃的時(shí)候一定要切合實(shí)際,必須要...
    平凡的進(jìn)步閱讀 142評(píng)論 0 0
  • 細(xì)節(jié)固然重要滨达,但不是所有的細(xì)節(jié)都重要奶稠。要先把握主題再談細(xì)節(jié)〖癖椋基本功能沒(méi)有實(shí)現(xiàn)锌订,去折磨細(xì)節(jié)就是扯淡。
    suxiliu閱讀 218評(píng)論 0 0