Reputation: 31
I'm implementing parallel background upload/download functionality in my app using workmanager. Everything works great except WorkManager runs too many jobs simultaneously and the phone becomes unresponsive until things spool down. I'd like to enqueue say 10 tasks and have WM execute say 2 in parallel until all 10 are done. WorkManager seems to execute 5-10 in parallel. I can not figure out how to do this. I've tried my own Configuration with just a single threaded executor and it doesn't make any difference. How can I constrain the number of jobs executed at once? I'm using 1.0.0-rc01.
Upvotes: 3
Views: 2556
Reputation: 1007
i test this example code and work for me without any bug, 10 parallel execution node(.setExecutor(Executors.newFixedThreadPool(10))
) for download file without any lag and freeze UI thread:
i use implementation 'android.arch.work:work-runtime:1.0.0-beta02'
.
on your 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>
in App.java
:
Configuration configuration = new Configuration.Builder()
.setExecutor(Executors.newFixedThreadPool(10))
.build();
WorkManager.initialize(getApplicationContext(), configuration);
in 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);
and for MyWorkA.java
to MyWorkJ.java
use this code(dummy download file):
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();
}
}
Upvotes: 1
Reputation: 425
If you are using ListenableWorker then custom Configuration with setExecutor will not help. Reason being this executor is only meant for running the Worker and not ListenableWorker. So in this particular case, you can have your own fixed thread pool, which you can use while starting the background process. One of the way to achieve this with fixed thread pool in 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
}
}
Note: If you are using your custom ListenableWorker, you need to maintain status of ListenableFuture like mentioned here.
Upvotes: 0
Reputation: 21134
You can use a custom Configuration
to define the number of Worker
s that you want to execute simultaneously. Something like this in your Application.onCreate()
. For more information read about how to initialize WorkManager
with a custom configuration.
val config = Configuration.Builder()
.setExecutor(yourExecutor)
.build()
WorkManager.initialize(context, config)
Upvotes: 2