1、前言

WorkManager是合适用于持久性作业的引荐处理方案。假如作业一直要经过运用重启和体系从头发动来调度,便是持久性的作业。因为大多数后台处理操作都是经过持久性作业完结的,因此 WorkManager 是适用于后台处理操作的首要引荐 API。 WorkManager 可处理三种类型的持久性作业:

  • 当即履行:有必要当即开端且很快就完结的使命,能够加急。
  • 长期运转:运转时刻或许较长(有或许超越 10 分钟)的使命。
  • 可延期履行:延期开端而且能够定时运转的预定使命。

2、运用

2.1、引证

implementation "androidx.work:work-runtime:2.7.1" //基础运用
implementation "androidx.work:work-multiprocess:2.7.1" //跨进程时引证

2.2 运用

履行一次性使命

Data data = new Data.Builder().putBoolean("is_test", false).build();
WorkManager.getInstance(this).enqueue(
        new OneTimeWorkRequest.Builder(Test.class) // 履行使命一次性使命
                .setInputMerger(NewInputMerge.class) // 输入数据兼并战略,这儿并没有用,链式处理时,多个上流履行成果兼并,作为下流输入数据
                .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) // 重试战略
                .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // 加急处理
                .addTag("test").addTag("huahua") // 标识
                .setInputData(data) // 输入数据
                .setInitialDelay(5, TimeUnit.SECONDS) // 履行延时时刻
                .setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()) // 束缚,部分束缚只对高版别有用
                .build()
);

履行周期性使命

WorkManager.getInstance(this).enqueue(
        new PeriodicWorkRequest.Builder(Test.class, 2, TimeUnit.HOURS) // 履行周期性使命,周期2小时
                .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES)
                .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
                .addTag("test").addTag("huahua")
                .setInputData(data)
                .setInitialDelay(5, TimeUnit.SECONDS) // 首次履行延时时刻
                .setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build())
                .build()
);

拥有姓名的使命

WorkManager.getInstance(this).enqueueUniqueWork("test", ExistingWorkPolicy.KEEP,
        new OneTimeWorkRequest.Builder(Test.class).build()); // 此种办法会对重复姓名的使命进行处理

监听状况

WorkManager.getInstance(this).getWorkInfosByTagLiveData("test").observe(this,
        new Observer<List<WorkInfo>>() {
            @Override
            public void onChanged(List<WorkInfo> workInfos) {
            }
        });

定义Work代码

public class Test extends Worker {
    public Test(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
        Data input = getInputData(); // 获取输入数据
        boolean isTest = input.getBoolean("is_test", true);
    }
    @NonNull
    @Override
    public Result doWork() {
        return Result.success();
    }
}

承继使Worker类,完结doWork()办法,此办法是完结使命的主体;doWork()回来的Result会通知 WorkManager 服务作业是否成功,以及作业失利时是否应重试作业。

  • Result.success():作业成功完结。
  • Result.failure():作业失利。
  • Result.retry():作业失利,应根据其重试方针在其他时刻测验。

装备初始化 不同的版别初始化不同,可是都是经过Provider来进行的,2.6之前是WorkManagerInitializer, 2.6之后是InitializationProvider;这儿依照2.7.1的版别来说,老版别有需求留言回复;有两种处理方案

  1. 移除默许Provider
  2. 依照Provider流程来进行

依照供给初始化流程处理

  1. 首先注意一个字符串装备:这个很重要,不建议修改
<string name="androidx_startup" translatable="false">androidx.startup</string>
  1. 供给完结Initializer的类,这个类是被调用的关键
  2. 移除默许完结
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" /> // 这表示移除
</provider>

默许完结Initializer类WorkManagerInitializer,运用的默许configuration

public final class WorkManagerInitializer implements Initializer<WorkManager> {
    private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
    @NonNull
    @Override
    public WorkManager create(@NonNull Context context) {
        Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
        WorkManager.initialize(context, new Configuration.Builder().build()); // 这儿供给Configuration
        return WorkManager.getInstance(context);
    }
    @NonNull
    @Override
    public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
        return Collections.emptyList(); // 供给需求履行的其它Initializer
    }
}
  1. 进行注册自定义完结; 在meta-data数据处理,key为你需求调用的初始化类,value有必要为R.sting.androidx_startup这个字符串的值; 如下
    <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge" >
            <meta-data
                android:name="androidx.work.WorkManagerInitializer" // 这儿为你的完结的Initializer
                android:value="androidx.startup" /> // 这儿有必要与R.sting.androidx_startup保持一致
        </provider>
    

