فایل های گسترش APK

Google Play ایجاب می کند که APK فشرده ای که کاربران دانلود می کنند بیش از 100 مگابایت نباشد. برای اکثر برنامه ها، این فضای کافی برای همه کدها و دارایی های برنامه است. با این حال، برخی از برنامه ها به فضای بیشتری برای گرافیک های با کیفیت بالا، فایل های رسانه ای یا سایر دارایی های بزرگ نیاز دارند. قبلاً، اگر حجم دانلود فشرده برنامه شما از 100 مگابایت فراتر می رفت، باید منابع اضافی را خودتان میزبانی کرده و زمانی که کاربر برنامه را باز می کرد، دانلود کنید. میزبانی و ارائه فایل های اضافی می تواند پرهزینه باشد و تجربه کاربر اغلب کمتر از حد ایده آل است. برای اینکه این فرآیند برای شما آسان‌تر و برای کاربران دلپذیرتر شود، Google Play به شما امکان می‌دهد دو فایل بسط بزرگ را که مکمل APK شما هستند، پیوست کنید.

Google Play فایل های توسعه را برای برنامه شما میزبانی می کند و آنها را بدون هزینه برای شما به دستگاه ارائه می دهد. فایل‌های توسعه‌دهنده در محل ذخیره‌سازی مشترک دستگاه (کارت SD یا پارتیشن قابل نصب بر روی USB؛ همچنین به عنوان حافظه خارجی شناخته می‌شود) ذخیره می‌شوند، جایی که برنامه شما می‌تواند به آنها دسترسی داشته باشد. در اکثر دستگاه‌ها، Google Play همزمان با دانلود APK، فایل(های) توسعه را دانلود می‌کند، بنابراین برنامه شما هر آنچه را که نیاز دارد، زمانی که کاربر برای اولین بار باز می‌کند، دارد. با این حال، در برخی موارد، برنامه شما باید هنگام شروع برنامه، فایل‌ها را از Google Play دانلود کند.

اگر می خواهید از استفاده از فایل های توسعه خودداری کنید و حجم دانلود فشرده برنامه شما بزرگتر از 100 مگابایت است، در عوض باید برنامه خود را با استفاده از Android App Bundles آپلود کنید که امکان دانلود فشرده تا 200 مگابایت را فراهم می کند. علاوه بر این، از آنجایی که استفاده از بسته‌های برنامه تولید APK و امضای آن به Google Play را به تعویق می‌اندازد، کاربران APK‌های بهینه‌سازی شده را فقط با کد و منابعی که برای اجرای برنامه شما نیاز دارند دانلود می‌کنند. شما نیازی به ساخت، امضا و مدیریت چندین فایل APK یا فایل های توسعه ندارید و کاربران بارگیری های کوچکتر و بهینه تری دریافت می کنند.

نمای کلی

هر بار که یک APK را با استفاده از کنسول Google Play آپلود می کنید، این گزینه را دارید که یک یا دو فایل توسعه را به APK اضافه کنید. هر فایل می تواند تا 2 گیگابایت باشد و می تواند هر فرمتی که شما انتخاب می کنید باشد، اما توصیه می کنیم از یک فایل فشرده برای حفظ پهنای باند در طول دانلود استفاده کنید. از نظر مفهومی، هر فایل توسعه نقش متفاوتی دارد:

  • فایل بسط اصلی فایل بسط اولیه برای منابع اضافی مورد نیاز برنامه شما است.
  • فایل بسط پچ اختیاری است و برای به روز رسانی های کوچک فایل توسعه اصلی در نظر گرفته شده است.

در حالی که می‌توانید از دو فایل توسعه به هر نحوی که می‌خواهید استفاده کنید، توصیه می‌کنیم که فایل توسعه اصلی دارایی‌های اولیه را ارائه دهد و اگر هرگز به‌روزرسانی شود، به ندرت انجام شود. فایل بسط وصله باید کوچکتر باشد و به عنوان "حامل پچ" عمل کند و با هر نسخه اصلی یا در صورت لزوم به روز شود.

با این حال، حتی اگر به‌روزرسانی برنامه شما فقط به یک فایل بسط وصله جدید نیاز داشته باشد، همچنان باید یک APK جدید با versionCode به‌روزرسانی‌شده در مانیفست آپلود کنید. (کنسول Play به شما اجازه آپلود فایل بسط را در یک APK موجود نمی دهد.)

توجه: فایل بسط پچ از نظر معنایی با فایل اصلی توسعه یکسان است—شما می توانید از هر فایل به هر نحوی که می خواهید استفاده کنید.

فرمت نام فایل

هر فایل توسعه ای که آپلود می کنید می تواند هر فرمتی باشد که شما انتخاب می کنید (ZIP، PDF، MP4، و غیره). همچنین می‌توانید از ابزار JOBB برای کپسوله‌سازی و رمزگذاری مجموعه‌ای از فایل‌های منبع و وصله‌های بعدی برای آن مجموعه استفاده کنید. صرف نظر از نوع فایل، Google Play آنها را حباب های باینری مات در نظر می گیرد و با استفاده از طرح زیر نام فایل ها را تغییر می دهد:

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

سه جزء در این طرح وجود دارد:

main یا patch
مشخص می‌کند که آیا فایل، فایل بسط دهنده اصلی یا پچ باشد. برای هر APK فقط یک فایل اصلی و یک فایل پچ می تواند وجود داشته باشد.
<expansion-version>
این یک عدد صحیح است که با کد نسخه APK که توسعه ابتدا با آن مرتبط است مطابقت دارد (با مقدار android:versionCode برنامه مطابقت دارد).

روی "اول" تاکید شده است زیرا اگرچه کنسول Play به شما امکان استفاده مجدد از یک فایل بسط آپلود شده با یک APK جدید را می دهد، اما نام فایل بسط دهنده تغییر نمی کند—نسخه ای که در اولین بارگذاری فایل روی آن اعمال شده را حفظ می کند.

<package-name>
نام بسته به سبک جاوا برنامه شما.

برای مثال، فرض کنید نسخه APK شما 314159 و نام بسته شما com.example.app است. اگر یک فایل بسط اصلی آپلود کنید، نام فایل به:

main.314159.com.example.app.obb

محل ذخیره سازی

