Hãy làm theo các bước trong hướng dẫn này để truy cập vào gói tài sản của ứng dụng qua mã Java.
Bản dựng cho Kotlin và Java
Hãy làm theo các bước sau đây để tích hợp Play Asset Delivery vào tệp Android App Bundle của dự án. Bạn không cần sử dụng Android Studio để thực hiện các bước này.
Cập nhật phiên bản trình bổ trợ Android cho Gradle trong tệp
build.gradle
của dự án lên phiên bản4.0.0
trở lên.Trong thư mục cấp cao nhất của dự án, tạo thư mục cho gói tài sản. Tên thư mục này được dùng làm tên gói tài sản. Tên gói tài sản phải bắt đầu bằng một chữ cái và chỉ được chứa chữ cái, số và dấu gạch dưới.
Trong thư mục gói tài sản, hãy tạo tệp
build.gradle
rồi thêm mã sau đây. Hãy nhớ chỉ định tên của gói tài sản và chỉ một loại hình phân phối:Groovy
// In the asset pack's build.gradle file: plugins { id 'com.android.asset-pack' } assetPack { packName = "asset-pack-name" // Directory name for the asset pack dynamicDelivery { deliveryType = "[ install-time | fast-follow | on-demand ]" } }
Kotlin
// In the asset pack's build.gradle.kts file: plugins { id("com.android.asset-pack") } assetPack { packName.set("asset-pack-name") // Directory name for the asset pack dynamicDelivery { deliveryType.set("[ install-time | fast-follow | on-demand ]") } }
Trong tệp
build.gradle
của ứng dụng thuộc dự án, hãy thêm tên của từng gói tài sản vào dự án như trình bày dưới đây:Groovy
// In the app build.gradle file: android { ... assetPacks = [":asset-pack-name", ":asset-pack2-name"] }
Kotlin
// In the app build.gradle.kts file: android { ... assetPacks += listOf(":asset-pack-name", ":asset-pack2-name") }
Trong tệp
settings.gradle
của dự án, hãy đưa tất cả gói tài sản vào dự án như trình bày dưới đây:Groovy
// In the settings.gradle file: include ':app' include ':asset-pack-name' include ':asset-pack2-name'
Kotlin
// In the settings.gradle.kts file: include(":app") include(":asset-pack-name") include(":asset-pack2-name")
Trong thư mục gói tài sản, hãy tạo thư mục con sau đây:
src/main/assets
.Đặt tài sản vào thư mục
src/main/assets
. Ở đây, bạn cũng có thể tạo thư mục con. Lúc này, cấu trúc thư mục cho ứng dụng sẽ có dạng như sau:build.gradle
settings.gradle
app/
asset-pack-name/build.gradle
asset-pack-name/src/main/assets/your-asset-directories
Xây dựng Android App Bundle bằng Gradle. Trong gói ứng dụng đã tạo, thư mục cấp cơ sở hiện bao gồm những phần tử sau:
asset-pack-name/manifest/AndroidManifest.xml
: Định cấu hình chế độ phân phối và giá trị nhận dạng của gói tài sảnasset-pack-name/assets/your-asset-directories
: Thư mục chứa tất cả các tài sản được phân phối trong gói tài sản
Gradle sẽ tạo tệp kê khai cho mỗi gói tài sản và xuất ra thư mục
assets/
cho bạn.(Không bắt buộc) Bao gồm Thư viện Play Asset Delivery nếu bạn định sử dụng tính năng phân phối tiếp nối nhanh và theo yêu cầu
Groovy
implementation "com.google.android.play:asset-delivery:2.2.2" // For Kotlin use asset-delivery-ktx implementation "com.google.android.play:asset-delivery-ktx:2.2.2"
Kotlin
implementation("com.google.android.play:asset-delivery:2.2.2") // For Kotlin use core-ktx implementation("com.google.android.play:asset-delivery-ktx:2.2.2")
(Không bắt buộc) Định cấu hình gói ứng dụng để hỗ trợ nhiều định dạng nén kết cấu.
Tích hợp với API Play Asset Delivery
API Java của Play Asset Delivery cung cấp lớp AssetPackManager
để yêu cầu các gói tài sản, quản lý tệp tải xuống và truy cập vào các tài sản. Hãy nhớ Thêm Thư viện Play Asset Delivery vào dự án của bạn trước.
Bạn triển khai API này theo loại hình phân phối của gói tài sản mà bạn muốn truy cập. Các bước này được thể hiện trong sơ đồ quy trình sau đây.
Hình 1. Sơ đồ quy trình truy cập gói tài sản
Phân phối khi cài đặt
Các gói tài sản được định cấu hình là install-time
sẽ có sẵn ngay khi khởi chạy ứng dụng. Hãy sử dụng AssetManager API trong Java để truy cập tài sản được phân phát ở chế độ này:
Kotlin
import android.content.res.AssetManager ... val context: Context = createPackageContext("com.example.app", 0) val assetManager: AssetManager = context.assets val stream: InputStream = assetManager.open("asset-name")
Java
import android.content.res.AssetManager; ... Context context = createPackageContext("com.example.app", 0); AssetManager assetManager = context.getAssets(); InputStream is = assetManager.open("asset-name");
Phân phối tiếp nối nhanh và theo yêu cầu
Các phần sau đây cho biết cách lấy thông tin về gói tài sản trước khi tải xuống, cách gọi API để bắt đầu tải xuống và cách truy cập gói đã tải xuống. Các phần này áp dụng cho các gói tài sản fast-follow
và on-demand
.
Kiểm tra trạng thái
Mỗi gói tài sản được lưu trữ tại một thư mục riêng ở bộ nhớ trong của ứng dụng.
Sử dụng phương thức getPackLocation()
để xác định thư mục gốc của gói tài sản. Phương thức này trả về các giá trị sau:
Giá trị trả về | Trạng thái |
---|---|
Một đối tượng AssetPackLocation hợp lệ |
Thư mục gốc của gói tài sản đã sẵn sàng để truy cập ngay tại assetsPath() |
null |
Không có gói tài sản hoặc các tài sản không xác định |
Xem thông tin tải xuống về gói tài sản
Ứng dụng phải cho biết kích thước của tệp tải xuống trước khi tìm nạp gói tài sản. Hãy dùng phương thức requestPackStates()
hoặc getPackStates()
để xác định kích thước của tệp tải xuống và liệu gói đã được tải xuống hay chưa.
Kotlin
suspend fun requestPackStates(packNames: List<String>): AssetPackStates
Java
Task<AssetPackStates> getPackStates(List<String> packNames)
requestPackStates()
là một hàm tạm ngưng (suspend function) trả về đối tượng AssetPackStates
trong khi getPackStates()
là một phương thức không đồng bộ trả về Task<AssetPackStates>
. Phương thức packStates()
của đối tượng AssetPackStates
trả về Map<String,
AssetPackState>
. Bản đồ này chứa trạng thái của mỗi gói tài sản được yêu cầu và được xác định theo tên:
Kotlin
AssetPackStates#packStates(): Map<String, AssetPackState>
Java
Map<String, AssetPackState> AssetPackStates#packStates()
Yêu cầu cuối cùng sẽ có dạng như sau:
Kotlin
const val assetPackName = "assetPackName" coroutineScope.launch { try { val assetPackStates: AssetPackStates = manager.requestPackStates(listOf(assetPackName)) val assetPackState: AssetPackState = assetPackStates.packStates()[assetPackName] } catch (e: RuntimeExecutionException) { Log.d("MainActivity", e.message) } }
Java
final String assetPackName = "myasset"; assetPackManager .getPackStates(Collections.singletonList(assetPackName)) .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() { @Override public void onComplete(Task<AssetPackStates> task) { AssetPackStates assetPackStates; try { assetPackStates = task.getResult(); AssetPackState assetPackState = assetPackStates.packStates().get(assetPackName); } catch (RuntimeExecutionException e) { Log.d("MainActivity", e.getMessage()); return; })
Các phương thức AssetPackState
sau đây cho biết kích thước của gói tài sản, số gói đã tải xuống từ trước đến nay (nếu được yêu cầu) và số gói đã chuyển vào ứng dụng:
Để biết trạng thái của gói tài sản, hãy sử dụng status()
. Phương thức này sẽ trả về trạng thái dưới dạng số nguyên tương ứng với một trường không đổi trong lớp AssetPackStatus
. Gói tài sản chưa được cài đặt sẽ có trạng thái AssetPackStatus.NOT_INSTALLED
.
Nếu một yêu cầu không thành công, hãy sử dụng phương thức errorCode()
. Phương thức này trả về giá trị tương ứng với một trường không đổi trong lớp AssetPackErrorCode
.
Cài đặt
Hãy sử dụng phương thức requestFetch()
hoặc fetch()
để tải gói tài sản xuống trong lần đầu tiên hoặc gọi để hoàn tất quá trình cập nhật một gói tài sản:
Kotlin
suspend fun AssetPackManager.requestFetch(packs: List<String>): AssetPackStates
Java
Task<AssetPackStates> fetch(List<String> packNames)
Phương thức này trả về một đối tượng AssetPackStates
chứa danh sách gói, kích thước và trạng thái tải xuống ban đầu của các gói đó.
Nếu một gói tài sản được yêu cầu qua requestFetch()
hoặc fetch()
và đang trong quá trình tải xuống, thì hệ thống sẽ trả về trạng thái tải xuống và không bắt đầu thêm quá trình tải xuống nào.
Theo dõi trạng thái tải xuống
Bạn nên triển khai một AssetPackStateUpdatedListener
để theo dõi tiến trình cài đặt gói tài sản. Thông tin cập nhật trạng thái cho từng gói được chia nhỏ để hỗ trợ việc theo dõi trạng thái của từng gói tài sản. Bạn có thể bắt đầu sử dụng các gói tài sản hiện có trước khi tất cả tài nguyên tải xuống khác theo yêu cầu của bạn hoàn tất.
Kotlin
fun registerListener(listener: AssetPackStateUpdatedListener) fun unregisterListener(listener: AssetPackStateUpdatedListener)
Java
void registerListener(AssetPackStateUpdatedListener listener) void unregisterListener(AssetPackStateUpdatedListener listener)
Tài nguyên tải xuống kích thước lớn
Nếu tệp tải xuống lớn hơn 200 MB và người dùng không sử dụng Wi-Fi, thì tệp tải xuống đó
không bắt đầu cho đến khi người dùng đồng ý một cách rõ ràng để tiếp tục với
tải xuống bằng kết nối dữ liệu di động. Tương tự, nếu tài nguyên tải xuống có kích thước lớn và người dùng mất Wi-Fi, thì quá trình tải xuống sẽ tạm dừng và cần có sự đồng ý rõ ràng của người dùng để tiếp tục sử dụng kết nối dữ liệu di động. Gói bị tạm dừng có trạng thái là
WAITING_FOR_WIFI
. Để kích hoạt quy trình giao diện người dùng nhắc người dùng đồng ý, hãy sử dụng
showConfirmationDialog()
.
Hãy lưu ý rằng nếu ứng dụng không gọi phương thức này, thì quá trình tải xuống sẽ tạm dừng và tự động tiếp tục lại khi người dùng có kết nối Wi-Fi.
Yêu cầu người dùng xác nhận
Nếu một gói có trạng thái REQUIRES_USER_CONFIRMATION
thì quá trình tải xuống sẽ không
tiếp tục cho đến khi người dùng chấp nhận hộp thoại hiển thị với
showConfirmationDialog()
.
Trạng thái này có thể xảy ra khi Play không nhận dạng được ứng dụng, chẳng hạn như khi
ứng dụng được tải không qua cửa hàng ứng dụng.
Lưu ý rằng việc gọi
showConfirmationDialog()
trong trường hợp này, ứng dụng sẽ được cập nhật. Sau khi cập nhật, bạn sẽ cần
để yêu cầu lại nội dung.
Sau đây là ví dụ về cách triển khai trình nghe:
Kotlin
private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.StartIntentSenderForResult() ) { result -> if (result.resultCode == RESULT_OK) { Log.d(TAG, "Confirmation dialog has been accepted.") } else if (result.resultCode == RESULT_CANCELED) { Log.d(TAG, "Confirmation dialog has been denied by the user.") } } assetPackManager.registerListener { assetPackState -> when(assetPackState.status()) { AssetPackStatus.PENDING -> { Log.i(TAG, "Pending") } AssetPackStatus.DOWNLOADING -> { val downloaded = assetPackState.bytesDownloaded() val totalSize = assetPackState.totalBytesToDownload() val percent = 100.0 * downloaded / totalSize Log.i(TAG, "PercentDone=" + String.format("%.2f", percent)) } AssetPackStatus.TRANSFERRING -> { // 100% downloaded and assets are being transferred. // Notify user to wait until transfer is complete. } AssetPackStatus.COMPLETED -> { // Asset pack is ready to use. Start the game. } AssetPackStatus.FAILED -> { // Request failed. Notify user. Log.e(TAG, assetPackState.errorCode()) } AssetPackStatus.CANCELED -> { // Request canceled. Notify user. } AssetPackStatus.WAITING_FOR_WIFI, AssetPackStatus.REQUIRES_USER_CONFIRMATION -> { if (!confirmationDialogShown) { assetPackManager.showConfirmationDialog(activityResultLauncher); confirmationDialogShown = true } } AssetPackStatus.NOT_INSTALLED -> { // Asset pack is not downloaded yet. } AssetPackStatus.UNKNOWN -> { Log.wtf(TAG, "Asset pack status unknown") } } }
Java
assetPackStateUpdateListener = new AssetPackStateUpdateListener() { private final ActivityResultLauncher<IntentSenderRequest> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == RESULT_OK) { Log.d(TAG, "Confirmation dialog has been accepted."); } else if (result.getResultCode() == RESULT_CANCELED) { Log.d(TAG, "Confirmation dialog has been denied by the user."); } } }); @Override public void onStateUpdate(AssetPackState assetPackState) { switch (assetPackState.status()) { case AssetPackStatus.PENDING: Log.i(TAG, "Pending"); break; case AssetPackStatus.DOWNLOADING: long downloaded = assetPackState.bytesDownloaded(); long totalSize = assetPackState.totalBytesToDownload(); double percent = 100.0 * downloaded / totalSize; Log.i(TAG, "PercentDone=" + String.format("%.2f", percent)); break; case AssetPackStatus.TRANSFERRING: // 100% downloaded and assets are being transferred. // Notify user to wait until transfer is complete. break; case AssetPackStatus.COMPLETED: // Asset pack is ready to use. Start the game. break; case AssetPackStatus.FAILED: // Request failed. Notify user. Log.e(TAG, assetPackState.errorCode()); break; case AssetPackStatus.CANCELED: // Request canceled. Notify user. break; case AssetPackStatus.WAITING_FOR_WIFI: case AssetPackStatus.REQUIRES_USER_CONFIRMATION: if (!confirmationDialogShown) { assetPackManager.showConfirmationDialog(activityResultLauncher); confirmationDialogShown = true; } break; case AssetPackStatus.NOT_INSTALLED: // Asset pack is not downloaded yet. break; case AssetPackStatus.UNKNOWN: Log.wtf(TAG, "Asset pack status unknown") break; } } }
Ngoài ra, bạn có thể sử dụng phương thức getPackStates()
để biết trạng thái của các tài nguyên đã tải xuống hiện có.
AssetPackStates
chứa thông tin về tiến trình tải xuống, trạng thái tải xuống và mọi mã lỗi thất bại (failure).
Truy cập gói tài sản
Bạn có thể truy cập gói tài sản bằng cách sử dụng lệnh gọi hệ thống tệp sau khi yêu cầu tải xuống đạt trạng thái COMPLETED
. Hãy sử dụng phương thức getPackLocation()
để lấy thư mục gốc của gói tài sản.
Các tài sản được lưu trữ trong thư mục assets
thuộc thư mục gốc của gói tài sản. Bạn có thể lấy đường dẫn đến thư mục assets
bằng cách sử dụng phương thức assetsPath()
thuận tiện.
Hãy sử dụng phương thức sau đây để lấy đường dẫn đến một tài sản cụ thể:
Kotlin
private fun getAbsoluteAssetPath(assetPack: String, relativeAssetPath: String): String? { val assetPackPath: AssetPackLocation = assetPackManager.getPackLocation(assetPack) // asset pack is not ready ?: return null val assetsFolderPath = assetPackPath.assetsPath() // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets") return FilenameUtils.concat(assetsFolderPath, relativeAssetPath) }
Java
private String getAbsoluteAssetPath(String assetPack, String relativeAssetPath) { AssetPackLocation assetPackPath = assetPackManager.getPackLocation(assetPack); if (assetPackPath == null) { // asset pack is not ready return null; } String assetsFolderPath = assetPackPath.assetsPath(); // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets"); String assetPath = FilenameUtils.concat(assetsFolderPath, relativeAssetPath); return assetPath; }
Các cách khác để gửi API Play Asset Delivery
Sau đây là một số phương thức API khác mà có thể bạn muốn sử dụng trong ứng dụng.
Huỷ yêu cầu
Sử dụng cancel()
để huỷ yêu cầu về gói tài sản đang hoạt động. Xin lưu ý rằng yêu cầu này là một thao tác được thực hiện trên cơ sở nỗ lực tối đa.
Xoá gói tài sản
Sử dụng requestRemovePack()
hoặc removePack()
để lên lịch xoá gói tài sản.
Xem thông tin vị trí của nhiều gói tài sản
Sử dụng getPackLocations()
để truy vấn trạng thái của nhiều gói tài sản cùng một lúc, các thao tác này sẽ trả về thông tin bản đồ và vị trí của các gói tài sản. Bản đồ do getPackLocations()
trả về chứa mục thông tin cho từng gói đang được tải xuống và cập nhật.
Bước tiếp theo
Kiểm thử Play Asset Delivery trên thiết bị và trên Google Play.