日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Jetpack】WorkManager

發布時間:2024/3/13 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Jetpack】WorkManager 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【JetPack】WorkManager

轉載自享學課堂-derry,非常感謝derry的講解

WorkManager有什么用:

一:確保重要的后臺任務,一定會被執行,后臺任務(例如:非及時性的 (請求服務器 及時性) 上傳,下載,同步數據 等)

二:內部對電量進行了優化,不需要我們去處理電量優化了

三:API 14 到 最新版本,都可以使用WorkManager來管你你的后臺任務

四:注意:WorkManager不能做保活操作

五:調度,管理,執行的后臺任務的場景,通常是是可延遲的后臺任務

WorkManager概述

你的任務不可能總是在前臺,但是還要確保你的那些重要任務執行,你們就可以放置 在后臺執行,那么WorkManager就能夠展現萬丈光芒了

WorkManger是Android Jetpack提供執行后臺任務管理的組件,它適用于需要保證 系統即使應用程序退出也會運行的任務,WorkManager API可以輕松指定可延遲的 異步任務以及何時運行它們,這些API允許您創建任務并將其交給WorkManager立 即運行或在適當的時間運行。

WorkManager根據設備API級別和應用程序狀態等因素選擇適當的方式來運行任務。如果WorkManager在應用程序運行時執行您的任務之一,WorkManager可以 在您應用程序進程的新線程中運行您的任務。如果您的應用程序未運行, WorkManager會選擇一種合適的方式來安排后臺任務 - 具體取決于設備API級別和包含的依賴項,WorkManager可能會使用 JobScheduler,Firebase JobDispatcher或 AlarmManager

?

WorkManager的各個角色

Worker:可以這樣理解,指定需要執行的任務,可以成為Workder類的子類,在實現的方法中,就可以執行任務邏輯了

WorkRequest:可以這樣理解,執行一項單一的任務

第一點:必須知道 WorkRequest對象必須指定Work執行的任務

第二點:需要知道 WorkRequest都有一個自動生成的唯一ID,可以使用ID執行取消 排隊任務 或 獲取任務狀態等操作

第三點:需要知道 WorkRequest是一個抽象的類;系統默認實現子類 OneTimeWorkRequest或PeriodicWorkRequest

第四點:需要知道 WorkRequest.Builder創建WorkRequest對象;相應的子類:OneTimeWorkRequest.Builder或PeriodicWorkRequest.Builder

第五點:需要知道 Constraints:指定對任務運行時間的限制(任務約束);使用 Constraints.Builder創建Constraints對象 ,并傳遞給WorkRequest.Builder

?

WorkManager的簡述:

WorkManager:排隊和管理工作請求;將WorkRequest 對象傳遞WorkManager的 任務隊列(注意:如果未指定任何約束, WorkManager立即運行任務)

WorkStatus的簡述:

WorkStatus:包含有關特定任務的信息;可以使用LiveData保存 WorkStatus對象,監聽任務狀態;如LiveData

分享使用文章

WorkManager的基本使用

官方文檔

WorkManage源碼分析中

?

為什么workmanager可以在app殺死之后還可以繼續工作,那是因為,這是由android系統級別的服務開啟的,數據存儲在room數據庫里,由系統輪詢,反射執行代碼塊,權限很大

?

1.初始化工作:

WorkManager.getInstance(this) // 各種初始化的工作

?

2.加入隊列,觸發后臺任務工作:

.enqueue(oneTimeWorkRequest2); // 加入隊列,觸發后臺任務工作

一、進入getInstance 初始化

WorkManagerImpl @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {synchronized (sLock) {WorkManagerImpl instance = getInstance();if (instance == null) {Context appContext = context.getApplicationContext();if (appContext instanceof Configuration.Provider) {initialize(appContext,((Configuration.Provider) appContext).getWorkManagerConfiguration());instance = getInstance(appContext);} else {throw new IllegalStateException("WorkManager is not initialized properly. You "+ "have explicitly disabled WorkManagerInitializer in your manifest, "+ "have not manually called WorkManager#initialize at this point, and "+ "your Application does not implement Configuration.Provider.");}}return instance;}}