وقتی Google Play فایل‌های توسعه را در دستگاهی دانلود می‌کند، آن‌ها را در مکان ذخیره‌سازی مشترک سیستم ذخیره می‌کند. برای اطمینان از رفتار مناسب، نباید فایل های توسعه را حذف، جابجا یا تغییر نام دهید. در صورتی که برنامه شما باید دانلود را از گوگل پلی انجام دهد، باید فایل ها را دقیقا در همان مکان ذخیره کنید.

متد getObbDir() مکان خاصی را برای فایل های توسعه شما به شکل زیر برمی گرداند:

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage> مسیر فضای ذخیره سازی مشترک است که از getExternalStorageDirectory() در دسترس است.
  • <package-name> نام بسته به سبک جاوا برنامه شما است که از getPackageName() در دسترس است.

برای هر برنامه، هرگز بیش از دو فایل توسعه در این فهرست وجود ندارد. یکی فایل توسعه اصلی و دیگری فایل توسعه پچ (در صورت لزوم). هنگامی که برنامه خود را با فایل های توسعه جدید به روز می کنید، نسخه های قبلی بازنویسی می شوند. از Android 4.4 (سطح API 19)، برنامه‌ها می‌توانند فایل‌های توسعه OBB را بدون اجازه ذخیره‌سازی خارجی بخوانند. با این حال، برخی از پیاده‌سازی‌های Android 6.0 (سطح API 23) و نسخه‌های بعدی هنوز به مجوز نیاز دارند، بنابراین باید مجوز READ_EXTERNAL_STORAGE را در مانیفست برنامه اعلام کنید و در زمان اجرا به شرح زیر درخواست مجوز کنید:

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

برای اندروید نسخه 6 و جدیدتر، باید در زمان اجرا مجوز حافظه خارجی درخواست شود. با این حال، برخی از پیاده سازی های اندروید برای خواندن فایل های 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 ارائه کرده ایم که داده های شما را مستقیماً از فایل ZIP می خواند.

احتیاط: برخلاف فایل‌های APK، هر فایلی که در فضای ذخیره‌سازی مشترک ذخیره شده است، توسط کاربر و سایر برنامه‌ها قابل خواندن است.

نکته: اگر فایل‌های رسانه‌ای را در یک ZIP بسته‌بندی می‌کنید، می‌توانید از تماس‌های پخش رسانه روی فایل‌ها با کنترل‌های افست و طول (مانند MediaPlayer.setDataSource() و SoundPool.load() ) بدون نیاز به باز کردن ZIP خود استفاده کنید. برای اینکه این کار انجام شود، نباید در هنگام ایجاد بسته های ZIP روی فایل های رسانه فشرده سازی اضافی انجام دهید. به عنوان مثال، هنگام استفاده از ابزار zip ، باید از گزینه -n برای تعیین پسوندهای فایلی که نباید فشرده شوند استفاده کنید:
zip -n .mp4;.ogg main_expansion media_files

فرآیند دانلود

اغلب اوقات، Google Play همزمان با دانلود APK در دستگاه، فایل های توسعه دهنده شما را دانلود و ذخیره می کند. با این حال، در برخی موارد Google Play نمی‌تواند فایل‌های توسعه را دانلود کند یا ممکن است کاربر فایل‌های توسعه‌دهنده دانلود شده قبلی را حذف کرده باشد. برای مدیریت این موقعیت‌ها، برنامه شما باید بتواند فایل‌ها را هنگام شروع فعالیت اصلی با استفاده از URL ارائه شده توسط Google Play بارگیری کند.

روند دانلود از سطح بالا به این صورت است:

  1. کاربر انتخاب می کند که برنامه شما را از Google Play نصب کند.
  2. اگر گوگل پلی بتواند فایل های توسعه دهنده را دانلود کند (که در اکثر دستگاه ها صدق می کند)، آنها را همراه با APK دانلود می کند.

    اگر Google Play نتواند فایل های توسعه را دانلود کند، فقط APK را دانلود می کند.

  3. وقتی کاربر برنامه شما را راه اندازی می کند، برنامه شما باید بررسی کند که آیا فایل های توسعه قبلاً در دستگاه ذخیره شده اند یا خیر.
    1. اگر بله، برنامه شما آماده کار است.
    2. اگر نه، برنامه شما باید فایل های توسعه را از طریق HTTP از Google Play دانلود کند. برنامه شما باید با استفاده از سرویس مجوز برنامه Google Play، درخواستی را به مشتری Google Play ارسال کند، که با نام، اندازه فایل، و URL برای هر فایل توسعه پاسخ می‌دهد. با استفاده از این اطلاعات، سپس فایل ها را دانلود کرده و در محل ذخیره سازی مناسب ذخیره می کنید.

احتیاط: در صورتی که هنگام شروع برنامه شما، فایل‌ها از قبل روی دستگاه نباشند، ضروری است که کد لازم را برای دانلود فایل‌های توسعه از Google Play وارد کنید. همانطور که در بخش زیر در مورد دانلود فایل های توسعه بحث شد، ما کتابخانه ای را در دسترس شما قرار داده ایم که این فرآیند را تا حد زیادی ساده می کند و دانلود را از یک سرویس با حداقل مقدار کد از شما انجام می دهد.

