在项目中同时拥有 GMS 和 HMS

And*_*dan 52 android google-play-services huawei-mobile-services

如何在应用程序中同时拥有 Google 移动服务和华为移动服务?

由于华为已经失去了GMS的许可,看来我们需要用华为提供的服务替换应用程序中使用的所有GMS服务。什么是“最佳实践”?使用口味并以某种方式单独处理每个类,还是复制粘贴项目并开始替换?或者……更好的是,有没有办法同时拥有和……以某种方式让应用程序根据它所在的设备决定使用哪种服务?显然,最后一个假设APK文件大小增加。

有任何想法吗?

And*_*dan 45

所以,我设法这样做:

定义了两种口味

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

    }
    hms {
        dimension "services"
        buildConfigField "String", "SERVICE_USED", '"h"'
    }
Run Code Online (Sandbox Code Playgroud)

每当我需要决定执行以下操作时,我都会在代码中使用“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'
}
Run Code Online (Sandbox Code Playgroud)

因为我使用的是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'
}
Run Code Online (Sandbox Code Playgroud)

只包含正确的插件。

然后我开始gms通过制作包装器并分离每种风格的代码来处理正在使用的每件事(地图、位置、推送通知、分析)。即对于推送通知,我创建了一个HPushNotifgetToken方法的。我在两种风格中定义了相同的类和方法,但我根据服务类型(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'
Run Code Online (Sandbox Code Playgroud)

gmshmsImplementation指味道的名字。只有在选择了适当的 BuildVariant(即正在构建适当的风格)时才会加载这些依赖项。

基本上,我为这两种情况包装了地图、分析、位置和推送通知的逻辑。这就是结构的外观。没什么特别的。

就是这样。当他们创建 HMS 时,他们基本上是按类复制 GMS,按方法复制。您将看到确切的方法名称与调用参数 even 和返回值完全匹配。它们 99.99% 相同。这让事情变得更容易。基本上你只需要复制两个类中的代码并导入正确的东西(在类的顶部)。您很少需要更改已为 GMS 编写的代码。

希望它可以帮助某人。

在此处输入图片说明


dea*_*ish 32

在我回答您的问题之前,先简要解释一下什么是 HMS 和 GMS:

  • HMS--华为移动服务
  • GMS代表谷歌移动服务

您可以在华为的应用程序商店(名为 AppGallery)中发布您的应用程序(使用 Google 的库),但此应用程序将可见并可下载,仅适用于包含 HMS+GMS 的华为设备(2020 年之前的所有设备都具有 HMS 和 GMS)。

然而,较新的手机,即 Mate 30 系列,P40 - 将只安装 HMS。因此,如果您想让您的应用对所有华为设备(HMS+GMS 和 HMS)可见,那么您必须在您的应用中实现检测用户设备上正在运行的服务的功能。它将决定调用什么适当的函数(即初始化华为地图或谷歌地图的实例)。

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

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

HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);
Run Code Online (Sandbox Code Playgroud)

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

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

GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

AFAIK 这些类(HuaweiApiAvailability/GoogleApiAvailability)在您实现任何华为工具包/Google 的库时可用。

  • 要访问“HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context)”,您只需要“implementation 'com.huawei.hms:base:4.0.2.300'”并添加“maven {url 'http://developer.huawei.hms:base:4.0.2.300'”。 com/repo/'} ` 到存储库 (4认同)

cap*_*ink 10

虽然这实际上取决于您的应用程序的架构,但目前有 2 个合理的替代方案;

  1. 使用风味和变体,这将为您提供更大的灵活性。建立体系结构和实现将相对更耗时,但它是一种提供良好代码隔离的干净方法。由于这些生态系统具有不同的市场(华为的 AppGallery),具有不同的风格和变体,因此建立单独的构建管道非常方便。它使您能够为不同的生态系统维护不同的 apk
  2. 使用包装器/桥接方法。简单地,实现包装类来决定和转发请求到相应的端点。通过这种方法,可以在两个市场上保持单一。HMS 实际上为此提供了一个强大的工具。它分析依赖于 GMS 的代码,然后自动生成包装类并将原始代码转换为使用包装类。它被称为“HMS 转换器”,甚至还有一个 Android Studio 插件。https://developer.huawei.com/consumer/en/huawei-t​​oolkit/

  • 如果您的应用程序将增长或随着 firbase/google 库的使用而增长,请不要使用第二种方法。我们使用了“HMS Converter”,如果您更改 gradle 文件中的一行,结果是半小时的构建时间。这就是为什么我们开始实现由变体和风格控制的抽象层。 (4认同)

Adi*_*i B 8

综合之前给出的所有好的答案:https://github.com/abusuioc/from-gms-to-hms#step-5-integrate-hms-sdks-in-your-app

对于大多数应用程序来说,推荐的方式是依赖 GMS 和 HMS SDK 的单一构建+在运行时(基于设备上的可用性)决定使用哪一个。


Mar*_*ler 8

必须设置googleand huaweiasproductFlavors以及随后的 as 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"
    }
}
Run Code Online (Sandbox Code Playgroud)

模块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"
}
Run Code Online (Sandbox Code Playgroud)

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

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


shi*_*ley 7

无论@ AndreiBogdan和@ deadfish的答案是正确的。我想补充一点:

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

  1. 如果选择G+H 解决方案,则需要检查 GMS 是否可用。如果GMS接口不能正常使用,则需要使用HMS。详情请参考@deadfish的回答。建议您使用此解决方案,它可以
  • 降低应用打包的复杂性。一个包可以发布到 Google Play 和 AppGallery。
  • 降低代码维护成本。在原有逻辑代码中增加了HMS+GMS适配层代码。这样就可以根据手机自动调用正确的代码。也就是说,您只需要在现有的逻辑代码上调用该方法来检查GMS是否可用,而无需维护两套代码。
  1. 如果选择G2H方案,兼容性测试的工作量很小。您只需要在华为手机上测试新的 APK。在华为应用市场和 Google Play 上发布您的应用,并使用不同的软件包。您在 AppGallery 上发布的应用仅包含华为的逻辑代码。您可以参考@AndreiBogdan的回答,或查看文档 Supporting Multiple Channels

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