Android 多渠道打包總結

爲何要多渠道

App通常會上傳多個應用商店,例如應用寶、小米、華爲、OPPO等。java

  1. 如今的手機都自帶應用商店,用戶通常會在自帶應用商店上搜索。android

  2. 產品、運營可統計使用不一樣手機品牌的用戶的消費狀況,App日活、月活、UV、PV等。api

傳統方式打包的痛點

若是App不是很出名,通常都須要本身打包上傳,除非像微信這種,商店自動抓包進行上傳。曾經參加過一個技術沙龍,微信的工程師說到,他們的熱更新技術,若是使用了360加固進行,就是失效,可是360應用商店必須加固後才能上傳,因此他們就不上傳了,最後360應用商店也仍是抓包上傳了(可見有實力就是不同)。微信

不一樣的渠道,可能App名字不一樣,App圖標Icon不一樣,若是手動替換的話,體力勞動特別累人,筆者就曾經作過一個app,須要給不一樣的景區生成app,每一個景區的圖標和app名字都不同,傳統方式就是打包完一個,手動替換再進行打包,10個還好,慢慢後面景區愈來愈多,上升了50個,工做量可想而知,並且還容易錯,每一個都須要手動檢查一遍(曾經花2個小時,對着屏幕一個個替換,眼睛都花了,天然容易出錯!),這樣進行幾回後,意識到這種重複勞動不該該由咱們來作,應該交給機器呀!app

Gradle配置多渠道打包

後面咱們搜索了一下資料,決定使用Gradle配置的方式進行打包,但他也有優缺點。ide

  • 優勢:就是每一個包都只須要配置好要替換的文件和佔位,便可開始打包。函數

  • 缺點:每次打包一個渠道包,都須要編譯一次,項目很是大的時候,可謂很是耗時!工具

Manifest佔位,動態替換meta標籤值,動態標識渠道

實現步驟

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

    • 使用productFlavors,添加2個渠道,例如360和應用寶。
    • 注意若是是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. 清單文件增長佔位標識
    • 例如咱們使用友盟進行渠道記錄,增長一個meta_data標籤,標籤名爲UMENG_CHANNEL,值是佔位符標識${channel}。

注意這個標識要和第一步的build.gradle文件中的productFlavors配置一致微信支付

  • 例如build.gradle。
//佔位名:channel,值:app_360
manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]
  • meta標籤。
<!-- 佔位名: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標籤,獲取其值便可。
//渠道工具類
public class ChannelUtil {
    /** * 獲取app包內的渠道標識 */
    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 "";
        }
    }
}

//調用
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(), "當前渠道:" + channel, Toast.LENGTH_SHORT).show();
    }
}

給不一樣的編譯環境配置清單Key

除了productFlavors中配置manifestPlaceholders生成渠道包外,在編譯環境buildTypes下也可使用,例如不一樣編譯環境下,高德地圖的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接入問題

通常咱們都會使用多個Lib進行分模塊開發,例如支付模塊的Pay庫中的微信回調Activity,7.0獲取文件須要使用FileProvider等,都須要制定包名,可是咱們的lib須要提供給其餘App進行接入,清單文件的配置須要接入方配置,若是配置比較多,並且後續版本須要更換配置,都須要接入方從新配置會比較麻煩,那麼可不能夠將配置留在Lib庫中,經過動態配置的方式動態配置呢。

  1. 例如微信支付的回調Activity配置,經過applicationId佔位,能夠動態獲取到接入方的包名,只要按照約定,在包名下創建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>

        <!-- 動態配置微信回調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進行佔位。
<?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>

動態指定App名稱

通過前面的配置已經足夠配置出不一樣渠道標識的渠道包了,咱們還能夠繼續拓展下,例如不一樣渠道的App名字不一樣。(通常由於有的應用商店會以App名稱而被拒...)

  1. 修改build.gradle文件。

manifestPlaceholders中添加一個標識,app_name,值爲對應的名稱。例如360渠道爲360渠道包,應用寶渠道爲應用寶渠道。

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

動態指定App包名

例如咱們debug環境下,但願測試包和正式包可以共存,那麼共存就須要包名不一樣,因此能夠對包名進行替換或者加後綴。

  1. 修改build.gradle文件。
    • applicationIdSuffix,給包名添加後綴。例如測試版加上.internal後綴。
    • applicationId,整個替換包名。(通常不會這麼幹,只是提一下)
//3.0Gradle開始必須添加緯度
flavorDimensions "default"
//多渠道打包配置
productFlavors {
    //開發版
    developer {
        //測試版加包名後綴,方便和正式版共存
        applicationIdSuffix ".internal"
        manifestPlaceholders = [app_name: "開發版", channel : "app_internal"]
    }
    production {
        //也能夠徹底替換包名
        applicationId "me.zh.demo"
        manifestPlaceholders = [app_name: "正式版",
                                channel: "app_internal"]
    }
}

動態生成變量

開發中,Log打印是必不可少的,可是咱們在正式環境是須要將Log打印去掉的,常規作法就是打印函數前加一個LogEnable的變量,將打印調用去掉。而咱們通常會在常量類中添加開關變量。

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

  • 指望:咱們但願不一樣buildType下的編譯環境,打印Log是不同的,例如buildType爲debug時,Log開關爲開,buildType爲release時,Log開關爲關。

  1. Gradle爲咱們提供了buildConfigField,用於動態生成變量在BuildConfig,那麼咱們給不一樣的buildType進行添加便可。
//編譯類型
buildTypes {
    debug {
        //打印開關變量
        buildConfigField("boolean", "LOG_ENABLE", "true")
        //省略其餘配置...
    }

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

不一樣渠道替換資源文件

能替換包名和App名字了,原覺得完畢,結果運營說,360商店咱們要出首發!圖標和啓動圖要加上360的標識!這時候就須要用到同結構、同名文件夾來替換了。

  1. 在和main文件夾下,創建同名渠道的文件夾,例如app_360渠道,創建的文件夾名爲app_360

  2. assets文件夾、res文件夾、以及AndroidManifest.xml清單文件結構都要和main中的一致。

  3. AppIcon同名便可替換。清單文件同名便可,內部特定不一樣的內容便可。

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

關於Java代碼多渠道

上面說到資源文件,創建同目錄進行替換,那麼Java代碼能夠同級目錄下,同名Java文件替換嗎?很遺憾的是不能夠,須要不一樣渠道,進行不一樣的邏輯時,只能經過剛纔ChannelUtil獲取渠道信息,進行邏輯判斷了。

總結

使用Gradle來進行配置,大大減小了咱們的工做量,媽媽不再用擔憂我打渠道包須要2小時啦,至於gradle每構建一個渠道包都編譯一次問題,能夠單獨給assets設置一個配置文件,後續使用zip修改配置文件,達到只打一個包,其餘包都使用打包工具進行打包,時間能夠節省很是多。

做者:h2coder 連接:https://www.jianshu.com/p/600cd4af6e44 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索