چک لیست توسعه

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

  1. ابتدا تعیین کنید که آیا حجم دانلود فشرده برنامه شما باید بیش از 100 مگابایت باشد یا خیر. فضا بسیار ارزشمند است و باید حجم کل دانلود خود را تا حد امکان کوچک نگه دارید. اگر برنامه شما از بیش از 100 مگابایت استفاده می‌کند تا چندین نسخه از دارایی‌های گرافیکی شما را برای تراکم صفحه‌های متعدد ارائه کند، به جای آن چندین APK را منتشر کنید که در آن هر APK فقط دارایی‌های مورد نیاز برای صفحه‌هایی را که هدف قرار می‌دهد، داشته باشد. برای بهترین نتایج هنگام انتشار در Google Play، یک Android App Bundle را آپلود کنید که شامل همه کدها و منابع کامپایل شده برنامه شما است، اما تولید APK و ثبت نام در Google Play را به تعویق می اندازد.
  2. تعیین کنید کدام منابع برنامه را از APK خود جدا کنید و آنها را در یک فایل بسته بندی کنید تا به عنوان فایل توسعه اصلی استفاده کنید.

    به طور معمول، شما باید فقط از فایل بسط پچ دوم در هنگام به روز رسانی فایل توسعه اصلی استفاده کنید. با این حال، اگر منابع شما از محدودیت 2 گیگابایت برای فایل توسعه اصلی فراتر رود، می توانید از فایل پچ برای بقیه دارایی های خود استفاده کنید.

  3. برنامه خود را به گونه ای توسعه دهید که از منابع فایل های بسط شما در مکان ذخیره سازی مشترک دستگاه استفاده کند.

    به یاد داشته باشید که نباید فایل های توسعه را حذف، جابجا یا تغییر نام دهید.

    اگر برنامه شما نیاز به قالب خاصی ندارد، پیشنهاد می‌کنیم فایل‌های ZIP را برای فایل‌های توسعه‌دهنده خود ایجاد کنید، سپس آنها را با استفاده از کتابخانه زیپ گسترش APK بخوانید.

  4. منطقی را به فعالیت اصلی برنامه خود اضافه کنید که بررسی می کند آیا فایل های توسعه در هنگام راه اندازی روی دستگاه هستند یا خیر. اگر فایل‌ها در دستگاه نیستند، از سرویس مجوز برنامه Google Play برای درخواست URL برای فایل‌های توسعه استفاده کنید، سپس آنها را دانلود و ذخیره کنید.

    برای کاهش تا حد زیادی مقدار کدی که باید بنویسید و اطمینان از تجربه کاربری خوب در حین دانلود، توصیه می کنیم برای پیاده سازی رفتار دانلود خود از Downloader Library استفاده کنید.

    اگر به جای استفاده از کتابخانه، سرویس دانلود خود را می سازید، توجه داشته باشید که نباید نام فایل های توسعه دهنده را تغییر دهید و باید آنها را در محل ذخیره سازی مناسب ذخیره کنید.

هنگامی که توسعه برنامه خود را به پایان رساندید، راهنمای آزمایش فایل های توسعه خود را دنبال کنید.

قوانین و محدودیت ها

افزودن فایل‌های بسط APK یک ویژگی است که هنگام آپلود برنامه خود با استفاده از کنسول Play در دسترس است. هنگام آپلود برنامه خود برای اولین بار یا به روز رسانی برنامه ای که از فایل های توسعه استفاده می کند، باید از قوانین و محدودیت های زیر آگاه باشید:

  1. هر فایل بسط نمی تواند بیش از 2 گیگابایت باشد.
  2. برای دانلود فایل های توسعه خود از Google Play، کاربر باید برنامه شما را از Google Play خریداری کرده باشد . اگر برنامه به روش دیگری نصب شده باشد، Google Play نشانی‌های اینترنتی فایل‌های بسط شما را ارائه نمی‌کند.
  3. هنگام انجام دانلود از داخل برنامه، نشانی اینترنتی که Google Play برای هر فایل ارائه می‌کند، برای هر بارگیری منحصربه‌فرد است و هر یک مدت کوتاهی پس از ارائه به برنامه شما منقضی می‌شود.
  4. اگر برنامه خود را با یک APK جدید به روز می کنید یا چندین APK را برای یک برنامه آپلود می کنید، می توانید فایل های بسطی را که برای یک APK قبلی آپلود کرده اید انتخاب کنید. نام فایل بسط تغییر نمی کند — نسخه دریافت شده توسط APK که فایل در ابتدا به آن مرتبط شده بود را حفظ می کند.
  5. اگر از فایل‌های توسعه در ترکیب با چندین APK برای ارائه فایل‌های بسط مختلف برای دستگاه‌های مختلف استفاده می‌کنید، همچنان باید برای هر دستگاه APK جداگانه آپلود کنید تا یک مقدار versionCode منحصربه‌فرد ارائه کنید و فیلترهای مختلفی را برای هر APK اعلام کنید.
  6. شما نمی‌توانید با تغییر فایل‌های توسعه به‌تنهایی به‌روزرسانی برای برنامه خود صادر کنید— باید یک APK جدید برای به‌روزرسانی برنامه خود آپلود کنید . اگر تغییرات شما فقط مربوط به دارایی‌های فایل‌های بسط شما می‌شود، می‌توانید APK خود را به سادگی با تغییر versionCode (و شاید versionName ) به‌روزرسانی کنید.

  7. داده های دیگر را در دایرکتوری obb/ ذخیره نکنید . اگر باید برخی از داده ها را باز کنید، آن ها را در مکانی که توسط getExternalFilesDir() مشخص شده ذخیره کنید.
  8. فایل گسترش .obb را حذف یا تغییر نام ندهید (مگر اینکه در حال انجام به روز رسانی هستید). انجام این کار باعث می شود Google Play (یا خود برنامه شما) بارها فایل توسعه را دانلود کند.
  9. هنگام به روز رسانی دستی یک فایل توسعه، باید فایل توسعه قبلی را حذف کنید.

دانلود فایل های توسعه

در بیشتر موارد، Google Play همزمان با نصب یا به‌روزرسانی APK، فایل‌های توسعه‌دهنده شما را دانلود و در دستگاه ذخیره می‌کند. به این ترتیب، هنگامی که برنامه شما برای اولین بار راه اندازی می شود، فایل های توسعه در دسترس هستند. با این حال، در برخی موارد، برنامه شما باید خود فایل‌های توسعه را با درخواست آن‌ها از URL ارائه شده در پاسخ از سرویس مجوز برنامه Google Play دانلود کند.

منطق اولیه ای که برای دانلود فایل های توسعه نیاز دارید به شرح زیر است:

  1. هنگامی که برنامه شما شروع به کار کرد، به دنبال فایل های توسعه در مکان ذخیره سازی مشترک (در فهرست راهنمای Android/obb/<package-name>/ ) بگردید.
    1. اگر فایل های توسعه وجود دارد، همه چیز آماده است و برنامه شما می تواند ادامه دهد.
    2. اگر فایل های توسعه وجود ندارد :
      1. درخواستی را با استفاده از مجوز برنامه Google Play برای دریافت نام، اندازه و نشانی‌های اینترنتی فایل توسعه برنامه خود انجام دهید.
      2. از URL های ارائه شده توسط Google Play برای دانلود فایل های توسعه و ذخیره فایل های توسعه استفاده کنید. باید فایل‌ها را در مکان ذخیره‌سازی مشترک ذخیره کنید ( Android/obb/<package-name>/ ) و از نام فایل دقیق ارائه‌شده توسط پاسخ Google Play استفاده کنید.

        توجه: نشانی اینترنتی که Google Play برای فایل های بسط شما ارائه می کند برای هر بارگیری منحصر به فرد است و هر یک مدت کوتاهی پس از ارائه به برنامه شما منقضی می شود.

