动态替换 AndroidManifest.xml 文件里的占位符(manifestPlaceholders)

438 阅读2分钟

manifestPlaceholders 是 Android 开发中一个非常有用的配置项,主要用于在构建过程中动态替换 AndroidManifest.xml 文件里的占位符,可以在不修改AndroidManifest.xml文件的情况下,动态的配置应用的各种信息,提高开发和打包的灵活性

1、使用场景

  • 多渠道打包:

    在进行多渠道打包时,不同的渠道可能需要不同的配置信息,比如应用的统计渠道ID、第三方服务的APP Key等。通过manifestPlaceholders可以在打包时根据不同的渠道动态的替换这些信息。

  • 环境区分:

    在开发、测试、生产等不同的环境下,可能需要使用不同的服务器地址、API Key等配置。使用manifestPlaceholders 可以方便的在不同环境下替换这些配置。

2、基本用法

在项目的build.gradle文件中,通过defaultConfig或者buildType或者productFlavors中定义manifestPlaceholders来替换AndroidManifest.xml文件中的占位符

1、 在清单文件中定义好代码中需要使用到的元数据

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test1"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Test1">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 动态替换的元数据,值在gradle脚本中使用manifestPlaceholders替换为真正的值 -->
        <meta-data
            android:name="BUGLY_APPID"
            android:value="${buglyAppId}" />
        <!-- 配置APP版本号 -->
        <meta-data
            android:name="BUGLY_APP_VERSION"
            android:value="${buglyAppVersion}" />
        <!-- 配置APP渠道号 -->
        <meta-data
            android:name="BUGLY_APP_CHANNEL"
            android:value="${buglyAppChannel}" />
        <!-- 配置Bugly调试模式(true或者false)-->
        <meta-data
            android:name="BUGLY_ENABLE_DEBUG"
            android:value="${buglyEnableDebug}" />

        <!-- 动态替换的元数据,值在gradle脚本中使用manifestPlaceholders替换为真正的值 -->
    </application>
</manifest>

2、在gradle文件中替换清单文件中的占位符

groove写法
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}

android {
    namespace 'com.lhy.test1'
    compileSdk 34

    defaultConfig {
        applicationId "com.lhy.test1"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        // 动态替换清单文件中的元数据
        manifestPlaceholders = [
                buglyAppId      : 'a2131231',
                buglyAppVersion : 1,
                buglyAppChannel : 'm_debug',
                buglyEnableDebug: true
        ]
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = '11'
    }
    buildFeatures {
        compose true
    }
}

dependencies {
    implementation libs.androidx.core.ktx
    implementation libs.androidx.lifecycle.runtime.ktx
    implementation libs.androidx.activity.compose
    implementation platform(libs.androidx.compose.bom)
    implementation libs.androidx.ui
    implementation libs.androidx.ui.graphics
    implementation libs.androidx.ui.tooling.preview
    implementation libs.androidx.material3
    testImplementation libs.junit
    androidTestImplementation libs.androidx.junit
    androidTestImplementation libs.androidx.espresso.core
    androidTestImplementation platform(libs.androidx.compose.bom)
    androidTestImplementation libs.androidx.ui.test.junit4
    debugImplementation libs.androidx.ui.tooling
    debugImplementation libs.androidx.ui.test.manifest
}
kts写法
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}

android {
    namespace = "com.lhy.test2"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.lhy.test2"
        minSdk = 21
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

        // 动态替换清单文件中的元数据
        manifestPlaceholders.putAll(
            mapOf(
                "buglyAppId" to "a2131231",
                "buglyAppVersion" to "1",
                "buglyAppChannel" to "m_debug",
                "buglyEnableDebug" to "true"
            )
        )
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        compose = true
    }
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
}

3、在代码中获取元数据

fun getAppMetaData(context: Context, key: String): String? {
    return try {
        val packageManager = context.packageManager
        val applicationInfo =
            packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
        applicationInfo.metaData[key].toString()
    } catch (e: PackageManager.NameNotFoundException) {
        e.printStackTrace()
        null
    }
}

或者使用Blankj的android工具库[AndroidUtilCode][MetaData 相关]来获取元数据