0

내 앱과 관련된 이유로 두 개의 서로 다른 productFlavor에서 두 가지 버전의 Google Play 서비스 라이브러리를 사용하고 싶습니다. (https://bintray.com/android/android-tools/com.google.gms.google-services/에서 사용 가능한 최신 버전에 대한 정보입니다)GooglePlayServices 라이브러리의 두 가지 버전을 서로 다른 두 가지 제품 버전에서 사용하는 방법은 무엇입니까?

구글 - 서비스 플러그인의 버전을 업데이트하여 어느 버전 충돌을 해결하십시오 또는 com.google의 버전을 업데이트 :하지만 Gradle을 나에게 익숙한 오류 메시지를 제공합니다 .android.gms ~ 10.2.4.

일반적으로 GPS 라이브러리의 일관된 버전을 사용하여이 문제를 해결할 것입니다. 하지만,이 경우에는 앱을 두 가지 별개의 맛으로 컴파일하기 때문에 불일치가 괜찮을 것이라고 생각했습니다.

응용 프로그램

buildscript { 
    repositories { 
     jcenter() 
    } 
    dependencies { 
     classpath 'com.android.tools.build:gradle:2.3.1' 
     classpath 'com.google.gms:google-services:3.0.0' 
    } 
} 

allprojects { 
    repositories { 
     jcenter() 
    } 
} 

task clean(type: Delete) { 
    delete rootProject.buildDir 
} 

Gradle을 버전 프로젝트 build.gradle

apply plugin: 'com.android.application' 

android { 
    compileSdkVersion 25 
    buildToolsVersion '25.0.2' 

    defaultConfig { 
     applicationId "com.albertcbraun.googleplayservicesversionconflicttestcase" 
     minSdkVersion 16 
     targetSdkVersion 25 
     versionCode 1 
     versionName "1.0" 
     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 
    productFlavors { 
     flavor1 { 
      applicationId 'com.albertcbraun.flavor1' 
     } 

     flavor2 { 
      applicationId 'com.albertcbraun.flavor2' 
     } 
    } 
} 


dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 
     exclude group: 'com.android.support', module: 'support-annotations' 
    }) 
    compile 'com.android.support:appcompat-v7:25.3.1' 
    compile 'com.android.support.constraint:constraint-layout:1.0.2' 
    testCompile 'junit:junit:4.12' 

    // these are the problematic lines. GPS versions differ: 
    flavor1Compile 'com.google.android.gms:play-services-identity:10.2.4' 
    flavor2Compile 'com.google.android.gms:play-services-identity:9.6.1' 
} 

apply plugin: 'com.google.gms.google-services' 

build.gradle : 3.3

안드로이드 스튜디오 버전은 확실히 작동하지 않습니다 : 2.3.1

+0

https://stackoverflow.com/questions/18196974/how-to-define-different-dependencies-for-different-product-flavors 응답과에 대한 – akash93

답변

0

======== 개정 2017년 4월 5일 ========

이, FWIW, 내가 할 수 있었다 실험이지만, GoogleServicesTask.java 및 GoogleServicesPlugin.groovy (그라데이션 용 GoogleServicesPlugin을 구성)의 버전 3.0.0에 대한 "향미 인식"을 해킹합니다.

원래 플러그인 은 build.gradle (findTargetVersion이라는 메서드에서)의 'compile'문을 검사하여 GPS 라이브러리 버전을 유추합니다. 그러나 나는 그것을 바꿨다. 이 해킹을 사용하면 확장 속성의 flavor 당 버전을 미리 지정할 수 있습니다.

이 접근법은 잘 테스트되지도 생산 준비도되어 있지 않지만, 입니다.이 두 가지 버전의 GPS 라이브러리를 서로 다른 두 종류의 제품으로 컴파일 할 수 있습니다. 참고 : Android Studio에서는 두 가지 버전 (빨간색 밑줄)이 있다는 사실에 대해 약간 불만하지만 AS는 여전히 빌드 바리안트 중 하나를 선택하고 실제로 빌드를 수행해야합니다. (적어도 나를 위해 않았다.)

먼저 어딘가 합리적인 같은 build.gradle에 (당신이 사용하고자하는 어떤 버전 이상)이 두 확장 값을 추가 : 주석,

ext.flavor1GPSVersion = "10.2.1" 
ext.flavor2GPSVersion = "10.2.4" 

둘째 또는 앱 모듈 빌드에서이 '적용'행을 삭제하십시오.Gradle을 :

마지막으로
apply plugin: GoogleServicesPlugin 

직접 붙여 그 build.gradle 파일의 맨 아래에 GoogleServicesTask.java 및 GoogleServicesPlugin.groovy의 수정 된 버전을 다음 (그리고 하단에있는 새로운 '플러그인을 적용'라인을 포함하는 기억) :

// ************************************************************// 
// ********** 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년 4월 4일 =======

더 이것으로 보면, 나는 구글 - 서비스 플러그를 소스로 발견 오류 메시지와 GPS 라이브러리의 한 버전 만 종속 된 제약 조건 cy를 사용할 수 있습니다. (위의 build.gradle에서 플러그인을 적용하면 'com.google.gms.google-services'이 표시되지만 오류 메시지는 표시되지 않지만 실제로 플러그인이 필요하므로 주석 달기가 필요합니다.

이렇게하려면 위의 앱 build.gradle 하단에 적용된 google-services 플러그인의 수정 된 버전을 만들어야합니다.

Google 서비스 플러그인은 GoogleServicesTask.java와 GoogleServicesPlugin.groovy의 두 파일로 구성됩니다. (이것들은 gradle 가정의 '캐쉬'서브 디렉토리 바로 아래에서 찾을 수 있습니다).

GoogleServicesTask.java는 findTargetVersion 메소드에서 찾은 GPS 라이브러리의 첫 번째 버전 만 사용하는 것으로 나타납니다.

(문자를 저장 밖으로 편집)

0

아니요 gradle은 동일한 라이브러리의 여러 버전을 지원하지 않습니다. 최신 버전 인 Gradle은 기본적으로 최신 충돌 버전을 사용합니다. 그러나이 동작을 변경할 수 있습니다. 이 방법을 사용하면 모든 버전 충돌시 열심히 실패하는 해상도를 구성 할 수 있습니다. 동일한 구성에서 동일한 종속성의 여러 버전 (그룹 및 이름이 동일 함). 여기에서

소스는 https://gradle.org/docs/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html

+0

감사 그 문서에 대한 포인터. 흥미 롭군. 그러나 주목할 것은 라이브러리가 GPS 라이브러리가 아닌 경우 android gradle plugin을 사용하면 두 가지 다른 유형으로 동일한 라이브러리의 두 가지 버전을 컴파일 할 수 있다는 것입니다. (예 : com.android.support:appcompat-v7:25.3.0 및 com.android.support:appcompat-v7:25.3.1) –

관련 문제