欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android多套環(huán)境的維護(hù)思路詳解

 更新時間:2022年11月22日 14:35:30   作者:TimeFine  
這篇文章主要為大家介紹了Android多套環(huán)境的維護(hù)思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、多套環(huán)境要注意的問題

記錄一下項(xiàng)目中多套環(huán)境維護(hù)的一種思路。

1、方便使用靈活配置

2、配置安全不會被覆寫

3、擴(kuò)展靈活

4、安裝包可動態(tài)切換環(huán)境,方便測試人員使用

二、解決思路

1、Android中的Properties文件是只讀的,打包后不可修改,所以用Properties文件維護(hù)所有的配置。

2、在一個安裝包內(nèi)動態(tài)切換環(huán)境,方便測試人員切換使用,這一點(diǎn)用MMKV來動態(tài)存儲。為了防止打包時可能出現(xiàn)的錯誤,這一點(diǎn)也需要Properties文件來控制。

三、Properties文件的封裝

package com.abc.kotlinstudio
import android.content.Context
import java.io.IOException
import java.util.*
object PropertiesUtil {
    private var pros: Properties? = null
    fun init(c: Context) {
        pros = Properties()
        try {
            val input = c.assets.open("appConfig.properties")
            pros?.load(input)
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    private fun getProperty(key: String, default: String): String {
        return pros?.getProperty(key, default) ?: default
    }
    /**
     * 判斷是否是國內(nèi)版本
     */
    fun isCN(): Boolean {
        return getProperty("isCN", "true").toBoolean()
    }
    /**
     * 判斷是否是正式環(huán)境
     */
    fun isRelease(): Boolean {
        return getProperty("isRelease", "false").toBoolean()
    }
    /**
     * 獲取版本的環(huán)境 dev test release
     * 如果isRelease為true就讀Properties文件,為false就讀MMKV存儲的值
     */
    fun getEnvironment(): Int = if (isRelease()) {  
        when (getProperty("environment", "test")) {
            "dev" -> {
                GlobalUrlConfig.EnvironmentConfig.DEV.value
            }
            "test" -> {
                GlobalUrlConfig.EnvironmentConfig.TEST.value
            }
            "release" -> {
                GlobalUrlConfig.EnvironmentConfig.RELEASE.value
            }
            else -> {
                GlobalUrlConfig.EnvironmentConfig.TEST.value
            }
        }
    } else {
        when (CacheUtil.getEnvironment(getProperty("environment", "test"))) {
            "dev" -> {
                GlobalUrlConfig.EnvironmentConfig.DEV.value
            }
            "test" -> {
                GlobalUrlConfig.EnvironmentConfig.TEST.value
            }
            "release" -> {
                GlobalUrlConfig.EnvironmentConfig.RELEASE.value
            }
            else -> {
                GlobalUrlConfig.EnvironmentConfig.TEST.value
            }
        }
    }
    /**
     * 獲取國內(nèi)外環(huán)境
     */
    fun getCN(): Int = if (isRelease()) {
        when (getProperty("isCN", "true")) {
            "true" -> {
                GlobalUrlConfig.CNConfig.CN.value
            }
            "false" -> {
                GlobalUrlConfig.CNConfig.I18N.value
            }
            else -> {
                GlobalUrlConfig.CNConfig.CN.value
            }
        }
    } else {
        when (CacheUtil.getCN(getProperty("isCN", "true"))) {
            "true" -> {
                GlobalUrlConfig.CNConfig.CN.value
            }
            "false" -> {
                GlobalUrlConfig.CNConfig.I18N.value
            }
            else -> {
                GlobalUrlConfig.CNConfig.CN.value
            }
        }
    }
}

注意二點(diǎn),打包時如果Properties文件isRelease為true則所有配置都讀Properties文件,如果為false就讀MMKV存儲的值;如果MMKV沒有存儲值,默認(rèn)值也是讀Properties文件。

內(nèi)容比較簡單:

isCN = true   //是否國內(nèi)環(huán)境 
isRelease = false   //是否release,比如日志的打印也可以用這個變量控制
#dev test release   //三種環(huán)境
environment = dev  //環(huán)境切換

四、MMKV封裝

package com.abc.kotlinstudio
import android.os.Parcelable
import com.tencent.mmkv.MMKV
import java.util.*
object CacheUtil {
    private var userId: Long = 0
    //公共存儲區(qū)的ID
    private const val STORAGE_PUBLIC_ID = "STORAGE_PUBLIC_ID"
    //------------------------公共區(qū)的鍵------------------
    //用戶登錄的Token
    const val KEY_PUBLIC_TOKEN = "KEY_PUBLIC_TOKEN"
    //------------------------私有區(qū)的鍵------------------
    //用戶是否第一次登錄
    const val KEY_USER_IS_FIRST = "KEY_USER_IS_FIRST"
    /**
     * 設(shè)置用戶的ID,根據(jù)用戶ID做私有化分區(qū)存儲
     */
    fun setUserId(userId: Long) {
        this.userId = userId
    }
    /**
     * 獲取MMKV對象
     * @param isStoragePublic true 公共存儲空間  false 用戶私有空間
     */
    fun getMMKV(isStoragePublic: Boolean): MMKV = if (isStoragePublic) {
        MMKV.mmkvWithID(STORAGE_PUBLIC_ID)
    } else {
        MMKV.mmkvWithID("$userId")
    }
    /**
     * 設(shè)置登錄后token
     */
    fun setToken(token: String) {
        put(KEY_PUBLIC_TOKEN, token, true)
    }
    /**
     * 獲取登錄后token
     */
    fun getToken(): String = getString(KEY_PUBLIC_TOKEN)
    /**
     * 設(shè)置MMKV存儲的環(huán)境
     */
    fun putEnvironment(value: String) {
        put("environment", value, true)
    }
    /**
     * 獲取MMKV存儲的環(huán)境
     */
    fun getEnvironment(defaultValue: String): String {
        return getString("environment", true, defaultValue)
    }
    /**
     * 設(shè)置MMKV存儲的國內(nèi)外環(huán)境
     */
    fun putCN(value: String) {
        put("isCN", value, true)
    }
    /**
     * 獲取MMKV存儲的國內(nèi)外環(huán)境
     */
    fun getCN(defaultValue: String): String {
        return getString("isCN", true, defaultValue)
    }
//------------------------------------------基礎(chǔ)方法區(qū)-----------------------------------------------
    /**
     * 基礎(chǔ)數(shù)據(jù)類型的存儲
     * @param key 存儲的key
     * @param value 存儲的值
     * @param isStoragePublic 是否存儲在公共區(qū)域 true 公共區(qū)域 false 私有區(qū)域
     */
    fun put(key: String, value: Any?, isStoragePublic: Boolean): Boolean {
        val mmkv = getMMKV(isStoragePublic)
        return when (value) {
            is String -> mmkv.encode(key, value)
            is Float -> mmkv.encode(key, value)
            is Boolean -> mmkv.encode(key, value)
            is Int -> mmkv.encode(key, value)
            is Long -> mmkv.encode(key, value)
            is Double -> mmkv.encode(key, value)
            is ByteArray -> mmkv.encode(key, value)
            else -> false
        }
    }
    /**
     * 這里使用安卓自帶的Parcelable序列化,它比java支持的Serializer序列化性能好些
     * @param isStoragePublic 是否存儲在公共區(qū)域 true 公共區(qū)域 false 私有區(qū)域
     */
    fun <T : Parcelable> put(key: String, t: T?, isStoragePublic: Boolean): Boolean {
        if (t == null) {
            return false
        }
        return getMMKV(isStoragePublic).encode(key, t)
    }
    /**
     * 存Set集合的數(shù)據(jù)
     * @param isStoragePublic 是否存儲在公共區(qū)域 true 公共區(qū)域 false 私有區(qū)域
     */
    fun put(key: String, sets: Set<String>?, isStoragePublic: Boolean): Boolean {
        if (sets == null) {
            return false
        }
        return getMMKV(isStoragePublic).encode(key, sets)
    }
    /**
     * 取數(shù)據(jù),因?yàn)樗接写鎯^(qū)用的多,所以這里給了默認(rèn)參數(shù)為私有區(qū)域,如果公共區(qū)域取要記得改成true.下同
     */
    fun getInt(key: String, isStoragePublic: Boolean = false, defaultValue: Int = 0): Int {
        return getMMKV(isStoragePublic).decodeInt(key, defaultValue)
    }
    fun getDouble(
        key: String,
        isStoragePublic: Boolean = false,
        defaultValue: Double = 0.00
    ): Double {
        return getMMKV(isStoragePublic).decodeDouble(key, defaultValue)
    }
    fun getLong(key: String, isStoragePublic: Boolean = false, defaultValue: Long = 0L): Long {
        return getMMKV(isStoragePublic).decodeLong(key, defaultValue)
    }
    fun getBoolean(
        key: String,
        isStoragePublic: Boolean = false,
        defaultValue: Boolean = false
    ): Boolean {
        return getMMKV(isStoragePublic).decodeBool(key, defaultValue)
    }
    fun getFloat(key: String, isStoragePublic: Boolean = false, defaultValue: Float = 0F): Float {
        return getMMKV(isStoragePublic).decodeFloat(key, defaultValue)
    }
    fun getByteArray(key: String, isStoragePublic: Boolean = false): ByteArray? {
        return getMMKV(isStoragePublic).decodeBytes(key)
    }
    fun getString(
        key: String,
        isStoragePublic: Boolean = false,
        defaultValue: String = ""
    ): String {
        return getMMKV(isStoragePublic).decodeString(key, defaultValue) ?: defaultValue
    }
    /**
     * getParcelable<Class>("")
     */
    inline fun <reified T : Parcelable> getParcelable(
        key: String,
        isStoragePublic: Boolean = false
    ): T? {
        return getMMKV(isStoragePublic).decodeParcelable(key, T::class.java)
    }
    fun getStringSet(key: String, isStoragePublic: Boolean = false): Set<String>? {
        return getMMKV(isStoragePublic).decodeStringSet(key, Collections.emptySet())
    }
    fun removeKey(key: String, isStoragePublic: Boolean = false) {
        getMMKV(isStoragePublic).removeValueForKey(key)
    }
    fun clearAll(isStoragePublic: Boolean = false) {
        getMMKV(isStoragePublic).clearAll()
    }
}

五、URL的配置

假設(shè)有國內(nèi)外以及host、h5_host環(huán)境 :

object GlobalUrlConfig {
    private val BASE_HOST_CN_DEV = "https://cn.dev.abc.com"
    private val BASE_HOST_CN_TEST = "https://cn.test.abc.com"
    private val BASE_HOST_CN_RELEASE = "https://cn.release.abc.com"
    private val BASE_HOST_I18N_DEV = "https://i18n.dev.abc.com"
    private val BASE_HOST_I18N_TEST = "https://i18n.test.abc.com"
    private val BASE_HOST_I18N_RELEASE = "https://i18n.release.abc.com"
    private val BASE_HOST_H5_CN_DEV = "https://cn.dev.h5.abc.com"
    private val BASE_HOST_H5_CN_TEST = "https://cn.test.h5.abc.com"
    private val BASE_HOST_H5_CN_RELEASE = "https://cn.release.h5.abc.com"
    private val BASE_HOST_H5_I18N_DEV = "https://i18n.dev.h5.abc.com"
    private val BASE_HOST_H5_I18N_TEST = "https://i18n.test.h5.abc.com"
    private val BASE_HOST_H5_I18N_RELEASE = "https://i18n.release.h5.abc.com"
    private val baseHostList: List<List<String>> = listOf(
        listOf(
            BASE_HOST_CN_DEV,
            BASE_HOST_CN_TEST,
            BASE_HOST_CN_RELEASE
        ), listOf(
            BASE_HOST_I18N_DEV,
            BASE_HOST_I18N_TEST,
            BASE_HOST_I18N_RELEASE
        )
    )
    private val baseHostH5List: List<List<String>> = listOf(
        listOf(
            BASE_HOST_H5_CN_DEV,
            BASE_HOST_H5_CN_TEST,
            BASE_HOST_H5_CN_RELEASE
        ), listOf(
            BASE_HOST_H5_I18N_DEV,
            BASE_HOST_H5_I18N_TEST,
            BASE_HOST_H5_I18N_RELEASE
        )
    )
    //base
    var BASE_HOST: String =
        baseHostList[PropertiesUtil.getCN()][PropertiesUtil.getEnvironment()]
    //base_h5    
    var BASE_H5_HOST: String =
        baseHostH5List[PropertiesUtil.getCN()][PropertiesUtil.getEnvironment()]
    enum class CNConfig(var value: Int) {
        CN(0), I18N(1)
    }
    enum class EnvironmentConfig(var value: Int) {
        DEV(0), TEST(1), RELEASE(2)
    }

六、測試人員可在打好的App動態(tài)切換

可以彈Dialog動態(tài)切換環(huán)境,下面為測試代碼:

//初始化
PropertiesUtil.init(this)
MMKV.initialize(this)
CacheUtil.setUserId(1000L)
val btSetCn = findViewById<AppCompatButton>(R.id.bt_set_cn)
val btSeti18n = findViewById<AppCompatButton>(R.id.bt_set_i8n)
val btSetDev = findViewById<AppCompatButton>(R.id.bt_set_dev)
val btSetTest = findViewById<AppCompatButton>(R.id.bt_set_test)
val btSetRelease = findViewById<AppCompatButton>(R.id.bt_set_release)
//App內(nèi)找個地方彈一個Dialog動態(tài)修改下面的參數(shù)即可。
btSetCn.setOnClickListener {
    CacheUtil.putCN("true")
    //重啟App(AndroidUtilCode工具類里面的方法)
    AppUtils.relaunchApp(true)
}
btSeti18n.setOnClickListener {
    CacheUtil.putCN("false")
    AppUtils.relaunchApp(true)
}
btSetDev.setOnClickListener {
    CacheUtil.putEnvironment("dev")
    AppUtils.relaunchApp(true)
}
btSetTest.setOnClickListener {
    CacheUtil.putEnvironment("test")
    AppUtils.relaunchApp(true)
}
btSetRelease.setOnClickListener {
    CacheUtil.putEnvironment("release")
    AppUtils.relaunchApp(true)
}

總結(jié)

一般會有4套環(huán)境: 開發(fā)環(huán)境,測試環(huán)境,預(yù)發(fā)布環(huán)境,正式環(huán)境。如果再區(qū)分國內(nèi)外則乘以2。除了base的主機(jī)一般還會引入其他主機(jī),比如h5的主機(jī),這樣會導(dǎo)致整個環(huán)境復(fù)雜多變。

剛開始是給測試打多渠道包,測試抱怨切環(huán)境,頻繁卸載安裝App很麻煩,于是做了這個優(yōu)化。上線時記得把Properties文件isRelease設(shè)置為true,則發(fā)布的包就不會有問題,這個一般都不會忘記,風(fēng)險很小。相比存文件或者其他形式安全很多。

寫的比較匆忙,代碼略粗糙,主要體現(xiàn)思路。

以上就是Android多套環(huán)境的維護(hù)思路詳解的詳細(xì)內(nèi)容,更多關(guān)于Android多套環(huán)境維護(hù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論