在一篇關(guān)于網(wǎng)絡(luò)優(yōu)化的博文中發(fā)現(xiàn)了這個詞啤斗。我們的項目中并沒有使用到這個東西乐横,但看了一下還是非常好用的,mark一下以后一定會用到吧娘锁。
貼官方文檔牙寞,喜歡自學(xué)的可以過去看看
貼官方Demo,官方給出的例子也是非常好的莫秆,里面用到了Messager间雀,以后會對它進行介紹。
這個類有什么用呢? 我們想象一種情景:
當(dāng)且僅當(dāng)設(shè)備在空閑狀態(tài), 并且使用wifi時, 自動下載新的Apk镊屎。
也許我們可能會這么做: 注冊一個廣播接受者 , 監(jiān)聽屏幕熄滅狀態(tài) , 熄滅之后檢查網(wǎng)絡(luò)狀態(tài) , 然后再在廣播接受者中啟動一個服務(wù)去下載新的apk惹挟。可以是可以缝驳,但是這個會不會過于復(fù)雜了连锯。而且如果出現(xiàn)需求變更......
比如希望在特定情況下再啟動事務(wù)归苍,比如說延遲若干時間之后,或者等手機空閑了再運行运怖,這樣一方面不會在系統(tǒng)資源緊張之時喧賓奪主拼弃,另一方面也起到削峰填谷提高系統(tǒng)效率的作用。對于這些額外的條件要求摇展,Service并不能直接支持吻氧,往往需要加入其他手段,才能較好地滿足相關(guān)的運行條件
- 對于延遲時間執(zhí)行咏连,通扯⑺铮考慮利用系統(tǒng)的鬧鐘管理器AlarmManager進行定時管理
- 對于是否聯(lián)網(wǎng)、是否充電祟滴、是否空閑振惰,一般要監(jiān)聽系統(tǒng)的相應(yīng)廣播,常見的系統(tǒng)廣播說明如下:
- 網(wǎng)絡(luò)狀態(tài)變化需要監(jiān)聽系統(tǒng)廣播android.net.conn.CONNECTIVITY_CHANGE踱启;
- 設(shè)備是否充電需要監(jiān)聽系統(tǒng)廣播Intent.ACTION_POWER_CONNECTED也就是android.intent.action.ACTION_POWER_CONNECTED报账;
- 設(shè)備是否空閑需要監(jiān)聽系統(tǒng)廣播Intent.ACTION_SCREEN_OFF也就是android.intent.action.SCREEN_OFF研底;
Android從5.0開始埠偿,增加支持一種特殊的機制,即任務(wù)調(diào)度JobScheduler榜晦,該工具集成了常見的幾種運行條件冠蒋,開發(fā)者只需添加少數(shù)幾行代碼,即可完成原來要多種組件配合的工作乾胶,使代碼變得更加優(yōu)(牛)雅(x)抖剿。
認識一下必須要用的工具
- JobScheduler: 任務(wù)調(diào)度器
- JobInfo : 任務(wù)概要信息
- JobService: 任務(wù)服務(wù),描述具體邏輯
簡單使用JobScheduler
- 創(chuàng)建JobService
我們具體的業(yè)務(wù)邏輯還是要寫在jobService中的, 所以自定義一個服務(wù)繼承自JobService 并重寫兩個抽象方法
onStartJob
:在任務(wù)開始執(zhí)行時觸發(fā)识窿。返回false表示執(zhí)行完畢斩郎,返回true表示需要開發(fā)者自己調(diào)用jobFinished方法通知系統(tǒng)已執(zhí)行完成。
onStopJob
喻频,在任務(wù)停止執(zhí)行時觸發(fā)缩宜。
- Activity中配置JobInfo
JobInfo是從來描述任務(wù)的執(zhí)行時間,條件甥温,策略等一系列的行為锻煌,使用Builder模式來獲取實例,這里摘一下官方給出的代碼
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
String delay = mDelayEditText.getText().toString();
if (!TextUtils.isEmpty(delay)) {
//設(shè)置至少延遲多久后執(zhí)行姻蚓,單位毫秒.
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString();
if (!TextUtils.isEmpty(deadline)) {
//設(shè)置最多延遲多久后執(zhí)行宋梧,單位毫秒。
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
//設(shè)置需要的網(wǎng)絡(luò)條件狰挡,有三個取值:
//JobInfo.NETWORK_TYPE_NONE(無網(wǎng)絡(luò)時執(zhí)行捂龄,默認)释涛、
//JobInfo.NETWORK_TYPE_ANY(有網(wǎng)絡(luò)時執(zhí)行)、
//JobInfo.NETWORK_TYPE_UNMETERED(網(wǎng)絡(luò)無需付費時執(zhí)行)
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
//是否在空閑時執(zhí)行
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
//是否在充電時執(zhí)行
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
// Extras, work duration.
PersistableBundle extras = new PersistableBundle();
String workDuration = mDurationTimeEditText.getText().toString();
if (TextUtils.isEmpty(workDuration)) {
workDuration = "1";
}
extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000);
builder.setExtras(extras);
// Schedule job
Log.d(TAG, "Scheduling job");
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(builder.build());
- setRequiredNetworkType:設(shè)置需要的網(wǎng)絡(luò)條件跺讯,有三個取值:-
JobInfo.NETWORK_TYPE_NONE(無網(wǎng)絡(luò)時執(zhí)行枢贿,默認)、JobInfo.NETWORK_TYPE_ANY(有網(wǎng)絡(luò)時執(zhí)行)刀脏、JobInfo.NETWORK_TYPE_UNMETERED(網(wǎng)絡(luò)無需付費時執(zhí)行) - setPersisted:重啟后是否還要繼續(xù)執(zhí)行局荚,此時需要聲明權(quán)限RECEIVE_BOOT_COMPLETED,否則會報錯“java.lang.IllegalArgumentException: Error: requested job be persisted without holding RECEIVE_BOOT_COMPLETED permission.”而且RECEIVE_BOOT_COMPLETED需要在安裝的時候就要聲明愈污,如果一開始沒聲明耀态,而在升級時才聲明,那么依然會報權(quán)限不足的錯誤暂雹。
- setRequiresCharging:是否在充電時執(zhí)行
- setRequiresDeviceIdle:是否在空閑時執(zhí)行
- setPeriodic:設(shè)置時間間隔首装,單位毫秒。該方法不能和
setMinimumLatency杭跪、setOverrideDeadline這兩個同時調(diào)用仙逻,否則會報錯“java.lang.IllegalArgumentException: Can't call setMinimumLatency() on a periodic job”,或者報錯“java.lang.IllegalArgumentException: Can't call setOverrideDeadline() on a periodic job”涧尿。 - setMinimumLatency:設(shè)置至少延遲多久后執(zhí)行系奉,單位毫秒。
- setOverrideDeadline:設(shè)置最多延遲多久后執(zhí)行姑廉,單位毫秒缺亮。
- setBackoffCriteria: 退避策略 , 可以設(shè)置等待時間以及重連策略
- build:完成條件設(shè)置,返回構(gòu)建好的JobInfo對象桥言。
- 聲明MyJobService并將jobinfo加入, 執(zhí)行
就是上面代碼的最后兩句了, 這里也可以看到JobScheduler 的本質(zhì)其實就是系統(tǒng)的服務(wù)萌踱。
這里吧把官方demo中的JobService拿出來說一下吧,其大致流程就是我們使用JobScheduler的方法号阿。它本質(zhì)上是一個Service
public class MyJobService extends JobService {
private static final String TAG = MyJobService.class.getSimpleName();
private Messenger mActivityMessenger;
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroyed");
}
/**
* When the app's MainActivity is created, it starts this service. This is so that the
* activity and this service can communicate back and forth. See "setUiCallback()"
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);
return START_NOT_STICKY;
}
@Override
public boolean onStartJob(final JobParameters params) {
// The work that this service "does" is simply wait for a certain duration and finish
// the job (on another thread).
sendMessage(MSG_COLOR_START, params.getJobId());
long duration = params.getExtras().getLong(WORK_DURATION_KEY);
// Uses a handler to delay the execution of jobFinished().
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
sendMessage(MSG_COLOR_STOP, params.getJobId());
jobFinished(params, false);
}
}, duration);
Log.i(TAG, "on start job: " + params.getJobId());
// Return true as there's more work to be done with this job.
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
// Stop tracking these job parameters, as we've 'finished' executing.
sendMessage(MSG_COLOR_STOP, params.getJobId());
Log.i(TAG, "on stop job: " + params.getJobId());
// Return false to drop the job.
return false;
}
private void sendMessage(int messageID, @Nullable Object params) {
// If this service is launched by the JobScheduler, there's no callback Messenger. It
// only exists when the MainActivity calls startService() with the callback in the Intent.
if (mActivityMessenger == null) {
Log.d(TAG, "Service is bound, not started. There's no callback to send a message to.");
return;
}
Message m = Message.obtain();
m.what = messageID;
m.obj = params;
try {
mActivityMessenger.send(m);
} catch (RemoteException e) {
Log.e(TAG, "Error passing service object back to activity.");
}
}
}
首先看必須重寫的兩個方法:
onStartJob和onStopJob并鸵,
- 在onStartJob方法中使用Messager發(fā)送了一條消息,Tag為MSG_COLOR_START扔涧, 我們暫時先不管消息發(fā)送到了哪里园担,先宏觀的看一下整個流程。然后發(fā)送了一個延時的消息MSG_COLOR_STOP扰柠,最后調(diào)用jobFinished方法結(jié)束粉铐。
- 在onStopJob方法中,直接發(fā)送了一條MSG_COLOR_STOP的消息
看來這里發(fā)送消息應(yīng)該是重頭戲了卤档。
首先回到Activity中蝙泼,我們在onStart方法中找到了這樣的代碼
Intent startServiceIntent = new Intent(this, MyJobService.class);
Messenger messengerIncoming = new Messenger(mHandler);
startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming);
startService(startServiceIntent);
在Messager創(chuàng)建的時候就將一個handler作為參數(shù)來構(gòu)造,之后作為extra來啟動了服務(wù)劝枣。所以我們就能在服務(wù)中心的onStartCommand方法中獲取到Messager的實例汤踏。接下來在服務(wù)的sendMessage方法中织鲸,也是直接用到了獲取到的messager來發(fā)送消息,那肯定是發(fā)送到MainActivity傳入的Handler中了溪胶,這里面的代碼就不贅述了搂擦,都懂得。
實現(xiàn)過程的源碼
JobScheduler的創(chuàng)建, 是使用的getSystemService , 我們首先點開JobScheduler類 , 尼瑪居然是一個抽象類? 喵喵喵?怎么辦呢? 可以去翻一下getSystemService 方法 , emm你會很驚喜的
既然是獲取系統(tǒng)服, 按照規(guī)范, 其命名方式一定是xxxService , 所以我們直接去源碼包下面搜JobScheduler, 看看會不會有什么發(fā)現(xiàn)
通過這種方式 , 我們可以找到獲取到的真正的服務(wù): JobSchedulerService
但是問題來了, 我們的App是一個進程 , 系統(tǒng)服務(wù)是在系統(tǒng)的進程, 兩者之間怎么進行通訊呢? 對了 , 進程間通訊 , 一定是用到了AIDL
繼續(xù)在JobSchedulerService中找 , 在第762行會發(fā)現(xiàn)
final class JobSchedulerStub extends IJobScheduler.Stub
那么根據(jù)AIDL的規(guī)范 , 一定有 IJobScheduler對應(yīng)的.aidl文件
打開這個文件 IJobScheduler.aidl :
interface IJobScheduler {
int schedule(in JobInfo job);
void cancel(int jobId);
void cancelAll();
List<JobInfo> getAllPendingJobs();
}
定義了aidl之后會自動生成相應(yīng)的接口類
既然這個接口的存根(Stub)被繼承 , 自然會重寫接口中的方法 ,這些重寫方法可以在JobSchedulerService$JobSchedulerStub中找到. 所以 , 我們在使用JobScheduler的一些列方法都應(yīng)該是在這里了吧 , 但是JobScheduler和JobScheduler和JobSchedulerService是怎么關(guān)聯(lián)在一起的呢?
因為JobScheduler是一個抽象類 , 必然有實現(xiàn)類 , 按照規(guī)范 , 明明應(yīng)該為xxxImpl. 我們在之前的搜索中已經(jīng)看到過它了 , 接下來點開看看詳情
public class JobSchedulerImpl extends JobScheduler
恩 , 沒毛病就是它了 , 實現(xiàn)類當(dāng)然會實現(xiàn)抽象方法中的抽象方法了
在看構(gòu)造方法 :
IJobScheduler mBinder;
/* package */ JobSchedulerImpl(IJobScheduler binder) {
mBinder = binder;
}
這里甚至直接命名為binder , 和我們綁定服務(wù)的套路一模一樣:
bindService中 , 重寫onServiceConnected , 將傳入的iBinder對象調(diào)用asInterface方法 ,就能獲得aidl對應(yīng)的接口實現(xiàn) , 也就可以調(diào)用對應(yīng)接口實現(xiàn)的方法.
再回到內(nèi)部類JobSchedulerStub 中: 首先我們來看Scheduler方法
@Override
public int schedule(JobInfo job) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "Scheduling job: " + job.toString());
}
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
enforceValidJobRequest(uid, job);
if (job.isPersisted()) {
if (!canPersistJobs(pid, uid)) {
throw new IllegalArgumentException("Error: requested job be persisted without"
+ " holding RECEIVE_BOOT_COMPLETED permission.");
}
}
long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.schedule(job, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
在針對pid和uid進行一系列的驗證之后, 最終還是調(diào)用了外部類的scheduler方法:
/**
* Entry point from client to schedule the provided job.
* This cancels the job if it's already been scheduled, and replaces it with the one provided.
* @param job JobInfo object containing execution parameters
* @param uId The package identifier of the application this job is for.
* @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
*/
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
大家看下注釋中的解釋 , 大致了解一下流程
這里重點說一下startTrackingJob方法
private void startTrackingJob(JobStatus jobStatus) {
boolean update;
boolean rocking;
synchronized (mJobs) {
update = mJobs.add(jobStatus);
rocking = mReadyToRock;
}
if (rocking) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
if (update) {
controller.maybeStopTrackingJob(jobStatus);
}
controller.maybeStartTrackingJob(jobStatus);
}
}
}
首先將jobStatus添加到JobStore中 , 根據(jù)是否添加成功 , 來決定是否執(zhí)行
controller.maybeStopTrackingJob(jobStatus); 這里也循環(huán)遍歷了控制器 . 乍一看是不是懵逼了 , 什么狗東西?
這里要提一下系統(tǒng)服務(wù)的創(chuàng)建過程了
Zygote--Linux的核心
啟動系統(tǒng)進程SystemServer時 , 就會創(chuàng)建一些關(guān)鍵的服務(wù)哗脖,比如AMS瀑踢,PMS,WMS等才避,其中包括JobSchedulerService橱夭。我們來看一下SystemServer這個類,一共一千多行不是很難桑逝。
首先這是一個java程序棘劣,自然要先去找main函數(shù),然后一步一步跟楞遏。
在run方法中茬暇,我們可以看到一大堆的設(shè)置,往后直接找服務(wù)相關(guān)的(267行)寡喝,三種服務(wù):
startBootstrapServices();
startCoreServices();
startOtherServices();
我們在startOtherServices中可以發(fā)現(xiàn),眾多的服務(wù)都是通過SystemServiceManager去開啟糙俗,它通過類名來反射構(gòu)造器來實例化相應(yīng)的Service,然后添加到系統(tǒng)服務(wù)集合中拘荡,最后啟動服務(wù)臼节。
我們在開發(fā)中使用getSystemService的方式得到的服務(wù)撬陵,就是從剛剛提到了系統(tǒng)服務(wù)集合中獲取的
既然是從構(gòu)造器來實例化服務(wù)的珊皿,所以我們再回到JobSchedulerService看他的構(gòu)造函數(shù),發(fā)現(xiàn)在構(gòu)造函數(shù)中巨税,構(gòu)造器的集合添加了一堆構(gòu)造器蟋定,然后hander分發(fā)事件,讀取本地的文件(data/system/job/jobs.xml)并執(zhí)行任務(wù)草添。
最終在maybeRunPendingJobsH()方法中,調(diào)用了executeRunnableJob方法驶兜,這個方法在JobServiceContext中。該方法中有onServiceConnected方法远寸,即建立鏈接抄淑。同時也有很多很熟悉的方法,就不一一列舉了驰后。
在服務(wù)建立鏈接的同時肆资,還進行防止睡眠等wakeLock操作,emmm灶芝,壞壞的郑原。接下來使用Handler發(fā)送了消息唉韭,what值為MSG_SERVICE_BOUND, 跟進去方法, 最后我們還能看到service.startJob的操作, 即命令服務(wù)執(zhí)行一個任務(wù)。
這樣就將整個流程大致穿起來了犯犁,細節(jié)的地方大家自行查看吧属愤,我覺得基本o98k,看源碼看困了酸役,睡會去- --