다음을 통해 공유


Android용 디바이스 릴레이 구현

Project Rome의 Device Relay 기능은 원격 실행(명령이라고도 함) 및 앱 서비스의 두 가지 주요 기능을 지원하는 API 집합으로 구성됩니다.

원격 시작 API는 로컬 디바이스에서 원격 디바이스의 프로세스가 시작되는 시나리오를 지원합니다. 예를 들어 사용자가 차량에서 휴대폰으로 라디오를 들을 수 있지만 집에 돌아오면 휴대폰을 사용하여 홈 스테레오에 연결된 Xbox로 재생을 전송합니다.

App Services API를 사용하면 애플리케이션이 임의의 콘텐츠를 포함하는 메시지를 보낼 수 있는 두 디바이스 간에 영구 파이프라인을 설정할 수 있습니다. 예를 들어 모바일 장치에서 실행되는 사진 공유 앱은 사진을 검색하기 위해 사용자의 PC와 연결을 설정할 수 있습니다.

Project Rome의 기능은 연결된 디바이스 플랫폼이라는 기본 플랫폼에서 지원됩니다. 이 가이드에서는 연결된 디바이스 플랫폼 사용을 시작하는 데 필요한 단계를 제공한 다음, 플랫폼을 사용하여 Device Relay 관련 기능을 구현하는 방법을 설명합니다.

아래 단계에서는 Project Rome Android 샘플 앱의 코드를 참조합니다.

연결된 모든 디바이스 기능의 경우 지원되는 아키텍처(armeabi-v7a, arm64-v8a, x86 또는 x86_64) 또는 에뮬레이터가 있는 Android 앱 개발 IDE 및 Android 디바이스가 필요합니다. 시스템에서 Android 4.4.2 이상을 실행해야 합니다.

연결된 디바이스 플랫폼 및 알림에 대한 예비 설정

원격 연결을 구현하기 전에 Android 앱에 원격 디바이스에 연결하고 알림을 보내고 받을 수 있는 기능을 제공하기 위해 수행해야 하는 몇 가지 단계가 있습니다.

앱 등록

MsA(Microsoft 계정) 또는 AAD(Azure Active Directory) 인증은 Project Rome SDK의 거의 모든 기능(인근 공유 API인 예외)에 필요합니다. MSA가 아직 없는 경우 사용하려면 account.microsoft.com에 등록하세요.

비고

AAD(Azure Active Directory) 계정은 디바이스 릴레이 API에서 지원되지 않습니다.

선택한 인증 방법을 사용하여 애플리케이션 등록 포털의 지침에 따라 Microsoft에 앱을 등록해야 합니다. Microsoft 개발자 계정이 없는 경우 계정을 만들어야 합니다.

MSA를 사용하여 앱을 등록하는 경우 클라이언트 ID 문자열을 받아야 합니다. 나중에 저장합니다. 이렇게 하면 앱이 Microsoft의 연결된 디바이스 플랫폼 리소스에 액세스할 수 있습니다. AAD를 사용하는 경우 클라이언트 ID 문자열을 가져오는 방법에 대한 지침은 Azure Active Directory 인증 라이브러리 를 참조하세요.

SDK 추가

다음 리포지토리 참조를 프로젝트의 루트에 있는 build.gradle 파일에 삽입합니다.

allprojects {
    repositories {
        jcenter()
    }
}

그런 다음, 다음 종속성을 프로젝트 폴더에 있는 build.gradle 파일에 삽입합니다.

dependencies { 
    ...
    implementation 'com.microsoft.connecteddevices:connecteddevices-sdk:+'
}

프로젝트의 AndroidManifest.xml 파일에서 <manifest> 요소 내에 다음 사용 권한을 추가합니다(아직 존재하지 않는 경우). 이렇게 하면 앱에서 인터넷에 연결하고 디바이스에서 Bluetooth 검색을 활성화하는 권한이 부여됩니다.

