GoogleApiClient로 Google API에 액세스 (지원 중단됨)

GoogleApiClient ('Google API 클라이언트') 객체를 사용하여 Google Play 서비스 라이브러리(예: Google 로그인, 게임, Drive)에서 제공되는 Google API에 액세스할 수 있습니다. Google API 클라이언트는 Google Play 서비스의 공통 진입점을 제공하고 사용자 기기와 각 Google 서비스 간의 네트워크 연결을 관리합니다.

그러나 최신 GoogleApi 인터페이스와 구현은 사용이 더 쉽고 Play 서비스 API에 액세스하는 데 선호되는 방법입니다. Google API 액세스를 참조하세요.

이 가이드에서는 다음을 수행하는 방법을 보여줍니다.

  • Google Play 서비스 연결을 자동으로 관리합니다.
  • Google Play 서비스에 동기 및 비동기 API 호출을 수행합니다.
  • 드물지만 필요한 경우 Google Play 서비스 연결을 수동으로 관리합니다. 자세한 내용은 수동 관리 연결을 참조하세요.
그림 1: Google API 클라이언트가 Google Play 게임즈 및 Google Drive와 같이 사용 가능한 Google Play 서비스를 연결하고 호출하기 위한 인터페이스를 제공하는 방법을 보여주는 그림

시작하려면 먼저 Android SDK용 Google Play 서비스 라이브러리 (버전 15 이상)를 설치해야 합니다. 아직 설정하지 않았다면 Google Play 서비스 SDK 설정의 안내를 따릅니다.

자동 관리 연결 시작

프로젝트가 Google Play 서비스 라이브러리에 연결되면 활동의 onCreate() 메서드에서 GoogleApiClient.Builder API를 사용하여 GoogleApiClient 인스턴스를 만듭니다. GoogleApiClient.Builder 클래스는 사용할 Google API와 원하는 OAuth 2.0 범위를 지정할 수 있는 메서드를 제공합니다. 다음은 Google Drive 서비스와 연결되는 GoogleApiClient 인스턴스를 만드는 코드 예입니다.

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Drive.API)
    .addScope(Drive.SCOPE_FILE)
    .build();

addApi()addScope()에 추가 호출을 추가하여 동일한 GoogleApiClient에 여러 API와 여러 범위를 추가할 수 있습니다.

중요: Wearable API를 다른 API와 함께 GoogleApiClient에 추가하면 Wear OS 앱이 설치되지 않은 기기에서 클라이언트 연결 오류가 발생할 수 있습니다. 연결 오류를 방지하려면 addApiIfAvailable() 메서드를 호출하고 Wearable API를 전달하여 클라이언트가 누락된 API를 정상적으로 처리할 수 있도록 합니다. 자세한 내용은 Wearable API 액세스를 참고하세요.

자동 관리 연결을 시작하려면 해결할 수 없는 연결 오류를 수신하도록 OnConnectionFailedListener 인터페이스의 구현을 지정해야 합니다. 자동 관리형 GoogleApiClient 인스턴스가 Google API에 연결을 시도하면 해결 가능한 연결 실패를 해결 (예: Google Play 서비스를 업데이트해야 하는 경우)하기 위해 UI를 자동으로 표시합니다. 해결할 수 없는 오류가 발생하면 onConnectionFailed()가 호출됩니다.

자동 관리 연결이 설정되거나 정지될 때 앱에서 알아야 하는 경우 ConnectionCallbacks 인터페이스의 선택적 구현을 지정할 수도 있습니다. 예를 들어 앱에서 Google API에 데이터를 쓰는 호출을 실행하는 경우 onConnected() 메서드가 호출된 후에만 호출되어야 합니다.

다음은 콜백 인터페이스를 구현하여 Google API 클라이언트에 추가하는 활동의 예입니다.

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import gms.drive.*;
import android.support.v4.app.FragmentActivity;

public class MyActivity extends FragmentActivity
        implements OnConnectionFailedListener {
    private GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this /* FragmentActivity */,
                                  this /* OnConnectionFailedListener */)
                .addApi(Drive.API)
                .addScope(Drive.SCOPE_FILE)
                .build();

        // ...
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        // An unresolvable error has occurred and a connection to Google APIs
        // could not be established. Display an error message, or handle
        // the failure silently

        // ...
    }
}

GoogleApiClient 인스턴스는 활동이 onStart()를 호출한 후 자동으로 연결되고 onStop()를 호출한 후 연결 해제됩니다. 앱은 GoogleApiClient를 빌드한 후 연결이 완료될 때까지 기다리지 않고 즉시 Google API에 읽기 요청을 시작할 수 있습니다.

