设备处于睡眠状态时如何保持ChromeCast会话的活动状态? [英] How to keep a ChromeCast session alive when the device is asleep?

查看:90
本文介绍了设备处于睡眠状态时如何保持ChromeCast会话的活动状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以将本地媒体内容流式传输到Chromecast接收器的应用.除在设备 处于睡眠状态且未使用外部电源供电时,会话将在大约5分钟(从屏幕变黑时开始算起)后终止/断开连接.

I have an app that can stream local media content to a Chromecast receiver. This mostly works, except that when the device is asleep and not on external power the session will die/disconnect after about 5 minutes (measured from when the screen goes blank).

我已经在这里查看了这个问题:

I've already had a look at this question here:

...并实施了两个建议的答案.

...and implemented both of the suggested answers.

具体来说,在我的应用清单中,我有:

Specifically, in my app's manifest, I have:

<uses-permission android:name="android.permission.WAKE_LOCK" />

然后在用于将媒体内容流式传输到Chromecast接收器的嵌入式HTTP服务器中,我正在做

And then in the embedded HTTP server that I use to stream media content to the Chromecast receiver, I'm doing:

PowerManager powerManager = (PowerManager)Environment.getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cast-server-cpu");
wakeLock.acquire();

WifiManager wifiManager = (WifiManager)Environment.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "cast-server-net");
wifiLock.acquire();

该代码在服务器线程启动时执行(在选择Chromecast路由时发生),并且直到服务器停止才释放锁.这种情况发生在我的MediaRouter.Callback实现中,该实现(包含上面问题的可接受答案)如下所示:

That code executes when the server thread starts (occurs when the Chromecast route is selected), and the locks aren't released until the server is stopped. That happens in my MediaRouter.Callback implementation, which (incorporating the accepted answer from the question above) looks like this:

this.mediaRouterCallback = new MediaRouter.Callback() {
    private MediaRouter.RouteInfo autoconnectRoute = null;

    @Override
    public void onRouteSelected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
        if (autoconnectRoute == null) {
            if (isPlaying()) {
                stop();
                playButton.setImageResource(R.drawable.icon_play);
            }

            castDevice = CastDevice.getFromBundle(routeInfo.getExtras());
            if (castServer != null) {
                castServer.stop();
            }

            //start the Chromecast file server
            castServer = new CastHttpFileServer();
            new Thread(castServer).start();
        }

        initCastClientListener();
        initRemoteMediaPlayer();
        launchReceiver();
    }

    @Override
    public void onRouteUnselected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
        if (! doesRouterContainRoute(mediaRouter, routeInfo)) {
            //XXX:  do not disconnect in this case (the unselect was not initiated by a user action); see https://stackoverflow.com/questions/18967879/how-do-i-keep-a-chromecast-route-alive-when-my-app-is-in-the-background-on-batte
            autoconnectRoute = routeInfo;
            return;
        }

        //user disconnected, teardown and stop playback
        if (isPlaying()) {
            stop();
            playButton.setImageResource(R.drawable.icon_play);
        }
        if (castServer != null) {
            castServer.stop();
            castServer = null;
        }

        teardown();
        castDevice = null;
    }

    @Override
    public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
        super.onRouteAdded(router, route);
        if (autoconnectRoute != null && route.getId().equals(autoconnectRoute.getId())) {
            this.onRouteSelected(router, route);
        }
    }

    private boolean doesRouterContainRoute(MediaRouter router, MediaRouter.RouteInfo route) {
        if (router == null || route == null) {
            return false;
        }

        for (MediaRouter.RouteInfo info : router.getRoutes()) {
            if (info.getId().equals(route.getId())) {
                return true;
            }
        }

        return false;
    }
};

在设备处于睡眠状态且未连接外部电源时,需要执行哪些操作才能使Chromecast会话保持活动状态?

What more needs to be done in order to keep the Chromecast session alive when the device is asleep and not connected to external power?

此外,考虑到该问题仅在设备连接到USB时才会重现,因此在这种情况下进行调试的有效方法是什么?

Also, considering that this issue only reproduces when the device is not connected to USB, what's an effective way to debug in this case?

修改

以下是会话终止时报告的堆栈跟踪(来自嵌入式HTTP服务器):

Here's the stacktrace that's reported when the session dies (from the embedded HTTP server):

java.net.SocketException: Software caused connection abort
 at java.net.SocketOutputStream.socketWrite0(Native Method)
 at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:112)
 at java.net.SocketOutputStream.write(SocketOutputStream.java:157)
 at com.myapp.CastHttpFileServer.writeResponse(CastHttpFileServer.java:124)
 at com.myapp.CastHttpFileServer.run(CastHttpFileServer.java:180)

我还注意到,我可以在我的RemoteMediaPlayer.OnStatusUpdatedListener实现中捕获此事件,例如:

And I've also noticed that I can capture this event in my RemoteMediaPlayer.OnStatusUpdatedListener implementation, like:

if (lastStatus == MediaStatus.PLAYER_STATE_PLAYING && mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_BUFFERING) {
    //buffer underrun/receiver went away because the wifi died; how do we fix this?
}

推荐答案

CommonsWare在其评论中拥有它的权利.我正在运行Android 7.1.2的设备上进行测试,并且打ze模式大约在闲置5分钟后会导致网络断开,并且无论我的应用程序持有的唤醒锁是什么.

CommonsWare had the right of it in his comment. I was testing on a device running Android 7.1.2, and Doze mode was causing the network to drop after about 5 minutes of inactivity and irrespective of the wake lock(s) my app was holding.

解决方法是在清单中添加以下权限:

The fix was to add the following permission in the manifest:

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

和然后更新我回调实现到请求打盹模式如果/禁用当选择CHROMECAST路线:

And then to update my onRouteSelected() callback implementation to request that Doze mode be disabled if/when a ChromeCast route is selected:

if (Build.VERSION.SDK_INT >= 23) {
    String packageName = context.getPackageName();
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if (! pm.isIgnoringBatteryOptimizations(packageName)) {
        //reguest that Doze mode be disabled
        Intent intent = new Intent();
        intent.setAction(
            Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
        intent.setData(Uri.parse("package:" + packageName));

        context.startActivity(intent);
    }

}

只要用户在提示时选择是",就将遵守该应用的唤醒锁,即使设备未充电,ChromeCast会话仍保持活动状态.

As long as the user selects 'Yes' when prompted, the app's wake locks are respected and the ChromeCast session stays alive even when the device is not charging.

这篇关于设备处于睡眠状态时如何保持ChromeCast会话的活动状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