Bluetooth 관련 권한은 Bluetooth 검색을 사용하는 데만 필요합니다. 연결된 디바이스 플랫폼의 다른 기능에는 필요하지 않습니다. 또한 ACCESS_COARSE_LOCATION은 Android SDK 21 이상에서만 필요합니다. Android SDK 23 이상에서 개발자가 사용자에게 런타임 시 위치 접근을 허가하도록 요청해야 합니다.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

다음으로 연결된 디바이스 기능을 사용할 활동 클래스로 이동합니다. 다음 패키지를 가져옵니다.

import com.microsoft.connecteddevices;
import com.microsoft.connecteddevices.remotesystems;
import com.microsoft.connecteddevices.remotesystems.commanding;

인증 및 계정 관리 설정

연결된 디바이스 플랫폼에는 등록 프로세스에서 사용할 유효한 OAuth 토큰이 필요합니다. OAuth 토큰을 생성하고 관리하는 데 원하는 방법을 사용할 수 있습니다. 그러나 개발자가 플랫폼을 쉽게 사용할 수 있도록 인증 공급 업체의 편의를 위해 새로 고침 토큰을 생성하고 관리하는 데 사용할 수 있는 iOS 샘플 앱의 일부로 포함시켰습니다.

ConnectedDevicesAccountManager 인터페이스를 직접 구현하려면, 다음 정보를 확인해 두십시오.

MSA를 사용하는 경우 다음과 같은 범위를 로그인 요청에 포함시켜야 합니다. "wl.offline_access", "ccs.ReadWrite", "dds.read", "dds.register", "wns.connect", "asimovrome.telemetry""https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp".

AAD 계정을 사용하는 경우 다음 대상을 요청해야 합니다. "https://cdpcs.access.microsoft.com", "https://cs.dds.microsoft.com", "https://wns.windows.com/""https://activity.microsoft.com".

비고

AAD(Azure Active Directory) 계정은 디바이스 릴레이 API에서 지원되지 않습니다.

제공된 ConnectedDevicesAccountManager 구현을 사용하든 사용하지 않든, AAD를 사용하는 경우 Azure Portal(portal.azure.com > Azure Active Directory > 앱 등록)의 앱 등록에서 다음 사용 권한을 지정해야 합니다.

  • Microsoft 활동 피드 서비스
    • 이 앱에 대한 사용자 알림 전송 및 수정
    • 사용자의 활동 피드에 앱 활동 읽기 및 쓰기
  • Windows 알림 서비스
    • Windows 알림 서비스에 디바이스 연결
  • Microsoft Device Directory 서비스
    • 디바이스 목록 보기
    • 디바이스 및 앱 목록에 추가
  • Microsoft Command Service
    • 사용자 디바이스와 통신
    • 사용자 디바이스 읽기

푸시 알림을 위해 앱 등록

Firebase Cloud Messaging 지원을 위해 Google에 애플리케이션을 등록합니다. 받는 보낸 사람 ID 및 서버 키를 기록해 둡니다. 나중에 필요합니다.

등록되면 푸시 알림 기능을 앱의 연결된 디바이스 플랫폼과 연결해야 합니다.

mNotificationRegistration = new ConnectedDevicesNotificationRegistration();
mNotificationRegistration.setType(ConnectedDevicesNotificationType.FCM);
mNotificationRegistration.setToken(token);
mNotificationRegistration.setAppId(Secrets.FCM_SENDER_ID);
mNotificationRegistration.setAppDisplayName("SampleApp");

디바이스 간 환경을 위해 Microsoft Windows 개발자 센터에 앱 등록

중요합니다

이 단계는 Project Rome 기능을 사용하여 Windows 이외의 디바이스에서 데이터에 액세스하거나 요청하려는 경우에만 필요합니다. Windows 디바이스만 대상으로 하는 경우 이 단계를 완료할 필요가 없습니다.

아래와 같이, 개발자 센터 대시보드로 이동하여 왼쪽 탐색 창의 디바이스 간 환경으로 이동하고 새 디바이스 간 앱 구성을 선택합니다. 개발자 센터 대시보드 – 디바이스 간 환경

