Truy cập vào các API của Google bằng GoogleApiClient (không dùng nữa)

Bạn có thể sử dụng đối tượng GoogleApiClient ("Ứng dụng API Google") để truy cập vào các API của Google có trong thư viện Dịch vụ Google Play (chẳng hạn như Đăng nhập bằng Google, Trò chơi và Drive). Ứng dụng API của Google cung cấp một điểm truy cập chung đến Dịch vụ Google Play và quản lý kết nối mạng giữa thiết bị của người dùng và từng dịch vụ của Google.

Tuy nhiên, giao diện GoogleApi mới hơn cùng với các phương thức triển khai giao diện này dễ sử dụng hơn và là cách ưu tiên để truy cập vào các API của Dịch vụ Play. Xem bài viết Truy cập vào các API của Google.

Hướng dẫn này sẽ chỉ cho bạn cách:

  • Tự động quản lý kết nối của bạn với Dịch vụ Google Play.
  • Thực hiện các lệnh gọi API đồng bộ và không đồng bộ tới bất kỳ Dịch vụ Google Play nào.
  • Quản lý thủ công việc kết nối của bạn với Dịch vụ Google Play trong một số ít trường hợp cần thiết. Để tìm hiểu thêm, hãy xem bài viết Kết nối được quản lý theo cách thủ công.
Hình 1: Hình minh hoạ cách Ứng dụng API Google cung cấp một giao diện để kết nối và thực hiện lệnh gọi đến bất kỳ dịch vụ nào hiện có của Google Play, chẳng hạn như Google Play Games và Google Drive.

Để bắt đầu, trước tiên, bạn phải cài đặt thư viện Dịch vụ Google Play (bản sửa đổi 15 trở lên) cho SDK Android của bạn. Nếu bạn chưa làm như vậy, hãy làm theo hướng dẫn trong bài viết Thiết lập SDK Dịch vụ Google Play.

Bắt đầu kết nối được quản lý tự động

Sau khi dự án của bạn được liên kết với thư viện Dịch vụ Google Play, hãy tạo một thực thể của GoogleApiClient bằng cách sử dụng các API GoogleApiClient.Builder trong phương thức onCreate() của hoạt động. Lớp GoogleApiClient.Builder cung cấp các phương thức cho phép bạn chỉ định API Google mà bạn muốn sử dụng và phạm vi OAuth 2.0 mong muốn. Dưới đây là mã ví dụ giúp tạo một thực thể GoogleApiClient kết nối với dịch vụ Google Drive:

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

Bạn có thể thêm nhiều API và nhiều phạm vi vào cùng một GoogleApiClient bằng cách thêm các lệnh gọi khác vào addApi()addScope().

Lưu ý quan trọng: Nếu đang thêm API Wearable cùng với các API khác vào GoogleApiClient, bạn có thể gặp lỗi kết nối máy khách trên những thiết bị không cài đặt ứng dụng Wear OS. Để tránh lỗi kết nối, hãy gọi phương thức addApiIfAvailable() và truyền vào API Wearable để cho phép ứng dụng của bạn xử lý linh hoạt API bị thiếu. Để biết thêm thông tin, hãy xem bài viết Truy cập API cho thiết bị đeo.

Để bắt đầu kết nối được quản lý tự động, bạn phải chỉ định phương thức triển khai cho giao diện OnConnectionFailedListener để nhận các lỗi kết nối không thể giải quyết. Khi thực thể GoogleApiClient được quản lý tự động cố gắng kết nối với các API của Google, thực thể đó sẽ tự động hiển thị giao diện người dùng để cố gắng khắc phục mọi lỗi kết nối có thể giải quyết (ví dụ: nếu cần cập nhật Dịch vụ Google Play). Nếu xảy ra lỗi và không thể giải quyết, bạn sẽ nhận được lệnh gọi đến onConnectionFailed().

Bạn cũng có thể chỉ định phương thức triển khai không bắt buộc cho giao diện ConnectionCallbacks nếu ứng dụng của bạn cần biết thời điểm thiết lập hoặc tạm ngưng kết nối được quản lý tự động. Ví dụ: nếu ứng dụng của bạn thực hiện lệnh gọi để ghi dữ liệu vào các API của Google, thì các API này chỉ nên được gọi sau khi phương thức onConnected() được gọi.

