您可通过以下步骤将 Android 发送器应用从 包含 CCL 的 Cast SDK v2 到 CAF。CCL 的所有功能 因此一旦完成迁移,您无需再使用 CCL。
Cast CAF 发送器 SDK 使用 CastContext 代表您管理 GoogleAPIClient。 CastContext 可为您管理生命周期、错误和回调,这大大 可简化 Cast 应用的开发流程。
简介
- 由于 CAF 发送器设计受 Cast Companion Library 的影响, 从 CCL 到 CAF 的迁移主要涉及 类及其方法。
- CAF 发送器仍作为 Google Play 服务的一部分进行分发 Android SDK 管理器。
- 以下新软件包 (
com.google.android.gms.cast.framework.*
) 添加到 CAF Sender 中,其功能与 CCL 类似, 的责任 Google Cast 设计核对清单。 - CAF 发送器提供符合 Cast 用户体验要求的微件; 这些微件与 CCL 提供的微件类似。
- CAF 发送器提供与 CCL 类似的异步回调, 并获取数据与 CCL 不同,CAF 发送器不提供任何空操作 各种接口方法的实现。
在后续部分中,我们将主要介绍 但很多情况下 概念也适用于 DataCastManager。
依赖项
CCL 和 CAF 与 AppCompat 支持库具有相同的依赖项, MediaRouter v7 支持库和 Google Play 服务。不过,区别在于 那就是 CAF 依赖于 Google Play 中提供的新 Cast 框架 服务 9.2.0 或更高版本。
在 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 的服务、活动和资源都会自动 与应用的清单和资源合并。
CAF 支持的最低 Android SDK 版本是 9 (Gingerbread);CCL 的最低 Android SDK 版本为 10。
CCL 提供了一种便捷方法
BaseCastManager.checkGooglePlayServices(activity)
,用于验证兼容的
设备上的 Google Play 服务版本。CAF 不
将其作为 Cast SDK 的一部分提供。按照流程操作
确保设备具有 Google Play 服务 APK
以确保在用户的 Google Play 中
因为更新可能不会立即面向所有用户发布。
您仍然需要对应用的 主题。
初始化
对于 CCL,VideoCastManager.initialize()
需要在
Application 实例的 onCreate()
方法。此逻辑应为
从应用类代码中移除
在 CAF 中,Cast 内容还需要明确的初始化步骤
框架。这涉及到初始化 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 的完整实现。
在“application”内声明 OptionsProvider的 AndroidManifest.xml 文件:
<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
方法中延迟初始化 CastContext
(而不是 Application
实例):
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
实现,可以添加
添加到您的 activity(使用 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() 方法,但 请使用 CAF 的 CastButtonFactory,而不要使用 CastManager.addMediaRouterButton 将 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 设备,并在用户点按时结束
“停止投放”按钮,或在发送器应用本身
终止。
在 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
类表示与 Cast 设备的会话。该类包含
控制设备音量和静音状态的方法
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
停用)。如果发件人应用
发送到后台或被终止(通过滑动离开或由于崩溃)
当 Cast 会话正在进行时,框架会尝试恢复该会话
当发送者应用返回前台或重新启动时触发会话;
由 SessionManager
自动处理,后者将发出
对任何已注册的 SessionManagerListener
实例进行适当的回调。
自定义渠道注册
CCL 提供了以下两种方法,供您创建发送给接收器的自定义消息通道:
CastConfiguration
允许您指定多个命名空间,CCL 将 然后为您创建频道DataCastManager
与 VideoCastManager 类似,但侧重于非媒体 用例。
CAF 不支持这两种创建自定义渠道的方式, 而是必须按照流程 添加自定义渠道 。
与 CCL 类似,对于媒体应用,无需明确 注册媒体控制通道。
媒体控件
在 CAF 中,RemoteMediaClient
类等同于 VideoCastManager
media 方法。RemoteMediaClient.Listener
相当于
VideoCastConsumer
方法。具体而言,
onRemoteMediaPlayerMetadataUpdated
和onRemoteMediaPlayerStatusUpdated
VideoCastConsumer
的方法映射到 onMetadataUpdated
,
onStatusUpdated
方法RemoteMediaClient.Listener
:
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
object;框架会自动将该对象实例化,并注册
会话启动时底层媒体频道(如果接收方应用
支持媒体命名空间。
RemoteMediaClient
可以作为 getRemoteMediaClient
方法访问
CastSession
对象。
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
提供的本地网络:
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();
}
});
}
}
请查看我们的 示例应用 了解用于显示介绍性叠加层的完整工作代码。
如需自定义介绍性叠加层的样式,请遵循以下流程: 自定义入门叠加层。
迷你控制器
在MiniController
MiniControllerFragment
您希望在其中显示迷你头像的 activity 的应用布局文件
控制器:
<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
;。
请参考以下使用 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
可帮助您构建媒体控件,
通知和锁定屏幕上发送方应用。通知和锁定
可以使用 CastOptions
在初始化
CastContext
。
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
会自动跟踪活动的可见性
显示媒体通知的时间,但 Gingerbread 除外。
(对于 Gingerbread,请参阅前面的说明
使用 registerLifecycleCallbacksBeforeIceCreamSandwich()
;CCL
VideoCastManager
incrementUiCounter
和 decrementUiCounter
调用
应删除。)
要自定义通知中显示的按钮,请按照 程序 向通知和锁定屏幕添加媒体控件。
展开的控制器
CCL 提供 VideoCastControllerActivity
和 VideoCastControllerFragment
以在投放媒体时显示展开的控制器。
您可以移除清单中的 VideoCastControllerActivity
声明。
在 CAF 中, 扩展 ExpandControllerActivity 并添加“投放”按钮。
自定义展开前显示的样式和按钮 请按照程序 自定义展开后的控制器。
音频焦点
与 CCL 一样,系统会自动管理音频焦点。
音量控制
对于 Gingerbread,与 CCL 一样,dispatchKeyEvent
是必需的。在 ICS 及更高版本中
CCL 和 CAF 音量控制的音量控件都会自动处理。
借助 CAF,您可以通过手机上的硬音量按钮控制投放音量 并在以下情况下显示可视音量条: 在支持的版本上进行投放。CAF 还通过 使用硬音量(即使您的应用不在前置屏幕、处于锁定状态或屏幕关闭时) 关闭。
字幕
在 Android KitKat 及更高版本中,可以通过“字幕”功能自定义字幕 设置,可在设置 > 下找到无障碍功能。Android 早期版本 则不具备此功能CCL 通过提供自定义 早期版本的设置以及委托给 KitKat 上的系统设置 及更高级别。
CAF 不提供用于更改字幕偏好设置的自定义设置。您
应移除清单中的 CaptionsPreferenceActivity
引用
和您的偏好设置 XML。
由于更改了TracksChooserDialog
字幕轨道由展开的控制器界面处理。
字幕 API 类似于 v2。
调试日志记录
CAF 不提供调试日志记录设置。
其他
CAF 不支持以下 CCL 功能:
- 通过提供
MediaAuthService
在播放之前获取授权 - 可配置的界面消息
示例应用
查看将 Android 通用音乐播放器 (uamp) 示例应用从 CCL 迁移到 CAF 的差异。
我们还提供了使用 CAF 的 Codelab 教程和示例应用。