项目中同时拥有 GMS 和 HMS
Posted
技术标签:
【中文标题】项目中同时拥有 GMS 和 HMS【英文标题】:Have both GMS and HMS in the project 【发布时间】:2020-05-15 09:13:11 【问题描述】:如何在应用中同时拥有 Google 移动服务和 华为 移动服务?
由于华为失去了GMS的许可,看来我们需要将应用中使用的所有GMS服务替换为华为提供的。什么是“最佳实践”?使用风味并以某种方式单独处理每个类,还是复制粘贴项目并开始替换?或者......更好的是,有没有办法同时拥有两者和......以某种方式让应用程序根据它所在的设备决定使用哪个服务?显然,最后一个假设会增加 APK 文件大小。
有什么想法吗?
【问题讨论】:
这有什么更新吗?最后你是怎么处理的? Firebase 分析和/或 Admob 呢?不是违反 Google/Admob 条款和条件吗?我问这个问题是因为我不想被 Google Play Dev 和 Admob 禁止。 嗨。检查我批准的答案。我回答了我自己的问题。希望它可以帮助某人......干杯。 【参考方案1】:所以,我设法做到了:
定义了两种口味
gms
dimension "services"
buildConfigField "String", "SERVICE_USED", '"g"'
hms
dimension "services"
buildConfigField "String", "SERVICE_USED", '"h"'
每当我需要决定执行以下操作时,我都会在代码中使用“g”和“h”:API 需要“android”或“ios”的deviceType
,并且包含华为构建我们定义了另一个常数“华为”。我使用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'
只包含正确的插件。
然后我开始处理使用gms
的每一件事(地图、位置、推送通知、分析),方法是制作一个包装器并分离每种风格的代码。即对于推送通知,我创建了一个HPushNotif
,它有一个getToken
方法。我在两种风格中定义了相同的类和方法,但我根据服务类型(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
之前的gms
和hms
指的是口味的名称。只有在选择了适当的 BuildVariant(即正在构建适当的风格)时才会加载这些依赖项。
基本上,我为这两种情况包装了地图、分析、位置和推送通知的逻辑。这就是结构的外观。没什么特别的。
就是这样。当他们创建 HMS 时,他们基本上是按类复制 GMS,按方法复制。您将看到确切的方法名称完全匹配,甚至与调用参数和返回值匹配。它们是 99.99% 相同的。这让事情变得更容易。基本上你只需要复制两个类中的代码并导入正确的东西(在类的顶部)。您很少需要更改已为 GMS 编写的代码。
希望对某人有所帮助。
【讨论】:
在我的情况下,我没有找到com.google.android.gms.common.api.ApiException
和 com.google.android.gms.common.api.ResolvableApiException
的替代品,它们是实现从谷歌地图已知的对话框以在应用内打开 GPS(无需重定向到设置)google.com/…当我检查华为“花瓣地图”时,只会提示 GPS 未启用,并且在应用程序中没有打开它的对话框。所以我不认为 99.99% 的相似度是真的;)
@mikep in hms 可以使用 com.huawei.hms.common.ApiException 和 com.huawei.hms.common.ResolvableApiException【参考方案2】:
在我回答你的问题之前,先简要解释一下什么是 HMS 和 GMS:
HMS 代表华为移动服务 GMS 代表 Google 移动服务您可以在华为的应用商店(名为 AppGallery)中发布您的应用(使用 Google 的库),但此应用仅对包含 HMS+GMS 的华为设备可见且可供下载(截至 2020 年所有设备均具有 HMS 和 GMS )。
但是,Mate 30 系列、P40 等较新的手机将仅安装 HMS。因此,如果您想让您的应用程序对所有华为设备(HMS+GMS 和 HMS)可见,那么您必须在应用程序中实现检测用户设备上正在运行的服务的功能。它将决定调用什么正确的函数(即初始化华为地图或谷歌地图的实例)。
下面是检测HMS和GMS的代码:
对于我们使用的华为移动服务:
HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);
https://developer.huawei.com/consumer/en/doc/development/HMS-References/huaweiapiavailability
对于我们使用的 Google 移动服务:
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;
如果您实现了任何华为工具包/Google 的库,则这些类 (HuaweiApiAvailability/GoogleApiAvailability) AFAIK 可用。
【讨论】:
我的应用程序支持不同的包名称进行发布和调试构建,以便能够在同一设备上安装这两个应用程序。当我尝试集成 com.huawei.agconnect 时,我收到错误,即来自 agconnect-services.json 的 package_name(例如:my.package.app)不等于真正的 my.package.app.dev 以进行调试构建。而且 agconnect-services.json 不支持很多包,如 google-services.json 要访问HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context)
,您只需implementation 'com.huawei.hms:base:4.0.2.300'
并将maven url 'http://developer.huawei.com/repo/'
添加到存储库
对于更高级的检测,包括让用户能够在 HMS/GMS 需要安装/更新时做出反应,请查看此处:github.com/abusuioc/…【参考方案3】:
虽然它确实取决于您应用的架构,但目前有 2 种合理的替代方案;
-
使用风味和变体,这将为您提供更大的灵活性。建立架构和实现会相对更耗时,但它是一种干净的方法,可以很好地隔离代码。由于这些生态系统具有不同的市场(华为的 AppGallery),具有不同的风格和变体,因此建立单独的构建管道非常方便。它使您能够为不同的生态系统维护不同的 apk
使用包装器/桥接方法。简单地说,实现包装类来决定请求并将请求转发到相应的端点。使用这种方法,可以为两个市场保持单一。 HMS 实际上为此提供了一个强大的工具。它分析依赖于 GMS 的代码,然后自动生成包装类并将原始代码转换为使用包装类。它被称为“HMS 转换器”,甚至还有一个 Android Studio 插件。 https://developer.huawei.com/consumer/en/huawei-toolkit/
【讨论】:
如果您的应用在使用 firbase/google 库时会增长或正在增长,请不要不要使用第二种方法。我们使用了“HMS 转换器”,如果您更改 gradle 文件中的一行,结果是半小时的构建时间。这就是我们开始实现由变体和风味控制的抽象层的原因。 你对这么长的构建时间有什么特别的发现吗?它真的与它生成的变体和风味有关,还是华为回购的网络问题? 如果您希望使用开源包装库而不是 HMS 转换器,请查看当前产品:github.com/abusuioc/from-gms-to-hms#wrappers【参考方案4】:@AndreiBogdan 和@deadfish 的答案都是正确的。我想补充一点:
首先,您需要根据应用场景和开发/测试成本选择合适的解决方案(G+H 或 G2H)。
-
如果选择G+H方案,则需要检查GMS是否可用。如果GMS接口不能正常使用,则需要HMS。有关详细信息,请参阅 @deadfish 的回答。建议您使用这个解决方案,它可以
如果选择G2H方案,兼容性测试工作量小。您只需要在华为手机上测试新的 APK。在 HUAWEI AppGallery 和 Google Play 上发布您的应用,使用不同的包。您在AppGallery上发布的应用只包含华为的逻辑代码。您可以参考@AndreiBogdan 的回答,或查看docs Supporting Multiple Channels。
正如@captaink 所说,您可以使用HMS Toolkit Convertor。它支持G+H和G2H转换。目前,HMS Toolkit 支持 Java 和 Kotlin。支持的Android Studio版本:3.3.2~4.1。
【讨论】:
【参考方案5】:综合之前给出的所有好的答案:https://github.com/abusuioc/from-gms-to-hms#step-5-integrate-hms-sdks-in-your-app
对于大多数应用程序,一个依赖于 GMS 和 HMS SDK 的单一构建 + 在运行时决定(基于设备上的可用性)使用哪一个是推荐的方式。
【讨论】:
欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted. 我猜你的答案@EsTeAa 是自动生成的。最初的问题太笼统,无法用几句话轻松回答。链接页面使用代码 sn-ps 提供了完整的答案。 但你想成为其中的一员吗?否则我不理解您个人资料中的标语:“曾经是人类,现在是机器人”:) 无论如何,我编辑了我的答案,我希望你现在会发现它更有用,不需要愚蠢的反对票。 我与 SOBotics 合作,反正我没有投反对票【参考方案6】:必须将google
和huawei
设置为productFlavors
,随后设置为sourceSets
。
根build.gradle
:
buildscript
repositories
google()
mavenCentral()
maven url "https://developer.huawei.com/repo/"
dependencies
classpath "com.android.tools.build:gradle:7.0.4"
classpath "com.google.gms:google-services:4.3.10"
classpath "com.huawei.agconnect:agcp:1.6.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.srcDir "src/google/java"
huawei
java.srcDir "src/huawei/java"
dependencies
/** Google Mobile 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.2.300"
huaweiImplementation "com.huawei.agconnect:agconnect-auth:1.6.2.300"
huaweiImplementation "com.huawei.agconnect:agconnect-apms:1.5.2.304"
huaweiImplementation "com.huawei.agconnect:agconnect-appmessaging:1.6.0.300"
huaweiImplementation "com.huawei.agconnect:agconnect-cloud-database:1.4.7.300"
huaweiImplementation "com.huawei.agconnect:agconnect-function:1.6.0.300"
huaweiImplementation "com.huawei.agconnect:agconnect-crash:1.6.0.300"
huaweiImplementation "com.huawei.agconnect:agconnect-remoteconfig:1.6.0.300"
huaweiImplementation "com.huawei.agconnect:agconnect-applinking:1.6.0.300"
huaweiImplementation "com.huawei.agconnect:agconnect-storage:1.3.1.200"
这允许为所有内容提供自定义实现;它将构建两个不同的工件。
在测试时必须考虑切换构建变体和测试设备 - 但可以在 IDE 运行配置中传递任务名称和设备序列号(以便在正确的测试设备上运行正确的构建变体)。
【讨论】:
以上是关于项目中同时拥有 GMS 和 HMS的主要内容,如果未能解决你的问题,请参考以下文章
使用多风格编译 GMS 和 HMS 包。但是GMS版本编译的时候报错
将代码转换为 (G+H) 后应用程序崩溃。 GMS 和 HMS 支持
获取包 com.google.android.gms.ads.identifier 不存在,同时尝试获取 google 广告 id