一、首先从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");