اگر برنامه شما رایگان است (نه یک برنامه پولی)، احتمالاً از سرویس مجوز برنامه استفاده نکرده اید. اساساً برای شما طراحی شده است تا سیاست‌های صدور مجوز را برای برنامه خود اعمال کنید و اطمینان حاصل کنید که کاربر حق استفاده از برنامه شما را دارد (او حقاً هزینه آن را در Google Play پرداخت کرده است). به منظور تسهیل عملکرد فایل توسعه، سرویس صدور مجوز برای ارائه پاسخی به برنامه شما که شامل URL فایل های توسعه برنامه شما است که در Google Play میزبانی می شوند، بهبود یافته است. بنابراین، حتی اگر برنامه شما برای کاربران رایگان باشد، برای استفاده از فایل‌های توسعه APK، باید کتابخانه تأیید مجوز (LVL) را نیز اضافه کنید. البته، اگر برنامه شما رایگان است، نیازی به اجرای تأیید مجوز ندارید—شما فقط به کتابخانه نیاز دارید تا درخواستی را انجام دهد که URL فایل های توسعه شما را برمی گرداند.

توجه: چه برنامه شما رایگان باشد و چه نباشد، Google Play تنها در صورتی آدرس های فایل بسط را برمی گرداند که کاربر برنامه شما را از Google Play خریداری کرده باشد.

علاوه بر LVL، به مجموعه‌ای از کد نیاز دارید که فایل‌های توسعه‌دهنده را از طریق اتصال HTTP دانلود کرده و در مکان مناسب در حافظه مشترک دستگاه ذخیره کند. همانطور که این روش را در برنامه خود ایجاد می کنید، چندین مسئله وجود دارد که باید در نظر بگیرید:

  • ممکن است دستگاه فضای کافی برای فایل‌های توسعه‌دهنده نداشته باشد، بنابراین باید قبل از شروع دانلود بررسی کنید و در صورت نبود فضای کافی به کاربر هشدار دهید.
  • دانلود فایل باید در یک سرویس پس‌زمینه انجام شود تا از مسدود شدن تعامل کاربر جلوگیری شود و به کاربر اجازه دهد تا برنامه شما را تا پایان دانلود ترک کند.
  • ممکن است خطاهای مختلفی در حین درخواست و دانلود رخ دهد که باید با ظرافت از آنها استفاده کنید.
  • اتصال شبکه می تواند در حین دانلود تغییر کند، بنابراین باید چنین تغییراتی را کنترل کنید و در صورت قطع شدن، دانلود را در صورت امکان از سر بگیرید.
  • در حالی که دانلود در پس‌زمینه انجام می‌شود، باید اعلانی ارائه دهید که پیشرفت دانلود را نشان می‌دهد، پس از اتمام آن به کاربر اطلاع می‌دهد و در صورت انتخاب کاربر را به برنامه شما بازمی‌گرداند.

برای ساده کردن این کار برای شما، کتابخانه Downloader را ساخته‌ایم که نشانی‌های اینترنتی فایل بسط را از طریق سرویس مجوز درخواست می‌کند، فایل‌های توسعه را دانلود می‌کند، همه کارهای فهرست‌شده در بالا را انجام می‌دهد و حتی اجازه می‌دهد فعالیت شما متوقف شود و بارگیری از سر گرفته شود. . با افزودن کتابخانه Downloader و چند قلاب کد به برنامه خود، تقریباً تمام کارهای دانلود فایل های توسعه برای شما کدگذاری شده است. به این ترتیب، به منظور ارائه بهترین تجربه کاربری با کمترین تلاش از جانب شما، توصیه می کنیم از کتابخانه Downloader برای دانلود فایل های بسط خود استفاده کنید. اطلاعات بخش‌های زیر نحوه ادغام کتابخانه در برنامه را توضیح می‌دهد.

اگر ترجیح می‌دهید راه‌حل خود را برای دانلود فایل‌های توسعه با استفاده از نشانی‌های اینترنتی Google Play ایجاد کنید، باید اسناد مجوز برنامه را برای انجام درخواست مجوز دنبال کنید، سپس نام، اندازه‌ها و نشانی‌های اینترنتی فایل بسط را از قسمت‌های اضافی پاسخ بازیابی کنید. شما باید از کلاس APKExpansionPolicy (که در کتابخانه تأیید مجوز موجود است) به عنوان خط مشی مجوز خود استفاده کنید، که نام فایل های توسعه، اندازه ها و آدرس های اینترنتی را از سرویس صدور مجوز می گیرد.

درباره کتابخانه دانلودر

برای استفاده از فایل‌های بسط APK با برنامه خود و ارائه بهترین تجربه کاربری با کمترین تلاش از جانب شما، توصیه می‌کنیم از کتابخانه بارگیری که در بسته کتابخانه گسترش APK Google Play موجود است استفاده کنید. این کتابخانه فایل‌های توسعه‌دهنده شما را در یک سرویس پس‌زمینه دانلود می‌کند، یک اعلان کاربر را با وضعیت دانلود نشان می‌دهد، از دست رفتن اتصال شبکه را مدیریت می‌کند، در صورت امکان بارگیری را از سر می‌گیرد و موارد دیگر.

برای پیاده سازی دانلود فایل های توسعه با استفاده از Downloader Library، تنها کاری که باید انجام دهید این است:

  • یک زیر کلاس Service و زیر کلاس BroadcastReceiver را گسترش دهید که هر کدام فقط به چند خط کد از شما نیاز دارند.
  • مقداری منطق به فعالیت اصلی خود اضافه کنید که بررسی می کند آیا فایل های توسعه قبلا دانلود شده اند یا خیر و در غیر این صورت، فرآیند دانلود را فراخوانی می کند و یک رابط کاربری پیشرفت را نشان می دهد.
  • یک رابط پاسخ به تماس را با چند روش در فعالیت اصلی خود اجرا کنید که به‌روزرسانی‌هایی را درباره پیشرفت دانلود دریافت می‌کند.

