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

GoogleApiClient ('Google API 클라이언트') 객체를 사용하여 Google Play 서비스 라이브러리(예: Google 로그인, 게임, 드라이브)에서 제공되는 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에 연결을 시도하면 해결 가능한 연결 실패를 해결하려고 UI를 자동으로 표시합니다 (예: Google Play 서비스를 업데이트해야 하는 경우). 해결할 수 없는 오류가 발생하면 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

        // ...
    }
}

활동에서 onStart()를 호출하면 GoogleApiClient 인스턴스가 자동으로 연결되고 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();

위의 예에서 GoogleApiClient를 사용할 수 없는 경우 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와의 연결을 진행합니다. 그러나 API 연결이 복구 가능한 오류 (예: OAuth 동의 해결 인텐트)와 함께 실패하면 클라이언트 연결 작업이 실패합니다. 자동 관리형 연결을 사용할 때 GoogleApiClient는 가능한 경우 이러한 오류 해결을 시도합니다. 수동으로 관리 연결을 사용하면 해결 인텐트가 포함된 ConnectionResultonConnectionFailed() 콜백에 전달됩니다. API 연결 실패는 실패에 대한 해결 방법이 없고 API가 addApiIfAvailable()와 함께 추가된 경우에만 무시됩니다. 수동 연결 실패 처리를 구현하는 방법은 연결 실패 처리를 참고하세요.

addApiIfAvailable()로 추가된 API가 연결된 GoogleApiClient 인스턴스에 항상 있는 것은 아니기 때문에 hasConnectedApi()를 사용하여 검사를 추가하여 이러한 API 호출을 보호해야 합니다. 클라이언트에서 전체 연결 작업이 성공했을 때 특정 API의 연결에 실패한 이유를 알아보려면 getConnectionResult()를 호출하고 ConnectionResult 객체에서 오류 코드를 가져옵니다. 클라이언트가 API가 클라이언트에 연결되어 있지 않을 때 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에 연결하는 경우 첫 번째 연결 시도는 실패하고 사용자 계정이 지정되지 않았으므로 앱이 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 서비스에 수동으로 연결할 수 있습니다.