在项目中同时具备GMS和HMS

75

如何在应用中同时使用Google Mobile Services和华为移动服务?

考虑到华为已丧失了GMS的许可,似乎我们需要用华为提供的服务来替换应用中使用的所有GMS服务。一个最佳实践是什么?使用flavors并以某种方式单独处理每个类别,还是复制粘贴项目并开始替换?或者更好的方法是,是否有一种方式可以同时拥有这两种服务,并让应用根据设备决定使用哪个服务?显然,后者将导致APK文件大小的增加。

有什么想法?


这个有任何更新吗?最终你是怎么处理的?Firebase分析和/或Admob呢?这不违反Google/Admob的条款和条件吗?我问这个是因为我不想被Google Play Dev和Admob封禁。 - Zbarcea Christian
嗨。请查看我的批准答案。我回答了自己的问题。希望能帮到别人...干杯。 - AndreiBogdan
6个回答

61

所以,我是这样做的:

定义了两个风味

    gms {
        dimension "services"
        buildConfigField "String", "SERVICE_USED", '"g"'

    }
    hms {
        dimension "services"
        buildConfigField "String", "SERVICE_USED", '"h"'
    }

每当我需要决定执行某些操作时,我会在代码中使用"g"和"h",例如API需要deviceType是"android"或"iOS",而且随着华为构建的加入,我们定义了另一个常量"huawei"。我使用SERVICE_USED来确定要发送哪个常量。

接着我在build.gradle文件的顶部添加了以下内容:

apply plugin: 'com.android.application'
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
    //*meh*
} else {
    apply plugin: 'io.fabric'
}

由于我使用的是fabric(而fabric / firebase ...实际上无法与HMS一起使用),并且我也将其放在了build.gradle的最底部

if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
    apply plugin: 'com.huawei.agconnect'
} else {
    apply plugin: 'com.google.gms.google-services'
}

只包括适当的插件。

然后,我开始处理使用(地图、位置、推送通知、分析)的每个内容,通过创建一个包装器并在每个flavour中分离代码。例如,对于推送通知,我创建了,它有一个方法。我在两个flavour中定义相同的类和方法,但根据服务类型(gms或hms)实现它们。

在项目中包含依赖项时,我使用这种类型的注释:

//GMS stuff
gmsImplementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
gmsImplementation 'com.google.firebase:firebase-core:16.0.9'
gmsImplementation 'com.google.firebase:firebase-messaging:18.0.0'
gmsImplementation 'com.google.firebase:firebase-crash:16.2.1'
gmsImplementation 'com.google.android.gms:play-services-maps:16.1.0'
gmsImplementation 'com.google.android.gms:play-services-location:16.0.0'
gmsImplementation 'com.google.android.gms:play-services-tagmanager:16.0.8'

//HMS stuff
hmsImplementation 'com.huawei.agconnect:agconnect-core:1.0.0.300'
hmsImplementation 'com.huawei.hms:push:4.0.3.301'
hmsImplementation 'com.huawei.hms:maps:4.0.1.301'
hmsImplementation 'com.huawei.hms:location:4.0.3.303'

Implementation 前的 gmshms 是指味道的名称。只有在选择适当的 BuildVariant(即正在构建适当的 flavor )时,才会加载这些依赖项。

基本上,我为地图、分析、位置和推送通知两种情况下都封装了逻辑。以下是结构。没有什么特别的。

就是这样。他们创建 HMS 时基本上是逐个复制 GMS 的类和方法。你会看到确切的方法名称完全匹配,甚至包括调用参数和返回值。它们的相似度达到了99.99%。这使得事情更容易。基本上,你只需要将代码复制到两个类中,并导入正确的东西(在类的顶部)。你很少需要更改已经为 GMS 编写的代码。

希望对某些人有所帮助。

enter image description here


1
在我的情况下,我没有找到替代com.google.android.gms.common.api.ApiExceptioncom.google.android.gms.common.api.ResolvableApiException的方法,这些方法是实现对话框所需的,该对话框已知来自谷歌地图,以在应用程序中打开GPS(无需重定向到设置)https://www.google.com/search?q=google+play+services+request+turn+on+gps&source=lnms&tbm=isch 当我检查华为“Petal Maps”时,只有警告显示GPS未启用,没有对话框可以在应用程序中打开它。因此,我不认为99.99%的相似性是正确的;) - mikep
1
@mikep 在 HMS 中,您可以使用 com.huawei.hms.common.ApiException 和 com.huawei.hms.common.ResolvableApiException。 - emre
getGradle().getStartParameter().getTaskRequests().toString().contains("Hms") 对我来说返回false。 - Vahit Keskin

45

在回答您的问题之前,这里简单解释一下什么是HMS和GMS:

  • HMS代表华为移动服务
  • GMS代表Google移动服务

您可以将使用Google库的应用程序发布到华为的应用商店(名为AppGallery),但这个应用程序只适用于包含HMS+GMS(所有设备直到2020年都有HMS和GMS)的华为设备,其他设备无法看到。