بخش‌های زیر نحوه راه‌اندازی برنامه خود را با استفاده از Downloader Library توضیح می‌دهند.

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

برای استفاده از Downloader Library، باید دو بسته را از SDK Manager دانلود کنید و کتابخانه های مناسب را به برنامه خود اضافه کنید.

ابتدا، Android SDK Manager ( ابزارها > مدیر SDK ) را باز کنید و در قسمت Appearance & Behavior > System Settings > Android SDK ، برگه SDK Tools را برای انتخاب و دانلود انتخاب کنید:

  • بسته کتابخانه مجوز Google Play
  • بسته کتابخانه گسترش Google Play APK

یک ماژول کتابخانه جدید برای کتابخانه تأیید مجوز و کتابخانه بارگیری ایجاد کنید. برای هر کتابخانه:

  1. File > New > New Module را انتخاب کنید.
  2. در پنجره Create New Module ، Android Library را انتخاب کنید و سپس Next را انتخاب کنید.
  3. نام برنامه/کتابخانه مانند «کتابخانه مجوز Google Play» و «Google Play Downloader Library» را مشخص کنید، حداقل سطح SDK را انتخاب کنید، سپس Finish را انتخاب کنید.
  4. فایل > ساختار پروژه را انتخاب کنید.
  5. برگه Properties را انتخاب کنید و در Library Repository ، از فهرست <sdk>/extras/google/ وارد کتابخانه شوید ( play_licensing/ برای کتابخانه تأیید مجوز یا play_apk_expansion/downloader_library/ برای کتابخانه دانلودر).
  6. برای ایجاد ماژول جدید ، OK را انتخاب کنید.

توجه: کتابخانه دانلودر به کتابخانه تأیید مجوز بستگی دارد. حتماً کتابخانه تأیید مجوز را به ویژگی های پروژه کتابخانه دانلودر اضافه کنید.

یا از طریق خط فرمان، پروژه خود را به‌روزرسانی کنید تا کتابخانه‌های زیر را در بر بگیرد:

  1. دایرکتوری ها را به دایرکتوری <sdk>/tools/ تغییر دهید.
  2. android update project با گزینه --library اجرا کنید تا هم LVL و هم Downloader Library را به پروژه خود اضافه کنید. به عنوان مثال:
    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 استفاده می کند. اگر قصد دارید از فایل های ZIP برای فایل های توسعه خود استفاده کنید، پیشنهاد می کنیم کتابخانه زیپ گسترش APK را نیز به برنامه خود اضافه کنید. برای اطلاعات بیشتر، به بخش زیر درباره استفاده از کتابخانه زیپ گسترش APK مراجعه کنید.

اعلام مجوزهای کاربر

برای دانلود فایل‌های توسعه، کتابخانه Downloader به چندین مجوز نیاز دارد که باید آنها را در فایل مانیفست برنامه خود اعلام کنید. آنها عبارتند از:

<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>

توجه: به‌طور پیش‌فرض، کتابخانه Downloader به API سطح 4 نیاز دارد، اما APK Expansion Zip Library به API سطح 5 نیاز دارد.

پیاده سازی سرویس دانلودر

برای انجام دانلودها در پس‌زمینه، کتابخانه Downloader زیرکلاس Service خود را به نام DownloaderService ارائه می‌کند که باید آن را گسترش دهید. DownloaderService علاوه بر دانلود فایل های توسعه برای شما:

  • یک BroadcastReceiver را ثبت می کند که به تغییرات اتصال شبکه دستگاه گوش می دهد (پخش CONNECTIVITY_ACTION ) تا در صورت لزوم بارگیری را متوقف کند (مانند از دست دادن اتصال) و در صورت امکان بارگیری را از سر بگیرد (اتصال به دست آمده است).
  • زنگ RTC_WAKEUP را برای بارگیری مجدد برای مواردی که سرویس کشته می شود، برنامه ریزی می کند.
  • یک Notification سفارشی ایجاد می کند که پیشرفت دانلود و هرگونه خطا یا تغییر وضعیت را نشان می دهد.
  • به برنامه شما اجازه می دهد تا به صورت دستی بارگیری را متوقف کرده و از سر بگیرد.
  • قبل از دانلود فایل‌های توسعه، تأیید می‌کند که فضای ذخیره‌سازی مشترک نصب شده و در دسترس است، فایل‌ها از قبل وجود ندارند، و فضای کافی وجود دارد. سپس در صورت عدم صحت به کاربر اطلاع می دهد.

تنها کاری که باید انجام دهید این است که یک کلاس در برنامه خود ایجاد کنید که کلاس DownloaderService را گسترش دهد و سه روش را برای ارائه جزئیات خاص برنامه لغو کند:

getPublicKey()
این باید رشته‌ای را برگرداند که کلید عمومی RSA با کد Base64 برای حساب ناشر شما است که از صفحه نمایه در کنسول Play در دسترس است ( به تنظیم برای صدور مجوز مراجعه کنید).
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 را به‌روزرسانی کنید تا کلید عمومی متعلق به حساب ناشر شما باشد. می توانید کلید را در Developer Console زیر اطلاعات نمایه خود پیدا کنید. این حتی هنگام آزمایش دانلودهای شما ضروری است.

به یاد داشته باشید که سرویس را در فایل مانیفست خود اعلام کنید:

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

پیاده سازی گیرنده آلارم

به منظور نظارت بر پیشرفت بارگیری فایل و در صورت لزوم دانلود را مجدداً راه اندازی می کند، DownloaderService زنگ RTC_WAKEUP را برنامه ریزی می کند که یک Intent به یک BroadcastReceiver در برنامه شما ارائه می دهد. شما باید BroadcastReceiver برای فراخوانی یک API از کتابخانه Downloader تعریف کنید که وضعیت دانلود را بررسی می کند و در صورت لزوم آن را مجدداً راه اندازی می کند.

برای فراخوانی DownloaderClientMarshaller.startDownloadServiceIfRequired() به سادگی باید روش onReceive() را لغو کنید.

به عنوان مثال:

کاتلین

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>

شروع دانلود

فعالیت اصلی در برنامه شما (فعالیتی که توسط نماد راه‌انداز شما شروع شده است) مسئول تأیید اینکه آیا فایل‌های توسعه از قبل روی دستگاه هستند و اگر نیستند، دانلود را آغاز می‌کند.

