APK এক্সপ্যানশন ফাইল

Google Play-এর নিয়ম অনুযায়ী ব্যবহারকারীর ডাউনলোড করা কম্প্রেস করা APK-এর সাইজ ১০০ এমবির বেশি হতে পারবে না। বেশিরভাগ অ্যাপের ক্ষেত্রে, অ্যাপের সব কোড ও অ্যাসেটের জন্য এটি যথেষ্ট স্পেস। তবে, কিছু অ্যাপের হাই-ফিডেলিটি গ্রাফিক্স, মিডিয়া ফাইল বা অন্যান্য বড় অ্যাসেটের জন্য আরও বেশি স্পেস প্রয়োজন হয়। আগে, আপনার অ্যাপের কম্প্রেস করা ডাউনলোড সাইজ ১০০ এমবির বেশি হলে, ব্যবহারকারী অ্যাপ খোলার সময় আপনাকে নিজেকেই অতিরিক্ত রিসোর্স হোস্ট ও ডাউনলোড করতে হত। অতিরিক্ত ফাইল হোস্ট ও পরিবেশন করা খরচসাপেক্ষ হতে পারে এবং ব্যবহারকারীর অভিজ্ঞতাও প্রায়শই আদর্শের চেয়ে কম হয়। এই প্রসেসটি আপনার জন্য সহজ এবং ব্যবহারকারীদের জন্য আরও আনন্দদায়ক করতে, Google Play আপনাকে দুটি বড় এক্সপ্যানশন ফাইল অ্যাটাচ করার অনুমতি দেয় যা আপনার APK-কে পরিপূরক করে।

Google Play আপনার অ্যাপের জন্য এক্সপ্যানশন ফাইল হোস্ট করে এবং কোনও খরচ ছাড়াই ডিভাইসে সেগুলি প্রদান করে। এক্সপ্যানশন ফাইল ডিভাইসের শেয়ার করা স্টোরেজ লোকেশনে (SD কার্ড বা USB-মাউন্ট করা পার্টিশন; "এক্সটার্নাল" স্টোরেজ নামেও পরিচিত) সেভ করা হয়, যেখান থেকে আপনার অ্যাপ সেগুলি অ্যাক্সেস করতে পারে। বেশিরভাগ ডিভাইসে, Google Play একই সময়ে এক্সপ্যানশন ফাইল ও APK ডাউনলোড করে, যাতে ব্যবহারকারী প্রথমবার অ্যাপ খোলার সময় প্রয়োজনীয় সবকিছুই অ্যাপে থাকে। তবে, কিছু ক্ষেত্রে, আপনার অ্যাপ চালু হওয়ার সময় Google Play থেকে ফাইল ডাউনলোড করতে হবে।

আপনি যদি এক্সপ্যানশন ফাইল ব্যবহার করা এড়াতে চান এবং আপনার অ্যাপের কম্প্রেস করা ডাউনলোড সাইজ ১০০ এমবির বেশি হয়, তাহলে আপনাকে Android App Bundles ব্যবহার করে অ্যাপ আপলোড করতে হবে, যা ৫০০ এমবি পর্যন্ত কম্প্রেস করা ডাউনলোড সাইজের অনুমতি দেয়। এছাড়াও, অ্যাপ বান্ডেল ব্যবহার করলে APK তৈরি ও সাইন করার কাজ Google Play-এর উপর ছেড়ে দেওয়া হয়, তাই ব্যবহারকারীরা শুধু আপনার অ্যাপ চালানোর জন্য প্রয়োজনীয় কোড ও রিসোর্স সহ অপ্টিমাইজ করা APK ডাউনলোড করেন। আপনাকে একাধিক APK বা এক্সপ্যানশন ফাইল তৈরি, সাইন ও ম্যানেজ করতে হয় না এবং ব্যবহারকারীরা ছোট, আরও অপ্টিমাইজ করা ফাইল ডাউনলোড করতে পারেন।

এক নজরে

Google Play Console ব্যবহার করে প্রতিবার APK আপলোড করার সময়, APK-তে এক বা দুটি এক্সপ্যানশন ফাইল যোগ করার বিকল্প আপনার কাছে থাকে। প্রতিটি ফাইল সর্বাধিক ২ জিবি সাইজের হতে পারে এবং আপনি যেকোনও ফর্ম্যাট বেছে নিতে পারেন, তবে ডাউনলোড করার সময় ব্যান্ডউইথ বাঁচাতে আমরা আপনাকে কম্প্রেস করা ফাইল ব্যবহার করার সাজেশন দিই। ধারণাগতভাবে, প্রতিটি এক্সপ্যানশন ফাইল আলাদা ভূমিকা পালন করে:

  • মেন এক্সপ্যানশন ফাইল হল আপনার অ্যাপের জন্য প্রয়োজনীয় অতিরিক্ত রিসোর্সের প্রাথমিক এক্সপ্যানশন ফাইল।
  • প্যাচ এক্সপ্যানশন ফাইলটি ঐচ্ছিক এবং এটি মেন এক্সপ্যানশন ফাইলে ছোটখাটো আপডেট করার জন্য ব্যবহার করা হয়।

আপনি নিজের ইচ্ছামতো দুটি এক্সপ্যানশন ফাইল ব্যবহার করতে পারলেও, আমরা সাজেস্ট করি যে মেন এক্সপ্যানশন ফাইল প্রাথমিক অ্যাসেট ডেলিভার করে এবং খুব কমই আপডেট করা উচিত; প্যাচ এক্সপ্যানশন ফাইল ছোট হওয়া উচিত এবং "প্যাচ ক্যারিয়ার" হিসেবে কাজ করা উচিত, প্রতিটি প্রধান রিলিজের সাথে বা প্রয়োজন অনুযায়ী আপডেট করা উচিত।

তবে, আপনার অ্যাপ আপডেট করার জন্য শুধুমাত্র নতুন প্যাচ এক্সপ্যানশন ফাইলের প্রয়োজন হলেও, আপনাকে ম্যানিফেস্টে আপডেট করা versionCode সহ নতুন APK আপলোড করতে হবে। (Play Console আপনাকে আগে থেকে থাকা APK-তে এক্সপ্যানশন ফাইল আপলোড করার অনুমতি দেয় না।)

মনে রাখবেন: প্যাচ এক্সপ্যানশন ফাইল ও মেন এক্সপ্যানশন ফাইল অর্থগতভাবে একই—আপনি প্রতিটি ফাইল যেভাবে খুশি ব্যবহার করতে পারেন।

ফাইলের নামের ফর্ম্যাট

আপনার আপলোড করা প্রতিটি এক্সপ্যানশন ফাইল আপনার বেছে নেওয়া যেকোনও ফর্ম্যাটের (ZIP, PDF, MP4 ইত্যাদি) হতে পারে। এছাড়াও, আপনি JOBB টুল ব্যবহার করে রিসোর্স ফাইলের সেট এবং সেই সেটের পরবর্তী প্যাচ এনক্যাপসুলেট ও এনক্রিপ্ট করতে পারবেন। ফাইলের ধরন যাই হোক না কেন, Google Play সেগুলিকে অস্বচ্ছ বাইনারি ব্লব হিসেবে বিবেচনা করে এবং নিম্নলিখিত স্কিম ব্যবহার করে ফাইলের নাম পরিবর্তন করে:

[main|patch].<expansion-version>.<package-name>.obb

এই স্কিমের তিনটি উপাদান রয়েছে:

main বা patch
ফাইলটি মেন নাকি প্যাচ এক্সপ্যানশন ফাইল তা উল্লেখ করে। প্রতিটি APK-এর জন্য শুধুমাত্র একটি মূল ফাইল এবং একটি প্যাচ ফাইল থাকতে পারে।
<expansion-version>
এটি একটি পূর্ণসংখ্যা যা APK-এর ভার্সন কোডের সাথে মেলে যার সাথে এক্সপ্যানশনটি প্রথম যুক্ত (এটি অ্যাপের android:versionCode মানের সাথে মেলে)।

"প্রথম" শব্দটির উপর জোর দেওয়া হয়েছে কারণ Play Console আপনাকে নতুন APK-এর সাথে আপলোড করা এক্সপ্যানশন ফাইল আবার ব্যবহার করার অনুমতি দিলেও, এক্সপ্যানশন ফাইলের নাম পরিবর্তন হয় না—আপনি প্রথমবার ফাইলটি আপলোড করার সময় যে ভার্সন প্রয়োগ করা হয়েছিল সেটিই থেকে যায়।

<package-name>
আপনার অ্যাপের জাভা-স্টাইল প্যাকেজের নাম।

যেমন, আপনার APK ভার্সন 314159 এবং প্যাকেজের নাম com.example.app হলে, আপনি মেন এক্সপ্যানশন ফাইল আপলোড করলে, ফাইলের নাম পরিবর্তন করে এটি করা হবে:

main.314159.com.example.app.obb

মেমরির জায়গা

Google Play কোনও ডিভাইসে আপনার এক্সপ্যানশন ফাইল ডাউনলোড করলে, এটি সিস্টেমের শেয়ার করা স্টোরেজ লোকেশনে সেভ করে। সঠিক আচরণ নিশ্চিত করতে, আপনাকে অবশ্যই এক্সপ্যানশন ফাইল মুছতে, সরাতে বা নাম পরিবর্তন করতে হবে না। আপনার অ্যাপকে Google Play থেকে ডাউনলোড করতে হলে, আপনাকে অবশ্যই একই লোকেশনে ফাইল সেভ করতে হবে।

getObbDir() পদ্ধতিটি নিম্নলিখিত ফর্মে আপনার এক্সপ্যানশন ফাইলের জন্য নির্দিষ্ট লোকেশন রিটার্ন করে:

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage> হল শেয়ার করা স্টোরেজ স্পেসের পাথ, যা getExternalStorageDirectory() থেকে উপলভ্য।
  • <package-name> হল আপনার অ্যাপের জাভা-স্টাইল প্যাকেজের নাম, যা getPackageName() থেকে উপলভ্য।

