কাজ পরিচালনা

একবার আপনি আপনার Worker এবং WorkRequest নির্ধারণ করে ফেললে, শেষ ধাপটি হলো আপনার কাজটিকে কিউতে যুক্ত করা। কাজ কিউতে যুক্ত করার সবচেয়ে সহজ উপায় হলো WorkManager-এর enqueue() মেথডটি কল করা এবং যে WorkRequest আপনি চালাতে চান, সেটি পাস করা।

কোটলিন

val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

জাভা

WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

কাজের পুনরাবৃত্তি এড়াতে কাজ কিউতে যুক্ত করার সময় সতর্কতা অবলম্বন করুন। উদাহরণস্বরূপ, একটি অ্যাপ প্রতি ২৪ ঘণ্টা পর পর তার লগগুলো একটি ব্যাকএন্ড সার্ভিসে আপলোড করার চেষ্টা করতে পারে। আপনি যদি সতর্ক না হন, তাহলে একই টাস্ক বারবার কিউতে যুক্ত হয়ে যেতে পারে, যদিও কাজটি কেবল একবারই চালানোর প্রয়োজন। এই উদ্দেশ্য পূরণের জন্য, আপনি কাজটি একটি স্বতন্ত্র কাজ হিসেবে শিডিউল করতে পারেন।

অনন্য কাজ

ইউনিক ওয়ার্ক একটি শক্তিশালী ধারণা যা নিশ্চিত করে যে, একটি নির্দিষ্ট নামে একই সময়ে আপনার কাজের কেবল একটিই ইনস্ট্যান্স থাকবে। আইডি-র মতো নয়, ইউনিক নামগুলো মানুষের পাঠযোগ্য এবং ওয়ার্কম্যানেজার দ্বারা স্বয়ংক্রিয়ভাবে তৈরি না হয়ে ডেভেলপার দ্বারা নির্দিষ্ট করা হয়। ট্যাগের মতো নয়, ইউনিক নামগুলো কাজের কেবল একটি ইনস্ট্যান্সের সাথেই যুক্ত থাকে।

স্বতন্ত্র কাজ এককালীন এবং পর্যায়ক্রমিক উভয় প্রকার কাজের ক্ষেত্রেই প্রয়োগ করা যেতে পারে। আপনি পুনরাবৃত্তিমূলক কাজ নাকি এককালীন কাজের সময়সূচী নির্ধারণ করছেন, তার উপর নির্ভর করে এই পদ্ধতিগুলোর মধ্যে যেকোনো একটিকে কল করার মাধ্যমে একটি স্বতন্ত্র কাজের ক্রম তৈরি করতে পারেন।

এই উভয় মেথডই ৩টি আর্গুমেন্ট গ্রহণ করে:

  • uniqueWorkName - কাজের অনুরোধটিকে অনন্যভাবে শনাক্ত করতে ব্যবহৃত একটি String
  • existingWorkPolicy - একটি enum যা WorkManager-কে বলে দেয় যে, যদি সেই অনন্য নামে আগে থেকেই কোনো অসমাপ্ত কাজের ধারা থাকে, তাহলে কী করতে হবে। আরও তথ্যের জন্য দ্বন্দ্ব নিরসন নীতি দেখুন।
  • work - সময়সূচী নির্ধারণের জন্য WorkRequest

স্বতন্ত্র কাজ ব্যবহার করে, আমরা পূর্বে উল্লিখিত আমাদের ডুপ্লিকেট শিডিউলিং সমস্যাটি সমাধান করতে পারি।

কোটলিন

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

জাভা

PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

এখন, যদি কিউতে আগে থেকেই একটি sendLogs জব থাকা অবস্থায় কোডটি চলে, তাহলে বিদ্যমান জবটি অপরিবর্তিত থাকে এবং কোনো নতুন জব যোগ করা হয় না।

যদি আপনাকে ধীরে ধীরে একাধিক কাজ একসাথে করতে হয়, তবে কাজের স্বতন্ত্র ক্রমও বেশ কার্যকর হতে পারে। উদাহরণস্বরূপ, একটি ফটো এডিটিং অ্যাপ ব্যবহারকারীদের একাধিক কাজ পূর্বাবস্থায় ফিরিয়ে আনার (আনডু) সুযোগ দিতে পারে। এই পূর্বাবস্থায় ফেরানো কাজগুলোর প্রত্যেকটিতে কিছুটা সময় লাগতে পারে, কিন্তু সেগুলো সঠিক ক্রমেই সম্পন্ন করতে হয়। এক্ষেত্রে, অ্যাপটি একটি "আনডু" চেইন তৈরি করতে পারে এবং প্রয়োজন অনুযায়ী প্রতিটি আনডু অপারেশনকে সেই চেইনে যুক্ত করতে পারে। আরও বিস্তারিত জানতে ‘চেইনিং ওয়ার্ক’ দেখুন।

সংঘাত নিরসন নীতি