개발자 센터 온보딩 프로세스에는 다음 단계가 필요합니다.

  • 지원되는 플랫폼 선택 - 앱이 존재하고 디바이스 간 환경에서 사용할 수 있는 플랫폼을 선택합니다. Graph 알림 통합의 경우, Windows, Android 및/또는 iOS 중에서 선택할 수 있습니다. 디바이스 간 환경 – 지원되는 플랫폼

  • 앱 ID 제공 – 앱이 있는 각 플랫폼의 앱 ID를 제공합니다. Android 앱의 경우 프로젝트를 만들 때 앱에 할당한 패키지 이름입니다. 패키지 이름은 프로젝트 개요 -> 일반의 Firebase 콘솔에서 찾을 수 있습니다. 플랫폼당 다른 ID(최대 10개)를 추가할 수 있습니다. 동일한 앱 또는 다른 앱의 버전이 여러 개인 경우, 동일한 사용자를 대상으로 하는 앱 서버에서 보낸 동일한 알림을 수신할 수 있도록 하려는 경우입니다. 디바이스 간 환경 – 앱 ID

  • MSA 및/또는 AAD 앱 등록에서 앱 ID를 선택하거나 제공합니다. MSA 또는 AAD 앱 등록에 해당하는 이러한 클라이언트 ID는 위의 이전 MSA/AAD 앱 등록 단계에서 확보했습니다. 디바이스 간 환경 – MSA 및 AAD 앱 등록

  • 그래프 알림 및 기타 연결된 디바이스 플랫폼 기능은 주요 플랫폼의 각 네이티브 알림 플랫폼을 활용하여 앱 클라이언트 엔드포인트, 즉 WNS(Windows UWP), FCM(Android용) 및 APNS(iOS용)로 알림을 보냅니다. 이러한 알림 플랫폼에 대한 자격 증명을 제공하여 사용자를 대상으로 하는 알림을 게시할 때 Graph 알림이 앱 서버에 대한 알림을 전달할 수 있도록 설정합니다. Android의 경우 클라우드 메시징 서비스를 사용하도록 설정하는 것은 Microsoft Graph 알림을 사용하기 위한 필수 구성 요소입니다. 또한 필요한 보낸 사람 ID는 Firebase Cloud Messaging Sender ID에 해당하며 API 키는 레거시 서버 키에 해당합니다. 둘 다 스크린샷에 표시된 것처럼 Cloud Messaging 탭의 Firebase 콘솔> - 프로젝트 -> 설정에서 찾을 수 있습니다. 디바이스 간 환경 – 푸시 자격 증명

  • 마지막 단계는 등록한 앱에 대해 디바이스 간 앱 ID와 같은 역할을 하는 도메인에 대한 소유권이 앱에 있다는 것을 확인하는 검증 프로세스인, 디바이스 간 앱 도메인을 확인하는 것입니다. 디바이스 간 환경 – 도메인 확인

플랫폼 사용

플랫폼 만들기

시작하려면 플랫폼을 인스턴스화하기만 하면됩니다.

ConnectedDevicesPlatform sPlatform = new ConnectedDevicesPlatform(context);

ConnectedDevicesAccountManager 이벤트를 구독하여 사용자 계정 처리

