问题
Using the iOS Sender API Framework, when my application goes in the background, the SDK tears down all connections and I cannot launch any more media until the app is put back in the foreground. My app plays audio and is allowed to run and stream in the background. Is there an option to tell Googlecast framework to keep the sockets opened?
Here's the log on backgrounding:
INFO: Will resign active
INFO: Now in background
FINE: -[GCKCastSocket disconnect] disconnect
FINE: -[GCKCastSocket doTeardownWithError:] doTeardownWithError
FINE: -[GCKCastSocket doTeardownWithError:] notifying delegate that socket is disconnected
FINE: -[GCKHeartbeatChannel didDisconnect] disconnected - stopping heartbeat timer if necessary
FINE: -[GCKCastSocket socketDidDisconnect:withError:] socketDidDisconnect:withError: "(null)"
Then when the app resumes: INFO: Going into foreground
FINE: -[GCKCastSocket doTeardownWithError:] doTeardownWithError
FINE: -[GCKCastSocket doTeardownWithError:] notifying delegate that socket is disconnected
FINE: -[GCKCastSocket connectToHost:port:withTimeout:] Connecting to "192.168.1.4" on port 8009l...
INFO: Did become active
FINE: -[GCKCastSocket socket:didConnectToHost:port:] socketDidConnect:
FINE: -[GCKCastSocket socketDidSecure:] socketDidSecure:
FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=1307
FINE: -[GCKDeviceAuthChannel didReceiveBinaryMessage:] Genuine Google device didDeviceAuthenticated=YES
FINE: -[GCKDeviceManager deviceAuthChannelDidAuthenticate:] Authentic device, connecting receiver channel
FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=474
FINE: -[GCKDeviceManager receiverControlChannel:didReceiveStatusForApplication:] Application to join (CC1AD845) is available
FINE: -[GCKDeviceManager connectAndNotifyDidConnectToApplication:launchedApplication:] Connected to application <0x1467c8f0:GCKApplicationMetadata> Default Media Receiver (CC1AD845), transport ID web-11
FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=135
FINE: -[GCKMediaControlChannel didReceiveTextMessage:] message received: {"type":"MEDIA_STATUS","status":[],"requestId":6}
回答1:
With the version 2.0 of the iOS Sender API, the GCKCastSocket
is closed upon receiving a UIApplicationDidEnterBackgroundNotification
and this is not something that can be configured.
That means:
- no new media can be pushed from the app to the chromecast while the app is in the background
- it is not possible to implement lock screen controls
Alternatives (custom receiver only):
- Send a list of media to be played to the receiver
- Fetch new media from the cloud directly from the receiver
See also this question or this one for more details.
回答2:
This is a solution I used for other needs, but I guess it could be applied here as well (not tested yet)
1. Create a background task handlers as dispatch_block_t, let's say
dispatch_block_t myDummyBackgroundTaskBlock = {
[[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
myDummyBackgroundTask = UIBackgroundTaskInvalid;
myDummyBackgroundTask = [app beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTask];
};
2. Define somewhere this background and foreground tasks handler
// foreground
-(void)handleTasksForApplicationInForeground {
if(myDummyBackgroundTask) { // reset that task
[[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTask];
myDummyBackgroundTask = UIBackgroundTaskInvalid;
}
}
// background
-(void) handleTasksForApplicationInBackground {
UIDevice *device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
if(backgroundSupported && backgroundEnabled) { // perform a background task
myDummyBackgroundTaskBlock = ^{
[[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTaskBlock];
myDummyBackgroundTaskBlock = UIBackgroundTaskInvalid;
};
SEL sel = @selector(doDummyBackgroundTask);
[self doBackgroundTaskAsync:sel];
[self performSelector:@selector(doBackgroundTaskAsync:) withObject:nil afterDelay:500.0f]; /// LP: this is the funny part since iOS will kill the task after 500 sec.
}
}
3. Now let's handle in the application delegate the background mode (As defined before you can activate background mode with different options in your app .plist):
-(void)applicationDidEnterBackground:(UIApplication *)application {
[self handleTasksForApplicationInBackground];
}
-(void)applicationWillEnterForeground:(UIApplication *)application {
[self handleTasksForApplicationInForeground];
}
4. Let's see what the background async task selector does
-(void) doBackgroundTaskAsync:(SEL)selector {
@try {
if( [[UIApplication sharedApplication] backgroundTimeRemaining] < 5 ) {
return;
}
if(!myDummyBackgroundTaskBlock) { // need to create again on-the-fly
myDummyBackgroundTaskBlock = ^{
[[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
myDummyBackgroundTask = UIBackgroundTaskInvalid;
};
}
myDummyBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTaskBlock];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
while ([[UIApplication sharedApplication] backgroundTimeRemaining] > 5.0) {
int delta = 5.0;
[self performSelector: selector ];
sleep(delta);
}
});
}
@catch (...) {
}
}
来源:https://stackoverflow.com/questions/22541221/google-chromecast-sdk-teardown-in-background