然而,新款手机如Mate 30系列、P40等只安装了HMS。因此,如果您想使您的应用对所有华为设备可见(HMS+GMS和HMS),则必须在您的应用程序中实现检测用户设备上的哪种服务的功能。它将决定调用哪个正确的函数(例如初始化华为地图或谷歌地图的实例)。

以下是检测HMS和GMS的代码:

对于华为移动服务,我们使用:

HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);

我们使用以下 Google 移动服务:

https://developer.huawei.com/consumer/zh-cn/doc/development/HMS-References/huaweiapiavailability

GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);

https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability

以下是正确处理检测HMS和GMS的代码:

public static boolean isHmsAvailable(Context context) {
    boolean isAvailable = false;
    if (null != context) {
        int result = HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);
        isAvailable = (com.huawei.hms.api.ConnectionResult.SUCCESS == result);
    }
    Log.i(TAG, "isHmsAvailable: " + isAvailable);
    return isAvailable;
}

public static boolean isGmsAvailable(Context context) {
    boolean isAvailable = false;
    if (null != context) {
        int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
        isAvailable = (com.google.android.gms.common.ConnectionResult.SUCCESS == result);
    }
    Log.i(TAG, "isGmsAvailable: " + isAvailable);
    return isAvailable;
}

据我所知,如果你实现了华为的开发工具包或谷歌的库,这些类(HuaweiApiAvailability/GoogleApiAvailability)将会可用。


1
我的应用程序支持在发布和调试版本中使用不同的包名,以便能够在同一设备上安装两个应用程序。当我尝试集成com.huawei.agconnect时,我会收到错误提示,指出agconnect-services.json文件中的package_name(例如:my.package.app)与调试版本的实际my.package.app.dev不相等。而且,agconnect-services.json文件不支持像google-services.json文件那样多个包。 - cosic
9
要访问 HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context),只需要在项目中添加 implementation 'com.huawei.hms:base:4.0.2.300' 并将 maven {url 'http://developer.huawei.com/repo/'} 加入到仓库中即可。 - cosic
对于更高级的检测,包括让用户有可能在需要安装/更新HMS/GMS时做出反应,请查看此处:https://github.com/abusuioc/from-gms-to-hms#在运行时检测设备上可用的移动服务 - Adi B
Cosic,我相信您需要在华为控制台内创建两个不同的项目,以便拥有两个不同的agconnect-services.json文件。根据我在这里阅读的内容,这似乎是可能的:https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-config-flavor-android-0000001057944603 - Ramiro G.M.
很好的回答!只是提醒您,您也可以将这些方法写在一行中:return HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context) == ConnectionResult.SUCCESS - Jorn Rigter

11

尽管这取决于您的应用程序架构,但到目前为止有两个合理的替代方案:

  1. 使用flavors和variants,这样可以使您更加灵活。建立架构和实现可能需要相对更多的时间,但它是一种干净的方法,提供了良好的代码隔离。由于这些生态系统具有不同的市场(华为的AppGallery),使用flavors和variants,很容易建立单独的构建流程。它使您能够维护不同的APK文件以适应不同的生态系统。
  2. 使用包装器/桥接方法。简单地说,实现包装器类来决定并将请求转发到相应的端点。使用此方法,可以为这两个市场维护单个版本。HMS实际上为此提供了一个强大的工具。它分析依赖于GMS的代码,然后自动生成包装器类并将原始代码转换为使用包装器类的代码。它被称为“HMS Converter”,甚至有一个Android Studio插件。 https://developer.huawei.com/consumer/en/huawei-toolkit/

4
如果你的应用程序在使用Firebase/Google库方面会增长或已经增长,请不要使用第二种方法。我们曾经使用过“HMS Converter”,结果是如果你更改gradle文件中的一行代码,构建时间需要半小时。这就是为什么我们开始实现由变量和风格控制的抽象层。 - user1185087
你对这个长时间构建的问题有什么特别的发现吗?它是真的与它生成的变体和风味有关,还是华为仓库的网络问题? - captaink
如果您希望使用开源包装库而不是HMS转换器,请查看当前提供的内容:https://github.com/abusuioc/from-gms-to-hms#wrappers - Adi B

8

需要将googlehuawei设置为productFlavors,然后作为sourceSets进行设置。


build.gradle根项目:

buildscript {
    repositories {
        google()
        mavenCentral()
        maven { url "https://developer.huawei.com/repo/" }
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.2.2"
        classpath "com.google.gms:google-services:4.3.13"
        classpath "com.huawei.agconnect:agcp:1.7.0.300"
    }
}

模块build.gradle

plugins {
    id "com.android.application"
    id "androidx.navigation.safeargs"
}

def json_huawei_release = "src/huaweiRelease/agconnect-services.json"
def json_huawei_debug = "src/huaweiDebug/agconnect-services.json"
def json_google = "src/google/google-services.json"

if (getGradle().getStartParameter().getTaskRequests().toString().contains('Huawei')) {
    if (project.file(json_huawei_debug).exists() || project.file(json_huawei_release).exists()) {
        apply plugin: "com.huawei.agconnect"
    }
}

