React Native:将多个 Maven url 匹配到特定的依赖项?

React Native: match multiple Maven urls to specific dependencies?

请不要将其标记为重复。我花了两天时间搜索 Github、Stack、Android Studio 文档、RN 文档和 Gradle 文档来寻找答案。

问题

我们的 React Native 项目不会构建在 Android 上。一个构建错误是

Could not find com.github.wix-playground:ahbottomnavigation:2.4.9

fix for this error是将maven { url 'https://jitpack.io' }添加到项目build.gradle

然而,在添加这个之后,我们得到第二个错误,即

Execution failed for task ':react-native-ble-plx:compileDebugJavaWithJavac'

The fix for this 正在将 maven { url 'https://maven.google.com' } 添加到 build.gradle

但是在添加第二个修复后,我们再次触发了第一个错误。

问题

有没有办法在 build.gradle 中列出两个 maven { <url> },这样每个 maven { <url> } 仅用于依赖它的特定依赖项?

我们可以强制所有依赖项使用相同的支持库版本吗?

如有任何帮助,我们将不胜感激。

我们的档案

项目级别build.gradle

buildscript {
    repositories {
        google()
        mavenLocal()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        mavenLocal()
        // maven { url 'https://maven.google.com' }
        maven { url 'https://jitpack.io' }     
        maven {
          url "$rootDir/../node_modules/react-native/android" // This URL still works
        }
    }
}
  ext {
        buildToolsVersion = "28.0.3"
        minSdkVersion = 19
        compileSdkVersion = 26
        targetSdkVersion = 26
        supportLibVersion = "28.0.0"
    }

    subprojects { subproject ->
    afterEvaluate {
        if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
            android {
                variantFilter { variant ->
                    def names = variant.flavors*.name
                    if (names.contains("reactNative51") || names.contains("reactNative56")) {
                        setIgnore(true)
                    }
                }
            }
        }
    }
}

app/build.gradle

buildscript {
    repositories {
        maven { url 'https://maven.fabric.io/public' }
    }
    dependencies {
        classpath 'io.fabric.tools:gradle:1.+'
    }
}

apply plugin: "com.android.application"
apply plugin: "io.fabric"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

repositories {
    maven { url 'https://maven.fabric.io/public' }
}

import com.android.build.OutputFile

project.ext.react = [
    entryFile: "index.js",
    bundleInStaging: true,
    devDisabledInStaging: true,
    inputExcludes: ["ios/**", "__tests__/**", "bundle_out/**"]
]

apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-sentry/sentry.gradle"

def enableSeparateBuildPerCPUArchitecture = false

def enableProguardInReleaseBuilds = false

def debugKeystorePropertiesFile = rootProject.file("keystores/debug.keystore.properties");
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))

def releaseKeystorePropertiesFile = rootProject.file("keystores/release.keystore.properties");
def releaseKeystoreProperties = new Properties()
releaseKeystoreProperties.load(new FileInputStream(releaseKeystorePropertiesFile))

android {
    compileSdkVersion 28
    buildToolsVersion '28.0.3'

    defaultConfig {
        applicationId "com.example"
        missingDimensionStrategy "RNN.reactNativeVersion", "reactNative55"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 177
        versionName "2.0.4"
        multiDexEnabled true
        ndk {
            abiFilters "armeabi-v7a", "x86"
        }
        manifestPlaceholders = [
            FABRIC_API_KEY: project.env.get("FABRIC_API_KEY"),
            FABRIC_SECRET: project.env.get("FABRIC_SECRET")
        ]
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    signingConfigs {
        debug {
            storeFile file(keystoreProperties['key.store'])
            storePassword keystoreProperties['key.store.password']
            keyAlias keystoreProperties['key.alias']
            keyPassword keystoreProperties['key.alias.password']
        }
        release {
            storeFile file(releaseKeystoreProperties['key.store'])
            storePassword releaseKeystoreProperties['key.store.password']
            keyAlias releaseKeystoreProperties['key.alias']
            keyPassword releaseKeystoreProperties['key.alias.password']
        }
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro", "proguard-devsupport.pro"
            testProguardFile 'proguard-debug.pro'
        }
        staging {
            signingConfig signingConfigs.debug
            matchingFallbacks = ['release', 'debug']
        }
        debug {
            signingConfig signingConfigs.debug
        }
    }
    // applicationVariants are e.g. debug, release
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def versionCodes = ["armeabi-v7a":1, "x86":2]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null) {  // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
            }
        }
    }
}

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support' && requested.name != 'multidex') {
            details.useVersion "${rootProject.ext.supportLibVersion}"
        }
    }
}