شروع دانلود با استفاده از Downloader Library به مراحل زیر نیاز دارد:

  1. بررسی کنید که آیا فایل ها دانلود شده اند یا خیر.

    کتابخانه Downloader شامل چند API در کلاس Helper برای کمک به این فرآیند است:

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

    به عنوان مثال، برنامه نمونه ارائه شده در بسته گسترش Apk، متد زیر را در متد 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 را برگرداند، برنامه باید دانلود را شروع کند.

  2. دانلود را با فراخوانی روش ثابت DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass) شروع کنید.

    این روش پارامترهای زیر را می گیرد:

    • context : Context برنامه شما.
    • notificationClient : یک PendingIntent برای شروع فعالیت اصلی شما. این در Notification ای که DownloaderService ایجاد می کند برای نشان دادن پیشرفت دانلود استفاده می شود. هنگامی که کاربر اعلان را انتخاب می کند، سیستم PendingIntent که در اینجا ارائه می کنید فراخوانی می کند و باید فعالیتی را که پیشرفت دانلود را نشان می دهد باز کند (معمولاً همان فعالیتی که بارگیری را شروع کرده است).
    • serviceClass : شی Class برای اجرای DownloaderService شما، برای شروع سرویس و شروع دانلود در صورت لزوم مورد نیاز است.

    این روش یک عدد صحیح را برمی‌گرداند که نشان می‌دهد آیا دانلود مورد نیاز است یا خیر. مقادیر ممکن عبارتند از:

    • NO_DOWNLOAD_REQUIRED : اگر فایل‌ها از قبل وجود داشته باشند یا دانلودی در حال انجام باشد، برگردانده می‌شود.
    • LVL_CHECK_REQUIRED : در صورت نیاز به تأیید مجوز برای به دست آوردن نشانی‌های اینترنتی فایل بسط داده می‌شود.
    • DOWNLOAD_REQUIRED : در صورتی که نشانی‌های اینترنتی فایل توسعه از قبل شناخته شده باشند، اما دانلود نشده باشند، برگردانده می‌شود.

    رفتار LVL_CHECK_REQUIRED و DOWNLOAD_REQUIRED اساساً یکسان است و شما معمولاً نیازی به نگرانی در مورد آنها ندارید. در فعالیت اصلی خود که startDownloadServiceIfRequired() را فراخوانی می کند، می توانید به سادگی بررسی کنید که آیا پاسخ NO_DOWNLOAD_REQUIRED است یا خیر. اگر پاسخ چیزی غیر از NO_DOWNLOAD_REQUIRED باشد، کتابخانه Downloader بارگیری را آغاز می کند و شما باید رابط کاربری فعالیت خود را برای نمایش پیشرفت دانلود به روز کنید (مرحله بعدی را ببینید). اگر پاسخ 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
    }
    
  3. هنگامی که متد startDownloadServiceIfRequired() چیزی غیر از NO_DOWNLOAD_REQUIRED برمی گرداند، با فراخوانی DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) یک نمونه از IStub ایجاد کنید. IStub یک اتصال بین فعالیت شما به سرویس بارگیری فراهم می کند، به طوری که فعالیت شما در مورد پیشرفت بارگیری تماس هایی دریافت می کند.

    برای اینکه IStub خود را با فراخوانی CreateStub() نمونه سازی کنید، باید پیاده سازی رابط IDownloaderClient و اجرای DownloaderService خود را به آن منتقل کنید. بخش بعدی در مورد دریافت پیشرفت دانلود، رابط IDownloaderClient را مورد بحث قرار می‌دهد، که معمولاً باید آن را در کلاس Activity خود پیاده‌سازی کنید تا بتوانید هنگام تغییر وضعیت دانلود، رابط کاربری Activity را به‌روزرسانی کنید.

    توصیه می کنیم پس از شروع دانلود startDownloadServiceIfRequired() برای نمونه سازی IStub خود در طول متد onCreate() فعالیت خود، 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() دریافت می‌کند، جایی که باید connect() در IStub فراخوانی کنید و آن را به Context برنامه خود منتقل کنید. برعکس، شما باید disconnect() را در اکتیویتی onStop() callback فراخوانی کنید.

    کاتلین

    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();
    }
    

    فراخوانی connect() در IStub فعالیت شما را به DownloaderService متصل می کند به طوری که فعالیت شما در رابطه با تغییرات در وضعیت دانلود از طریق رابط IDownloaderClient پاسخ تماس دریافت می کند.

دریافت پیشرفت دانلود

برای دریافت به‌روزرسانی‌های مربوط به پیشرفت دانلود و تعامل با DownloaderService ، باید رابط IDownloaderClient کتابخانه دانلود را پیاده‌سازی کنید. معمولاً فعالیتی که برای شروع دانلود استفاده می کنید باید این رابط را پیاده سازی کند تا پیشرفت دانلود را نمایش دهد و درخواست ها را به سرویس ارسال کند.

روش های واسط مورد نیاز برای IDownloaderClient عبارتند از:

onServiceConnected(Messenger m)
پس از اینکه IStub در فعالیت خود نمونه سازی کردید، تماسی با این روش دریافت خواهید کرد که یک شی Messenger را که به نمونه DownloaderService شما متصل است، ارسال می کند. برای ارسال درخواست‌ها به سرویس، مانند توقف موقت و از سرگیری دانلودها، باید با 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 یکی از چندین مقدار ممکن است که توسط یکی از ثابت های STATE_* کلاس IDownloaderClient مشخص شده است.

برای ارائه یک پیام مفید به کاربران خود، می توانید با فراخوانی Helpers.getDownloaderStringResourceIDFromState() یک رشته متناظر برای هر وضعیت درخواست کنید. این شناسه منبع یکی از رشته های همراه با کتابخانه دانلود را برمی گرداند. به عنوان مثال، رشته "دانلود متوقف شد زیرا در حال رومینگ هستید" با STATE_PAUSED_ROAMING مطابقت دارد.

onDownloadProgress(DownloadProgressInfo progress)
سرویس دانلود این را برای ارائه یک شی DownloadProgressInfo فراخوانی می‌کند، که اطلاعات مختلفی را در مورد پیشرفت دانلود، از جمله زمان تخمینی باقی‌مانده، سرعت فعلی، پیشرفت کلی و کل توضیح می‌دهد تا بتوانید رابط کاربری پیشرفت دانلود را به‌روزرسانی کنید.