第一個if不會進來,為什么呢,因為這是第二次執行,APK清單文件里面(第一次)執行 ,此時我們打開apk查看清單文件發現

<providerandroid:name="androidx.work.impl.WorkManagerInitializer"android:exported="false"android:multiprocess="true"android:authorities="com.derry.workmanagersimple.workmanager-init"android:directBootAware="false" />

在這里實例化,同樣走到了上面初始化的方法,初始化的時候,最終返回workmanagerImpl的實現類

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public class WorkManagerInitializer extends ContentProvider {@Overridepublic boolean onCreate() {// Initialize WorkManager with the default configuration.WorkManager.initialize(getContext(), new Configuration.Builder().build());return true;} }

現在返回到初始化的代碼的第二個if里面,new WorkManagerImpl

最終會走到一個方法,這里初始化了room的數據庫,作為持久性保存的地方,以及其他配置信息

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)public WorkManagerImpl(@NonNull Context context,@NonNull Configuration configuration,@NonNull TaskExecutor workTaskExecutor,@NonNull WorkDatabase database) {Context applicationContext = context.getApplicationContext();Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);Processor processor = new Processor(context,configuration,workTaskExecutor,database,schedulers);internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);}private void internalInit(@NonNull Context context,@NonNull Configuration configuration,@NonNull TaskExecutor workTaskExecutor,@NonNull WorkDatabase workDatabase,@NonNull List<Scheduler> schedulers,@NonNull Processor processor) {context = context.getApplicationContext();mContext = context;mConfiguration = configuration;mWorkTaskExecutor = workTaskExecutor;mWorkDatabase = workDatabase;mSchedulers = schedulers;mProcessor = processor;mPreferenceUtils = new PreferenceUtils(workDatabase);mForceStopRunnableCompleted = false;// Checks for app force stops.mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));}

其中有一個貪婪執行器,埋下伏筆

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)@NonNullpublic List<Scheduler> createSchedulers(@NonNull Context context,@NonNull TaskExecutor taskExecutor) {return Arrays.asList(Schedulers.createBestAvailableBackgroundScheduler(context, this),// Specify the task executor directly here as this happens before internalInit.// GreedyScheduler creates ConstraintTrackers and controllers eagerly.new GreedyScheduler(context, taskExecutor, this));}

返回來,在代碼里getInstance的時候,是第二次初始化,就只是獲取了對應的workmanagerImpl類,單例的

初始化環節結束

二、執行流程

首先上圖,整體的類uml關系圖

?

首先進入enque

workmanagerImpl

@Override@NonNullpublic Operation enqueue(@NonNull List<? extends WorkRequest> workRequests) {// This error is not being propagated as part of the Operation, as we want the// app to crash during development. Having no workRequests is always a developer error.if (workRequests.isEmpty()) {throw new IllegalArgumentException("enqueue needs at least one WorkRequest.");}return new WorkContinuationImpl(this, workRequests).enqueue();}

WorkContinuationImpl

@Overridepublic @NonNull Operation enqueue() {// Only enqueue if not already enqueued.if (!mEnqueued) {// The runnable walks the hierarchy of the continuations// and marks them enqueued using the markEnqueued() method, parent first. //這里開啟了線程,使用線程池EnqueueRunnable runnable = new EnqueueRunnable(this);mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);mOperation = runnable.getOperation();} else {Logger.get().warning(TAG,String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));}return mOperation;}

EnqueueRunnable 來看一下做了什么

@Overridepublic void run() {try {if (mWorkContinuation.hasCycles()) {throw new IllegalStateException(String.format("WorkContinuation has cycles (%s)", mWorkContinuation));} //建立數據庫的初始化boolean needsScheduling = addToDatabase();if (needsScheduling) {// Enable RescheduleReceiver, only when there are Worker's that need scheduling.final Context context =mWorkContinuation.getWorkManagerImpl().getApplicationContext();PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);scheduleWorkInBackground();}mOperation.setState(Operation.SUCCESS);} catch (Throwable exception) {mOperation.setState(new Operation.State.FAILURE(exception));}}@VisibleForTestingpublic void scheduleWorkInBackground() {WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();Schedulers.schedule( //獲取配置的信息,開啟執行調度workManager.getConfiguration(),workManager.getWorkDatabase(),workManager.getSchedulers());}

來看一下schedule,保存了所有的manager,使用了貪婪執行器GreedyScheduler,添加

public static void schedule(@NonNull Configuration configuration,@NonNull WorkDatabase workDatabase,List<Scheduler> schedulers) {if (schedulers == null || schedulers.size() == 0) {return;} //遍歷。將任務同步到數據庫WorkSpecDao workSpecDao = workDatabase.workSpecDao();List<WorkSpec> eligibleWorkSpecs;//開啟事務。可以回滾workDatabase.beginTransaction();try {eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(configuration.getMaxSchedulerLimit());if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {long now = System.currentTimeMillis();// Mark all the WorkSpecs as scheduled.// Calls to Scheduler#schedule() could potentially result in more schedules// on a separate thread. Therefore, this needs to be done first.for (WorkSpec workSpec : eligibleWorkSpecs) {workSpecDao.markWorkSpecScheduled(workSpec.id, now);}}workDatabase.setTransactionSuccessful();} finally {workDatabase.endTransaction();} //if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);// Delegate to the underlying scheduler.for (Scheduler scheduler : schedulers) { //這里對應之前的貪婪執行器GreedySchedulerscheduler.schedule(eligibleWorkSpecsArray);}}}

GreedyScheduler

@Overridepublic void schedule(@NonNull WorkSpec... workSpecs) {...registerExecutionListenerIfNeeded();List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();List<String> constrainedWorkSpecIds = new ArrayList<>();for (WorkSpec workSpec : workSpecs) {if (workSpec.state == WorkInfo.State.ENQUEUED&& !workSpec.isPeriodic()&& workSpec.initialDelay == 0L&& !workSpec.isBackedOff()) {if (workSpec.hasConstraints()) {//有約束的執行流程if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {// Ignore requests that have an idle mode constraint.Logger.get().debug(TAG,String.format("Ignoring WorkSpec %s, Requires device idle.",workSpec));} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {// Ignore requests that have content uri triggers.Logger.get().debug(TAG,String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",workSpec));} else {constrainedWorkSpecs.add(workSpec);constrainedWorkSpecIds.add(workSpec.id);}} else { //無約束條件的執行流程Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));mWorkManagerImpl.startWork(workSpec.id);}}}synchronized (mLock) {if (!constrainedWorkSpecs.isEmpty()) {Logger.get().debug(TAG, String.format("Starting tracking for [%s]",TextUtils.join(",", constrainedWorkSpecIds)));mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);}}}

startwork在impl里

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)public void startWork(@NonNull String workSpecId,@Nullable WorkerParameters.RuntimeExtras runtimeExtras) { //拿到線程池,將任務丟給線程池執行mWorkTaskExecutor.executeOnBackgroundThread(new StartWorkRunnable(this, workSpecId, runtimeExtras));}

具體看一下這個線程

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public class StartWorkRunnable implements Runnable {private WorkManagerImpl mWorkManagerImpl;private String mWorkSpecId;private WorkerParameters.RuntimeExtras mRuntimeExtras;public StartWorkRunnable(WorkManagerImpl workManagerImpl,String workSpecId,WorkerParameters.RuntimeExtras runtimeExtras) {mWorkManagerImpl = workManagerImpl;mWorkSpecId = workSpecId;mRuntimeExtras = runtimeExtras;}@Overridepublic void run() {mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);} }

Processor

public boolean startWork(@NonNull String id,@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {WorkerWrapper workWrapper;... //將任務包裹起來,交給線程池workWrapper =new WorkerWrapper.Builder(mAppContext,mConfiguration,mWorkTaskExecutor,this,mWorkDatabase,id).withSchedulers(mSchedulers).withRuntimeExtras(runtimeExtras).build();ListenableFuture<Boolean> future = workWrapper.getFuture();future.addListener(new FutureListener(this, id, future),mWorkTaskExecutor.getMainThreadExecutor());mEnqueuedWorkMap.put(id, workWrapper);}mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));return true;}

WorkerWrapper

@WorkerThread@Overridepublic void run() {mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);mWorkDescription = createWorkDescription(mTags);runWorker();}

?

private void runWorker() {...// Call mWorker.startWork() on the main thread.mWorkTaskExecutor.getMainThreadExecutor().execute(new Runnable() {@Overridepublic void run() {try {Logger.get().debug(TAG, String.format("Starting work for %s",mWorkSpec.workerClassName));mInnerFuture = mWorker.startWork();future.setFuture(mInnerFuture);} catch (Throwable e) {future.setException(e);}}});...}

Worker

doWork就是我們實現的work類的實現方法,因為步驟都是異步操作,所以可以執行睡眠等操作

@Overridepublic final @NonNull ListenableFuture<Result> startWork() {mFuture = SettableFuture.create();getBackgroundExecutor().execute(new Runnable() {@Overridepublic void run() {try {Result result = doWork(); //doWorkmFuture.set(result);} catch (Throwable throwable) {mFuture.setException(throwable);}}});return mFuture;}@WorkerThreadpublic abstract @NonNull Result doWork();

?

至此,主線流程打通

三、一些疑問

1、為什么飛行模式到網絡連接打開就能收到work的啟動

應用殺掉以后,系統服務還在。會有‘輪詢機’會讀取room數據庫,apk的清單文件,已經把所有的信息保存進去了

當網絡打開的時候,接收信息的是誰呢,

ConstraintProxy

abstract class ConstraintProxy extends BroadcastReceiver {private static final String TAG = Logger.tagWithPrefix("ConstraintProxy");@Overridepublic void onReceive(Context context, Intent intent) {Logger.get().debug(TAG, String.format("onReceive : %s", intent));Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);context.startService(constraintChangedIntent);} }

?

CommandHandler

static Intent createConstraintsChangedIntent(@NonNull Context context) {Intent intent = new Intent(context, SystemAlarmService.class);intent.setAction(ACTION_CONSTRAINTS_CHANGED);return intent;}

?

SystemAlarmService系統鬧鐘分發器

intent == createConstraintsChangedIntent返回 ? ? ? ? ?

startId == ACTION_CONSTRAINTS_CHANGED
mDispatcher.add(intent, startId);

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {super.onStartCommand(intent, flags, startId);if (mIsShutdown) {Logger.get().info(TAG,"Re-initializing SystemAlarmDispatcher after a request to shut-down.");// Destroy the old dispatcher to complete it's lifecycle.mDispatcher.onDestroy();// Create a new dispatcher to setup a new lifecycle.initializeDispatcher();// Set mIsShutdown to false, to correctly accept new commands.mIsShutdown = false;}if (intent != null) {mDispatcher.add(intent, startId);}// If the service were to crash, we want all unacknowledged Intents to get redelivered.return Service.START_REDELIVER_INTENT;}

SystemAlarmDispatcher

processCommand(); 執行此命令?

@MainThreadpublic boolean add(@NonNull final Intent intent, final int startId) {Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));assertMainThread();String action = intent.getAction();if (TextUtils.isEmpty(action)) {Logger.get().warning(TAG, "Unknown command. Ignoring");return false;}// If we have a constraints changed intent in the queue don't add a second one. We are// treating this intent as special because every time a worker with constraints is complete// it kicks off an update for constraint proxies.if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {return false;}intent.putExtra(KEY_START_ID, startId);synchronized (mIntents) {boolean hasCommands = !mIntents.isEmpty();mIntents.add(intent);if (!hasCommands) {// Only call processCommand if this is the first command.// The call to dequeueAndCheckForCompletion will process the remaining commands// in the order that they were added.processCommand();}}return true;}

?

@MainThread@SuppressWarnings("FutureReturnValueIgnored")private void processCommand() {assertMainThread();... //電池優化final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(mContext,String.format("%s (%s)", action, startId));try {Logger.get().debug(TAG, String.format("Acquiring operation wake lock (%s) %s",action,wakeLock));wakeLock.acquire(); //注意這里mCommandHandler.onHandleIntent(mCurrentIntent, startId,.....} finally {processCommandLock.release();}}

注意這里走到之前定義的字段ACTION_CONSTRAINTS_CHANGED

目的是更換標記將ACTION_CONSTRAINTS_CHANGED 換標記 ACTION_DELAY_MET

void onHandleIntent(@NonNull Intent intent,int startId,@NonNull SystemAlarmDispatcher dispatcher) {String action = intent.getAction();if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {handleConstraintsChanged(intent, startId, dispatcher);} else if (ACTION_RESCHEDULE.equals(action)) {handleReschedule(intent, startId, dispatcher);} else {Bundle extras = intent.getExtras();if (!hasKeys(extras, KEY_WORKSPEC_ID)) {Logger.get().error(TAG,String.format("Invalid request for %s, requires %s.",action,KEY_WORKSPEC_ID));} else {if (ACTION_SCHEDULE_WORK.equals(action)) {handleScheduleWorkIntent(intent, startId, dispatcher);} else if (ACTION_DELAY_MET.equals(action)) {handleDelayMet(intent, startId, dispatcher);} else if (ACTION_STOP_WORK.equals(action)) {handleStopWork(intent, dispatcher);} else if (ACTION_EXECUTION_COMPLETED.equals(action)) {handleExecutionCompleted(intent, startId);} else {Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));}}}}

接下來就會執行,目的是為了做執行操作,會重新添加add,所以上面的if會執行好幾次

else if (ACTION_DELAY_MET.equals(action)) {handleDelayMet(intent, startId, dispatcher);}

這個執行操作一點一點往里點,最終會點到startwork,調用到我們自己的work實現類里,完成喚醒操作

public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {......synchronized (mLock) {if (mCurrentState == STATE_INITIAL) {mCurrentState = STATE_START_REQUESTED;Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));// Constraints met, schedule execution// Not using WorkManagerImpl#startWork() here because we need to know if the// processor actually enqueued the work here. //銜接到了之前的starrkworkboolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);if (isEnqueued) {// setup timers to enforce quotas on workers that have// been enqueuedmDispatcher.getWorkTimer().startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this); ......}}

?


WorkManager是干嘛用的?
答:處理非及時任務,舉例子:每天同步一次數據到服務器,這種類似的需求,不是及時執行,但是又保證會執行的非及時任務。

WorkManager是怎么保證,當我把APP殺掉后呢?
答:記錄用戶的所有信息并全部保存到數據庫,而并非保存在內存中,這樣做的好處,就是持久性保存記錄,所有APP被殺掉后 依然可以獲取所有任務信息。

你研究過WorKManager源碼,任務是怎么保證一定執行的呀?
答:Android操作系統會在系統級別服務中,來判斷用戶的約束條件,當約束條件滿足時就會執行任務,但是觸發檢測是采用廣播的形式處理的,例如:網絡連接成功 就觸發...。

?

?

?

?

?

?

?

?

總結

以上是生活随笔為你收集整理的【Jetpack】WorkManager的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。