স্বতন্ত্র কাজ শিডিউল করার সময়, কোনো দ্বন্দ্ব দেখা দিলে কী ব্যবস্থা নিতে হবে তা আপনাকে WorkManager-কে অবশ্যই জানাতে হবে। কাজটি কিউতে যুক্ত করার সময় একটি enum পাস করার মাধ্যমে এটি করা হয়।

এককালীন কাজের জন্য, আপনাকে একটি ExistingWorkPolicy প্রদান করতে হয়, যা দ্বন্দ্ব নিরসনের জন্য ৪টি বিকল্প সমর্থন করে।

  • বিদ্যমান কাজকে নতুন কাজ দিয়ে REPLACE । এই বিকল্পটি বিদ্যমান কাজটি বাতিল করে দেয়।
  • বিদ্যমান কাজটি KEEP এবং নতুন কাজটি উপেক্ষা করুন।
  • বিদ্যমান কাজের শেষে নতুন কাজটি APPEND । এই নীতির ফলে আপনার নতুন কাজটি বিদ্যমান কাজের সাথে সংযুক্ত হয়ে যাবে এবং বিদ্যমান কাজটি শেষ হওয়ার পর চলবে।

বিদ্যমান কাজটি নতুন কাজের জন্য একটি পূর্বশর্ত হয়ে ওঠে। যদি বিদ্যমান কাজটি CANCELLED বা FAILED হয়ে যায়, তাহলে নতুন কাজটিও CANCELLED বা FAILED হয়ে যায়। যদি আপনি চান যে বিদ্যমান কাজের অবস্থা নির্বিশেষে নতুন কাজটি চালু হোক, তাহলে এর পরিবর্তে APPEND_OR_REPLACE ব্যবহার করুন।

  • APPEND_OR_REPLACE ফাংশনটি APPEND মতোই কাজ করে, তবে এর একটি পার্থক্য হলো এটি পূর্বশর্তমূলক কাজের অবস্থার উপর নির্ভরশীল নয়। যদি বিদ্যমান কাজটি CANCELLED বা FAILED হয়ে যায়, তবুও নতুন কাজটি চলতে থাকে।

পিরিয়ডিক ওয়ার্কের জন্য, আপনাকে একটি ExistingPeriodicWorkPolicy প্রদান করতে হয়, যা REPLACE এবং KEEP এই দুটি অপশন সমর্থন করে। এই অপশনগুলো তাদের ExistingWorkPolicy প্রতিরূপগুলোর মতোই কাজ করে।

আপনার কাজ পর্যবেক্ষণ করা

কাজ কিউতে যুক্ত করার পর যেকোনো সময়ে, আপনি WorkManager-কে তার name , id অথবা এর সাথে যুক্ত কোনো tag দিয়ে কোয়েরি করে সেটির স্ট্যাটাস চেক করতে পারেন।

কোটলিন

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

জাভা

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>

কোয়েরিটি একটি WorkInfo অবজেক্টের ListenableFuture রিটার্ন করে, যার মধ্যে ওয়ার্কটির id , এর ট্যাগ, এর বর্তমান State এবং Result.success(outputData) ব্যবহার করে প্রাপ্ত যেকোনো আউটপুট ডেটাসেট অন্তর্ভুক্ত থাকে।

প্রতিটি মেথডের LiveData এবং Flow ভ্যারিয়েন্টগুলো একটি লিসেনার রেজিস্টার করার মাধ্যমে আপনাকে WorkInfo এর পরিবর্তনগুলো পর্যবেক্ষণ করার সুযোগ দেয়। উদাহরণস্বরূপ, যদি আপনি কোনো কাজ সফলভাবে শেষ হলে ব্যবহারকারীকে একটি বার্তা দেখাতে চান, তাহলে আপনি তা নিম্নোক্তভাবে সেট আপ করতে পারেন:

কোটলিন

workManager.getWorkInfoByIdFlow(syncWorker.id)
          .collect{ workInfo ->
              if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
                  Snackbar.make(requireView(),
                      R.string.work_completed, Snackbar.LENGTH_SHORT)
                      .show()
              }
          }

জাভা

workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

জটিল কাজের প্রশ্ন

WorkManager 2.4.0 এবং এর পরবর্তী সংস্করণগুলো WorkQuery অবজেক্ট ব্যবহার করে কিউতে থাকা জবগুলোর জন্য জটিল কোয়েরি সমর্থন করে। WorkQuery কোনো কাজের ট্যাগ, স্টেট এবং অনন্য কাজের নামের সমন্বয়ে কোয়েরি করা সমর্থন করে।

নিম্নলিখিত উদাহরণটি দেখায় যে কীভাবে আপনি 'syncTag' ট্যাগযুক্ত সেই সমস্ত কাজ খুঁজে পেতে পারেন, যেগুলি FAILED বা CANCELLED অবস্থায় আছে এবং যেগুলির একটি অনন্য কাজের নাম ' preProcess ' বা ' sync ' রয়েছে।

কোটলিন

val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

জাভা

WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

