通过以下步骤,您可以将 Android 发送器应用从 Cast SDK v2 与 CCL 转换为 CAF。CCL 的所有功能都已在 CAF 中实现,因此迁移后,您不再需要使用 CCL。
Cast CAF Sender SDK 使用 CastContext 代表您管理 GoogleAPIClient。CastContext 可为您管理生命周期、错误和回调,从而大大简化 Cast 应用的开发工作。
简介
- 由于 CAF 发送者设计受到 Cast Companion 库的影响,因此从 CCL 到 CAF 发送者的迁移主要涉及类及其方法的一对一映射。
- 而 CAF 发件人仍会使用 Android SDK 管理器作为 Google Play 服务的一部分进行分发。
- 新添加到 CAF 发送者软件包 (
com.google.android.gms.cast.framework.*
) 并具有与 CCL 类似的功能,可负责遵守 Google Cast 设计核对清单。 - CAF Sender 提供符合 Cast 用户体验要求的 widget;这些 widget 与 CCL 提供的 widget 类似。
- CAF Sender 提供类似于 CCL 的异步回调,用于跟踪状态和获取数据。与 CCL 不同,CAF Sender 不提供各种接口方法的任何空操作实现。
在以下部分中,我们将主要重点介绍基于 CCL 的 VideoCastManager 的视频中心应用,但在很多情况下,同样的概念也适用于 DataCastManager。
依赖项
CCL 和 CAF 依赖于 AppCompat 支持库、MediaRouter v7 支持库和 Google Play 服务。不过,区别在于 CAF 依赖于 Google Play 服务 9.2.0 或更高版本中提供的新 Cast 框架。
在 build.gradle 文件中,移除 com.google.android.gms:play-services-cast
和 com.google.android.libraries.cast.companionlibrary:ccl
的依赖项,然后添加新的 Cast 框架:
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:mediarouter-v7:23.4.0'
compile 'com.google.android.gms:play-services-cast-framework:9.4.0'
}
您还可以移除 Google Play 服务元数据:
<meta‐data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
属于 CAF 的所有服务、activity 和资源都会自动与您的应用清单和资源合并。
CAF 支持的最低 Android SDK 版本为 9 (Gingerbread);CCL 的最低 Android SDK 版本为 10。
CCL 提供了一种便捷方法 BaseCastManager.checkGooglePlayServices(activity)
,用于验证设备上是否有兼容的 Google Play 服务版本。CAF 不会在 Cast SDK 中提供此信息。按照确保设备已安装 Google Play 服务 APK 中的说明确保用户设备上安装的是正确的 Google Play 服务 APK,因为更新可能不会立即覆盖所有用户。
您仍然需要为应用的主题使用 Theme.AppCompat 的变体。
初始化
对于 CCL,需要在应用实例的 onCreate()
方法中调用 VideoCastManager.initialize()
。此逻辑应该从 Application 类代码中移除。
在 CAF 中,投放框架还需要明确的初始化步骤。这涉及初始化 CastContext
单例,使用适当的 OptionsProvider
指定接收器应用 ID 和任何其他全局选项。CastContext
通过提供客户端与之互动的单例来与 CCL 的 VideoCastManager
发挥类似的作用。OptionsProvider
与 CCL 的 CastConfiguration
类似,可让您配置 Cast 框架功能。
如果您当前的 CCL CastConfiguration.Builder
如下所示:
VideoCastManager.initialize(
getApplicationContext(),
new CastConfiguration.Builder(context.getString(R.string.app_id))
.enableWifiReconnection()
.enableAutoReconnect()
.build());
然后,在 CAF 中,使用 CastOptions.Builder
的以下 CastOptionsProvider
类似:
public class CastOptionsProvider implements OptionsProvider {
@Override
public CastOptions getCastOptions(Context context) {
return new CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.build();
}
@Override
public List<SessionProvider> getAdditionalSessionProviders(
Context context) {
return null;
}
}
查看我们的示例应用,了解 OptionsProvider 的完整实现。
在 AndroidManifest.xml 文件的“application”元素中声明 OptionsProvider:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.refplayer.CastOptionsProvider"
/>
</application>
在每个 Activity
的 onCreate
方法(而不是 Application
实例)中延迟初始化 CastContext
:
private CastContext mCastContext;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video_browser);
setupActionBar();
mCastContext = CastContext.getSharedInstance(this);
}
如需访问 CastContext
单例,请使用以下命令:
mCastContext = CastContext.getSharedInstance(this);
设备发现
CCL 的 VideoCastManager
incrementUiCounter
和 decrementUiCounter
应该从 Activities
的 onResume
和 onPause
方法中移除。
在 CAF 中,当应用进入前台并转到后台时,框架会自动启动和停止发现过程。
“投放”按钮和“投放”对话框
与 CCL 一样,这些组件由 MediaRouter v7 支持库提供。
“投放”按钮仍由 MediaRouteButton
实现,并可以作为菜单项添加到菜单项中(使用 ActionBar
或 Toolbar
)。
菜单 xml 中 MediaRouteActionProvider
的声明与 CCL 相同:
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
app:showAsAction="always"/>
与 CCL 类似,替换每个 activity 的 onCreateOptionMenu() 方法,但不使用 CastManager.addMediaRouterButton,而是使用 CAF 的 CastButtonFactory 将 MediaRouteButton 连接到 Cast 框架:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.browse, menu);
CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
menu,
R.id.media_route_menu_item);
return true;
}
设备控制
与 CCL 类似,在 CAF 中,设备控制主要由框架处理。
发送者应用无需处理(也不应尝试)连接到设备并使用 GoogleApiClient
启动接收者应用。
发送者和接收者之间的互动现在以“会话”的形式表示。SessionManager
类会处理会话生命周期,并自动启动和停止会话以响应用户手势:用户在 Cast 对话框中选择 Cast 设备时,会话即开始;当用户在 Cast 对话框中点按“Stop Casting”按钮时,或发送者应用本身终止时,会话便会结束。
在 CCL 中,您必须扩展 VideoCastConsumerImpl
类才能跟踪投放会话状态:
private final VideoCastConsumer mCastConsumer = new VideoCastConsumerImpl() {
public void onApplicationConnected(ApplicationMetadata appMetadata,
String sessionId,
boolean wasLaunched) {}
public void onDisconnectionReason(int reason) {}
public void onDisconnected() {}
}
在 CAF 中,可以通过向 SessionManager
注册 SessionManagerListener
来向发送者应用通知会话生命周期事件。SessionManagerListener 回调为所有会话生命周期事件定义回调方法。
以下 SessionManagerListener
方法是从 CCL 的 VideoCastConsumer
接口映射的:
VideoCastConsumer.onApplicationConnected
->SessionManagerListener.onSessionStarted
VideoCastConsumer.onDisconnected
->SessionManagerListener.onSessionEnded
声明一个实现 SessionManagerListener
接口的类,并将 VideoCastConsumerImpl
逻辑移至匹配方法中:
private class CastSessionManagerListener implements SessionManagerListener<CastSession> {
public void onSessionEnded(CastSession session, int error) {}
public void onSessionStarted(CastSession session, String sessionId) {}
public void onSessionEnding(CastSession session) {}
...
}
CastSession
类表示与投放设备的会话。该类具有用于控制设备音量和静音状态的方法,CCL 在 BaseCastManager
中执行此操作。
如需添加使用方,而不是使用 CCL VideoCastManager
,请执行以下操作:
VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);
现在注册 SessionManagerListener
:
mCastSessionManager =
CastContext.getSharedInstance(this).getSessionManager();
mCastSessionManagerListener = new CastSessionManagerListener();
mCastSessionManager.addSessionManagerListener(mCastSessionManagerListener,
CastSession.class);
如需停止监听 CCL 中的事件,请执行以下操作:
VideoCastManager.getInstance().removeVideoCastConsumer(mCastConsumer);
现在,使用 SessionManager
停止监听会话事件:
mCastSessionManager.removeSessionManagerListener(mCastSessionManagerListener,
CastSession.class);
如需明确与投放设备断开连接,可使用 CCL:
VideoCastManager.disconnectDevice(boolean stopAppOnExit,
boolean clearPersistedConnectionData,
boolean setDefaultRoute)
对于 CAF,请使用 SessionManager
:
CastContext.getSharedInstance(this).getSessionManager()
.endCurrentSession(true);
为了确定发送者是否连接到接收者,CCL 提供 VideoCastManager.getInstance().isConnected()
,但在 CAF 中使用 SessionManager
:
public boolean isConnected() {
CastSession castSession = CastContext.getSharedInstance(mAppContext)
.getSessionManager()
.getCurrentCastSession();
return (castSession != null && castSession.isConnected());
}
在 CAF 中,音量/静音状态更改通知仍会通过 Cast.Listener
中的回调方法传递;这些监听器通过 CastSession
注册。所有剩余的设备状态通知均通过 CastStateListener
回调传递;这些监听器通过 CastSession
注册。确保在当关联的 fragment、activity 或应用进入后台时仍取消注册监听器。
重新连接逻辑
CAF 尝试重建因暂时 Wi-Fi 信号丢失或其他网络连接错误而丢失的网络连接。此操作现在在会话级别完成;会话可能会在连接中断时进入“挂起”状态,并在连接恢复时回到“已连接”状态。在此过程中,框架会负责重新连接到接收器应用以及重新连接任何投放渠道。
CAF 提供自己的重新连接服务,因此您可以从清单中移除 CCL ReconnectionService
:
<service android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>
此外,您无需在清单中为重新连接逻辑提供以下权限:
<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE"/>
CAF 重新连接服务默认处于启用状态,但可以使用 CastOptions
停用。
此外,CAF 还会默认启用自动恢复会话功能(可通过 CastOptions
停用)。如果在发送会话过程中发送应用被发送到后台或因其滑动而被终止,则框架会在发送器应用返回前台或重新启动时尝试恢复该会话;由 SessionManager
中的相应实例自动处理此问题。
自定义频道注册
CCL 提供两种向接收器创建自定义消息通道的方法:
CastConfiguration
可让您指定多个命名空间,然后 CCL 将为您创建渠道。DataCastManager
与 VideoCastManager 类似,但侧重于非媒体用例。
CAF 不支持这两种创建自定义渠道的方式 - 您必须按照针对发送者应用添加自定义渠道的步骤操作。
与 CCL 类似,对于媒体应用,无需显式注册媒体控制通道。
媒体控件
在 CAF 中,RemoteMediaClient
类等效于 VideoCastManager
媒体方法。RemoteMediaClient.Listener
等效于 VideoCastConsumer
方法。特别是,VideoCastConsumer
的 onRemoteMediaPlayerMetadataUpdated
和 onRemoteMediaPlayerStatusUpdated
方法分别映射到 RemoteMediaClient.Listener
的 onMetadataUpdated
和 onStatusUpdated
方法:
private class CastMediaClientListener implements RemoteMediaClient.Listener {
@Override
public void onMetadataUpdated() {
setMetadataFromRemote();
}
@Override
public void onStatusUpdated() {
updatePlaybackState();
}
@Override
public void onSendingRemoteMediaRequest() {
}
@Override
public void onQueueStatusUpdated() {
}
@Override
public void onPreloadStatusUpdated() {
}
}
无需显式初始化或注册 RemoteMediaClient
对象;如果连接的接收器应用连接到媒体命名空间,则框架会在会话启动时自动实例化该对象并注册底层媒体通道。
您可以将 RemoteMediaClient
作为 CastSession
对象的 getRemoteMediaClient
方法进行访问。
CastSession castSession = CastContext.getSharedInstance(mAppContext)
.getSessionManager()
.getCurrentCastSession();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClientListener = new CastMediaClientListener();
而非 CCL:
VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);
现在使用 CAF:
mRemoteMediaClient.addListener(mRemoteMediaClientListener);
可以向 RemoteMediaClient
注册任意数量的监听器,这样多个发送者组件就可以共享与会话关联的单个 RemoteMediaClient
实例。
CCL 的 VideoCastManager
提供了处理媒体播放的方法:
VideoCastManager manager = VideoCastManager.getInstance();
if (manager.isRemoteMediaLoaded()) {
manager.pause();
mCurrentPosition = (int) manager.getCurrentMediaPosition();
}
这些现在由 RemoteMediaClient 在 CAF 中实现:
if (mRemoteMediaClient.hasMediaSession()) {
mRemoteMediaClient.pause();
mCurrentPosition =
(int)mRemoteMediaClient.getApproximateStreamPosition();
}
在 CAF 中,在 RemoteMediaClient
上发出的所有媒体请求都会通过 PendingResult
回调返回 RemoteMediaClient.MediaChannelResult
,该回调可用于跟踪请求的进度和最终结果。
CCL 和 CAF 都使用 MediaInfo
和 MediaMetadata
类来表示媒体项和加载媒体。
如需在 CCL 中加载媒体,请使用 VideoCastManager
:
VideoCastManager.getInstance().loadMedia(media, autoPlay, mCurrentPosition, customData);
在 CAF 中,RemoteMediaClient
用于加载媒体:
mRemoteMediaClient.load(media, autoPlay, mCurrentPosition, customData);
为了在接收器上获取当前媒体会话的 Media
信息和状态,CCL 使用 VideoCastManager
:
MediaInfo mediaInfo = VideoCastManager.getInstance()
.getRemoteMediaInformation();
int status = VideoCastManager.getInstance().getPlaybackStatus();
int idleReason = VideoCastManager.getInstance().getIdleReason();
在 CAF 中,请使用 RemoteMediaClient
获取相同的信息:
MediaInfo mediaInfo = mRemoteMediaClient.getMediaInfo();
int status = mRemoteMediaClient.getPlayerState();
int idleReason = mRemoteMediaClient.getIdleReason();
介绍性叠加层
与 CCL 类似,CAF 提供了一个自定义视图 IntroductoryOverlay
,以便在首次向用户显示“投放”按钮时将其突出显示。
与其使用 CCL 的 VideoCastConsumer
onCastAvailabilityChanged
方法确定何时显示叠加层,不如声明 CastStateListener
,以确定 MediaRouter
在本地网络上发现 Cast 设备后,何时会显示“投放”按钮:
private IntroductoryOverlay mIntroductoryOverlay;
private MenuItem mMediaRouteMenuItem;
protected void onCreate(Bundle savedInstanceState) {
...
mCastStateListener = new CastStateListener() {
@Override
public void onCastStateChanged(int newState) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay();
}
}
};
mCastContext = CastContext.getSharedInstance(this);
mCastContext.registerLifecycleCallbacksBeforeIceCreamSandwich(this,
savedInstanceState);
}
protected void onResume() {
mCastContext.addCastStateListener(mCastStateListener);
...
}
protected void onPause() {
mCastContext.removeCastStateListener(mCastStateListener);
...
}
跟踪 MediaRouteMenuItem
实例:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.browse, menu);
mMediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(
getApplicationContext(), menu,
R.id.media_route_menu_item);
showIntroductoryOverlay();
return true;
}
检查 MediaRouteButton
是否可见,以便可以显示介绍性叠加层:
private void showIntroductoryOverlay() {
if (mIntroductoryOverlay != null) {
mIntroductoryOverlay.remove();
}
if ((mMediaRouteMenuItem != null) && mMediaRouteMenuItem.isVisible()) {
new Handler().post(new Runnable() {
@Override
public void run() {
mIntroductoryOverlay = new IntroductoryOverlay.Builder(
VideoBrowserActivity.this, mMediaRouteMenuItem)
.setTitleText(getString(R.string.introducing_cast))
.setOverlayColor(R.color.primary)
.setSingleTime()
.setOnOverlayDismissedListener(
new IntroductoryOverlay
.OnOverlayDismissedListener() {
@Override
public void onOverlayDismissed() {
mIntroductoryOverlay = null;
}
})
.build();
mIntroductoryOverlay.show();
}
});
}
}
查看我们的示例应用,获取用于显示介绍性叠加层的完整工作代码。
如需自定义介绍性叠加层的样式,请按照自定义简介叠加层中的步骤操作。
迷你控制器
对于要在其中显示迷你控制器的 activity,请在应用布局文件中使用 CAF 的 MiniControllerFragment
,而不是 CCL 的 MiniController
:
<fragment
android:id="@+id/cast_mini_controller"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:castShowImageThumbnail="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
CAF 不支持 CCL 的 MiniController
支持的手动配置,也不支持 Autoplay
功能。
如需自定义迷你控制器的样式和按钮,请按照自定义迷你控制器中的步骤操作。
通知和锁定屏幕
与 CCL 的 VideoCastNotificationService
类似,CAF 提供了一个 MediaNotificationService
,用于管理在投放时媒体通知的显示。
您需要从清单中移除以下内容:
VideoIntentReceiver
VideoCastNotificationService
CCL 支持通过 CastConfiguration.Builder
提供自定义通知服务;CAF 不支持该服务。
考虑使用 CCL 进行以下 CastManager
初始化:
VideoCastManager.initialize(
getApplicationContext(),
new CastConfiguration.Builder(
context.getString(R.string.app_id))
.addNotificationAction(
CastConfiguration.NOTIFICATION_ACTION_PLAY_PAUSE,true)
.addNotificationAction(
CastConfiguration.NOTIFICATION_ACTION_DISCONNECT,true)
.build());
对于 CAF 中的等效配置,SDK 提供了一个 NotificationsOptions.Builder
,以帮助您将通知和锁定屏幕的媒体控件内置到发送者应用中。在初始化 CastContext
时,可以使用 CastOptions
启用通知和锁定屏幕控件。
public CastOptions getCastOptions(Context context) {
NotificationOptions notificationOptions =
new NotificationOptions.Builder()
.setActions(Arrays.asList(
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{0, 1})
.build();
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build();
return new CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build();
}
在 CAF 中,通知和锁定屏幕控件始终处于启用状态。另请注意,系统默认提供播放/暂停和停止投射按钮。CAF 将自动跟踪 activity 的可见性,以确定何时显示媒体通知(Gingerbread 除外)。(有关 Gingerbread,请参阅之前的说明,了解如何使用 registerLifecycleCallbacksBeforeIceCreamSandwich()
;CCL 的 VideoCastManager
incrementUiCounter
和 decrementUiCounter
调用应该被移除。)
如需自定义通知中显示的按钮,请按照将媒体控件添加到通知和锁定屏幕中的步骤操作。
展开的控制器
CCL 提供 VideoCastControllerActivity
和 VideoCastControllerFragment
,以便在投放媒体时显示展开的控制器。
您可以在清单中移除 VideoCastControllerActivity
声明。
在 CAF 中,您必须扩展 ExpandedControllerActivity 并添加“投放”按钮。
如需自定义展开的控制器中显示的样式和按钮,请按照自定义展开的控制器一文中的步骤操作。
音频焦点
与 CCL 一样,音频焦点会自动管理。
音量控制
对于 Gingerbread,需要像 CCL 一样使用 dispatchKeyEvent
。在 ICS 及更高版本中,CCL 和 CAF 音量控制会自动处理。
通过 CAF,您可以在应用 activity 内使用手机上的硬音量按钮控制投射音量,并在支持的版本上进行投放时显示视觉音量条。即使应用不在前方、处于锁定状态或屏幕处于关闭状态时,CAF 也可通过硬音量来处理音量变化。
字幕
在 Android KitKat 及更高版本中,可以通过“设置”>“无障碍”下的“字幕设置”自定义字幕。不过,较低版本的 Android 不支持此功能。CCL 通过为早期版本提供自定义设置并委托给 KitKat 及更高版本上的系统设置来处理此问题。
CAF 不提供更改字幕偏好设置的自定义设置。您应在清单和偏好设置 XML 中移除 CaptionsPreferenceActivity
引用。
因为不再需要更改字幕轨道,所以 CCL 的 TracksChooserDialog
由扩展控制器界面处理。
CAF 中的字幕 API 与 v2 类似。
调试日志记录
CAF 不提供调试日志记录设置。
其他
CAF 不支持以下 CCL 功能:
- 在播放前通过提供
MediaAuthService
获取授权 - 可配置的界面消息
示例应用
查看用于将 Universal Music Player for Android (uamp) 示例应用从 CCL 迁移到 CAF 的差异。
我们还提供使用 CAF 的 Codelab 教程和示例应用。