Dưới đây là một hoạt động mẫu triển khai giao diện gọi lại và thêm các giao diện đó vào Ứng dụng API của Google:

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

        // ...
    }
}

Thực thể GoogleApiClient sẽ tự động kết nối sau khi hoạt động của bạn gọi onStart() và ngắt kết nối sau khi gọi onStop(). Ứng dụng của bạn có thể bắt đầu gửi yêu cầu đọc ngay cho các API của Google sau khi tạo GoogleApiClient mà không cần chờ kết nối hoàn tất.

Kết nối với các dịch vụ của Google

Sau khi kết nối, ứng dụng của bạn có thể thực hiện các lệnh gọi đọc và ghi bằng API dành riêng cho dịch vụ mà ứng dụng được uỷ quyền, như được chỉ định bởi các API và phạm vi mà bạn đã thêm vào thực thể GoogleApiClient.

Lưu ý: Trước khi thực hiện lệnh gọi đến các dịch vụ cụ thể của Google, trước tiên, bạn có thể phải đăng ký ứng dụng của mình trong Google Developer Console. Để biết hướng dẫn, hãy tham khảo hướng dẫn bắt đầu sử dụng phù hợp cho API mà bạn đang sử dụng, chẳng hạn như Google Drive hoặc Đăng nhập bằng Google.

Khi thực hiện yêu cầu đọc hoặc ghi bằng GoogleApiClient, ứng dụng API sẽ trả về đối tượng PendingResult đại diện cho yêu cầu đó. Điều này xảy ra ngay lập tức, trước khi yêu cầu được gửi đến dịch vụ của Google mà ứng dụng của bạn đang gọi.

Ví dụ: dưới đây là yêu cầu đọc tệp trên Google Drive cung cấp đối tượng PendingResult:

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

Sau khi ứng dụng của bạn có một đối tượng PendingResult, ứng dụng có thể chỉ định xem yêu cầu sẽ được xử lý dưới dạng lệnh gọi không đồng bộ hay lệnh gọi đồng bộ.

Lưu ý: Ứng dụng của bạn có thể thêm các yêu cầu đọc vào hàng đợi trong khi không kết nối với Dịch vụ Google Play. Ví dụ: ứng dụng của bạn có thể gọi các phương thức để đọc một tệp trên Google Drive, bất kể thực thể GoogleApiClient của bạn đã được kết nối hay chưa. Sau khi thiết lập kết nối, các yêu cầu đọc trong hàng đợi sẽ thực thi. Các yêu cầu ghi sẽ tạo ra lỗi nếu ứng dụng của bạn gọi các phương thức ghi của Dịch vụ Google Play trong khi Ứng dụng API của Google không được kết nối.

Sử dụng lệnh gọi không đồng bộ

Để làm cho yêu cầu không đồng bộ, hãy gọi setResultCallback() trên PendingResult và cung cấp phương thức triển khai giao diện ResultCallback. Ví dụ: dưới đây là yêu cầu được thực thi không đồng bộ:

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.
            // ...
        }
    });
}

Khi ứng dụng của bạn nhận được một đối tượng Result trong lệnh gọi lại onResult(), đối tượng đó sẽ được phân phối dưới dạng một thực thể của lớp con thích hợp do API bạn đang sử dụng chỉ định, chẳng hạn như DriveApi.MetadataBufferResult.

Sử dụng lệnh gọi đồng bộ

Nếu muốn mã thực thi theo một thứ tự được xác định nghiêm ngặt, có thể do kết quả của một lệnh gọi cần làm đối số cho một lệnh gọi khác, bạn có thể đồng bộ hoá yêu cầu của mình bằng cách gọi await() trên PendingResult. Thao tác này sẽ chặn luồng và trả về đối tượng Result khi yêu cầu hoàn tất. Đối tượng này được phân phối dưới dạng một thực thể của lớp con thích hợp do API mà bạn sử dụng chỉ định, ví dụ: DriveApi.MetadataBufferResult.