نکته: برای نمونه‌هایی از این تماس‌های برگشتی که رابط کاربری پیشرفت بارگیری را به‌روزرسانی می‌کنند، SampleDownloaderActivity در برنامه نمونه ارائه شده با بسته توسعه Apk ببینید.

برخی از روش‌های عمومی برای رابط IDownloaderService که ممکن است مفید باشند عبارتند از:

requestPauseDownload()
دانلود را متوقف می کند.
requestContinueDownload()
دانلود متوقف شده را از سر می گیرد.
setDownloadFlags(int flags)
تنظیمات برگزیده کاربر را برای انواع شبکه ای که دانلود فایل ها در آن ها مجاز است، تنظیم می کند. پیاده سازی فعلی از یک پرچم پشتیبانی می کند، FLAGS_DOWNLOAD_OVER_CELLULAR ، اما شما می توانید دیگران را اضافه کنید. به‌طور پیش‌فرض، این پرچم فعال نیست ، بنابراین کاربر برای دانلود فایل‌های توسعه باید روی Wi-Fi باشد. ممکن است بخواهید یک اولویت کاربر برای فعال کردن بارگیری از طریق شبکه تلفن همراه ارائه دهید. در این صورت می توانید تماس بگیرید:

کاتلین

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

جاوا

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

با استفاده از APKExpansionPolicy

اگر تصمیم دارید به جای استفاده از Google Play Downloader Library، سرویس دانلودکننده خود را بسازید، همچنان باید از APKExpansionPolicy که در کتابخانه تأیید مجوز ارائه شده است استفاده کنید. کلاس APKExpansionPolicy تقریباً با ServerManagedPolicy یکسان است (در کتابخانه تأیید مجوز Google Play موجود است) اما شامل مدیریت اضافی برای پاسخ های اضافی فایل گسترش APK است.

توجه: اگر از کتابخانه بارگیری استفاده می کنید همانطور که در بخش قبلی بحث شده است ، کتابخانه تمام تعامل با APKExpansionPolicy را انجام می دهد ، بنابراین نیازی نیست که مستقیماً از این کلاس استفاده کنید.

این کلاس شامل روش هایی برای کمک به شما در دریافت اطلاعات لازم در مورد پرونده های گسترش موجود است:

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

برای کسب اطلاعات بیشتر در مورد نحوه استفاده از APKExpansionPolicy هنگام استفاده از کتابخانه Downloader ، به مستندات اضافه کردن مجوز به برنامه خود مراجعه کنید ، که توضیح می دهد نحوه اجرای یک خط مشی مجوز مانند این یکی را توضیح می دهد.

خواندن پرونده انبساط

پس از ذخیره پرونده های گسترش APK در دستگاه ، نحوه خواندن پرونده های خود به نوع پرونده ای که استفاده کرده اید بستگی دارد. همانطور که در مروری بحث شده است ، پرونده های گسترش شما می توانند هر نوع پرونده مورد نظر خود باشند ، اما با استفاده از یک فرمت نام پرونده خاص تغییر نام داده می شوند و در <shared-storage>/Android/obb/<package-name>/ ذخیره می شوند.

صرف نظر از نحوه خواندن پرونده های خود ، همیشه باید ابتدا بررسی کنید که ذخیره خارجی برای خواندن در دسترس است. این احتمال وجود دارد که کاربر از طریق USB ذخیره شده به رایانه نصب کند یا در واقع کارت SD را حذف کرده است.

توجه: وقتی برنامه شما شروع می شود ، همیشه باید با فراخوانی getExternalStorageState() فضای ذخیره سازی خارجی در دسترس باشد و قابل خواندن باشد. این یکی از چندین رشته ممکن است که نمایانگر وضعیت ذخیره خارجی است. برای اینکه توسط برنامه شما قابل خواندن باشد ، مقدار بازده باید MEDIA_MOUNTED باشد.

دریافت نام پرونده

همانطور که در نمای کلی توضیح داده شده است ، پرونده های انبساط APK شما با استفاده از یک قالب نام فایل خاص ذخیره می شوند:

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

برای به دست آوردن مکان و نام پرونده های گسترش خود ، باید از روشهای getExternalStorageDirectory() و getPackageName() استفاده کنید تا مسیر پرونده های خود را بسازید.

در اینجا روشی است که می توانید در برنامه خود استفاده کنید تا آرایه ای داشته باشید که حاوی مسیر کامل برای هر دو پرونده گسترش شما باشد:

کاتلین

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 هنگام شروع بارگیری ، با پرس و جو از نام پرونده گسترش با روش getExpansionFileName(int index) کلاس APKExpansionPolicy است. سپس می توانید با خواندن فایل SharedPreferences هنگام دسترسی به پرونده گسترش ، کد نسخه را دریافت کنید.

برای اطلاعات بیشتر در مورد خواندن از ذخیره سازی مشترک ، به اسناد ذخیره سازی داده ها مراجعه کنید.

با استفاده از کتابخانه زیپ انبساط APK

بسته گسترش APK Google Market شامل کتابخانه ای به نام کتابخانه ZIP گسترش 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
یک فایل زیپ را در ذخیره سازی مشترک نشان می دهد و تمام کارها را برای ارائه یک سیستم فایل مجازی بر اساس پرونده های ZIP شما انجام می دهد. می توانید با استفاده از APKExpansionSupport.getAPKExpansionZipFile() یا با ZipResourceFile با عبور از آن مسیر پرونده گسترش خود را دریافت کنید. این کلاس شامل انواع روشهای مفید است ، اما شما به طور کلی نیازی به دسترسی به اکثر آنها ندارید. چند روش مهم عبارتند از:
getInputStream(String assetPath)
برای خواندن یک پرونده در پرونده ZIP ، یک InputStream فراهم می کند. assetPath باید مسیر پرونده مورد نظر باشد ، نسبت به ریشه محتویات پرونده ZIP.
getAssetFileDescriptor(String assetPath)
یک AssetFileDescriptor را برای یک پرونده در پرونده ZIP فراهم می کند. assetPath باید مسیر پرونده مورد نظر باشد ، نسبت به ریشه محتویات پرونده ZIP. این برای برخی از API های Android که به یک AssetFileDescriptor مانند برخی از API های MediaPlayer نیاز دارند ، مفید است.
APEZProvider
بیشتر برنامه ها نیازی به استفاده از این کلاس ندارند. این کلاس یک ContentProvider را تعریف می کند که داده ها را از پرونده های ZIP از طریق یک ارائه دهنده محتوا Uri به منظور دسترسی به فایل برای برخی از API های Android که انتظار دسترسی Uri به پرونده های رسانه ای را دارند ، مارش می دهد. به عنوان مثال ، اگر می خواهید ویدئویی با VideoView.setVideoURI() پخش کنید ، این مفید است.

