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와 여러 범위를 추가할 수 있습니다.

중요: GoogleApiClient에 다른 API와 함께 Wearable API를 추가하는 경우 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에서 앱을 등록해야 할 수 있습니다. 자세한 내용은 사용 중인 API(예: Google Drive 또는 Google 로그인)에 관한 적절한 시작 가이드를 참고하세요.

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

예를 들어 다음은 Google Drive에서 PendingResult 객체를 제공하는 파일을 읽으라는 요청입니다.

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<String, Void, Void> {
    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 독립형 사용

앱이 다른 Google API는 사용하지 않고 Wearable 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();

위 예에서 GoogleApiClientWearable API를 사용할 수 없는 경우 Wearable API에 연결하지 않고도 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를 사용하여 Google Drive 또는 Google Play 게임즈와 같이 인증이 필요한 API에 연결하는 경우 첫 번째 연결 시도가 실패하고 사용자 계정이 지정되지 않아 앱에 onConnectionFailed() 호출이 SIGN_IN_REQUIRED 오류와 함께 수신될 가능성이 큽니다.

연결 실패 처리

앱이 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 서비스에 수동으로 연결할 수 있습니다.