อินพุตทีวีของคุณต้องให้ข้อมูลคู่มือโปรแกรมอิเล็กทรอนิกส์ (EPG) เป็นเวลาอย่างน้อย 1 ช่องในกิจกรรมการตั้งค่า นอกจากนี้คุณควรอัปเดตข้อมูลเป็นระยะๆ ข้อมูลโดยพิจารณาจากขนาดของการอัปเดตและเธรดการประมวลผล ที่จัดการเรื่องนี้ นอกจากนี้ ยังระบุลิงก์แอปสำหรับช่องต่างๆ ได้อีกด้วย ที่พาผู้ใช้ไปยังเนื้อหาและกิจกรรมที่เกี่ยวข้อง บทเรียนนี้จะกล่าวถึงการสร้างและอัปเดตข้อมูลช่องและโปรแกรมใน ฐานข้อมูลระบบโดยคำนึงถึงปัจจัยเหล่านี้
ลองใช้ แอปตัวอย่างของบริการอินพุตทีวี
รับสิทธิ์
เพื่อให้อินพุตของทีวีทำงานกับข้อมูล EPG ได้ อินพุตดังกล่าวจะต้องประกาศ สิทธิ์การเขียนในไฟล์ Manifest ของ Android ดังนี้
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
ลงทะเบียนช่องในฐานข้อมูล
ฐานข้อมูลระบบ Android TV จะเก็บบันทึกข้อมูลช่องสำหรับอินพุตทีวี ในการตั้งค่า
สำหรับแต่ละแชแนล คุณต้องจับคู่ข้อมูลช่องของคุณกับฟิลด์ต่อไปนี้
TvContract.Channels
ชั้นเรียน:
COLUMN_DISPLAY_NAME
- ชื่อที่แสดงของ ช่องสัญญาณCOLUMN_DISPLAY_NUMBER
- แชแนลที่แสดง หมายเลขCOLUMN_INPUT_ID
- รหัสของบริการอินพุตทีวีCOLUMN_SERVICE_TYPE
- ประเภทบริการของช่องCOLUMN_TYPE
- มาตรฐานการออกอากาศของช่อง ประเภทCOLUMN_VIDEO_FORMAT
- รูปแบบวิดีโอเริ่มต้น สำหรับช่อง
แม้ว่าเฟรมเวิร์กของอินพุตทีวีจะมีทั่วไปพอที่จะรองรับทั้งการออกอากาศแบบดั้งเดิมและ เนื้อหาการแพร่ภาพและเสียงผ่านโครงข่ายอินเทอร์เน็ต (Over The Top หรือ OTT) โดยไม่มีความแตกต่างใดๆ คุณอาจต้องการกำหนดคอลัมน์ต่อไปนี้ใน นอกเหนือจากรายการข้างต้นเพื่อให้ระบุช่องออกอากาศแบบดั้งเดิมได้ดียิ่งขึ้น:
COLUMN_ORIGINAL_NETWORK_ID
- โทรทัศน์ รหัสเครือข่ายCOLUMN_SERVICE_ID
- รหัสบริการCOLUMN_TRANSPORT_STREAM_ID
- ลำธารขนส่ง รหัส
หากต้องการระบุรายละเอียดลิงก์แอปสำหรับช่อง คุณต้อง อัปเดตช่องเพิ่มเติมบางช่อง ดูข้อมูลเพิ่มเติมเกี่ยวกับช่องลิงก์แอปได้ที่ เพิ่มข้อมูล App Link
สำหรับอินพุตทีวีสตรีมมิงทางอินเทอร์เน็ต ให้กำหนดค่าของคุณเองให้กับข้อมูลข้างต้นเพื่อให้ แต่ละแชแนลจะถูกระบุ โดยไม่ซ้ำกัน
ดึงข้อมูลเมตาของช่อง (ใน XML, JSON หรืออะไรก็ได้) จากเซิร์ฟเวอร์แบ็กเอนด์และในการตั้งค่า กิจกรรมจะจับคู่ค่ากับฐานข้อมูลระบบดังนี้
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);
ในตัวอย่างด้านบน channel
เป็นออบเจ็กต์ที่เก็บข้อมูลเมตาของช่องจาก
เซิร์ฟเวอร์แบ็กเอนด์
นำเสนอข้อมูลช่องและรายการ
แอป System TV นำเสนอข้อมูลช่องและรายการแก่ผู้ใช้เมื่อพวกเขาดูช่องต่างๆ ดังที่แสดงในรูปที่ 1 ตรวจสอบว่าข้อมูลช่องและรายการทำงานร่วมกับแอป System TV ได้ ผู้นำเสนอข้อมูลช่องและโปรแกรม โปรดปฏิบัติตามหลักเกณฑ์ด้านล่าง
- หมายเลขช่อง (
COLUMN_DISPLAY_NUMBER
) - ไอคอน
(
android:icon
ในส่วน ไฟล์ Manifest ของอินพุตทีวี) - รายละเอียดโปรแกรม (
COLUMN_SHORT_DESCRIPTION
) - ชื่อโปรแกรม (
COLUMN_TITLE
) - โลโก้ของช่อง (
TvContract.Channels.Logo
)- ใช้สี #EEEEEE ให้ตรงกับข้อความโดยรอบ
- ไม่รวมระยะห่างจากขอบ
- ภาพโปสเตอร์ (
COLUMN_POSTER_ART_URI
)- สัดส่วนภาพระหว่าง 16:9 และ 4:3
แอป System TV ให้ข้อมูลเดียวกันผ่านคู่มือรายการทีวี ซึ่งรวมถึงภาพโปสเตอร์ ดังที่แสดงในรูปที่ 2
อัปเดตข้อมูลช่อง
เมื่ออัปเดตข้อมูลแชแนลที่มีอยู่ ให้ใช้
update()
แทนการลบและเพิ่มข้อมูลอีกครั้ง คุณระบุข้อมูลเวอร์ชันปัจจุบันได้
โดยใช้ Channels.COLUMN_VERSION_NUMBER
และ Programs.COLUMN_VERSION_NUMBER
เมื่อเลือกระเบียนที่จะอัปเดต
หมายเหตุ: กำลังเพิ่มข้อมูลช่องลงใน ContentProvider
อาจต้องใช้เวลา เพิ่มรายการปัจจุบัน (รายการภายใน 2 ชั่วโมงนับจากเวลาปัจจุบัน)
เฉพาะเมื่อคุณกำหนดค่า EpgSyncJobService
เพื่ออัปเดตส่วนที่เหลือเท่านั้น
ของข้อมูลช่องในเบื้องหลัง โปรดดู
ตัวอย่างแอปตัวอย่างสำหรับ Android TV Live TV
ข้อมูลแชแนลการโหลดเป็นกลุ่ม
เมื่ออัปเดตฐานข้อมูลระบบด้วยข้อมูลช่องจำนวนมาก ให้ใช้ ContentResolver
วันที่ applyBatch()
หรือ
bulkInsert()
ตัวอย่างการใช้ 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(); } }
ประมวลผลข้อมูลช่องแบบไม่พร้อมกัน
การจัดการข้อมูล เช่น การดึงข้อมูลสตรีมจากเซิร์ฟเวอร์หรือการเข้าถึงฐานข้อมูล
ไม่บล็อกเธรด UI การใช้ AsyncTask
ถือเป็น 1
วิธีดำเนินการอัปเดตแบบไม่พร้อมกัน เช่น เมื่อโหลดข้อมูลแชแนลจากเซิร์ฟเวอร์แบ็กเอนด์
คุณใช้ AsyncTask
ได้ดังนี้
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(); } } } }
หากต้องการอัปเดตข้อมูล EPG เป็นประจำ โปรดพิจารณาใช้
WorkManager
เพื่อเรียกใช้กระบวนการอัปเดตในระหว่างที่ไม่มีความเคลื่อนไหว เช่น ทุกวัน เวลา 3:00 น.
เทคนิคอื่นๆ ในการแยกงานอัปเดตข้อมูลจากเธรด UI ได้แก่การใช้
HandlerThread
หรือคุณอาจใช้คลาสของคุณเองโดยใช้ Looper
และ Handler
ชั้นเรียน โปรดดู
กระบวนการและชุดข้อความสำหรับข้อมูลเพิ่มเติม
เพิ่มข้อมูลลิงก์แอป
ช่องสามารถใช้ลิงก์แอปเพื่อให้ผู้ใช้เปิดตัว ขณะดูเนื้อหาของช่อง แอปของช่องใช้งาน ลิงก์แอปเพื่อขยายการมีส่วนร่วมของผู้ใช้ด้วยการเปิดตัวกิจกรรมที่แสดง ข้อมูลที่เกี่ยวข้องหรือเนื้อหาเพิ่มเติม เช่น คุณสามารถใช้ลิงก์แอป ทำสิ่งต่อไปนี้ได้
- แนะนำให้ผู้ใช้ค้นหาและซื้อเนื้อหาที่เกี่ยวข้อง
- ให้ข้อมูลเพิ่มเติมเกี่ยวกับเนื้อหาที่กำลังเล่นอยู่
- ขณะดูคอนเทนต์แบบเป็นตอนๆ ให้เริ่มดูตอนถัดไปใน ซีรีส์
- ให้ผู้ใช้โต้ตอบกับเนื้อหา เช่น ให้คะแนนหรือรีวิว เนื้อหาโดยไม่ขัดจังหวะการเล่นเนื้อหา
ลิงก์แอปจะปรากฏเมื่อผู้ใช้กดเลือกเพื่อแสดง เมนูทีวีขณะรับชมเนื้อหาของช่อง
เมื่อผู้ใช้เลือกลิงก์ของแอป ระบบจะเริ่มกิจกรรมโดยใช้ URI ของ Intent ที่แอปช่องระบุไว้ เนื้อหาของช่องจะเล่นต่อไป ขณะที่กิจกรรม App Link ทำงานอยู่ ผู้ใช้สามารถกลับไปยังช่องได้ โดยการกดกลับ
ระบุข้อมูลช่องทางลิงก์แอป
Android TV จะสร้าง App Link ให้แต่ละช่องโดยอัตโนมัติ
โดยใช้ข้อมูลจากช่อง วิธีระบุข้อมูล App Link
โปรดระบุรายละเอียดต่อไปนี้ใน
TvContract.Channels
ช่อง:
COLUMN_APP_LINK_COLOR
- สีเฉพาะจุดของลิงก์แอปสำหรับช่องนี้ เช่น สีเฉพาะจุด ดูรูปที่ 2 ข้อความเสริม 3COLUMN_APP_LINK_ICON_URI
- URI สำหรับไอคอนป้ายแอปของลิงก์แอปสำหรับช่องนี้ สำหรับ ตัวอย่างไอคอนป้ายแอป ดูรูปที่ 2 ข้อความเสริม 2COLUMN_APP_LINK_INTENT_URI
- URI ของ Intent ของลิงก์แอปสำหรับช่องนี้ คุณสร้าง URI ได้ กำลังใช้toUri(int)
กับURI_INTENT_SCHEME
และ แปลง URI กลับไปเป็น Intent เดิมด้วยparseUri()
COLUMN_APP_LINK_POSTER_ART_URI
- URI สำหรับภาพโปสเตอร์ที่ใช้เป็นพื้นหลังของลิงก์แอป สำหรับช่องนี้ สำหรับภาพโปสเตอร์ตัวอย่าง ให้ดูรูปที่ 2 ข้อความเสริม 1COLUMN_APP_LINK_TEXT
- ข้อความลิงก์ที่สื่อความหมายของลิงก์แอปสำหรับช่องนี้ ตัวอย่างเช่น คำอธิบายลิงก์แอป ให้ดูข้อความในรูปที่ 2 ไฮไลต์ที่ 3
หากระบบไม่ได้ระบุข้อมูลลิงก์ของแอปไว้ในข้อมูลแชแนล สร้างลิงก์แอปเริ่มต้น ระบบจะเลือกรายละเอียดเริ่มต้นดังนี้
- สำหรับ URI ของ Intent
(
COLUMN_APP_LINK_INTENT_URI
), ระบบจะใช้ACTION_MAIN
กิจกรรมสำหรับหมวดหมู่CATEGORY_LEANBACK_LAUNCHER
ซึ่งโดยทั่วไปกำหนดไว้ในไฟล์ Manifest ของแอป หากไม่ได้กำหนดกิจกรรมนี้ไว้ ลิงก์แอปที่ใช้งานไม่ได้จะปรากฏขึ้น หาก เมื่อผู้ใช้คลิกแล้วจะไม่มีอะไรเกิดขึ้น - สำหรับข้อความอธิบาย
(
COLUMN_APP_LINK_TEXT
) ระบบ ใช้ "เปิด app-name" หากไม่ได้กำหนด URI จุดประสงค์ของลิงก์แอปที่ใช้งานได้ ระบบจะใช้ "ไม่มีลิงก์ที่พร้อมใช้งาน" - สำหรับสีเฉพาะจุด
(
COLUMN_APP_LINK_COLOR
), ระบบจะใช้สีเริ่มต้นของแอป - สำหรับภาพโปสเตอร์
(
COLUMN_APP_LINK_POSTER_ART_URI
), ระบบจะใช้แบนเนอร์หน้าจอหลักของแอป หากแอปไม่มี ระบบจะใช้รูปภาพแอปทีวีเริ่มต้น - สำหรับไอคอนป้าย
(
COLUMN_APP_LINK_ICON_URI
) ระบบจะใช้ป้ายที่แสดงชื่อแอป หากระบบใช้ แบนเนอร์แอปหรือรูปภาพแอปเริ่มต้นสำหรับภาพโปสเตอร์ ระบบจะไม่แสดงป้ายแอป
คุณระบุรายละเอียดลิงก์แอปสำหรับช่องใน
กิจกรรมการตั้งค่า คุณสามารถอัปเดตรายละเอียดลิงก์แอปเหล่านี้ได้ทุกเมื่อ ดังนั้น
หาก App Link ต้องตรงกับการเปลี่ยนแปลงช่อง ให้อัปเดตแอป
รายละเอียดลิงก์และการโทร
ContentResolver.update()
ตามต้องการ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการอัปเดต
โปรดดูอัปเดตข้อมูลช่อง