গুগল প্লে-এর নিয়ম অনুযায়ী, ব্যবহারকারীদের ডাউনলোড করা কম্প্রেসড APK ফাইলের সাইজ ১০০ মেগাবাইটের বেশি হওয়া উচিত নয়। বেশিরভাগ অ্যাপের জন্য, এর সমস্ত কোড এবং অ্যাসেটের জন্য এই পরিমাণ জায়গাই যথেষ্ট। তবে, কিছু অ্যাপের ক্ষেত্রে হাই-ফিডেলিটি গ্রাফিক্স, মিডিয়া ফাইল বা অন্যান্য বড় অ্যাসেটের জন্য আরও বেশি জায়গার প্রয়োজন হয়। আগে, যদি আপনার অ্যাপের কম্প্রেসড ডাউনলোডের সাইজ ১০০ মেগাবাইটের বেশি হতো, তাহলে ব্যবহারকারী যখন অ্যাপটি খুলত, তখন আপনাকে অতিরিক্ত রিসোর্সগুলো নিজে থেকেই হোস্ট এবং ডাউনলোড করতে হতো। এই অতিরিক্ত ফাইলগুলো হোস্ট করা এবং সরবরাহ করা ব্যয়বহুল হতে পারে এবং এর ফলে ব্যবহারকারীর অভিজ্ঞতাও প্রায়শই আশানুরূপ হয় না। এই প্রক্রিয়াটি আপনার জন্য সহজ এবং ব্যবহারকারীদের জন্য আরও আনন্দদায়ক করতে, গুগল প্লে এখন আপনার APK-এর সাথে দুটি বড় এক্সপ্যানশন ফাইল সংযুক্ত করার সুযোগ দেয়।
গুগল প্লে আপনার অ্যাপের জন্য এক্সপ্যানশন ফাইলগুলো হোস্ট করে এবং কোনো খরচ ছাড়াই সেগুলো আপনার ডিভাইসে সরবরাহ করে। এক্সপ্যানশন ফাইলগুলো ডিভাইসের শেয়ার্ড স্টোরেজ লোকেশনে (এসডি কার্ড বা ইউএসবি-মাউন্টেবল পার্টিশন; যা "এক্সটার্নাল" স্টোরেজ নামেও পরিচিত) সেভ করা হয়, যেখান থেকে আপনার অ্যাপ সেগুলো অ্যাক্সেস করতে পারে। বেশিরভাগ ডিভাইসে, গুগল প্লে APK ডাউনলোড করার সময়েই এক্সপ্যানশন ফাইলগুলো ডাউনলোড করে নেয়, ফলে ব্যবহারকারী যখন প্রথমবার অ্যাপটি খোলে, তখন তার প্রয়োজনীয় সবকিছুই সেখানে থাকে। তবে, কিছু কিছু ক্ষেত্রে, আপনার অ্যাপ চালু হওয়ার সময়েই গুগল প্লে থেকে ফাইলগুলো ডাউনলোড করতে হয়।
আপনি যদি এক্সপ্যানশন ফাইল ব্যবহার করা এড়াতে চান এবং আপনার অ্যাপের কম্প্রেসড ডাউনলোড সাইজ ১০০ মেগাবাইটের বেশি হয়, তবে এর পরিবর্তে আপনার অ্যাপটি অ্যান্ড্রয়েড অ্যাপ বান্ডেল ব্যবহার করে আপলোড করা উচিত, যা ৫০০ মেগাবাইট পর্যন্ত কম্প্রেসড ডাউনলোড সাইজের সুবিধা দেয়। এছাড়াও, যেহেতু অ্যাপ বান্ডেল ব্যবহার করলে এপিকে তৈরি এবং সাইন করার কাজটি গুগল প্লে-এর উপর ছেড়ে দেওয়া হয়, তাই ব্যবহারকারীরা অপটিমাইজড এপিকে ডাউনলোড করেন, যাতে শুধুমাত্র আপনার অ্যাপটি চালানোর জন্য প্রয়োজনীয় কোড এবং রিসোর্স থাকে। আপনাকে একাধিক এপিকে বা এক্সপ্যানশন ফাইল তৈরি, সাইন এবং পরিচালনা করতে হয় না, এবং ব্যবহারকারীরা ছোট ও আরও অপটিমাইজড ডাউনলোড পান।
সংক্ষিপ্ত বিবরণ
প্রতিবার গুগল প্লে কনসোল ব্যবহার করে একটি APK আপলোড করার সময়, আপনার কাছে APK-টিতে এক বা দুটি এক্সপ্যানশন ফাইল যোগ করার বিকল্প থাকে। প্রতিটি ফাইলের আকার ২ জিবি পর্যন্ত হতে পারে এবং এটি আপনার পছন্দের যেকোনো ফরম্যাটের হতে পারে, তবে ডাউনলোডের সময় ব্যান্ডউইথ সাশ্রয় করার জন্য আমরা একটি কম্প্রেসড ফাইল ব্যবহার করার পরামর্শ দিই। ধারণাগতভাবে, প্রতিটি এক্সপ্যানশন ফাইলের একটি ভিন্ন ভূমিকা রয়েছে:
- মূল এক্সপ্যানশন ফাইলটি হলো আপনার অ্যাপের জন্য প্রয়োজনীয় অতিরিক্ত রিসোর্সগুলোর প্রাথমিক এক্সপ্যানশন ফাইল।
- প্যাচ সম্প্রসারণ ফাইলটি ঐচ্ছিক এবং এটি মূল সম্প্রসারণ ফাইলটিতে ছোটখাটো হালনাগাদ করার জন্য ব্যবহৃত হয়।
যদিও আপনি দুটি এক্সপ্যানশন ফাইল আপনার ইচ্ছামতো ব্যবহার করতে পারেন, আমরা সুপারিশ করি যে মূল এক্সপ্যানশন ফাইলটি যেন প্রাথমিক অ্যাসেটগুলো সরবরাহ করে এবং এটি যেন খুব কমই, বা একেবারেই আপডেট করা না হয়; প্যাচ এক্সপ্যানশন ফাইলটি আকারে ছোট হওয়া উচিত এবং এটি একটি 'প্যাচ বাহক' হিসেবে কাজ করবে, যা প্রতিটি প্রধান রিলিজের সাথে বা প্রয়োজন অনুযায়ী আপডেট করা হবে।
তবে, আপনার অ্যাপ আপডেটের জন্য শুধুমাত্র একটি নতুন প্যাচ এক্সপ্যানশন ফাইলের প্রয়োজন হলেও, আপনাকে অবশ্যই ম্যানিফেস্টে একটি আপডেট করা versionCode সহ একটি নতুন APK আপলোড করতে হবে। (প্লে কনসোল আপনাকে বিদ্যমান কোনো APK-তে এক্সপ্যানশন ফাইল আপলোড করার অনুমতি দেয় না।)
দ্রষ্টব্য: প্যাচ সম্প্রসারণ ফাইলটি অর্থগতভাবে মূল সম্প্রসারণ ফাইলের মতোই—আপনি প্রতিটি ফাইল আপনার ইচ্ছামতো ব্যবহার করতে পারেন।
ফাইলের নামের ফরম্যাট
আপনার আপলোড করা প্রতিটি এক্সপ্যানশন ফাইল আপনার পছন্দের যেকোনো ফরম্যাটের (ZIP, PDF, MP4, ইত্যাদি) হতে পারে। এছাড়াও, আপনি JOBB টুল ব্যবহার করে এক সেট রিসোর্স ফাইল এবং সেই সেটের জন্য পরবর্তী প্যাচগুলোকে এনক্যাপসুলেট ও এনক্রিপ্ট করতে পারেন। ফাইলের ধরন নির্বিশেষে, গুগল প্লে সেগুলোকে অস্বচ্ছ বাইনারি ব্লব হিসেবে বিবেচনা করে এবং নিম্নলিখিত স্কিম ব্যবহার করে ফাইলগুলোর নাম পরিবর্তন করে:
[main|patch].<expansion-version>.<package-name>.obb
এই পরিকল্পনার তিনটি উপাদান রয়েছে:
-
mainবাpatch - ফাইলটি মূল ফাইল নাকি প্যাচ সম্প্রসারণ ফাইল, তা নির্দিষ্ট করে। প্রতিটি APK-এর জন্য কেবল একটি মূল ফাইল এবং একটি প্যাচ ফাইল থাকতে পারে।
-
<expansion-version> - এটি একটি পূর্ণসংখ্যা যা সেই APK-এর ভার্সন কোডের সাথে মেলে যার সাথে এক্সপ্যানশনটি প্রথম যুক্ত হয় (এটি অ্যাপের
android:versionCodeমানের সাথে মেলে)।"প্রথম" শব্দটির উপর জোর দেওয়া হয়েছে, কারণ প্লে কনসোল আপনাকে একটি নতুন APK-এর সাথে আপলোড করা এক্সপ্যানশন ফাইল পুনরায় ব্যবহার করার অনুমতি দিলেও, এক্সপ্যানশন ফাইলটির নাম পরিবর্তন হয় না—আপনি যখন প্রথম ফাইলটি আপলোড করেছিলেন, তখন এতে যে সংস্করণটি প্রয়োগ করা হয়েছিল, সেটিই এটি ধরে রাখে।
-
<package-name> - আপনার অ্যাপের জাভা-স্টাইলের প্যাকেজ নাম।
উদাহরণস্বরূপ, ধরুন আপনার APK ভার্সন হলো 314159 এবং আপনার প্যাকেজ নেম হলো com.example.app। আপনি যদি একটি প্রধান এক্সপ্যানশন ফাইল আপলোড করেন, তাহলে ফাইলটির নাম পরিবর্তন করে রাখা হয়:
main.314159.com.example.app.obb
স্টোরেজ অবস্থান
যখন গুগল প্লে আপনার এক্সপ্যানশন ফাইলগুলো কোনো ডিভাইসে ডাউনলোড করে, তখন সেগুলো সিস্টেমের শেয়ার্ড স্টোরেজ লোকেশনে সেভ করে। সঠিক কার্যকারিতা নিশ্চিত করতে, আপনাকে অবশ্যই এক্সপ্যানশন ফাইলগুলো ডিলিট, মুভ বা রিনেম করা থেকে বিরত থাকতে হবে। যদি আপনার অ্যাপকে সরাসরি গুগল প্লে থেকেই ডাউনলোড করতে হয়, তবে আপনাকে ফাইলগুলো ঠিক একই লোকেশনে সেভ করতে হবে।
getObbDir() মেথডটি আপনার এক্সপ্যানশন ফাইলগুলির নির্দিষ্ট অবস্থান নিম্নলিখিত আকারে ফেরত দেয়:
<shared-storage>/Android/obb/<package-name>/
-
<shared-storage>হলো শেয়ার্ড স্টোরেজ স্পেসের পাথ, যাgetExternalStorageDirectory()থেকে পাওয়া যায়। -
<package-name>হলো আপনার অ্যাপের জাভা-স্টাইলের প্যাকেজ নেম, যাgetPackageName()থেকে পাওয়া যায়।
প্রতিটি অ্যাপের জন্য, এই ডিরেক্টরিতে কখনোই দুইটির বেশি এক্সপ্যানশন ফাইল থাকে না। একটি হলো মূল এক্সপ্যানশন ফাইল এবং অন্যটি হলো প্যাচ এক্সপ্যানশন ফাইল (প্রয়োজন হলে)। আপনি যখন নতুন এক্সপ্যানশন ফাইল দিয়ে আপনার অ্যাপ আপডেট করেন, তখন পূর্ববর্তী সংস্করণগুলো ওভাররাইট হয়ে যায়। অ্যান্ড্রয়েড ৪.৪ (এপিআই লেভেল ১৯) থেকে, অ্যাপগুলো এক্সটার্নাল স্টোরেজ পারমিশন ছাড়াই OBB এক্সপ্যানশন ফাইল পড়তে পারে। তবে, অ্যান্ড্রয়েড ৬.০ (এপিআই লেভেল ২৩) এবং তার পরবর্তী সংস্করণের কিছু ইমপ্লিমেন্টেশনে এখনও পারমিশনের প্রয়োজন হয়, তাই আপনাকে অ্যাপ ম্যানিফেস্টে READ_EXTERNAL_STORAGE পারমিশনটি ডিক্লেয়ার করতে হবে এবং রানটাইমে নিম্নরূপভাবে পারমিশনের জন্য অনুরোধ করতে হবে:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
অ্যান্ড্রয়েড সংস্করণ ৬ এবং তার পরবর্তী সংস্করণগুলোর জন্য, রানটাইমে এক্সটার্নাল স্টোরেজের অনুমতি চাইতে হয়। তবে, অ্যান্ড্রয়েডের কিছু সংস্করণে OBB ফাইল পড়ার জন্য অনুমতির প্রয়োজন হয় না। এক্সটার্নাল স্টোরেজের অনুমতি চাওয়ার আগে কীভাবে রিড অ্যাক্সেস আছে তা পরীক্ষা করতে হয়, তা নিচের কোড স্নিপেটটিতে দেখানো হলো:
কোটলিন
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 Expansion Zip Library নামে একটি লাইব্রেরি প্রজেক্ট দিয়েছি যা আপনার ডেটা সরাসরি ZIP ফাইল থেকে পড়ে নেয়।
সতর্কতা: APK ফাইলের মতো নয়, শেয়ার্ড স্টোরেজে সংরক্ষিত যেকোনো ফাইল ব্যবহারকারী এবং অন্যান্য অ্যাপ পড়তে পারে।
পরামর্শ: আপনি যদি মিডিয়া ফাইলগুলোকে একটি ZIP ফাইলে প্যাকেজ করেন, তাহলে আপনার ZIP ফাইলটি আনপ্যাক না করেই অফসেট এবং লেংথ কন্ট্রোলসহ ফাইলগুলোতে মিডিয়া প্লেব্যাক কল (যেমন MediaPlayer.setDataSource() এবং SoundPool.load() ) ব্যবহার করতে পারবেন। এটি কাজ করার জন্য, ZIP প্যাকেজ তৈরি করার সময় মিডিয়া ফাইলগুলোর উপর অতিরিক্ত কম্প্রেশন করা যাবে না। উদাহরণস্বরূপ, zip টুল ব্যবহার করার সময়, যে ফাইল সাফিক্সগুলো কম্প্রেস করা হবে না, তা নির্দিষ্ট করার জন্য আপনার -n অপশনটি ব্যবহার করা উচিত:
zip -n .mp4;.ogg main_expansion media_files
ডাউনলোড প্রক্রিয়া
বেশিরভাগ সময়, গুগল প্লে ডিভাইসে APK ডাউনলোড করার সাথে সাথেই আপনার এক্সপ্যানশন ফাইলগুলোও ডাউনলোড করে সেভ করে। তবে, কিছু ক্ষেত্রে গুগল প্লে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে পারে না অথবা ব্যবহারকারী আগে ডাউনলোড করা এক্সপ্যানশন ফাইলগুলো মুছে ফেলতে পারেন। এই পরিস্থিতিগুলো সামাল দেওয়ার জন্য, আপনার অ্যাপটিকে অবশ্যই গুগল প্লে দ্বারা প্রদত্ত একটি URL ব্যবহার করে, মূল অ্যাক্টিভিটি শুরু হওয়ার সাথে সাথেই ফাইলগুলো নিজে থেকে ডাউনলোড করতে সক্ষম হতে হবে।
উচ্চ স্তরে ডাউনলোড প্রক্রিয়াটি দেখতে এইরকম:
- ব্যবহারকারী গুগল প্লে থেকে আপনার অ্যাপটি ইনস্টল করতে চান।
- যদি গুগল প্লে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে পারে (যা বেশিরভাগ ডিভাইসের ক্ষেত্রেই ঘটে), তবে এটি APK-এর সাথেই সেগুলো ডাউনলোড করে নেয়।
যদি গুগল প্লে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে না পারে, তবে এটি শুধু APK ফাইলটি ডাউনলোড করে।
- যখন ব্যবহারকারী আপনার অ্যাপটি চালু করবেন, তখন আপনার অ্যাপকে অবশ্যই যাচাই করতে হবে যে এক্সপ্যানশন ফাইলগুলো ডিভাইসে আগে থেকেই সংরক্ষিত আছে কি না।
- যদি উত্তর হ্যাঁ হয়, তাহলে আপনার অ্যাপটি ব্যবহারের জন্য প্রস্তুত।
- অন্যথায়, আপনার অ্যাপকে অবশ্যই গুগল প্লে থেকে HTTP-এর মাধ্যমে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে হবে। আপনার অ্যাপকে গুগল প্লে-এর অ্যাপ লাইসেন্সিং পরিষেবা ব্যবহার করে গুগল প্লে ক্লায়েন্টের কাছে একটি অনুরোধ পাঠাতে হবে, যা প্রতিটি এক্সপ্যানশন ফাইলের নাম, ফাইলের আকার এবং URL সহ প্রতিক্রিয়া জানায়। এই তথ্য দিয়ে, আপনি ফাইলগুলি ডাউনলোড করে সঠিক স্টোরেজ লোকেশনে সংরক্ষণ করবেন।
সতর্কতা: আপনার অ্যাপ চালু হওয়ার সময় যদি এক্সপ্যানশন ফাইলগুলো ডিভাইসে আগে থেকে না থাকে, তবে গুগল প্লে থেকে সেগুলো ডাউনলোড করার জন্য প্রয়োজনীয় কোড অন্তর্ভুক্ত করা অত্যন্ত গুরুত্বপূর্ণ। এক্সপ্যানশন ফাইল ডাউনলোড করা সম্পর্কিত পরবর্তী বিভাগে যেমন আলোচনা করা হয়েছে, আমরা আপনার জন্য একটি লাইব্রেরি উপলব্ধ করেছি যা এই প্রক্রিয়াটিকে অনেক সহজ করে তোলে এবং আপনার পক্ষ থেকে ন্যূনতম কোডের মাধ্যমে একটি পরিষেবা থেকে ডাউনলোড সম্পন্ন করে।
উন্নয়ন চেকলিস্ট
আপনার অ্যাপে এক্সপ্যানশন ফাইল ব্যবহার করার জন্য যে কাজগুলো করতে হবে তার একটি সারসংক্ষেপ নিচে দেওয়া হলো:
- প্রথমে নির্ধারণ করুন আপনার অ্যাপের কম্প্রেসড ডাউনলোড সাইজ ১০০ মেগাবাইটের বেশি হওয়া প্রয়োজন কিনা। জায়গা খুবই মূল্যবান এবং আপনার মোট ডাউনলোড সাইজ যতটা সম্ভব কম রাখা উচিত। যদি আপনার অ্যাপ বিভিন্ন স্ক্রিন ডেনসিটির জন্য গ্রাফিক অ্যাসেটের একাধিক সংস্করণ সরবরাহ করতে ১০০ মেগাবাইটের বেশি জায়গা ব্যবহার করে, তবে এর পরিবর্তে একাধিক APK প্রকাশ করার কথা বিবেচনা করুন, যেখানে প্রতিটি APK-তে শুধুমাত্র সেই স্ক্রিনগুলোর জন্য প্রয়োজনীয় অ্যাসেটগুলো থাকবে। গুগল প্লে-তে প্রকাশ করার সময় সেরা ফলাফলের জন্য, একটি অ্যান্ড্রয়েড অ্যাপ বান্ডেল (Android App Bundle) আপলোড করুন, যাতে আপনার অ্যাপের সমস্ত কম্পাইল করা কোড এবং রিসোর্স অন্তর্ভুক্ত থাকে, কিন্তু APK তৈরি এবং সাইন করার কাজটি গুগল প্লে-এর জন্য স্থগিত রাখা হয়।
- আপনার APK থেকে কোন অ্যাপ রিসোর্সগুলো আলাদা করবেন তা নির্ধারণ করুন এবং সেগুলোকে একটি ফাইলে প্যাকেজ করে প্রধান এক্সপ্যানশন ফাইল হিসেবে ব্যবহার করুন।
সাধারণত, মূল এক্সপ্যানশন ফাইলটি আপডেট করার সময়ই কেবল দ্বিতীয় প্যাচ এক্সপ্যানশন ফাইলটি ব্যবহার করা উচিত। তবে, যদি আপনার রিসোর্স মূল এক্সপ্যানশন ফাইলের জন্য নির্ধারিত ২ জিবি সীমা অতিক্রম করে, তাহলে আপনি আপনার বাকি অ্যাসেটগুলোর জন্য প্যাচ ফাইলটি ব্যবহার করতে পারেন।
- আপনার অ্যাপটি এমনভাবে তৈরি করুন যাতে এটি ডিভাইসের শেয়ার্ড স্টোরেজ লোকেশনে থাকা এক্সপ্যানশন ফাইলগুলোর রিসোর্স ব্যবহার করতে পারে।
মনে রাখবেন যে, আপনি এক্সপ্যানশন ফাইলগুলো ডিলিট, মুভ বা রিনেম করতে পারবেন না।
যদি আপনার অ্যাপের জন্য কোনো নির্দিষ্ট ফরম্যাটের প্রয়োজন না হয়, তাহলে আমরা পরামর্শ দিই যে আপনি আপনার এক্সপ্যানশন ফাইলগুলোর জন্য ZIP ফাইল তৈরি করুন এবং তারপর APK এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার করে সেগুলো পড়ুন।
- আপনার অ্যাপের প্রধান অ্যাক্টিভিটিতে এমন লজিক যোগ করুন যা অ্যাপ চালু হওয়ার সময় ডিভাইসে এক্সপ্যানশন ফাইলগুলো আছে কিনা তা পরীক্ষা করবে। যদি ফাইলগুলো ডিভাইসে না থাকে, তাহলে গুগল প্লে-র অ্যাপ লাইসেন্সিং পরিষেবা ব্যবহার করে এক্সপ্যানশন ফাইলগুলোর ইউআরএল-এর জন্য অনুরোধ করুন, তারপর সেগুলো ডাউনলোড করে সংরক্ষণ করুন।
আপনার লেখার কোডের পরিমাণ ব্যাপকভাবে কমাতে এবং ডাউনলোডের সময় ব্যবহারকারীর ভালো অভিজ্ঞতা নিশ্চিত করতে, আমরা আপনার ডাউনলোড প্রক্রিয়াটি বাস্তবায়নের জন্য ডাউনলোডার লাইব্রেরি ব্যবহার করার পরামর্শ দিই।
লাইব্রেরি ব্যবহার না করে যদি আপনি নিজের ডাউনলোড পরিষেবা তৈরি করেন, তবে মনে রাখবেন যে আপনাকে অবশ্যই এক্সপ্যানশন ফাইলগুলির নাম পরিবর্তন করা যাবে না এবং সেগুলিকে সঠিক স্টোরেজ লোকেশনে সংরক্ষণ করতে হবে।
আপনার অ্যাপ ডেভেলপমেন্ট শেষ হয়ে গেলে, আপনার এক্সপ্যানশন ফাইলগুলো পরীক্ষা করার জন্য নির্দেশিকাটি অনুসরণ করুন।
নিয়ম ও সীমাবদ্ধতা
প্লে কনসোল ব্যবহার করে আপনার অ্যাপ আপলোড করার সময় APK এক্সপ্যানশন ফাইল যোগ করার সুবিধাটি পাওয়া যায়। প্রথমবারের মতো আপনার অ্যাপ আপলোড করার সময় অথবা এক্সপ্যানশন ফাইল ব্যবহার করে এমন কোনো অ্যাপ আপডেট করার সময়, আপনাকে নিম্নলিখিত নিয়ম এবং সীমাবদ্ধতাগুলো সম্পর্কে অবশ্যই সচেতন থাকতে হবে:
- প্রতিটি এক্সপ্যানশন ফাইলের সাইজ ২ জিবির বেশি হতে পারবে না।
- গুগল প্লে থেকে আপনার এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার জন্য, ব্যবহারকারীকে অবশ্যই গুগল প্লে থেকে আপনার অ্যাপটি সংগ্রহ করতে হবে । যদি অ্যাপটি অন্য কোনো উপায়ে ইনস্টল করা হয়ে থাকে, তাহলে গুগল প্লে আপনার এক্সপ্যানশন ফাইলগুলোর ইউআরএল সরবরাহ করবে না।
- আপনার অ্যাপের ভেতর থেকে ডাউনলোড করার সময়, গুগল প্লে প্রতিটি ফাইলের জন্য যে URL প্রদান করে তা প্রতিটি ডাউনলোডের জন্য অনন্য হয় এবং আপনার অ্যাপে দেওয়ার কিছুক্ষণের মধ্যেই সেটির মেয়াদ শেষ হয়ে যায়।
- আপনি যদি একটি নতুন APK দিয়ে আপনার অ্যাপ আপডেট করেন অথবা একই অ্যাপের জন্য একাধিক APK আপলোড করেন, তাহলে আপনি পূর্ববর্তী কোনো APK-এর জন্য আপলোড করা এক্সপ্যানশন ফাইলগুলো নির্বাচন করতে পারবেন। এক্সপ্যানশন ফাইলটির নাম পরিবর্তন হয় না — এটি মূলত যে APK-এর সাথে যুক্ত ছিল, সেই APK দ্বারা প্রাপ্ত সংস্করণটিই ধরে রাখে।
- যদি আপনি বিভিন্ন ডিভাইসের জন্য ভিন্ন ভিন্ন এক্সপ্যানশন ফাইল সরবরাহ করার উদ্দেশ্যে একাধিক APK একসাথে ব্যবহার করেন, তাহলেও আপনাকে প্রতিটি ডিভাইসের জন্য আলাদা APK আপলোড করতে হবে, যাতে একটি অনন্য
versionCodeভ্যালু প্রদান করা যায় এবং প্রতিটি APK-এর জন্য ভিন্ন ভিন্ন ফিল্টার ঘোষণা করা যায়। - শুধুমাত্র এক্সপ্যানশন ফাইল পরিবর্তন করে আপনি আপনার অ্যাপ আপডেট করতে পারবেন না— আপনার অ্যাপ আপডেট করার জন্য আপনাকে অবশ্যই একটি নতুন APK আপলোড করতে হবে । যদি আপনার পরিবর্তনগুলো শুধুমাত্র এক্সপ্যানশন ফাইলের অ্যাসেটগুলোর সাথে সম্পর্কিত হয়, তাহলে আপনি কেবল
versionCode(এবং সম্ভবতversionNameও) পরিবর্তন করে আপনার APK আপডেট করতে পারেন। - আপনার
obb/ডিরেক্টরিতে অন্য কোনো ডেটা সংরক্ষণ করবেন না । যদি আপনাকে কিছু ডেটা আনপ্যাক করতেই হয়, তবে তাgetExternalFilesDir()দ্বারা নির্দিষ্ট করা স্থানে সংরক্ষণ করুন। -
.obbএক্সপ্যানশন ফাইলটি ডিলিট বা রিনেম করবেন না (যদি না আপনি কোনো আপডেট করছেন)। এমনটা করলে গুগল প্লে (অথবা আপনার অ্যাপটি নিজেই) এক্সপ্যানশন ফাইলটি বারবার ডাউনলোড করবে। - ম্যানুয়ালি কোনো এক্সপ্যানশন ফাইল আপডেট করার সময়, আপনাকে অবশ্যই পূর্ববর্তী এক্সপ্যানশন ফাইলটি মুছে ফেলতে হবে।
সম্প্রসারণ ফাইলগুলি ডাউনলোড করা হচ্ছে
বেশিরভাগ ক্ষেত্রে, গুগল প্লে যখন APK ইনস্টল বা আপডেট করে, ঠিক সেই সময়েই আপনার এক্সপ্যানশন ফাইলগুলো ডাউনলোড করে ডিভাইসে সেভ করে রাখে। এর ফলে, আপনার অ্যাপটি প্রথমবার চালু হওয়ার সময়ই এক্সপ্যানশন ফাইলগুলো উপলব্ধ থাকে। তবে, কিছু কিছু ক্ষেত্রে গুগল প্লে-র অ্যাপ লাইসেন্সিং সার্ভিসের প্রতিক্রিয়া হিসেবে আপনাকে দেওয়া একটি URL থেকে অনুরোধ করে আপনার অ্যাপটিকে নিজেই এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে হয়।
আপনার এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার জন্য প্রয়োজনীয় মূল কার্যপ্রণালীটি হলো নিম্নরূপ:
- আপনার অ্যাপ চালু হলে, শেয়ার্ড স্টোরেজ লোকেশনে (
Android/obb/<package-name>/ডিরেক্টরিতে) এক্সপ্যানশন ফাইলগুলো খুঁজুন।- যদি এক্সপ্যানশন ফাইলগুলো থাকে, তাহলে আর কোনো সমস্যা নেই এবং আপনার অ্যাপটি কাজ চালিয়ে যেতে পারে।
- যদি সম্প্রসারণ ফাইলগুলো না থাকে:
- আপনার অ্যাপের এক্সপ্যানশন ফাইলের নাম, সাইজ এবং ইউআরএল পেতে গুগল প্লে-এর অ্যাপ লাইসেন্সিং ব্যবহার করে একটি অনুরোধ করুন।
- গুগল প্লে দ্বারা প্রদত্ত URL ব্যবহার করে এক্সপ্যানশন ফাইলগুলি ডাউনলোড ও সংরক্ষণ করুন। আপনাকে অবশ্যই ফাইলগুলি শেয়ার্ড স্টোরেজ লোকেশনে (
Android/obb/<package-name>/) সংরক্ষণ করতে হবে এবং গুগল প্লে-এর উত্তরে দেওয়া সঠিক ফাইলের নামটি ব্যবহার করতে হবে।দ্রষ্টব্য: আপনার এক্সপ্যানশন ফাইলগুলোর জন্য গুগল প্লে যে URL প্রদান করে, তা প্রতিটি ডাউনলোডের জন্য অনন্য এবং আপনার অ্যাপে দেওয়ার অল্প সময়ের মধ্যেই সেটির মেয়াদ শেষ হয়ে যায়।
আপনার অ্যাপটি যদি বিনামূল্যে হয় (পেইড অ্যাপ নয়), তাহলে সম্ভবত আপনি অ্যাপ লাইসেন্সিং পরিষেবাটি ব্যবহার করেননি। এটি মূলত আপনার অ্যাপের জন্য লাইসেন্সিং নীতি প্রয়োগ করতে এবং ব্যবহারকারীর আপনার অ্যাপটি ব্যবহার করার অধিকার আছে কিনা তা নিশ্চিত করার জন্য ডিজাইন করা হয়েছে (তারা গুগল প্লে-তে এর জন্য যথাযথভাবে অর্থ প্রদান করেছে)। এক্সপ্যানশন ফাইলের কার্যকারিতা সহজ করার জন্য, লাইসেন্সিং পরিষেবাটিকে উন্নত করা হয়েছে যাতে এটি আপনার অ্যাপে এমন একটি প্রতিক্রিয়া প্রদান করে, যেখানে গুগল প্লে-তে হোস্ট করা আপনার অ্যাপের এক্সপ্যানশন ফাইলগুলির URL অন্তর্ভুক্ত থাকে। সুতরাং, আপনার অ্যাপ ব্যবহারকারীদের জন্য বিনামূল্যে হলেও, APK এক্সপ্যানশন ফাইল ব্যবহার করার জন্য আপনাকে লাইসেন্স ভেরিফিকেশন লাইব্রেরি (LVL) অন্তর্ভুক্ত করতে হবে। অবশ্যই, আপনার অ্যাপ বিনামূল্যে হলে, আপনাকে লাইসেন্স যাচাইকরণ প্রয়োগ করার প্রয়োজন নেই—আপনার এক্সপ্যানশন ফাইলগুলির URL ফেরত দেয় এমন অনুরোধটি সম্পাদন করার জন্য কেবল লাইব্রেরিটির প্রয়োজন।
দ্রষ্টব্য: আপনার অ্যাপটি বিনামূল্যে হোক বা না হোক, গুগল প্লে শুধুমাত্র তখনই এক্সপ্যানশন ফাইলের URL দেখায়, যখন ব্যবহারকারী গুগল প্লে থেকে আপনার অ্যাপটি সংগ্রহ করেন।
LVL-এর পাশাপাশি, আপনার এমন এক সেট কোডের প্রয়োজন হবে যা HTTP সংযোগের মাধ্যমে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করে ডিভাইসের শেয়ার্ড স্টোরেজের সঠিক স্থানে সংরক্ষণ করবে। আপনার অ্যাপে এই পদ্ধতিটি যুক্ত করার সময়, কয়েকটি বিষয় আপনার বিবেচনায় রাখা উচিত:
- ডিভাইসটিতে এক্সপ্যানশন ফাইলগুলোর জন্য পর্যাপ্ত জায়গা নাও থাকতে পারে, তাই ডাউনলোড শুরু করার আগে যাচাই করে নেওয়া উচিত এবং জায়গা না থাকলে ব্যবহারকারীকে সতর্ক করা উচিত।
- ফাইল ডাউনলোড একটি ব্যাকগ্রাউন্ড সার্ভিসে হওয়া উচিত, যাতে ব্যবহারকারীর কার্যকলাপে বাধা সৃষ্টি না হয় এবং ডাউনলোড সম্পন্ন হওয়া পর্যন্ত ব্যবহারকারী আপনার অ্যাপ থেকে বেরিয়ে যেতে পারেন।
- অনুরোধ এবং ডাউনলোডের সময় বিভিন্ন ধরনের ত্রুটি ঘটতে পারে, যা আপনাকে দক্ষতার সাথে সামাল দিতে হবে।
- ডাউনলোড চলাকালীন নেটওয়ার্ক সংযোগ পরিবর্তিত হতে পারে, তাই আপনার উচিত এই ধরনের পরিবর্তনগুলো সামাল দেওয়া এবং ডাউনলোড বাধাগ্রস্ত হলে, সম্ভব হলে তা পুনরায় শুরু করা।
- ব্যাকগ্রাউন্ডে ডাউনলোড চলাকালীন, আপনাকে একটি নোটিফিকেশন দিতে হবে যা ডাউনলোডের অগ্রগতি দেখাবে, ডাউনলোড শেষ হলে ব্যবহারকারীকে জানাবে এবং নির্বাচিত হলে ব্যবহারকারীকে আপনার অ্যাপে ফিরিয়ে নিয়ে যাবে।
আপনার জন্য এই কাজটি সহজ করতে, আমরা ডাউনলোডার লাইব্রেরি তৈরি করেছি, যা লাইসেন্সিং সার্ভিসের মাধ্যমে এক্সপ্যানশন ফাইলের URL-গুলো অনুরোধ করে, এক্সপ্যানশন ফাইলগুলো ডাউনলোড করে, উপরে তালিকাভুক্ত সমস্ত কাজ সম্পাদন করে এবং এমনকি আপনার অ্যাক্টিভিটিকে ডাউনলোডটি পজ ও রিজুম করার সুযোগ দেয়। আপনার অ্যাপে ডাউনলোডার লাইব্রেরি এবং কয়েকটি কোড হুক যোগ করার মাধ্যমে, এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার প্রায় সমস্ত কাজ আপনার জন্য আগে থেকেই কোড করা থাকে। তাই, আপনার পক্ষ থেকে ন্যূনতম প্রচেষ্টায় সেরা ব্যবহারকারীর অভিজ্ঞতা প্রদানের জন্য, আমরা আপনার এক্সপ্যানশন ফাইলগুলো ডাউনলোড করতে ডাউনলোডার লাইব্রেরি ব্যবহার করার পরামর্শ দিই। নিম্নলিখিত বিভাগগুলিতে আপনার অ্যাপে লাইব্রেরিটি কীভাবে ইন্টিগ্রেট করবেন তা ব্যাখ্যা করা হয়েছে।
আপনি যদি গুগল প্লে ইউআরএল ব্যবহার করে এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার জন্য নিজের কোনো সমাধান তৈরি করতে চান, তবে আপনাকে অবশ্যই অ্যাপের লাইসেন্সিং ডকুমেন্টেশন অনুসরণ করে একটি লাইসেন্স অনুরোধ করতে হবে এবং তারপর রেসপন্স এক্সট্রাস থেকে এক্সপ্যানশন ফাইলের নাম, সাইজ ও ইউআরএল সংগ্রহ করতে হবে। আপনার লাইসেন্সিং পলিসি হিসেবে APKExpansionPolicy ক্লাসটি (যা লাইসেন্স ভেরিফিকেশন লাইব্রেরিতে অন্তর্ভুক্ত) ব্যবহার করা উচিত, যা লাইসেন্সিং সার্ভিস থেকে এক্সপ্যানশন ফাইলের নাম, সাইজ ও ইউআরএলগুলো সংগ্রহ করে।
ডাউনলোডার লাইব্রেরি সম্পর্কে
আপনার অ্যাপের সাথে APK এক্সপ্যানশন ফাইল ব্যবহার করতে এবং আপনার পক্ষ থেকে ন্যূনতম প্রচেষ্টায় সেরা ব্যবহারকারীর অভিজ্ঞতা প্রদান করতে, আমরা আপনাকে Google Play APK এক্সপ্যানশন লাইব্রেরি প্যাকেজের অন্তর্ভুক্ত ডাউনলোডার লাইব্রেরিটি ব্যবহার করার পরামর্শ দিই। এই লাইব্রেরিটি একটি ব্যাকগ্রাউন্ড সার্ভিসে আপনার এক্সপ্যানশন ফাইলগুলো ডাউনলোড করে, ডাউনলোডের অবস্থা জানিয়ে ব্যবহারকারীকে নোটিফিকেশন দেখায়, নেটওয়ার্ক সংযোগ বিচ্ছিন্ন হলে তা সামাল দেয়, সম্ভব হলে ডাউনলোড পুনরায় শুরু করে এবং আরও অনেক কিছু করে।
ডাউনলোডার লাইব্রেরি ব্যবহার করে এক্সপ্যানশন ফাইল ডাউনলোড কার্যকর করতে, আপনাকে যা করতে হবে তা হলো:
- একটি বিশেষ
Serviceসাবক্লাস এবংBroadcastReceiverসাবক্লাস এক্সটেন্ড করুন, যার প্রতিটির জন্য আপনার মাত্র কয়েকটি কোড লাইন প্রয়োজন হবে। - আপনার প্রধান অ্যাক্টিভিটিতে এমন একটি লজিক যোগ করুন যা পরীক্ষা করে দেখবে এক্সপ্যানশন ফাইলগুলো আগে থেকেই ডাউনলোড করা হয়েছে কিনা এবং যদি না হয়ে থাকে, তবে ডাউনলোড প্রক্রিয়াটি শুরু করবে ও একটি অগ্রগতি UI প্রদর্শন করবে।
- আপনার মেইন অ্যাক্টিভিটিতে কয়েকটি মেথডসহ একটি কলব্যাক ইন্টারফেস তৈরি করুন, যা ডাউনলোডের অগ্রগতি সম্পর্কে আপডেট গ্রহণ করবে।
নিম্নলিখিত বিভাগগুলিতে ডাউনলোডার লাইব্রেরি ব্যবহার করে আপনার অ্যাপটি কীভাবে সেট আপ করবেন তা ব্যাখ্যা করা হয়েছে।
ডাউনলোডার লাইব্রেরি ব্যবহারের প্রস্তুতি
ডাউনলোডার লাইব্রেরি ব্যবহার করার জন্য, আপনাকে এসডিকে ম্যানেজার থেকে দুটি প্যাকেজ ডাউনলোড করতে হবে এবং আপনার অ্যাপে উপযুক্ত লাইব্রেরিগুলো যুক্ত করতে হবে।
প্রথমে, অ্যান্ড্রয়েড এসডিকে ম্যানেজার খুলুন ( টুলস > এসডিকে ম্যানেজার ), এবং অ্যাপিয়ারেন্স অ্যান্ড বিহেভিয়ার > সিস্টেম সেটিংস > অ্যান্ড্রয়েড এসডিকে-এর অধীনে, এসডিকে টুলস ট্যাব থেকে নির্বাচন করে ডাউনলোড করুন:
- গুগল প্লে লাইসেন্সিং লাইব্রেরি প্যাকেজ
- গুগল প্লে এপিকে সম্প্রসারণ লাইব্রেরি প্যাকেজ
লাইসেন্স যাচাইকরণ লাইব্রেরি এবং ডাউনলোডার লাইব্রেরির জন্য একটি নতুন লাইব্রেরি মডিউল তৈরি করুন। প্রতিটি লাইব্রেরির জন্য:
- ফাইল > নতুন > নতুন মডিউল নির্বাচন করুন।
- Create New Module উইন্ডোতে, Android Library নির্বাচন করুন এবং তারপর Next নির্বাচন করুন।
- 'Google Play License Library' এবং 'Google Play Downloader Library'-এর মতো একটি অ্যাপ/লাইব্রেরির নাম নির্দিষ্ট করুন, ন্যূনতম SDK স্তর (Minimum SDK level) বেছে নিন, তারপর 'Finish ' নির্বাচন করুন।
- ফাইল > প্রজেক্ট স্ট্রাকচার নির্বাচন করুন।
- প্রোপার্টিজ ট্যাবটি নির্বাচন করুন এবং লাইব্রেরি রিপোজিটরি- তে,
<sdk>/extras/google/ডিরেক্টরি থেকে লাইব্রেরিটি প্রবেশ করান (লাইসেন্স ভেরিফিকেশন লাইব্রেরির জন্যplay_licensing/অথবা ডাউনলোডার লাইব্রেরির জন্যplay_apk_expansion/downloader_library/)। - নতুন মডিউলটি তৈরি করতে OK নির্বাচন করুন।
দ্রষ্টব্য: ডাউনলোডার লাইব্রেরিটি লাইসেন্স ভেরিফিকেশন লাইব্রেরির উপর নির্ভরশীল। ডাউনলোডার লাইব্রেরির প্রজেক্ট প্রপার্টিজে লাইসেন্স ভেরিফিকেশন লাইব্রেরিটি অবশ্যই যোগ করুন।
অথবা, কমান্ড লাইন থেকে, লাইব্রেরিগুলো অন্তর্ভুক্ত করতে আপনার প্রজেক্টটি আপডেট করুন:
-
<sdk>/tools/ডিরেক্টরিতে পরিবর্তন করুন। - আপনার প্রজেক্টে 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
আপনার অ্যাপে লাইসেন্স ভেরিফিকেশন লাইব্রেরি এবং ডাউনলোডার লাইব্রেরি উভয়ই যুক্ত করা থাকলে, আপনি গুগল প্লে থেকে এক্সপ্যানশন ফাইল ডাউনলোড করার সুবিধাটি দ্রুত ইন্টিগ্রেট করতে পারবেন। এক্সপ্যানশন ফাইলগুলোর জন্য আপনি যে ফরম্যাটটি বেছে নেবেন এবং শেয়ার্ড স্টোরেজ থেকে কীভাবে সেগুলো পড়বেন, তা একটি আলাদা বাস্তবায়ন পদ্ধতি, যা আপনার অ্যাপের প্রয়োজন অনুযায়ী বিবেচনা করা উচিত।
পরামর্শ: এপিকে এক্সপ্যানশন প্যাকেজে একটি নমুনা অ্যাপ রয়েছে যা দেখায় কিভাবে একটি অ্যাপে ডাউনলোডার লাইব্রেরি ব্যবহার করতে হয়। এই নমুনাটিতে এপিকে এক্সপ্যানশন প্যাকেজে উপলব্ধ এপিকে এক্সপ্যানশন জিপ লাইব্রেরি নামক একটি তৃতীয় লাইব্রেরি ব্যবহার করা হয়েছে। আপনি যদি আপনার এক্সপ্যানশন ফাইলগুলোর জন্য জিপ ফাইল ব্যবহার করার পরিকল্পনা করেন, তাহলে আমরা আপনাকে আপনার অ্যাপে এপিকে এক্সপ্যানশন জিপ লাইব্রেরিটিও যুক্ত করার পরামর্শ দিচ্ছি। আরও তথ্যের জন্য, নিচে এপিকে এক্সপ্যানশন জিপ লাইব্রেরি ব্যবহার বিষয়ক অংশটি দেখুন।
ব্যবহারকারীর অনুমতি ঘোষণা করা
এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার জন্য ডাউনলোডার লাইব্রেরির বেশ কিছু অনুমতির প্রয়োজন হয়, যেগুলো আপনাকে আপনার অ্যাপের ম্যানিফেস্ট ফাইলে অবশ্যই ঘোষণা করতে হবে। সেগুলো হলো:
<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>
দ্রষ্টব্য: ডিফল্টরূপে, ডাউনলোডার লাইব্রেরির জন্য এপিআই লেভেল ৪ প্রয়োজন, কিন্তু এপিকে এক্সপ্যানশন জিপ লাইব্রেরির জন্য এপিআই লেভেল ৫ প্রয়োজন।
ডাউনলোডার পরিষেবা বাস্তবায়ন করা হচ্ছে
ব্যাকগ্রাউন্ডে ডাউনলোড করার জন্য, ডাউনলোডার লাইব্রেরি তার নিজস্ব Service সাবক্লাস প্রদান করে যার নাম DownloaderService , এবং আপনার এটি এক্সটেন্ড করা উচিত। আপনার জন্য এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার পাশাপাশি, DownloaderService আরও যা করে তা হলো:
- একটি
BroadcastReceiverরেজিস্টার করে যা ডিভাইসের নেটওয়ার্ক সংযোগের পরিবর্তন (CONNECTIVITY_ACTIONব্রডকাস্ট) শোনার জন্য কাজ করে, যাতে প্রয়োজনে (যেমন সংযোগ বিচ্ছিন্ন হওয়ার কারণে) ডাউনলোডটি থামানো যায় এবং সম্ভব হলে (সংযোগ ফিরে এলে) ডাউনলোডটি পুনরায় শুরু করা যায়। - যেসব ক্ষেত্রে সার্ভিসটি বন্ধ হয়ে যায়, সেসব ক্ষেত্রে ডাউনলোডটি পুনরায় চেষ্টা করার জন্য একটি
RTC_WAKEUPঅ্যালার্ম নির্ধারণ করে। - একটি কাস্টম
Notificationতৈরি করে যা ডাউনলোডের অগ্রগতি এবং যেকোনো ত্রুটি বা অবস্থার পরিবর্তন প্রদর্শন করে। - আপনার অ্যাপকে ম্যানুয়ালি ডাউনলোডটি থামানো এবং পুনরায় শুরু করার অনুমতি দেয়।
- এক্সপ্যানশন ফাইলগুলো ডাউনলোড করার আগে, এটি যাচাই করে যে শেয়ার্ড স্টোরেজটি মাউন্ট করা ও উপলব্ধ আছে কিনা, ফাইলগুলো আগে থেকে বিদ্যমান নেই কিনা এবং পর্যাপ্ত জায়গা আছে কিনা। এরপর যদি এর কোনোটি সত্য না হয়, তবে ব্যবহারকারীকে অবহিত করে।
আপনাকে যা করতে হবে তা হলো, আপনার অ্যাপে DownloaderService ক্লাসকে এক্সটেন্ড করে একটি ক্লাস তৈরি করতে হবে এবং অ্যাপের নির্দিষ্ট বিবরণ দেওয়ার জন্য তিনটি মেথড ওভাররাইড করতে হবে:
-
getPublicKey() - এর থেকে অবশ্যই একটি স্ট্রিং রিটার্ন করতে হবে, যা হবে আপনার পাবলিশার অ্যাকাউন্টের Base64-এনকোডেড RSA পাবলিক কী। এই পাবলিক কী-টি প্লে কনসোলের প্রোফাইল পেজ থেকে পাওয়া যাবে ( লাইসেন্সিংয়ের জন্য সেট আপ দেখুন)।
-
getSALT() - এটি অবশ্যই র্যান্ডম বাইটের একটি অ্যারে রিটার্ন করবে যা লাইসেন্সিং
PolicyএকটিObfuscatorতৈরি করতে ব্যবহার করে। সল্ট নিশ্চিত করে যে আপনার অবফাসকেটেডSharedPreferencesফাইল, যেখানে আপনার লাইসেন্সিং ডেটা সংরক্ষিত থাকে, তা অনন্য এবং অশনাক্তযোগ্য হবে। -
getAlarmReceiverClassName() - এটি অবশ্যই আপনার অ্যাপের
BroadcastReceiverএর ক্লাস নেম রিটার্ন করবে, যেটি ডাউনলোড পুনরায় শুরু করার অ্যালার্মটি গ্রহণ করবে (যা ডাউনলোডার সার্ভিস অপ্রত্যাশিতভাবে বন্ধ হয়ে গেলে ঘটতে পারে)।
উদাহরণস্বরূপ, এখানে DownloaderService এর একটি সম্পূর্ণ বাস্তবায়ন দেওয়া হলো:
কোটলিন
// 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 ভ্যালুটি আপনার পাবলিশার অ্যাকাউন্টের পাবলিক কী দিয়ে আপডেট করতে হবে। আপনি ডেভেলপার কনসোলে আপনার প্রোফাইল তথ্যের অধীনে কী-টি খুঁজে পাবেন। আপনার ডাউনলোডগুলো পরীক্ষা করার সময়েও এটি আবশ্যক।
আপনার ম্যানিফেস্ট ফাইলে সার্ভিসটি ডিক্লেয়ার করতে মনে রাখবেন:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
অ্যালার্ম রিসিভার বাস্তবায়ন করা
ফাইল ডাউনলোডের অগ্রগতি নিরীক্ষণ করতে এবং প্রয়োজনে ডাউনলোড পুনরায় শুরু করতে, DownloaderService একটি RTC_WAKEUP অ্যালার্ম নির্ধারণ করে, যা আপনার অ্যাপের BroadcastReceiver এ একটি Intent পাঠায়। আপনাকে অবশ্যই BroadcastReceiver টিকে এমনভাবে সংজ্ঞায়িত করতে হবে যাতে এটি Downloader Library-এর একটি API কল করে, যা ডাউনলোডের অবস্থা পরীক্ষা করে এবং প্রয়োজনে তা পুনরায় শুরু করে।
আপনাকে শুধু onReceive() মেথডটি ওভাররাইড করে DownloaderClientMarshaller.startDownloadServiceIfRequired() কল করতে হবে।
উদাহরণস্বরূপ:
কোটলিন
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>
ডাউনলোড শুরু হচ্ছে
আপনার অ্যাপের প্রধান অ্যাক্টিভিটি (যেটি আপনার লঞ্চার আইকন দ্বারা চালু হয়) ডিভাইসে এক্সপ্যানশন ফাইলগুলো আগে থেকেই আছে কিনা তা যাচাই করার এবং না থাকলে ডাউনলোড শুরু করার দায়িত্বে থাকে।
ডাউনলোডার লাইব্রেরি ব্যবহার করে ডাউনলোড শুরু করতে নিম্নলিখিত পদ্ধতিগুলো অনুসরণ করতে হবে:
- ফাইলগুলো ডাউনলোড হয়েছে কিনা তা যাচাই করুন।
এই প্রক্রিয়াটিতে সাহায্য করার জন্য ডাউনলোডার লাইব্রেরির
Helperক্লাসে কিছু এপিআই অন্তর্ভুক্ত রয়েছে:-
getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode) -
doesFileExist(Context c, String fileName, long fileSize)
উদাহরণস্বরূপ, Apk Expansion প্যাকেজে দেওয়া নমুনা অ্যাপটি ডিভাইসে এক্সপ্যানশন ফাইলগুলো আগে থেকেই আছে কিনা তা পরীক্ষা করার জন্য অ্যাক্টিভিটির
onCreate()মেথডে নিম্নলিখিত মেথডটি কল করে:কোটলিন
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 রিটার্ন করে, তাহলে অ্যাপটিকে অবশ্যই ডাউনলোড শুরু করতে হবে।
-
-
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হয় , তাহলে ফাইলগুলি উপলব্ধ আছে এবং আপনার অ্যাপ চালু হতে পারে।উদাহরণস্বরূপ:
কোটলিন
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 }
-
- যখন
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()ফলাফলের জবাবে এইভাবে সাড়া দিতে পারেন:কোটলিন
// 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()কল আসে, এবং তখনই আপনার অ্যাপেরContextপাস করেIStubএর উপরconnect()কল করা উচিত। এর বিপরীতে, আপনার অ্যাক্টিভিটিরonStop()কলব্যাকেdisconnect()কল করা উচিত।কোটলিন
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অবজেক্ট পাস করে। সার্ভিসে রিকোয়েস্ট পাঠাতে, যেমন ডাউনলোড পজ বা রিজুম করতে, আপনাকে অবশ্যইDownloaderServiceMarshaller.CreateProxy()কল করে সার্ভিসের সাথে সংযুক্তIDownloaderServiceইন্টারফেসটি গ্রহণ করতে হবে।একটি প্রস্তাবিত বাস্তবায়ন দেখতে এইরকম:
কোটলিন
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()কল করে প্রতিটি স্টেটের জন্য একটি সংশ্লিষ্ট স্ট্রিং অনুরোধ করতে পারেন। এটি ডাউনলোডার লাইব্রেরির সাথে বান্ডেল করা স্ট্রিংগুলোর মধ্যে একটির রিসোর্স আইডি রিটার্ন করে। উদাহরণস্বরূপ, "Download paused because you are roaming" স্ট্রিংটিSTATE_PAUSED_ROAMINGসাথে সম্পর্কিত। -
onDownloadProgress(DownloadProgressInfo progress) - ডাউনলোড সার্ভিসটি একটি
DownloadProgressInfoঅবজেক্ট সরবরাহ করার জন্য এটিকে কল করে, যা ডাউনলোডের অগ্রগতি সম্পর্কে বিভিন্ন তথ্য বর্ণনা করে, যার মধ্যে রয়েছে আনুমানিক অবশিষ্ট সময়, বর্তমান গতি, সামগ্রিক অগ্রগতি এবং মোট পরিমাণ, যাতে আপনি ডাউনলোড অগ্রগতির UI আপডেট করতে পারেন।
পরামর্শ: ডাউনলোড অগ্রগতির UI আপডেট করে এমন কলব্যাকগুলির উদাহরণের জন্য, Apk Expansion প্যাকেজের সাথে প্রদত্ত নমুনা অ্যাপের SampleDownloaderActivity দেখুন।
IDownloaderService ইন্টারফেসের কিছু পাবলিক মেথড যা আপনার কাজে লাগতে পারে, সেগুলো হলো:
-
requestPauseDownload() - ডাউনলোডটি থামিয়ে দেয়।
-
requestContinueDownload() - স্থগিত থাকা ডাউনলোড পুনরায় শুরু করে।
-
setDownloadFlags(int flags) - কোন ধরনের নেটওয়ার্কে ফাইল ডাউনলোড করা যাবে, তার জন্য ব্যবহারকারীর পছন্দ নির্ধারণ করে। বর্তমান ইমপ্লিমেন্টেশনটি একটি ফ্ল্যাগ,
FLAGS_DOWNLOAD_OVER_CELLULAR, সমর্থন করে, কিন্তু আপনি অন্য ফ্ল্যাগও যোগ করতে পারেন। ডিফল্টরূপে, এই ফ্ল্যাগটি সক্রিয় থাকে না , তাই এক্সপ্যানশন ফাইল ডাউনলোড করার জন্য ব্যবহারকারীকে অবশ্যই ওয়াই-ফাই-এ সংযুক্ত থাকতে হবে। আপনি সেলুলার নেটওয়ার্কের মাধ্যমে ডাউনলোড সক্ষম করার জন্য একটি ইউজার প্রেফারেন্স প্রদান করতে চাইতে পারেন। সেক্ষেত্রে, আপনি কল করতে পারেন:কোটলিন
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
জাভা
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
APKExpansionPolicy ব্যবহার করে
If you decide to build your own downloader service instead of using the Google Play Downloader Library , you should still use the APKExpansionPolicy that's provided in the License Verification Library. The APKExpansionPolicy class is nearly identical to ServerManagedPolicy (available in the Google Play License Verification Library) but includes additional handling for the APK expansion file response extras.
Note: If you do use the Downloader Library as discussed in the previous section, the library performs all interaction with the APKExpansionPolicy so you don't have to use this class directly.
The class includes methods to help you get the necessary information about the available expansion files:
-
getExpansionURLCount() -
getExpansionURL(int index) -
getExpansionFileName(int index) -
getExpansionFileSize(int index)
For more information about how to use the APKExpansionPolicy when you're not using the Downloader Library , see the documentation for Adding Licensing to Your App , which explains how to implement a license policy such as this one.
Reading the Expansion File
Once your APK expansion files are saved on the device, how you read your files depends on the type of file you've used. As discussed in the overview , your expansion files can be any kind of file you want, but are renamed using a particular file name format and are saved to <shared-storage>/Android/obb/<package-name>/ .
Regardless of how you read your files, you should always first check that the external storage is available for reading. There's a chance that the user has the storage mounted to a computer over USB or has actually removed the SD card.
Note: When your app starts, you should always check whether the external storage space is available and readable by calling getExternalStorageState() . This returns one of several possible strings that represent the state of the external storage. In order for it to be readable by your app, the return value must be MEDIA_MOUNTED .
Getting the file names
As described in the overview , your APK expansion files are saved using a specific file name format:
[main|patch].<expansion-version>.<package-name>.obb
To get the location and names of your expansion files, you should use the getExternalStorageDirectory() and getPackageName() methods to construct the path to your files.
Here's a method you can use in your app to get an array containing the complete path to both your expansion files:
কোটলিন
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; }
You can call this method by passing it your app Context and the desired expansion file's version.
There are many ways you could determine the expansion file version number. One simple way is to save the version in a SharedPreferences file when the download begins, by querying the expansion file name with the APKExpansionPolicy class's getExpansionFileName(int index) method. You can then get the version code by reading the SharedPreferences file when you want to access the expansion file.
For more information about reading from the shared storage, see the Data Storage documentation.
Using the APK Expansion Zip Library
The Google Market Apk Expansion package includes a library called the APK Expansion Zip Library (located in <sdk>/extras/google/google_market_apk_expansion/zip_file/ ). This is an optional library that helps you read your expansion files when they're saved as ZIP files. Using this library allows you to easily read resources from your ZIP expansion files as a virtual file system.
The APK Expansion Zip Library includes the following classes and APIs:
-
APKExpansionSupport - Provides some methods to access expansion file names and ZIP files:
-
getAPKExpansionFiles() - The same method shown above that returns the complete file path to both expansion files.
-
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion) - Returns a
ZipResourceFilerepresenting the sum of both the main file and patch file. That is, if you specify both themainVersionand thepatchVersion, this returns aZipResourceFilethat provides read access to all the data, with the patch file's data merged on top of the main file.
-
-
ZipResourceFile - Represents a ZIP file on the shared storage and performs all the work to provide a virtual file system based on your ZIP files. You can get an instance using
APKExpansionSupport.getAPKExpansionZipFile()or with theZipResourceFileby passing it the path to your expansion file. This class includes a variety of useful methods, but you generally don't need to access most of them. A couple of important methods are:-
getInputStream(String assetPath) - Provides an
InputStreamto read a file within the ZIP file. TheassetPathmust be the path to the desired file, relative to the root of the ZIP file contents. -
getAssetFileDescriptor(String assetPath) - Provides an
AssetFileDescriptorfor a file within the ZIP file. TheassetPathmust be the path to the desired file, relative to the root of the ZIP file contents. This is useful for certain Android APIs that require anAssetFileDescriptor, such as someMediaPlayerAPIs.
-
-
APEZProvider - Most apps don't need to use this class. This class defines a
ContentProviderthat marshals the data from the ZIP files through a content providerUriin order to provide file access for certain Android APIs that expectUriaccess to media files. For example, this is useful if you want to play a video withVideoView.setVideoURI().
Skipping ZIP compression of media files
If you're using your expansion files to store media files, a ZIP file still allows you to use Android media playback calls that provide offset and length controls (such as MediaPlayer.setDataSource() and SoundPool.load() ). In order for this to work, you must not perform additional compression on the media files when creating the ZIP packages. For example, when using the zip tool, you should use the -n option to specify the file suffixes that should not be compressed:
zip -n .mp4;.ogg main_expansion media_files
Reading from a ZIP file
When using the APK Expansion Zip Library, reading a file from your ZIP usually requires the following:
কোটলিন
// 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);
The above code provides access to any file that exists in either your main expansion file or patch expansion file, by reading from a merged map of all the files from both files. All you need to provide the getAPKExpansionFile() method is your app android.content.Context and the version number for both the main expansion file and patch expansion file.
If you'd rather read from a specific expansion file, you can use the ZipResourceFile constructor with the path to the desired expansion file:
কোটলিন
// 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);
For more information about using this library for your expansion files, look at the sample app's SampleDownloaderActivity class, which includes additional code to verify the downloaded files using CRC. Beware that if you use this sample as the basis for your own implementation, it requires that you declare the byte size of your expansion files in the xAPKS array.
Testing Your Expansion Files
Before publishing your app, there are two things you should test: Reading the expansion files and downloading the files.
Testing file reads
Before you upload your app to Google Play, you should test your app's ability to read the files from the shared storage. All you need to do is add the files to the appropriate location on the device shared storage and launch your app:
- On your device, create the appropriate directory on the shared storage where Google Play will save your files.
For example, if your package name is
com.example.android, you need to create the directoryAndroid/obb/com.example.android/on the shared storage space. (Plug in your test device to your computer to mount the shared storage and manually create this directory.) - Manually add the expansion files to that directory. Be sure that you rename your files to match the file name format that Google Play will use.
For example, regardless of the file type, the main expansion file for the
com.example.androidapp should bemain.0300110.com.example.android.obb. The version code can be whatever value you want. Just remember:- The main expansion file always starts with
mainand the patch file starts withpatch. - The package name always matches that of the APK to which the file is attached on Google Play.
- The main expansion file always starts with
- Now that the expansion file(s) are on the device, you can install and run your app to test your expansion file(s).
Here are some reminders about handling the expansion files:
- Do not delete or rename the
.obbexpansion files (even if you unpack the data to a different location). Doing so will cause Google Play (or your app itself) to repeatedly download the expansion file. - Do not save other data into your
obb/directory . If you must unpack some data, save it into the location specified bygetExternalFilesDir().
Testing file downloads
Because your app must sometimes manually download the expansion files when it first opens, it's important that you test this process to be sure your app can successfully query for the URLs, download the files, and save them to the device.
To test your app's implementation of the manual download procedure, you can publish it to the internal test track, so it's only available to authorized testers. If everything works as expected, your app should begin downloading the expansion files as soon as the main activity starts.
Note: Previously you could test an app by uploading an unpublished "draft" version. This functionality is no longer supported. Instead, you must publish it to an internal, closed, or open testing track. For more information, see Draft Apps are No Longer Supported .
Updating Your app
One of the great benefits to using expansion files on Google Play is the ability to update your app without re-downloading all of the original assets. Because Google Play allows you to provide two expansion files with each APK, you can use the second file as a "patch" that provides updates and new assets. Doing so avoids the need to re-download the main expansion file which could be large and expensive for users.
The patch expansion file is technically the same as the main expansion file and neither the Android system nor Google Play perform actual patching between your main and patch expansion files. Your app code must perform any necessary patches itself.
If you use ZIP files as your expansion files, the APK Expansion Zip Library that's included with the Apk Expansion package includes the ability to merge your patch file with the main expansion file.
Note: Even if you only need to make changes to the patch expansion file, you must still update the APK in order for Google Play to perform an update. If you don't require code changes in the app, you should simply update the versionCode in the manifest.
As long as you don't change the main expansion file that's associated with the APK in the Play Console, users who previously installed your app will not download the main expansion file. Existing users receive only the updated APK and the new patch expansion file (retaining the previous main expansion file).
Here are a few issues to keep in mind regarding updates to expansion files:
- There can be only two expansion files for your app at a time. One main expansion file and one patch expansion file. During an update to a file, Google Play deletes the previous version (and so must your app when performing manual updates).
- When adding a patch expansion file, the Android system does not actually patch your app or main expansion file. You must design your app to support the patch data. However, the Apk Expansion package includes a library for using ZIP files as expansion files, which merges the data from the patch file into the main expansion file so you can easily read all the expansion file data.