플랫폼에 액세스하려면 인증된 사용자가 필요합니다. 유효한 계정이 사용되고 있는지 확인하려면 ConnectedDevicesAccountManager 이벤트를 구독해야 합니다.

 ConnectedDevicesPlatform sPlatform.getAccountManager().accessTokenRequested().subscribe((accountManager, args) -> {

    // Get access token
}
 ConnectedDevicesPlatform sPlatform.getAccountManager().accessTokenInvalidated().subscribe((accountManager, args) -> {

    // Refresh and renew existing access token
}

ConnectedDevicesNotificationRegistrationManager 이벤트 구독

마찬가지로 플랫폼은 알림을 사용하여 디바이스 간에 명령을 전달합니다. 따라서 클라우드 등록 상태가 사용 중인 계정에 유효한지 확인하려면 ConnectedDevicesNotificationRegistrationManager 이벤트를 구독해야 합니다. ConnectedDevicesNotificationRegistrationState를 사용하여 상태 확인

ConnectedDevicesPlatform sPlatform.getNotificationRegistrationManager().notificationRegistrationStateChanged().subscribe((notificationRegistrationManager, args) -> {
    
     // Check state using ConnectedDevicesNotificationRegistrationState enum

}

플랫폼 시작

이제 플랫폼이 초기화되고 이벤트 처리기가 준비되었으므로 원격 시스템 디바이스 검색을 시작할 준비가 되었습니다.

ConnectedDevicesPlatform sPlatform.start();

앱에 알려진 사용자 계정 검색

앱에 알려진 사용자 계정 목록이 ConnectedDevicesAccountManager와 제대로 동기화되었는지 확인해야 합니다.

ConnectedDevicesAccountManager.addAccountAsync를 사용하여 새 사용자 계정을 추가합니다.

 public synchronized AsyncOperation<ConnectedDevicesAddAccountResult> addAccountToAccountManagerAsync(ConnectedDevicesAccount account) {
        return ConnectedDevicesPlatform sPlatform.getAccountManager().addAccountAsync(account);
    }

잘못된 계정을 제거하려면 ConnectedDevicesAccountManager.removeAccountAsync를 사용할 수 있습니다.

 public synchronized AsyncOperation<ConnectedDevicesAddAccountResult> removeAccountToAccountManagerAsync(ConnectedDevicesAccount account) {
        return ConnectedDevicesPlatform sPlatform.getAccountManager().removeAccountAsync(account);
    }

원격 디바이스 및 앱 검색

RemoteSystemWatcher 인스턴스는 이 섹션의 핵심 기능을 처리합니다. 원격 시스템을 검색하는 클래스에서 선언합니다.

private RemoteSystemWatcher mWatcher = null;

감시자를 만들고 디바이스 검색을 시작하기 전에 검색 필터를 추가하여 앱이 대상으로 지정할 디바이스 종류를 결정할 수 있습니다. 사용자 입력에 의해 결정되거나 사용 사례에 따라 앱에 하드 코딩될 수 있습니다.

private void onStartWatcherClicked() {
    // RemoteSystemWatcher cannot be started unless the platform is 
    // initialized and the user is logged in. It may be helpful to use
    // methods like these to track the state of the application.
    // See the sample app for their implementations.
    if (!getMainActivity().isSignedIn()) {
        return;
    }
    if (!getMainActivity().isPlatformInitialized()) {
        return;
    }

    // create and populate a list of filters. The filter choices below are arbitrary.
    ArrayList<RemoteSystemFilter> filters = new ArrayList<>();

    /*  RemoteSystemDiscoveryType filters can be used to filter the types of Remote Systems you discover.
    Possible values:
        ANY(0),
        PROXIMAL(1),
        CLOUD(2),
        SPATIALLY_PROXIMAL(3)
    */
    filters.add(new RemoteSystemDiscoveryTypeFilter(RemoteSystemDiscoveryType.ANY));

    /*  RemoteSystemStatusType filters can be used to filter the status of Remote Systems you discover.
    Possible values:
        ANY(0),
        AVAILABLE(1)
    */
    filters.add(new RemoteSystemStatusTypeFilter(RemoteSystemStatusType.AVAILABLE));


    /*  RemoteSystemKindFilter can filter the types of devices you want to discover.
    Possible values are members of the RemoteSystemKinds class.
    */
    String[] kinds = new String[] { RemoteSystemKinds.Phone(), RemoteSystemKinds.Tablet() };

    RemoteSystemKindFilter kindFilter = new RemoteSystemKindFilter(kinds);
    filters.add(kindFilter);

    /*  RemoteSystemAuthorizationKind determines the category of user whose system(s) you want to discover.
    Possible values:
        SAME_USER(0),
        ANONYMOUS(1)
    */
    filters.add(new RemoteSystemAuthorizationKindFilter(RemoteSystemAuthorizationKind.SAME_USER)); 
    // ...

이 시점에서 앱은 앱이 검색된 디바이스를 구문 분석하고 상호 작용하는 방법을 결정하는 감시자 개체를 초기화할 수 있습니다.

    // ...

    // Create a RemoteSystemWatcher
    mWatcher = new RemoteSystemWatcher(filters.toArray(new RemoteSystemFilter[filters.size()]));
    }

    // ...

앱은 검색된 디바이스 집합( RemoteSystem 인스턴스로 표시됨)을 유지하고 사용 가능한 디바이스 및 앱(예: 표시 이름 및 디바이스 유형)에 대한 정보를 UI에 표시하는 것이 좋습니다.

다음 클래스 스텁은 감시자 인스턴스에 대한 이벤트 수신기로 사용할 수 있습니다.

private class RemoteSystemAddedListener implements EventListener<RemoteSystemWatcher, RemoteSystem> {
    @Override
    public void onEvent(RemoteSystemWatcher remoteSystemWatcher, RemoteSystem remoteSystem) {
        // add instance "remoteSystem" to program memory. See sample for full implementation.
    }
}

private class RemoteSystemUpdatedListener implements EventListener<RemoteSystemWatcher, RemoteSystem> {
    @Override
    public void onEvent(RemoteSystemWatcher remoteSystemWatcher, RemoteSystem remoteSystem) {
        // update instance "remoteSystem" in program memory. See sample for full implementation.
    }
}

private class RemoteSystemRemovedListener implements EventListener<RemoteSystemWatcher, RemoteSystem> {
    @Override
    public void onEvent(RemoteSystemWatcher remoteSystemWatcher, RemoteSystem remoteSystem) {
        // remove instance "remoteSystem" from program memory. See sample for full implementation.
    }
}

private class RemoteSystemWatcherErrorOccurredListener implements EventListener<RemoteSystemWatcher, RemoteSystemWatcherError> {
    @Override
    public void onEvent(RemoteSystemWatcher remoteSystemWatcher, RemoteSystemWatcherError remoteSystemWatcherError) {
        // handle the error
    }
}

호출되면 mWatcher.start 원격 시스템 활동을 감시하기 시작하고 검색된 디바이스 집합에서 디바이스가 검색, 업데이트 또는 제거될 때 이벤트가 발생합니다. 백그라운드에서 지속적으로 스캔하므로 불필요한 네트워크 통신 및 배터리 소모를 방지하기 위해 더 이상 필요하지 않은 경우 감시자를 중지하는 것이 좋습니다.

// if you call this from the activity's onPause method, you ensure that
// it will stop when the activity exits the foreground.
public void stopWatcher() {
    if (mWatcher == null) {
        return;
    }
    mWatcher.stop();
}

사용 사례 예: 원격 시작 및 원격 앱 서비스 구현

코드의 이 시점에서 사용 가능한 디바이스를 참조하는 RemoteSystem 개체의 작업 목록이 있어야 합니다. 이러한 디바이스로 수행하는 작업은 앱의 기능에 따라 달라집니다. 상호 작용의 주요 유형은 원격 시작 및 원격 앱 서비스입니다. 다음 섹션에서 설명합니다.

A) 원격 시작

다음 코드에서는 이러한 디바이스 중 하나를 선택한 다음(이상적으로는 UI 컨트롤을 통해 수행됨) RemoteLauncher 를 사용하여 앱 호환 URI를 전달하여 앱을 시작하는 방법을 보여 줍니다.

원격 시작은 원격 디바이스(이 경우 호스트 디바이스가 해당 URI 체계에 대한 기본 앱으로 지정된 URI를 시작) 또는 해당 디바이스의 특정 원격 애플리케이션을 대상으로 할 수 있다는 점에 유의해야 합니다.

이전 섹션에서 설명한 것처럼 검색은 먼저 디바이스 수준에서 발생하지만(RemoteSystem은 디바이스를 나타낸다) RemoteSystem 인스턴스에서 메서드를 호출 getApplications 하여 연결된 디바이스 플랫폼을 사용하도록 등록된 원격 디바이스의 앱을 나타내는 RemoteSystemApp 개체의 배열을 가져올 수 있습니다(위의 예비 단계에서 자신의 앱을 등록한 것처럼). RemoteSystemRemoteSystemApp을 모두 사용하여 URI를 시작하는 데 필요한 RemoteSystemConnectionRequest를 생성할 수 있습니다.

// this could be a RemoteSystemApp instead. Either way, it 
// must be defined somewhere in the application before the launch operation
// is called.
private RemoteSystem target; 

// ...

/**
* Responsible for calling into the Rome API to launch the given URI and provides
* the logic to handle the RemoteLaunchUriStatus response.
* @param uri URI to launch
* @param target The target for the launch URI request
*/
private void launchUri(final String uri, final RemoteSystem target, final long messageId)
{
    RemoteLauncher remoteLauncher = new RemoteLauncher();

    AsyncOperation<RemoteLaunchUriStatus> resultOperation = remoteLauncher.launchUriAsync(new RemoteSystemConnectionRequest(target), uri);
    // ...

반환된 AsyncOperation 을 사용하여 시작 시도의 결과를 처리합니다.

    // ...
    resultOperation.whenCompleteAsync(new AsyncOperation.ResultBiConsumer<RemoteLaunchUriStatus, Throwable>() {
        @Override
        public void accept(RemoteLaunchUriStatus status, Throwable throwable) throws Throwable {
            if (throwable != null) {
                // handle the exception, referencing "throwable"
            } else {
                if (status == RemoteLaunchUriStatus.SUCCESS) {
                    // report the success case
                } else {
                    // report the non-success case, referencing "status"
                }
            }
        }
    });
}

전송되는 URI에 따라 원격 디바이스에서 특정 상태 또는 구성으로 앱을 시작할 수 있습니다. 이렇게 하면 중단 없이 다른 장치에서 영화 시청과 같은 사용자 작업을 계속할 수 있습니다.

사용 사례에 따라 대상 시스템의 앱이 URI를 처리할 수 없거나 여러 앱이 URI를 처리할 수 없는 경우를 처리해야 할 수 있습니다. RemoteLauncher 클래스 및 RemoteLauncherOptions 클래스는 이 작업을 수행하는 방법을 설명합니다.

B) 원격 앱 서비스

Android 앱은 연결된 디바이스 포털을 사용하여 다른 디바이스의 앱 서비스와 상호 작용할 수 있습니다. 이렇게 하면 호스트 디바이스의 포그라운드로 앱을 가져올 필요 없이 다른 디바이스와 통신할 수 있는 여러 가지 방법이 제공됩니다.

대상 디바이스에서 앱 서비스 설정

이 가이드에서는 Windows용 로마 테스트 앱을 대상 앱 서비스로 사용합니다. 따라서 아래 코드는 Android 앱이 지정된 원격 시스템에서 해당 특정 앱 서비스를 찾도록 합니다. 이 시나리오를 테스트하려면 Windows 디바이스에서 로마 테스트 앱을 다운로드하고 위의 예비 단계에서 사용한 것과 동일한 MSA로 로그인했는지 확인합니다.

사용자 고유의 UWP 앱 서비스를 작성하는 방법에 대한 지침은 UWP(앱 서비스 만들기 및 사용)를 참조하세요. 서비스가 연결된 디바이스와 호환되도록 하려면 몇 가지 변경을 수행해야 합니다. 이 작업을 수행하는 방법에 대한 지침은 원격 앱 서비스에 대한 UWP 가이드 를 참조하세요.

클라이언트 디바이스에서 App Service 연결 열기

Android 앱은 원격 디바이스 또는 애플리케이션에 대한 참조를 획득해야 합니다. 시작 섹션과 마찬가지로 이 시나리오에서는 시스템에서 사용 가능한 앱을 나타내는 RemoteSystem 또는 RemoteSystemApp에서 생성할 수 있는 RemoteSystemConnectionRequest를 사용해야 합니다.

// this could be a RemoteSystemApp instead. Either way, it 
// must be defined somewhere in the application before the app service
// connection is opened.
private RemoteSystem target = null;

또한 앱은 앱 서비스 이름 및 패키지 식별자라는 두 개의 문자열을 사용하여 대상 앱 서비스를 식별해야 합니다. 앱 서비스 공급자의 소스 코드에서 찾을 수 있습니다(Windows 앱 서비스에 대한 이 문자열을 가져오는 방법에 대한 자세한 내용은 UWP(앱 서비스 만들기 및 사용) 참조). 이러한 문자열은 AppServiceConnection 인스턴스에 공급되는 AppServiceDescription을 생성합니다.

// this is defined below
private AppServiceConnection connection = null; 


/**
* Creates an AppService connection with the current identifier and service name and opens the
* app service connection.
*/
private void onNewConnectionButtonClicked()
{
    connection = new AppServiceConnection();

    // these hard-coded strings must be defined elsewhere in the app
    connection.setAppServiceDescription(new AppServiceDescription(mAppServiceName, mPackageIdentifier));

    // this method is defined below
    openAppServiceConnection();
}
/**
* Establish an app service connection.
* Opens the given AppService connection using the connection request. Once the connection is
* opened, it adds the listeners for request-received and close. Catches all exceptions
* to show behavior of API surface exceptions.
*/
private void openAppServiceConnection()
{
    // optionally report outbound request
    // ...

    RemoteSystemConnectionRequest connectionRequest = new RemoteSystemConnectionRequest(target));

    /* Will asynchronously open the app service connection using the given connection request
    * When this is done, we log the traffic in the UI for visibility to the user (for sample purposes)
    * We can check the status of the connection to determine whether or not it was successful, and show 
    * some UI if it wasn't (in this case it is part of the list item).
    */
    connection.openRemoteAsync(connectionRequest)
        .thenAcceptAsync(new AsyncOperation.ResultConsumer<AppServiceConnectionStatus>() {
            @Override
            public void accept(AppServiceConnectionStatus appServiceConnectionStatus) throws Throwable {
                // optionally report inbound response
                // ...

                if (appServiceConnectionStatus != AppServiceConnectionStatus.SUCCESS) {
                    // report the error, referencing "appServiceConnectionStatus"
                    return;
                }
            }
        })
        .exceptionally(new AsyncOperation.ResultFunction<Throwable, Void>() {
            @Override
            public Void apply(Throwable throwable) throws Throwable {
                // report the exception
                return null;
            }
        });
}

