Android WorkManager同时启动了太多的作业

3

我正在使用WorkManager在我的应用中实现并行的后台上传和下载功能。除了WorkManager运行太多任务同时执行,直到任务都完成之前手机变得无响应,其他的都很好。我想要排队10个任务,并让WM同时执行其中2个,直到所有10个任务完成。但是事实上,WorkManager似乎会并行执行5-10个任务。我无法找到如何做到这一点。我尝试使用自己的Configuration,只使用单线程执行器,但没有任何区别。我该如何限制一次执行的任务数?我正在使用1.0.0-rc01版本。

3个回答

2
你可以使用自定义的Configuration来定义同时执行的Worker数量。在你的Application.onCreate()中添加以下内容即可。要了解更多信息,请阅读如何使用自定义配置初始化WorkManager的文章。"最初的回答"
val config = Configuration.Builder()
  .setExecutor(yourExecutor)
  .build()

WorkManager.initialize(context, config)

1
我尝试过使用单线程执行器,但没有任何区别。 - 2fours
请确保正确初始化WorkManager。您需要“禁用”默认的初始化程序,否则第二次调用“initialize”将不起作用。您只会看到一个警告,表示已经初始化。 - Rahul
3
2.1.0-beta02版本仍存在此问题。如果我使用ListenableWorker,则会有所不同。无论我是否覆盖配置,它都会启动比Executors.newFixedThreadPool(2)允许的工作要多得多,比如10-20个而不是2个。默认初始化程序已禁用。 - 2fours
1
我和@2fours有着相同的经历。我尝试了.setExecutor(Executors.newFixedThreadPool(5)),但它并没有限制在给定时间内运行的工作线程数为5个,我的测试结果明确超过了这个数量。 - Etienne Lawlor
我在他们的错误跟踪器上开了一个问题 https://issuetracker.google.com/issues/155370056 - Etienne Lawlor

1

我测试了这个示例代码,没有任何错误,使用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.javaMyWorkJ.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();
    }
}

0

如果您正在使用ListenableWorker,那么使用自定义ConfigurationsetExecutor将无法帮助。原因是这个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的状态。


当你处理RxWorker而不是ListenableWorker时,会显示什么?我创建的RxWorker目前在override fun createWork(): Single<Result> { }中实现了所有逻辑,那么在RxWorker的情况下会有什么改变呢? - Etienne Lawlor
在这种情况下,您可以在配置中向执行程序提供固定的线程池。但我看到这对您不起作用。因此,作为替代方案,您可以覆盖RxWorker类中的getBackgroundScheduler()方法,并返回具有固定线程池的调度程序。就像这样 - protected Scheduler getBackgroundScheduler() { return Schedulers.from(MyExecutorService.getExecutorService()); } - shanx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接