访问 Google API

当您想要调用由 Google Play 服务提供支持的 SDK(例如 Google 登录或 ML Kit)中的某个 API 时,您需要先创建 API 客户端对象的实例。这些对象会自动管理与 Google Play 服务的连接。连接可用时,每个 API 客户端对象都会按顺序执行请求。否则,客户端对象会将请求加入队列。除非文档另有说明,否则客户端对象的构建成本很低;您可以每次都创建新的 API 客户端来调用 API 方法。

本指南介绍了如何向由 Google Play 服务提供支持的任何 SDK 发出 API 调用,包括如何访问无需授权需要授权的服务。

开始使用

首先,在应用项目中添加必要的工具和依赖项,如设置 Google Play 服务指南中所述。

无需授权时的访问

如需访问不需要 API 授权的服务,请获取服务的客户端对象的实例,并将当前 Context 或当前 Activity 传递给该实例。在执行任何 API 调用之前,系统会提示用户根据需要升级 Google Play 服务。

例如,如需使用 Android 专用 Fused Location Provider 获取设备的最后已知位置,请添加以下代码段中显示的逻辑:

Kotlin

// Code required for requesting location permissions omitted for brevity.
val client = LocationServices.getFusedLocationProviderClient(this)

// Get the last known location. In some rare situations, this can be null.
client.lastLocation.addOnSuccessListener { location : Location? ->
    location?.let {
        // Logic to handle location object.
    }
}

Java

// Code required for requesting location permissions omitted for brevity.
FusedLocationProviderClient client =
        LocationServices.getFusedLocationProviderClient(this);

// Get the last known location. In some rare situations, this can be null.
client.getLastLocation()
        .addOnSuccessListener(this, location -> {
            if (location != null) {
                // Logic to handle location object.
            }
        });

需要授权时的访问

如需访问需要用户授权的服务,请完成以下步骤:

  1. 登录用户
  2. 请求访问服务所需范围的权限
  3. 获取服务的客户端对象的实例,并将用户的 GoogleSignInAccount 对象以及 ContextActivity 对象传递给该实例。

以下示例实现了使用 Google Fit API 读取用户的每日步数。如需在完整项目上下文中查看类似的实现,请参阅 GitHub 上的 BasicHistoryApiKotlin 应用的主要 activity。

Kotlin

class FitFragment : Fragment() {
    private val fitnessOptions: FitnessOptions by lazy {
        FitnessOptions.builder()
            .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
            .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
            .build()
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        fitSignIn()
    }

    /*
     * Checks whether the user is signed in. If so, executes the specified
     * function. If the user is not signed in, initiates the sign-in flow,
     * specifying the function to execute after the user signs in.
     */
    private fun fitSignIn() {
        if (oAuthPermissionsApproved()) {
            readDailySteps()
        } else {
            GoogleSignIn.requestPermissions(
                this,
                SIGN_IN_REQUEST_CODE,
                getGoogleAccount(),
                fitnessOptions
            )
        }
    }

    private fun oAuthPermissionsApproved() =
        GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)

    /*
     * Gets a Google account for use in creating the fitness client. This is
     * achieved by either using the last signed-in account, or if necessary,
     * prompting the user to sign in. It's better to use the
     * getAccountForExtension() method instead of the getLastSignedInAccount()
     * method because the latter can return null if there has been no sign in
     * before.
     */
    private fun getGoogleAccount(): GoogleSignInAccount =
        GoogleSignIn.getAccountForExtension(requireContext(), fitnessOptions)

    /*
     * Handles the callback from the OAuth sign in flow, executing the function
     * after sign-in is complete.
     */
    override fun onActivityResult(
            requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (resultCode) {
            RESULT_OK -> {
                readDailySteps()
            }
            else -> {
                // Handle error.
            }
        }
    }

    /*
     * Reads the current daily step total.
     */
    private fun readDailySteps() {
        Fitness.getHistoryClient(requireContext(), getGoogleAccount())
            .readDailyTotal(DataType.TYPE_STEP_COUNT_DELTA)
            .addOnSuccessListener { dataSet ->
                val total = when {
                    dataSet.isEmpty -> 0
                    else -> dataSet.dataPoints.first()
                            .getValue(Field.FIELD_STEPS).asInt()
                }

                Log.i(TAG, "Total steps: $total")
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "There was a problem getting the step count.", e)
            }
    }

    companion object {
        const val SIGN_IN_REQUEST_CODE = 1001
    }
}

Java

public class FitFragment extends Fragment {
    private final FitnessOptions fitnessOptions = FitnessOptions.builder()
            .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
            .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
            .build();

    @Override
    public void onViewCreated(
            @NotNull View view, @Nullable Bundle savedInstanceState) {
        fitSignIn();
    }

    /*
     * Checks whether the user is signed in. If so, executes the specified
     * function. If the user is not signed in, initiates the sign-in flow,
     * specifying the function to execute after the user signs in.
     */
    private void fitSignIn() {
        if (oAuthPermissionsApproved()) {
            readDailySteps();
        } else {
            GoogleSignIn.requestPermissions(this, SIGN_IN_REQUEST_CODE,
                    getGoogleAccount(), fitnessOptions);
        }
    }

    private boolean oAuthPermissionsApproved() {
        return GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions);
    }

    /*
     * Gets a Google account for use in creating the fitness client. This is
     * achieved by either using the last signed-in account, or if necessary,
     * prompting the user to sign in. It's better to use the
     * getAccountForExtension() method instead of the getLastSignedInAccount()
     * method because the latter can return null if there has been no sign in
     * before.
     */
    private GoogleSignInAccount getGoogleAccount() {
        return GoogleSignIn.getAccountForExtension(
                requireContext(), fitnessOptions);
    }

    /*
     * Handles the callback from the OAuth sign in flow, executing the function
     * after sign-in is complete.
     */
    @Override
    public void onActivityResult(
            int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            readDailySteps();
        } else {
            // Handle error.
        }
    }

    /*
     * Reads the current daily step total.
     */
    private void readDailySteps() {
        AtomicInteger total = new AtomicInteger();
        Fitness.getHistoryClient(requireContext(), getGoogleAccount())
                .readDailyTotal(DataType.TYPE_STEP_COUNT_DELTA)
                .addOnSuccessListener(dataSet -> {
                    if (!dataSet.isEmpty())
                        total.set(Integer.parseInt(dataSet.getDataPoints()
                                .get(0).getValue(FIELD_STEPS).toString()));
                        Log.i(TAG, "Total steps: $total");
                })
                .addOnFailureListener(e -> {
                    Log.w(TAG, "There was a problem getting the step count.", e);
                });
    }

    private static final int SIGN_IN_REQUEST_CODE = 1001;
}

检查 API 的可用性

在应用中启用依赖于 Google Play 服务 API 的功能之前,请添加检查设备上 API 可用性的代码。为此,请调用 checkApiAvailability()

以下代码段演示了如何检查融合位置信息提供程序的可用性。

Kotlin

fun getLastLocationIfApiAvailable(context: Context?): Task<Location>? {
    val client = getFusedLocationProviderClient(context)
    return GoogleApiAvailability.getInstance()
        .checkApiAvailability(client)
        .onSuccessTask { _ -> client.lastLocation }
        .addOnFailureListener { _ -> Log.d(TAG, "Location unavailable.")}
}

Java

public Task<Location> getLastLocationIfApiAvailable(Context context) {
    FusedLocationProviderClient client =
            getFusedLocationProviderClient(context);
    return GoogleApiAvailability.getInstance()
            .checkApiAvailability(client)
            .onSuccessTask(unused -> client.getLastLocation())
            .addOnFailureListener(e -> Log.d(TAG, "Location unavailable."));
}