最近买了一本android内核剖析,讲的很好,也是正在看,看到理解context这章,写一个博客复习复习。
我也是边看书边看网上讲解学的。
网上讲解的很好,这里分享几个很好的链接 http://blog.csdn.net/lmj623565791/article/details/40481055
http://www.2cto.com/kf/201505/403099.html
每个activity,service使用的context调用的方法,都是在contextImpl这个类中,继承结构
一个app的context数量 = activity数量 + service数量 + 1
这个1是application。
我们现在从源码里查看为什么activity和service调用context类的方法,这些方法的实现都是来自contextImpl这个类呢?
我们先看activity的源码(我使用的是21版本的)。
activity——》contextWrapper ——》context。context类的本身是一个纯abstract类,而contextWrapper是一个包装的类,同时它提供了attachBaseContext()方法用于指定真正的context对象。
我们现在看activity对应的context如何创建的。
每一个app都有从activityThread类开始的。启动activity时,ams会通过ipc调用到activityThread的scheduleLaunchActivity方法。在方法中会创建一个ActivityClientRecord一个对象,这是一个本地数据类,里面定义了一个packageinfo。然后会发送一个消息sendMessage(H.LAUNCH_ACTIVITY, r),将ActivityClientRecord实例传递过去。然后在handleMessage方法中找到对应H.LAUNCH_ACTIVITY 的执行
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
IVoiceInteractor voiceInteractor, int procState, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;.......}
getPackageInfo() synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
return packageInfo;
}
}
我们可以发现先是从mPackages这个全局变量里以包名为key获取packageInfo,如果没有就创建,然后以包名为key,存放在mPackages里面。mpackages:
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<String, WeakReference<LoadedApk>>();然后返回到这个方法中 handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
Activity a = performLaunchActivity(r, customIntent);
........
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......... if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
.......
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
........
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config); Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);<span style="font-family: Arial, Helvetica, sans-serif;"> activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
........
return activity;
}
我们再去看看activity的attach方法
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, IVoiceInteractor voiceInteractor) {
// 这个方法最后会把context对象传递给contextwrapper类的mBase,而这个mbase就是contextImpl对象,所以我们activity使用context的方法,这些方法其实都是contextimpl实现的。
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
.......
}
然后我们看为什么context的访问资源的唯一性。
前面说了使用的context都是contextimpl里面实现的方法,我们去看看contextimpl的源码。我们看getResources方法。
@Override
public Resources getResources() {
return mResources;
}
返回全局变量mResources。
我们找一找mResources在哪里赋值。
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration) {
.........
mPackageInfo = packageInfo;
mResourcesManager = ResourcesManager.getInstance();
.............
Resources resources = packageInfo.getResources(mainThread);
if (resources != null) {
if (activityToken != null
|| displayId != Display.DEFAULT_DISPLAY
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
overrideConfiguration, compatInfo, activityToken);
}
}
mResources = resources;
..........
}
可以看到 Resources resources = packageInfo.getResources(mainThread); 是这样创建的,而packageinfo是传递进来的,上面我们说的activity对应的context创建,传入的packageInfo全局是唯一的(忘记的可以去看,activitythread类的 getPackageInfo方法 )。
mResourcesManager.getTopLevelResources()mResourcesManager的创建是一个单例模式:Resources resources = packageInfo.getResources(mainThread);
由此可以看出全局使用的资源的唯一。