Do lệnh gọi await() sẽ chặn luồng cho đến khi có kết quả, nên ứng dụng của bạn không được thực hiện các yêu cầu đồng bộ đến API của Google trên luồng giao diện người dùng. Ứng dụng của bạn có thể tạo một luồng mới bằng đối tượng AsyncTask và dùng luồng đó để thực hiện yêu cầu đồng bộ.

Ví dụ sau đây trình bày cách gửi một yêu cầu tệp tới Google Drive dưới dạng lệnh gọi đồng bộ:

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
        // ...
    }
}

Truy cập vào API cho thiết bị đeo

API Thiết bị đeo cung cấp kênh giao tiếp cho các ứng dụng chạy trên thiết bị cầm tay và thiết bị đeo. API này bao gồm một tập hợp các đối tượng dữ liệu mà hệ thống có thể gửi và đồng bộ hoá, cũng như các trình nghe thông báo cho ứng dụng về các sự kiện quan trọng bằng cách sử dụng lớp dữ liệu. API thiết bị đeo có sẵn trên các thiết bị chạy Android 4.3 (API cấp 18) trở lên khi một thiết bị đeo được kết nối và ứng dụng đồng hành Wear OS được cài đặt trên thiết bị.

Sử dụng API Wearable độc lập

Nếu ứng dụng của bạn sử dụng API cho thiết bị đeo mà không sử dụng các API khác của Google, thì bạn có thể thêm API này bằng cách gọi phương thức addApi(). Ví dụ sau cho biết cách thêm API cho thiết bị đeo vào thực thể GoogleApiClient:

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

Trong trường hợp không có API cho thiết bị đeo, các yêu cầu kết nối chứa API cho thiết bị đeo sẽ không thành công kèm theo mã lỗi API_UNAVAILABLE.

Ví dụ sau đây cho biết cách xác định xem API cho thiết bị đeo có hoạt động hay không:

// 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
    }
    // ...
}

Sử dụng API cho thiết bị đeo với các API khác của Google

Nếu ứng dụng của bạn dùng API cho thiết bị đeo cùng với các API khác của Google, hãy gọi phương thức addApiIfAvailable() rồi truyền API cho thiết bị đeo để kiểm tra xem có API đó hay không. Bạn có thể dựa vào quy trình kiểm tra này để giúp ứng dụng xử lý linh hoạt các trường hợp không có API.

Ví dụ sau cho biết cách truy cập vào API Thiết bị đeo cùng với API Drive:

// 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();

Trong ví dụ trên, GoogleApiClient có thể kết nối thành công với Google Drive mà không cần kết nối với API Thiết bị đeo nếu không có sẵn. Sau khi bạn kết nối thực thể GoogleApiClient, hãy đảm bảo rằng API cho thiết bị đeo đã có sẵn trước khi thực hiện lệnh gọi API:

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

Bỏ qua các lỗi kết nối API

Nếu bạn gọi addApi()GoogleApiClient không thể kết nối thành công với API đó, thì toàn bộ thao tác kết nối cho ứng dụng đó sẽ không thành công và kích hoạt lệnh gọi lại onConnectionFailed().

Bạn có thể đăng ký một kết nối API bị lỗi để được bỏ qua bằng cách sử dụng addApiIfAvailable(). Nếu một API được thêm bằng addApiIfAvailable() không kết nối được do lỗi không thể khôi phục (như API_UNAVAILABLE cho Wear), thì API đó sẽ bị loại bỏ khỏi GoogleApiClient và ứng dụng sẽ tiếp tục kết nối với các API khác. Tuy nhiên, nếu có kết nối API không thành công kèm theo lỗi có thể khôi phục (như ý định giải quyết sự đồng ý của OAuth), thì thao tác kết nối ứng dụng sẽ không hoạt động. Khi sử dụng kết nối được quản lý tự động, GoogleApiClient sẽ cố gắng giải quyết các lỗi đó khi có thể. Khi sử dụng kết nối được quản lý thủ công, ConnectionResult chứa ý định phân giải sẽ được gửi đến lệnh gọi lại onConnectionFailed(). Các lỗi kết nối API chỉ bị bỏ qua nếu không có cách giải quyết cho lỗi và API đã được thêm bằng addApiIfAvailable(). Để tìm hiểu cách triển khai quá trình xử lý lỗi kết nối theo cách thủ công, hãy xem bài viết Xử lý lỗi kết nối.