Google 서비스와 통신

연결 후 클라이언트는 GoogleApiClient 인스턴스에 추가한 API 및 범위에 지정된 대로 앱이 승인된 서비스별 API를 사용하여 읽기 및 쓰기 호출을 할 수 있습니다.

참고: 특정 Google 서비스를 호출하기 전에 먼저 Google Developer Console에 앱을 등록해야 할 수도 있습니다. 자세한 내용은 Google Drive 또는 Google 로그인 등 사용 중인 API에 해당하는 시작 가이드를 참고하세요.

GoogleApiClient를 사용하여 읽기 또는 쓰기 요청을 수행하면 API 클라이언트에서 요청을 나타내는 PendingResult 객체를 반환합니다. 이는 앱이 호출하는 Google 서비스에 요청이 전달되기 직전에 발생합니다.

예를 들어 다음은 PendingResult 객체를 제공하는 Google Drive의 파일 읽기 요청입니다.

Query query = new Query.Builder()
        .addFilter(Filters.eq(SearchableField.TITLE, filename));
PendingResult<DriveApi.MetadataBufferResult> result = Drive.DriveApi.query(mGoogleApiClient, query);

앱에 PendingResult 객체가 있으면 앱은 요청을 비동기 호출로 처리할지 동기 호출로 처리할지 지정할 수 있습니다.

도움말: Google Play 서비스에 연결되어 있지 않은 동안 앱이 읽기 요청을 대기열에 추가할 수 있습니다. 예를 들어 앱은 GoogleApiClient 인스턴스가 아직 연결되어 있는지와 관계없이 메서드를 호출하여 Google Drive에서 파일을 읽을 수 있습니다. 연결이 설정되면 큐에 추가된 읽기 요청이 실행됩니다. Google API 클라이언트가 연결되어 있지 않은 상태에서 앱이 Google Play 서비스 쓰기 메서드를 호출하면 쓰기 요청에서 오류가 발생합니다.

비동기 호출 사용

요청을 비동기식으로 만들려면 PendingResult에서 setResultCallback()를 호출하고 ResultCallback 인터페이스의 구현을 제공하세요. 예를 들어 다음은 비동기식으로 실행되는 요청입니다.

private void loadFile(String filename) {
    // Create a query for a specific filename in Drive.
    Query query = new Query.Builder()
            .addFilter(Filters.eq(SearchableField.TITLE, filename))
            .build();
    // Invoke the query asynchronously with a callback method
    Drive.DriveApi.query(mGoogleApiClient, query)
            .setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
        @Override
        public void onResult(DriveApi.MetadataBufferResult result) {
            // Success! Handle the query result.
            // ...
        }
    });
}

앱이 onResult() 콜백에서 Result 객체를 수신하면 이 객체는 사용 중인 API에서 지정한 대로 적절한 서브클래스의 인스턴스로 전달됩니다(예: DriveApi.MetadataBufferResult).

동기 호출 사용

한 호출의 결과가 다른 호출의 인수로 필요하기 때문에 코드를 엄격하게 정의된 순서로 실행하려면 PendingResult에서 await()를 호출하여 요청을 동기식으로 만들면 됩니다. 그러면 스레드가 차단되고 요청이 완료되면 Result 객체가 반환됩니다. 이 객체는 사용 중인 API에 의해 지정된 대로 적절한 서브클래스의 인스턴스로 전달됩니다(예: DriveApi.MetadataBufferResult).

await()를 호출하면 결과가 도착할 때까지 스레드가 차단되므로 앱은 UI 스레드에서 Google API에 동기식 요청을 하면 안 됩니다. 앱은 AsyncTask 객체를 사용하여 새 스레드를 만들고 이 스레드를 사용하여 동기식 요청을 할 수 있습니다.

다음 예는 동기 호출로 Google Drive에 파일을 요청하는 방법을 보여줍니다.

private void loadFile(String filename) {
    new GetFileTask().execute(filename);
}

private class GetFileTask extends AsyncTask {
    protected void doInBackground(String filename) {
        Query query = new Query.Builder()
                .addFilter(Filters.eq(SearchableField.TITLE, filename))
                .build();
        // Invoke the query synchronously
        DriveApi.MetadataBufferResult result =
                Drive.DriveApi.query(mGoogleApiClient, query).await();

        // Continue doing other stuff synchronously
        // ...
    }
}

Wearable API 액세스

