Threading در ListenableWorker

در شرایط خاص، ممکن است نیاز داشته باشید که یک استراتژی threading سفارشی ارائه کنید. به عنوان مثال، ممکن است لازم باشد یک عملیات ناهمزمان مبتنی بر تماس را مدیریت کنید. WorkManager این مورد استفاده را با ListenableWorker پشتیبانی می کند. ListenableWorker اساسی ترین API کارگر است. Worker ، CoroutineWorker و RxWorker همگی از این کلاس مشتق شده‌اند. ListenableWorker فقط زمان شروع و توقف کار را علامت می‌دهد و کار را کاملاً به شما واگذار می‌کند. سیگنال شروع کار بر روی نخ اصلی فراخوانی می شود، بنابراین بسیار مهم است که به صورت دستی به یک رشته پس زمینه انتخابی خود بروید.

متد انتزاعی ListenableWorker.startWork() یک ListenableFuture از Result برمی‌گرداند. ListenableFuture یک رابط سبک وزن است: Future است که عملکردی را برای اتصال شنوندگان و انتشار استثناها فراهم می کند. در روش startWork ، انتظار می‌رود که یک ListenableFuture را برگردانید، که پس از تکمیل آن، آن را با Result عملیات تنظیم می‌کنید. شما می توانید نمونه های ListenableFuture را به یکی از دو روش ایجاد کنید:

  1. اگر از Guava استفاده می کنید، از ListeningExecutorService استفاده کنید.
  2. در غیر این صورت، councurrent-futures در فایل gradle خود قرار دهید و از CallbackToFutureAdapter استفاده کنید.

اگر می‌خواهید کاری را بر اساس یک تماس ناهمزمان اجرا کنید، این کار را انجام می‌دهید:

کاتلین

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    successes++
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

جاوا

public class CallbackWorker extends ListenableWorker {

    public CallbackWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    successes++;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            for (int i = 0; i < 100; i++) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}

اگر کار شما متوقف شود چه اتفاقی می افتد؟ یک ListenableWorker 's ListenableFuture همیشه زمانی که انتظار می رود کار متوقف شود، لغو می شود. با استفاده از CallbackToFutureAdapter ، به سادگی باید یک شنونده لغو اضافه کنید، به شرح زیر:

کاتلین

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    ++successes
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

 completer.addCancellationListener(cancelDownloadsRunnable, executor)

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

جاوا

public class CallbackWorker extends ListenableWorker {

    public CallbackWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    ++successes;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            completer.addCancellationListener(cancelDownloadsRunnable, executor);

            for (int i = 0; i < 100; ++i) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}

اجرای ListenableWorker در فرآیندی متفاوت

همچنین می‌توانید با استفاده از RemoteListenableWorker ، پیاده‌سازی ListenableWorker ، یک کارگر را به یک فرآیند خاص متصل کنید.

RemoteListenableWorker با دو آرگومان اضافی که به عنوان بخشی از داده‌های ورودی هنگام ایجاد درخواست کاری ارائه می‌کنید، به فرآیند خاصی متصل می‌شود: ARGUMENT_CLASS_NAME و ARGUMENT_PACKAGE_NAME .

مثال زیر ساخت یک درخواست کاری را نشان می دهد که به یک فرآیند خاص وابسته است:

کاتلین

val PACKAGE_NAME = "com.example.background.multiprocess"

val serviceName = RemoteWorkerService::class.java.name
val componentName = ComponentName(PACKAGE_NAME, serviceName)

val data: Data = Data.Builder()
   .putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
   .putString(ARGUMENT_CLASS_NAME, componentName.className)
   .build()

return OneTimeWorkRequest.Builder(ExampleRemoteListenableWorker::class.java)
   .setInputData(data)
   .build()

جاوا

String PACKAGE_NAME = "com.example.background.multiprocess";

String serviceName = RemoteWorkerService.class.getName();
ComponentName componentName = new ComponentName(PACKAGE_NAME, serviceName);

Data data = new Data.Builder()
        .putString(ARGUMENT_PACKAGE_NAME, componentName.getPackageName())
        .putString(ARGUMENT_CLASS_NAME, componentName.getClassName())
        .build();

return new OneTimeWorkRequest.Builder(ExampleRemoteListenableWorker.class)
        .setInputData(data)
        .build();

برای هر RemoteWorkerService ، همچنین باید یک تعریف سرویس را در فایل AndroidManifest.xml خود اضافه کنید:

<manifest ... >
    <service
            android:name="androidx.work.multiprocess.RemoteWorkerService"
            android:exported="false"
            android:process=":worker1" />

        <service
            android:name=".RemoteWorkerService2"
            android:exported="false"
            android:process=":worker2" />
    ...
</manifest>

نمونه ها