Đầu vào TV của bạn phải cung cấp dữ liệu Hướng dẫn chương trình điện tử (EPG) ít nhất một kênh trong hoạt động thiết lập. Bạn cũng nên định kỳ cập nhật dữ liệu, có cân nhắc quy mô của bản cập nhật và luồng xử lý xử lý vấn đề đó. Ngoài ra, bạn có thể cung cấp đường liên kết ứng dụng cho các kênh giúp hướng dẫn người dùng đến nội dung và hoạt động có liên quan. Bài học này thảo luận về việc tạo và cập nhật dữ liệu kênh và chương trình trên cơ sở dữ liệu hệ thống của mình để cân nhắc đến những điều này.
Dùng thử Ứng dụng mẫu TV Input Service.
Xin cấp quyền
Để đầu vào TV hoạt động với dữ liệu EPG, đầu vào đó phải khai báo quyền ghi vào tệp kê khai Android như sau:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Đăng ký kênh trong cơ sở dữ liệu
Cơ sở dữ liệu hệ thống Android TV duy trì các bản ghi dữ liệu kênh cho đầu vào TV. Trong phần thiết lập
đối với mỗi kênh, bạn phải liên kết dữ liệu kênh với các trường sau đây
Lớp TvContract.Channels
:
COLUMN_DISPLAY_NAME
– tên hiển thị của kênhCOLUMN_DISPLAY_NUMBER
– kênh hiển thị sốCOLUMN_INPUT_ID
– Mã nhận dạng của dịch vụ đầu vào TVCOLUMN_SERVICE_TYPE
– loại dịch vụ của kênhCOLUMN_TYPE
– tiêu chuẩn phát sóng của kênh lượt chuyển đổiCOLUMN_VIDEO_FORMAT
- định dạng video mặc định đối với kênh
Mặc dù khung đầu vào TV đủ tổng quát để xử lý cả phát sóng truyền thống và nội dung trực tiếp qua Internet (OTT) mà không có bất kỳ sự phân biệt nào, bạn có thể muốn xác định các cột sau trong ngoài những tiêu chí nêu trên để xác định tốt hơn các kênh phát sóng truyền thống:
COLUMN_ORIGINAL_NETWORK_ID
– tivi mã mạngCOLUMN_SERVICE_ID
– mã dịch vụCOLUMN_TRANSPORT_STREAM_ID
– luồng truyền tải giá trị nhận dạng
Nếu muốn cung cấp thông tin chi tiết về đường liên kết ứng dụng cho các kênh của mình, bạn cần hãy cập nhật một số trường bổ sung. Để biết thêm thông tin về trường liên kết ứng dụng, hãy xem Thêm thông tin về đường liên kết ứng dụng.
Đối với đầu vào TV dựa trên tính năng phát trực tuyến qua Internet, hãy gán các giá trị của riêng bạn cho các giá trị trên sao cho phù hợp mỗi kênh có thể được xác định duy nhất.
Lấy siêu dữ liệu kênh (ở định dạng XML, JSON hoặc bất kỳ định dạng nào khác) từ máy chủ phụ trợ và trong phần thiết lập hoạt động ánh xạ các giá trị vào cơ sở dữ liệu hệ thống như sau:
Kotlin
val values = ContentValues().apply { put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number) put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name) put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId) put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId) put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId) put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat) } val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)
Java
ContentValues values = new ContentValues(); values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number); values.put(Channels.COLUMN_DISPLAY_NAME, channel.name); values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId); values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId); values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId); values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat); Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
Trong ví dụ trên, channel
là một đối tượng lưu giữ siêu dữ liệu kênh từ
máy chủ phụ trợ.
Trình bày thông tin về kênh và chương trình
Ứng dụng TV hệ thống hiển thị thông tin về kênh và chương trình cho người dùng khi họ lướt xem các kênh. như minh hoạ trong hình 1. Để đảm bảo thông tin về kênh và chương trình phù hợp với người trình bày thông tin về kênh và chương trình, hãy làm theo các nguyên tắc dưới đây.
- Số kênh (
COLUMN_DISPLAY_NUMBER
) - Biểu tượng
(
android:icon
trong tệp kê khai của đầu vào TV) - Mô tả chương trình (
COLUMN_SHORT_DESCRIPTION
) - Tên chương trình (
COLUMN_TITLE
) - Biểu trưng của kênh (
TvContract.Channels.Logo
)- Sử dụng màu #EEEEEE để khớp với văn bản xung quanh
- Không thêm khoảng đệm
- Ảnh áp phích (
COLUMN_POSTER_ART_URI
)- Tỷ lệ khung hình từ 16:9 đến 4:3
![](https://developer.android.com/static/images/tv/channel-info.png?authuser=19&hl=vi)
Hình 1. Kênh ứng dụng TV hệ thống và người trình bày thông tin chương trình.
Ứng dụng truyền hình hệ thống cung cấp cùng một thông tin thông qua hướng dẫn chương trình, bao gồm ảnh áp phích, như minh hoạ trong hình 2.
![](https://developer.android.com/static/images/tv/prog-guide.png?authuser=19&hl=vi)
Hình 2. Hướng dẫn chương trình ứng dụng TV hệ thống.
Cập nhật dữ liệu kênh
Khi cập nhật dữ liệu kênh hiện có, hãy sử dụng
update()
thay vì xoá và thêm lại dữ liệu. Bạn có thể xác định phiên bản hiện tại của dữ liệu
bằng cách sử dụng Channels.COLUMN_VERSION_NUMBER
và Programs.COLUMN_VERSION_NUMBER
khi chọn các bản ghi cần cập nhật.
Lưu ý: Thêm dữ liệu kênh vào ContentProvider
có thể tốn thời gian. Thêm các chương trình hiện tại (các chương trình trong vòng hai giờ kể từ thời điểm hiện tại)
chỉ khi bạn định cấu hình EpgSyncJobService
để cập nhật các thông tin còn lại
của dữ liệu kênh trong nền. Xem
Ví dụ: ứng dụng mẫu Android TV Live TV.
Tải hàng loạt dữ liệu kênh
Khi cập nhật cơ sở dữ liệu hệ thống với một lượng lớn dữ liệu kênh, hãy sử dụng ContentResolver
applyBatch()
hoặc
bulkInsert()
. Dưới đây là ví dụ về cách sử dụng applyBatch()
:
Kotlin
val ops = ArrayList<ContentProviderOperation>() val programsCount = channelInfo.mPrograms.size channelInfo.mPrograms.forEachIndexed { index, program -> ops += ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI).run { withValues(programs[index]) withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) withValue( TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000 ) build() } programStartSec += program.durationSec if (index % 100 == 99 || index == programsCount - 1) { try { contentResolver.applyBatch(TvContract.AUTHORITY, ops) } catch (e: RemoteException) { Log.e(TAG, "Failed to insert programs.", e) return } catch (e: OperationApplicationException) { Log.e(TAG, "Failed to insert programs.", e) return } ops.clear() } }
Java
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); int programsCount = channelInfo.mPrograms.size(); for (int j = 0; j < programsCount; ++j) { ProgramInfo program = channelInfo.mPrograms.get(j); ops.add(ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI) .withValues(programs.get(j)) .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000) .build()); programStartSec = programStartSec + program.durationSec; if (j % 100 == 99 || j == programsCount - 1) { try { getContentResolver().applyBatch(TvContract.AUTHORITY, ops); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to insert programs.", e); return; } ops.clear(); } }
Xử lý dữ liệu kênh một cách không đồng bộ
Thao tác đối với dữ liệu, chẳng hạn như tìm nạp luồng từ máy chủ hoặc truy cập cơ sở dữ liệu,
không chặn luồng giao diện người dùng. Sử dụng AsyncTask
là một
để thực hiện cập nhật không đồng bộ. Ví dụ: khi tải thông tin kênh từ một máy chủ phụ trợ,
bạn có thể sử dụng AsyncTask
như sau:
Kotlin
private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() { override fun doInBackground(vararg uris: Uri) { try { fetchUri(uris[0]) } catch (e: IOException) { Log.d("LoadTvInputTask", "fetchUri error") } } @Throws(IOException::class) private fun fetchUri(videoUri: Uri) { context.contentResolver.openInputStream(videoUri).use { inputStream -> Xml.newPullParser().also { parser -> try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) sTvInput = ChannelXMLParser.parseTvInput(parser) sSampleChannels = ChannelXMLParser.parseChannelXML(parser) } catch (e: XmlPullParserException) { e.printStackTrace() } } } } }
Java
private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> { private Context mContext; public LoadTvInputTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... uris) { try { fetchUri(uris[0]); } catch (IOException e) { Log.d("LoadTvInputTask", "fetchUri error"); } return null; } private void fetchUri(Uri videoUri) throws IOException { InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(videoUri); XmlPullParser parser = Xml.newPullParser(); try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); sTvInput = ChannelXMLParser.parseTvInput(parser); sSampleChannels = ChannelXMLParser.parseChannelXML(parser); } catch (XmlPullParserException e) { e.printStackTrace(); } } finally { if (inputStream != null) { inputStream.close(); } } } }
Nếu bạn cần thường xuyên cập nhật dữ liệu EPG, hãy cân nhắc sử dụng
WorkManager
chạy quá trình cập nhật trong thời gian không hoạt động, chẳng hạn như lúc 3 giờ sáng hằng ngày
Có một số kỹ thuật khác để tách riêng tác vụ cập nhật dữ liệu khỏi luồng giao diện người dùng: sử dụng tham số
HandlerThread
hoặc bạn có thể triển khai lớp của riêng mình bằng cách sử dụng Looper
và Handler
lớp. Xem
Các quy trình và luồng để biết thêm thông tin.
Thêm thông tin về đường liên kết đến ứng dụng
Kênh có thể sử dụng đường liên kết ứng dụng để cho phép người dùng dễ dàng khởi chạy một ứng dụng hoạt động trong lúc xem nội dung kênh. Hoạt động sử dụng ứng dụng của kênh đường liên kết ứng dụng để tăng mức độ tương tác của người dùng bằng cách khởi chạy các hoạt động cho thấy liên quan hoặc nội dung bổ sung. Ví dụ: bạn có thể sử dụng đường liên kết đến ứng dụng để làm như sau:
- Hướng dẫn người dùng khám phá và mua nội dung có liên quan.
- Cung cấp thêm thông tin về nội dung đang phát.
- Khi đang xem nội dung nhiều tập, hãy bắt đầu xem tập tiếp theo trong loạt phim.
- Cho phép người dùng tương tác với nội dung, chẳng hạn như xếp hạng hoặc đánh giá nội dung mà không làm gián đoạn quá trình phát nội dung.
Đường liên kết đến ứng dụng sẽ hiển thị khi người dùng nhấn vào Select (Chọn) để hiển thị Trình đơn TV trong khi xem nội dung kênh.
![](https://developer.android.com/static/images/training/tv/tif/app-link.png?authuser=19&hl=vi)
Hình 1. Ví dụ về đường liên kết đến ứng dụng được hiển thị trên hàng Channels (Kênh) khi nội dung kênh được hiển thị.
Khi người dùng chọn đường liên kết đến ứng dụng, hệ thống sẽ bắt đầu một hoạt động bằng cách sử dụng URI ý định do ứng dụng kênh chỉ định. Nội dung trên kênh sẽ tiếp tục phát trong khi hoạt động liên kết ứng dụng đang hoạt động. Người dùng có thể quay lại kênh bằng cách nhấn Quay lại.
Cung cấp dữ liệu về kênh liên kết ứng dụng
Android TV tự động tạo một đường liên kết đến ứng dụng cho mỗi kênh,
bằng cách sử dụng thông tin từ dữ liệu kênh. Cách cung cấp thông tin về đường liên kết đến ứng dụng:
hãy chỉ định các thông tin chi tiết sau trong
Trường TvContract.Channels
:
COLUMN_APP_LINK_COLOR
– màu nhấn của liên kết ứng dụng cho kênh này. Ví dụ về màu nhấn, xem hình 2, chú thích 3.COLUMN_APP_LINK_ICON_URI
– URI cho biểu tượng huy hiệu ứng dụng của đường liên kết đến ứng dụng của kênh này. Đối với ví dụ về biểu tượng huy hiệu ứng dụng, xem hình 2, chú thích 2.COLUMN_APP_LINK_INTENT_URI
– URI ý định của đường liên kết đến ứng dụng cho kênh này. Bạn có thể tạo URI sử dụngtoUri(int)
vớiURI_INTENT_SCHEME
và chuyển đổi URI trở lại ý định ban đầu bằngparseUri()
.COLUMN_APP_LINK_POSTER_ART_URI
– URI cho ảnh áp phích được dùng làm nền của đường liên kết đến ứng dụng cho kênh này. Để xem ví dụ về hình ảnh áp phích, vui lòng xem hình 2, chú thích 1.COLUMN_APP_LINK_TEXT
– Văn bản mô tả của đường liên kết đến ứng dụng cho kênh này. Ví dụ nội dung mô tả về đường liên kết đến ứng dụng, xem văn bản trong hình 2, chú thích 3.
![](https://developer.android.com/static/images/training/tv/tif/app-link-diagram.png?authuser=19&hl=vi)
Hình 2. Thông tin chi tiết về đường liên kết đến ứng dụng.
Nếu dữ liệu kênh không chỉ định thông tin về đường liên kết ứng dụng, hệ thống sẽ tạo một đường liên kết mặc định đến ứng dụng. Hệ thống sẽ chọn các thông tin chi tiết mặc định như sau:
- Đối với URI ý định
(
COLUMN_APP_LINK_INTENT_URI
), hệ thống sẽ sử dụngACTION_MAIN
hoạt động cho danh mụcCATEGORY_LEANBACK_LAUNCHER
, thường được xác định trong tệp kê khai ứng dụng. Nếu hoạt động này không được xác định, một đường liên kết đến ứng dụng không hoạt động sẽ xuất hiện nếu người dùng nhấp vào thì sẽ không có gì xảy ra. - Đối với văn bản mô tả
(
COLUMN_APP_LINK_TEXT
), hệ thống sử dụng "Mở app-name". Nếu không xác định URI ý định của đường liên kết ứng dụng khả thi, hệ thống sẽ sử dụng "Không có liên kết nào". - Dành cho màu nhấn
(
COLUMN_APP_LINK_COLOR
), hệ thống sẽ sử dụng màu mặc định của ứng dụng. - Đối với hình ảnh áp phích
(
COLUMN_APP_LINK_POSTER_ART_URI
), hệ thống sẽ dùng biểu ngữ trên màn hình chính của ứng dụng. Nếu ứng dụng không cung cấp biểu ngữ, hệ thống sẽ sử dụng hình ảnh ứng dụng TV mặc định. - Đối với biểu tượng huy hiệu
(
COLUMN_APP_LINK_ICON_URI
), hệ thống sử dụng huy hiệu hiển thị tên ứng dụng. Nếu hệ thống cũng đang sử dụng biểu ngữ ứng dụng hoặc hình ảnh ứng dụng mặc định cho hình ảnh áp phích, sẽ không có huy hiệu ứng dụng nào hiển thị.
Bạn chỉ định thông tin chi tiết về đường liên kết ứng dụng cho các kênh trong
thiết lập hoạt động. Bạn có thể cập nhật các chi tiết về đường liên kết đến ứng dụng này bất cứ lúc nào để
nếu một đường liên kết đến ứng dụng cần phù hợp với các thay đổi trên kênh, hãy cập nhật ứng dụng
thông tin về đường liên kết và cuộc gọi
ContentResolver.update()
nếu cần. Để biết thêm thông tin về việc cập nhật
dữ liệu kênh, hãy xem Cập nhật dữ liệu kênh.