Vì các API được thêm bằng addApiIfAvailable() không phải lúc nào cũng hiện diện trong thực thể GoogleApiClient đã kết nối, nên bạn cần bảo vệ các lệnh gọi đến những API này bằng cách thêm quy trình kiểm tra thông qua hasConnectedApi(). Để tìm hiểu lý do tại sao một API cụ thể không kết nối được trong khi toàn bộ thao tác kết nối thành công với ứng dụng, hãy gọi getConnectionResult() và nhận mã lỗi từ đối tượng ConnectionResult. Nếu ứng dụng gọi một API khi API này không được kết nối với ứng dụng, thì lệnh gọi sẽ không thực hiện được bằng mã trạng thái API_NOT_AVAILABLE.

Nếu API bạn đang thêm thông qua addApiIfAvailable() yêu cầu một hoặc nhiều phạm vi, hãy thêm các phạm vi đó làm tham số trong lệnh gọi phương thức addApiIfAvailable() thay vì sử dụng phương thức addScope(). Bạn có thể không yêu cầu các phạm vi được thêm bằng phương pháp này nếu kết nối API không thành công trước khi nhận được sự đồng ý OAuth, trong khi các phạm vi được thêm bằng addScope() sẽ luôn được yêu cầu.

Kết nối được quản lý theo cách thủ công

Phần lớn hướng dẫn này cho bạn biết cách sử dụng phương thức enableAutoManage để bắt đầu một kết nối được quản lý tự động với các lỗi được giải quyết tự động. Trong hầu hết trường hợp, đây là cách tốt nhất và dễ dàng nhất để kết nối với các API của Google từ ứng dụng Android. Tuy nhiên, có một số trường hợp bạn cần sử dụng kết nối được quản lý thủ công với các API của Google trong ứng dụng của mình:

  • Để truy cập vào các API của Google bên ngoài một hoạt động hoặc duy trì quyền kiểm soát kết nối API
  • Để tuỳ chỉnh hoạt động xử lý và giải quyết lỗi kết nối

Phần này đưa ra ví dụ về những trường hợp này và các trường hợp sử dụng nâng cao khác.

Bắt đầu kết nối được quản lý theo cách thủ công

Để bắt đầu kết nối được quản lý theo cách thủ công với GoogleApiClient, bạn phải chỉ định phương thức triển khai cho giao diện gọi lại, ConnectionCallbacksOnConnectionFailedListener. Các giao diện này nhận được lệnh gọi lại để phản hồi phương thức connect() không đồng bộ khi kết nối thành công, không thành công hoặc bị tạm ngưng với Dịch vụ Google Play.

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

Khi quản lý kết nối theo cách thủ công, bạn sẽ cần gọi phương thức connect()disconnect() ở các điểm phù hợp trong vòng đời của ứng dụng. Trong ngữ cảnh hoạt động, cách tốt nhất là gọi connect() trong phương thức onStart() của hoạt động và disconnect() trong phương thức onStop() của hoạt động. Phương thức connect()disconnect() sẽ tự động được gọi khi sử dụng kết nối được quản lý tự động.

Nếu bạn đang sử dụng GoogleApiClient để kết nối với các API yêu cầu xác thực, chẳng hạn như Google Drive hoặc Google Play Games, thì rất có thể lần kết nối đầu tiên của bạn sẽ không thành công và ứng dụng của bạn sẽ nhận được lệnh gọi đến onConnectionFailed() kèm theo lỗi SIGN_IN_REQUIRED do tài khoản người dùng không được chỉ định.

Xử lý lỗi kết nối