প্রতিটি অ্যাপের জন্য, এই ডিরেক্টরিতে কখনই দুটির বেশি এক্সপ্যানশন ফাইল থাকে না। একটি হল মেন এক্সপ্যানশন ফাইল এবং অন্যটি হল প্যাচ এক্সপ্যানশন ফাইল (প্রয়োজন হলে)। নতুন এক্সপ্যানশন ফাইলের মাধ্যমে আপনার অ্যাপ আপডেট করলে, আগের ভার্সন ওভাররাইট হয়ে যায়। Android 4.4 (API লেভেল ১৯) থেকে, অ্যাপ এক্সটার্নাল স্টোরেজ অনুমতি ছাড়াই OBB এক্সপ্যানশন ফাইল পড়তে পারে। তবে, Android 6.0 (API লেভেল ২৩) এবং তার পরবর্তী ভার্সনের কিছু ইমপ্লিমেন্টেশনে এখনও অনুমতি প্রয়োজন, তাই আপনাকে অ্যাপ ম্যানিফেস্টে READ_EXTERNAL_STORAGE অনুমতি ঘোষণা করতে হবে এবং রানটাইমে নিম্নলিখিতভাবে অনুমতি চাইতে হবে:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Android ভার্সন ৬ ও তার পরবর্তী ভার্সনের জন্য, রানটাইমে এক্সটার্নাল স্টোরেজ অ্যাক্সেস করার অনুমতি চাইতে হবে। তবে, Android-এর কিছু ইমপ্লিমেন্টেশনে OBB ফাইল পড়ার জন্য অনুমতির প্রয়োজন হয় না। এক্সটার্নাল স্টোরেজের অনুমতি চাওয়ার আগে কীভাবে রিড অ্যাক্সেস চেক করতে হয় তা নিম্নলিখিত কোড স্নিপেট থেকে জানতে পারবেন:

Kotlin

val obb = File(obb_filename)
var open_failed = false

try {
    BufferedReader(FileReader(obb)).also { br ->
        ReadObbFile(br)
    }
} catch (e: IOException) {
    open_failed = true
}

if (open_failed) {
    // request READ_EXTERNAL_STORAGE permission before reading OBB file
    ReadObbFileWithPermission()
}

জাভা

File obb = new File(obb_filename);
 boolean open_failed = false;

 try {
     BufferedReader br = new BufferedReader(new FileReader(obb));
     open_failed = false;
     ReadObbFile(br);
 } catch (IOException e) {
     open_failed = true;
 }

 if (open_failed) {
     // request READ_EXTERNAL_STORAGE permission before reading OBB file
     ReadObbFileWithPermission();
 }

আপনার এক্সপ্যানশন ফাইলের কন্টেন্ট আনপ্যাক করতে হলে, পরে OBB এক্সপ্যানশন ফাইল মুছে দেবেন না এবং একই ডিরেক্টরিতে আনপ্যাক করা ডেটা সেভ করবেন না। getExternalFilesDir()-এর মাধ্যমে নির্দিষ্ট করা ডিরেক্টরিতে আপনার আনপ্যাক করা ফাইল সেভ করা উচিত। তবে, সম্ভব হলে, এমন এক্সপ্যানশন ফাইল ফর্ম্যাট ব্যবহার করা সবচেয়ে ভালো যা আপনাকে ডেটা আনপ্যাক করার পরিবর্তে সরাসরি ফাইল থেকে পড়তে দেয়। যেমন, আমরা APK এক্সপ্যানশন জিপ লাইব্রেরি নামে একটি লাইব্রেরি প্রোজেক্ট প্রদান করেছি যা সরাসরি জিপ ফাইল থেকে আপনার ডেটা পড়ে।

সতর্কতা: APK ফাইলের মতো নয়, শেয়ার করা স্টোরেজে সেভ করা যেকোনও ফাইল ব্যবহারকারী এবং অন্যান্য অ্যাপ পড়তে পারে।

পরামর্শ: আপনি যদি কোনও জিপ ফাইলে মিডিয়া ফাইল প্যাকেজ করেন, তাহলে আপনি অফসেট এবং দৈর্ঘ্য কন্ট্রোল (যেমন MediaPlayer.setDataSource() এবং SoundPool.load()) সহ ফাইলগুলিতে মিডিয়া প্লেব্যাক কল ব্যবহার করতে পারেন, আপনার জিপ আনপ্যাক করার প্রয়োজন নেই। এটি কাজ করার জন্য, ZIP প্যাকেজ তৈরি করার সময় আপনাকে মিডিয়া ফাইলগুলিতে অতিরিক্ত কম্প্রেশন করা চলবে না। যেমন, zip টুল ব্যবহার করার সময়, আপনাকে -n বিকল্প ব্যবহার করে সেইসব ফাইলের সাফিক্স উল্লেখ করতে হবে যেগুলি কম্প্রেস করা উচিত নয়:
zip -n .mp4;.ogg main_expansion media_files

ডাউনলোড করার প্রসেস

বেশিরভাগ সময়, Google Play আপনার এক্সপ্যানশন ফাইল ডাউনলোড ও সেভ করে, একই সময়ে এটি ডিভাইসে APK ডাউনলোড করে। তবে, কিছু ক্ষেত্রে Google Play এক্সপ্যানশন ফাইল ডাউনলোড করতে পারে না অথবা ব্যবহারকারী আগে ডাউনলোড করা এক্সপ্যানশন ফাইল মুছে দিয়ে থাকতে পারেন। এইসব পরিস্থিতি সামাল দিতে, Google Play-এর দেওয়া URL ব্যবহার করে, মূল অ্যাক্টিভিটি শুরু হওয়ার সময় আপনার অ্যাপকে নিজেই ফাইল ডাউনলোড করতে পারতে হবে।

ডাউনলোড করার প্রসেসটি এক নজরে দেখে নিন:

  1. ব্যবহারকারী Google Play থেকে আপনার অ্যাপ ইনস্টল করার বিকল্প বেছে নেন।
  2. Google Play যদি এক্সপ্যানশন ফাইল ডাউনলোড করতে পারে (যা বেশিরভাগ ডিভাইসের ক্ষেত্রে হয়), তাহলে এটি APK-এর সাথে সেগুলি ডাউনলোড করে।

    Google Play যদি এক্সপ্যানশন ফাইল ডাউনলোড করতে না পারে, তাহলে এটি শুধুমাত্র APK ডাউনলোড করে।

  3. ব্যবহারকারী আপনার অ্যাপ চালু করলে, এক্সপ্যানশন ফাইল ডিভাইসে আগে থেকেই সেভ করা আছে কিনা তা আপনার অ্যাপকে অবশ্যই চেক করতে হবে।
    1. হ্যাঁ হলে, আপনার অ্যাপ ব্যবহারের জন্য রেডি।
    2. না হলে, আপনার অ্যাপকে Google Play থেকে HTTP-এর মাধ্যমে এক্সপ্যানশন ফাইল ডাউনলোড করতে হবে। আপনার অ্যাপকে Google Play-এর অ্যাপ লাইসেন্সিং পরিষেবা ব্যবহার করে Google Play ক্লায়েন্টকে অনুরোধ পাঠাতে হবে, যা প্রতিটি এক্সপ্যানশন ফাইলের নাম, ফাইলের সাইজ এবং URL সহ উত্তর দেয়। এই তথ্য ব্যবহার করে, আপনি ফাইল ডাউনলোড করে সঠিক স্টোরেজ লোকেশনে সেভ করতে পারবেন।

সতর্কতা: আপনার অ্যাপ চালু করার সময় ডিভাইসে ফাইল না থাকলে, Google Play থেকে এক্সপ্যানশন ফাইল ডাউনলোড করার জন্য প্রয়োজনীয় কোড যোগ করা অত্যন্ত গুরুত্বপূর্ণ। এক্সপ্যানশন ফাইল ডাউনলোড করা সম্পর্কে নিম্নলিখিত বিভাগে আলোচনা করা হয়েছে, আমরা আপনার জন্য একটি লাইব্রেরি উপলভ্য করেছি যা এই প্রসেসকে অনেক সহজ করে এবং আপনার থেকে ন্যূনতম পরিমাণ কোড সহ কোনও পরিষেবা থেকে ডাউনলোড করে।

ডেভেলপমেন্ট চেকলিস্ট

আপনার অ্যাপের সাথে এক্সপ্যানশন ফাইল ব্যবহার করার জন্য আপনাকে যেসব টাস্ক করতে হবে তার সারসংক্ষেপ এখানে দেওয়া হল:

  1. প্রথমে নির্ধারণ করুন যে আপনার অ্যাপের কম্প্রেস করা ডাউনলোড সাইজ ১০০ এমবির বেশি হতে হবে কিনা। স্পেস মূল্যবান এবং আপনার মোট ডাউনলোডের সাইজ যতটা সম্ভব ছোট রাখা উচিত। আপনার অ্যাপ যদি একাধিক স্ক্রিন ডেনসিটির জন্য আপনার গ্রাফিক অ্যাসেটের একাধিক ভার্সন প্রদান করার জন্য ১০০ এমবি-র বেশি ব্যবহার করে, তাহলে এর পরিবর্তে একাধিক APK প্রকাশ করার কথা বিবেচনা করুন, যেখানে প্রতিটি APK-তে শুধুমাত্র সেই অ্যাসেটগুলি থাকে যা এটি টার্গেট করা স্ক্রিনের জন্য প্রয়োজন। Google Play-তে প্রকাশ করার সময় সেরা ফলাফল পেতে, Android অ্যাপ বান্ডেল আপলোড করুন, যার মধ্যে আপনার অ্যাপের সব কম্পাইল করা কোড ও রিসোর্স থাকে, তবে APK তৈরি ও সাইন করার কাজ Google Play-এর জন্য স্থগিত রাখে।
  2. আপনার APK থেকে কোন অ্যাপ রিসোর্স আলাদা করতে হবে তা নির্ধারণ করুন এবং সেগুলিকে একটি ফাইলে প্যাকেজ করুন যাতে সেগুলি প্রধান এক্সপ্যানশন ফাইল হিসেবে ব্যবহার করা যায়।

    সাধারণত, মেন এক্সপ্যানশন ফাইল আপডেট করার সময় আপনার শুধু দ্বিতীয় প্যাচ এক্সপ্যানশন ফাইল ব্যবহার করা উচিত। তবে, আপনার রিসোর্স যদি মূল এক্সপ্যানশন ফাইলের জন্য ২ জিবি সীমা অতিক্রম করে, তাহলে আপনি বাকি অ্যাসেটের জন্য প্যাচ ফাইল ব্যবহার করতে পারেন।

  3. আপনার অ্যাপ এমনভাবে ডেভেলপ করুন যাতে এটি ডিভাইসের শেয়ার করা স্টোরেজ লোকেশনে আপনার এক্সপ্যানশন ফাইলের রিসোর্স ব্যবহার করে।

    মনে রাখবেন, আপনি এক্সপ্যানশন ফাইল মুছতে, সরাতে বা সেটির নাম পরিবর্তন করতে পারবেন না।

    আপনার অ্যাপের জন্য নির্দিষ্ট কোনও ফর্ম্যাটের প্রয়োজন না হলে, আমরা সাজেস্ট করি যে আপনি এক্সপ্যানশন ফাইলের জন্য জিপ ফাইল তৈরি করুন, তারপর APK এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার করে সেগুলি পড়ুন।

  4. আপনার অ্যাপের প্রধান অ্যাক্টিভিটিতে লজিক যোগ করুন যা চেক করে যে এক্সপ্যানশন ফাইলগুলি স্টার্ট-আপের সময় ডিভাইসে আছে কিনা। ফাইলগুলি ডিভাইসে না থাকলে, এক্সপ্যানশন ফাইলের URL-এর জন্য অনুরোধ করতে Google Play-এর অ্যাপ লাইসেন্সিং পরিষেবা ব্যবহার করুন, তারপর সেগুলি ডাউনলোড করে সেভ করুন।

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

    আপনি যদি লাইব্রেরি ব্যবহার না করে নিজের ডাউনলোড পরিষেবা তৈরি করেন, তাহলে মনে রাখবেন যে আপনাকে অবশ্যই এক্সপ্যানশন ফাইলের নাম পরিবর্তন করা চলবে না এবং সেগুলি সঠিক স্টোরেজ লোকেশনে সেভ করতে হবে।

