Android setContentView加载流程以及换肤应用

一、首先从Activity的启动讲起

1、Activity的启动是通过ActivityThread的performLaunchActivity开始。
2、然后创建Activity的上下文 ContextImpl appContext = createBaseContextForActivity()
3、然后创建Activity( activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent))
4、此时给Window 赋值,赋值是从Activity的成员变量中拿到的mWindow进行赋值的,但是当下为空。
5、直到执行Activity的attach方法之后才真正赋值 mWindow = new PhoneWindow()


6、然后执行Activity的onCreate方法,调用setContentView方法,实际调用的是PhoneWindow的setContentView方法
7、先创建DecorView,DecorView实际就是一个FrameLayout,然后生成mContentParent = generateLayout(mDecor);
8、generateLayout(mDecor) 方法先从framework加载 xxx.xml 布局文件,然后add到DecorView中。
9、mContentParent 这个就是 xxx.xml 布局文件中ID为content的控件 android:id="@android:id/content"
10、mLayoutInflater.inflate(layoutResID, mContentParent); 这句是把我们自己的布局文件add到 ID为content中的控件中。

具体把自己的布局文件add到content的操作

在add的过程中,先解析XML文件,然后创建出对应的View
XML文件view的创建过程:
// 判断View是不是全路径名,如果不是的,那么加载的就是 "android.view.XXX" 这个包下的View
createViewFromTag(){
	if (-1 == name.indexOf('.')) {
		view = onCreateView(parent, name, attrs);
    } else {
		view = createView(name, null, attrs);
	}
}
// 先从缓存中找对应的构造器,如果没有找到,那么就通过反射,获取构造器,然后放到map中,最后创建出这个View的实例对象返回
createView(){
	View view = constructor.newInstance(args);
}

在这里插入图片描述
二、换肤的资源加载流程
资源的加载:宿主APK加载皮肤APK,通过,AssetManager中的 addAssetPath方法进行的,后台是通过读流进行的
Android读取资源表面上是通过 context.getResource().getString()
实际是通过
第一:mResourcesImpl.getAssets()先获取Resource的实现 ResourcesImpl
第二:然后通过Resource的实现ResourcesImpl获取AssetManager,
第三:最后都是通过AssetManager获取对应的资源

三、小插曲Java原生观察者模式的使用

public class Test {
    public static void main(String[] args) {
        // 这个是观察者
        ObserveUser user = new ObserveUser();
        // 被观察者
        ObservableUser ob = new ObservableUser();
        ob.addObserver(user);
        // 注意这块,必须要调用,否则通知不到观察者,看源码即可理解
        ob.setChanged2();
        ob.notifyObservers("sfasdf");
    }
}

public class ObservableUser extends Observable {
    @Override
    protected synchronized void setChanged() {
        super.setChanged();
    }
    public synchronized void setChanged2(){
        setChanged();
    }
}

// 观察者
public class ObserveUser implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("----------,"+ arg);
    }
}

四、简单的换肤库
全局的管理,被观察者,通知所有观察者,以方便换肤,这个库的写的有些简单,只能用作Demo,如果想要商用的话,可能性能方面不是很合适。

// 被观察者,
public class SkinManager extends Observable {
    private volatile static SkinManager instance;
    /**
     * Activity生命周期回调
     */
    private Application mContext;

    /**
     * 初始化 必须在Application中先进行初始化
     *
     * @param application
     */
    public static void init(Application application) {
        if (instance == null) {
            synchronized (SkinManager.class) {
                if (instance == null) {
                    instance = new SkinManager(application);
                }
            }
        }
    }

    private SkinManager(Application application) {
        mContext = application;
        //共享首选项 用于记录当前使用的皮肤
        SkinPreference.init(application);
        //资源管理类 用于从 app/皮肤 中加载资源
        SkinResources.init(application);
        //注册Activity生命周期,并设置被观察者
        ApplicationActivityLifecycle skinActivityLifecycle = new ApplicationActivityLifecycle(this);
        application.registerActivityLifecycleCallbacks(skinActivityLifecycle);
        //加载上次使用保存的皮肤
        loadSkin(SkinPreference.getInstance().getSkin());
    }