앱 서비스로 보낼 메시지 만들기

보낼 메시지를 저장할 변수를 선언합니다. Android에서 원격 앱 서비스로 보내는 메시지는 유형입니다.

private Map<String, Object> mMessagePayload = null;

비고

앱이 다른 플랫폼의 앱 서비스와 통신하는 경우 연결된 디바이스 플랫폼은 을 수신 플랫폼의 일치 구문으로 변환합니다. 예를 들어 이 앱에서 Windows 앱 서비스로 전송된 은 .NET Framework의 ValueSet 개체로 변환된 다음, 앱 서비스에서 해석할 수 있습니다. 다른 방향으로 전달된 정보는 역방향으로 변환됩니다.

다음 메서드는 로마 테스트 앱의 Windows용 앱 서비스에서 해석할 수 있는 메시지를 작성합니다.

/**
* Send the current message payload through all selected AppService connections on a non-UI
* thread as to not block user interaction, a result of the delay between API calls.
*/
private void onMessageButtonClicked()
{
    mMessagePayload = new HashMap<>();
    // Add the required fields of RomanApp on Windows
    DateFormat df = new SimpleDateFormat(DATE_FORMAT);
    mMessagePayload.put("Type", "ping");
    mMessagePayload.put("CreationDate", df.format(new Date()));
    mMessagePayload.put("TargetId", "check if this field needs to be included");

    // this method is defined below.
    sendMessage(connection, mMessagePayload);
}