আপনার অ্যাপ ডেভেলপমেন্ট সম্পূর্ণ হয়ে গেলে, আপনার এক্সপ্যানশন ফাইল পরীক্ষা করা সংক্রান্ত নির্দেশিকা অনুসরণ করুন।

নিয়ম ও সীমাবদ্ধতা

APK এক্সপ্যানশন ফাইল যোগ করার ফিচারটি আপনি Play Console ব্যবহার করে অ্যাপ আপলোড করার সময় উপলভ্য হয়। প্রথমবার আপনার অ্যাপ আপলোড করার সময় বা এক্সপ্যানশন ফাইল ব্যবহার করে এমন কোনও অ্যাপ আপডেট করার সময়, আপনাকে নিম্নলিখিত নিয়ম ও সীমাবদ্ধতা সম্পর্কে সচেতন থাকতে হবে:

  1. প্রতিটি এক্সপ্যানশন ফাইলের সাইজ ২ জিবির বেশি হতে পারবে না।
  2. Google Play থেকে আপনার এক্সপ্যানশন ফাইল ডাউনলোড করতে, ব্যবহারকারীকে Google Play থেকে আপনার অ্যাপ পেতে হবে। অ্যাপটি অন্য কোনও উপায়ে ইনস্টল করা হলে, Google Play আপনার এক্সপ্যানশন ফাইলের URL প্রদান করবে না।
  3. আপনার অ্যাপ থেকে ডাউনলোড করার সময়, Google Play প্রতিটি ফাইলের জন্য যে URL প্রদান করে, সেটি প্রতিটি ডাউনলোডের জন্য অনন্য এবং আপনার অ্যাপে দেওয়ার কিছুক্ষণের মধ্যেই প্রতিটি URL-এর মেয়াদ শেষ হয়ে যায়।
  4. আপনি যদি নতুন APK দিয়ে আপনার অ্যাপ আপডেট করেন বা একই অ্যাপের জন্য একাধিক APK আপলোড করেন, তাহলে আপনি আগের APK-এর জন্য আপলোড করা এক্সপ্যানশন ফাইল বেছে নিতে পারবেন। এক্সপ্যানশন ফাইলের নাম পরিবর্তন হয় না—এটি সেই APK-এর মাধ্যমে পাওয়া ভার্সনটিই রেখে দেয় যার সাথে ফাইলটি মূলত যুক্ত ছিল।
  5. আপনি যদি বিভিন্ন ডিভাইসের জন্য বিভিন্ন এক্সপ্যানশন ফাইল প্রদান করার জন্য একাধিক APK-এর সাথে এক্সপ্যানশন ফাইল ব্যবহার করেন, তাহলে আপনাকে এখনও প্রতিটি ডিভাইসের জন্য আলাদা আলাদা APK আপলোড করতে হবে যাতে একটি অনন্য versionCode ভ্যালু প্রদান করা যায় এবং প্রতিটি APK-এর জন্য আলাদা আলাদা ফিল্টার ঘোষণা করা যায়।
  6. আপনি শুধু এক্সপ্যানশন ফাইল পরিবর্তন করে আপনার অ্যাপে কোনও আপডেট ইস্যু করতে পারবেন না—আপনার অ্যাপ আপডেট করতে আপনাকে অবশ্যই একটি নতুন APK আপলোড করতে হবে। আপনার পরিবর্তনগুলি যদি শুধুমাত্র আপনার এক্সপ্যানশন ফাইলের অ্যাসেট সম্পর্কিত হয়, তাহলে আপনি শুধু versionCode (এবং সম্ভবত versionName) পরিবর্তন করে আপনার APK আপডেট করতে পারেন।
  7. আপনার obb/ ডিরেক্টরিতে অন্য ডেটা সেভ করবেন না। আপনাকে কিছু ডেটা আনপ্যাক করতে হলে, getExternalFilesDir()-এর মাধ্যমে নির্দিষ্ট করা লোকেশনে এটি সেভ করুন।
  8. .obb এক্সপ্যানশন ফাইল মুছবেন না বা সেটির নাম পরিবর্তন করবেন না (যদি না আপনি আপডেট করছেন)। এটি করলে Google Play (বা আপনার অ্যাপ) বারবার এক্সপ্যানশন ফাইল ডাউনলোড করবে।
  9. ম্যানুয়ালি এক্সপ্যানশন ফাইল আপডেট করার সময়, আপনাকে অবশ্যই আগের এক্সপ্যানশন ফাইল মুছে ফেলতে হবে।

এক্সপ্যানশন ফাইল ডাউনলোড করা হচ্ছে

বেশিরভাগ ক্ষেত্রে, Google Play আপনার ডিভাইসে APK ইনস্টল বা আপডেট করার সময় একই সাথে এক্সপ্যানশন ফাইল ডাউনলোড ও সেভ করে। এইভাবে, আপনার অ্যাপ প্রথমবার লঞ্চ করার সময় এক্সপ্যানশন ফাইল উপলভ্য থাকে। তবে, কিছু ক্ষেত্রে আপনার অ্যাপকে অবশ্যই Google Play-এর অ্যাপ লাইসেন্সিং পরিষেবা থেকে পাওয়া উত্তরে আপনাকে দেওয়া URL থেকে অনুরোধ করে এক্সপ্যানশন ফাইল নিজেই ডাউনলোড করতে হবে।

আপনার এক্সপ্যানশন ফাইল ডাউনলোড করার জন্য যে বেসিক লজিক প্রয়োজন তা নিচে দেওয়া হল:

  1. আপনার অ্যাপ চালু হলে, শেয়ার করা স্টোরেজ লোকেশনে (Android/obb/<package-name>/ ডিরেক্টরিতে) এক্সপ্যানশন ফাইল খুঁজুন।
    1. এক্সপ্যানশন ফাইল থাকলে, আপনি সব সেট করে ফেলেছেন এবং আপনার অ্যাপ কাজ করা চালিয়ে যেতে পারবে।
    2. এক্সপ্যানশন ফাইল সেখানে না থাকলে:
      1. আপনার অ্যাপের এক্সপ্যানশন ফাইলের নাম, সাইজ ও URL পেতে Google Play-এর অ্যাপ লাইসেন্সিং ব্যবহার করে অনুরোধ করুন।
      2. এক্সপ্যানশন ফাইল ডাউনলোড ও সেভ করতে Google Play-এর দেওয়া URL ব্যবহার করুন। আপনাকে শেয়ার করা স্টোরেজ লোকেশনে (Android/obb/<package-name>/) ফাইল সেভ করতেই হবে এবং Google Play-এর উত্তরে দেওয়া ফাইলের সঠিক নাম ব্যবহার করতে হবে।

        মনে রাখবেন: আপনার এক্সপ্যানশন ফাইলের জন্য Google Play যে URL প্রদান করে, সেটি প্রতিটি ডাউনলোডের জন্য অনন্য এবং আপনার অ্যাপে দেওয়ার কিছুক্ষণের মধ্যেই প্রতিটি URL-এর মেয়াদ শেষ হয়ে যায়।

আপনার অ্যাপটি ফ্রি (দামযুক্ত অ্যাপ নয়) হলে, আপনি সম্ভবত অ্যাপ লাইসেন্সিং পরিষেবা ব্যবহার করেননি। এটি মূলত আপনার অ্যাপের জন্য লাইসেন্সিং নীতি এনফোর্স করতে এবং ব্যবহারকারীর কাছে আপনার অ্যাপ ব্যবহার করার অধিকার আছে কিনা তা নিশ্চিত করার জন্য ডিজাইন করা হয়েছে (তারা Google Play-তে এর জন্য যথাযথভাবে অর্থ প্রদান করেছে)। এক্সপ্যানশন ফাইল কার্যকারিতা সহজতর করার জন্য, লাইসেন্সিং পরিষেবাটি আপনার অ্যাপে একটি প্রতিক্রিয়া প্রদান করার জন্য উন্নত করা হয়েছে যার মধ্যে Google Play-তে হোস্ট করা আপনার অ্যাপের এক্সপ্যানশন ফাইলের URL অন্তর্ভুক্ত রয়েছে। তাই, আপনার অ্যাপ ব্যবহারকারীদের জন্য ফ্রি হলেও, APK এক্সপ্যানশন ফাইল ব্যবহার করার জন্য আপনাকে লাইসেন্স যাচাইকরণ লাইব্রেরি (LVL) অন্তর্ভুক্ত করতে হবে। অবশ্যই, আপনার অ্যাপ ফ্রি হলে, আপনাকে লাইসেন্স যাচাইকরণ এনফোর্স করতে হবে না—আপনার শুধু লাইব্রেরি প্রয়োজন যা আপনার এক্সপ্যানশন ফাইলের URL ফিরিয়ে দেওয়ার অনুরোধ করে।

মনে রাখবেন: আপনার অ্যাপ ফ্রি হোক বা না হোক, ব্যবহারকারী Google Play থেকে আপনার অ্যাপ ডাউনলোড করলে তবেই Google Play এক্সপ্যানশন ফাইলের URL দেখায়।

LVL ছাড়াও, আপনার এমন একটি কোড সেট প্রয়োজন যা HTTP কানেকশনের মাধ্যমে এক্সপ্যানশন ফাইল ডাউনলোড করে এবং ডিভাইসের শেয়ার করা স্টোরেজে সঠিক লোকেশনে সেগুলি সেভ করে। আপনার অ্যাপে এই পদ্ধতি তৈরি করার সময়, আপনাকে বেশ কিছু সমস্যা বিবেচনা করতে হবে:

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

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

