针对运行 Fire OS 7 的 Amazon Fire TV 设备进行开发


针对运行 Fire OS 7 的 Amazon Fire TV 设备进行开发

Fire OS 7 是基于 Android 9 Pie。您可以按照以下指南确保您的应用与 Fire OS 7 的兼容性。

运行 Fire OS 7 和之前的 Fire OS 版本的设备

2019 年,亚马逊将推出运行 Fire OS 7 的 Fire TV 设备。Fire OS 7 是基于 Android 9 Pie(API 级别 28)。

大多数 2018 Amazon Fire TV 设备运行的是 Fire OS 6,这是基于 Android Nougat(Android 7.1.2 级别 25)的版本。以前的 Fire TV 设备仍然运行 Fire OS 5(Android 5.1 级别 22)。

有关 Fire TV 设备和版本的详细列表,请参阅 Fire OS 版本

亚马逊通常不会提高现有设备的 FOS 级别,因此发布的亚马逊设备很可能保持在其现有级别。

Fire OS 7 中的安卓更改

从 Fire OS 6 升级到 Fire OS 7 需要从 Nougat (Android 7.1.2) 通过 Oreo (Android 8.0) 过渡到 Pie (Android 9)。

Android 9 和 Android 8.0 中引入的更改要求您在应用中更改代码,然后应用才能在 Fire OS 7 设备上正常工作。

Android 9 更改

Android 9 的主要变化包括:

  • 隐私变更

    应用将具有对 Wifi 的有限访问。在后台运行时,应用对用户输入和传感器数据的访问权限将受到限制。

您可以在 Android 9 更改中了解这些更改

Android 8.0 更改

Android 8.0 的主要变化包括:

  • 通知通道

    所有通知(包括推荐和合作伙伴管理的推荐)都必须与通道关联。有关通道的信息,请参阅有关通知通道的安卓培训。有关示例代码和其他详细信息,请参阅 Fire OS 7 中的通知

  • 后台服务

    Android 8.0 限制后台服务的使用,因此,任何使用后台服务刷新其推荐的 PMR 应用都将无法刷新 FOS7 上的推荐。安卓建议使用作业计划程序来避开后台服务的限制。请参阅后台服务

  • 本机库

    如果本机库包含任何同时可写和可执行的负载段,则不再加载本机库。

  • 权限

    应用需要明确请求每个权限,即使在同一个权限组中也是如此。

您可以在 Android 8.0 更改中了解这些更改。

Fire OS 7 的 Android 9 奇偶校验

Fire OS 7 支持 Android 9 中的所有功能。

请记住,虽然 Fire OS 7 具有 Android 9 奇偶校验,但您无法在 Amazon Fire 设备上使用谷歌服务。相反,您必须对您所需的服务(例如应用内购买)使用应用和游戏服务软件开发工具包

有关更多详细信息,请参阅 Amazon Fire TV 开发与 Android TV 开发

让您的应用面向 Fire OS 7 设备

用户可以在 Fire OS 5、Fire OS 6 或 Fire OS 7 设备上运行您的应用。要最大限度地提高应用与设备上的 Fire OS 版本的兼容性,我们建议您根据 SDK 级别确定目标设备。

在您的代码中,您可以检查 Build.VERSION.SDK_INT 是否大于等于 28(Android 9 API 级别)来确定目标 Fire OS 7 设备。

有关更多详细信息,请参阅识别 Fire TV 设备

另请参阅支持不同平台版本

在 Fire OS 7 设备上测试应用的兼容性

目前,您可以通过连接到实际设备来测试应用与 Fire OS 7 的兼容性。应用测试服务仅提供极少的兼容性测试,不会告诉您您的应用是否与 Fire OS 7 设备兼容。

要为 minSdkVersion 和 targetSdkVersion 设置的内容

一般来说,应将您的 minSdkVersion 设置为 22 或更低版本(将其设置为您的应用需要的最低 API 级别)。这将使您的应用可供所有 Amazon Fire TV 设备(运行 Fire OS 5 和 6)以及大多数 Fire 平板电脑设备使用。将 targetSdkVersion 设置为您测试应用时所用的最高 API 级别。

以下是关于这些设置的更多背景。在应用清单(或 build.gradle 文件)中,minSdkVersion 属性设置了应用正常运行所需的最低 SDK 级别。(不支持该 API 级别的设备不应允许在不受支持的设备上安装应用。)

Fire OS 5 设备基于 API 级别 22 (Lollipop 5.1)。Fire OS 7 设备基于 API 级别 28 (Android 9)。通过将 minSdkVersion 设置为 22,您指示您的应用要求设备至少具有 API 级别 22 才能正常运行。

minSdkVersion 设置为 22 时,您的应用还将安装在具有更高 API 级别(例如 28)的任何设备上,因为安卓级别是向后兼容的。API 级别 28 通常包含级别 1 到级别 28 的所有 API(每个版本都添加到上一个版本中)。

但是,假设您想利用 Android 9 中的 API(API 级别 28)。如果您将 minSdkVersion 设置为 22,则您的应用将安装在没有 API 级别 28 的 Fire OS 5 设备上。因此,您必须以防御方式编码来检查设备级别,如果设备不支持该 API 级别,则回退到替代方案。您的代码可能如下所示:

if (Build.VERSION.SDK_INT >= 28) {
 Log.v(TAG, "Yes, this is an API level 28 or higher device");
} else {
 Log.v(TAG, "No, this is not an API level 28 or higher device");
}

此代码检查设备的 API 级别是否大于或等于 28,如果是,则运行代码。如果没有,它会回退到 else 逻辑。

