The second Android 11 Developer Preview is now available, test it out and share your feedback.

Integrate asset delivery (Java)

Use the steps in this guide to access your app’s asset packs from your Java code. If you haven't built an app bundle with asset packs, see Build for native or Java before going further.

Overview

The Play Core Java API provides the AssetPackManager class for requesting asset packs, managing downloads, and accessing the assets. You implement this API according to the delivery type of the asset pack you wish to access. These steps are shown in the following flowchart.

Asset pack flow diagram for the Java programming language

Figure 1. Flow diagram for accessing asset packs

Install-time delivery

Asset packs configured as install-time are immediately available at app launch. Use the Java AssetManager API to access assets served in this mode:

import android.content.res.AssetManager;
...
Context context = createPackageContext("com.example.app", 0);
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("asset-name");

Fast-follow and on-demand delivery

The following sections show how to get information about asset packs before downloading them, how to call the API to start the download, and then how to access the downloaded packs. These sections apply to fast-follow and on-demand asset packs.

Check status

Each asset pack is stored in a separate folder in the app's internal storage. Use the getPackLocation() method to determine the root folder of an asset pack. This method returns the following values:

Return value Status
A valid AssetPackLocation object Asset pack root folder is ready for immediate access at assetsPath()
null Unknown asset pack or assets are not available

Get download information about asset packs

Apps are required to disclose the size of the download before fetching the asset pack. Use the getPackStates() method to determine the size of the download and whether the pack is already downloading.

Task<AssetPackStates> getPackStates(List<String> packNames)

getPackStates() is an asynchronous method that returns a task. The task’s result contains an AssetPackStates object. The packStates() method of an AssetPackStates object returns a Map<String, AssetPackState>. This map contains the state of each requested asset pack, keyed by its name:

Map<String, AssetPackState> AssetPackStates#packStates()

The final request is shown by the following:

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

The following AssetPackState methods provide the size of the asset pack, the downloaded amount so far (if requested), and the amount already transferred to the app:

To get the status of an asset pack, use the status() method, which returns the status as an integer that corresponds to a constant field in the AssetPackStatus class. An asset pack that’s not installed yet has the status AssetPackStatus.NOT_INSTALLED.

If a request fails, use the errorCode() method, whose return value corresponds to a constant field in the AssetPackErrorCode class.

Install

Use the fetch() method to download an asset pack for the first time or call for the update of an asset pack to complete:

Task<AssetPackStates> fetch(List<String> packNames)

This method returns an AssetPackStates object containing a list of packs and their initial download states and sizes. If an asset pack requested via fetch() is already downloading, the download status is returned and no additional download is started.

Monitor download states

You should implement a listener to track the installation progress of asset packs. The status updates are broken down per pack to support tracking the status of individual asset packs. You can start using available asset packs before all other downloads for your request have completed.

Large downloads

If the download is larger than 150 MB and the user is not on Wi-Fi, the download does not start until the user explicitly gives their consent to proceed with the download using a mobile data connection. Similarly, if the download is large and the user loses Wi-Fi, the download is paused and explicit consent is required to proceed using a mobile data connection. A paused pack has state WAITING_FOR_WIFI. To trigger the UI flow to prompt the user for consent, use the showCellularDataConfirmation() method.

Note that if the app does not call this method, the download is paused and will resume automatically only when the user is back on a Wi-Fi connection.

The following is an example implementation of a listener:

assetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    @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:
          if (!waitForWifiConfirmationShown) {
            assetPackManager.showCellularDataConfirmation(MainActivity.this)
              .addOnSuccessListener(new OnSuccessListener<Integer> () {
                @Override
                public void onSuccess(Integer resultCode) {
                  if (resultCode == RESULT_OK) {
                    Log.d(TAG, "Confirmation dialog has been accepted.");
                  } else if (resultCode == RESULT_CANCELED) {
                    Log.d(TAG, "Confirmation dialog has been denied by the user.");
                  }
                }
              });
            waitForWifiConfirmationShown = true;
          }
          break;

        case AssetPackStatus.NOT_INSTALLED:
          // Asset pack is not downloaded yet.
          break;
      }
    }
}

Alternatively, you can use the getPackStates() method to get the status of current downloads. AssetPackStates contains the download progress, download status, and any failure error codes.

Access asset packs

You can access an asset pack using file system calls after the download request reaches the COMPLETED state. Use the getPackLocation() method to get the root folder of the asset pack.

Assets are stored in the assets directory within the asset pack root directory. You can get the path to the assets directory by using the convenience method assetsPath(). Use the following method to get the path to a specific asset:

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

Other Play Core API methods

The following are some additional API methods you may want to use in your app.

Cancel request

Use cancel() to cancel an active asset pack request. Note that this request is a best-effort operation.

Remove an asset pack

Use removePack() to schedule the removal of an asset pack.

Get locations of multiple asset packs

Use getPackLocations() to query the status of multiple asset packs in bulk, which returns a map of asset packs and their locations. The map returned by getPackLocations() contains an entry for each pack that is currently downloaded and up-to-date.

Next step

Test Play Asset Delivery locally and from Google Play.