중요합니다

원격 앱 서비스 시나리오에서 앱과 서비스 간에 전달되는 은 다음 형식을 준수해야 합니다. 키는 문자열이어야 하고, 값은 문자열, 정수 또는 실수와 같은 보관된 숫자 형식, 보관된 부울, android.graphics.Point, android.graphics.Rect, java.util.Date, java.util.UUID, 이러한 형식의 동종 배열 또는 이 사양을 충족하는 다른 Map 개체일 수 있습니다.

App Service에 메시지 보내기

앱 서비스 연결이 설정되고 메시지가 만들어지면 앱 서비스로 보내는 것은 간단하며 앱 서비스 연결 인스턴스 및 메시지에 대한 참조가 있는 앱의 어디에서나 수행할 수 있습니다.


// When assigning message IDs, use an incremental counter
private AtomicInteger mMessageIdAppServices = new AtomicInteger(0);

// ...

/**
* Send a message using the app service connection
* Send the given Map object through the given AppServiceConnection. Uses an internal messageId
* for logging purposes.
* @param connection AppServiceConnection to send the Map payload
* @param message Payload to be translated to a ValueSet
*/
private void sendMessage(final AppServiceConnection connection, Map<String, Object> message)
{
    final long messageId = mMessageIdAppServices.incrementAndGet();

    connection.sendMessageAsync(message)
        .thenAcceptAsync(new AsyncOperation.ResultConsumer<AppServiceResponse>() {
            @Override
            public void accept(AppServiceResponse appServiceResponse) throws Throwable {
                // optionally report an inbound response

                // this method is defined below
                handleAppServiceResponse(appServiceResponse, messageId);
            }
        })
        .exceptionally(new AsyncOperation.ResultFunction<Throwable, Void>() {
            @Override
            public Void apply(Throwable throwable) throws Throwable {
                // report the exception, referencing "throwable"

                return null;
            }
        });

    // optionally log the outbound message request here, referencing 
    // connection.getAppServiceDescription().getName() as the recipient.
}

