Android 换肤的思路

结合代码看更好理解: https://github.com/zcwfeng/zcw_android_demo/tree/master/android_skin

换肤思路:

我们需要解决的几个问题

1.什么时候换肤?

xml加载前换肤,如果xml加载后换肤,用户将会看见换肤之前的色彩,用户体验不好。

2.皮肤是什么?

皮肤就是apk,是一个资源包,包含了颜色、图片等。

3.什么样的控件应该进行换肤?

包含背景图片的控件,例如textView文字颜色。

4.皮肤与已安装的资源如何匹配?

资源名字匹配

思路解析

首先换肤的基本思想是更换资源索引和路径(图片,颜色值,背景等等),需要注意就是规划好,比如颜色值 要提出来到color.xml 中,不要写死成“#xxxxxx”

我们制作一个皮肤插件包,这个就由一个资源apk来承载,用到了系统application初始化时候的原理,会在LoadApk的时候加载Resources通过AssertManager进行资源的加载



实现这个就需要在布局Xml加载之前setContentView()替换掉,这样用户体验比较好(之后替换也是可以但是会不自然发生切换)

根据源码:需要在自己代码setContentView 之前,自己实现一个SkinFactory extends Factory2 。 并且把这个SkinFactory 设置
类似,LayoutInflaterCompat.setFactory(getLayoutInflater(),skinFactory);

这里需要注意,setFactory 之后会有一个标记 为 true,我们需要用反射吧这个true改成false,来避免只能设置一次

整体思路:

用一个SkinFactoryManager 管理类处理资源管理过滤。我们需要对要更换的view进行扫描记录,过滤和更换对应的属性和资源的路径等。

1. 设置自己实现Factory2 的SkinFactory  
2.我们需要 用一个数据结构记录下,我们应该换肤的View,然后过滤View的属性进行换肤替换

List<我们要更换的View>
    List<(View 的属性)>
        LIst<Paie(属性,view)>

3. 获取资源,通过SkinManager 加载apk 资源  
4. 执行更换,通过记录的List<View> 循环View 设置颜色,背景等属性

原理分析

四个方面去分析原理

1.UI布局流程分析
2.LayoutInflate原理
3.Android资源加载流程
4 .插件化换肤原理分析

UI 布局流程分析

一般情况下我们会有这两个入口

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);

ActivityThread------>main函数 是app启动过程,这个过程先忽略,

我们入口从performLaunchActivity开始

->「起始点」
我们跟踪到方法临时变量 window = r.mPendingRemoveWindow

    r.mPendingRemoveWindow  初始值 null

    继续搜索r.mPendingRemoveWindow = 找到赋值点

    r.mPendingRemoveWindow = r.window;

    继续搜索 r.window 赋值点,为null的不用看

    r.window = r.activity.getWindow();—————就是Activity上的属性mWindow

    目前位置看回到起始点,我们的赋值为空,只是拿到Activity的属性mWindow的引用

    随后我们会调用activity.attach

    Activity————mWindow = new PhoneWindow(this, window, activityConfigCallback);

「总结点1:--------------- 层次Activity---> PhoneWindow」

    上面的setContentView 就是在PhoneWindow中实现的,所以看下PhoneWindow的源代码

    installDecor();———其实就是 mDecor = new DecorView  中间做了写操作

    查看 mDector 就是PhoneWindow的成员

「总结点2: 层次 Activity——>PhoneWindow——->DecorView」

    installDector—> generateLayout

    看到源码注释:  Inflate the window decor 解析decor

    layoutResource 会有各种R.layout.xxxxx     这些其实都是AS中的模板等,还有一些其他的

    去Framework 源码搜索,screen_simplev 看下他的结构

-> screen_simple.xml  这就是我们的root布局文件

    <LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
          android:inflatedId="@+id/action_mode_bar"
          android:layout="@layout/action_mode_bar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:theme="?attr/actionBarTheme" />
<FrameLayout
     android:id="@android:id/content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:foregroundInsidePadding="false"
     android:foregroundGravity="fill_horizontal|top"
     android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

    如果我们的布局文件选定,就会调用
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)

    DectorVIew 就会调用 final View root = inflater.inflate(layoutResource, null);解析刚刚的布局

    再通过addView的方式把他加到DectorView

「总结点3: 层次 Activity——>PhoneWindow——->DecorView()---> 我们的content的布局xml」

PhoneWindow 再次 inflate ———> mLayoutInflater.inflate(layoutResID, mContentParent); 
而这个mContentParent 就是我们的DectorView

回到 PhoneWindow 创建generateLayout-》mContentParent

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

content 就是我们布局中的content。  所以  DectorView—mContentParent—FrameLayout

最终调用了LayouInflater->inflate 方法


    根据Tag创建临时temp
        
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);


if (!attachToRoot) {
    // Set the layout params for temp if we are not
    // attaching. (If we are, we use addView, below)
    temp.setLayoutParams(params);
}

判断如果attchToRoot = false 我们的参数就会通过xml 获取属性写进去 如果true 就需要我们addView 手动将布局参数填写进去

LayoutInflater继续往下看createViewFromTag ——> createView()
看到是通过根据view name进行反射构造方法。

继续看tryCreateView 在onCreateVIew 之前
会有三个工厂,Factory2(包含父类的),Factory(无相关父类),Factory2 privateFactory. 如果工厂不为null后面的onCreateView就会被拦截

——————Hook点

所以换肤的思路,一个是 实现Factory,对 onCrateView 提前进行拦截

第二个思路,重写Inflate(会有一定的侵入性质)

资源加载的原理

apk 包 resource.asrc 二进制文件信息

-> 入口:handleBindApplication

看到一个关键信息

mInstrumentation = new Instrumentation(); 创建了一个仪表盘


初始化我们的Application
new ContextImpl
然后获取我们的资源
LoadedApk->getResources-> getOrCreateResources——> createResourcesImpl(ResourceImpl)

->final AssetManager assets = createAssetManager(key);

总结点1:加载的层次调用

LoadedAPK
    Resources
        AssertManager


根据mInstrumentation 信息调用Application初始化
mInstrumentation.callApplicationOnCreate(app);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343