2.3 重要概念

使命标识

  • id : 经过WorkRequest目标getStringId获取,每次增加一次使命,就会得到仅有的id
  • name :使命姓名,暂时一次性使命才能够有; 一个使命最多一个姓名,而一个姓名能够对应多个使命; 同姓名使命能够经过定义不同姓名冲突来处理
  • tag: 一个使命能够拥有多个tag,一个tag能够对应多个使命

使命类型

  • 一次性使命: 仅仅履行一次, 假如成果回来retry时,依照重试退避方针,进行重试,直至成功
  • 周期性使命: 依照周期履行,不管成果回来什么状况,每次周期内仅仅履行一次

使命链

运用 WorkManager 创立作业链并将其参加行列。作业链用于指定多个依存使命并定义这些使命的运转顺序。运用WorkManager.beginWith(OneTimeWorkRequest或WorkManager.beginWith(List)回来WorkContinuation实例,然后经过其then(OneTimeWorkRequest)或then(List)增加使命链,最后运用WorkContinuation.enqueue()进行履行。

使命链先增加的使命为后续使命的先决条件, 也便是前面使命成功了后边使命才会履行

重试退避方针

针对worker履行回来Result.retry()时处理战略, 运用办法setBackoffCriteria设置, 有两个目标,战略和延时时刻(允许的最小值,即 10 秒);状况有下面2种

  • 线性LINEAR: 每次失利后从头开启使命时刻延时为 延时时刻次数倍(延时时刻 * n)
  • EXPONENTIAL: 每次失利后从头开启使命时刻延时为 延时时刻的指数倍(延时时刻 * 2^n)

输入兼并器

针对一次性使命,且在作业链中,父级作业请求的输出将作为子级的输入传入的场景中运用。输入中会存在相同关键字,这时,输入就会存在冲突

WorkManager 供给两种不同类型的InputMerger:

  • OverwritingInputMerger:会测验将一切输入中的一切键增加到输出中。假如发生冲突,它会覆盖先前设置的键。
  • ArrayCreatingInputMerger: 会测验兼并输入,并在必要时创立数组。

冲突处理方针

调度仅有作业时,发生冲突时要履行的操作。能够经过在将作业参加行列时传递一个枚举来完结此意图。

关于一次性作业,您需求供给一个ExistingWorkPolicy,有4 个选项。

  • REPLACE:用新作业替换现有作业。此选项将撤销现有作业。
  • KEEP:保留现有作业,并忽略新作业。
  • APPEND:将新作业附加到现有作业的末尾。此方针将导致您的新作业到现有作业,在现有作业完结后运转。 现有作业将成为新作业的先决条件。假如现有作业变为CANCELLEDFAILED状况,新作业也会变为CANCELLEDFAILED。假如您期望不管现有作业的状况怎么都运转新作业,请改用APPEND_OR_REPLACE
  • APPEND_OR_REPLACE: 类似于APPEND,不过它并不依靠于先决条件作业状况。**即便现有作业变为CANCELLEDFAILED状况,新作业仍会运转。

束缚条件

使命履行的先决条件,运用Contraints.Builder()进行构建实例。有一下束缚

  • NetworkType : 束缚运转作业所需的网络类型。
  • BatteryNotLow : 假如设置为 true,那么当设备处于“电量不足形式”时,作业不会运转。
  • RequiresCharging : 假如设置为 true,那么作业只能在设备充电时运转。
  • DeviceIdle:假如设置为 true,则要求用户的设备有必要处于空闲状况,才能运转作业。在运转批量操作时,此束缚会十分有用;若是不必此束缚,批量操作或许会降低用户设备上正在活跃运转的其他运用的功能。
  • StorageNotLow: 假如设置为 true,那么当用户设备上的存储空间不足时,作业不会运转。

还有以下束缚,关于>=24的版别有用:

  • setContentUriTriggers办法: 设置触发使命的uri
  • setTriggerContentUpdateDelay办法:设置触发履行的延迟时刻
  • setTriggerMaxContentDelay办法:设置处分履行的最大延时

3、原理

合理分为几个部分来说

1. 束缚检测: 束缚检测的逻辑以及完结
2. 使命调度: 3种调度器, alarm、greedy、JobScheduler; 用于引发使命履行
3. 使命履行流程 :请求的包装,使命怎么参加调度器,以及调度完结后履行使命概况

这儿有需求了解的一个技能点:SettableFuture,完结了ListenableFuture接口,而且增加了下面接口ListenableFuture。这个类在源码中频频运用

ListenableFuture只要一个办法

void addListener(Runnable listener, Executor executor)

调用了这个办法,这个类才有运用含义;调用之后,表示SettableFuture若完结,则运用executor履行listener

另外一个让我觉得有意思的当地,或许说不同之前future的当地是, SettableFuture未完结Runnable接口,也便是其成果不是来源于自己,来源于调用下面3个办法

    public boolean set(@Nullable V value) // 正常设置成果
    public boolean setException(Throwable throwable) // 履行成果反常
    public boolean setFuture(ListenableFuture<? extends V> future) // 设置履行成果来源,也便是ListenableFuture的履行成果

3.1 束缚检测

类图如下

WorkManager使用以及源码理解
结合源码剖析,定论有:

  • WorkConstraintsTracker类:运用进口,结构器传入WorkConstraintsCallback为成果回调,也能够直接调用办法areAllConstraintsMet来判别是否满意束缚
  • ConstraintController类:一种束缚操控器类,经过OnConstraintUpdatedCallback回调实时成果给WorkConstraintsTracker, 经过ConstraintListener与ConstraintTracker联络
  • ConstraintTracker:实际束缚值的检测者,ConstraintListener回调实时传递值与ConstraintController;经过setState触发回调

实际检测技能点,也便是ConstraintTracker完结

3.1.1 StorageNotLowTracker类

存储空间的是否低,根据体系播送来处理

Intent.ACTION_DEVICE_STORAGE_OK // 存储空间够用
Intent.ACTION_DEVICE_STORAGE_LOW // 存储空间很低

开始检测状况:

@Override
public Boolean getInitialState() {
    Intent intent = mAppContext.registerReceiver(null, getIntentFilter());
    if (intent == null || intent.getAction() == null) {
        return true;
    } else {
        switch (intent.getAction()) {
            case Intent.ACTION_DEVICE_STORAGE_OK:
                return true;
            case Intent.ACTION_DEVICE_STORAGE_LOW:
                return false;
            default:
                return null;
        }
    }
}

实时更新经过接纳播送信息:

public void onBroadcastReceive(Context context, @NonNull Intent intent) {
    if (intent.getAction() == null) {
        return;
    }
    switch (intent.getAction()) {
        case Intent.ACTION_DEVICE_STORAGE_OK:
            setState(true);
            break;
        case Intent.ACTION_DEVICE_STORAGE_LOW:
            setState(false);
            break;
    }
}

3.1.2 BatteryChargingTracker类

充电状况,同样经过播送来进行处理;

接纳播送根据版别不同而不同如下:

    public IntentFilter getIntentFilter() {
        IntentFilter intentFilter = new IntentFilter();
        if (Build.VERSION.SDK_INT >= 23) {
            intentFilter.addAction(BatteryManager.ACTION_CHARGING);
            intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
        } else {
            intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
            intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        }
        return intentFilter;
    }

初始状况:

    @Override
    public Boolean getInitialState() {
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent intent = mAppContext.registerReceiver(null, intentFilter);
        if (intent == null) {
            Logger.get().error(TAG, "getInitialState - null intent received");
            return null;
        }
        return isBatteryChangedIntentCharging(intent);
    }
    private boolean isBatteryChangedIntentCharging(Intent intent) {
        boolean charging;
        if (Build.VERSION.SDK_INT >= 23) {
            int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
            charging = (status == BatteryManager.BATTERY_STATUS_CHARGING
                    || status == BatteryManager.BATTERY_STATUS_FULL);
        } else {
            int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
            charging = (chargePlug != 0);
        }
        return charging;
    }

实时状况:

@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
    String action = intent.getAction();
    if (action == null) {
        return;
    }
    Logger.get().debug(TAG, String.format("Received %s", action));
    switch (action) {
        case BatteryManager.ACTION_CHARGING:
            setState(true);
            break;
        case BatteryManager.ACTION_DISCHARGING:
            setState(false);
            break;
        case Intent.ACTION_POWER_CONNECTED:
            setState(true);
            break;
        case Intent.ACTION_POWER_DISCONNECTED:
            setState(false);
            break;
    }
}

