Android源码时序图如下所示:

App源码如下所示:
package com.study.skin
import android.app.Application
import com.study.skinlib.SkinManager
class App : Application() {
override fun onCreate() {
super.onCreate()
SkinManager.init(this)
}
}
SkinManager源码如下所示,其源码如下所示:
package com.study.skinlib
import android.app.Application
import android.content.pm.PackageManager
import android.content.res.AssetManager
import android.content.res.Resources
import android.text.TextUtils
import com.study.skinlib.utils.SkinResource
import java.util.*
/**
* 皮肤管理类
*/
class SkinManager : Observable {
private lateinit var mApplication: Application
private constructor(application: Application) {
mApplication = application
//初始化皮肤路径首选项
SkinPreference.init(application)
//资源管理类 用于从app/皮肤中加载资源
SkinResource.init(application)
//给Application注册Activity生命周期监听类
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl(this))
//加载上次使用保存的皮肤
SkinPreference.getInstance()?.getSkin()?.apply {
loadSkin(this)
}
}
/**
* 加载皮肤
*/
fun loadSkin(skinPath: String?) {
if (TextUtils.isEmpty(skinPath)) {
//还原默认皮肤
SkinPreference.getInstance()?.reset()
SkinResource.getInstance()?.reset()
} else {
try {
//宿主app的 resources;
val appResource = mApplication.getResources()
//
//反射创建AssetManager 与 Resource
val assetManager = AssetManager::class.java.newInstance()
//资源路径设置 目录或压缩包
val addAssetPath = assetManager.javaClass.getMethod(
"addAssetPath",
String::class.java
)
addAssetPath.invoke(assetManager, skinPath)
//根据当前的设备显示器信息 与 配置(横竖屏、语言等) 创建Resources
val skinResource = Resources(
assetManager,
appResource.getDisplayMetrics(),
appResource.getConfiguration()
)
//获取外部Apk(皮肤包) 包名
val mPm = mApplication.getPackageManager()
val info = mPm.getPackageArchiveInfo(
skinPath, PackageManager
.GET_ACTIVITIES
)
val packageName = info!!.packageName
SkinResource.getInstance()?.applySkin(skinResource, packageName)
//记录
skinPath?.apply {
SkinPreference.getInstance()?.setSkin(this)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
//通知采集的View 更新皮肤
//被观察者改变 通知所有观察者
setChanged()
notifyObservers(null)
}
companion object {
@Volatile
private var instance: SkinManager? = null
/**
* 初始化操作
*/
fun init(mApplication: Application) {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = SkinManager(mApplication)
}
}
}
}
fun getInstance() = instance
}
}
SkinPreference源码如下所示:
package com.study.skinlib
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
/**
* 皮肤路径首选项设置
*/
class SkinPreference {
private lateinit var mPref: SharedPreferences
private constructor(context: Context) {
mPref = context.getSharedPreferences(SKIN_SHARED, Context.MODE_PRIVATE)
}
/**
* 设置皮肤资源路径
*/
fun setSkin(skinPath: String) = mPref.edit().putString(KEY_SKIN_PATH, skinPath).apply()
/**
* 重置皮肤路径
*/
fun reset() = mPref.edit().remove(KEY_SKIN_PATH).apply()
/**
* 获取皮肤路径
*/
fun getSkin(): String? = mPref.getString(KEY_SKIN_PATH, null)
companion object {
val SKIN_SHARED = "skins"
val KEY_SKIN_PATH = "skin-path"
@Volatile
private var instance: SkinPreference? = null
fun init(context: Context) {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = SkinPreference(context)
}
}
}
}
fun getInstance() = instance
}
}
SkinResource源码如下所示:
package com.study.skinlib.utils
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.text.TextUtils
class SkinResource {
//app原始的resource
private lateinit var mAppResource: Resources
//皮肤的resource
private var mSkinResources: Resources? = null
//皮肤包的名字
private lateinit var mSkinPkgName: String
//是否使用默认皮肤包
private var isDefaultSkin = true
private constructor(context: Context) {
mAppResource = context.resources
}
fun applySkin(resources: Resources, pkgName: String) {
mSkinResources = resources
mSkinPkgName = pkgName
//是否使用默认皮肤
isDefaultSkin = TextUtils.isEmpty(pkgName) || resources == null
}
/**
* 1.通过原始app中的resId(R.color.XX)获取到自己的 名字
* 2.根据名字和类型获取皮肤包中的ID
*/
fun getIdentifier(resId: Int): Int {
if (isDefaultSkin) {
return resId
}
//获取资源的名称和类型
val resName = mAppResource.getResourceEntryName(resId)
val resType = mAppResource.getResourceTypeName(resId)
//通过插件的Resources获取到插件中相同名称和类型的id
return mSkinResources?.getIdentifier(resName, resType, mSkinPkgName) ?: 0
}
/**
* 输入主APP的ID,到皮肤APK文件中去找到对应ID的颜色值
*
* @param resId
* @return
*/
fun getColor(resId: Int): Int {
if (isDefaultSkin) {
return mAppResource.getColor(resId)
}
val skinId = getIdentifier(resId)
return if (skinId == 0) {
mAppResource.getColor(resId)
} else mSkinResources?.getColor(skinId) ?: 0
}
fun getColorStateList(resId: Int): ColorStateList? {
if (isDefaultSkin) {
return mAppResource.getColorStateList(resId)
}
val skinId = getIdentifier(resId)
return if (skinId == 0) {
mAppResource.getColorStateList(resId)
} else mSkinResources?.getColorStateList(skinId)
}
fun getDrawable(resId: Int): Drawable? {
if (isDefaultSkin) {
return mAppResource.getDrawable(resId)
}
//通过 app的resource 获取id 对应的 资源名 与 资源类型
//找到 皮肤包 匹配 的 资源名资源类型 的 皮肤包的 资源 ID
val skinId = getIdentifier(resId)
return if (skinId == 0) {
mAppResource.getDrawable(resId)
} else mSkinResources?.getDrawable(skinId)
}
/**
* 可能是Color 也可能是drawable
*
* @return
*/
fun getBackground(resId: Int): Any? {
//根据资源id获取资源类型
val resourceTypeName = mAppResource.getResourceTypeName(resId)
return if ("color" == resourceTypeName) {
getColor(resId)
} else {
// drawable
getDrawable(resId)
}
}
/**
* 重置资源
*/
fun reset() {
mSkinResources = null
mSkinPkgName = ""
isDefaultSkin = true
}
companion object {
@Volatile
private var instance: SkinResource? = null
/**
* 初始化操作
*/
fun init(context: Context) {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = SkinResource(context)
}
}
}
}
fun getInstance() = instance
}
}
ActivityLifecycleCallbacksImpl源码如下所示:
package com.study.skinlib
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.ArrayMap
import android.view.LayoutInflater
import androidx.core.view.LayoutInflaterCompat
import com.study.skinlib.utils.SkinThemeUtils
import java.util.*
/**
* Activity生命周期回调管理类
*/
class ActivityLifecycleCallbacksImpl : Application.ActivityLifecycleCallbacks {
private var mObserable: Observable
private val mLayoutInflaterFactories = ArrayMap<Activity, SkinLayoutInflaterFactory>()
public constructor(observable: Observable){
mObserable = observable
}
/**
* Activity的onCreate方法中dispatchActivityCreated(savedInstanceState);分发事件
* setContentView方法之前调用
*/
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
/**
* 更新状态栏
*/
SkinThemeUtils.updateStatusBarColor(activity)
/**
* 更新布局视图
*/
//获得Activity的布局加载器
val layoutInflater = activity.layoutInflater
try {
//Android 布局加载器 使用 mFactorySet 标记是否设置过Factory
//如设置过抛出一次
//设置 mFactorySet 标签为false
val field = LayoutInflater::class.java.getDeclaredField("mFactorySet")
field.isAccessible = true
field.setBoolean(layoutInflater, false)
} catch (e: Exception) {
e.printStackTrace()
}
//使用factory2 设置布局加载工程
val skinLayoutInflaterFactory = SkinLayoutInflaterFactory(activity)
//内部也是通过反射来修改layoutInflater的值
LayoutInflaterCompat.setFactory2(layoutInflater, skinLayoutInflaterFactory)
mLayoutInflaterFactories[activity] = skinLayoutInflaterFactory
mObserable.addObserver(skinLayoutInflaterFactory)
}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityDestroyed(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
}
SkinLayoutInflaterFactory实现了Factory2,其源码如下所示:
package com.study.skinlib
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import com.study.skinlib.utils.SkinThemeUtils
import java.lang.Exception
import java.lang.reflect.Constructor
import java.util.*
/**
* 皮肤控件填充器
*/
class SkinLayoutInflaterFactory : LayoutInflater.Factory2, Observer {
lateinit var mActivity: Activity
lateinit var skinAttribute: SkinAttribute
public constructor(activity: Activity) {
mActivity = activity
skinAttribute = SkinAttribute()
}
/**
* 如果利用Factory2创建控件,那么就会通过该方法来创建
*/
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? {
var view = createSDKView(name, context, attrs)
if (view == null) {
view = createView(name, context, attrs)
}
//记录我们所创建的view
if (view != null) {
//加载控件属性
//加载属性
skinAttribute.look(view, attrs)
}
return view
}
fun createSDKView(
name: String,
context: Context,
attrs: AttributeSet
): View? {
//如果包含,说明是自定义控件或者Google提供的扩展view
if (-1 != name.indexOf(".")) {
return null
}
//不包含".",则需要在name前面加上包名,通过反射创建控件
for (prefix in mClassPrefixList) {
var view = createView(prefix + name, context, attrs)
if (view != null) {
return view
}
}
return null
}
/**
* 创建view
*/
private fun createView(qualityName: String, context: Context, attrs: AttributeSet): View? {
var constructor = findConstructor(context, qualityName)
return constructor?.newInstance(context, attrs) ?: null
}
/**
* 查找构造函数
*/
private fun findConstructor(context: Context, qualityName: String): Constructor<out View>? {
var constructor = mConstructorMap[qualityName]
if (constructor == null) {
try {
val clazz = context.classLoader.loadClass(qualityName).asSubclass(View::class.java)
constructor = clazz.getConstructor(*mConstructorSignature)
mConstructorMap[qualityName] = constructor
} catch (e: Exception) {
Log.e("factory::", e.message)
}
}
return constructor
}
/**
* Factory2的实现类不调用该接口
*/
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? = null
/**
* 观察者接受到被观察的改变通知调用该方法
*/
override fun update(o: Observable?, arg: Any?) {
SkinThemeUtils.updateStatusBarColor(mActivity)
skinAttribute.applySkin()
}
companion object {
/**
* 控件的报名
*/
val mClassPrefixList = arrayOf(
"android.widget.",
"android.webkit.",
"android.app.",
"android.view."
)
/**
* 创建view的构造函数
*/
val mConstructorSignature = arrayOf(
Context::class.java, AttributeSet::class.java
)
/**
* view与构造函数的map
*/
val mConstructorMap = hashMapOf<String, Constructor<out View>>()
}
}
SkinAttribute的源码如下所示:
package com.study.skinlib
import android.util.AttributeSet
import android.view.View
import com.study.skinlib.data.SkinPair
import com.study.skinlib.utils.SkinThemeUtils
/**
* 页面控件属性管理器
*/
class SkinAttribute {
private val mAttributes = arrayListOf<String>()
init {
mAttributes.add("background")
mAttributes.add("src")
mAttributes.add("textColor")
mAttributes.add("drawableLeft")
mAttributes.add("drawableTop")
mAttributes.add("drawableRight")
mAttributes.add("drawableBottom")
}
//记录页面控件中View的属性
private val mSkinViews = arrayListOf<SkinView>()
/**
* 实施替换
*/
fun look(view: View, attrs: AttributeSet) {
var mSkinPars = arrayListOf<SkinPair>()
//0<attrs.attributeCount
for (i in 0 until attrs.attributeCount) {
//获取属性名
val attributeName = attrs.getAttributeName(i)
if (mAttributes.contains(attributeName)) {
//获取属性值,宿主Apk的android:background="@color/main_bg"中@color/main_bg的引用值,例如@2130968629
val attributeValue = attrs.getAttributeValue(i)
//例如针对color的属性的属性值写死
if (attributeValue.startsWith("#")) {
continue
}
var resId = 0
// 以 ?开头的表示使用 属性
if (attributeValue.startsWith("?")) {
val attrId = Integer.parseInt(attributeValue.substring(1))
resId = SkinThemeUtils.getResId(view.context, intArrayOf(attrId))[0]
} else {
// 正常以 @ 开头
resId = Integer.parseInt(attributeValue.substring(1))
}
val skinPair = SkinPair(attributeName, resId)
mSkinPars.add(skinPair)
}
}
if (!mSkinPars.isEmpty() || view is SkinViewSupport) {
val skinView = SkinView(view, mSkinPars)
// 如果选择过皮肤 ,调用 一次 applySkin 加载皮肤的资源
skinView.applySkin()
mSkinViews.add(skinView)
}
}
/**
* 对所有的view中的所有的属性进行皮肤修改
*/
fun applySkin() {
for (mSkinView in mSkinViews) {
mSkinView.applySkin()
}
}
}
SkinView的源码如下所示:
package com.study.skinlib
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.ViewCompat
import com.study.skinlib.data.SkinPair
import com.study.skinlib.utils.SkinResource
/**
* 控件View及其使用的属性
*/
class SkinView(var view: View, var skinPairs: List<SkinPair>) {
/**
* 对控件的View修改其属性值
*/
fun applySkin() {
//自定义控件替换颜色
if (view is SkinViewSupport) {
(view as SkinViewSupport).applySkin()
}
var left: Drawable? = null
var top: Drawable? = null
var right: Drawable? = null
var bottom: Drawable? = null
for (skinPair in skinPairs) {
when (skinPair.attributeName) {
"background" -> {
var background = SkinResource.getInstance()?.getBackground(skinPair
.resId)
//背景可能是 @color 也可能是 @drawable
if (background is Int) {
view.setBackgroundColor(background)
} else {
ViewCompat.setBackground(view, background as Drawable)
}
}
"src" -> {
var background = SkinResource.getInstance()?.getBackground(skinPair
.resId)
if (background is Int) {
(view as ImageView).setImageDrawable(ColorDrawable(background))
} else {
(view as ImageView).setImageDrawable(background as Drawable)
}
}
"textColor" -> {
(view as TextView).setTextColor(SkinResource.getInstance()?.getColorStateList(skinPair.resId))
}
"drawableLeft" -> {
left = SkinResource.getInstance()?.getDrawable(skinPair.resId)
}
"drawableTop" -> {
top = SkinResource.getInstance()?.getDrawable(skinPair.resId)
}
"drawableRight" -> {
right = SkinResource.getInstance()?.getDrawable(skinPair.resId)
}
"drawableBottom" -> {
bottom = SkinResource.getInstance()?.getDrawable(skinPair.resId)
}
else -> {
}
}
}
if (view is TextView) {
if (left != null || top != null || right != null || bottom != null) {
(view as TextView).setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom)
}
}
}
}