WorkManagerの原理解析と互換性テスト
24119 ワード
WorkManagerの原理解析と互換性テスト
前言:
@TargetApi(Build.VERSION_CODES.O)
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
LogHelper.logD("NotificationWorker#doWork, inputData:" + inputData.getKeyValueMap().toString());
String text = inputData.getString("text");
NotificationManager notificationManager = (NotificationManager)getApplicationContext().getSystemService(
Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(getApplicationContext());
Notification notification = builder
.setTicker("WorkMgrNtf")
.setContentTitle("WorkMgrNtf")
.setContentText(text)
.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
.build();
notificationManager.notify(NTF_ID, notification);
return Result.SUCCESS;
}
Data inputData = new Data.Builder()
.putString("text", "PeriodicWorkRequest, ts:" + System.currentTimeMillis())
.build();
Constraints constraint = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
long period = Math.max(5, Integer.parseInt(mEdtPeriod.getText().toString()));
final WorkRequest workRequest = new PeriodicWorkRequest.Builder(NotificationWorker.class, period,
TimeUnit.MINUTES)
.setConstraints(constraint)
.setInputData(inputData)
.build();
mPeriodRequestId = workRequest.getId();
WorkManager.getInstance().enqueue(workRequest);
WorkManager.getInstance().getStatusById(workRequest.getId())
.observeForever(new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
LogHelper.logD(
"OnWorkStatusChanged, requestId:" + workRequest.getId() + ", status:" + workStatus);
}
});
コア機能と主流プロセスソース分析
WorkManager初期化
"androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.example.ali.workmgrdemo.workmanager-init"
android:directBootAware="false" />
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
......
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor());
}
sDelegatedInstance = sDefaultInstance;
}
}
}
WorkRequest実行プロセス
OneTimeWorkRequest実行プロセス
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void setDelegate(WorkManagerImpl delegate) {
synchronized (sLock) {
sDelegatedInstance = delegate;
}
}
PeriodicWorkRequest実行プロセス
内蔵スレッドプール
public class WorkManagerTaskExecutor implements TaskExecutor {
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final Executor mMainThreadExecutor = new Executor() {
@Override
public void execute(@NonNull Runnable command) {
postToMainThread(command);
}
};
// Avoiding synthetic accessor.
volatile Thread mCurrentBackgroundExecutorThread;
private final ThreadFactory mBackgroundThreadFactory = new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable r) {
// Delegate to the default factory, but keep track of the current thread being used.
Thread thread = Executors.defaultThreadFactory().newThread(r);
mCurrentBackgroundExecutorThread = thread;
return thread;
}
};
private final ExecutorService mBackgroundExecutor =
Executors.newSingleThreadExecutor(mBackgroundThreadFactory);
@Override
public void postToMainThread(Runnable r) {
mMainThreadHandler.post(r);
}
@Override
public Executor getMainThreadExecutor() {
return mMainThreadExecutor;
}
@Override
public void executeOnBackgroundThread(Runnable r) {
mBackgroundExecutor.execute(r);
}
@Override
public Executor getBackgroundExecutor() {
return mBackgroundExecutor;
}
@NonNull
@Override
public Thread getBackgroundExecutorThread() {
return mCurrentBackgroundExecutorThread;
}
}
Schedulerタスクスケジューラ
Schedulerリスト
public @NonNull List getSchedulers() {
// Initialized at construction time. So no need to synchronize.
if (mSchedulers == null) {
mSchedulers = Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(mContext, this),
new GreedyScheduler(mContext, this));
}
return mSchedulers;
}
static @NonNull Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
boolean enableFirebaseJobService = false;
boolean enableSystemAlarmService = false;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
try {
scheduler = tryCreateFirebaseJobScheduler(context);
enableFirebaseJobService = true;
Logger.debug(TAG, "Created FirebaseJobScheduler");
} catch (Exception e) {
// Also catches the exception thrown if Play Services was not found on the device.
scheduler = new SystemAlarmScheduler(context);
enableSystemAlarmService = true;
Logger.debug(TAG, "Created SystemAlarmScheduler");
}
}
try {
Class firebaseJobServiceClass = Class.forName(FIREBASE_JOB_SERVICE_CLASSNAME);
setComponentEnabled(context, firebaseJobServiceClass, enableFirebaseJobService);
} catch (ClassNotFoundException e) {
// Do nothing.
}
setComponentEnabled(context, SystemAlarmService.class, enableSystemAlarmService);
return scheduler;
}
* apiLevel>=23, SystemJobScheduler, JobScheculer , ,
* apiLevel<23
* FirebaseJobService
* PlayService, , SystemAlarmScheduler, AlarmManager
サイクルタスクScheduler
if (!workSpec.hasConstraints()) {
Logger.debug(TAG, String.format("Setting up Alarms for %s", workSpecId));
Alarms.setAlarm(mContext, dispatcher.getWorkManager(), workSpecId, triggerAt);
} else {
// Schedule an alarm irrespective of whether all constraints matched.
Logger.debug(TAG,
String.format("Opportunistically setting an alarm for %s", workSpecId));
Alarms.setAlarm(
mContext,
dispatcher.getWorkManager(),
workSpecId,
triggerAt);
JobInfo.Builder builder = new JobInfo.Builder(jobId, mWorkServiceComponent)
.setRequiredNetworkType(jobInfoNetworkType)
.setRequiresCharging(constraints.requiresCharging())
.setRequiresDeviceIdle(constraints.requiresDeviceIdle())
.setExtras(extras);
if (workSpec.isPeriodic()) {
if (Build.VERSION.SDK_INT >= 24) {
builder.setPeriodic(workSpec.intervalDuration, workSpec.flexDuration);
} else {
Logger.debug(TAG,
"Flex duration is currently not supported before API 24. Ignoring.");
builder.setPeriodic(workSpec.intervalDuration);
}
}
private int[] getConstraints(WorkSpec workSpec) {
Constraints constraints = workSpec.constraints;
List mConstraints = new ArrayList<>();
if (Build.VERSION.SDK_INT >= 23 && constraints.requiresDeviceIdle()) {
mConstraints.add(Constraint.DEVICE_IDLE);
}
if (constraints.requiresCharging()) {
mConstraints.add(Constraint.DEVICE_CHARGING);
}
if (constraints.requiresBatteryNotLow()) {
Logger.warning(TAG,
"Battery Not Low is not a supported constraint "
+ "with FirebaseJobDispatcher");
}
if (constraints.requiresStorageNotLow()) {
Logger.warning(TAG, "Storage Not Low is not a supported constraint "
+ "with FirebaseJobDispatcher");
}
switch (constraints.getRequiredNetworkType()) {
case NOT_REQUIRED: {
// Don't add a constraint.
break;
}
case CONNECTED: {
mConstraints.add(Constraint.ON_ANY_NETWORK);
break;
}
case UNMETERED: {
mConstraints.add(Constraint.ON_UNMETERED_NETWORK);
break;
}
case NOT_ROAMING: {
Logger.warning(TAG, "Not Roaming Network is not a supported constraint with "
+ "FirebaseJobDispatcher. Falling back to Any Network constraint.");
mConstraints.add(Constraint.ON_ANY_NETWORK);
break;
}
case METERED: {
Logger.warning(TAG, "Metered Network is not a supported constraint with "
+ "FirebaseJobDispatcher. Falling back to Any Network constraint.");
mConstraints.add(Constraint.ON_ANY_NETWORK);
break;
}
}
return toIntArray(mConstraints);
}
private void setExecutionTrigger(Job.Builder builder, WorkSpec workSpec) {
if (Build.VERSION.SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
builder.setTrigger(createContentUriTriggers(workSpec));
} else if (workSpec.isPeriodic()) {
builder.setTrigger(createPeriodicTrigger(workSpec));
builder.setRecurring(true);
} else {
builder.setTrigger(Trigger.NOW);
}
}
Workerデータ構造
こうそくじょうけんせいぎょ
サポートされる制約
実装の原理
互換性テスト
10-11 11:28:46.031 3030-3129/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
, app , ( 150min, 15min, 10 )
10-11 14:02:59.727 9889-9950/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:04.878 9889-9953/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:09.974 9889-9956/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:15.093 9889-9959/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:20.208 9889-9950/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:25.334 9889-9953/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:30.406 9889-9956/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:35.489 9889-9959/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:40.665 9889-9950/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
10-11 14:03:43.777 9889-9953/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539228520797}
* B
2018-10-10 22:08:47.614 15986-16054/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}
2018-10-10 22:26:36.969 15986-16287/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}
2018-10-10 22:56:17.391 16769-16797/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}
2018-10-10 23:10:18.087 17082-17104/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}
2018-10-10 23:23:42.589 17082-17290/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539180522495}
* C
2018-10-13 07:37:02.576 16995-17018/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539385621858}
2018-10-13 07:52:02.524 18022-18053/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539385621858}
2018-10-13 08:07:02.582 18554-18584/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539385621858}
2018-10-13 08:22:01.989 18554-19170/com.example.ali.workmgrdemo D/TAG_WORK_MGR: NotificationWorker#doWork, inputData:{text=PeriodicWorkRequest, ts:1539385621858}
推奨される使用シーンおよび注意事項
転載先:https://juejin.im/post/5cd6779851882569217b9b72