3.1.3 BatteryNotLowTracker类

电量是否低,同样经过播送实时获取状况
    Intent.ACTION_BATTERY_OKAY //电量正常
    Intent.ACTION_BATTERY_LOW //电量低

初始状况:

    @Override
    public Boolean getInitialState() {
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent intent = mAppContext.registerReceiver(null, intentFilter);
        if (intent == null) {
            Logger.get().error(TAG, "getInitialState - null intent received");
            return null;
        }
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        float batteryPercentage = level / (float) scale;
        return (status == BatteryManager.BATTERY_STATUS_UNKNOWN
                || batteryPercentage > BATTERY_LOW_THRESHOLD); // 这儿是小于15%算是低
    }

实时更新:

    @Override
    public void onBroadcastReceive(Context context, @NonNull Intent intent) {
        if (intent.getAction() == null) {
            return;
        }
        Logger.get().debug(TAG, String.format("Received %s", intent.getAction()));
        switch (intent.getAction()) {
            case Intent.ACTION_BATTERY_OKAY:
                setState(true);
                break;
            case Intent.ACTION_BATTERY_LOW:
                setState(false);
                break;
        }
    }

3.1.4 NetworkStateTracker类

网络状况,这个根据版别不同运用也不同

  • 大于等于24时, ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback networkCallback) 来进行监听网络状况改变

  • 小于24时,经过播送ConnectivityManager.CONNECTIVITY_ACTION来进行处理