if (getGradle().getStartParameter().getTaskRequests().toString().contains('Google')) {
    if (project.file(json_google).exists()) {
        println "found: ${project.file(json_google)}"
        apply plugin: "com.google.gms.google-services"
        apply plugin: "com.google.firebase.crashlytics"
    } else {
        println "missing: ${project.file(json_google)}"
    }
}

android {

    ...
    flavorDimensions "vendor"
    productFlavors {
        google {
            dimension "vendor"
            versionNameSuffix "-google"
        }
        huawei {
            dimension "vendor"
            versionNameSuffix "-huawei"
        }
    }

    sourceSets {
        google {
            java.srcDirs = ['src/main/java', 'src/google/java']
        }
        huawei {
            java.srcDirs = ['src/main/java', 'src/huawei/java']
        }
    }
}

dependencies {

    /** Google Play Services */
    googleImplementation "com.google.android.gms:play-services-base:18.0.1"
    googleImplementation "com.google.android.gms:play-services-basement:18.0.0"
    googleImplementation "com.google.android.gms:play-services-auth:20.0.0"
    googleImplementation "com.google.android.gms:play-services-identity:18.0.0"
    googleImplementation "com.google.android.gms:play-services-oss-licenses:17.0.0"

    /** Google Firebase */
    googleImplementation "com.google.firebase:firebase-auth:21.0.1"
    googleImplementation "com.google.firebase:firebase-database:20.0.3"
    googleImplementation "com.google.firebase:firebase-messaging:23.0.0"
    googleImplementation "com.google.firebase:firebase-functions:20.0.1"
    googleImplementation "com.google.firebase:firebase-crashlytics:18.2.6"
    googleImplementation "com.google.firebase:firebase-analytics:20.0.2"
    googleImplementation "com.google.firebase:firebase-perf:20.0.4"
    // googleImplementation "com.firebaseui:firebase-ui-auth:8.0.0"

    /** Huawei Mobile Services */
    huaweiImplementation "com.huawei.hms:base:6.1.0.302"
    huaweiImplementation "com.huawei.hms:push:6.1.0.300"
    huaweiImplementation "com.huawei.hms:hianalytics:6.3.0.300"

    /** Huawei AppGallery Connect */
    huaweiImplementation "com.huawei.agconnect:agconnect-core:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-auth:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-remoteconfig:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-function:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-cloud-database:1.5.2.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-applinking:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-crash:1.6.5.300"
    huaweiImplementation "com.huawei.agconnect:agconnect-apms:1.5.2.309"
    huaweiImplementation "com.huawei.agconnect:agconnect-storage:1.5.0.100"
    huaweiImplementation "com.huawei.agconnect:agconnect-appmessaging:1.6.5.300"
}

这允许为所有内容提供自定义实现;它将构建两个不同的构件。

在测试时需要考虑切换构建变体和测试设备 - 但可以在IDE运行配置中传递任务名称和设备序列号(以便在正确的测试设备上运行正确的构建变体)。


8

1
欢迎提供解决方案的链接,但请确保您的答案即使没有链接也是有用的:在链接周围添加上下文,以便其他用户了解它的内容和原因,然后引用您链接的页面中最相关的部分,以防目标页面无法访问。仅仅提供链接的答案可能会被删除。 - STA
我猜你的答案@EsTeAa是自动生成的。原始问题太泛泛而谈,很难用几句话回答。链接页面提供了完整的答案和代码片段。 - Adi B
但是你希望成为一个机器人吗?否则我就不理解你个人资料中的标语:“曾经是人类,现在是机器人”了 :) 我已经编辑了我的回答,希望你现在会发现它更有用了,没有必要做出愚蠢的负评。 - Adi B
我与 SOBotics 合作,无论如何我没有为审查投反对票。 - STA

7

安德烈博格丹(@AndreiBogdan)和deadfish(@deadfish)的回答都是正确的。我想再补充一下:

首先,您需要根据应用程序场景和开发/测试成本选择适当的解决方案(G+H或G2H)

  1. 如果您选择G+H解决方案,则需要检查GMS是否可用。如果无法正确使用GMS接口,则需要使用HMS。有关详细信息,请参阅deadfish(@deadfish)的答案。建议您使用此解决方案,它可以:
  • 降低应用程序打包的复杂性。一个包可以发布到Google Play和AppGallery。
  • 减少代码维护成本。在原始逻辑代码中添加了HMS + GMS适配层代码。通过这种方式,可以根据手机自动调用适当的代码。也就是说,您只需要调用方法来检查现有逻辑代码中是否可用GMS,而不需要维护两套代码。
  1. 如果您选择G2H解决方案,则兼容性测试的工作量较小。您只需要在华为手机上测试新的APK即可。使用不同的包在HUAWEI AppGallery和Google Play上发布您的应用程序。在AppGallery上发布的应用程序仅包含华为的逻辑代码。您可以参考AndreiBogdan(@AndreiBogdan)的答案,或查看文档支持多个渠道

  2. 正如captaink(@captaink)所说,您可以使用HMS Toolkit Convertor。它支持G+H和G2H转换。目前,HMS Toolkit支持Java和Kotlin。支持的Android Studio版本:3.3.2~4.1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接