======== ======== REVISED 2017.04.05
Dies ist experimentell, aber FWIW, ich war in der Lage zu hack in "Flavour Awareness" auf Version 3.0.0 von GoogleServicesTask.java und GoogleServicesPlugin.groovy (die das GoogleServicesPlugin für grandle ausmachen).
Das ursprüngliche Plugin leitet die GPS-Bibliothek-Version durch Prüfung der 'compile' Anweisungen in Ihrem build.gradle (in einer Methode namens findTargetVersion). Aber ich habe das geändert. Mit diesem Hack geben Sie diese Versionen pro Flavor in den Erweiterungseigenschaften vor.
Dieser Ansatz ist weder gut getestet noch produktionstauglich, aber es ist in der Lage, zwei verschiedene Versionen der GPS-Bibliotheken mit zwei verschiedenen Produktaromen zu kompilieren. Beachten Sie außerdem: Android Studio beklagt sich ein wenig über die Tatsache, dass Sie zwei verschiedene Versionen haben (rote Unterstreichung), aber AS sollte Ihnen weiterhin erlauben, einen Ihrer Geschmacksrichtungen in Build-Varianten auszuwählen und tatsächlich einen Build auszuführen. (Zumindest hat es für mich.)
Zuerst fügen Sie diese beiden Verlängerungswerte (oder was auch immer Versionen, die Sie verwenden möchten) in der gleichen build.gradle, irgendwo sinnvoll:
ext.flavor1GPSVersion = "10.2.1"
ext.flavor2GPSVersion = "10.2.4"
Zweitens Kommentar aus oder entfernen Sie diese Anwendungszeile aus dem Build Ihres App-Moduls.gradle:
apply plugin: GoogleServicesPlugin
Schließlich einfügen, direkt die folgende modifizierte Versionen des GoogleServicesTask.java und GoogleServicesPlugin.groovy in den Boden dieser build.gradle-Datei (und erinnere mich an die neue ‚anwenden Plugin‘ Zeile am unteren Rand schließen):
// ************************************************************//
// ********** Multi Flavor Google Services Plugin *************//
// ************************************************************//
import org.gradle.api.tasks.Optional;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
class MultiFlavorGoogleServicesPlugin implements Plugin<Project> {
public final static String JSON_FILE_NAME = 'google-services.json'
public final static String MODULE_GROUP = "com.google.android.gms"
public final static String MODULE_GROUP_FIREBASE = "com.google.firebase"
public final static String MODULE_CORE = "firebase-core"
public final static String MINIMUM_VERSION = "9.0.0"
private static final String TAG = "GoogleServicesPlugin";
@Override
void apply(Project project) {
if (project.plugins.hasPlugin("android") ||
project.plugins.hasPlugin("com.android.application")) {
// this is a bit fragile but since this is internal usage this is ok
// (another plugin could declare itself to be 'android')
for (def flavor : project.android.productFlavors) {
addDependency(project, flavor.name)
}
setupPlugin(project, false)
return
}
if (project.plugins.hasPlugin("android-library") ||
project.plugins.hasPlugin("com.android.library")) {
// this is a bit fragile but since this is internal usage this is ok
// (another plugin could declare itself to be 'android-library')
for (def flavor : project.android.productFlavors) {
addDependency(project, flavor.name)
}
setupPlugin(project, true)
return
}
// If the google-service plugin is applied before any android plugin.
// We should warn that google service plugin should be applied at
// the bottom of build file.
showWarningForPluginLocation(project)
// Setup google-services plugin after android plugin is applied.
project.plugins.withId("android", {
setupPlugin(project, false)
})
project.plugins.withId("android-library", {
setupPlugin(project, true)
})
// Add dependencies after the build file is evaluate and hopefully it
// can be execute before android plugin process the dependencies.
for (def flavor : project.android.productFlavors) {
project.afterEvaluate({
addDependency(project, flavor.name)
})
}
}
private static void showWarningForPluginLocation(Project project) {
project.getLogger().warn(
"please apply google-services plugin at the bottom of the build file.")
}
private static boolean checkMinimumVersion(Project project, String flavorName) {
String[] subTargetVersions = findTargetVersion(project, flavorName).split("\\.") //targetVersion.split("\\.")
String[] subMinimumVersions = MINIMUM_VERSION.split("\\.")
for (int i = 0; i < subTargetVersions.length && i < subMinimumVersions.length; i++) {
Integer subTargetVersion = Integer.valueOf(subTargetVersions[i])
Integer subMinimumVersion = Integer.valueOf(subMinimumVersions[i])
if (subTargetVersion > subMinimumVersion) {
return true;
} else if (subTargetVersion < subMinimumVersion) {
return false;
}
}
return subTargetVersions.length >= subMinimumVersions.length;
}
private void addDependency(Project project, String flavorName) {
//targetVersion = findTargetVersion(project).split("-")[0]
if (checkMinimumVersion(project, flavorName)) {
// If the target version is not lower than the minimum version
project.dependencies.add('compile', MODULE_GROUP_FIREBASE + ':' + MODULE_CORE + ':' + findTargetVersion(project, flavorName).split("-")[0])
} else {
throw new GradleException("Version: " + targetVersion + " is lower than the minimum version (" +
MINIMUM_VERSION + ") required for google-services plugin.")
}
}
private static String findTargetVersion(Project project, String flavorName) {
return project.ext[flavorName + "GPSVersion"];
}
private void setupPlugin(Project project, boolean isLibrary) {
if (isLibrary) {
project.android.libraryVariants.all { variant ->
handleVariant(project, variant)
}
} else {
project.android.applicationVariants.all { variant ->
handleVariant(project, variant)
}
}
}
private static void handleVariant(Project project,
def variant) {
File quickstartFile = null
String variantName = "$variant.dirName";
String[] variantTokens = variantName.split('/')
List<String> fileLocation = new ArrayList<>()
FlavorAwareGoogleServicesTask task = project.tasks
.create("process${variant.name.capitalize()}GoogleServices",
FlavorAwareGoogleServicesTask)
if (variantTokens.length == 2) {
// If flavor and buildType are found.
String flavorName = variantTokens[0]
String buildType = variantTokens[1]
fileLocation.add('src/' + flavorName + '/' + buildType)
fileLocation.add('src/' + buildType + '/' + flavorName)
fileLocation.add('src/' + flavorName)
fileLocation.add('src/' + buildType)
task.moduleVersion = findTargetVersion(project, flavorName)
task.flavorName = flavorName;
} else if (variantTokens.length == 1) {
// If only buildType is found.
fileLocation.add('src/' + variantTokens[0])
}
String searchedLocation = System.lineSeparator()
for (String location : fileLocation) {
File jsonFile = project.file(location + '/' + JSON_FILE_NAME)
searchedLocation = searchedLocation + jsonFile.getPath() + System.lineSeparator()
if (jsonFile.isFile()) {
quickstartFile = jsonFile
break
}
}
if (quickstartFile == null) {
quickstartFile = project.file(JSON_FILE_NAME)
searchedLocation = searchedLocation + quickstartFile.getPath()
}
File outputDir =
project.file("$project.buildDir/generated/res/google-services/$variant.dirName")
task.quickstartFile = quickstartFile
task.intermediateDir = outputDir
task.packageName = variant.applicationId
task.moduleGroup = MODULE_GROUP
// Use the target version for the task.
//task.moduleVersion = targetVersion;
variant.registerResGeneratingTask(task, outputDir)
task.searchedLocation = searchedLocation
}
}
/**
* Helper task for plugin
* */
public class FlavorAwareGoogleServicesTask extends DefaultTask {
private static final String STATUS_DISABLED = "1";
private static final String STATUS_ENABLED = "2";
private static final String OAUTH_CLIENT_TYPE_WEB = "3";
/**
* The input is not technically optional but we want to control the error message.
* Without @Optional, Gradle will complain itself the file is missing.
*/
@InputFile @Optional
public File quickstartFile;
@OutputDirectory
public File intermediateDir;
@Input
public String packageName;
@Input
public String moduleGroup;
@Input
public String moduleVersion;
@Input
public String searchedLocation;
@Input
public String flavorName;
@TaskAction
public void action() throws IOException {
checkVersionConflict();
if (!quickstartFile.isFile()) {
throw new GradleException(String.format("File %s is missing. " +
"The Google Services Plugin cannot function without it. %n Searched Location: %s",
quickstartFile.getName(), searchedLocation));
}
getProject().getLogger().warn("Parsing json file: " + quickstartFile.getPath());
// delete content of outputdir.
deleteFolder(intermediateDir);
if (!intermediateDir.mkdirs()) {
throw new GradleException("Failed to create folder: " + intermediateDir);
}
JsonElement root = new JsonParser().parse(Files.newReader(quickstartFile, Charsets.UTF_8));
if (!root.isJsonObject()) {
throw new GradleException("Malformed root json");
}
JsonObject rootObject = root.getAsJsonObject();
Map<String, String> resValues = new TreeMap<String, String>();
Map<String, Map<String, String>> resAttributes = new TreeMap<String, Map<String, String>>();
handleProjectNumberAndProjectId(rootObject, resValues);
handleFirebaseUrl(rootObject, resValues);
JsonObject clientObject = getClientForPackageName(rootObject);
if (clientObject != null) {
handleAnalytics(clientObject, resValues);
handleMapsService(clientObject, resValues);
handleGoogleApiKey(clientObject, resValues);
handleGoogleAppId(clientObject, resValues);
handleWebClientId(clientObject, resValues);
} else {
throw new GradleException("No matching client found for package name '" + packageName + "'");
}
// write the values file.
File values = new File(intermediateDir, "values");
if (!values.exists() && !values.mkdirs()) {
throw new GradleException("Failed to create folder: " + values);
}
Files.write(getValuesContent(resValues, resAttributes), new File(values, "values.xml"), Charsets.UTF_8);
}
/**
* Check if there is any conflict between Play-Services Version
*/
private void checkVersionConflict() {
Project project = getProject();
ConfigurationContainer configurations = project.getConfigurations();
if (configurations == null) {
return;
}
boolean hasConflict = false;
for (Configuration configuration : configurations) {
if (configuration == null) {
continue;
}
if (configuration.name.startsWith(flavorName + "Compile")) {
DependencySet dependencies = configuration.getDependencies();
if (dependencies == null) {
continue;
}
for (Dependency dependency : dependencies) {
if (dependency == null || dependency.getGroup() == null || dependency.getVersion() == null) {
continue;
}
println("checkVersionConflict for flavor:" + flavorName +
" comparing moduleGroup:" + moduleGroup + " to " + dependency.getGroup() +
" moduleVersion:" + moduleVersion + " to " + dependency.getVersion());
if (dependency.getGroup().equals(moduleGroup)
&& !dependency.getVersion().equals(moduleVersion)) {
hasConflict = true;
project.getLogger().warn("Found " + dependency.getGroup() + ":" +
dependency.getName() + ":" + dependency.getVersion() + ", but version " +
moduleVersion + " is needed for the google-services plugin.");
}
}
}
}
if (hasConflict) {
throw new GradleException("Please fix the version conflict either by updating the version " +
"of the google-services plugin (information about the latest version is available at " +
"https://bintray.com/android/android-tools/com.google.gms.google-services/) or updating " +
"the version of " + moduleGroup + " to " + moduleVersion + ".");
}
}
private void handleFirebaseUrl(JsonObject rootObject, Map<String, String> resValues)
throws IOException {
JsonObject projectInfo = rootObject.getAsJsonObject("project_info");
if (projectInfo == null) {
throw new GradleException("Missing project_info object");
}
JsonPrimitive firebaseUrl = projectInfo.getAsJsonPrimitive("firebase_url");
if (firebaseUrl != null) {
resValues.put("firebase_database_url", firebaseUrl.getAsString());
}
}
/**
* Handle project_info/project_number for @string/gcm_defaultSenderId, and fill the res map with the read value.
* @param rootObject the root Json object.
* @throws IOException
*/
private void handleProjectNumberAndProjectId(JsonObject rootObject, Map<String, String> resValues)
throws IOException {
JsonObject projectInfo = rootObject.getAsJsonObject("project_info");
if (projectInfo == null) {
throw new GradleException("Missing project_info object");
}
JsonPrimitive projectNumber = projectInfo.getAsJsonPrimitive("project_number");
if (projectNumber == null) {
throw new GradleException("Missing project_info/project_number object");
}
resValues.put("gcm_defaultSenderId", projectNumber.getAsString());
JsonPrimitive bucketName = projectInfo.getAsJsonPrimitive("storage_bucket");
if (bucketName != null) {
resValues.put("google_storage_bucket", bucketName.getAsString());
}
}
private void handleWebClientId(JsonObject clientObject, Map<String, String> resValues) {
JsonArray array = clientObject.getAsJsonArray("oauth_client");
if (array != null) {
final int count = array.size();
for (int i = 0 ; i < count ; i++) {
JsonElement oauthClientElement = array.get(i);
if (oauthClientElement == null || !oauthClientElement.isJsonObject()) {
continue;
}
JsonObject oauthClientObject = oauthClientElement.getAsJsonObject();
JsonPrimitive clientType = oauthClientObject.getAsJsonPrimitive("client_type");
if (clientType == null) {
continue;
}
String clientTypeStr = clientType.getAsString();
if (!OAUTH_CLIENT_TYPE_WEB.equals(clientTypeStr)) {
continue;
}
JsonPrimitive clientId = oauthClientObject.getAsJsonPrimitive("client_id");
if (clientId == null) {
continue;
}
resValues.put("default_web_client_id", clientId.getAsString());
return;
}
}
}
/**
* Handle a client object for analytics (@xml/global_tracker)
* @param clientObject the client Json object.
* @throws IOException
*/
private void handleAnalytics(JsonObject clientObject, Map<String, String> resValues)
throws IOException {
JsonObject analyticsService = getServiceByName(clientObject, "analytics_service");
if (analyticsService == null) return;
JsonObject analyticsProp = analyticsService.getAsJsonObject("analytics_property");
if (analyticsProp == null) return;
JsonPrimitive trackingId = analyticsProp.getAsJsonPrimitive("tracking_id");
if (trackingId == null) return;
resValues.put("ga_trackingId", trackingId.getAsString());
File xml = new File(intermediateDir, "xml");
if (!xml.exists() && !xml.mkdirs()) {
throw new GradleException("Failed to create folder: " + xml);
}
Files.write(getGlobalTrackerContent(
trackingId.getAsString()),
new File(xml, "global_tracker.xml"),
Charsets.UTF_8);
}
/**
* Handle a client object for maps (@string/google_maps_key).
* @param clientObject the client Json object.
* @throws IOException
*/
private void handleMapsService(JsonObject clientObject, Map<String, String> resValues)
throws IOException {
JsonObject mapsService = getServiceByName(clientObject, "maps_service");
if (mapsService == null) return;
String apiKey = getAndroidApiKey(clientObject);
if (apiKey != null) {
resValues.put("google_maps_key", apiKey);
return;
}
throw new GradleException("Missing api_key/current_key object");
}
private void handleGoogleApiKey(JsonObject clientObject, Map<String, String> resValues) {
String apiKey = getAndroidApiKey(clientObject);
if (apiKey != null) {
resValues.put("google_api_key", apiKey);
// TODO: remove this once SDK starts to use google_api_key.
resValues.put("google_crash_reporting_api_key", apiKey);
return;
}
// if google_crash_reporting_api_key is missing.
// throw new GradleException("Missing api_key/current_key object");
throw new GradleException("Missing api_key/current_key object");
}
private String getAndroidApiKey(JsonObject clientObject) {
JsonArray array = clientObject.getAsJsonArray("api_key");
if (array != null) {
final int count = array.size();
for (int i = 0 ; i < count ; i++) {
JsonElement apiKeyElement = array.get(i);
if (apiKeyElement == null || !apiKeyElement.isJsonObject()) {
continue;
}
JsonObject apiKeyObject = apiKeyElement.getAsJsonObject();
JsonPrimitive currentKey = apiKeyObject.getAsJsonPrimitive("current_key");
if (currentKey == null) {
continue;
}
return currentKey.getAsString();
}
}
return null;
}
/**
* find an item in the "client" array that match the package name of the app
* @param jsonObject the root json object.
* @return a JsonObject representing the client entry or null if no match is found.
*/
private JsonObject getClientForPackageName(JsonObject jsonObject) {
JsonArray array = jsonObject.getAsJsonArray("client");
if (array != null) {
final int count = array.size();
for (int i = 0 ; i < count ; i++) {
JsonElement clientElement = array.get(i);
if (clientElement == null || !clientElement.isJsonObject()) {
continue;
}
JsonObject clientObject = clientElement.getAsJsonObject();
JsonObject clientInfo = clientObject.getAsJsonObject("client_info");
if (clientInfo == null) continue;
JsonObject androidClientInfo = clientInfo.getAsJsonObject("android_client_info");
if (androidClientInfo == null) continue;
JsonPrimitive clientPackageName = androidClientInfo.getAsJsonPrimitive("package_name");
if (clientPackageName == null) continue;
if (packageName.equals(clientPackageName.getAsString())) {
return clientObject;
}
}
}
return null;
}
/**
* Handle a client object for Google App Id.
*/
private void handleGoogleAppId(JsonObject clientObject, Map<String, String> resValues)
throws IOException {
JsonObject clientInfo = clientObject.getAsJsonObject("client_info");
if (clientInfo == null) {
// Should not happen
throw new GradleException("Client does not have client info");
}
JsonPrimitive googleAppId = clientInfo.getAsJsonPrimitive("mobilesdk_app_id");
if (googleAppId == null) return;
String googleAppIdStr = googleAppId.getAsString();
if (Strings.isNullOrEmpty(googleAppIdStr)) return;
resValues.put("google_app_id", googleAppIdStr);
}
/**
* Finds a service by name in the client object. Returns null if the service is not found
* or if the service is disabled.
*
* @param clientObject the json object that represents the client.
* @param serviceName the service name
* @return the service if found.
*/
private JsonObject getServiceByName(JsonObject clientObject, String serviceName) {
JsonObject services = clientObject.getAsJsonObject("services");
if (services == null) return null;
JsonObject service = services.getAsJsonObject(serviceName);
if (service == null) return null;
JsonPrimitive status = service.getAsJsonPrimitive("status");
if (status == null) return null;
String statusStr = status.getAsString();
if (STATUS_DISABLED.equals(statusStr)) return null;
if (!STATUS_ENABLED.equals(statusStr)) {
getLogger().warn(String.format("Status with value '%1$s' for service '%2$s' is unknown",
statusStr,
serviceName));
return null;
}
return service;
}
private static String getGlobalTrackerContent(String ga_trackingId) {
return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<resources>\n" +
" <string name=\"ga_trackingId\" translatable=\"false\">" + ga_trackingId + "</string>\n" +
"</resources>\n";
}
private static String getValuesContent(Map<String, String> values,
Map<String, Map<String, String>> attributes) {
StringBuilder sb = new StringBuilder(256);
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<resources>\n");
for (Map.Entry<String, String> entry : values.entrySet()) {
String name = entry.getKey();
sb.append(" <string name=\"").append(name).append("\" translatable=\"false\"");
if (attributes.containsKey(name)) {
for (Map.Entry<String, String> attr : attributes.get(name).entrySet()) {
sb.append(" ").append(attr.getKey()).append("=\"")
.append(attr.getValue()).append("\"");
}
}
sb.append(">").append(entry.getValue()).append("</string>\n");
}
sb.append("</resources>\n");
return sb.toString();
}
private static void deleteFolder(final File folder) {
if (!folder.exists()) {
return;
}
File[] files = folder.listFiles();
if (files != null) {
for (final File file : files) {
if (file.isDirectory()) {
deleteFolder(file);
} else {
if (!file.delete()) {
throw new GradleException("Failed to delete: " + file);
}
}
}
}
if (!folder.delete()) {
throw new GradleException("Failed to delete: " + folder);
}
}
}
apply plugin: MultiFlavorGoogleServicesPlugin
======== ORIGINAL 2017.04.04 =======
in diese Suche gefunden weiter, ich, dass die google-Dienste-Plugin ist die Quelle der Fehlermeldung und der Einschränkung, dass nur eine Version einer GPS-Bibliothek abhängig ist cy kann verwendet werden. (Wenn Sie auskommentieren, verwenden Sie das Plug-in: 'com.google.gms.google-services' in der obigen build.gradle, dann tritt die Fehlermeldung nicht auf. Aber Sie brauchen dieses Plugin wirklich, also kommentieren Sie es keine Lösung.)
Um dies zu tun, müssten Sie eine modifizierte Version des Google-Services-Plugins erstellen (das unten in der App build.gradle oben angewendet wird).
Das Google-Services-Plug-in besteht aus zwei Dateien: GoogleServicesTask.java und GoogleServicesPlugin.groovy. (Diese können tief unter dem "Caches" -Unterverzeichnis von Gradle Home gefunden werden).
Offenbar besteht GoogleServicesTask.java darauf, nur die erste Version der GPS-Bibliothek zu verwenden, die es findet (in der Methode findTargetVersion).
(edited out Zeichen zu speichern)
https://stackoverflow.com/questions/18196974/how-to-define-different-dependencies-for-different-product-flavors – akash93