Google Play URL ব্যবহার করে এক্সপ্যানশন ফাইল ডাউনলোড করার জন্য আপনি যদি নিজের সমাধান ডেভেলপ করতে চান, তাহলে লাইসেন্সের অনুরোধ করার জন্য আপনাকে অবশ্যই অ্যাপ লাইসেন্সিং সংক্রান্ত ডকুমেন্টেশন অনুসরণ করতে হবে, তারপর এক্সপ্যানশন ফাইলের নাম, সাইজ এবং URL রেসপন্স এক্সট্রা থেকে ফিরিয়ে আনতে হবে। আপনার লাইসেন্সিং নীতি হিসেবে APKExpansionPolicy ক্লাস (লাইসেন্স যাচাইকরণ লাইব্রেরিতে অন্তর্ভুক্ত) ব্যবহার করা উচিত, যা লাইসেন্সিং পরিষেবা থেকে এক্সপ্যানশন ফাইলের নাম, সাইজ এবং URL ক্যাপচার করে..

ডাউনলোডার লাইব্রেরি সম্পর্কে

আপনার অ্যাপের সাথে APK এক্সপ্যানশন ফাইল ব্যবহার করতে এবং আপনার পক্ষ থেকে ন্যূনতম প্রচেষ্টার মাধ্যমে ব্যবহারকারীকে সেরা অভিজ্ঞতা প্রদান করতে, আমরা আপনাকে Google Play APK এক্সপ্যানশন লাইব্রেরি প্যাকেজে অন্তর্ভুক্ত ডাউনলোডার লাইব্রেরি ব্যবহার করার সাজেশন দিই। এই লাইব্রেরি ব্যাকগ্রাউন্ড পরিষেবায় আপনার এক্সপ্যানশন ফাইল ডাউনলোড করে, ডাউনলোডের স্ট্যাটাস সহ ব্যবহারকারীকে বিজ্ঞপ্তি দেখায়, নেটওয়ার্ক কানেক্টিভিটি না থাকলে তা ম্যানেজ করে, সম্ভব হলে ডাউনলোড আবার শুরু করে এবং আরও অনেক কিছু করে।

ডাউনলোডার লাইব্রেরি ব্যবহার করে এক্সপ্যানশন ফাইল ডাউনলোড করার সুবিধা যোগ করতে, আপনাকে শুধু এগুলি করতে হবে:

  • একটি বিশেষ Service সাবক্লাস এবং BroadcastReceiver সাবক্লাস এক্সটেন্ড করুন, যার প্রতিটির জন্য আপনার থেকে মাত্র কয়েকটি লাইনের কোড প্রয়োজন।
  • আপনার মূল অ্যাক্টিভিটিতে কিছু লজিক যোগ করুন যা এক্সপ্যানশন ফাইলগুলি ইতিমধ্যেই ডাউনলোড করা হয়েছে কিনা তা পরীক্ষা করে এবং যদি না হয়, ডাউনলোড প্রক্রিয়া শুরু করে এবং একটি প্রগ্রেস UI প্রদর্শন করে।
  • আপনার মূল অ্যাক্টিভিটিতে কয়েকটি পদ্ধতি সহ কলব্যাক ইন্টারফেস প্রয়োগ করুন যা ডাউনলোডের প্রগ্রেস সম্পর্কে আপডেট পায়।

ডাউনলোডার লাইব্রেরি ব্যবহার করে কীভাবে আপনার অ্যাপ সেট-আপ করবেন তা নিম্নলিখিত বিভাগে ব্যাখ্যা করা হয়েছে।

ডাউনলোডার লাইব্রেরি ব্যবহার করার জন্য প্রস্তুত করা হচ্ছে

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

প্রথমে, Android SDK Manager (টুল > SDK Manager) খুলুন এবং অ্যাপিয়ারেন্স ও আচরণ > সিস্টেম সেটিংস > Android SDK বিকল্পের অধীনে, SDK টুল ট্যাব বেছে নিয়ে এগুলি ডাউনলোড করুন:

  • Google Play লাইসেন্সিং লাইব্রেরি প্যাকেজ
  • Google Play APK এক্সপ্যানশন লাইব্রেরি প্যাকেজ

লাইসেন্স যাচাইকরণ লাইব্রেরি এবং ডাউনলোডার লাইব্রেরির জন্য একটি নতুন লাইব্রেরি মডিউল তৈরি করুন। প্রতিটি লাইব্রেরির জন্য:

  1. ফাইল > নতুন > নতুন মডিউল বিকল্প বেছে নিন।
  2. নতুন মডিউল তৈরি করুন উইন্ডোতে, Android লাইব্রেরি বেছে নিন, এবং তারপরে পরবর্তী বিকল্প বেছে নিন।
  3. "Google Play লাইসেন্স লাইব্রেরি" এবং "Google Play ডাউনলোডার লাইব্রেরি"-এর মতো অ্যাপ/লাইব্রেরির নাম উল্লেখ করুন, ন্যূনতম SDK লেভেল বেছে নিন, তারপর শেষ করুন বিকল্প বেছে নিন।
  4. ফাইল > প্রোজেক্ট স্ট্রাকচার বেছে নিন।
  5. প্রপার্টি ট্যাব বেছে নিন এবং লাইব্রেরি রিপোজিটরি-তে, <sdk>/extras/google/ ডিরেক্টরি থেকে লাইব্রেরি লিখুন (লাইসেন্স যাচাইকরণ লাইব্রেরির জন্য play_licensing/ অথবা ডাউনলোডার লাইব্রেরির জন্য play_apk_expansion/downloader_library/)।
  6. নতুন মডিউল তৈরি করতে ঠিক আছে বিকল্প বেছে নিন।

মনে রাখবেন: ডাউনলোডার লাইব্রেরি লাইসেন্স যাচাইকরণ লাইব্রেরির উপর নির্ভর করে। ডাউনলোডার লাইব্রেরির প্রোজেক্ট প্রপার্টিতে লাইসেন্স যাচাইকরণ লাইব্রেরি যোগ করতে ভুলবেন না।

অথবা, কমান্ড লাইন থেকে, লাইব্রেরি অন্তর্ভুক্ত করতে আপনার প্রোজেক্ট আপডেট করুন:

  1. <sdk>/tools/ ডিরেক্টরিতে ডিরেক্টরি পরিবর্তন করুন।
  2. আপনার প্রোজেক্টে LVL এবং ডাউনলোডার লাইব্রেরি, দুটিই যোগ করতে, --library বিকল্পের সাথে android update project এক্সিকিউট করুন। যেমন:
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

আপনার অ্যাপে লাইসেন্স যাচাইকরণ লাইব্রেরি এবং ডাউনলোডার লাইব্রেরি, দুটিই যোগ করা থাকলে, Google Play থেকে এক্সপ্যানশন ফাইল ডাউনলোড করার ক্ষমতা আপনি দ্রুত ইন্টিগ্রেট করতে পারবেন। এক্সপ্যানশন ফাইলের জন্য আপনি যে ফর্ম্যাট বেছে নেন এবং শেয়ার করা স্টোরেজ থেকে আপনি কীভাবে সেগুলি পড়েন তা হল একটি আলাদা ইমপ্লিমেন্টেশন যা আপনার অ্যাপের প্রয়োজনের উপর ভিত্তি করে আপনার বিবেচনা করা উচিত।

পরামর্শ: Apk এক্সপ্যানশন প্যাকেজে একটি স্যাম্পেল অ্যাপ থাকে যা দেখায় যে কীভাবে কোনও অ্যাপে ডাউনলোডার লাইব্রেরি ব্যবহার করতে হয়। স্যাম্পেলটি Apk এক্সপ্যানশন প্যাকেজে উপলভ্য তৃতীয় লাইব্রেরি ব্যবহার করে, যাকে APK এক্সপ্যানশন জিপ লাইব্রেরি বলা হয়। আপনি যদি এক্সপ্যানশন ফাইলের জন্য জিপ ফাইল ব্যবহার করার পরিকল্পনা করেন, তাহলে আমরা সাজেস্ট করি যে আপনি নিজের অ্যাপে APK এক্সপ্যানশন জিপ লাইব্রেরিও যোগ করুন। আরও তথ্যের জন্য, APK এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার করা সম্পর্কে নিচের বিভাগটি দেখুন।

ব্যবহারকারীর অনুমতি ঘোষণা করা

এক্সপ্যানশন ফাইল ডাউনলোড করার জন্য, ডাউনলোডার লাইব্রেরির বেশ কিছু অনুমতি প্রয়োজন যা আপনাকে অ্যাপের ম্যানিফেস্ট ফাইলে ঘোষণা করতে হবে। সেগুলি হল:

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

মনে রাখবেন: ডিফল্ট হিসেবে, ডাউনলোডার লাইব্রেরির জন্য API লেভেল ৪ প্রয়োজন, কিন্তু APK এক্সপ্যানশন জিপ লাইব্রেরির জন্য API লেভেল ৫ প্রয়োজন।

ডাউনলোডার পরিষেবা প্রয়োগ করা

ব্যাকগ্রাউন্ডে ডাউনলোড করার জন্য, ডাউনলোডার লাইব্রেরি তার নিজস্ব Service সাবক্লাস প্রদান করে, যাকে DownloaderService বলা হয়। আপনাকে এটি এক্সটেন্ড করতে হবে। আপনার জন্য এক্সপ্যানশন ফাইল ডাউনলোড করার পাশাপাশি, DownloaderService আরও যা যা করে:

  • একটি BroadcastReceiver রেজিস্টার করে যা ডিভাইসের নেটওয়ার্ক কানেক্টিভিটিতে (CONNECTIVITY_ACTION ব্রডকাস্ট) পরিবর্তন লক্ষ্য করে যাতে প্রয়োজন হলে (যেমন কানেক্টিভিটি না থাকলে) ডাউনলোড পজ করা যায় এবং সম্ভব হলে (কানেক্টিভিটি ফিরে এলে) ডাউনলোড আবার শুরু করা যায়।
  • পরিষেবা বন্ধ হয়ে গেলে, ডাউনলোড আবার করার জন্য RTC_WAKEUP অ্যালার্ম শিডিউল করে।
  • কাস্টম Notification তৈরি করে যা ডাউনলোডের প্রগ্রেস এবং কোনও সমস্যা বা স্ট্যাটাস পরিবর্তন দেখায়।
  • আপনার অ্যাপকে ম্যানুয়ালি ডাউনলোড পজ ও আবার চালু করার অনুমতি দেয়।
  • এক্সপ্যানশন ফাইল ডাউনলোড করার আগে, শেয়ার করা স্টোরেজ মাউন্ট করা ও উপলভ্য আছে কিনা, ফাইলগুলি আগে থেকেই আছে কিনা এবং পর্যাপ্ত জায়গা আছে কিনা তা যাচাই করে। তারপর, এর মধ্যে কোনওটি সত্য না হলে ব্যবহারকারীকে বিজ্ঞপ্তি পাঠায়।