Wearable API는 휴대기기와 웨어러블 기기에서 실행되는 앱을 위한 통신 채널을 제공합니다. API는 시스템에서 전송하고 동기화할 수 있는 일련의 데이터 객체와 데이터 영역을 사용하여 앱에 중요한 이벤트를 알리는 리스너로 구성됩니다. Wearable API는 웨어러블 기기가 연결되어 있고 기기에 Wear OS 호환 앱이 설치된 경우 Android 4.3 (API 수준 18) 이상을 실행하는 기기에서 사용할 수 있습니다.

독립형 Wearable API 사용

앱이 Wearable API를 사용하지만 다른 Google API는 사용하지 않는 경우 addApi() 메서드를 호출하여 이 API를 추가할 수 있습니다. 다음 예에서는 GoogleApiClient 인스턴스에 Wearable API를 추가하는 방법을 보여줍니다.

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Wearable.API)
    .build();

Wearable API를 사용할 수 없는 상황에서는 Wearable API가 포함된 연결 요청이 API_UNAVAILABLE 오류 코드와 함께 실패합니다.

다음 예는 Wearable API의 사용 가능 여부를 확인하는 방법을 보여줍니다.

// Connection failed listener method for a client that only
// requests access to the Wearable API
@Override
public void onConnectionFailed(ConnectionResult result) {
    if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) {
        // The Wearable API is unavailable
    }
    // ...
}

다른 Google API와 함께 Wearable API 사용

앱에서 다른 Google API와 함께 Wearable API를 사용하는 경우 addApiIfAvailable() 메서드를 호출하고 Wearable API를 전달하여 사용할 수 있는지 확인합니다. 이 확인 기능을 사용하면 앱에서 API를 사용할 수 없는 사례를 적절하게 처리하도록 할 수 있습니다.

다음 예에서는 Drive API와 함께 Wearable API에 액세스하는 방법을 보여줍니다.

// Create a GoogleApiClient instance
mGoogleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this /* FragmentActivity */,
                          this /* OnConnectionFailedListener */)
        .addApi(Drive.API)
        .addApiIfAvailable(Wearable.API)
        .addScope(Drive.SCOPE_FILE)
        .build();

위의 예에서 Wearable API를 사용할 수 없는 경우 연결하지 않고 GoogleApiClient를 Google Drive에 연결할 수 있습니다. GoogleApiClient 인스턴스를 연결한 후 API를 호출하기 전에 Wearable API를 사용할 수 있는지 확인합니다.

boolean wearAvailable = mGoogleApiClient.hasConnectedApi(Wearable.API);

API 연결 실패 무시

addApi()를 호출했는데 GoogleApiClient가 해당 API에 성공적으로 연결할 수 없으면 클라이언트의 전체 연결 작업이 실패하고 onConnectionFailed() 콜백을 트리거합니다.

addApiIfAvailable()를 사용하여 무시되도록 API 연결 실패를 등록할 수 있습니다. addApiIfAvailable()로 추가된 API가 복구할 수 없는 오류(예: Wear의 경우 API_UNAVAILABLE)로 인해 연결에 실패하면 해당 API가 GoogleApiClient에서 삭제되고 클라이언트는 다른 API에 연결을 계속합니다. 하지만 복구 가능한 오류 (예: OAuth 동의 확인 인텐트)로 인해 API 연결이 실패하면 클라이언트 연결 작업이 실패합니다. 자동 관리 연결을 사용하는 경우 GoogleApiClient은 가능한 경우 이러한 오류를 해결하려고 시도합니다. 수동으로 관리 연결을 사용하는 경우 해결 인텐트가 포함된 ConnectionResultonConnectionFailed() 콜백에 전달됩니다. API 연결 실패는 실패에 대한 해결 방법이 없고 API가 addApiIfAvailable()와 함께 추가된 경우에만 무시됩니다. 수동 연결 실패 처리를 구현하는 방법은 연결 실패 처리를 참고하세요.

addApiIfAvailable()로 추가된 API가 연결된 GoogleApiClient 인스턴스에 항상 있는 것은 아니므로 hasConnectedApi()를 사용하여 검사를 추가하여 이러한 API 호출을 보호해야 합니다. 클라이언트에 대한 전체 연결 작업이 성공했을 때 특정 API의 연결에 실패한 이유를 알아보려면 getConnectionResult()를 호출하고 ConnectionResult 객체에서 오류 코드를 가져옵니다. 클라이언트가 연결되지 않은 상태에서 API를 호출하면 API_NOT_AVAILABLE 상태 코드와 함께 호출이 실패합니다.