默认情况下,如果未指定 targetSdkVersion,它将使用与 minSdkVersion 相同的值。targetSdkVersion 允许您设置已针对应用测试的最新 API 级别。基于此值,安卓将确保此级别的设备上的正确行为。

例如,如果您将 targetSdkVersion 设置为 23 或更高版本(Marshmallow 的版本),安卓将应用 Marshmallow 中包含的运行时权限检查功能。但是,如果 targetSdkVersion 低于 23(在 Marshmallow 中推出运行时权限检查之前),安卓不会将此行为应用于您的应用。

不推荐使用 maxSdkVersion 属性,但是如果您想让您的应用在 Fire OS 5 设备上可用,则可以将 maxSdkVersion 设置为 22。如果您想让您的应用 在 Fire OS 7 设备上可用,您可以将 minSdkVersion 设置为 28。

有关更多信息,请参阅以下内容:

支持

如果您在 Fire OS 7 上注意到您的应用存在任何问题,请在 Fire 平板电脑论坛Amazon Fire TV 和 Fire TV Stick 论坛中记下此问题。

Fire OS 7 中的通知

从 Android 8.0(API 级别 26)开始,所有通知(包括推荐)都必须分配给一个通道。如果您的应用发送通知或推荐,您需要创建一个通道并将通道与通知关联。没有通道 ID 的通知将被删除。

有关推荐通道的安卓信息

要向通知添加频道,请执行以下操作:

  1. 创建通知通道并将其注册到通知管理器。
  2. 使用以下方法之一设置通知的通道 ID:
    • 使用 createContentRecommendation 并使用反射来设置通道 ID
    • Notification.Builder 与通道 ID 结合使用

创建通道并将其注册到通知管理器

  1. 构建具有唯一通道 ID、用户可见名称和重要性级别的 NotificationChannel 对象。
  2. 或者,使用 setDescription 指定用户在系统设置中看到的描述。
  3. 通过将通知通道传递给方法 createNotificationChannel 来注册通知通道。
private void createNotificationChannel() {
    // 创建通知通道,但仅限于 API 26+,因为
    // NotificationChannel 类是新的,不在支持库中
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // 将通道注册到系统中;您无法更改重要性
        // 或之后的其他通知行为
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

设置通知的通道 ID

方法 1:将 ContentRecommendation 与反射结合使用来设置通道 ID

Notification notification = createContentRecommendation(largeIcon, notificatonId);

if (context.getApplicationInfo().targetSdkVersion >= 26) {
    Log.d(TAG, "SDk target version >= 26 which is Android O");

    try {
        Field channel = notification.getClass().getDeclaredField("mChannelId");
        channel.setAccessible(true);
        channel.set(notification, StringTerms.CHANNEL_ID);
    }
    catch (Exception e) {
        Log.d(TAG, "Can't set ChannelId", e);
    }
}

方法 2:使用 Notification.Builder 设置通道 ID

public Notification getNotificationObject(Context context) {
    Notification.Builder builder = new Notification.Builder(context, "channelId");
    RecommendationExtender recExtender = new RecommendationExtender();

    // 对通知对象中的所有内容推荐数据进行编码

    builder.setCategory(Notification.CATEGORY_RECOMMENDATION);
    builder.setContentTitle(mTitle);
    builder.setContentText(mText);
    builder.setContentInfo(mSourceName);
    builder.setLargeIcon(mContentImage);
    builder.setSmallIcon(mBadgeIconId);
    if (mBackgroundImageUri != null) {
        builder.getExtras().putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, mBackgroundImageUri);
    }
    builder.setColor(mColor);
    builder.setGroup(mGroup);
    builder.setSortKey(mSortKey);
    builder.setProgress(mProgressMax, mProgressAmount, false);
    builder.setAutoCancel(mAutoDismiss);

    if (mContentIntentData != null) {
        PendingIntent contentPending;
        if (mContentIntentData.mType == INTENT_TYPE_ACTIVITY) {
            contentPending = PendingIntent.getActivity(context, mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
            mContentIntentData.mOptions);
        }
        else if (mContentIntentData.mType == INTENT_TYPE_SERVICE) {
            contentPending = PendingIntent.getService(context, mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
            else { // Default:INTENT_TYPE_BROADCAST{
            contentPending = PendingIntent.getBroadcast(context,
            mContentIntentData.mRequestCode,
            mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
        builder.setContentIntent(contentPending);
    }

    if (mDismissIntentData != null) {
        PendingIntent dismissPending;
        if (mDismissIntentData.mType == INTENT_TYPE_ACTIVITY) {
            dismissPending = PendingIntent.getActivity(context, mDismissIntentData.mRequestCode,
            mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
            mDismissIntentData.mOptions);
        }
        else if (mDismissIntentData.mType == INTENT_TYPE_SERVICE) {
            dismissPending = PendingIntent.getService(context, mDismissIntentData.mRequestCode,
            mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
            else { // Default:INTENT_TYPE_BROADCAST{
                dismissPending = PendingIntent.getBroadcast(context,
                mDismissIntentData.mRequestCode,
                mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
        builder.setDeleteIntent(dismissPending);
    }

    recExtender.setContentTypes(mContentTypes);
    recExtender.setGenres(mContentGenres);
    recExtender.setPricingInformation(mPriceType, mPriceValue);
    recExtender.setStatus(mStatus);
    recExtender.setMaturityRating(mMaturityRating);
    recExtender.setRunningTime(mRunningTime);

    builder.extend(recExtender);
    Notification notif = builder.build();
    return notif;
}