简体   繁体   English

设备处于睡眠状态时如何保持ChromeCast会话的活动状态?

[英]How to keep a ChromeCast session alive when the device is asleep?

I have an app that can stream local media content to a Chromecast receiver. 我有一个可以将本地媒体内容流式传输到Chromecast接收器的应用。 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). 这主要工作的,所不同的是当设备处于睡眠状态,而不是在外部电源约5分钟后,会话将死/断开(从当屏幕变成空白测量)。

I've already had a look at this question here: 我已经在这里查看了这个问题:

How do I keep a ChromeCast route alive when my app is in the background on battery power? 当我的应用程序处于电池供电后台时,如何使ChromeCast路由保持活动状态?

...and implemented both of the suggested answers. ...并实施了两个建议的答案。

Specifically, in my app's manifest, I have: 具体来说,在我的应用清单中,我有:

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

And then in the embedded HTTP server that I use to stream media content to the Chromecast receiver, I'm doing: 然后,在用于将媒体内容流式传输到Chromecast接收器的嵌入式HTTP服务器中,我正在做:

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();

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. 该代码在服务器线程启动时执行(在选择Chromecast路由时发生),并且直到服务器停止才释放锁。 That happens in my MediaRouter.Callback implementation, which (incorporating the accepted answer from the question above) looks like this: 这发生在我的MediaRouter.Callback实现中,该实现(包含上面问题的可接受答案)如下所示:

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;
    }
};

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? 当设备处于睡眠状态且未连接外部电源时,为了使Chromecast会话保持活动状态,还需要做些什么?

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

Edit 编辑

Here's the stacktrace that's reported when the session dies (from the embedded HTTP server): 这是会话终止时(从嵌入式HTTP服务器)报告的堆栈跟踪:

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)

And I've also noticed that I can capture this event in my RemoteMediaPlayer.OnStatusUpdatedListener implementation, like: 而且我还注意到,我可以在RemoteMediaPlayer.OnStatusUpdatedListener实现中捕获此事件,例如:

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 had the right of it in his comment. CommonsWare在他的评论中对此保留权利。 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 . 我正在运行Android 7.1.2的设备上进行测试,并且打Do模式导致网络闲置约5分钟后且无论我的应用持有的唤醒锁为何, 网络 均掉线

The fix was to add the following permission in the manifest: 解决方法是在清单中添加以下权限:

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

And then to update my onRouteSelected() callback implementation to request that Doze mode be disabled if/when a ChromeCast route is selected: 然后更新我的onRouteSelected()回调实现,以请求/如果选择了ChromeCast路由,则请禁用“打ze”模式:

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);
    }

}

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会话仍保持活动状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM