无法访问 GMS

GMS(Google Mobile Services)为 Android 应用提供了基于云服务的 API,包括广告、游戏、地图、视觉图像、身份验证、电子钱包、App 分析等等很多功能,GMS API 被国际化的 App 广泛使用。GMS 不是 AOSP(Android Open Source Project)的一部分,只存在于通过授权的设备里,如果设备没有 GMS 那么 App 运行时可能会像下面这样。

在没有 GMS 的设备上运行使用了 GMS API 的 App

上图左边是一开源 App,它调用了 GMS 的 Vision 功能来扫描二维码(稍后会详细分析)。右边则是一个知名网站(Quora)的 Android 客户端,它在打开时即弹出这样的对话框,估计是调用了 GMS 的身份验证功能。当然,应该有更多的国际化 App 在没有 GMS 的设备上运行会弹出类似的提示,我就不去尝试了。

接下来看那个开源 App(shadowsocks-android)是如何调用 GMS API 的,我是点击菜单的“Scan QR Code”弹出提示的,追踪代码进入ScannerActivity.kt,在开头处可以看到不少 GMS API 的 import,开源组件 xyz.belvi.mobilevisionbarcodescanner.BarcodeRetriever 也依赖于 GMS 功能。

...
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.samples.vision.barcodereader.BarcodeCapture
import com.google.android.gms.samples.vision.barcodereader.BarcodeGraphic
import com.google.android.gms.vision.Frame
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
import xyz.belvi.mobilevisionbarcodescanner.BarcodeRetriever

跳到 OnCreate 处(第 70 行)。这里会调用 GMS 功能创建 QRCode 扫描器,后面打开相机扫码。如果 API 是不可用则会显示一些信息(开头图片)然后进入 fallback(第 62 行),跳转到应用商店的一页面。

private fun fallback() {
    try {
        startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(
                "market://details?id=com.github.sumimakito.awesomeqrsample")))
    } catch (_: ActivityNotFoundException) { }
    finish()
}

...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    detector = BarcodeDetector.Builder(this)
            .setBarcodeFormats(Barcode.QR_CODE)
            .build()
    if (!detector.isOperational) {
        val availability = GoogleApiAvailability.getInstance()
        val dialog = availability.getErrorDialog(this, availability.isGooglePlayServicesAvailable(this),
                REQUEST_GOOGLE_API)
        if (dialog == null) {
            Toast.makeText(this, R.string.common_google_play_services_notification_ticker, Toast.LENGTH_SHORT)
                    .show()
            fallback()
        } else {
            dialog.setOnDismissListener { fallback() }
            dialog.show()
        }
        return
    }
...

逻辑很清晰,就是一 GMS 功能的使用,失败则会提示信息。国内手机很多没有 GMS,包括我手上这台手机也没有,这样就没法支持国际化的 App 正常运行。上面说的跳转到商店页面,国内手机系统都一般都要修改应用商店,不管是网页里打开 Google Play App 还是 App 内跳转都会跳到国内商店页面(系统半年没更新,不知道现在的情况),从而被完全的本土化了。

对于开源软件有另外一个办法就是把依赖于 GMS 的功能替换为本地模块,就是 App 直接集成相应能力不依赖于云端服务。像这个扫码功能,一般都是 zxing 实现的,集成 zxing 模块修改相应代码即可。而对于广泛的商业流行 App 来说没有 GMS 就比较麻烦,毕竟这些已经是全球应用生态系统的一部分。

App 集成扫码能力


2019-09-02