Android那些疑惑(1)-Application中setTheme为什么不生效

Application中setTheme为什么不生效

Android开发者们都知道在AndroidManifest.xml中可以通过Application的theme标签来设置全局的theme,后续的Activity都可以继承这个主题。不过在自定义Application中通过代码动态去setTheme是不会生效的。为什么不会生效呢,我们可以通过以下来解决这个问题。

Activity是怎么继承到Application的主题的

1.ActivityThread.performLaunchActivity()

在ActivityThread.performLaunchActivity方法中,在回调Activity onCreate之前会设置Activity的主题。

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);
}

2.ActivityInfo.getThemeResource()

//获取Activity主题的策略如下:
//1.假如在AndroidManifest.xml中设置了Activity的主题,那么优先使用Activity的主题
//2.没有设置Activity的主题那么使用Application设置的主题
//3.假如Application也没有设置主题,那么什么都不做。
public final int getThemeResource() {
   return theme != 0 ? theme : applicationInfo.theme;
}

很明显,要想全局设置Activity的主题,必须设置applicationInfo.theme。首先我们发现applicationInfo只是ActivityInfo一个变量,那么ActivityInfo又是怎么创建的呢?

3.Activity.attach()

熟悉AMS的开发者很容易联想到attach这个方法。是的,在performLaunchActivity流程中,可以获取到Ams传递过来的ActivityInfo .

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);
        ...
}

4.PackageManagerService

那么在AMS传递过来的ActivityInfo是怎么生成的呢?这一系列的问题自然而来。这里就涉及到PMS的知识了。
在开机阶段,PMS会扫描整个手机上的app安装目录,每一个APK都会被解释然后保存到pms的mPackages。

final ArrayMap<String, PackageParser.Package> mPackages = 
        new ArrayMap<String, PackageParser.Package>();

一个Package就代表一个apk,Package里面保存了Activity列表,Receiver列表,Service列表等

 public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
 public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
 public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
 public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
 public final ArrayList<Service> services = new ArrayList<Service>(0);

其中Activity里面就有我们想要的ActivityInfo,而applitionInfo的设置也是初始化的时候给赋值的。

public final static class Activity extends Component<ActivityIntentInfo> {
        public final ActivityInfo info;
        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
            super(args, _info);
            info = _info;
            //这里赋值,一个包下所有Activity的applicationInfo一样
            info.applicationInfo = args.owner.applicationInfo;
        }
}

可以知道,applicationInfo存在于pms中,在开机阶段或者安装阶段被初始化,然后ams启动相关Activity的时候,都会向pms请求,接着pms根据请求返回保存在内存中的ActivityInfo,通过Binder机制和App进程交互。所以,从上面我们可以知道要更改整个Application中的主题必须AndroidManifest.xml中修改,因为你无法修改PMS的东西。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页