ActivityThread.java
performLaunchActivity
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
}
r.setState(ON_CREATE);
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
PhoneWindow.java
插件化换肤-app
MyTabLayout.java
public class MyTabLayout extends TabLayout implements SkinViewSupport {
int tabIndicatorColorResId;
int tabTextColorResId;
public MyTabLayout(Context context) {
this(context, null, 0);
}
public MyTabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout,
defStyleAttr, 0);
tabIndicatorColorResId = a.getResourceId(R.styleable.TabLayout_tabIndicatorColor, 0);
tabTextColorResId = a.getResourceId(R.styleable.TabLayout_tabTextColor, 0);
a.recycle();
}
@Override
public void applySkin() {
// 自定义View中,实现公共接口,来修改色值
if (tabIndicatorColorResId != 0) {
int tabIndicatorColor = SkinResources.getInstance().getColor(tabIndicatorColorResId);
setSelectedTabIndicatorColor(tabIndicatorColor);
}
if (tabTextColorResId != 0) {
ColorStateList tabTextColor = SkinResources.getInstance().getColorStateList
(tabTextColorResId);
setTabTextColors(tabTextColor);
}
}
}
MyApplication.java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SkinManager.init(this);
}
}
SkinActivity.java
public class SkinActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_skin);
}
public void change(View view) {
//换肤,收包裹,皮肤包是独立的apk包,可以来自网络下载
SkinManager.getInstance().loadSkin("/data/data/com.enjoy.skin/skin/skin-debug.apk");
}
public void restore(View view) {
SkinManager.getInstance().loadSkin(null);
}
}
插件化换肤-lib
utils/SkinResource
// 保存原来的Resource和新创建的Resource
public class SkinResources {
private String mSkinPkgName;
private boolean isDefaultSkin = true;
// app原始的resource
private Resources mAppResources;
// 皮肤包的resource
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;
mSkinPkgName = "";
isDefaultSkin = true;
}
public void applySkin(Resources resources, String pkgName) {
mSkinResources = resources;
mSkinPkgName = pkgName;
//是否使用默认皮肤
isDefaultSkin = TextUtils.isEmpty(pkgName) || resources == null;
}
/**
* 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;
}
/**
* 输入主APP的ID,到皮肤APK文件中去找到对应ID的颜色值
* @param resId
* @return
*/
public int getColor(int resId){
if(isDefaultSkin){
return mAppResources.getColor(resId);
}
int skinId=getIdentifier(resId);
if(skinId==0){
return mAppResources.getColor(resId);
}
return mSkinResources.getColor(skinId);
}
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);
}
}
}
utils/SkinThemeUtils
public class SkinThemeUtils {
private static int[] APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS = {
android.support.v7.appcompat.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);
}
}
}
ApplicationActivitqyLifecycle
public class ApplicationActivitqyLifecycle implements Application.ActivityLifecycleCallbacks {
private Observable mObserable;
private ArrayMap<Activity, SkinLayoutInflaterFactory> mLayoutInflaterFactories = new
ArrayMap<>();
public ApplicationActivityLifecycle(Observable observable) {
mObserable = observable;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
/**
* 更新状态栏
*/
SkinThemeUtils.updateStatusBarColor(activity);
/**
* 更新布局视图
*/
//获得Activity的布局加载器
LayoutInflater layoutInflater = activity.getLayoutInflater();
try {
//Android 布局加载器 使用 mFactorySet 标记是否设置过Factory
//如设置过抛出一次
//设置 mFactorySet 标签为false
Field field = LayoutInflater.class.getDeclaredField("mFactorySet");
field.setAccessible(true);
field.setBoolean(layoutInflater, false);
} catch (Exception e) {
e.printStackTrace();
}
//使用factory2 设置布局加载工程
SkinLayoutInflaterFactory skinLayoutInflaterFactory = new SkinLayoutInflaterFactory(activity);
LayoutInflaterCompat.setFactory2(layoutInflater, skinLayoutInflaterFactory);
mLayoutInflaterFactories.put(activity, skinLayoutInflaterFactory);
mObserable.addObserver(skinLayoutInflaterFactory);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
SkinLayoutInflaterFactory observer = mLayoutInflaterFactories.remove(activity);
SkinManager.getInstance().deleteObserver(observer);
}
}
SkinAttribute.java
public class SkinAttribute {
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与属性信息
private List<SkinView> mSkinViews = new ArrayList<>();
//记录下一个VIEW身上哪几个属性需要换肤textColor/src
public void look(View view, AttributeSet attrs) {
List<SkinPair> mSkinPars = new ArrayList<>();
for (int i = 0; i < attrs.getAttributeCount(); i++) {
//获得属性名 textColor/background
String attributeName = attrs.getAttributeName(i);
if (mAttributes.contains(attributeName)) {
// #
// ?722727272
// @722727272
String attributeValue = attrs.getAttributeValue(i);
// 比如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));
}
SkinPair skinPair = new SkinPair(attributeName, resId);
mSkinPars.add(skinPair);
}
}
if (!mSkinPars.isEmpty() || view instanceof SkinViewSupport) {
SkinView skinView = new SkinView(view, mSkinPars);
// 如果选择过皮肤 ,调用 一次 applySkin 加载皮肤的资源
skinView.applySkin();
mSkinViews.add(skinView);
}
}
/*
对所有的view中的所有的属性进行皮肤修改
*/
public void applySkin() {
for (SkinView mSkinView : mSkinViews) {
mSkinView.applySkin();
}
}
static class SkinView {
View view;
//这个View的能被 换肤的属性与它对应的id 集合
List<SkinPair> skinPairs;
public SkinView(View view, List<SkinPair> skinPairs) {
this.view = view;
this.skinPairs = skinPairs;
}
/**
* 对一个View中的所有的属性进行修改
*/
public void applySkin() {
applySkinSupport();
for (SkinPair skinPair : skinPairs) {
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;
}
}
}
SkinLayoutInflatorFactory.java
public class SkinLayoutInflaterFactory implements LayoutInflater.Factory2, Observer {
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>>();
// 当选择新皮肤后需要替换View与之对应的属性
// 页面属性管理器
private SkinAttribute skinAttribute;
// 用于获取窗口的状态框的信息
private Activity activity;
public SkinLayoutInflaterFactory(Activity activity) {
this.activity = activity;
skinAttribute = new SkinAttribute();
}
@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);
}
//这就是我们加入的逻辑
if (null != view) {
//加载属性
skinAttribute.look(view, attrs);
}
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;
}
//如果有人发送通知,这里就会执行
@Override
public void update(Observable o, Object arg) {
SkinThemeUtils.updateStatusBarColor(activity);
skinAttribute.applySkin();
}
}
SkinManager.java
public class SkinManager extends Observable {
private volatile static SkinManager instance;
/**
* Activity生命周期回调
*/
private ApplicationActivityLifecycle skinActivityLifecycle;
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生命周期,并设置被观察者
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;
SkinResources.getInstance().applySkin(skinResource, packageName);
//记录
SkinPreference.getInstance().setSkin(skinPath);
} catch (Exception e) {
e.printStackTrace();
}
}
//通知采集的View 更新皮肤
//被观察者改变 通知所有观察者
setChanged();
notifyObservers(null);
}
}
SkinPreference.java
public class SkinPreference {
private static final String SKIN_SHARED = "skins";
private static final String KEY_SKIN_PATH = "skin-path";
private volatile static SkinPreference instance;
private final SharedPreferences mPref;
public static void init(Context context) {
if (instance == null) {
synchronized (SkinPreference.class) {
if (instance == null) {
instance = new SkinPreference(context.getApplicationContext());
}
}
}
}
public static SkinPreference getInstance() {
return instance;
}
private SkinPreference(Context context) {
mPref = context.getSharedPreferences(SKIN_SHARED, Context.MODE_PRIVATE);
}
public void setSkin(String skinPath) {
mPref.edit().putString(KEY_SKIN_PATH, skinPath).apply();
}
public void reset() {
mPref.edit().remove(KEY_SKIN_PATH).apply();
}
public String getSkin() {
return mPref.getString(KEY_SKIN_PATH, null);
}
}
SkinSupport.java
public interface SkinViewSupport {
void applySkin();
}
- 在ActivityLifeCycle中保存Factrory2(保存了Activity),里面保存所有符合要求的View(SkinSupport), 在onDestroy在移除掉
- 加载apk, 并创建一个新的AssetManager(反射创建),创建一个新的Resource。使用的时候通过id获取原来resource中的name和type,找到新Resource中的新值