    public static SkinManager getInstance() {
        return instance;
    }

    /**
     * 记载皮肤并应用
     *
     * @param skinPath 皮肤路径 如果为空则使用默认皮肤
     */
    public void loadSkin(String skinPath) {
        if (TextUtils.isEmpty(skinPath)) {
            //还原默认皮肤
            SkinPreference.getInstance().reset();
            SkinResources.getInstance().reset();
        } else {
            try {
                //宿主app的 resources;
                Resources appResource = mContext.getResources();
                //反射创建AssetManager 与 Resource
                AssetManager assetManager = AssetManager.class.newInstance();
                //资源路径设置 目录或压缩包
                Method addAssetPath = assetManager.getClass().getMethod("addAssetPath",
                        String.class);
                addAssetPath.invoke(assetManager, skinPath);
                //根据当前的设备显示器信息 与 配置(横竖屏、语言等) 创建Resources
                Resources skinResource = new Resources(assetManager, appResource.getDisplayMetrics
                        (), appResource.getConfiguration());

                //获取外部Apk(皮肤包) 包名
                PackageManager mPm = mContext.getPackageManager();
                // 这块一定要授予外部的读写权限
                PackageInfo info = mPm.getPackageArchiveInfo(skinPath, PackageManager
                        .GET_ACTIVITIES);
                String packageName = info.packageName;
                Log.w(SkinAttribute.TAG, " skinPackageName :" + packageName);
                SkinResources.getInstance().applySkin(skinResource, packageName);
                //记录
                SkinPreference.getInstance().setSkin(skinPath);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //通知采集的View 更新皮肤
        //被观察者改变 通知所有观察者
        setChanged();
        notifyObservers(null);
    }
}

找到所有Activity布局文件中的View,保存起来方便换肤

public class ApplicationActivityLifecycle implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onActivityStarted(@NonNull Activity activity) {}

    @Override
    public void onActivityResumed(@NonNull Activity activity) {}

    @Override
    public void onActivityPaused(@NonNull Activity activity) {}

    @Override
    public void onActivityStopped(@NonNull Activity activity) {}

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {}

    // 这个就是被观察者
    private Observable mObserable;
    private ArrayMap<Activity, SkinLayoutInflaterFactory> mActivityAndSkinFactory = new
            ArrayMap<>();
    public ApplicationActivityLifecycle(Observable observable) {
        mObserable = observable;
    }
    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
        LayoutInflater inflater = activity.getLayoutInflater();
        try {
            Field field = LayoutInflater.class.getDeclaredField("mFactorySet");
//            为什么这样写不行呢
//            Field field = inflater.getClass().getDeclaredField("mFactorySet");
            field.setAccessible(true);
            field.setBoolean(inflater,false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 这个是观察者
        SkinLayoutInflaterFactory skinLayoutInflaterFactory = new SkinLayoutInflaterFactory(activity);
        inflater.setFactory2(skinLayoutInflaterFactory);
        mActivityAndSkinFactory.put(activity,skinLayoutInflaterFactory);
        // 将观察者,添加到被观察者中
        mObserable.addObserver(skinLayoutInflaterFactory);

    }
    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {
        SkinLayoutInflaterFactory observer = mActivityAndSkinFactory.remove(activity);
        SkinManager.getInstance().deleteObserver(observer);
    }
}

用来接管系统view的生产过程,这个是观察者,当被观察者发生变化的时候,通知观察者

// 用来接管系统view的生产过程,这个是观察者,当被观察者发生变化的时候,通知观察者
public class SkinLayoutInflaterFactory implements LayoutInflater.Factory2, Observer {
    // 在这些包下找view
    private static final String[] mClassPrefixList = {
            "android.widget.",
            "android.webkit.",
            "android.app.",
            "android.view."
    };
    //记录对应VIEW的构造函数
    private static final Class<?>[] mConstructorSignature = new Class[]{
            Context.class, AttributeSet.class};


    private static final HashMap<String, Constructor<? extends View>> mConstructorMap =
            new HashMap<>();

    // 记录一个 Activity 对应一个 皮肤属性
    private SkinAttribute skinAttribute;
    // 用于获取窗口的状态框的信息
    private Activity activity;

    public SkinLayoutInflaterFactory(Activity activity){
        this.skinAttribute = new SkinAttribute();
        this.activity = activity;
    }

    @Nullable
    @Override
    public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attributeSet) {
        View view = createSDKView(name, context, attributeSet);
        Log.w(SkinAttribute.TAG,"View : " + view);
        if (null == view){
            // 这种基本就是 com.lgj.FlowLayout View
            view = createView(name, context, attributeSet);
        }
        // 这个就是我们自己的逻辑
        if (view != null){
            // 保存一个activity 所有需要换肤的view
            skinAttribute.look(view,attributeSet);
            SkinThemeUtils.updateStatusBarColor(activity);
        }
        return view;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull String s, @NonNull Context context, @NonNull AttributeSet attributeSet) {
        return null;
    }

    private View createSDKView(String name,Context context,AttributeSet attrs){
        // 表示view是自定义的或者是是,系统 androidx.viewpager.widget.ViewPager 的这种view
        if (-1 != name.indexOf(".")){
            return null;
        }
        // 这个是在所有涉及的包中进行尝试创建对应的View
        for (int i = 0; i < mClassPrefixList.length; i++) {
            View view = createView(mClassPrefixList[i] + name, context, attrs);
            if (view != null){
                return view;
            }
        }
        return null;
    }

    // 通过反射new 出对应的view
    private View createView(String name,Context context,AttributeSet attrs){
        Constructor<? extends View> constructor = findConstructor(context, name);
        try {
            View view = constructor.newInstance(context,attrs);
            return view;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 找构造方法
    private Constructor<? extends View> findConstructor(Context context,String name){
        Constructor<? extends View> constructor = mConstructorMap.get(name);
        if (constructor != null){
            return constructor;
        }
        try {
            Class<? extends View> clazz = context.getClassLoader().loadClass(name).asSubclass(View.class);
            constructor = clazz.getConstructor(mConstructorSignature);
            mConstructorMap.put(name, constructor);
            return constructor;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public void update(Observable observable, Object o) {
        SkinThemeUtils.updateStatusBarColor(activity);
        skinAttribute.applySkin();
    }
}

皮肤属性的记录者

public class SkinAttribute {
    public static String TAG = "LGJ : ";

    private static final List<String> mAttributes = new ArrayList<>();

    // 记录换肤的所有属性
    static {
        mAttributes.add("background");
        mAttributes.add("src");
        mAttributes.add("textColor");
        mAttributes.add("drawableLeft");
        mAttributes.add("drawableTop");
        mAttributes.add("drawableRight");
        mAttributes.add("drawableBottom");
    }

    // 记录换肤所要操作的 所有view
    ArrayList<SkinView> mSkinViewList = new ArrayList<>();


    // 找到这个View中所有需要替换的属性
    public void look(View view, AttributeSet attrs) {
        // 这个集合是装在所有需要替换的属性的
        ArrayList<SkinPair> mSkinPairs = new ArrayList<>();
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            String attributeName = attrs.getAttributeName(i);
            if (mAttributes.contains(attributeName)) {
                String attributeValue = attrs.getAttributeValue(i);
                Log.w(TAG, "attributeValue :" + attributeValue);
                //比如 color 以# 开头,表示写死的颜色,不可用于换肤
                if (attributeValue.startsWith("#")) {// 这种是没有办法替换的
                    continue;
                }
                int resId;
                if (attributeValue.startsWith("?")) {
                    int attrId = Integer.parseInt(attributeValue.substring(1));
                    resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0];
                } else {
                    // 正常以 @ 开头
                    resId = Integer.parseInt(attributeValue.substring(1));
                }

                mSkinPairs.add(new SkinPair(attributeName, resId));
            }
        }
        if (!mSkinPairs.isEmpty() || view instanceof SkinViewSupport) {
            SkinView skinView = new SkinView(view, mSkinPairs);
            // 如果选择过皮肤 ,调用 一次 applySkin 加载皮肤的资源
            skinView.applySkin();

            mSkinViewList.add(skinView);
        }
    }

    //对所有的view中的所有的属性进行皮肤修改
    public void applySkin() {
        for (SkinView mSkinView : mSkinViewList) {
            mSkinView.applySkin();
        }
    }

    // 这个是一个view对应的它身上所有换肤的集合
    static class SkinView {
        View view;
        ArrayList<SkinPair> skinPairsList;

        public SkinView(View view, ArrayList<SkinPair> skinPairsList) {
            this.view = view;
            this.skinPairsList = skinPairsList;
        }

        /**
         * 对一个View中的所有的属性进行修改
         */
        public void applySkin() {
            applySkinSupport();
            for (SkinPair skinPair : skinPairsList) {
                Drawable left = null, top = null, right = null, bottom = null;
                switch (skinPair.attributeName) {
                    case "background":
                        Object background = SkinResources.getInstance().getBackground(skinPair
                                .resId);
                        //背景可能是 @color 也可能是 @drawable
                        if (background instanceof Integer) {
                            view.setBackgroundColor((int) background);
                        } else {
                            ViewCompat.setBackground(view, (Drawable) background);
                        }
                        break;
                    case "src":
                        background = SkinResources.getInstance().getBackground(skinPair
                                .resId);
                        if (background instanceof Integer) {
                            ((ImageView) view).setImageDrawable(new ColorDrawable((Integer)
                                    background));
                        } else {
                            ((ImageView) view).setImageDrawable((Drawable) background);
                        }
                        break;
                    case "textColor":
                        ((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList
                                (skinPair.resId));
                        break;
                    case "drawableLeft":
                        left = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableTop":
                        top = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableRight":
                        right = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    case "drawableBottom":
                        bottom = SkinResources.getInstance().getDrawable(skinPair.resId);
                        break;
                    default:
                        break;
                }
                if (null != left || null != right || null != top || null != bottom) {
                    ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right,
                            bottom);
                }
            }
        }

        private void applySkinSupport() {
            if (view instanceof SkinViewSupport) {
                ((SkinViewSupport) view).applySkin();
            }
        }

    }

    static class SkinPair {
        // 属性名
        String attributeName;
        // 对应的资源id
        int resId;

        public SkinPair(String attributeName, int resId) {
            this.attributeName = attributeName;
            this.resId = resId;
        }
    }
}

三个工具类

public class SkinResources {
    // 皮肤包的包名
    private String mSkinPackageName;
    // 是否使用默认的皮肤
    private boolean isDefaultSkin = true;
    // 原始APP的resource
    private Resources mAppResources;
    // 皮肤包的resources
    private Resources mSkinResources;

    private SkinResources(Context context) {
        mAppResources = context.getResources();
    }
    //单例
    private volatile static SkinResources instance;
    public static void init(Context context) {
        if (instance == null) {
            synchronized (SkinResources.class) {
                if (instance == null) {
                    instance = new SkinResources(context);
                }
            }
        }
    }
    public static SkinResources getInstance() {
        return instance;
    }


    // 不想使用皮肤包的资源
    public void reset() {
        mSkinResources = null;
        mSkinPackageName = "";
        isDefaultSkin = true;
        Log.w(SkinAttribute.TAG," reset -------------");
    }
    // 应用皮肤包的资源
    public void applySkin(Resources resources, String pkgName) {
        mSkinResources = resources;
        mSkinPackageName = pkgName;
        //是否使用默认皮肤
        isDefaultSkin = TextUtils.isEmpty(pkgName) || resources == null;
    }
    /*
    * 1、通过原始APP中的resId,获取自己的名字
    * 2、根据名字获和类型,获取皮肤包中的ID
    * */

    public int getIdentifier(int resId){
        // 如果是默认的话,就直接返回,如果不是则获取皮肤包中的资源
        if (isDefaultSkin){
            return resId;
        }
        String resourceName = mAppResources.getResourceEntryName(resId);
        String typeName = mAppResources.getResourceTypeName(resId);
        // 获取插件中的资源id
        int skinId = mSkinResources.getIdentifier(resourceName, typeName, mSkinPackageName);
        return skinId;
    }
    // 获取皮肤包中的颜色值
    public int getColor(int resId){
        if (isDefaultSkin){
            return mAppResources.getColor(resId);
        }
        int skinId = getIdentifier(resId);
        // 如果皮肤包中没有对应的值
        if (skinId == 0){
           return mAppResources.getColor(resId);
        }
        int skinColor = mSkinResources.getColor(skinId);
        return skinColor;
    }


    public ColorStateList getColorStateList(int resId) {
        if (isDefaultSkin) {
            return mAppResources.getColorStateList(resId);
        }
        int skinId = getIdentifier(resId);
        if (skinId == 0) {
            return mAppResources.getColorStateList(resId);
        }
        return mSkinResources.getColorStateList(skinId);
    }

    public Drawable getDrawable(int resId) {
        if (isDefaultSkin) {
            return mAppResources.getDrawable(resId);
        }
        //通过 app的resource 获取id 对应的 资源名 与 资源类型
        //找到 皮肤包 匹配 的 资源名资源类型 的 皮肤包的 资源 ID
        int skinId = getIdentifier(resId);
        if (skinId == 0) {
            return mAppResources.getDrawable(resId);
        }
        return mSkinResources.getDrawable(skinId);
    }
    /**
     * 可能是Color 也可能是drawable
     * @return
     */
    public Object getBackground(int resId) {
        String resourceTypeName = mAppResources.getResourceTypeName(resId);
        if ("color".equals(resourceTypeName)) {
            return getColor(resId);
        } else {
            // drawable
            return getDrawable(resId);
        }
    }
}

public class SkinThemeUtils {

    private static int[] APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS = {
            android.R.attr.colorPrimaryDark
    };
    // 分别是状态栏的颜色和,导航栏的颜色
    private static int[] STATUSBAR_COLOR_ATTRS = {android.R.attr.statusBarColor, android.R.attr
            .navigationBarColor
    };
    /**
     * 获得theme中的属性中定义的 资源id
     * @param context
     * @param attrs
     * @return
     */
    public static int[] getResId(Context context, int[] attrs) {
        int[] resIds = new int[attrs.length];
        TypedArray a = context.obtainStyledAttributes(attrs);
        for (int i = 0; i < attrs.length; i++) {
            resIds[i] = a.getResourceId(i, 0);
        }
        a.recycle();
        return resIds;
    }
  public static void updateStatusBarColor(Activity activity) {
        //5.0以上才能修改
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return;
        }
        //获得 statusBarColor 与 nanavigationBarColor (状态栏颜色)
        //当与 colorPrimaryDark  不同时 以statusBarColor为准
        int[] resIds = getResId(activity, STATUSBAR_COLOR_ATTRS);
        int statusBarColorResId = resIds[0];
        int navigationBarColor = resIds[1];

        //如果直接在style中写入固定颜色值(而不是 @color/XXX ) 获得0
        if (statusBarColorResId != 0) {
            int color = SkinResources.getInstance().getColor(statusBarColorResId);
            activity.getWindow().setStatusBarColor(color);
        } else {
            //获得 colorPrimaryDark
            int colorPrimaryDarkResId = getResId(activity, APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS)[0];
            if (colorPrimaryDarkResId != 0) {
                int color = SkinResources.getInstance().getColor(colorPrimaryDarkResId);
                activity.getWindow().setStatusBarColor(color);
            }
        }
        if (navigationBarColor != 0) {
            int color = SkinResources.getInstance().getColor
                    (navigationBarColor);
            activity.getWindow().setNavigationBarColor(color);
        }
    }
}

// 此接口是用于自己new 的view实现换肤的方案
public interface SkinViewSupport {
    void applySkin();
}

使用的话,需要在Application中初始化

 SkinManager.init(this);

然后需要在换肤的地方直接调用就行,传递皮肤包apk的路径

SkinManager.getInstance().loadSkin("/storage/emulated/0/Android/data/skinapk-debug.apk");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值