将 CCL 发送器应用迁移到 Cast 应用框架 (CAF)

您可通过以下步骤将 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-castcom.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 类似 通过提供客户端与之交互的单例来VideoCastManagerOptionsProvider 类似于 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>

在每个 ActivityonCreate 方法中延迟初始化 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 incrementUiCounterdecrementUiCounter 应 从 ActivitiesonResumeonPause 方法中移除。

在 CAF 中,发现过程由 框架(当应用进入前台并进入后台时), 。

“投放”按钮和“投放”对话框

与 CCL 一样,这些组件由 MediaRouter v7 支持提供 库。

“投放”按钮仍由 MediaRouteButton 实现,可以添加 添加到您的 activity(使用 ActionBarToolbar),作为菜单项 。

菜单 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 方法。具体而言, onRemoteMediaPlayerMetadataUpdatedonRemoteMediaPlayerStatusUpdated VideoCastConsumer 的方法映射到 onMetadataUpdatedonStatusUpdated 方法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 均使用 MediaInfoMediaMetadata 类来表示 以及加载媒体。

如需在 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();
            }
        });
    }
}

请查看我们的 示例应用 了解用于显示介绍性叠加层的完整工作代码。

如需自定义介绍性叠加层的样式,请遵循以下流程: 自定义入门叠加层

迷你控制器

MiniControllerMiniControllerFragment 您希望在其中显示迷你头像的 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 incrementUiCounterdecrementUiCounter 调用 应删除。)

要自定义通知中显示的按钮,请按照 程序 向通知和锁定屏幕添加媒体控件

展开的控制器

CCL 提供 VideoCastControllerActivityVideoCastControllerFragment 以在投放媒体时显示展开的控制器。

您可以移除清单中的 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 教程示例应用