پرش از فشرده سازی زیپ پرونده های رسانه ای

اگر از پرونده های گسترش خود برای ذخیره پرونده های رسانه ای استفاده می کنید ، یک فایل ZIP هنوز به شما امکان می دهد از تماس های پخش رسانه ای Android استفاده کنید که کنترل های افست و طول را ارائه می دهد (مانند MediaPlayer.setDataSource() و SoundPool.load() ). برای این کار ، شما نباید هنگام ایجاد بسته های ZIP ، فشرده سازی اضافی را روی پرونده های رسانه ای انجام دهید. به عنوان مثال ، هنگام استفاده از ابزار zip ، باید از گزینه -n برای مشخص کردن پسوندهای فایل استفاده کنید که نباید فشرده شوند:

zip -n .mp4;.ogg main_expansion media_files

خواندن از یک فایل زیپ

هنگام استفاده از کتابخانه زیپ انبساط APK ، خواندن پرونده ای از زیپ شما معمولاً به موارد زیر نیاز دارد:

کاتلین

// 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 با مسیر پرونده گسترش مورد نظر استفاده کنید:

کاتلین

// 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 (یا خود برنامه شما) به طور مکرر پرونده انبساط را بارگیری کند.
  • داده های دیگر را در Directory obb/ Directory خود ذخیره نکنید . اگر باید برخی از داده ها را باز کنید ، آن را در مکان مشخص شده توسط getExternalFilesDir() ذخیره کنید.

بارگیری فایل های تست

از آنجا که برنامه شما باید گاهی اوقات پرونده های انبساط را به صورت دستی بارگیری کند ، مهم است که این فرآیند را آزمایش کنید تا مطمئن شوید که برنامه شما می تواند با موفقیت برای URL ها پرس و جو کند ، پرونده ها را بارگیری کند و آنها را در دستگاه ذخیره کند.

برای آزمایش اجرای برنامه خود از روش بارگیری دستی ، می توانید آن را در مسیر آزمایش داخلی منتشر کنید ، بنابراین فقط در دسترس آزمایش کنندگان مجاز است. اگر همه چیز همانطور که انتظار می رود کار کند ، برنامه شما باید به محض شروع فعالیت اصلی ، فایلهای انبساط را بارگیری کند.

توجه: قبلاً می توانید با بارگذاری نسخه "پیش نویس" منتشر نشده ، یک برنامه را آزمایش کنید. این قابلیت دیگر پشتیبانی نمی شود. در عوض ، شما باید آن را به یک مسیر آزمایش داخلی ، بسته یا باز منتشر کنید. برای اطلاعات بیشتر ، به پیش نویس برنامه ها دیگر پشتیبانی نمی شود .

برنامه خود را به روز کنید

یکی از مزایای عالی برای استفاده از پرونده های گسترش در Google Play امکان به روزرسانی برنامه شما بدون بارگیری مجدد تمام دارایی های اصلی است. از آنجا که Google Play به شما امکان می دهد دو فایل انبساط را با هر APK ارائه دهید ، می توانید از پرونده دوم به عنوان "پچ" استفاده کنید که به روزرسانی ها و دارایی های جدید را ارائه می دهد. انجام این کار از نیاز به بارگیری مجدد پرونده اصلی توسعه که می تواند برای کاربران بزرگ و گران باشد ، جلوگیری می کند.

پرونده گسترش پچ از نظر فنی همان پرونده اصلی توسعه است و نه سیستم Android و نه Google Play وصله واقعی بین پرونده های اصلی و پچ شما را انجام نمی دهند. کد برنامه شما باید هرگونه تکه های لازم را انجام دهد.

اگر از پرونده های ZIP به عنوان پرونده های انبساط خود استفاده می کنید ، کتابخانه ZIP گسترش APK که با بسته گسترش APK گنجانده شده است ، شامل امکان ادغام فایل پچ شما با پرونده اصلی گسترش است.

توجه: حتی اگر فقط نیاز به ایجاد تغییراتی در پرونده گسترش پچ دارید ، هنوز هم باید APK را به روز کنید تا Google Play بتواند به روزرسانی را انجام دهد. اگر نیازی به تغییر کد در برنامه ندارید ، باید به سادگی versionCode در مانیفست به روز کنید.

تا زمانی که پرونده اصلی گسترش را که با APK در کنسول Play همراه است تغییر ندهید ، کاربرانی که قبلاً برنامه شما را نصب کرده بودند ، پرونده اصلی گسترش را بارگیری نمی کنند. کاربران موجود فقط APK به روز شده و پرونده جدید گسترش پچ را دریافت می کنند (حفظ پرونده اصلی توسعه قبلی).

در اینجا چند مورد وجود دارد که باید در مورد به روزرسانی پرونده های انبساط در نظر داشته باشید:

  • فقط دو پرونده گسترش برای برنامه شما وجود دارد. یک پرونده اصلی توسعه و یک پرونده انبساط پچ. در حین بروزرسانی در یک پرونده ، Google Play نسخه قبلی را حذف می کند (و همچنین باید هنگام انجام به روزرسانی های دستی برنامه شما را انجام دهید).
  • هنگام افزودن یک فایل انبساط پچ ، سیستم Android در واقع برنامه شما یا پرونده اصلی گسترش را وصله نمی کند. برای پشتیبانی از داده های پچ باید برنامه خود را طراحی کنید. با این حال ، بسته انبساط APK شامل یک کتابخانه برای استفاده از پرونده های ZIP به عنوان پرونده های انبساط است ، که داده ها را از پرونده پچ به پرونده اصلی توسعه ادغام می کند ، بنابراین می توانید به راحتی تمام داده های پرونده گسترش را بخوانید.