Khi ứng dụng của bạn nhận được lệnh gọi đến lệnh gọi lại onConnectionFailed(), bạn nên gọi hasResolution() trên đối tượng ConnectionResult đã cung cấp. Nếu giá trị trả về là giá trị true, ứng dụng của bạn có thể yêu cầu người dùng tiến hành xử lý lỗi ngay lập tức bằng cách gọi startResolutionForResult() trên đối tượng ConnectionResult. Phương thức startResolutionForResult() sẽ hoạt động giống như startActivityForResult() trong trường hợp này và khởi chạy một hoạt động phù hợp với ngữ cảnh giúp người dùng giải quyết lỗi (chẳng hạn như một hoạt động giúp người dùng chọn tài khoản).

Nếu hasResolution() trả về giá trị false, ứng dụng của bạn phải gọi GoogleApiAvailability.getErrorDialog(), truyền mã lỗi vào phương thức này. Thao tác này sẽ trả về Dialog do các dịch vụ của Google Play cung cấp phù hợp với lỗi. Hộp thoại này có thể chỉ cung cấp thông báo giải thích lỗi, hoặc cũng có thể đưa ra thao tác để khởi chạy một hoạt động có thể khắc phục lỗi (chẳng hạn như khi người dùng cần cài đặt phiên bản mới hơn của Dịch vụ Google Play).

Ví dụ: phương thức gọi lại onConnectionFailed() của bạn giờ đây sẽ có dạng như sau:

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();
        }
    }
}

Sau khi người dùng hoàn tất hộp thoại do startResolutionForResult() cung cấp hoặc đóng thông báo do GoogleApiAvailability.getErrorDialog() cung cấp, hoạt động của bạn sẽ nhận được lệnh gọi lại onActivityResult() kèm theo mã kết quả RESULT_OK. Sau đó, ứng dụng của bạn có thể gọi lại hàm connect(). Ví dụ:

@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();
            }
        }
    }
}

Trong mã ở trên, bạn có thể nhận thấy boolean mResolvingError. Tính năng này theo dõi trạng thái ứng dụng trong khi người dùng đang giải quyết lỗi để tránh lặp đi lặp lại các lần giải quyết cùng một lỗi. Ví dụ: mặc dù hộp thoại bộ chọn tài khoản sẽ hiển thị để giúp người dùng giải quyết lỗi SIGN_IN_REQUIRED, nhưng người dùng có thể xoay màn hình. Thao tác này sẽ tạo lại hoạt động và khiến phương thức onStart() được gọi lại, sau đó sẽ gọi lại connect(). Khi đó, một lệnh gọi khác đến startResolutionForResult() sẽ tạo ra một hộp thoại bộ chọn tài khoản khác ở phía trước hộp thoại hiện có.

Boolean này chỉ phục vụ đúng mục đích khi nó vẫn tồn tại trên các thực thể hoạt động. Phần tiếp theo giải thích cách duy trì trạng thái xử lý lỗi của ứng dụng bất kể các thao tác hoặc sự kiện khác của người dùng xảy ra trên thiết bị.

Duy trì trạng thái trong khi giải quyết lỗi

Để tránh thực thi mã trong onConnectionFailed() khi đang cố gắng giải quyết lỗi trước đó, bạn cần giữ lại một giá trị boolean theo dõi liệu ứng dụng của bạn có đang cố gắng giải quyết lỗi hay không.

Như trong mã ví dụ ở trên, ứng dụng của bạn phải đặt giá trị boolean thành true mỗi khi gọi startResolutionForResult() hoặc hiển thị hộp thoại từ GoogleApiAvailability.getErrorDialog(). Sau đó, khi ứng dụng của bạn nhận được RESULT_OK trong lệnh gọi lại onActivityResult(), hãy đặt giá trị boolean thành false.

Để theo dõi boolean khi hoạt động khởi động lại (chẳng hạn như khi người dùng xoay màn hình), hãy lưu boolean đó trong dữ liệu thực thể đã lưu của hoạt động bằng cách sử dụng 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);
}

Sau đó, hãy khôi phục trạng thái đã lưu trong onCreate():

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

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

Giờ đây, bạn đã có thể chạy ứng dụng một cách an toàn và kết nối với các Dịch vụ Google Play theo cách thủ công.