Dzięki interfejsom API Device Discovery i Secure Connection Interfejs Sessions API zapewnia zaawansowane funkcje abstrakcyjne pozwalające na bezproblemową obsługę konwersji na różnych urządzeniach i aplikacji. Sesja reprezentuje środowisko użytkownika aplikacji, które może i przekazywane między urządzeniami.
Interfejs Sessions API opiera się również na koncepcji osobistych i wspólnych doświadczenia reprezentowane przez przeniesienie sesji i przypadki użycia udziału sesji . Ten diagram przedstawia ogólne sesje:

Tworzenie i przenoszenie sesji
Interfejs Sessions API odróżnia urządzenie źródłowe od urządzenia odbierającego. Urządzenie źródłowe tworzy sesję i wyszukuje które może obsłużyć sesję. Użytkownik urządzenia źródłowego wybiera urządzenie z listy wyświetlanej w oknie systemowym. Gdy użytkownik wybiera urządzenie odbierające, sesja źródłowa zostaje przeniesiona i usunięta z urządzenia źródłowego.
Aby przenieść sesję, musisz ją najpierw utworzyć przy użyciu tych parametry:
- Tag sesji aplikacji – identyfikator, który pozwala rozróżnić między wieloma sesjami w aplikacji.
Następnie zainicjuj przenoszenie, używając tych parametrów:
DeviceFilter
do filtrowania urządzeń, które mogą obsłużyć sesję.- Obiekt wywołania zwrotnego implementujący
OriginatingSessionStateCallback
Na urządzeniu źródłowym utwórz sesję zgodnie z tym przykładem:
private val HELLO_WORLD_TRANSFER_ACTION = "hello_world_transfer"
private lateinit var originatingSession: OriginatingSession
private lateinit var sessions: Sessions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sessions = Sessions.create(context = this)
}
suspend fun transferSession() {
val sessionId =
sessions.createSession(
ApplicationSessionTag("hello_world_transfer"),
)
originatingSession =
sessions.transferSession(
sessionId,
StartComponentRequest.Builder()
.setAction(HELLO_WORLD_TRANSFER_ACTION)
.setReason("Transfer reason here")
.build(),
emptyList(),
HelloWorldTransferSessionStateCallback()
)
}
private static final String HELLO_WORLD_TRANSFER_ACTION = "hello_world_transfer";
private OriginatingSession originatingSession;
private Sessions sessions;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sessions = Sessions.create(/* context= */ this);
}
public void transferSession() {
SessionId sessionId = sessions.createSession(new ApplicationSessionTag("hello_world_transfer"));
ListenableFuture<OriginatingSession> originatingSessionFuture =
sessions.transferSessionFuture(
sessionId,
new StartComponentRequest.Builder()
.setAction(HELLO_WORLD_TRANSFER_ACTION)
.setReason("Transfer reason here")
.build(),
Collections.emptyList(),
new HelloWorldTransferSessionStateCallback());
Futures.addCallback(
originatingSessionFuture,
new FutureCallback<>() {
@Override
public void onSuccess(OriginatingSession result) {
// Do nothing, handled in HelloWorldTransferSessionStateCallback
originatingSession = result;
}
@Override
public void onFailure(Throwable t) {
Log.d(TAG, "onFailure called for transferSessionFuture", t);
}
},
mainExecutor);
}
Następnie zdefiniuj wywołanie zwrotne sesji na urządzeniu źródłowym:
private inner class HelloWorldTransferSessionStateCallback : OriginatingSessionStateCallback {
override fun onConnected(sessionId: SessionId) {
val startupRemoteConnection = originatingSession.getStartupRemoteConnection()
lifecycleScope.launchWhenResumed {
startupRemoteConnection.send("hello, world".toByteArray(UTF_8))
startupRemoteConnection.registerReceiver(
object : SessionConnectionReceiver {
override fun onMessageReceived(participant: SessionParticipant, payload: ByteArray) {
val ok = payload.contentEquals("ok".toByteArray(UTF_8))
Log.d(TAG, "Session transfer initialized. ok=$ok")
}
}
)
}
}
override fun onSessionTransferred(sessionId: SessionId) {
Log.d(TAG, "Transfer done.")
}
override fun onTransferFailure(sessionId: SessionId, exception: SessionException) {
// Handle error
}
}
private class HelloWorldTransferSessionStateCallback implements OriginatingSessionStateCallback {
@Override
public void onConnected(SessionId sessionId) {
SessionRemoteConnection startupRemoteConnection =
originatingSession.getStartupRemoteConnection();
Futures.addCallback(
startupRemoteConnection.sendFuture("hello, world".getBytes()),
new FutureCallback<>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "Successfully sent initialization message");
}
@Override
public void onFailure(Throwable t) {
Log.d(TAG, "Failed to send initialization message", t);
}
},
mainExecutor);
}
@Override
public void onSessionTransferred(SessionId sessionId) {
Log.d(TAG, "Transfer done.");
}
@Override
public void onTransferFailure(SessionId sessionId, SessionException exception) {
// Handle error
}
}
Po zainicjowaniu przenoszenia sesji urządzenie odbierające otrzyma wywołanie zwrotne
w metodzie onNewIntent(intent: Intent)
. Dane intencji zawierają wszystko
niezbędnych do przeniesienia sesji.
Aby dokończyć przenoszenie na urządzeniu odbierającym:
private lateinit var sessions: Sessions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sessions = Sessions.create(context = this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
lifecycleScope.launchWhenResumed {
val receivingSession =
sessions.getReceivingSession(intent, HelloWorldReceivingSessionStateCallback())
// Get info from receiving device and init.
val startupRemoteConnection = receivingSession.getStartupRemoteConnection()
startupRemoteConnection.registerReceiver(
object : SessionConnectionReceiver {
override fun onMessageReceived(participant: SessionParticipant, payload: ByteArray) {
lifecycleScope.launchWhenResumed {
val transferInfo = String(payload)
startupRemoteConnection.send("ok".toByteArray(UTF_8))
// Complete transfer.
Log.d(TAG, "Transfer info: " + transferInfo)
receivingSession.onComplete()
}
}
}
)
}
}
private inner class HelloWorldReceivingSessionStateCallback : ReceivingSessionStateCallback {
override fun onTransferFailure(sessionId: SessionId, exception: SessionException) {
// Handle error
}
}
private Sessions sessions;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sessions = Sessions.create(/* context= */ this);
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
ListenableFuture<ReceivingSession> receivingSessionFuture =
sessions.getReceivingSessionFuture(intent, new HelloWorldReceivingSessionStateCallback());
ListenableFuture<Void> registerReceiverFuture =
Futures.transform(
receivingSessionFuture,
receivingSession -> {
SessionRemoteConnection startupRemoteConnection =
receivingSession.getStartupRemoteConnection();
SessionConnectionReceiver receiver =
(participant, payload) -> {
Log.d(
TAG,
"Successfully received initialization message of size: " + payload.length);
applicationInitialization(receivingSession, payload);
};
startupRemoteConnection.registerReceiver(receiver);
return null;
},
mainExecutor);
Futures.addCallback(
registerReceiverFuture,
new FutureCallback<Void>() {
@Override
public void onSuccess(Void unused) {
Log.d(TAG, "Connection receiver registerd successfully");
}
@Override
public void onFailure(Throwable t) {
Log.w(TAG, "Failed to register connection receiver", t);
}
},
mainExecutor);
}
private void applicationInitialization(ReceivingSession receivingSession, byte[] initMessage) {
ListenableFuture<SessionId> disconnectFuture =
Futures.transform(
receivingSession.onCompleteFuture(),
sessionId -> {
Log.d(TAG, "Succeeded to complete receive transfer for: " + sessionId);
return sessionId;
},
mainExecutor);
Futures.addCallback(
disconnectFuture,
new FutureCallback<SessionId>() {
@Override
public void onSuccess(SessionId result) {
Log.d(TAG, "Succeeded to remove the old session: " + result);
}
@Override
public void onFailure(Throwable t) {
Log.d(TAG, "Failed to remove the old session, which is now orphaned", t);
}
},
mainExecutor);
}
private static class HelloWorldReceivingSessionStateCallback
implements ReceivingSessionStateCallback {
@Override
public void onTransferFailure(SessionId sessionId, SessionException exception) {
// Handle error
}
}
Urządzenie odbierające może teraz kontynuować działanie.
Udostępnianie sesji
Udostępniając sesję, możesz zaprosić do grupy inne osoby w pobliżu Na przykład:
- Udostępnij lokalizację na mapie jako pasażer bezpośrednio do samochodu znajomego.
- Udostępnij niedzielną trasę rowerową innym osobom, z którymi jeździsz na rowerze.
- Zbieraj produkty, aby składać je grupowo, nie podając przy tym telefonu.
- Oddaj głos grupowy na kolejny program telewizyjny do obejrzenia razem.
Gdy użytkownik udostępnia sesję innemu urządzeniu, źródło wyszukuje i przedstawia urządzenia, które mogą dołączyć do sesji, oraz użytkownik wybiera urządzenia odbierające. Aplikacja prosi użytkownika na urządzenie odbierające, aby dołączyć do sesji z urządzenia źródłowego. Adres odbierający na urządzeniu zostanie przyznana sesja dodatkowa do interakcji z daną sesją na urządzenia źródłowego. Aplikacje mogą również dodawać kolejnych uczestników do swoich trwającej sesji współdzielonej.
Proces udostępniania sesji jest podobny do przenoszenia sesji.
ale zamiast dzwonić pod numer transferSession
, zadzwoń pod numer shareSession
. Druga
są różne metody wywołania zwrotnego stanu sesji.
// Originating side.
private val HELLO_WORLD_SHARE_ACTION = "hello_world_share"
private var activePrimarySession: PrimarySession? = null
private lateinit var sessions: Sessions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sessions = Sessions.create(context = this)
}
suspend fun shareSession() {
val sessionId = sessions.createSession(ApplicationSessionTag("hello_world_share"))
activePrimarySession =
sessions.shareSession(
sessionId,
StartComponentRequest.Builder()
.setAction(HELLO_WORLD_SHARE_ACTION)
.setReason("Share reason here")
.build(),
emptyList(),
HelloWorldShareSessionStateCallback(),
)
}
private inner class HelloWorldShareSessionStateCallback : PrimarySessionStateCallback {
override fun onShareInitiated(sessionId: SessionId, numPotentialParticipants: Int) {
// Custom logic here for when n devices can potentially join.
// e.g. if there were 0, cancel/error if desired,
// if non-0 maybe spin until numPotentialParticipants join etc.
}
override fun onParticipantJoined(sessionId: SessionId, participant: SessionParticipant) {
// Custom join logic here
lifecycleScope.launchWhenResumed {
// Example logic: send only to the participant who just joined.
val connection =
checkNotNull(activePrimarySession).getSecondaryRemoteConnectionForParticipant(participant)
connection.send("Initing hello, world.".toByteArray(UTF_8))
connection.registerReceiver(
object : SessionConnectionReceiver {
override fun onMessageReceived(participant: SessionParticipant, payload: ByteArray) {
val ok = payload.contentEquals("ok".toByteArray(UTF_8))
Log.d(TAG, "Session share initialized. ok=$ok")
// Example logic: broadcast to all participants, including the one
// that just joined.
lifecycleScope.launchWhenResumed {
checkNotNull(activePrimarySession)
.broadcastToSecondaries("hello, all.".toByteArray(UTF_8))
}
}
}
)
}
}
override fun onParticipantDeparted(sessionId: SessionId, participant: SessionParticipant) {
// Custom leave logic here.
}
override fun onPrimarySessionCleanup(sessionId: SessionId) {
// Custom cleanup logic here.
activePrimarySession = null
}
override fun onShareFailureWithParticipant(
sessionId: SessionId,
exception: SessionException,
participant: SessionParticipant
) {
// Handle error
}
}
// Originating side
private static final String HELLO_WORLD_SHARE_ACTION = "hello_world_share";
@Nullable private PrimarySession activePrimarySession = null;
private Sessions sessions;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sessions = Sessions.create(/* context= */ this);
}
private void shareSession() {
SessionId sessionId = sessions.createSession(new ApplicationSessionTag("hello_world_share"));
ListenableFuture<PrimarySession> shareSessionFuture =
sessions.shareSessionFuture(
sessionId,
new StartComponentRequest.Builder()
.setAction(HELLO_WORLD_SHARE_ACTION)
.setReason("Share reason here")
.build(),
Collections.emptyList(),
new HelloWorldShareSessionStateCallback());
Futures.addCallback(
shareSessionFuture,
new FutureCallback<PrimarySession>() {
@Override
public void onSuccess(PrimarySession primarySession) {
activePrimarySession = primarySession;
}
@Override
public void onFailure(Throwable t) {
Log.d(TAG, "Failed to share session", t);
}
},
mainExecutor);
}
private class HelloWorldShareSessionStateCallback implements PrimarySessionStateCallback {
@Override
public void onShareInitiated(SessionId sessionId, int numPotentialParticipants) {
// Custom logic here for when n devices can potentially join.
// e.g. if there were 0, cancel/error if desired,
// if non-0 maybe spin until numPotentialParticipants join etc.
}
@Override
public void onParticipantJoined(SessionId sessionId, SessionParticipant participant) {
PrimarySession joinedSession = activePrimarySession;
if (joinedSession == null) {
return;
}
SessionRemoteConnection connection =
joinedSession.getSecondaryRemoteConnectionForParticipant(participant);
Futures.addCallback(
connection.sendFuture("Initiating hello, world.".getBytes()),
new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
// Send successful.
}
@Override
public void onFailure(Throwable t) {
// Failed to send.
}
},
mainExecutor);
connection.registerReceiver(
new SessionConnectionReceiver() {
@Override
public void onMessageReceived(SessionParticipant participant, byte[] payload) {
boolean ok = new String(payload, UTF_8).equals("ok");
Log.d(TAG, "Session share initialized. ok=" + ok);
// Example logic: broadcast to all participants, including the one
// that just joined.
Futures.addCallback(
joinedSession.broadcastToSecondariesFuture("hello, all.".getBytes()),
new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
// Broadcast successful.
}
@Override
public void onFailure(Throwable t) {
// Failed to broadcast hello world.
}
},
mainExecutor);
}
});
}
@Override
public void onParticipantDeparted(SessionId sessionId, SessionParticipant participant) {
// Custom leave logic here.
}
@Override
public void onPrimarySessionCleanup(SessionId sessionId) {
// Custom cleanup logic here.
activePrimarySession = null;
}
@Override
public void onShareFailureWithParticipant(
SessionId sessionId, SessionException exception, SessionParticipant participant) {
// Custom error handling logic here.
}
}
Po stronie odbiorcy:
// Receiving side.
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
lifecycleScope.launchWhenResumed {
val secondarySession =
sessions.getSecondarySession(intent, HelloWorldSecondaryShareSessionStateCallback())
val remoteConnection = secondarySession.getDefaultRemoteConnection()
remoteConnection.registerReceiver(
object : SessionConnectionReceiver {
override fun onMessageReceived(participant: SessionParticipant, payload: ByteArray) {
Log.d(TAG, "Payload received: ${String(payload)}")
}
}
)
}
}
private inner class HelloWorldSecondaryShareSessionStateCallback : SecondarySessionStateCallback {
override fun onSecondarySessionCleanup(sessionId: SessionId) {
// Custom cleanup logic here.
}
}
// Receiving side.
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
sessions = Sessions.create(this);
ListenableFuture<SecondarySession> secondarySessionFuture =
sessions.getSecondarySessionFuture(
intent, new HelloWorldSecondaryShareSessionStateCallback());
Futures.addCallback(
secondarySessionFuture,
new FutureCallback<SecondarySession>() {
@Override
public void onSuccess(SecondarySession secondarySession) {
SessionRemoteConnection remoteConnection =
secondarySession.getDefaultRemoteConnection();
remoteConnection.registerReceiver(
new SessionConnectionReceiver() {
@Override
public void onMessageReceived(SessionParticipant participant, byte[] payload) {
Log.d(TAG, "Payload received: " + new String(payload, UTF_8));
}
});
}
@Override
public void onFailure(Throwable t) {
// Handle error.
}
},
mainExecutor);
}
private static class HelloWorldSecondaryShareSessionStateCallback
implements SecondarySessionStateCallback {
@Override
public void onSecondarySessionCleanup(SessionId sessionId) {
// Custom cleanup logic here.
}
}