addApiIfAvailable()를 통해 추가하는 API에 하나 이상의 범위가 필요한 경우 addScope() 메서드를 사용하는 대신 addApiIfAvailable() 메서드 호출에서 이러한 범위를 매개변수로 추가합니다. 이 접근 방식을 사용하여 추가된 범위는 OAuth 동의를 받기 전에 API 연결이 실패하는 경우 요청되지 않을 수 있지만 addScope()로 추가된 범위는 항상 요청됩니다.

수동 관리형 연결

이 가이드의 대부분은 enableAutoManage 메서드를 사용하여 자동으로 해결된 오류로 자동 관리 연결을 시작하는 방법을 보여줍니다. 대부분의 경우 이 방법이 Android 앱에서 Google API에 연결하는 가장 쉽고 효과적인 방법입니다. 하지만 다음과 같이 앱에서 Google API에 대한 수동 관리 연결을 사용해야 할 수도 있습니다.

  • 활동 외부에서 Google API에 액세스하거나 API 연결 관리 권한을 유지하기 위해
  • 연결 오류 처리 및 해결 방법 맞춤설정

이 섹션에서는 이러한 사용 사례 및 기타 고급 사용 사례의 예를 제공합니다.

수동 관리 연결 시작

GoogleApiClient에 대한 수동 관리 연결을 시작하려면 콜백 인터페이스 ConnectionCallbacksOnConnectionFailedListener의 구현을 지정해야 합니다. 이러한 인터페이스는 Google Play 서비스 연결이 성공, 실패 또는 정지되면 비동기 connect() 메서드에 대한 응답으로 콜백을 수신합니다.

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Drive.API)
            .addScope(Drive.SCOPE_FILE)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build()

연결을 수동으로 관리하는 경우 앱 수명 주기의 적절한 지점에서 connect()disconnect() 메서드를 호출해야 합니다. 활동 컨텍스트에서는 활동의 onStart() 메서드에서 connect()를 호출하고 활동의 onStop() 메서드에서 disconnect()을 호출하는 것이 가장 좋습니다. 자동 관리 연결을 사용할 때 connect()disconnect() 메서드가 자동으로 호출됩니다.

GoogleApiClient를 사용하여 인증이 필요한 API(예: Google Drive 또는 Google Play 게임즈)에 연결하는 경우 첫 번째 연결 시도가 실패하고 앱은 사용자 계정이 지정되지 않았기 때문에 SIGN_IN_REQUIRED 오류와 함께 onConnectionFailed() 호출을 수신할 가능성이 높습니다.

연결 실패 처리

앱이 onConnectionFailed() 콜백 호출을 수신하면 제공된 ConnectionResult 객체에서 hasResolution()를 호출해야 합니다. true를 반환하면 앱은 ConnectionResult 객체에서 startResolutionForResult()을 호출하여 사용자에게 오류 해결을 위한 즉각적인 조치를 취하도록 요청할 수 있습니다. 이 상황에서 startResolutionForResult() 메서드는 startActivityForResult()와 동일하게 동작하며 사용자가 오류를 해결하는 데 도움이 되는 컨텍스트에 적절한 활동 (예: 사용자가 계정을 선택하도록 돕는 활동)을 실행합니다.

hasResolution()가 false를 반환하면 앱은 GoogleApiAvailability.getErrorDialog()를 호출하여 이 메서드에 오류 코드를 전달해야 합니다. 이렇게 하면 오류에 적합한 Google Play 서비스에서 제공하는 Dialog가 반환됩니다. 대화상자는 단순히 오류를 설명하는 메시지를 제공하거나, 오류를 해결할 수 있는 활동을 실행하는 작업을 제공할 수도 있습니다(예: 사용자가 최신 버전의 Google Play 서비스를 설치해야 하는 경우).

예를 들어 이제 onConnectionFailed() 콜백 메서드가 다음과 같이 표시됩니다.

