步骤3: 插入第一个频道
现在介绍如何插入第一个频道。除了这张图表之外,还可以查看Android开发基础知识中的TIF架构图。

电视输入会在电视输入框架(TIF)数据库中插入频道和节目元数据。此数据将用于在Fire TV直播部分中显示服务的直播内容。电视输入频道和节目元数据必须为最新状态,且与应用内部数据相匹配。步骤3和4将演示如何插入此数据并使其保持最新状态。
向清单中添加权限
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
必须先在AndroidManifest.xml
中添加这些权限,然后您的应用才能与TIF数据库交互。
在Android电视数据库中插入频道元数据
以下示例展示了如何在Android电视数据库中插入基本频道。在SetupActivity类中添加以下代码: 在Android电视数据库中插入基本频道的方法有两种:可以在一个类或对象中插入频道:
import android.content.ContentValues;
import android.media.tv.TvContract;
import android.util.Log;
import android.net.Uri;
private long insertChannel() {
ContentValues values = new contentValues();
values.put(TvContract.Channels.COLUMN_INPUT_ID, "com.example.android.sampletvinput/.RichTvInputService");
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "My Test Channel");
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "3");
Uri uri = getApplicationContext().getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
Log.i("SetupActivity", "Channel Inserted! Uri: " + uri);
long channelId = Long.parseLong(uri.getLastPathSegment());
return channelId;
}
import androidx.tvprovider.media.tv.Channel;
import android.media.tv.TvContract;
import android.util.Log;
import android.net.Uri;
private long insertChannel() {
Channel testChannel = new Channel.Builder()
.setDisplayName("My Test Channel")
.setDisplayNumber("3")
.setInputId("com.example.android.sampletvinput/.RichTvInputService")
.build();
Uri uri = getApplicationContext().getContentResolver().insert(TvContract.Channels.CONTENT_URI, testChannel.toContentValues());
Log.i("SetupActivity", "Channel Inserted! Uri: " + uri);
long channelId = Long.parseLong(uri.getLastPathSegment());
return channelId;
}
接下来,在SetupActivity中调用onCreate()
方法(取代现有代码)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rich_setup);
insertChannel();
}
Activity | 是否必需? | 输入 | 说明 |
---|---|---|---|
COLUMN_INPUT_ID |
是 | TvInputService的完整类路径 | 示例: TvInputService位于应用主程序包中,完整类路径为<应用程序包>/<TvInputService的相对路径> 。 如果TvInputService位于单独的程序包中,则inputId应为<应用程序包>/<完整单独包+TvInputService的路径> 。 |
TvContract.Channels.CONTENT_URI |
是 | 这是指向Android电视数据库中频道表的URI。 | |
ContentResolver.bulkInsert() 或ContentResolver.applyBatch() |
是,位于生产代码中 | 以上任意一项都可以确保在一次数据库操作中完成所有频道插入。 |
插入GracenoteId
如果没有使用Gracenote,请跳过本节。
Gracenote是与Fire TV集成的电视目录提供方,能够从云端提供频道和节目的元数据。如果您的内容已与Gracenote集成,则可以提供唯一的ID,供Fire TV用来收集元数据。如果有兴趣与Gracenote集成,请联系您的亚马逊联系人以了解更多信息。
以下示例展示了如何使用亚马逊合约密钥将唯一频道Gracenote ID插入到JSON对象中,以显示频道类型和ID。可以在SetupActivity.java
中插入channels函数。
/**
* 外部ID的类型,类型列表需要被定义为wiki中的常量(以下列表的合约)
* 空值或无效数据将导致元数据的服务匹配失败
*/
private final static String AMZ_KEY_EXTERNAL_ID_TYPE = "externalIdType";
/**
* Gracenote输入类型的ID
*/
private final static String EXTERNAL_ID_TYPE_GRACENOTE_ONTV = "gracenote_ontv"; // gracenote ontv id
private final static String EXTERNAL_ID_TYPE_GRACENOTE_GVD = "gracenote_gvd"; // gracenote gvd id
/**
* 外部ID的值,用于匹配服务元数据。
* 空值或无效数据将导致元数据的服务匹配失败
*/
private final static String AMZ_KEY_EXTERNAL_ID_VALUE = "externalIdValue";
...
try {
String jsonString = new JSONObject()
.put(AMZ_KEY_EXTERNAL_ID_TYPE, EXTERNAL_ID_TYPE_GRACENOTE_ONTV)
.put(AMZ_KEY_EXTERNAL_ID_VALUE, < gracenote Id > )
.put(AMZ_KEY_PLAYBACK_DEEP_LINK_URI, playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME))
.toString();
values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
} catch (JSONException e) {
Log.e(TAG, "Error when adding data to blob " + e);
}
Activity | 是否必需? | 说明 |
---|---|---|
externalIdType 和externalIdValue |
是 | 这些字段名称属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供Gracenote信息。请勿更改这些字符串。 |
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA |
是 | 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供深层链接和Gracenote信息。 |
- 如果您的Gracenote ID属于其他类型,请查看这是哪个类型。如果不确定,请联系您的亚马逊联系人。
-
如果您计划使用Gracenote,但尚未拥有Gracenote ID,则可能暂时需要采用以下方式来进行开发。在美国/英国/德国,可以使用以下示例ID: 10171(迪士尼频道)、10240 (HBO)和12131(Cartoon Network),带有gracenote_ontv
externalIdType
。对于所有其他市场,可以使用以下示例ID,即带有gracenote_gvdexternalIdType
的GN9BBXQSECYVNGW
(HBO)。重要须知: 如果频道支持深层链接和gracenoteId,则应使用上述合约将这二者插入同一JSON对象中。
如果使用深层链接进行播放
如果选择使用深层链接,则使用亚马逊合约密钥字符串playbackDeepLinkUri
将深层链接插入到JSON对象中。
/**
* 用于在外部播放器中插入播放的深层链接的URI。
* 空值或无效数据将导致默认与GLive TV Native播放器集成
*/
private final static String AMZ_KEY_PLAYBACK_DEEP_LINK_URI = "playbackDeepLinkUri";
...
Intent playbackDeepLinkIntent = new Intent();
...
// 构建频道的contentValues
ContentValues values = new contentValues();
values.put(Channels.COLUMN_INPUT_ID, inputId);
values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
...
// 构建深层链接Intent
playbackDeepLinkIntent = //提供方频道的深层链接Intent
...
try {
String jsonString = new JSONObject()
.put(AMZ_KEY_PLAYBACK_DEEP_LINK_URI, playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME))
.toString();
// 将jsonString添加到频道的contentValues
values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
} catch (JSONException e) {
Log.i(TAG, "Error when adding data to blob " + e);
}
Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
Activity | 是否必需? | 说明 |
---|---|---|
playbackDeepLinkUri |
是 | 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供频道的深层链接信息。请勿更改此字符串。 |
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA |
是 | 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供深层链接和Gracenote信息。 |
检查点 - 在Fire TV的UI中显示一个频道
- 在Fire TV上构建并安装您的APK。
- 导航到“Settings(设置)”>“Live TV(电视直播)”>“Sync Sources(同步来源)”,并选择相应的来源。
- 导航到主页 >“On Now”行。插入的频道应显示为卡片(内容框,有时称为磁贴)之一。如果没有使用Gracenote,则将看到一个带有频道名称的灰色磁贴。如果您的设备上有多个来自其他来源的频道,则设备可能无法显示这些频道(存在限制)。
- 导航到“Live TV”>“Channel Guide”,打开“Options(选项)”菜单(3行)>“Filter Channels(筛选频道)”>“您的输入名称”。插入的频道应显示为屏幕上的一行。
- 导航到“Settings”>“Live TV”>“Manage Channels(管理频道)”。输入名称(来自作业服务XML文件)应显示在列表下方,并且插入的频道应该已被分配给该输入名称。
- (如果使用深层链接)单击“On Now”行的频道卡片。此时应用应该启动并显示预期的频道。
- (如果已集成Gracenote)频道将在“On Now”行和“Channel Guide”中显示完整的节目元数据。
故障排除
频道没有显示在“On Now(正在播放)”行或“Channel Guide(频道指南)”中
- 请参阅“检查点”以确认是否已在允许列表中添加该频道。
- 确认频道的inputId是否与TvInputService的完整类路径等同。
- 确认调试APK和生产APK是否具有相同的程序包名称。
- 确认频道是否正确插入到TIF中。
- 插入之后,为频道信息创建硬编码查询,以确保频道位于数据库中。
- 确认亚马逊能否正确提取该频道。
-
插入频道之前,请查看adb日志:
对于Mac/Linux,请查看
adb logcat | grep StationSync
对于Windows,请查看
adb logcat | findstr StationSync
-
插入频道后,您应该能够看到类似下文所示的日志。“Added(已添加)”意味着亚马逊正在识别Android电视数据库中的新频道。
-
08-07 15:24:57.101 11882 11941 I StationSync: Started full channel sync
08-07 15:24:57.188 11882 11941 I StationSync: Finished full channel sync, found: 15, added: 1, removed: 0, updated: 0
频道在“On Now”行中显示为没有图像的空白磁贴(仅显示频道名称)
- 如果频道未集成Gracenote,则此情况属于预期行为。如果已集成Gracenote,请参阅以下信息。
频道具有Gracenote ID,但“On Now”行或“Channel Guide”中没有显示元数据
- 确保您清楚自己的源支持onTV还是GVD,并在
TvContractUtils
中准确定义这一点。Amazon Catalog在某些市场支持onTV。如果亚马逊的支持情况与您拥有的Gracenote ID不匹配,请联系您的亚马逊联系人。他们可能会与Gracenote共同修正该问题,或切换到TIF。 - 重复检查Gracenote ID值。onTV仅使用数字值,而GVD使用字母数字。
后续步骤
如果没有使用Gracenote,请跳过本步骤: 步骤4: 插入节目。
如果正在使用Gracenote,请跳过步骤4,然后转到以下内容: 步骤5: 在Fire TV UI中播放。