Android 多渠道打包總結(jié)

為什么要多渠道

App一般會上傳多個(gè)應(yīng)用商店,例如應(yīng)用寶、小米、華為、OPPO等。

  1. 現(xiàn)在的手機(jī)都自帶應(yīng)用商店,用戶一般會在自帶應(yīng)用商店上搜索。

  2. 產(chǎn)品、運(yùn)營可統(tǒng)計(jì)使用不同手機(jī)品牌的用戶的消費(fèi)情況,App日活、月活、UV、PV等。

傳統(tǒng)方式打包的痛點(diǎn)

如果App不是很出名,一般都需要自己打包上傳,除非像微信這種,商店自動抓包進(jìn)行上傳。曾經(jīng)參加過一個(gè)技術(shù)沙龍,微信的工程師說到,他們的熱更新技術(shù),如果使用了360加固進(jìn)行,就是失效,但是360應(yīng)用商店必須加固后才能上傳,所以他們就不上傳了,最后360應(yīng)用商店也還是抓包上傳了(可見有實(shí)力就是不一樣)。

不同的渠道,可能App名字不同,App圖標(biāo)Icon不同,如果手動替換的話,體力勞動特別累人,筆者就曾經(jīng)做過一個(gè)app,需要給不同的景區(qū)生成app,每個(gè)景區(qū)的圖標(biāo)和app名字都不一樣,傳統(tǒng)方式就是打包完一個(gè),手動替換再進(jìn)行打包,10個(gè)還好,慢慢后面景區(qū)越來越多,上升了50個(gè),工作量可想而知,而且還容易錯(cuò),每個(gè)都需要手動檢查一遍(曾經(jīng)花2個(gè)小時(shí),對著屏幕一個(gè)個(gè)替換,眼睛都花了,自然容易出錯(cuò)!),這樣進(jìn)行幾次后,意識到這種重復(fù)勞動不應(yīng)該由我們來做,應(yīng)該交給機(jī)器呀!

Gradle配置多渠道打包

后面我們搜索了一下資料,決定使用Gradle配置的方式進(jìn)行打包,但他也有優(yōu)缺點(diǎn)。

  • 優(yōu)點(diǎn):就是每個(gè)包都只需要配置好要替換的文件和占位,即可開始打包。

  • 缺點(diǎn):每次打包一個(gè)渠道包,都需要編譯一次,項(xiàng)目非常大的時(shí)候,可謂非常耗時(shí)!

Manifest占位,動態(tài)替換meta標(biāo)簽值,動態(tài)標(biāo)識渠道

實(shí)現(xiàn)步驟

  1. 配置App模塊的build.gradle文件。

    • 使用productFlavors,添加2個(gè)渠道,例如360和應(yīng)用寶。
    • 注意如果是3.0版本的Gradle,必須要添加上flavorDimensions緯度。
    • 使用manifestPlaceholders,增加清單文件占位,格式:[占位名:值, 占位名:值]。
apply plugin: 'com.android.application'

android {
    //省略其他配置...

    //3.0版本Gradle開始必須添加緯度
    flavorDimensions "default"
    
    //多渠道打包配置
    productFlavors {
        //渠道包
        app_360 {
            manifestPlaceholders = [channel: "app_360"]
        }
        app_qq {
            manifestPlaceholders = [channel: "app_qq"]
        }
    }
}
  1. 清單文件增加占位標(biāo)識
    • 例如我們使用友盟進(jìn)行渠道記錄,增加一個(gè)meta_data標(biāo)簽,標(biāo)簽名為UMENG_CHANNEL,值是占位符標(biāo)識${channel}。

注意這個(gè)標(biāo)識要和第一步的build.gradle文件中的productFlavors配置一致

  • 例如build.gradle。
//占位名:channel,值:app_360
manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]
  • meta標(biāo)簽。
<!-- 占位名:channel,不包括${} -->
<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${channel}" />
  • 完整配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!-- 省略其他配置... -->

        <!-- 多渠道配置 -->
        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${channel}" />
    </application>
</manifest>
  1. 在Java代碼層讀取meta標(biāo)簽,獲取其值即可。
//渠道工具類
public class ChannelUtil {
    /**
     * 獲取app包內(nèi)的渠道標(biāo)識
     */
    public static String getChannel(Context context) {
        try {
            PackageManager manager = context.getPackageManager();
            ApplicationInfo appInfo = manager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            return appInfo.metaData.getString("UMENG_CHANNEL");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return "";
        }
    }
}

//調(diào)用
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String channel = ChannelUtil.getChannel(getApplicationContext());
        Toast.makeText(getApplicationContext(), "當(dāng)前渠道:" + channel, Toast.LENGTH_SHORT).show();
    }
}

給不同的編譯環(huán)境配置清單Key

除了productFlavors中配置manifestPlaceholders生成渠道包外,在編譯環(huán)境buildTypes下也可以使用,例如不同編譯環(huán)境下,高德地圖的key不同,就可以在buildTypes中使用。

  1. 修改build.gradle配置。
buildTypes {
    debug {
        manifestPlaceholders = [amap_key: "amp_debug_xxxxxkey"]
        //省略其他配置...
    }

    release {
        manifestPlaceholders = [amap_key: "amp_release_xxxxxkey"]
        //省略其他配置...
    }
}
  1. 清單文件中添加高德Key配置。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- 配置高德key -->
        <meta-data
            android:name="com.amap.api.v2.apikey"
            android:value="${amap_key}" />
    </application>
</manifest>

Lib模塊共享,App接入問題

