赞
踩
1.创建skip.apk,里面只有要修改的资源文件,没有代码
2.将skip.apk放在data/data目录下
3.通过原apk中的资源id获取到资源的name和type,然后通过mSkinResources.getIdentifier获取到皮肤包中的资源(mSkinResources是skip.apk资源包中获取的Resources,下面会讲怎么获取skip.apk中的资源)
- /**
- * 1.通过原始app中的resId(R.color.XX)获取到自己的 名字
- * 2.根据名字和类型获取皮肤包中的ID
- */
- public int getIdentifier(int resId){
- if(isDefaultSkin){
- return resId;
- }
- String resName=mAppResources.getResourceEntryName(resId);
- String resType=mAppResources.getResourceTypeName(resId);
- int skinId=mSkinResources.getIdentifier(resName,resType,mSkinPkgName);
- return skinId;
- }
4.对view更换新的资源
当我们的app进程创建出来的时候,回执行我们app的入口函数ActivityThread.main()方法
- //创建ActivityThread
- ActivityThread thread = new ActivityThread();
- thread.attach(true, 0);
- //查看attach
- final IActivityManager mgr = ActivityManager.getService();
- //通过AMS的代理对象,会执行AMS的attachApplication()
- mgr.attachApplication(mAppThread, startSeq);
- //来到AMS中,通过ApplicationThread的代理类,执行thread.bindApplication
- //查看ApplicationThread的bindApplication()方法,通过handler执行handleBindApplication()
- //handleBindApplication()中会makeApplication(data.restrictedBackupMode, null);
- //makeApplication 中ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
- static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
- if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
- null);
- //获取Resources
- context.setResources(packageInfo.getResources());
- return context;
- }
- //LoadApk
- public Resources getResources() {
- if (mResources == null) {
- final String[] splitPaths;
- try {
- splitPaths = getSplitPaths(null);
- } catch (NameNotFoundException e) {
- // This should never fail.
- throw new AssertionError("null split not found");
- }
-
- mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
- getClassLoader());
- }
- return mResources;
- }
- private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
- @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
- ...
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
- if (resourcesImpl == null) {
- return null;
- }
-
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
-
- final Resources resources;
- if (activityToken != null) {
- resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl, key.mCompatInfo);
- } else {
- resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
- }
- return resources;
- }
- }
重点重点,创建我们的Resource
- private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
- final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
- daj.setCompatibilityInfo(key.mCompatInfo);
- //关键代码
-
- final AssetManager assets = createAssetManager(key);
- if (assets == null) {
- return null;
- }
-
- final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
- final Configuration config = generateConfig(key, dm);
- final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
-
- if (DEBUG) {
- Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
- }
- return impl;
- }
到这里,我们将新的资源加载进去了,demo代码
- //宿主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());
activity中setContentView(layoutResID); 调用到getWindow().setContentView(layoutResID);Window的实现类是PhoneWindow
- public void setContentView(int layoutResID) {
- // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
- // decor, when theme attributes and the like are crystalized. Do not check the feature
- // before this happens.
- if (mContentParent == null) {
- installDecor();
- } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- mContentParent.removeAllViews();
- }
-
- if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
- getContext());
- transitionTo(newScene);
- } else {
- //重点看inflate工程
- mLayoutInflater.inflate(layoutResID, mContentParent);
- }
- mContentParent.requestApplyInsets();
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();
- }
- mContentParentExplicitlySet = true;
- }
一直跟进去,到LayoutInflater中
- public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
- synchronized (mConstructorArgs) {
- ...
- // Temp is the root view that was found in the xml
- //创建view
- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
-
- ...
- }
- }
- View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
- boolean ignoreThemeAttr) {
-
- ...
- try {
- View view;
- //重点来了
- //默认mFactory2为空,这个是Google默认给我们留的一个入口,我们重写mFactory2后就可以修改view, 换肤我们就从这儿开始,LayoutInflaterCompat.setFactory2(layoutInflater, skinLayoutInflaterFactory);
- if (mFactory2 != null) {
- view = mFactory2.onCreateView(parent, name, context, attrs);
- } else if (mFactory != null) {
- //默认是用的mFactory
- view = mFactory.onCreateView(name, context, attrs);
- } else {
- view = null;
- }
-
- if (view == null && mPrivateFactory != null) {
- view = mPrivateFactory.onCreateView(parent, name, context, attrs);
- }
-
- if (view == null) {
- final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = context;
- try {
- //上面创建失败,创建view的工程,我们会在mFactory2中抄袭这块儿代码
- //普通view
- if (-1 == name.indexOf('.')) {
- view = onCreateView(parent, name, attrs);
- } else {
- //a.b.c.View一般是自定义view
- view = createView(name, null, attrs);
- }
- } finally {
- mConstructorArgs[0] = lastContext;
- }
- }
-
- return view;
- }
- ...
- }
- public interface Factory2 extends Factory {
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
- }
2.1创建SkinLayoutInflaterFactory.java实现Factory2接口,重写onCreateView()创建我们的View
抄写LayoutInflater中创建view的方式
- public class SkinLayoutInflaterFactory implements LayoutInflater.Factory2 {
-
- 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<String, Constructor<? extends View>>();
-
-
- private Activity activity;
-
-
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- //换肤就是在需要时候替换 View的属性(src、background等)
- //所以这里创建 View,从而修改View属性
- View view = createSDKView(name, context, attrs);
- if (null == view) {
- view = createView(name, context, attrs);
- }
- //拿到view,就可以对其更换资源
-
- return view;
- }
-
-
- private View createSDKView(String name, Context context, AttributeSet
- attrs) {
- //如果包含 . 则不是SDK中的view 可能是自定义view包括support库中的View
- if (-1 != name.indexOf('.')) {
- return null;
- }
- //不包含就要在解析的 节点 name前,拼上: android.widget. 等尝试去反射
- for (int i = 0; i < mClassPrefixList.length; i++) {
- View view = createView(mClassPrefixList[i] + name, context, attrs);
- if(view!=null){
- return view;
- }
- }
- return null;
- }
-
- private View createView(String name, Context context, AttributeSet
- attrs) {
- Constructor<? extends View> constructor = findConstructor(context, name);
- try {
- return constructor.newInstance(context, attrs);
- } catch (Exception e) {
- }
- return null;
- }
-
-
-
- private Constructor<? extends View> findConstructor(Context context, String name) {
- Constructor<? extends View> constructor = mConstructorMap.get(name);
- if (constructor == null) {
- try {
- Class<? extends View> clazz = context.getClassLoader().loadClass
- (name).asSubclass(View.class);
- constructor = clazz.getConstructor(mConstructorSignature);
- mConstructorMap.put(name, constructor);
- } catch (Exception e) {
- }
- }
- return constructor;
- }
-
-
-
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- return null;
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。