আপনাকে শুধু অ্যাপে একটি ক্লাস তৈরি করতে হবে যা DownloaderService ক্লাসকে এক্সটেন্ড করে এবং নির্দিষ্ট অ্যাপের বিবরণ প্রদান করার জন্য তিনটি পদ্ধতি ওভাররাইড করে:

getPublicKey()
এটি অবশ্যই একটি স্ট্রিং রিটার্ন করবে যা আপনার প্রকাশক অ্যাকাউন্টের জন্য Base64-এনকোড করা RSA সর্বজনীন কী, যা Play Console-এর প্রোফাইল পৃষ্ঠা থেকে উপলভ্য (লাইসেন্সিংয়ের জন্য সেট-আপ করা দেখুন)।
getSALT()
এটি অবশ্যই র‍্যান্ডম বাইটের একটি অ্যারে রিটার্ন করবে যা লাইসেন্সিং Policy Obfuscator তৈরি করতে ব্যবহার করে। সল্ট নিশ্চিত করে যে আপনার অস্পষ্ট SharedPreferences ফাইল যাতে আপনার লাইসেন্সিং ডেটা সেভ করা আছে তা অনন্য এবং খুঁজে পাওয়া যাবে না।
getAlarmReceiverClassName()
এটি আপনার অ্যাপে BroadcastReceiver-এর ক্লাস নাম অবশ্যই ফিরিয়ে দেবে যা ডাউনলোড আবার শুরু করা উচিত (যা ডাউনলোডার পরিষেবা অপ্রত্যাশিতভাবে বন্ধ হয়ে গেলে হতে পারে) তা নির্দেশ করে অ্যালার্ম পাবে।

যেমন, এখানে DownloaderService-এর সম্পূর্ণ প্রয়োগ দেখানো হয়েছে:

Kotlin

// You must use the public key belonging to your publisher account
const val BASE64_PUBLIC_KEY = "YourLVLKey"
// You should also modify this salt
val SALT = byteArrayOf(
        1, 42, -12, -1, 54, 98, -100, -12, 43, 2,
        -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
)

class SampleDownloaderService : DownloaderService() {

    override fun getPublicKey(): String = BASE64_PUBLIC_KEY

    override fun getSALT(): ByteArray = SALT

    override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name
}

জাভা

public class SampleDownloaderService extends DownloaderService {
    // You must use the public key belonging to your publisher account
    public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
    // You should also modify this salt
    public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
            -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }

    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }
}

বিজ্ঞপ্তি: আপনার প্রকাশক অ্যাকাউন্টের সর্বজনীন কী হিসেবে BASE64_PUBLIC_KEY ভ্যালু আপডেট করতে হবে। আপনি Developer Console-এ আপনার প্রোফাইল তথ্যের মধ্যে কী খুঁজে পাবেন। আপনার ডাউনলোড পরীক্ষা করার সময়ও এটি প্রয়োজন।

আপনার ম্যানিফেস্ট ফাইলে পরিষেবাটি ঘোষণা করতে ভুলবেন না:

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

অ্যালার্ম রিসিভার প্রয়োগ করা হচ্ছে

ফাইল ডাউনলোডের প্রোগ্রেস মনিটর করতে এবং প্রয়োজন হলে ডাউনলোড রিস্টার্ট করতে, DownloaderService একটি RTC_WAKEUP অ্যালার্ম শিডিউল করে যা আপনার অ্যাপের BroadcastReceiver-এ একটি Intent ডেলিভার করে। ডাউনলোডার লাইব্রেরি থেকে API কল করার জন্য আপনাকে অবশ্যই BroadcastReceiver সংজ্ঞায়িত করতে হবে যা ডাউনলোডের স্ট্যাটাস চেক করে এবং প্রয়োজন হলে এটি রিস্টার্ট করে।

DownloaderClientMarshaller.startDownloadServiceIfRequired() কল করার জন্য আপনাকে শুধু onReceive() পদ্ধতি ওভাররাইড করতে হবে।

যেমন:

Kotlin

class SampleAlarmReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context,
                    intent,
                    SampleDownloaderService::class.java
            )
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
    }
}

জাভা

public class SampleAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
                intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

মনে রাখবেন যে এটি সেই ক্লাস যার জন্য আপনার পরিষেবার getAlarmReceiverClassName() পদ্ধতিতে আপনাকে অবশ্যই নাম ফেরত দিতে হবে (আগের বিভাগটি দেখুন)।

আপনার ম্যানিফেস্ট ফাইলে রিসিভার ঘোষণা করতে ভুলবেন না:

<app ...>
    <receiver android:name=".SampleAlarmReceiver" />
    ...
</app>

ডাউনলোড শুরু হচ্ছে

আপনার অ্যাপের প্রধান অ্যাক্টিভিটি (যেটি আপনার লঞ্চার আইকন দ্বারা শুরু হয়) এক্সপ্যানশন ফাইলগুলি ইতিমধ্যেই ডিভাইসে আছে কিনা তা যাচাই করার জন্য এবং না থাকলে ডাউনলোড শুরু করার জন্য দায়ী।

ডাউনলোডার লাইব্রেরি ব্যবহার করে ডাউনলোড শুরু করার জন্য নিম্নলিখিত পদ্ধতিগুলি প্রয়োজন:

  1. ফাইল ডাউনলোড হয়েছে কিনা তা চেক করুন।

    এই প্রসেসে সাহায্য করার জন্য ডাউনলোডার লাইব্রেরিতে Helper ক্লাসে কিছু API অন্তর্ভুক্ত থাকে:

    • getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
    • doesFileExist(Context c, String fileName, long fileSize)

    যেমন, APK এক্সপ্যানশন প্যাকেজে দেওয়া স্যাম্পেল অ্যাপ, এক্সপ্যানশন ফাইল ডিভাইসে আগে থেকেই আছে কিনা তা চেক করতে অ্যাক্টিভিটির onCreate() পদ্ধতিতে নিম্নলিখিত পদ্ধতি কল করে:

    Kotlin

    fun expansionFilesDelivered(): Boolean {
        xAPKS.forEach { xf ->
            Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName ->
                if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                    return false
            }
        }
        return true
    }

    জাভা

    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase,
                xf.fileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                return false;
        }
        return true;
    }

    এই ক্ষেত্রে, প্রতিটি XAPKFile অবজেক্টে পরিচিত এক্সপ্যানশন ফাইলের ভার্সন নম্বর ও ফাইলের সাইজ এবং এটি মূল এক্সপ্যানশন ফাইল কিনা তা বোঝাতে বুলিয়ান থাকে। (বিস্তারিত জানতে স্যাম্পেল অ্যাপের SampleDownloaderActivity ক্লাস দেখুন।)

    এই পদ্ধতি false রিটার্ন করলে, অ্যাপকে অবশ্যই ডাউনলোড শুরু করতে হবে।

  2. DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass) স্ট্যাটিক মেথড কল করে ডাউনলোড শুরু করুন।

    এই পদ্ধতিতে নিম্নলিখিত প্যারামিটার ব্যবহার করা হয়:

    • context: আপনার অ্যাপের Context।
    • notificationClient: আপনার প্রধান অ্যাক্টিভিটি শুরু করার জন্য PendingIntent। ডাউনলোডের প্রোগ্রেস দেখানোর জন্য DownloaderService যে Notification তৈরি করে, এটি সেখানে ব্যবহার করা হয়। ব্যবহারকারী বিজ্ঞপ্তি বেছে নিলে, সিস্টেম আপনার দেওয়া PendingIntent চালু করে এবং অ্যাক্টিভিটি খুলে ডাউনলোড প্রগ্রেস দেখায় (সাধারণত যে অ্যাক্টিভিটি ডাউনলোড শুরু করেছে সেটিই)।
    • serviceClass: DownloaderService-এর আপনার ইমপ্লিমেন্টেশনের জন্য Class অবজেক্ট, পরিষেবা শুরু করতে এবং প্রয়োজন হলে ডাউনলোড শুরু করতে প্রয়োজন।

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

    • NO_DOWNLOAD_REQUIRED: ফাইল আগে থেকেই থাকলে বা ডাউনলোড প্রসেস আগে থেকেই চালু থাকলে, এটি রিটার্ন করা হয়।
    • LVL_CHECK_REQUIRED: এক্সপ্যানশন ফাইল URL পেতে লাইসেন্স যাচাইকরণের প্রয়োজন হলে, এটি রিটার্ন করা হয়।
    • DOWNLOAD_REQUIRED: এক্সপ্যানশন ফাইলের URL আগে থেকেই জানা থাকলে, কিন্তু ডাউনলোড করা না হলে এটি রিটার্ন করা হয়।

    LVL_CHECK_REQUIRED এবং DOWNLOAD_REQUIRED-এর আচরণ মূলত একই এবং সাধারণত আপনাকে এগুলি নিয়ে চিন্তা করতে হবে না। আপনার প্রধান অ্যাক্টিভিটি যা startDownloadServiceIfRequired()-কে কল করে, সেখানে আপনি সহজেই চেক করতে পারবেন যে উত্তরটি NO_DOWNLOAD_REQUIRED কিনা। উত্তর NO_DOWNLOAD_REQUIRED ছাড়া অন্য কিছু হলে, ডাউনলোডার লাইব্রেরি ডাউনলোড শুরু করে এবং ডাউনলোডের প্রগ্রেস দেখানোর জন্য আপনার অ্যাক্টিভিটি UI আপডেট করা উচিত (পরবর্তী ধাপ দেখুন)। উত্তর যদি NO_DOWNLOAD_REQUIRED হয়, তাহলে ফাইল উপলভ্য আছে এবং আপনার অ্যাপ শুরু হতে পারে।

    যেমন:

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            val pendingIntent =
                    // Build an Intent to start this activity from the Notification
                    Intent(this, MainActivity::class.java).apply {
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }.let { notifierIntent ->
                        PendingIntent.getActivity(
                                this,
                                0,
                                notifierIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                    }
    
    
            // Start the download service (if required)
            val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp() // Expansion files are available, start the app
    }

    জাভা

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent = new Intent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return;
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp(); // Expansion files are available, start the app
    }
  3. startDownloadServiceIfRequired() পদ্ধতি NO_DOWNLOAD_REQUIRED ছাড়া অন্য কিছু রিটার্ন করলে, DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) কল করে IStub-এর ইনস্ট্যান্স তৈরি করুন। IStub আপনার অ্যাক্টিভিটি ও ডাউনলোডার পরিষেবার মধ্যে একটি বাইন্ডিং প্রদান করে যাতে আপনার অ্যাক্টিভিটি ডাউনলোডের প্রগ্রেস সম্পর্কে কলব্যাক পায়।

    CreateStub() কল করে আপনার IStub ইনস্ট্যান্টিয়েট করতে, আপনাকে অবশ্যই এটি পাস করতে হবে IDownloaderClient ইন্টারফেস এবং আপনার DownloaderService-এর একটি ইমপ্লিমেন্টেশন ইমপ্লিমেন্টেশন। ডাউনলোডের প্রগ্রেস রিসিভ করা সংক্রান্ত পরবর্তী বিভাগে IDownloaderClient ইন্টারফেস নিয়ে আলোচনা করা হয়েছে, যা সাধারণত আপনার Activity ক্লাসে প্রয়োগ করা উচিত যাতে ডাউনলোডের স্ট্যাটাস পরিবর্তন হলে আপনি অ্যাক্টিভিটি UI আপডেট করতে পারেন।

    startDownloadServiceIfRequired() ডাউনলোড শুরু করার পরে, আপনার অ্যাক্টিভিটির onCreate() মেথড চলাকালীন IStub ইনস্ট্যান্টিয়েট করতে CreateStub() কল করার জন্য আমরা সাজেস্ট করি।

    যেমন, onCreate()-এর জন্য আগের কোড স্যাম্পেলে, আপনি startDownloadServiceIfRequired() ফলাফলে এইভাবে উত্তর দিতে পারেন:

    Kotlin

            // Start the download service (if required)
            val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this@MainActivity,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub =
                        DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java)
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui)
                return
            }

    জাভা

            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                        SampleDownloaderService.class);
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui);
                return;
            }

    onCreate() পদ্ধতি রিটার্ন করার পরে, আপনার অ্যাক্টিভিটি onResume()-এ একটি কল পায়, যেখানে আপনাকে তারপর IStub-এ connect() কল করতে হবে, এটি আপনার অ্যাপের Context পাস করে। বিপরীতভাবে, আপনার অ্যাক্টিভিটির onStop() কলব্যাকে disconnect() কল করা উচিত।

    Kotlin

    override fun onResume() {
        downloaderClientStub?.connect(this)
        super.onResume()
    }
    
    override fun onStop() {
        downloaderClientStub?.disconnect(this)
        super.onStop()
    }

    জাভা

    @Override
    protected void onResume() {
        if (null != downloaderClientStub) {
            downloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protected void onStop() {
        if (null != downloaderClientStub) {
            downloaderClientStub.disconnect(this);
        }
        super.onStop();
    }

    IStub-এ connect() কল করলে আপনার অ্যাক্টিভিটি DownloaderService-এর সাথে বাইন্ড হয়ে যায় যাতে আপনার অ্যাক্টিভিটি IDownloaderClient ইন্টারফেসের মাধ্যমে ডাউনলোড স্ট্যাটাসে পরিবর্তন সংক্রান্ত কলব্যাক পায়।

ডাউনলোড প্রগ্রেস রিসিভ করা হচ্ছে

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

IDownloaderClient-এর জন্য প্রয়োজনীয় ইন্টারফেস পদ্ধতিগুলি হল:

onServiceConnected(Messenger m)
আপনার অ্যাক্টিভিটিতে IStub ইনস্ট্যান্টিয়েট করার পরে, আপনি এই পদ্ধতিতে একটি কল পাবেন, যা আপনার DownloaderService-এর ইনস্ট্যান্সের সাথে কানেক্ট করা Messenger অবজেক্ট পাস করে। পরিষেবায় অনুরোধ পাঠাতে, যেমন ডাউনলোড পজ ও আবার চালু করতে, পরিষেবার সাথে কানেক্ট করা IDownloaderService ইন্টারফেস পেতে আপনাকে অবশ্যই DownloaderServiceMarshaller.CreateProxy() কল করতে হবে।

প্রয়োগ সংক্রান্ত সাজেশন এইরকম দেখতে হয়:

Kotlin

private var remoteService: IDownloaderService? = null
...

override fun onServiceConnected(m: Messenger) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
        downloaderClientStub?.messenger?.also { messenger ->
            onClientUpdated(messenger)
        }
    }
}