一般我們都會使用多個(gè)Lib進(jìn)行分模塊開發(fā),例如支付模塊的Pay庫中的微信回調(diào)Activity,7.0獲取文件需要使用FileProvider等,都需要制定包名,但是我們的lib需要提供給其他App進(jìn)行接入,清單文件的配置需要接入方配置,如果配置比較多,而且后續(xù)版本需要更換配置,都需要接入方重新配置會比較麻煩,那么可不可以將配置留在Lib庫中,通過動態(tài)配置的方式動態(tài)配置呢。

  1. 例如微信支付的回調(diào)Activity配置,通過applicationId占位,可以動態(tài)獲取到接入方的包名,只要按照約定,在包名下建立wxapi包下,建立WXEntryActivity文件即可。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- 動態(tài)配置微信回調(diào)Activity -->
        <activity
            android:name="${applicationId}.wxapi.WXEntryActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
            
        <!-- 省略其他配置... -->
    </application>
</manifest>
  1. 再例如圖片選擇庫,使用到了FileProvider,我們不希望接入方配置,則也可以使用applicationId進(jìn)行占位。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- FileProvider配置 -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        <!-- 省略其他配置... -->
    </application>
</manifest>

動態(tài)指定App名稱

經(jīng)過前面的配置已經(jīng)足夠配置出不同渠道標(biāo)識的渠道包了,我們還可以繼續(xù)拓展下,例如不同渠道的App名字不同。(一般因?yàn)橛械膽?yīng)用商店會以App名稱而被拒...)

  1. 修改build.gradle文件。

manifestPlaceholders中添加一個(gè)標(biāo)識,app_name,值為對應(yīng)的名稱。例如360渠道為360渠道包,應(yīng)用寶渠道為應(yīng)用寶渠道。

//3.0Gradle開始必須添加緯度
flavorDimensions "default"
//多渠道打包配置
productFlavors {
        //渠道包
        app_360 {
            manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]
        }
        app_qq {
            manifestPlaceholders = [app_name: "應(yīng)用寶渠道包", channel: "app_qq"]
        }
}

動態(tài)指定App包名

例如我們debug環(huán)境下,希望測試包和正式包能夠共存,那么共存就需要包名不同,所以可以對包名進(jìn)行替換或者加后綴。

  1. 修改build.gradle文件。
    • applicationIdSuffix,給包名添加后綴。例如測試版加上.internal后綴。
    • applicationId,整個(gè)替換包名。(一般不會這么干,只是提一下)
//3.0Gradle開始必須添加緯度
flavorDimensions "default"
//多渠道打包配置
productFlavors {
    //開發(fā)版
    developer {
        //測試版加包名后綴,方便和正式版共存
        applicationIdSuffix ".internal"
        manifestPlaceholders = [app_name: "開發(fā)版", channel : "app_internal"]
    }
    production {
        //也可以完全替換包名
        applicationId "me.zh.demo"
        manifestPlaceholders = [app_name: "正式版",
                                channel: "app_internal"]
    }
}

動態(tài)生成變量

開發(fā)中,Log打印是必不可少的,但是我們在正式環(huán)境是需要將Log打印去掉的,常規(guī)做法就是打印函數(shù)前加一個(gè)LogEnable的變量,將打印調(diào)用去掉。而我們一般會在常量類中添加開關(guān)變量。

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Logger.setDelegate(new L());
        Logger.setLogPrintEnable(true);
    }
}
  • 問題:而每次打包之前都需要將開關(guān)變量設(shè)置為false,很容易忘。

  • 期望:我們希望不同buildType下的編譯環(huán)境,打印Log是不一樣的,例如buildType為debug時(shí),Log開關(guān)為開,buildType為release時(shí),Log開關(guān)為關(guān)。

  1. Gradle為我們提供了buildConfigField,用于動態(tài)生成變量在BuildConfig,那么我們給不同的buildType進(jìn)行添加即可。
//編譯類型
buildTypes {
    debug {
        //打印開關(guān)變量
        buildConfigField("boolean", "LOG_ENABLE", "true")
        //省略其他配置...
    }

    release {
        buildConfigField("boolean", "LOG_ENABLE", "false")
        //省略其他配置...
    }
}
  1. 在Java代碼中,設(shè)置BuildConfig中生成的變量即可。
public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Logger.setDelegate(new L());
        Logger.setLogPrintEnable(BuildConfig.LOG_ENABLE);
    }
}

不同渠道替換資源文件

能替換包名和App名字了,原以為完畢,結(jié)果運(yùn)營說,360商店我們要出首發(fā)!圖標(biāo)和啟動圖要加上360的標(biāo)識!這時(shí)候就需要用到同結(jié)構(gòu)、同名文件夾來替換了。

  1. 在和main文件夾下,建立同名渠道的文件夾,例如app_360渠道,建立的文件夾名為app_360。

  2. assets文件夾、res文件夾、以及AndroidManifest.xml清單文件結(jié)構(gòu)都要和main中的一致。

  3. AppIcon同名即可替換。清單文件同名即可,內(nèi)部特定不同的內(nèi)容即可。

多渠道配置1.png
多渠道配置2.png

關(guān)于Java代碼多渠道

上面說到資源文件,建立同目錄進(jìn)行替換,那么Java代碼可以同級目錄下,同名Java文件替換嗎?很遺憾的是不可以,需要不同渠道,進(jìn)行不同的邏輯時(shí),只能通過剛才ChannelUtil獲取渠道信息,進(jìn)行邏輯判斷了。

總結(jié)

使用Gradle來進(jìn)行配置,大大減少了我們的工作量,媽媽再也不用擔(dān)心我打渠道包需要2小時(shí)啦,至于gradle每構(gòu)建一個(gè)渠道包都編譯一次問題,可以單獨(dú)給assets設(shè)置一個(gè)配置文件,后續(xù)使用zip修改配置文件,達(dá)到只打一個(gè)包,其他包都使用打包工具進(jìn)行打包,時(shí)間可以節(jié)省非常多。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容