TV 입력은 최소한 다음과 같은 전자 프로그램 가이드 (EPG) 데이터를 제공해야 합니다. 하나의 채널을 의미합니다. 또한 해당 정책을 정기적으로 업데이트 크기 및 처리 스레드를 고려하여 데이터 처리 살펴보겠습니다 채널의 앱 링크를 제공할 수도 있습니다. 사용자를 관련 콘텐츠 및 활동으로 안내하는 이 과정에서는 Google 애널리틱스 360에서 채널 및 프로그램 데이터를 만들고 시스템 데이터베이스를 개선해야 합니다
TV 입력 서비스 샘플 앱을 다운로드합니다.
권한 얻기
TV 입력에서 EPG 데이터를 사용하려면 쓰기 권한을 Android 매니페스트 파일에 다음과 같이 작성합니다.
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
데이터베이스에 채널 등록
Android TV 시스템 데이터베이스는 TV 입력에 사용되는 채널 데이터의 기록을 보관합니다. 설정에서
데이터를 표시하려면 각 채널에 대해 채널 데이터를
TvContract.Channels
클래스:
COLUMN_DISPLAY_NAME
- 채널COLUMN_DISPLAY_NUMBER
- 표시되는 채널 숫자COLUMN_INPUT_ID
- TV 입력 서비스 IDCOLUMN_SERVICE_TYPE
- 채널의 서비스 유형COLUMN_TYPE
- 채널의 방송 표준 유형COLUMN_VIDEO_FORMAT
- 기본 동영상 형식 채널
TV 입력 프레임워크는 전통적인 방송과 TV를 모두 처리할 만큼 일반적이지만 구분 없이 오버더톱 (OTT) 콘텐츠를 구별하는 것이 아니라면 다음과 같은 열을 정의하는 것이 좋습니다. 기존 방송 채널을 더 잘 식별할 수 있습니다.
COLUMN_ORIGINAL_NETWORK_ID
- 텔레비전 네트워크 IDCOLUMN_SERVICE_ID
- 서비스 IDCOLUMN_TRANSPORT_STREAM_ID
- 전송 스트림 신분증
채널의 앱 링크 세부정보를 제공하려면 다음 요건을 충족해야 합니다. 일부 추가 필드를 업데이트합니다. 앱 링크 필드에 대한 자세한 내용은 다음을 참고하세요. 앱 링크 정보를 추가합니다.
인터넷 스트리밍 기반 TV 입력의 경우 위의 항목에 원하는 값을 적절하게 할당하여 각 채널을 고유하게 식별할 수 있습니다
설정에서 채널 메타데이터를 백엔드 서버로부터 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)
자바
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
는
백엔드 서버입니다
채널 및 프로그램 정보 표시
시스템 TV 앱은 사용자가 채널을 넘길 때 채널 및 프로그램 정보를 표시합니다. Cloud CDN을 사용 설정합니다 채널 및 프로그램 정보가 시스템 TV 앱의 채널 및 프로그램 정보 발표자는 아래의 가이드라인을 따르세요.
- 채널 번호 (
COLUMN_DISPLAY_NUMBER
) - 아이콘
(
android:icon
, TV 입력의 매니페스트) - 포인트 제도 설명 (
COLUMN_SHORT_DESCRIPTION
) - 프로그램 제목(
COLUMN_TITLE
) - 채널 로고 (
TvContract.Channels.Logo
) <ph type="x-smartling-placeholder">- </ph>
- 주변 텍스트와 일치하도록 #EEEEEE 색상 사용
- 패딩 포함 안 함
- 포스터 아트 (
COLUMN_POSTER_ART_URI
) <ph type="x-smartling-placeholder">- </ph>
- 가로세로 비율(16:9~4:3)
![](https://developer.android.com/static/images/tv/channel-info.png?authuser=0000&hl=ko)
그림 1. 시스템 TV 앱 채널 및 프로그램 정보 프레젠터
시스템 TV 앱은 프로그램 가이드를 통해 포스터 아트, Cloud CDN을 사용 설정합니다
![](https://developer.android.com/static/images/tv/prog-guide.png?authuser=0000&hl=ko)
그림 2. 시스템 TV 앱 프로그램 가이드
채널 데이터 업데이트
기존 채널 데이터를 업데이트하는 경우
update()
메서드를 사용하여 데이터를 삭제할 수 있습니다. 데이터의 현재 버전을 확인할 수 있습니다.
Channels.COLUMN_VERSION_NUMBER
사용
및 Programs.COLUMN_VERSION_NUMBER
변경할 수 있습니다.
참고: ContentProvider
에 채널 데이터 추가
시간이 걸릴 수 있습니다 현재 프로그램 추가 (현재 시간으로부터 2시간 이내의 프로그램)
나머지를 업데이트하도록 EpgSyncJobService
를 구성할 때만
백그라운드에 채널 데이터가 표시됩니다 자세한 내용은
<ph type="x-smartling-placeholder"></ph>
Android TV 실시간 TV 샘플 앱을 참조하세요.
채널 데이터 일괄 로드
많은 양의 채널 데이터가 포함된 시스템 데이터베이스를 업데이트할 때는 ContentResolver
를 사용합니다.
applyBatch()
또는
<ph type="x-smartling-placeholder">bulkInsert()
</ph>
메서드를 사용하여 축소하도록 요청합니다. 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() } }
자바
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
사용은
업데이트를 비동기적으로 수행하는 방법입니다. 예를 들어 백엔드 서버에서 채널 정보를 로드할 때
다음과 같이 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() } } } } }
자바
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시).
데이터 업데이트 작업을 UI 스레드에서 분리하는 다른 기법에는
HandlerThread
클래스를 설정하거나 Looper
를 사용하여 직접 구현할 수도 있습니다.
및 Handler
클래스 를 참조하세요.
프로세스 및 스레드를 참고하세요.
앱 링크 정보 추가
채널에서 앱 링크를 사용하여 사용자가 쉽게 관련 앱을 실행하도록 할 수 있음 시청자의 활동입니다. 채널 앱에서 앱 링크가 표시되는 활동을 시작하여 사용자 참여를 추가 콘텐츠를 제공할 수 있습니다. 예를 들어 앱 링크를 사용하여 다음 작업을 수행합니다.
- 사용자가 관련 콘텐츠를 찾고 구매하도록 안내합니다.
- 현재 재생 중인 콘텐츠에 대한 추가 정보를 제공합니다.
- 에피소드 형태의 콘텐츠를 시청하는 동안 다음 에피소드를 이어서 시청할 수 있습니다. Google Cloud 시리즈를 확인해 보세요
- 사용자가 콘텐츠와 상호작용하도록 허용(예: 평점 또는 리뷰) 콘텐츠를 재생하도록 끊김 없이 재생할 수 있습니다
사용자가 선택을 눌러 채널 콘텐츠를 시청하는 동안 TV 메뉴가 표시됩니다.
![](https://developer.android.com/static/images/training/tv/tif/app-link.png?authuser=0000&hl=ko)
그림 1. 예시 앱 링크 채널 콘텐츠가 표시되는 동안 채널 행에 표시됩니다.
사용자가 앱 링크를 선택하면 시스템은 채널 앱이 지정한 인텐트 URI입니다. 채널 콘텐츠가 계속 재생됩니다. 앱 링크 활동이 활성 상태인 경우 사용자가 채널로 돌아갈 수 있습니다. 뒤로를 눌러 콘텐츠를 다시 볼 수 있습니다.
앱 링크 채널 데이터 제공
Android TV는 각 채널의 앱 링크를 자동으로 생성하므로
채널 데이터의 정보를 사용합니다. 앱 링크 정보를 제공하려면 다음 단계를 따르세요.
다음 세부정보를 지정합니다.
TvContract.Channels
필드:
COLUMN_APP_LINK_COLOR
- 이 채널의 앱 링크의 강조 색상입니다. 강조 색상의 예로 그림 2, 콜아웃 3을 참고하세요.COLUMN_APP_LINK_ICON_URI
~ 이 채널의 앱 링크에 있는 앱 배지 아이콘의 URI입니다. 예시 앱 배지 아이콘, 그림 2, 콜아웃 2 참고COLUMN_APP_LINK_INTENT_URI
~ 이 채널에 대한 앱 링크의 인텐트 URI입니다. URI를 만들어 다음 코드로toUri(int)
사용 중URI_INTENT_SCHEME
및 URI를 다시 원래 인텐트로 변환하고parseUri()
입니다.COLUMN_APP_LINK_POSTER_ART_URI
- 앱 링크의 배경으로 사용되는 포스터 아트의 URI 이 채널에 대해 자세히 알아보세요. 포스터 이미지의 예는 그림 2, 콜아웃 1을 참고하세요.COLUMN_APP_LINK_TEXT
~ 채널의 앱 링크를 설명하는 링크 텍스트입니다. 예를 들면 다음과 같습니다. 앱 링크 설명의 경우 그림 2, 콜아웃 3의 텍스트를 참고하세요.
![](https://developer.android.com/static/images/training/tv/tif/app-link-diagram.png?authuser=0000&hl=ko)
그림 2. 앱 링크 세부정보
채널 데이터가 앱 링크 정보를 지정하지 않으면 시스템은 기본 앱 링크를 생성합니다. 시스템에서는 다음과 같이 기본 세부정보를 선택합니다.
- 인텐트 URI:
(
COLUMN_APP_LINK_INTENT_URI
), 시스템은ACTION_MAIN
를 사용합니다. 일반적으로 앱 매니페스트에 정의된CATEGORY_LEANBACK_LAUNCHER
카테고리의 활동입니다. 이 활동이 정의되어 있지 않으면 작동하지 않는 앱 링크가 나타납니다. 아무 일도 일어나지 않습니다. - 설명 텍스트의 경우
(
COLUMN_APP_LINK_TEXT
) - 시스템 'app-name 열기'를 사용합니다. 실행 가능한 앱 링크 인텐트 URI가 정의되지 않은 경우 '링크 없음'이라는 메시지가 표시됩니다. - 강조 색상
(
COLUMN_APP_LINK_COLOR
), 기본 앱 색상을 사용합니다. - 포스터 이미지
(
COLUMN_APP_LINK_POSTER_ART_URI
), 시스템에서 앱의 홈 화면 배너를 사용합니다. 앱에서 배너가 있으면 시스템에서는 기본 TV 앱 이미지를 사용합니다. - 배지 아이콘
(
COLUMN_APP_LINK_ICON_URI
), 시스템에서 앱 이름을 표시하는 배지를 사용합니다. 시스템에서 앱 배너 또는 포스터 이미지의 기본 앱 이미지만 있다면 앱 배지가 표시되지 않습니다.
채널의 앱 링크 세부정보는 앱의
설정할 수 있습니다. 이러한 앱 링크 세부정보는 언제든지 업데이트할 수 있으므로
앱 링크가 채널 변경사항과 일치해야 하는 경우 앱을 업데이트하세요.
링크 세부정보 및 통화
ContentResolver.update()
를 사용합니다. 자세한 내용은
자세한 내용은 채널 데이터 업데이트를 참조하세요.