জাভা

private IDownloaderService remoteService;
...

@Override
public void onServiceConnected(Messenger m) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m);
    remoteService.onClientUpdated(downloaderClientStub.getMessenger());
}

IDownloaderService অবজেক্ট ইনিশিয়ালাইজ করার মাধ্যমে, আপনি ডাউনলোডার পরিষেবায় কমান্ড পাঠাতে পারবেন, যেমন ডাউনলোড পজ ও আবার শুরু করা (requestPauseDownload() এবং requestContinueDownload())।

onDownloadStateChanged(int newState)
ডাউনলোড স্ট্যাটাসে কোনও পরিবর্তন হলে, যেমন ডাউনলোড শুরু বা শেষ হলে, ডাউনলোড পরিষেবা এটি কল করে।

newState ভ্যালু, IDownloaderClient ক্লাসের STATE_* কনস্ট্যান্টের মাধ্যমে -এ উল্লেখ করা সম্ভাব্য একাধিক ভ্যালুর মধ্যে একটি হবে।

আপনার ব্যবহারকারীদের একটি উপযোগী মেসেজ প্রদান করতে, আপনি Helpers.getDownloaderStringResourceIDFromState() কল করে প্রতিটি অবস্থার জন্য একটি সংশ্লিষ্ট স্ট্রিংয়ের অনুরোধ করতে পারেন। এটি ডাউনলোডার লাইব্রেরির সাথে বান্ডেল করা স্ট্রিংগুলির একটির জন্য রিসোর্স আইডি ফেরত দেয়। যেমন, "আপনি রোমিংয়ে আছেন বলে ডাউনলোড পজ করা হয়েছে" স্ট্রিংটি STATE_PAUSED_ROAMING-এর সাথে সম্পর্কিত।

onDownloadProgress(DownloadProgressInfo progress)
ডাউনলোড পরিষেবা এটি কল করে DownloadProgressInfo অবজেক্ট ডেলিভার করে, যা ডাউনলোডের অগ্রগতি সম্পর্কে বিভিন্ন তথ্য বর্ণনা করে, যার মধ্যে রয়েছে আনুমানিক বাকি সময়, বর্তমান গতি, সামগ্রিক অগ্রগতি এবং মোট যাতে আপনি ডাউনলোড অগ্রগতি UI আপডেট করতে পারেন।

পরামর্শ: ডাউনলোড প্রগ্রেস UI আপডেট করে এমন কলব্যাকের উদাহরণের জন্য, Apk এক্সপ্যানশন প্যাকেজের সাথে দেওয়া স্যাম্পেল অ্যাপে SampleDownloaderActivity দেখুন।

IDownloaderService ইন্টারফেসের জন্য কিছু পাবলিক পদ্ধতি যা আপনার কাজে লাগতে পারে:

requestPauseDownload()
ডাউনলোড পজ করে।
requestContinueDownload()
পজ করা ডাউনলোড আবার চালু করে।
setDownloadFlags(int flags)
কোন ধরনের নেটওয়ার্কে ফাইল ডাউনলোড করা যাবে সেই ব্যাপারে ব্যবহারকারীর পছন্দ সেট করে। বর্তমানে একটি ফ্ল্যাগ, FLAGS_DOWNLOAD_OVER_CELLULAR, ব্যবহার করা যায়, তবে আপনি অন্যান্য ফ্ল্যাগ যোগ করতে পারেন। ডিফল্ট হিসেবে, এই ফ্ল্যাগ চালু করা থাকে না, তাই ব্যবহারকারীকে এক্সপ্যানশন ফাইল ডাউনলোড করতে হলে ওয়াই-ফাই নেটওয়ার্কে থাকতে হবে। মোবাইল নেটওয়ার্কের মাধ্যমে ডাউনলোড চালু করার জন্য আপনি ব্যবহারকারীর পছন্দ প্রদান করতে চাইবেন। সেক্ষেত্রে, আপনি এই নম্বরে কল করতে পারেন:

Kotlin

remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
    ...
    setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR)
}

জাভা

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

APKExpansionPolicy ব্যবহার করা

Google Play ডাউনলোডার লাইব্রেরি ব্যবহার করার পরিবর্তে আপনি যদি নিজের ডাউনলোডার পরিষেবা তৈরি করার সিদ্ধান্ত নেন, তাহলে আপনাকে লাইসেন্স যাচাইকরণ লাইব্রেরিতে দেওয়া APKExpansionPolicy ব্যবহার করতে হবে। APKExpansionPolicy ক্লাসটি প্রায় ServerManagedPolicy-এর (Google Play লাইসেন্স যাচাইকরণ লাইব্রেরিতে উপলভ্য) মতোই, তবে এতে APK এক্সপ্যানশন ফাইল রেসপন্স এক্সট্রার জন্য অতিরিক্ত হ্যান্ডলিং অন্তর্ভুক্ত রয়েছে।

মনে রাখবেন: আপনি যদি আগের বিভাগে আলোচনা করা ডাউনলোডার লাইব্রেরি ব্যবহার করেন, তাহলে লাইব্রেরি APKExpansionPolicy-এর সাথে সমস্ত ইন্টার‍্যাকশন করে, তাই আপনাকে সরাসরি এই ক্লাস ব্যবহার করতে হবে না।

উপলভ্য এক্সপ্যানশন ফাইল সম্পর্কে প্রয়োজনীয় তথ্য পেতে আপনাকে সাহায্য করার জন্য এই ক্লাসে পদ্ধতি অন্তর্ভুক্ত আছে:

  • getExpansionURLCount()
  • getExpansionURL(int index)
  • getExpansionFileName(int index)
  • getExpansionFileSize(int index)

আপনি ডাউনলোডার লাইব্রেরি ব্যবহার না করলে, কীভাবে APKExpansionPolicy ব্যবহার করবেন সেই সম্পর্কে আরও তথ্য পেতে, আপনার অ্যাপে লাইসেন্সিং যোগ করা সংক্রান্ত ডকুমেন্টেশন দেখুন, যেখানে এই ধরনের লাইসেন্স নীতি কীভাবে প্রয়োগ করতে হয় তা ব্যাখ্যা করা হয়েছে।

এক্সপ্যানশন ফাইল পড়া হচ্ছে