public class MyActivity extends Activity
        implements ConnectionCallbacks, OnConnectionFailedListener {

    // Request code to use when launching the resolution activity
    private static final int REQUEST_RESOLVE_ERROR = 1001;
    // Unique tag for the error dialog fragment
    private static final String DIALOG_ERROR = "dialog_error";
    // Bool to track whether the app is already resolving an error
    private boolean mResolvingError = false;

    // ...

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        if (mResolvingError) {
            // Already attempting to resolve an error.
            return;
        } else if (result.hasResolution()) {
            try {
                mResolvingError = true;
                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
            } catch (SendIntentException e) {
                // There was an error with the resolution intent. Try again.
                mGoogleApiClient.connect();
            }
        } else {
            // Show dialog using GoogleApiAvailability.getErrorDialog()
            showErrorDialog(result.getErrorCode());
            mResolvingError = true;
        }
    }

    // The rest of this code is all about building the error dialog

    /* Creates a dialog for an error message */
    private void showErrorDialog(int errorCode) {
        // Create a fragment for the error dialog
        ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
        // Pass the error that should be displayed
        Bundle args = new Bundle();
        args.putInt(DIALOG_ERROR, errorCode);
        dialogFragment.setArguments(args);
        dialogFragment.show(getSupportFragmentManager(), "errordialog");
    }

    /* Called from ErrorDialogFragment when the dialog is dismissed. */
    public void onDialogDismissed() {
        mResolvingError = false;
    }

    /* A fragment to display an error dialog */
    public static class ErrorDialogFragment extends DialogFragment {
        public ErrorDialogFragment() { }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Get the error code and retrieve the appropriate dialog
            int errorCode = this.getArguments().getInt(DIALOG_ERROR);
            return GoogleApiAvailability.getInstance().getErrorDialog(
                    this.getActivity(), errorCode, REQUEST_RESOLVE_ERROR);
        }

        @Override
        public void onDismiss(DialogInterface dialog) {
            ((MyActivity) getActivity()).onDialogDismissed();
        }
    }
}

사용자가 startResolutionForResult()에서 제공하는 대화상자를 완료하거나 GoogleApiAvailability.getErrorDialog()에서 제공한 메시지를 닫으면 활동은 RESULT_OK 결과 코드와 함께 onActivityResult() 콜백을 수신합니다. 그러면 앱에서 connect()를 다시 호출할 수 있습니다. 예를 들면 다음과 같습니다.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_RESOLVE_ERROR) {
        mResolvingError = false;
        if (resultCode == RESULT_OK) {
            // Make sure the app is not already connected or attempting to connect
            if (!mGoogleApiClient.isConnecting() &&
                    !mGoogleApiClient.isConnected()) {
                mGoogleApiClient.connect();
            }
        }
    }
}

위 코드에서는 불리언 mResolvingError를 확인했을 것입니다. 이렇게 하면 사용자가 오류를 해결하는 동안 앱 상태를 추적하여 동일한 오류를 해결하려는 반복적인 시도를 방지할 수 있습니다. 예를 들어 사용자가 SIGN_IN_REQUIRED 오류를 해결하는 데 도움이 되도록 계정 선택 도구 대화상자가 표시되는 동안 사용자가 화면을 회전할 수 있습니다. 이렇게 하면 활동이 다시 생성되고 onStart() 메서드가 다시 호출된 후 connect()가 다시 호출됩니다. 이렇게 하면 startResolutionForResult()가 다시 호출되어 기존 계정 선택 도구 앞에 다른 계정 선택 도구 대화상자가 생성됩니다.

이 불리언은 활동 인스턴스 간에 지속되는 경우에만 의도된 용도로 사용됩니다. 다음 섹션에서는 기기에서 발생하는 다른 사용자 작업이나 이벤트에도 불구하고 앱의 오류 처리 상태를 유지하는 방법을 설명합니다.

오류를 해결하는 동안 상태 유지

오류 해결을 위한 이전 시도가 진행되는 동안 onConnectionFailed()에서 코드가 실행되지 않도록 하려면 앱에서 이미 오류를 해결하려고 하는지 여부를 추적하는 불리언 값을 유지해야 합니다.

위의 코드 예와 같이 앱은 startResolutionForResult()를 호출하거나 GoogleApiAvailability.getErrorDialog()에서 대화상자를 표시할 때마다 불리언을 true로 설정해야 합니다. 그런 다음 앱이 onActivityResult() 콜백에서 RESULT_OK를 수신하면 불리언을 false로 설정합니다.

활동이 다시 시작될 때 (예: 사용자가 화면을 회전할 때) 불리언을 추적하려면 onSaveInstanceState()를 사용하여 활동의 저장된 인스턴스 데이터에 불리언을 저장합니다.

private static final String STATE_RESOLVING_ERROR = "resolving_error";

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(STATE_RESOLVING_ERROR, mResolvingError);
}

그런 다음 onCreate() 중에 저장된 상태를 복구합니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...
    mResolvingError = savedInstanceState != null
            && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false);
}

이제 앱을 안전하게 실행하고 Google Play 서비스에 수동으로 연결할 준비가 되었습니다.