dependencies {
    compile('com.google.android.gms:play-services-gcm:11.8.0') {
        force = true
    }
    compile project(':react-native-push-notification')
    implementation project(':react-native-awesome-card-io')
    implementation project(':react-native-fabric')
    implementation project(':react-native-randombytes')
    implementation project(':react-native-linear-gradient')
    implementation project(':react-native-spinkit')
    implementation project(':react-native-keychain')
    implementation project(':react-native-vector-icons')
    implementation project(':react-native-ble-plx')
    implementation project(':react-native-config')
    implementation project(':react-native-sentry')
    implementation project(':react-native-device-info')
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.0.0-alpha3'
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    implementation "com.android.support:appcompat-v7:27.1.0"
    implementation 'com.android.support:design:27.1.0'
    implementation "com.facebook.react:react-native:+"  // From node_modules
    implementation('com.crashlytics.sdk.android:crashlytics:2.9.3@aar') {
        transitive = true;
    }
    implementation project(':react-native-navigation')
    implementation project(':react-native-tcp')
    implementation 'com.android.support:design:25.4.0'
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    implementation 'com.android.support:multidex:1.0.3'
    implementation ('com.github.wix-playground:ahbottomnavigation:2.4.9') {
        exclude group: "com.android.support"
    }
}

task copyDownloadableDepsToLibs(type: Copy) {
    from configurations.compile
    into 'libs'
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- < Only if you're using GCM or localNotificationSchedule() > -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
    <!-- < Only if you're using GCM or localNotificationSchedule() > -->
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
      android:name="android.support.multidex.MultiDexApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:allowBackup="false"
      android:fullBackupContent="false"
      android:theme="@style/AppTheme">

        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_name"
            android:value="YOUR NOTIFICATION CHANNEL NAME"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_description"
            android:value="YOUR NOTIFICATION CHANNEL DESCRIPTION"/>
        <!-- Change the resource name to your App's accent color - or any other color you want -->
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_color"
            android:resource="@android:color/white"/>

        <!-- < Only if you're using GCM or localNotificationSchedule() > -->
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>
        <!-- < Only if you're using GCM or localNotificationSchedule() > -->

        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>

        <!-- < Only if you're using GCM or localNotificationSchedule() > -->
        <service
            android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerServiceGcm"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>

      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:screenOrientation="portrait"
        android:windowSoftInputMode="adjustPan">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
      <meta-data
        android:name="io.fabric.ApiKey"
        android:value="${FABRIC_API_KEY}"
      />
      <meta-data
        android:name="io.fabric.ApiSecret"
        android:value="${FABRIC_SECRET}"
       />
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>

Is there a way to list both maven { }s in build.gradle in such a way the each maven { } is used only for the specific dependency relying on it?

Matching repositories to dependencies 是一项孵化功能。

目前您可以查看 official doc 关于定义多个 repo:

You can define more than one repository for resolving dependencies. Declaring multiple repositories is helpful if some dependencies are only available in one repository but not the other.

The order of declaration determines how Gradle will check for dependencies at runtime. If Gradle finds a module descriptor in a particular repository, it will attempt to download all of the artifacts for that module from the same repository. You can learn more about the inner workings of Gradle’s resolution mechanism.

还有

Can we force the same support library version across all dependencies?

您可以:

  • 从其中一个依赖项中排除冲突的 module/library
  • 明确声明支持库版本

例如: 例如:

  implementation "your dependency:$version" {
       exclude group: "org.xxxx.xxx", module: "xxxxxxx"
   }

例如:

android {
    configurations.all {
        resolutionStrategy.force 'com.android.support:support-xxxx:XX.YY.ZZ'
    }
}

请注意这种方法,因为您可能会在某些库中强制使用落后版本。

我们使用了解决策略(见上面的评论)来解决我们的问题。此外,在我们的 app/build.gradle 中,我们决定不使用 androidx 支持库实现,而是使用旧的 com.android.support:xxx 库。最后,解决方案看起来像这样。

app/build.gradle

// Force the same support library version across all dependencies
configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support' && requested.name != 'multidex') {
            details.useVersion "${rootProject.ext.supportLibVersion}"
        }
    }
}

implementation 'com.android.support:design:25.4.0'
implementation 'com.android.support:appcompatv7:${rootProject.ext.supportLibVersion}'

使用这个在单个命令中添加多个依赖项

yarn add react-native-gesture-handler react-native-modal-dropdown react-native-vector-icons react-navigation



npm install --save react-native-gesture-handler react-native-modal-dropdown react-native-vector-icons react-navigation

以及明智地使用 React Native 版本

react-native init --version="react-native@0.36.0" MyNewApp

react-native init --version="react-native@0.59.8" MyNewAppName