আপনার APK এক্সপ্যানশন ফাইল ডিভাইসে সেভ হয়ে গেলে, আপনি কীভাবে ফাইল পড়বেন তা নির্ভর করে আপনি কোন ধরনের ফাইল ব্যবহার করেছেন তার উপর। এক নজরে বিভাগে যেমন আলোচনা করা হয়েছে, আপনার এক্সপ্যানশন ফাইল যেকোনও ধরনের ফাইল হতে পারে, তবে নির্দিষ্ট ফাইলের নামের ফর্ম্যাট ব্যবহার করে সেটির নাম পরিবর্তন করা হয় এবং <shared-storage>/Android/obb/<package-name>/-এ সেভ করা হয়।

আপনি যেভাবে ফাইল পড়ুন না কেন, আপনাকে সবসময় প্রথমে চেক করতে হবে যে এক্সটার্নাল স্টোরেজ পড়ার জন্য উপলভ্য আছে কিনা। এমন হতে পারে যে ব্যবহারকারী USB-এর মাধ্যমে কম্পিউটারে স্টোরেজ মাউন্ট করেছেন অথবা আসলে SD কার্ড সরিয়ে দিয়েছেন।

মনে রাখবেন: আপনার অ্যাপ চালু হলে, এক্সটার্নাল স্টোরেজ স্পেস উপলভ্য আছে কিনা এবং getExternalStorageState() কল করে তা পড়া যাবে কিনা তা সবসময় চেক করে নেওয়া উচিত। এটি এক্সটার্নাল স্টোরেজের অবস্থা দেখায় এমন একাধিক সম্ভাব্য স্ট্রিংয়ের মধ্যে একটি দেখায়। আপনার অ্যাপ যাতে এটি পড়তে পারে, তার জন্য রিটার্ন ভ্যালু অবশ্যই MEDIA_MOUNTED হতে হবে।

ফাইলের নাম পাওয়া যাচ্ছে

এক নজরে বিভাগে যেমন বর্ণনা করা হয়েছে, আপনার APK এক্সপ্যানশন ফাইল নির্দিষ্ট ফাইল নামের ফর্ম্যাট ব্যবহার করে সেভ করা হয়:

[main|patch].<expansion-version>.<package-name>.obb

আপনার এক্সপ্যানশন ফাইলের লোকেশন ও নাম পেতে, আপনার ফাইলের পাথ তৈরি করতে getExternalStorageDirectory() ও getPackageName() পদ্ধতি ব্যবহার করা উচিত।

এখানে একটি পদ্ধতি দেওয়া হল যা আপনি আপনার অ্যাপে ব্যবহার করে আপনার দুটি এক্সপ্যানশন ফাইলের সম্পূর্ণ পাথ সহ একটি অ্যারে পেতে পারেন:

Kotlin

fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> {
    val packageName = ctx.packageName
    val ret = mutableListOf<String>()
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        // Build the full path to the app's expansion files
        val root = Environment.getExternalStorageDirectory()
        val expPath = File(root.toString() + EXP_PATH + packageName)

        // Check that expansion file path exists
        if (expPath.exists()) {
            if (mainVersion > 0) {
                val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb"
                val main = File(strMainPath)
                if (main.isFile) {
                    ret += strMainPath
                }
            }
            if (patchVersion > 0) {
                val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb"
                val main = File(strPatchPath)
                if (main.isFile) {
                    ret += strPatchPath
                }
            }
        }
    }
    return ret.toTypedArray()
}

জাভা

// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";

static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
      int patchVersion) {
    String packageName = ctx.getPackageName();
    Vector<String> ret = new Vector<String>();
    if (Environment.getExternalStorageState()
          .equals(Environment.MEDIA_MOUNTED)) {
        // Build the full path to the app's expansion files
        File root = Environment.getExternalStorageDirectory();
        File expPath = new File(root.toString() + EXP_PATH + packageName);

        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                        ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                        ret.add(strPatchPath);
                }
            }
        }
    }
    String[] retArray = new String[ret.size()];
    ret.toArray(retArray);
    return retArray;
}

আপনি এই পদ্ধতিটি আপনার অ্যাপ Context এবং কাঙ্ক্ষিত এক্সপ্যানশন ফাইলের ভার্সন পাস করে কল করতে পারেন।

আপনি অনেক উপায়ে এক্সপ্যানশন ফাইলের ভার্সন নম্বর নির্ধারণ করতে পারেন। একটি সহজ উপায় হল, ডাউনলোড শুরু হলে SharedPreferences ফাইলে ভার্সন সেভ করা, APKExpansionPolicy ক্লাসের getExpansionFileName(int index) পদ্ধতি ব্যবহার করে এক্সপ্যানশন ফাইলের নাম কোয়েরি করার মাধ্যমে। আপনি এক্সপ্যানশন ফাইল অ্যাক্সেস করতে চাইলে, SharedPreferences ফাইল পড়ে ভার্সন কোড পেতে পারেন।

শেয়ার করা স্টোরেজ থেকে ডেটা রিড করার বিষয়ে আরও তথ্য পেতে, ডেটা স্টোরেজ ডকুমেন্টেশন দেখুন।

APK এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার করা

Google Market Apk এক্সপ্যানশন প্যাকেজে APK এক্সপ্যানশন জিপ লাইব্রেরি (<sdk>/extras/google/google_market_apk_expansion/zip_file/-এ অবস্থিত) নামে একটি লাইব্রেরি অন্তর্ভুক্ত থাকে। এটি একটি ঐচ্ছিক লাইব্রেরি যা আপনার এক্সপ্যানশন ফাইলগুলি জিপ ফাইল হিসাবে সেভ করা থাকলে সেগুলি পড়তে সাহায্য করে। এই লাইব্রেরি ব্যবহার করলে, আপনি ভার্চুয়াল ফাইল সিস্টেম হিসেবে আপনার ZIP এক্সপ্যানশন ফাইল থেকে সহজেই রিসোর্স পড়তে পারবেন।

APK এক্সপ্যানশন জিপ লাইব্রেরিতে নিম্নলিখিত ক্লাস ও API অন্তর্ভুক্ত থাকে:

APKExpansionSupport
এক্সপ্যানশন ফাইলের নাম এবং ZIP ফাইল অ্যাক্সেস করার জন্য কিছু পদ্ধতি প্রদান করে:
getAPKExpansionFiles()
উপরে দেখানো একই পদ্ধতি যা দুটি এক্সপ্যানশন ফাইলের সম্পূর্ণ ফাইল পাথ রিটার্ন করে।
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
মূল ফাইল ও প্যাচ ফাইল, দুটির যোগফল বোঝায় এমন একটি ZipResourceFile ফেরত দেয়। অর্থাৎ, আপনি যদি mainVersion এবং patchVersion, দুটিই উল্লেখ করেন, তাহলে এটি একটি ZipResourceFile রিটার্ন করে যা সব ডেটায় রিড অ্যাক্সেস প্রদান করে, যেখানে প্যাচ ফাইলের ডেটা মূল ফাইলের উপরে মার্জ করা থাকে।
ZipResourceFile
শেয়ার করা স্টোরেজে একটি জিপ ফাইলকে দেখায় এবং আপনার জিপ ফাইলের উপর ভিত্তি করে একটি ভার্চুয়াল ফাইল সিস্টেম প্রদান করার জন্য সব কাজ করে। আপনি APKExpansionSupport.getAPKExpansionZipFile() ব্যবহার করে বা ZipResourceFile-এর সাথে আপনার এক্সপ্যানশন ফাইলের পাথ পাস করে একটি ইনস্ট্যান্স পেতে পারেন। এই ক্লাসে বিভিন্ন ধরনের উপযোগী পদ্ধতি অন্তর্ভুক্ত আছে, তবে সাধারণত আপনাকে এগুলির বেশিরভাগই অ্যাক্সেস করতে হবে না। দুটি গুরুত্বপূর্ণ পদ্ধতি হল:
getInputStream(String assetPath)
, ZIP ফাইলের মধ্যে থাকা ফাইল পড়ার জন্য InputStream প্রদান করে। assetPath অবশ্যই কাঙ্খিত ফাইলের পাথ হতে হবে, যা জিপ ফাইল কন্টেন্টের রুটের সাথে সম্পর্কিত।
getAssetFileDescriptor(String assetPath)
জিপ ফাইলের মধ্যে থাকা কোনও ফাইলের জন্য AssetFileDescriptor প্রদান করে। assetPath অবশ্যই কাঙ্খিত ফাইলের পাথ হতে হবে, যা ZIP ফাইলের কন্টেন্টের রুটের সাথে সম্পর্কিত। এটি নির্দিষ্ট কিছু Android API-এর জন্য উপযোগী যেগুলির জন্য AssetFileDescriptor প্রয়োজন হয়, যেমন কিছু MediaPlayer API।
APEZProvider
বেশিরভাগ অ্যাপের ক্ষেত্রে এই ক্লাস ব্যবহার করার প্রয়োজন হয় না। এই ক্লাসটি একটি ContentProvider সংজ্ঞায়িত করে যা একটি কন্টেন্ট প্রোভাইডার Uri-এর মাধ্যমে ZIP ফাইল থেকে ডেটা মার্শাল করে যাতে নির্দিষ্ট Android API-এর জন্য ফাইল অ্যাক্সেস প্রদান করা যায় যা মিডিয়া ফাইলগুলিতে Uri অ্যাক্সেস আশা করে। যেমন, আপনি VideoView.setVideoURI()-এর মাধ্যমে কোনও ভিডিও চালাতে চাইলে এটি কাজে লাগতে পারে।

মিডিয়া ফাইলের ZIP কমপ্রেশন এড়িয়ে যাওয়া হচ্ছে

আপনি যদি মিডিয়া ফাইল স্টোর করার জন্য এক্সপ্যানশন ফাইল ব্যবহার করেন, তাহলে জিপ ফাইল এখনও আপনাকে Android মিডিয়া প্লেব্যাক কল ব্যবহার করার অনুমতি দেয় যা অফসেট এবং দৈর্ঘ্য কন্ট্রোল (যেমন MediaPlayer.setDataSource() এবং SoundPool.load()) প্রদান করে। এটি কাজ করার জন্য, জিপ প্যাকেজ তৈরি করার সময় আপনাকে মিডিয়া ফাইলে অতিরিক্ত কম্প্রেশন পারফর্ম করলে চলবে না। যেমন, zip টুল ব্যবহার করার সময়, কম্প্রেস করা উচিত নয় এমন ফাইল সাফিক্স নির্দিষ্ট করতে, আপনাকে -n বিকল্প ব্যবহার করতে হবে:

zip -n .mp4;.ogg main_expansion media_files

জিপ ফাইল থেকে পড়া হচ্ছে

APK এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার করার সময়, আপনার জিপ থেকে ফাইল পড়তে সাধারণত নিম্নলিখিতগুলি প্রয়োজন হয়:

Kotlin