初始状况:

NetworkState getActiveNetworkState() {
    NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
    boolean isConnected = info != null && info.isConnected();
    boolean isValidated = isActiveNetworkValidated();
    boolean isMetered = ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager);
    boolean isNotRoaming = info != null && !info.isRoaming();
    return new NetworkState(isConnected, isValidated, isMetered, isNotRoaming);
}                                                  

实时状况: 在回调NetworkCallback或许接纳到播送onReceive时,调用getActiveNetworkState得到

3.2 使命调度器

完结Scheduler接口,调度器有3个:

  • SystemJobScheduler: 运用JobSceuler来完结使命引发;小于23版别时运用
  • SystemAlarmScheduler: 运用Alarm来完结使命引发;大于等于23版别运用
  • GreedyScheduler:在进程内直接调用,不同android版别均会建立 其实还有一个androidx.work.impl.background.gcm.GcmScheduler,这个完结需求凭借google的GCM推送服务来完结,这儿用了反射(国内不支持),而且优先于SystemAlarmScheduler

调度器的创立在WorkManagerImpl类

   public List<Scheduler> createSchedulers(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor taskExecutor) {
        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this), // 能够被体系引发调用手法
                new GreedyScheduler(context, configuration, taskExecutor, this)); // 进程内调用手法
    }

调度器被调用的逻辑在Schedulers

public static void schedule(Configuration configuration,WorkDatabase workDatabase,List<Scheduler> schedulers) {
        if (schedulers == null || schedulers.size() == 0) {
            return;
        }
        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
        List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
        List<WorkSpec> allEligibleWorkSpecs;
        workDatabase.beginTransaction();
        try {
            eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(configuration.getMaxSchedulerLimit()); 
            allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(MAX_GREEDY_SCHEDULER_LIMIT);
            if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) {
                long now = System.currentTimeMillis();
                for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {
                    workSpecDao.markWorkSpecScheduled(workSpec.id, now); // 进行插槽状况处理,非默许才会被GreedyScheduler进行调度
                }
            }
            workDatabase.setTransactionSuccessful();
        } finally {
            workDatabase.endTransaction();
        }
        if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) {
            WorkSpec[] eligibleWorkSpecsArray =
                    new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
            eligibleWorkSpecsArray =
                    eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
            for (Scheduler scheduler : schedulers) {
                if (scheduler.hasLimitedSchedulingSlots()) {
                    scheduler.schedule(eligibleWorkSpecsArray);
                }
            }
        }
        if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
            WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
            enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
            for (Scheduler scheduler : schedulers) {
                if (!scheduler.hasLimitedSchedulingSlots()) {
                    scheduler.schedule(enqueuedWorkSpecsArray);
                }
            }
        }
    }
  • GreedyScheduler调度器处理使命正在排队,使命最大束缚是200条;其它调度器履行在排队且未被调度器处理的,且和现在调度器现已处理的总和不超越装备中的最大束缚,默许是版别23是10,其它版别20

3.2.1 GreedyScheduler调度器

类图:

WorkManager使用以及源码理解
经过流程剖析有以下定论:

  1. 延时经过DelayedWorkTracker来处理,其实是经过Handler机制处理,在主Handler中进行
  2. 束缚经过WorkConstraintsTracker来进行检测,经过自身完结回调WorkConstraintsCallback来处理
  3. 满意履行条件的使命,经过WorkManagerImpl进行履行
  4. 完结ExecutionListener接口,来检测现已完结的使命,避免重复履行
  5. 有必要在装备的进程中处理,默许为运用主进程

3.2.2 SystemJobScheduler调度器

类图:

