Android可保活的、不依附于应用进程存活的后台任务框架:WorkManager
WorkManager旨在实现不依附与App进程的后台线程化任务。举例来说,假设App在本地有一个大小约2GB的文件需要上传到远程服务器。App希望在手机空闲且设备资源充足的情况下将这2GB大小的文件上传。
同时,该上传任务不受App进程存活与否影响(比如当前App进程被kill掉,仍能执行该任务)。这种类型的后台任务明显区别于过往的依附于App进程的Thread线程池一类的耗时任务。WorkManager实现的,是独立的、不依附于App进程的真正的后台耗时任务。
在过往,常规实现的Android后台任务(service,线程等),很容易受到寄宿的App进程情况影响,影响主要来自于无法保证寄宿的App进程一定存活。
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
static final String TIME = "time";
static final String DATA = "data";
static final String ID = "id";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
test();
}
private void test() {
String TAG = "tag";
OneTimeWorkRequest a = getRequest(TAG, 1000, "A", TestWorker.class);
OneTimeWorkRequest b = getRequest(TAG, 2000, "B", TestWorker.class);
OneTimeWorkRequest c = getRequest(TAG, 3000, "C", TestWorker.class);
OneTimeWorkRequest d = getRequest(TAG, 4000, "D", TestWorker.class);
WorkManager.getInstance(this)
.enqueue(Arrays.asList(a, b, c, d));
LiveData<List<WorkInfo>> liveData = WorkManager.getInstance(this).getWorkInfosByTagLiveData(TAG);
liveData.observe(this, new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
System.out.println("长度:" + workInfos.size());
for (WorkInfo info : workInfos) {
if (info.getState() == WorkInfo.State.SUCCEEDED) {
Data data = info.getOutputData();
String s = data.getString(DATA);
Long time = data.getLong(TIME, -1);
String id = data.getString(ID);
System.out.println("onChanged:" + id + " " + s + " " + time);
}
}
}
});
System.out.println("UI Main线程Id:" + Thread.currentThread().getId());
//取消任务。
//UUID workId = workRequest.getId();
//WorkManager.getInstance().cancelByWorkId(workId);
}
private OneTimeWorkRequest getRequest(String tag, int time, String id, Class cls) {
//传递的参数数据。
Data data = new Data.Builder()
.putInt(TIME, time)
.putString(ID, id)
.putString(DATA, "phil " + id)
.build();
Constraints constraints = new Constraints.Builder()//触发规则。
.setRequiresStorageNotLow(false)
.setRequiresBatteryNotLow(false)
.setRequiresCharging(false)
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)//对络连接不做要求。
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(cls)
.setInputData(data)
.setConstraints(constraints)
.addTag(tag)
.build();
return request;
}
}
package zhangphil.demo;
import android.content.Context;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class TestWorker extends Worker {
private String id;
private int time;
public TestWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
Data data = workerParams.getInputData();
time = data.getInt(MainActivity.TIME, -1);
id = data.getString(MainActivity.ID);
String content = data.getString(MainActivity.DATA);
System.out.println("Worker:传入数据->id:" + id + " time:" + time + " data:" + content);
}
/**
* doWork()函数体已经线程化。
*
* @return
*/
@NonNull
@Override
public Result doWork() {
//模拟耗时操作。
SystemClock.sleep(time);
System.out.println("doWork:" + id + "-线程Id:" + Thread.currentThread().getId() + " 完成");
//传输数据给外部监听者。
Data data = new Data.Builder()
.putString(MainActivity.ID, id)
.putString(MainActivity.DATA, "数据回传")
.putLong(MainActivity.TIME, System.currentTimeMillis())
.build();
return Result.success(data);
}
@Override
public void onStopped() {
super.onStopped();
}
}
输出:
15477-15477/? I/System.out: UI Main线程Id:2
15477-15498/? I/System.out: Worker:传入数据->id:A time:1000 data:phil A
15477-15496/? I/System.out: Worker:传入数据->id:B time:2000 data:phil B
15477-15498/? I/System.out: Worker:传入数据->id:C time:3000 data:phil C
15477-15493/? I/System.out: Worker:传入数据->id:D time:4000 data:phil D
15477-15477/? I/System.out: 长度:4
15477-15477/? I/System.out: 长度:4
15477-15502/zhangphil.app I/System.out: doWork:A-线程Id:609 完成
15477-15477/zhangphil.app I/System.out: 长度:4
15477-15477/zhangphil.app I/System.out: onChanged:A 数据回传 1561542140123
15477-15503/zhangphil.app I/System.out: doWork:B-线程Id:610 完成
15477-15477/zhangphil.app I/System.out: 长度:4
15477-15477/zhangphil.app I/System.out: onChanged:B 数据回传 1561542141126
15477-15477/zhangphil.app I/System.out: onChanged:A 数据回传 1561542140123
15477-15505/zhangphil.app I/System.out: doWork:C-线程Id:611 完成
15477-15477/zhangphil.app I/System.out: 长度:4
15477-15477/zhangphil.app I/System.out: onChanged:C 数据回传 1561542142127
15477-15477/zhangphil.app I/System.out: onChanged:B 数据回传 1561542141126
15477-15477/zhangphil.app I/System.out: onChanged:A 数据回传 1561542140123
15477-15502/zhangphil.app I/System.out: doWork:D-线程Id:609 完成
15477-15477/zhangphil.app I/System.out: 长度:4
15477-15477/zhangphil.app I/System.out: onChanged:C 数据回传 1561542142127
15477-15477/zhangphil.app I/System.out: onChanged:B 数据回传 1561542141126
15477-15477/zhangphil.app I/System.out: onChanged:A 数据回传 1561542140123
15477-15477/zhangphil.app I/System.out: onChanged:D 数据回传 1561542144125
注意:本例基于WorkManager版本 2.1.0-beta02
implementation "androidx.work:work-runtime:2.1.0-beta02"
经验证发现,如果把a,b,c,d四个任务以任务链的形式组合任务,即使用WorkManager的beginWith-then设计任务链,至少存在一个问题:
在WorkManager构建阶段,通过setInputData传递给Worker的数据只有第一个任务可以成功,后面的传递失败。
具体原因不明,可能是Android官方还没有形成稳定的正式版。
附:
《Android体系架构:WorkManager》https://zhangphil.blog.csdn.net/article/details/89399972