// Get a ZipResourceFile representing a merger of both the main and patch files
val expansionFile =
        APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

জাভা

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile =
    APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

উপরের কোডটি আপনার মেন এক্সপ্যানশন ফাইল বা প্যাচ এক্সপ্যানশন ফাইলে থাকা যেকোনও ফাইল অ্যাক্সেস করার অনুমতি দেয়। এটি দুটি ফাইলেরই সব ফাইলের একটি মার্জ করা ম্যাপ থেকে রিড করে। getAPKExpansionFile() মেথড ব্যবহার করার জন্য আপনাকে শুধু android.content.Context অ্যাপের নাম এবং মেন এক্সপ্যানশন ফাইল ও প্যাচ এক্সপ্যানশন ফাইল, দুটিরই ভার্সন নম্বর দিতে হবে।

আপনি যদি কোনও নির্দিষ্ট এক্সপ্যানশন ফাইল থেকে পড়তে চান, তাহলে আপনি কাঙ্ক্ষিত এক্সপ্যানশন ফাইলের পাথ সহ ZipResourceFile কনস্ট্রাক্টর ব্যবহার করতে পারেন:

Kotlin

// Get a ZipResourceFile representing a specific expansion file
val expansionFile = ZipResourceFile(filePathToMyZip)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

জাভা

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

আপনার এক্সপ্যানশন ফাইলের জন্য এই লাইব্রেরি ব্যবহার করার ব্যাপারে আরও তথ্যের জন্য, স্যাম্পেল অ্যাপের SampleDownloaderActivity ক্লাস দেখুন, যাতে CRC ব্যবহার করে ডাউনলোড করা ফাইল যাচাই করার জন্য অতিরিক্ত কোড অন্তর্ভুক্ত থাকে। মনে রাখবেন যে আপনি যদি নিজের ইমপ্লিমেন্টেশনের ভিত্তি হিসেবে এই স্যাম্পেল ব্যবহার করেন, তাহলে আপনাকে xAPKS অ্যারেতে আপনার এক্সপ্যানশন ফাইলের বাইট সাইজ ঘোষণা করতে হবে।

আপনার এক্সপ্যানশন ফাইল পরীক্ষা করা হচ্ছে

আপনার অ্যাপ প্রকাশ করার আগে, আপনাকে দুটি জিনিস পরীক্ষা করতে হবে: এক্সপ্যানশন ফাইল পড়া এবং ফাইল ডাউনলোড করা।

ফাইল রিড টেস্ট করা হচ্ছে

Google Play-তে আপনার অ্যাপ আপলোড করার আগে, শেয়ার করা স্টোরেজ থেকে ফাইল রিড করার জন্য আপনার অ্যাপের ক্ষমতা পরীক্ষা করা উচিত। আপনাকে শুধু ডিভাইসের শেয়ার করা স্টোরেজের সঠিক লোকেশনে ফাইল যোগ করতে হবে এবং আপনার অ্যাপ লঞ্চ করতে হবে:

  1. আপনার ডিভাইসে, শেয়ার করা স্টোরেজে উপযুক্ত ডিরেক্টরি তৈরি করুন যেখানে Google Play আপনার ফাইল সেভ করবে।

    যেমন, আপনার প্যাকেজের নাম com.example.android হলে, আপনাকে শেয়ার করা স্টোরেজ স্পেসে Android/obb/com.example.android/ ডিরেক্টরি তৈরি করতে হবে। (শেয়ার করা স্টোরেজ মাউন্ট করতে এবং ম্যানুয়ালি এই ডিরেক্টরি তৈরি করতে আপনার কম্পিউটারে টেস্ট ডিভাইস প্লাগ-ইন করুন।)

  2. সেই ডিরেক্টরিতে ম্যানুয়ালি এক্সপ্যানশন ফাইল যোগ করুন। Google Play যে ফাইলের নামের ফর্ম্যাট ব্যবহার করবে, তার সাথে ম্যাচ করার জন্য আপনার ফাইলের নাম পরিবর্তন করতে ভুলবেন না।

    যেমন, ফাইলের ধরন যাই হোক না কেন, com.example.android অ্যাপের জন্য মেন এক্সপ্যানশন ফাইল main.0300110.com.example.android.obb হতে হবে। ভার্সন কোড আপনার পছন্দমতো যেকোনও ভ্যালু হতে পারে। শুধু মনে রাখবেন:

    • মেন এক্সপ্যানশন ফাইল সবসময় main দিয়ে শুরু হয় এবং প্যাচ ফাইল patch দিয়ে শুরু হয়।
    • প্যাকেজের নাম সবসময় সেই APK-এর সাথে মেলে যার সাথে Google Play-তে ফাইলটি অ্যাটাচ করা আছে।
  3. এখন যেহেতু এক্সপ্যানশন ফাইল ডিভাইসে আছে, তাই আপনি এক্সপ্যানশন ফাইল পরীক্ষা করার জন্য অ্যাপ ইনস্টল ও রান করতে পারবেন।

এক্সপ্যানশন ফাইল ম্যানেজ করার ব্যাপারে কিছু রিমাইন্ডার এখানে দেওয়া হল:

  • .obb এক্সপ্যানশন ফাইল মুছবেন না বা সেটির নাম পরিবর্তন করবেন না (এমনকি আপনি অন্য কোনও লোকেশনে ডেটা আনপ্যাক করলেও)। এটি করলে Google Play (বা আপনার অ্যাপ নিজেই) বারবার এক্সপ্যানশন ফাইল ডাউনলোড করবে।
  • আপনার obb/ ডিরেক্টরিতে অন্য ডেটা সেভ করবেন না। আপনাকে কিছু ডেটা আনপ্যাক করতে হলে, getExternalFilesDir()-এর মাধ্যমে নির্দিষ্ট করা লোকেশনে এটি সেভ করুন।

ফাইল ডাউনলোড পরীক্ষা করা হচ্ছে

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

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

মনে রাখবেন: আগে আপনি কোনও অ্যাপের অপ্রকাশিত "ড্রাফ্ট" ভার্সন আপলোড করে সেটি পরীক্ষা করতে পারতেন। এই কার্যকারিতা আর কাজ করে না। পরিবর্তে, আপনাকে এটি ইন্টার্নাল, ক্লোজড বা ওপেন টেস্টিং ট্র্যাকে প্রকাশ করতে হবে। আরও তথ্যের জন্য, ড্রাফ্ট অ্যাপ আর কাজ করে না নিবন্ধটি দেখুন।

আপনার অ্যাপ আপডেট করা হচ্ছে

Google Play-তে এক্সপ্যানশন ফাইল ব্যবহার করার একটি বড় সুবিধা হল, সমস্ত আসল অ্যাসেট আবার ডাউনলোড না করেই আপনার অ্যাপ আপডেট করার ক্ষমতা। Google Play আপনাকে প্রতিটি APK-এর সাথে দুটি এক্সপ্যানশন ফাইল প্রদান করার অনুমতি দেয়, তাই আপনি দ্বিতীয় ফাইলটি "প্যাচ" হিসেবে ব্যবহার করতে পারেন যা আপডেট ও নতুন অ্যাসেট প্রদান করে। এটি করলে, মেন এক্সপ্যানশন ফাইল আবার ডাউনলোড করার প্রয়োজন হয় না, যা ব্যবহারকারীদের জন্য বড় ও ব্যয়বহুল হতে পারে।

প্যাচ এক্সপ্যানশন ফাইল টেকনিক্যালি মেন এক্সপ্যানশন ফাইলের মতোই এবং Android সিস্টেম বা Google Play, আপনার মেন ও প্যাচ এক্সপ্যানশন ফাইলের মধ্যে আসল প্যাচিং করে না। আপনার অ্যাপ কোডকে অবশ্যই প্রয়োজনীয় প্যাচগুলি নিজেই পারফর্ম করতে হবে।

আপনি এক্সপ্যানশন ফাইল হিসেবে জিপ ফাইল ব্যবহার করলে, Apk এক্সপ্যানশন প্যাকেজের সাথে অন্তর্ভুক্ত APK এক্সপ্যানশন জিপ লাইব্রেরি-তে আপনার প্যাচ ফাইলকে মেন এক্সপ্যানশন ফাইলের সাথে মার্জ করার ক্ষমতা থাকে।

মনে রাখবেন: আপনাকে শুধু প্যাচ এক্সপ্যানশন ফাইলে পরিবর্তন করতে হলেও, Google Play যাতে আপডেট করতে পারে তার জন্য আপনাকে APK আপডেট করতে হবে। অ্যাপে কোড পরিবর্তন করার প্রয়োজন না হলে, আপনাকে শুধু ম্যানিফেস্টে versionCode আপডেট করতে হবে।

Play Console-এ APK-এর সাথে যুক্ত মূল এক্সপ্যানশন ফাইলটি পরিবর্তন না করা পর্যন্ত, যেসব ব্যবহারকারী আগে আপনার অ্যাপ ইনস্টল করেছেন তারা মূল এক্সপ্যানশন ফাইলটি ডাউনলোড করবেন না। পুরনো ব্যবহারকারীরা শুধু আপডেট করা APK এবং নতুন প্যাচ এক্সপ্যানশন ফাইল (আগের মেন এক্সপ্যানশন ফাইল রেখে) পান।

এক্সপ্যানশন ফাইল আপডেট করার ব্যাপারে কয়েকটি বিষয় মনে রাখতে হবে:

  • আপনার অ্যাপের জন্য একবারে শুধু দুটি এক্সপ্যানশন ফাইল থাকতে পারে। একটি মেন এক্সপ্যানশন ফাইল ও একটি প্যাচ এক্সপ্যানশন ফাইল। কোনও ফাইল আপডেট করার সময়, Google Play আগের ভার্সন মুছে দেয় (এবং ম্যানুয়াল আপডেট করার সময় আপনার অ্যাপকেও তাই করতে হবে)।
  • প্যাচ এক্সপ্যানশন ফাইল যোগ করার সময়, Android সিস্টেম আসলে আপনার অ্যাপ বা মেন এক্সপ্যানশন ফাইল প্যাচ করে না। প্যাচ ডেটা যাতে কাজ করে সেইভাবে আপনার অ্যাপ ডিজাইন করতে হবে। তবে, Apk এক্সপ্যানশন প্যাকেজে এক্সপ্যানশন ফাইল হিসেবে জিপ ফাইল ব্যবহার করার জন্য একটি লাইব্রেরি অন্তর্ভুক্ত থাকে, যা প্যাচ ফাইল থেকে ডেটা মূল এক্সপ্যানশন ফাইলে মার্জ করে যাতে আপনি সহজেই সব এক্সপ্যানশন ফাইলের ডেটা পড়তে পারেন।