যদি আপনাকে এমন কোনো ডেটা ট্রান্সফার করতে হয় যা সম্পন্ন হতে দীর্ঘ সময় লাগতে পারে, তাহলে আপনি একটি JobScheduler জব তৈরি করে সেটিকে ইউজার-ইনিশিয়েটেড ডেটা ট্রান্সফার (UIDT) জব হিসেবে চিহ্নিত করতে পারেন। UIDT জবগুলো মূলত দীর্ঘ সময় ধরে চলা ডেটা ট্রান্সফারের জন্য তৈরি, যা ডিভাইস ব্যবহারকারী শুরু করেন, যেমন কোনো রিমোট সার্ভার থেকে ফাইল ডাউনলোড করা। অ্যান্ড্রয়েড ১৪ (এপিআই লেভেল ৩৪)-এর সাথে UIDT জব চালু করা হয়েছিল।
ব্যবহারকারী-প্রবর্তিত ডেটা স্থানান্তর কাজগুলো ব্যবহারকারী দ্বারা শুরু করা হয়। এই কাজগুলোর জন্য একটি বিজ্ঞপ্তির প্রয়োজন হয়, এগুলো অবিলম্বে শুরু হয় এবং সিস্টেমের অবস্থা সাপেক্ষে দীর্ঘ সময় ধরে চলতে পারে। আপনি একই সাথে একাধিক ব্যবহারকারী-প্রবর্তিত ডেটা স্থানান্তর কাজ চালাতে পারেন।
ব্যবহারকারী কর্তৃক শুরু করা কাজগুলো অবশ্যই এমন সময়ে শিডিউল করতে হবে যখন অ্যাপ্লিকেশনটি ব্যবহারকারীর কাছে দৃশ্যমান থাকে (অথবা অনুমোদিত শর্তগুলোর কোনো একটিতে)। সমস্ত সীমাবদ্ধতা পূরণ হওয়ার পর, সিস্টেমের স্বাস্থ্য সংক্রান্ত বিধিনিষেধ সাপেক্ষে, ব্যবহারকারী কর্তৃক শুরু করা কাজগুলো অপারেটিং সিস্টেম দ্বারা নির্বাহ করা যেতে পারে। কাজটি কতক্ষণ চলবে তা নির্ধারণ করার জন্য সিস্টেমটি প্রদত্ত আনুমানিক পেলোড সাইজও ব্যবহার করতে পারে।
ব্যবহারকারী-সূচিত ডেটা স্থানান্তর কাজের সময়সূচী করুন
ব্যবহারকারী কর্তৃক শুরু করা ডেটা-স্থানান্তর কাজটি চালানোর জন্য, নিম্নলিখিতগুলি করুন:
নিশ্চিত করুন যে আপনার অ্যাপের ম্যানিফেস্টে
JobServiceএবং সংশ্লিষ্ট অনুমতিগুলো ঘোষণা করা হয়েছে:<service android:name="com.example.app.CustomTransferService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false"> ... </service>এছাড়াও, আপনার ডেটা স্থানান্তরের জন্য
JobServiceএর একটি কংক্রিট সাবক্লাস সংজ্ঞায়িত করুন:কোটলিন
class CustomTransferService : JobService() { ... }
জাভা
class CustomTransferService extends JobService() { .... }
ম্যানিফেস্টে
RUN_USER_INITIATED_JOBSপারমিশনটি ঘোষণা করুন:<manifest ...> <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" /> <application ...> ... </application> </manifest>JobInfoঅবজেক্ট তৈরি করার সময়setUserInitiated()মেথডটি কল করুন। (এই মেথডটি অ্যান্ড্রয়েড ১৪ থেকে উপলব্ধ।) এছাড়াও, আপনার জব তৈরি করার সময়setEstimatedNetworkBytes()কল করে পেলোড সাইজের একটি আনুমানিক ধারণা দেওয়ার পরামর্শ দেওয়া হয়।কোটলিন
val networkRequestBuilder = NetworkRequest.Builder() // Add or remove capabilities based on your requirements. // For example, this code specifies that the job won't run // unless there's a connection to the internet (not just a local // network), and the connection doesn't charge per-byte. .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_METERED) .build() val jobInfo = JobInfo.Builder(jobId, ComponentName(mContext, CustomTransferService::class.java)) // ... .setUserInitiated(true) .setRequiredNetwork(networkRequestBuilder) // Provide your estimate of the network traffic here .setEstimatedNetworkBytes(1024 * 1024 * 1024, 1024 * 1024 * 1024) // ... .build()
জাভা
NetworkRequest networkRequest = new NetworkRequest.Builder() // Add or remove capabilities based on your requirements. // For example, this code specifies that the job won't run // unless there's a connection to the internet (not just a local // network), and the connection doesn't charge per-byte. .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_METERED) .build(); JobInfo jobInfo = JobInfo.Builder(jobId, new ComponentName(mContext, CustomTransferService.class)) // ... .setUserInitiated(true) .setRequiredNetwork(networkRequest) // Provide your estimate of the network traffic here .setEstimatedNetworkBytes(1024 * 1024 * 1024, 1024 * 1024 * 1024) // ... .build();
জবটি এক্সিকিউট হওয়ার সময়,
JobServiceঅবজেক্টেsetNotification()কল করুন।setNotification()কল করলে ব্যবহারকারী টাস্ক ম্যানেজার এবং স্ট্যাটাস বার নোটিফিকেশন এরিয়া, উভয় স্থানেই জানতে পারেন যে জবটি চলছে।কার্য সম্পাদন সম্পন্ন হলে, সিস্টেমকে কাজটি শেষ হয়েছে বা পুনরায় নির্ধারণ করা উচিত, তা জানানোর জন্য
jobFinished()কল করুন।কোটলিন
class CustomTransferService: JobService() { private val scope = CoroutineScope(Dispatchers.IO) @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) override fun onStartJob(params: JobParameters): Boolean { val notification = Notification.Builder(applicationContext, NOTIFICATION_CHANNEL_ID) .setContentTitle("My user-initiated data transfer job") .setSmallIcon(android.R.mipmap.myicon) .setContentText("Job is running") .build() setNotification(params, notification.id, notification, JobService.JOB_END_NOTIFICATION_POLICY_DETACH) // Execute the work associated with this job asynchronously. scope.launch { doDownload(params) } return true } private suspend fun doDownload(params: JobParameters) { // Run the relevant async download task, then call // jobFinished once the task is completed. jobFinished(params, false) } // Called when the system stops the job. override fun onStopJob(params: JobParameters?): Boolean { // Asynchronously record job-related data, such as the // stop reason. return true // or return false if job should end entirely } }
জাভা
class CustomTransferService extends JobService{ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @Override public boolean onStartJob(JobParameters params) { Notification notification = Notification.Builder(getBaseContext(), NOTIFICATION_CHANNEL_ID) .setContentTitle("My user-initiated data transfer job") .setSmallIcon(android.R.mipmap.myicon) .setContentText("Job is running") .build(); setNotification(params, notification.id, notification, JobService.JOB_END_NOTIFICATION_POLICY_DETACH) // Execute the work associated with this job asynchronously. new Thread(() -> doDownload(params)).start(); return true; } private void doDownload(JobParameters params) { // Run the relevant async download task, then call // jobFinished once the task is completed. jobFinished(params, false); } // Called when the system stops the job. @Override public boolean onStopJob(JobParameters params) { // Asynchronously record job-related data, such as the // stop reason. return true; // or return false if job should end entirely } }
ব্যবহারকারীকে কাজের অবস্থা ও অগ্রগতি সম্পর্কে অবহিত রাখতে পর্যায়ক্রমে নোটিফিকেশনটি আপডেট করুন। যদি আপনি কাজটি শিডিউল করার আগে ট্রান্সফার সাইজ নির্ধারণ করতে না পারেন, অথবা আনুমানিক ট্রান্সফার সাইজ আপডেট করার প্রয়োজন হয়, তাহলে ট্রান্সফার সাইজ জানা যাওয়ার পর তা আপডেট করতে নতুন API,
updateEstimatedNetworkBytes()ব্যবহার করুন।
সুপারিশসমূহ
UIDT জবগুলো কার্যকরভাবে চালানোর জন্য, নিম্নলিখিত পদক্ষেপগুলো অনুসরণ করুন:
কাজটি কখন সম্পাদন করা হবে তা নির্দিষ্ট করার জন্য নেটওয়ার্কের সীমাবদ্ধতা এবং কাজ সম্পাদনের সীমাবদ্ধতা স্পষ্টভাবে সংজ্ঞায়িত করুন।
onStartJob()-এ টাস্কটি অ্যাসিঙ্ক্রোনাসভাবে সম্পাদন করুন; উদাহরণস্বরূপ, আপনি একটি কো-রুটিন ব্যবহার করে এটি করতে পারেন। যদি আপনি টাস্কটি অ্যাসিঙ্ক্রোনাসভাবে না চালান, তাহলে কাজটি মেইন থ্রেডে চলে এবং এটিকে ব্লক করে দিতে পারে, যা একটি ANR (অ্যাক্সিডেন্টাল রিকোয়েস্ট) ঘটাতে পারে।অপ্রয়োজনীয়ভাবে কাজটি বেশি সময় ধরে চলা এড়াতে, ট্রান্সফার সফল হোক বা ব্যর্থ হোক, তা শেষ হলেই
jobFinished()কল করুন। এর ফলে, কাজটি অপ্রয়োজনীয়ভাবে বেশি সময় ধরে চলবে না। কাজটি কেন বন্ধ করা হয়েছিল তা জানতে,onStopJob()কলব্যাক মেথডটি ইমপ্লিমেন্ট করুন এবংJobParameters.getStopReason()কল করুন।
পশ্চাদপদ সামঞ্জস্য
বর্তমানে এমন কোনো জেটপ্যাক লাইব্রেরি নেই যা UIDT জব সমর্থন করে। এই কারণে, আমরা সুপারিশ করি যে আপনি আপনার পরিবর্তনকে এমন কোড দিয়ে সুরক্ষিত করুন যা যাচাই করে যে আপনি অ্যান্ড্রয়েড ১৪ বা তার উচ্চতর সংস্করণে কাজ করছেন। অ্যান্ড্রয়েডের নিম্নতর সংস্করণগুলিতে, একটি বিকল্প পদ্ধতি হিসেবে আপনি WorkManager-এর ফোরগ্রাউন্ড সার্ভিস ইমপ্লিমেন্টেশন ব্যবহার করতে পারেন।
উপযুক্ত সিস্টেম সংস্করণ যাচাই করার কোডের একটি উদাহরণ এখানে দেওয়া হলো:
কোটলিন
fun beginTask() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { scheduleDownloadFGSWorker(context) } else { scheduleDownloadUIDTJob(context) } } private fun scheduleDownloadUIDTJob(context: Context) { // build jobInfo val jobScheduler: JobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler jobScheduler.schedule(jobInfo) } private fun scheduleDownloadFGSWorker(context: Context) { val myWorkRequest = OneTimeWorkRequest.from(DownloadWorker::class.java) WorkManager.getInstance(context).enqueue(myWorkRequest) }
জাভা
public void beginTask() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { scheduleDownloadFGSWorker(context); } else { scheduleDownloadUIDTJob(context); } } private void scheduleDownloadUIDTJob(Context context) { // build jobInfo JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); jobScheduler.schedule(jobInfo); } private void scheduleDownloadFGSWorker(Context context) { OneTimeWorkRequest myWorkRequest = OneTimeWorkRequest.from(DownloadWorker.class); WorkManager.getInstance(context).enqueue(myWorkRequest) }
UIDT চাকরি বন্ধ করুন
ব্যবহারকারী এবং সিস্টেম উভয়ই ব্যবহারকারীর সূচনা স্থানান্তর কাজ বন্ধ করতে পারে।
ব্যবহারকারীর দ্বারা, টাস্ক ম্যানেজার থেকে
ব্যবহারকারী টাস্ক ম্যানেজারে প্রদর্শিত একটি ব্যবহারকারী-সূচিত ডেটা স্থানান্তর কাজ বন্ধ করতে পারে।
ব্যবহারকারী Stop চাপার মুহুর্তে, সিস্টেমটি নিম্নলিখিতগুলি করে:
- আপনার অ্যাপের প্রক্রিয়া অবিলম্বে বন্ধ করে দেয়, অন্যান্য সমস্ত চাকরি বা ফোরগ্রাউন্ড পরিষেবা চলমান সহ।
- কোনো চলমান কাজের জন্য
onStopJob()কল করে না। - ব্যবহারকারীর দৃশ্যমান কাজগুলিকে পুনঃনির্ধারণ করা থেকে আটকায়৷
এই কারণে, চাকরির জন্য পোস্ট করা বিজ্ঞপ্তিতে নিয়ন্ত্রণ প্রদান করার পরামর্শ দেওয়া হয় যাতে করে কাজটি বন্ধ করা যায় এবং পুনঃনির্ধারণ করা যায়।
মনে রাখবেন, বিশেষ পরিস্থিতিতে, টাস্ক ম্যানেজারে কাজের পাশে স্টপ বোতামটি প্রদর্শিত হয় না, বা কাজটি টাস্ক ম্যানেজারে মোটেও দেখানো হয় না।
সিস্টেম দ্বারা
নিয়মিত চাকরির বিপরীতে, ব্যবহারকারীর সূচনাকৃত ডেটা স্থানান্তর কাজগুলি অ্যাপ স্ট্যান্ডবাই বাকেট কোটা দ্বারা প্রভাবিত হয় না। যাইহোক, সিস্টেম এখনও কাজ বন্ধ করে যদি নিম্নলিখিত শর্তগুলির মধ্যে কোনটি ঘটে:
- একটি বিকাশকারী-সংজ্ঞায়িত সীমাবদ্ধতা আর পূরণ করা হয় না।
- সিস্টেমটি নির্ধারণ করে যে কাজটি ডেটা স্থানান্তরের কাজটি সম্পূর্ণ করার জন্য প্রয়োজনের চেয়ে বেশি সময় ধরে চলছে।
- সিস্টেমকে সিস্টেমের স্বাস্থ্যকে অগ্রাধিকার দিতে হবে এবং বর্ধিত তাপীয় অবস্থার কারণে চাকরি বন্ধ করতে হবে।
- কম ডিভাইসের মেমরির কারণে অ্যাপ প্রক্রিয়াটি বন্ধ হয়ে যায়।
কম ডিভাইস মেমরি ছাড়া অন্য কারণে সিস্টেম দ্বারা কাজটি বন্ধ হয়ে গেলে, সিস্টেমটি onStopJob() কল করে এবং সিস্টেমটি এমন একটি সময়ে কাজটি পুনরায় চেষ্টা করে যা সিস্টেমটি সর্বোত্তম বলে মনে করে। onStopJob() কল না করা হলেও আপনার অ্যাপটি ডেটা স্থানান্তরের অবস্থা বজায় রাখতে পারে এবং onStartJob() আবার কল করা হলে আপনার অ্যাপ এই অবস্থা পুনরুদ্ধার করতে পারে তা নিশ্চিত করুন।
ব্যবহারকারী-সূচিত ডেটা স্থানান্তর কাজের সময় নির্ধারণের জন্য শর্তাবলী অনুমোদিত
অ্যাপগুলি শুধুমাত্র ব্যবহারকারীর সূচনা করা ডেটা ট্রান্সফার কাজ শুরু করতে পারে যদি অ্যাপটি দৃশ্যমান উইন্ডোতে থাকে, অথবা যদি কিছু শর্ত পূরণ করা হয়:
- যদি একটি অ্যাপ ব্যাকগ্রাউন্ড থেকে ক্রিয়াকলাপগুলি চালু করতে পারে , তবে এটি ব্যাকগ্রাউন্ড থেকে ব্যবহারকারী-সূচিত ডেটা স্থানান্তর কাজগুলিও চালু করতে পারে।
- সাম্প্রতিক স্ক্রিনে বিদ্যমান টাস্কের ব্যাক স্ট্যাকে যদি কোনো অ্যাপের কোনো অ্যাক্টিভিটি থাকে, তাহলে এটি শুধুমাত্র ব্যবহারকারীর সূচিত ডেটা ট্রান্সফার কাজ চালানোর অনুমতি দেয় না।
যদি কাজটি এমন সময়ে চালানোর জন্য নির্ধারিত হয় যখন প্রয়োজনীয় শর্ত পূরণ করা হয় না, চাকরিটি ব্যর্থ হয় এবং একটি RESULT_FAILURE ত্রুটি কোড ফেরত দেয়।
ব্যবহারকারী-সূচিত ডেটা স্থানান্তর কাজের জন্য অনুমোদিত সীমাবদ্ধতা
কাজগুলোকে সর্বোত্তম গতিতে চালানোর জন্য, অ্যান্ড্রয়েড প্রতিটি কাজের ধরনের ওপর সীমাবদ্ধতা আরোপ করার সুবিধা দেয়। এই সীমাবদ্ধতাগুলো অ্যান্ড্রয়েড ১৩ থেকে উপলব্ধ।
দ্রষ্টব্য : নিম্নলিখিত সারণীটি শুধুমাত্র প্রতিটি কাজের ধরণের মধ্যে ভিন্ন সীমাবদ্ধতাগুলির তুলনা করে। সমস্ত সীমাবদ্ধতার জন্য জবশিডিউলার ডেভেলপার পৃষ্ঠা বা কাজের সীমাবদ্ধতা দেখুন।
নিম্নলিখিত সারণীটি একটি নির্দিষ্ট জব কনস্ট্রেইন্ট সমর্থনকারী বিভিন্ন ধরনের কাজের তালিকা, সেইসাথে WorkManager দ্বারা সমর্থিত জব কনস্ট্রেইন্টগুলোর সেট দেখায়। কোনো জব কনস্ট্রেইন্ট পদ্ধতির নাম অনুসারে সারণীটি ফিল্টার করতে, সারণীর আগে থাকা সার্চ বারটি ব্যবহার করুন।
ব্যবহারকারী-প্রবর্তিত ডেটা স্থানান্তর কাজের ক্ষেত্রে এই সীমাবদ্ধতাগুলো অনুমোদিত:
-
setBackoffCriteria(JobInfo.BACKOFF_POLICY_EXPONENTIAL) -
setClipData() -
setEstimatedNetworkBytes() -
setMinimumNetworkChunkBytes() -
setPersisted() -
setNamespace() -
setRequiredNetwork() -
setRequiredNetworkType() -
setRequiresBatteryNotLow() -
setRequiresCharging() -
setRequiresStorageNotLow()
টেস্টিং
নিম্নলিখিত তালিকাটি আপনার অ্যাপের কাজগুলি ম্যানুয়ালি পরীক্ষা করার কিছু পদক্ষেপ দেখায়:
- চাকরির আইডি পেতে, চাকরি তৈরির উপর সংজ্ঞায়িত মান পান।
অবিলম্বে একটি কাজ চালানোর জন্য, বা একটি বন্ধ কাজ পুনরায় চেষ্টা করার জন্য, একটি টার্মিনাল উইন্ডোতে নিম্নলিখিত কমান্ডটি চালান:
adb shell cmd jobscheduler run -f APP_PACKAGE_NAME JOB_ID
সিস্টেম জোর করে একটি কাজ বন্ধ করার অনুকরণ করতে (সিস্টেম স্বাস্থ্য বা কোটার বাইরের অবস্থার কারণে), একটি টার্মিনাল উইন্ডোতে নিম্নলিখিত কমান্ডটি চালান:
adb shell cmd jobscheduler timeout TEST_APP_PACKAGE TEST_JOB_ID
এছাড়াও দেখুন
অতিরিক্ত সম্পদ
ব্যবহারকারী-সূচিত ডেটা স্থানান্তর সম্পর্কে আরও তথ্যের জন্য, নিম্নলিখিত অতিরিক্ত সংস্থানগুলি দেখুন:
- ইউআইডিটি ইন্টিগ্রেশনের উপর কেস স্টাডি: ইউজার ইনিশিয়েটেড ডেটা ট্রান্সফার API ব্যবহার করে গুগল ম্যাপস ডাউনলোডের নির্ভরযোগ্যতা 10% উন্নত করেছে