WorkManager使用以及源码理解

  1. SystemJobInfoConverter进行数据转化JobScheduler需求的入参,以及其它数据相互转化
  2. SystemJobService负责调用WorkManagerImpl类办法去履行使命;以及使命履行完结的处理
  3. 束缚由JobScheduler自己内部进行;而这儿只需求调用办法即可

这个调度器比较简单,其完结首要经过了JobScheduler的完结,JobScheduler的完结原理这儿不做介绍

3.2.3 SystemAlarmScheduler调度器

这个调度器完结就比较复杂了;类图:

WorkManager使用以及源码理解

  1. SystemAlarmService服务,使命各种状况经过发动此服务来处理
  2. SystemAlarmDispatcher, 使命各种状况分发处理节点,并回传无使命处理状况,毁掉服务
  3. CommandHander具体处理各种intent事情,以及内部使命调度完结后收尾(完结了ExecutionListener接口,其含义却不是使命现已履行完结后的处理)
  4. ConstraintsCommandHandler:处理了束缚状况,运用WorkConstraintsTracker检测其时状况;事情是由*Proxy类播送发动service从而通知的, ConstraintProxyUpdateReceiver操控各种束缚播送是否可用
  5. Alarms,运用AlramManager机制处理使命延时,并发动服务
  6. RescheduleReceiver:开机或许时刻事情播送,重发动WorkManager处理剩下使命的;这儿只要静态注册,非一切版别有用
  7. DelayMetCommandHandler:进行束缚实时更新判别,并在满意履行时经过WorkManagerImpl履行使命;在类图中没有标明其完结了WorkTimer.TimeLimitExceededListener(这个是对使命从调度状况到运转状况的一个操控,10分钟未完结状况改变从头处理)
  8. 运用了PowerManager.WakeLock确保调度进程中cpu运转

3.3、使命流程

使命流程涉及调度的具体进程,在以下流程图中就会直接从Schedulers办法直接异步履行发动使命;调度进程省略;

3.3.1 enqueue流程

WorkManager调用时,均是先包装成WorkContinuationImpl然后调用其办法enqueue履行;

WorkContinuationImpl: 包含了一个链表,指向了一切依靠的WorkContinuation目标;也记录了使命自身的一些信息和流程状况

流程图

WorkManager使用以及源码理解

  • WorkerWrapper:handleResult办法后续是对不同成果以及使命类别,对使命数据进行相应处理;其中进行了输入数据的兼并;在履行中多个关键点对撤销进行校验处理
  • WorkForegroundRunnable:发动前台服务,这个需求在完结Work中供给通知信息(办法getForegroundInfoAsync);假如有加急处理,也是发动前台服务
  • ListenableWorker中,经过setProgressAsync能够设置进度数据;成功时,doWork回来的成果中含有成功数据
  • EnqueueRunnable:enqueueWorkWithPrerequisites办法中对重复使命依据战略进行了处理
  • CancelWorkRunable:iterativelyCancelWorkAndDependents删除了依靠其的使命
  • 中间大量运用SettableFuture来让各个Runnable履行等候其履行的先决条件

3.3.2 cancel流程

撤销时,能够经过使命的id、name、tag或许撤销一切;不管是哪种调用,都是经过标识获取满意条件的使命id调集,并进行撤销;其流程也仅仅在id调集查询时不同罢了

流程图

WorkManager使用以及源码理解

  • WorkWrapper进行打断时,其流程也便是使命履行时打断处理,在履行进程中多处对打断进行了检测处理
  • 撤销时,对调度、履行中的使命别离进行撤销;一起同步状况到数据库

4、总结

上面仅仅大约介绍了常规运用,以及相关类图、流程,代码细节并没有展现。代码中的一些技能点需求至少会用

  • AlarmMananger
  • JobScheduler
  • 电量低、内存不足、充电状况、网络状况检测以及实时更新
  • SettableFuture
  • ……

从代码架构上,也有需求去多考虑的当地

  • 各个部分的别离笼统
  • 播送、服务组件的打开关闭、以及服务的毁掉、使命履行结束后的回调收尾等功能的考量
  • 数据库表的规划
  • 不管哪个版别,均有两个调度器去履行使命,他们有或许去处理同一个使命,这带来的复杂度处理逻辑以及优势
  • ……

假如在此文章中您有所收获,请给作者一个鼓舞,点个赞,谢谢支持

技能改变都很快,但基础技能、理论知识永远都是那些;作者期望在余后的日子中,对常用技能点进行基础知识分享;假如你觉得文章写的不错,请给予重视和点赞;假如文章存在错误,也请多多指教!