একটি WorkQuery এর প্রতিটি উপাদান (ট্যাগ, স্টেট বা নাম) অন্যগুলোর সাথে AND দ্বারা যুক্ত হয়। একটি উপাদানের প্রতিটি মান OR যুক্ত হয়। উদাহরণস্বরূপ: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)

WorkQuery তার LiveData-এর সমতুল্য getWorkInfosLiveData() এবং Flow-এর সমতুল্য getWorkInfosFlow() সাথেও কাজ করে।

কাজ বাতিল করা এবং বন্ধ করা

আপনার পূর্বে সারিতে রাখা কাজটি যদি আর চালানোর প্রয়োজন না হয়, তবে আপনি এটি বাতিল করার জন্য অনুরোধ করতে পারেন। কাজের name , id অথবা এর সাথে যুক্ত কোনো tag ব্যবহার করে কাজটি বাতিল করা যায়।

কোটলিন

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

জাভা

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

অভ্যন্তরীণভাবে, WorkManager কাজটি বা ওয়ার্কটির State (State) পরীক্ষা করে। যদি কাজটি ইতিমধ্যে শেষ হয়ে গিয়ে থাকে, তবে কিছুই ঘটে না। অন্যথায়, ওয়ার্কটির অবস্থা CANCELLED এ পরিবর্তন করা হয় এবং কাজটি ভবিষ্যতে আর চলবে না। এই ওয়ার্কটির উপর নির্ভরশীল যেকোনো WorkRequest জবও CANCELLED হয়ে যাবে।

RUNNING ওয়ার্ক গ্রহণ করলে ListenableWorker.onStopped() মেথডটি কল করা হয়। যেকোনো সম্ভাব্য পরিষ্করণ পরিচালনা করতে এই মেথডটি ওভাররাইড করুন। আরও তথ্যের জন্য ‘stop a running worker’ দেখুন।

একজন দৌড়ানো কর্মীকে থামান

বিভিন্ন কারণে আপনার চলমান Worker WorkManager দ্বারা বন্ধ হয়ে যেতে পারে:

  • আপনি স্পষ্টভাবে এটি বাতিল করার জন্য অনুরোধ করেছেন (উদাহরণস্বরূপ, WorkManager.cancelWorkById(UUID) কল করে)।
  • স্বতন্ত্র কাজের ক্ষেত্রে, আপনি REPLACE ExistingWorkPolicy সহ একটি নতুন WorkRequest স্পষ্টভাবে কিউতে যুক্ত করেছেন। পুরানো WorkRequest অবিলম্বে বাতিল বলে গণ্য করা হয়।
  • আপনার কাজের সীমাবদ্ধতাগুলো আর পূরণ হচ্ছে না।
  • সিস্টেম কোনো কারণে আপনার অ্যাপকে কাজটি বন্ধ করার নির্দেশ দিয়েছে। ১০ মিনিটের নির্ধারিত সময়সীমা অতিক্রম করলে এমনটা হতে পারে। কাজটি পরবর্তী সময়ে পুনরায় চেষ্টা করার জন্য নির্ধারিত হয়েছে।

এই পরিস্থিতিতে, আপনার কর্মীকে থামানো হয়েছে।

আপনার চলমান যেকোনো কাজ সম্মিলিতভাবে বন্ধ করে দেওয়া এবং আপনার ওয়ার্কার যে রিসোর্সগুলো ধরে রেখেছে, সেগুলো ছেড়ে দেওয়া উচিত। উদাহরণস্বরূপ, এই পর্যায়ে আপনার ডাটাবেস এবং ফাইলের খোলা হ্যান্ডেলগুলো বন্ধ করে দেওয়া উচিত। আপনার ওয়ার্কার কখন বন্ধ হচ্ছে তা বোঝার জন্য আপনার কাছে দুটি পদ্ধতি রয়েছে।

onStopped() কলব্যাক

আপনার Worker বন্ধ হওয়ার সাথে সাথেই WorkManager ListenableWorker.onStopped() মেথডটি কল করে। আপনার ধরে রাখা যেকোনো রিসোর্স বন্ধ করতে এই মেথডটি ওভাররাইড করুন।

isStopped() প্রপার্টি

আপনার ওয়ার্কারটি ইতিমধ্যেই বন্ধ করা হয়েছে কিনা তা পরীক্ষা করতে আপনি ListenableWorker.isStopped() মেথডটি কল করতে পারেন। আপনি যদি আপনার ওয়ার্কারে দীর্ঘ সময় ধরে চলা বা পুনরাবৃত্তিমূলক অপারেশন সম্পাদন করেন, তবে আপনার এই প্রপার্টিটি ঘন ঘন পরীক্ষা করা উচিত এবং যত তাড়াতাড়ি সম্ভব কাজ বন্ধ করার সংকেত হিসাবে এটি ব্যবহার করা উচিত।

দ্রষ্টব্য: যে Worker onStop সিগন্যাল পেয়েছে, WorkManager সেই Worker-এর সেট করা Result উপেক্ষা করে, কারণ Worker-টিকে ইতিমধ্যেই বন্ধ বলে গণ্য করা হয়।