앱 서비스의 응답은 다음 메서드에 의해 수신되고 해석됩니다.

private void handleAppServiceResponse(AppServiceResponse appServiceResponse, long messageId)
{
    AppServiceResponseStatus status = appServiceResponse.getStatus();
    if (status == AppServiceResponseStatus.SUCCESS)
    {
        // the message was delivered successfully; interpret the response.
        Map<String, Object> response;
        response = appServiceResponse.getMessage();

        // in the Roman App case, the response contains the date it was created.
        String dateStr = (String)response.get("CreationDate");
        DateFormat df = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());

        // in this very simple use case, we compare the dates to get the total
        // transit time of the message response.
        try {
            Date startDate = df.parse(dateStr);
            Date nowDate = new Date();
            long diff = nowDate.getTime() - startDate.getTime();
            // do something with "diff"
        } catch (ParseException e) { e.printStackTrace(); }
    }
}

로마 앱의 경우 응답에는 생성된 날짜가 포함되므로 이 매우 간단한 사용 사례에서는 날짜를 비교하여 메시지 응답의 총 전송 시간을 가져올 수 있습니다.

그러면 원격 앱 서비스와 단일 메시지 교환이 종료됩니다.

앱 서비스 통신 완료

앱이 대상 디바이스의 앱 서비스와 상호 작용을 마치면 두 디바이스 간의 연결을 닫습니다.

// Close the given AppService connection
private void closeAppServiceConnection()
{
    connection.close();
}