我正在使用WorkManager在我的应用中实现并行的后台上传和下载功能。除了WorkManager运行太多任务同时执行,直到任务都完成之前手机变得无响应,其他的都很好。我想要排队10个任务,并让WM同时执行其中2个,直到所有10个任务完成。但是事实上,WorkManager似乎会并行执行5-10个任务。我无法找到如何做到这一点。我尝试使用自己的Configuration,只使用单线程执行器,但没有任何区别。我该如何限制一次执行的任务数?我正在使用1.0.0-rc01版本。
我正在使用WorkManager在我的应用中实现并行的后台上传和下载功能。除了WorkManager运行太多任务同时执行,直到任务都完成之前手机变得无响应,其他的都很好。我想要排队10个任务,并让WM同时执行其中2个,直到所有10个任务完成。但是事实上,WorkManager似乎会并行执行5-10个任务。我无法找到如何做到这一点。我尝试使用自己的Configuration,只使用单线程执行器,但没有任何区别。我该如何限制一次执行的任务数?我正在使用1.0.0-rc01版本。
Configuration
来定义同时执行的Worker
数量。在你的Application.onCreate()
中添加以下内容即可。要了解更多信息,请阅读如何使用自定义配置初始化WorkManager
的文章。"最初的回答"val config = Configuration.Builder()
.setExecutor(yourExecutor)
.build()
WorkManager.initialize(context, config)
我测试了这个示例代码,没有任何错误,使用10个并行执行节点(.setExecutor(Executors.newFixedThreadPool(10))
)下载文件,没有任何延迟和冻结UI线程:
我使用 implementation 'android.arch.work:work-runtime:1.0.0-beta02'
.
在你的 AndroidManifest.xml
文件中:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="de.stocard.stocard.workmanager-init"
android:enabled="false"
tools:replace="android:authorities"/>
</application>
App.java
中:Configuration configuration = new Configuration.Builder()
.setExecutor(Executors.newFixedThreadPool(10))
.build();
WorkManager.initialize(getApplicationContext(), configuration);
MainActivity.java
中:OneTimeWorkRequest MyWorkA = new OneTimeWorkRequest.Builder(MyWorkA.class)
.build();
OneTimeWorkRequest MyWorkB = new OneTimeWorkRequest.Builder(MyWorkB.class)
.build();
OneTimeWorkRequest MyWorkC = new OneTimeWorkRequest.Builder(MyWorkC.class)
.build();
OneTimeWorkRequest MyWorkD = new OneTimeWorkRequest.Builder(MyWorkD.class)
.build();
OneTimeWorkRequest MyWorkE = new OneTimeWorkRequest.Builder(MyWorkE.class)
.build();
OneTimeWorkRequest MyWorkF = new OneTimeWorkRequest.Builder(MyWorkF.class)
.build();
OneTimeWorkRequest MyWorkG = new OneTimeWorkRequest.Builder(MyWorkG.class)
.build();
OneTimeWorkRequest MyWorkH = new OneTimeWorkRequest.Builder(MyWorkH.class)
.build();
OneTimeWorkRequest MyWorkI = new OneTimeWorkRequest.Builder(MyWorkI.class)
.build();
OneTimeWorkRequest MyWorkJ = new OneTimeWorkRequest.Builder(MyWorkJ.class)
.build();
OneTimeWorkRequest MyWorkK = new OneTimeWorkRequest.Builder(MyWork.class)
.build();
OneTimeWorkRequest MyWorkL = new OneTimeWorkRequest.Builder(MyWorkL.class)
.build();
List<OneTimeWorkRequest> allWorker = new ArrayList<>();
allWorker.add(MyWorkA);
allWorker.add(MyWorkB);
allWorker.add(MyWorkC);
allWorker.add(MyWorkD);
allWorker.add(MyWorkE);
allWorker.add(MyWorkF);
allWorker.add(MyWorkG);
allWorker.add(MyWorkH);
allWorker.add(MyWorkI);
allWorker.add(MyWorkJ);
WorkManager.getInstance()
.enqueue(allWorker);
对于 MyWorkA.java
到 MyWorkJ.java
,请使用以下代码(虚拟下载文件):
public class MyWorkA extends Worker {
private static final String TAB = MyWorkA.class.getSimpleName();
public MyWorkA(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.e(TAB,"My WorkA");
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL("http://ipv4.download.thinkbroadband.com/20MB.zip");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
}
int fileLength = connection.getContentLength();
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_nameA.zip");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
if (fileLength > 0)
output.write(data, 0, count);
}
} catch (Exception e) {
Log.i("test", e.getMessage());
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return Result.success();
}
}
如果您正在使用ListenableWorker,那么使用自定义Configuration和setExecutor将无法帮助。原因是这个executor只用于运行Worker而不是ListenableWorker。所以在这种特殊情况下,您可以拥有自己的固定线程池,在启动后台进程时使用它。其中一种实现方式是使用ExecutorService中的固定线程池-
public class MyExecutorService {
private static ExecutorService executorService = null;
public static ExecutorService getExecutorService() {
if (executorService == null) {
executorService = Executors.newFixedThreadPool(2);
}
return executorService;
}
}
public class MyListenableWorker extends ListenableWorker {
public MyListenableWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public ListenableFuture<Result> startWork() {
MyExecutorService.getExecutorService().execute(new Runnable() {
@Override
public void run() {
doBackgroundWork();
}
});
return future; // see note below
}
}
注意:如果您正在使用自定义的ListenableWorker,您需要像这里所述一样维护ListenableFuture的状态。
override fun createWork(): Single<Result> { }
中实现了所有逻辑,那么在RxWorker的情况下会有什么改变呢? - Etienne Lawlor
.setExecutor(Executors.newFixedThreadPool(5))
,但它并没有限制在给定时间内运行的工作线程数为5个,我的测试结果明确超过了这个数量。 - Etienne Lawlor