这里有关于Context的一切

-

写在前面:

当我还是一个24K纯Android新手的时候(现在是也是个小Android萌新),拿着工具书对着电脑敲敲打打,那个时候我就有一个非常大的疑问:Context到底为何这么牛?show一个Dialog,启动一个Activity,Service,发送一个Broadcast,还有各种方法需要传入的参数。几乎在Android中,Context的身影处处可见,所以弄懂它,似乎是一件迫在眉睫的事,所以深呼吸,整理思路,来看看Context到底是什么。
转载请注明出处。MeloDev

零:官方定义

好吧如果你无法翻墙,推荐你两个可以看官网文档的网站:

Android官方文档国内镜像站点

Android中文API

我们来看看官方文档中,Context的解释

Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

  • 一个应用环境的全局信息,字面意思是上下文的意思;
  • Context是一个抽象类;
  • 允许我们通过Context获取各种资源,服务,或者去启动一个Activity,发送一个广播,等等;

怎么去理解Context呢?其实Context就是给Android应用程序提供了一个可以实现各种操作的土壤环境,Context为Android提供了各种资源、功能、服务。如果说编写一个Android程序像搭建一座房子,那Context就为Android提供了土地,木材,和染料(启动一个Activity,弹出一个Dialog),并且能提供呼叫各种将房屋建得更完善的其他帮助(发送一个广播,启动一个服务等)。

一:继承关系

Context继承关系

通过继承关系可以看到,Context直接子类为ContextIml(实现类)和ContextWrapper(包装类)

再看看ContextWrapper的子类有什么,看到熟悉的Service和Application了吧,不过看到这里你一定有个疑问,为什么Activity和他们哥俩不在一个继承层级呢?而是Activity又继承了ContextThemeWrapper,那么ContextWrapper和ContextThemeWrapper的区别在哪里呢?

看到这两个类的名字,相信你心里已经有了答案,对,区别在Theme

该类内部包含了主题(Theme)相关的接口,即android:theme属性指定的。
只有Activity需要主题,Service不需要主题,
所以Service直接继承于ContextWrapper类。而Activity因为含有Theme属性的缘故,所以继承自ContextThemeWrapper。

所以说,Context所调用的资源是不同的了?保留这个疑问,继续向下看。

二:源码阅读

看到了继承结构,我们分别来看看Context及其子类的一些源码

  • Context
public abstract class Context{// 获取应用程序包的AssetManager实例public abstract AssetManager getAssets();// 获取应用程序包的Resources实例public abstract Resources getResources();// 获取PackageManager实例,以查看全局package信息public abstract PackageManager getPackageManager();// 获取应用程序包的ContentResolver实例public abstract ContentResolver getContentResolver();// 它返回当前进程的主线程的Looper,此线程分发调用给应用组件(activities, services等)public abstract Looper getMainLooper();// 返回当前进程的单实例全局Application对象的Contextpublic abstract Context getApplicationContext();// 从string表中获取本地化的、格式化的字符序列public final CharSequence getText(int resId){return getResources().getText(resId);}// 从string表中获取本地化的字符串public final String getString(int resId){return getResources().getString(resId);}public final String getString(int resId, Object... formatArgs){return getResources().getString(resId, formatArgs);}// 返回一个可用于获取包中类信息的class loaderpublic abstract ClassLoader getClassLoader();// 返回应用程序包名public abstract String getPackageName();// 返回应用程序信息public abstract ApplicationInfo getApplicationInfo();// 根据文件名获取SharedPreferencespublic abstract SharedPreferences getSharedPreferences(String name,int mode);// 其根目录为: Environment.getExternalStorageDirectory()public abstract File getExternalFilesDir(String type);// 返回应用程序obb文件路径public abstract File getObbDir();// 启动一个新的activitypublic abstract void startActivity(Intent intent);// 启动一个新的activitypublic void startActivityAsUser(Intent intent, UserHandle user){throw new RuntimeException("Not implemented. Must override in a subclass.");}// 启动一个新的activity// intent: 将被启动的activity的描述信息// options: 描述activity将如何被启动public abstract void startActivity(Intent intent, Bundle options);// 启动多个新的activitypublic abstract void startActivities(Intent[] intents);// 启动多个新的activitypublic abstract void startActivities(Intent[] intents, Bundle options);// 广播一个intent给所有感兴趣的接收者,异步机制public abstract void sendBroadcast(Intent intent);// 广播一个intent给所有感兴趣的接收者,异步机制public abstract void sendBroadcast(Intent intent,String receiverPermission);public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission);public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras);public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,String receiverPermission);// 注册一个BroadcastReceiver,且它将在主activity线程中运行public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter);public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter, String broadcastPermission, Handler scheduler);public abstract void unregisterReceiver(BroadcastReceiver receiver);// 请求启动一个application servicepublic abstract ComponentName startService(Intent service);// 请求停止一个application servicepublic abstract boolean stopService(Intent service);// 连接一个应用服务,它定义了application和service间的依赖关系public abstract boolean bindService(Intent service, ServiceConnection conn,int flags);// 断开一个应用服务,当服务重新开始时,将不再接收到调用,// 且服务允许随时停止public abstract void unbindService(ServiceConnection conn);public abstract Object getSystemService(String name);public abstract int checkPermission(String permission, int pid, int uid);// 返回一个新的与application name对应的Context对象public abstract Context createPackageContext(String packageName,int flags) throws PackageManager.NameNotFoundException;// 返回基于当前Context对象的新对象,其资源与display相匹配public abstract Context createDisplayContext(Display display);}

Context的源码算上注释有3000行之多,这里贴出一些重要代码,可以看到,Context几乎包含了所有你能想到的,一个Android程序需要的资源和操作,Context自己就像一个App一样,启动Activity、Service,发送Broadcast,拿到assets下的资源,获取SharedPreferences等等。

但Context只是一个顶层接口啊,又是谁帮他实现了操作呢?是ContextWrapper吗?

  • ContextWrapper
public class ContextWrapper extends Context{Context mBase; //该属性指向一个ContextIml实例public ContextWrapper(Context base){mBase = base;}/*** @param base The new base context for this wrapper.* 创建Application、Service、Activity,会调用该方法给mBase属性赋值*/protected void attachBaseContext(Context base){if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}@Overridepublic Looper getMainLooper(){return mBase.getMainLooper();}@Overridepublic Object getSystemService(String name){return mBase.getSystemService(name);}@Overridepublic void startActivity(Intent intent){mBase.startActivity(intent);}
}

好吧,ContextWrapper好像很懒的样子,它把所有操作都丢给了mBase,mBase又是谁呢?在构造方法和attachBaseContext方法中,指向了一个Context实例,ContextIml,我们赶紧来看看ContextIml的源码!

  • ContextImpl
/*** Common implementation of Context API, which provides the base* context object for Activity and other application components.*/
class ContextImpl extends Context{private final static String TAG = "ContextImpl";private final static boolean DEBUG = false;private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =new HashMap<String, SharedPreferencesImpl>();/*package*/ LoadedApk mPackageInfo; // 关键数据成员private String mBasePackageName;private Resources mResources;/*package*/ ActivityThread mMainThread; // 主线程@Overridepublic AssetManager getAssets(){return getResources().getAssets();}@Overridepublic Looper getMainLooper(){return mMainThread.getLooper();}@Overridepublic Object getSystemService(String name){ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);return fetcher == null ? null : fetcher.getService(this);}@Overridepublic void startActivity(Intent intent, Bundle options){warnIfCallingFromSystemProcess();if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {throw new AndroidRuntimeException("Calling startActivity() from outside of an Activity "+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."+ " Is this really what you want?");}mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null,(Activity)null, intent, -1, options);}
}

其实ContextImpl才是在Context的所有继承结构中唯一一个真正实现了Context中方法的类。其它Context的子类,Application,Activity,Service,都是与ContextImpl相关联,去获取资源和服务,并没有真正自己去实现,这里就不贴上ContextThemeWrapper的源码了,它是为Activity添加了一些Theme的属性,不再赘述。

思路越来越清晰,我们现在就是要去寻找,Activity,Service,Application是何时与ContextImpl完成绑定关联的。

三:关联时机

我们都知道ActivityThreadmain方法,是整个Android程序的入口,所以去探究ActivityThread类,也是一件非常重要的事。

推荐一篇文章,去了解下ActivityThread

ActivityThread简介

贴出ActivityThread的main方法部分重要的代码

public final class ActivityThread{  static ContextImpl mSystemContext = null;  static IPackageManager sPackageManager;// 创建ApplicationThread实例,以接收AMS指令并执行final ApplicationThread mAppThread = new ApplicationThread();  final Looper mLooper = Looper.myLooper();  final HashMap<IBinder, ActivityClientRecord> mActivities  = new HashMap<IBinder, ActivityClientRecord>();  final HashMap<IBinder, Service> mServices  = new HashMap<IBinder, Service>();  final H mH = new H();  Application mInitialApplication;  final ArrayList<Application> mAllApplications  = new ArrayList<Application>();  static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>();  Instrumentation mInstrumentation;  static Handler sMainThreadHandler;  // set once in main()private class H extends Handler{  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");  ActivityClientRecord r = (ActivityClientRecord)msg.obj;  r.packageInfo = getPackageInfoNoCheck(  r.activityInfo.applicationInfo, r.compatInfo);  handleLaunchActivity(r, null);  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  } break;  ...  }  if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));  }  ...  }  private class ApplicationThread extends ApplicationThreadNative{  private void updatePendingConfiguration(Configuration config){  synchronized (mPackages) {  if (mPendingConfiguration == null ||  mPendingConfiguration.isOtherSeqNewer(config)) {  mPendingConfiguration = config;  }  }  }  public final void schedulePauseActivity(IBinder token, boolean finished,  boolean userLeaving, int configChanges){  queueOrSendMessage(  finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,  token,  (userLeaving ? 1 : 0),  configChanges);  }  // we use token to identify this activity without having to send the// activity itself back to the activity manager. (matters more with ipc)public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,  ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,  Bundle state, List<ResultInfo> pendingResults,  List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,  String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler){  ActivityClientRecord r = new ActivityClientRecord();  r.token = token;  r.ident = ident;  r.intent = intent;  r.activityInfo = info;  r.compatInfo = compatInfo;  r.state = state;  r.pendingResults = pendingResults;  r.pendingIntents = pendingNewIntents;  r.startsNotResumed = notResumed;  r.isForward = isForward;  r.profileFile = profileName;  r.profileFd = profileFd;  r.autoStopProfiler = autoStopProfiler;  updatePendingConfiguration(curConfig);  queueOrSendMessage(H.LAUNCH_ACTIVITY, r);  }  ...  }public static void main(String[] args){  SamplingProfilerIntegration.start();  // CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);  Environment.initForCurrentUser();  // Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());  Process.setArgV0("<pre-initialized>");  Looper.prepareMainLooper();  // 创建ActivityThread实例ActivityThread thread = new ActivityThread();  thread.attach(false);  if (sMainThreadHandler == null) {  sMainThreadHandler = thread.getHandler();  }  AsyncTask.init();  if (false) {  Looper.myLooper().setMessageLogging(new  LogPrinter(Log.DEBUG, "ActivityThread"));  }  Looper.loop();  throw new RuntimeException("Main thread loop unexpectedly exited");  } public void handleMessage(Message msg){  if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));  switch (msg.what) {  case LAUNCH_ACTIVITY: { // 创建Activity对象Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  ActivityClientRecord r = (ActivityClientRecord)msg.obj;  r.packageInfo = getPackageInfoNoCheck(  r.activityInfo.applicationInfo, r.compatInfo);  handleLaunchActivity(r, null);  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  } break;  case BIND_APPLICATION: // 创建Application对象Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");  AppBindData data = (AppBindData)msg.obj;  handleBindApplication(data);  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  break;  case CREATE_SERVICE: // 创建Service对象Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");  handleCreateService((CreateServiceData)msg.obj);  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  break;  case BIND_SERVICE:  // Bind Service对象Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");  handleBindService((BindServiceData)msg.obj);  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  break;  }  }
}

也许你不能完全看懂、理解这些代码,不过没关系,直接告诉你结论吧,ActivityThread的一个内部类H,里面定义了activity、service等启动、销毁等事件的响应,也就是说activity、service的启动、销毁都是在ActivityThread中进行的。

当然了,一个Activity或者Service的从创建到启动是相当复杂的,其中还涉及的Binder机制等等原理,推荐给大家两篇博文,去慢慢研读消化吧。

Activity启动原理详解

Service启动原理分析

准备工作不知不觉就做了这么多,差点忘了正事,我们还是要继续寻找ApplicationActivityService是何时与ContextImpl进行关联的。

  • Application
   // ActivityThread.javaprivate void handleBindApplication(AppBindData data){ try {// If the app is being launched for full backup or restore, bring it up in// a restricted environment with the base application class.Application app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;...} finally {StrictMode.setThreadPolicy(savedPolicy);}}// LoadedApk.javapublic Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation){if (mApplication != null) {return mApplication;}Application app = null;String appClass = mApplicationInfo.className;if (forceDefaultAppClass || (appClass == null)) {appClass = "android.app.Application";}try {java.lang.ClassLoader cl = getClassLoader();ContextImpl appContext = new ContextImpl(); // 创建ContextImpl实例appContext.init(this, null, mActivityThread);app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);appContext.setOuterContext(app); // 将Application实例传递给Context实例} catch (Exception e) {...}mActivityThread.mAllApplications.add(app);mApplication = app;return app;}

每个应用程序在第一次启动时,都会首先创建一个Application对象。从startActivity流程可知,创建Application的时机在handleBindApplication()方法中,该函数位于 ActivityThread.java类

  • Activity
      private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent){...Activity a = performLaunchActivity(r, customIntent); // 到下一步if (a != null) {r.createdConfig = new Configuration(mConfiguration);Bundle oldState = r.state;handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);...}...}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){...    Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {...}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (activity != null) {Context appContext = createBaseContextForActivity(r, activity); // 创建ContextCharSequence 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);activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config);if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstances = null;activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}mActivities.put(r.token, r);} catch (SuperNotCalledException e) {...} catch (Exception e) {...}return activity;}
    private Context createBaseContextForActivity(ActivityClientRecord r,final Activity activity){ContextImpl appContext = new ContextImpl();  // 创建ContextImpl实例appContext.init(r.packageInfo, r.token, this);appContext.setOuterContext(activity);// For debugging purposes, if the activity's package name contains the value of// the "debug.use-second-display" system property as a substring, then show// its content on a secondary display if there is one.Context baseContext = appContext;String pkgName = SystemProperties.get("debug.second-display.pkg");if (pkgName != null && !pkgName.isEmpty()&& r.packageInfo.mPackageName.contains(pkgName)) {DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();for (int displayId : dm.getDisplayIds()) {if (displayId != Display.DEFAULT_DISPLAY) {Display display = dm.getRealDisplay(displayId);baseContext = appContext.createDisplayContext(display);break;}}}return baseContext;}

通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调onCreate(),onStart()方法等,函数位于 ActivityThread.java类。

  • Service
    private void handleCreateService(CreateServiceData data){// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;try {java.lang.ClassLoader cl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to instantiate service " + data.info.name+ ": " + e.toString(), e);}}try {if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);ContextImpl context = new ContextImpl(); // 创建ContextImpl实例context.init(packageInfo, null, this);Application app = packageInfo.makeApplication(false, mInstrumentation);context.setOuterContext(service);service.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());service.onCreate();mServices.put(data.token, service);try {ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 0, 0, 0);} catch (RemoteException e) {// nothing to do.}} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to create service " + data.info.name+ ": " + e.toString(), e);}}}

通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类

看到这里,相信你对Context的理解更进一步了,现在我们知道了Context是什么,它为Android提供了怎样的资源、功能、和服务,又在什么时候将Application、Activity、Service与ContextImpl相关联,但是所请求的资源是不是同一套资源呢?

在这里你一定说:“当然不是,不同的Context对象明显是有区别的,用法也不同”

但是其实他们访问的,确确实实,是同一套资源。

三:Context资源详解

来吧,看看不同Context对象的区别和用法的不同,参见以下表格。

Context用法区别

这张表格是不是又支持了你的观点(也就是一直认为的,Context资源对象是不同的),但是还是要再次强调一次,它们所请求的,确确实实是同一块资源,看看上面进行关联的源码,都走进了Context实现类的init方法,拨云见日,我们去看看init方法吧。

查看ContextImpl类源码可以看到,getResources方法直接返回内部的mResources变量

final void init(LoadedApk packageInfo,  IBinder activityToken, ActivityThread mainThread,  Resources container){  mPackageInfo = packageInfo;  mResources = mPackageInfo.getResources(mainThread);  if (mResources != null && container != null  && container.getCompatibilityInfo().applicationScale !=  mResources.getCompatibilityInfo().applicationScale) {  if (DEBUG) {  Log.d(TAG, "loaded context has different scaling. Using container's" +  " compatiblity info:" + container.getDisplayMetrics());  }  mResources = mainThread.getTopLevelResources(  mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());  }  mMainThread = mainThread;  mContentResolver = new ApplicationContentResolver(this, mainThread);  setActivityToken(activityToken);
}

mResources又是调用LoadedApkgetResources方法进行赋值。代码如下。

public Resources getResources(ActivityThread mainThread){  if (mResources == null) {  mResources = mainThread.getTopLevelResources(mResDir, this);  }  return mResources;
}

从代码中可以看到,最终mResources的赋值是由AcitivtyThreadgetTopLevelResources方法返回。代码如下。

Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo){  ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);  Resources r;  synchronized (mPackages) {  // Resources is app scale dependent.if (false) {  Slog.w(TAG, "getTopLevelResources: " + resDir + " / "  + compInfo.applicationScale);  }  WeakReference<Resources> wr = mActiveResources.get(key);  r = wr != null ? wr.get() : null;  if (r != null && r.getAssets().isUpToDate()) {  if (false) {  Slog.w(TAG, "Returning cached resources " + r + " " + resDir  + ": appScale=" + r.getCompatibilityInfo().applicationScale);  }  return r;  }  }  AssetManager assets = new AssetManager();  if (assets.addAssetPath(resDir) == 0) {  return null;  }  DisplayMetrics metrics = getDisplayMetricsLocked(false);  r = new Resources(assets, metrics, getConfiguration(), compInfo);  if (false) {  Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "  + r.getConfiguration() + " appScale="  + r.getCompatibilityInfo().applicationScale);  }  synchronized (mPackages) {  WeakReference<Resources> wr = mActiveResources.get(key);  Resources existing = wr != null ? wr.get() : null;  if (existing != null && existing.getAssets().isUpToDate()) {  // Someone else already created the resources while we were// unlocked; go ahead and use theirs.r.getAssets().close();  return existing;  }  // XXX need to remove entries when weak references go awaymActiveResources.put(key, new WeakReference<Resources>(r));  return r;  }
}

以上代码中,mActiveResources对象内部保存了该应用程序所使用到的所有Resources对象,其类型为WeakReference,所以当内存紧张时,可以释放Resources占用的资源,自然这不是我们探究的重点,ResourcesKey的构造需要resDir和compInfo.applicationScale。resdDir变量的含义是资源文件所在路径,实际指的是APK程序所在路径,比如可以是:/data/app/com.haii.android.xxx-1.apk,该apk会对应/data/dalvik-cache目录下的:data@app@com.haii.android.xxx-1.apk@classes.dex文件。

所以结论来了:

如果一个应用程序没有访问该程序以外的资源,那么mActiveResources变量中就仅有一个Resources对象。

总结:

当ActivityThread类中创建Application、Service、Activity的同时,完成了与ContextImpl的关联绑定,通过ContextImpl类中init方法,获得了一个唯一的Resources对象,根据上述代码中资源的请求机制,再加上ResourcesManager采用单例模式,这样就保证了不同的ContextImpl访问的是同一套资源。

如果这篇博客现在就结束了,你一定会杀了我 - -,现在我们就来分析下,是什么造成了唯一的这个Resources,却展现出了“不同”。

举个通俗易懂的例子,我和我老妈都拿到同一块土豆,但是因为我们处理这个土豆的方法有区别,导致这个土豆最后表现出来的也不一样,我想把它做成薯片,我妈妈把它炒成了土豆丝,:-D。

再具体一点,比如除了Activity可以创建一个Dialog,其它Context都不可以创建Dialog。比如在Application中创建Dialog会报错,还有Application和Service可以启动一个Activity,但是需要创建一个新的task。比如你在Application中调用startActivity(intent)时系统也会崩溃报错。

报错的原因并不是因为他们拿到的Context资源不同,拿到的都是一个Resoucres对象,但是在创建Dialog的时候会使用到Context对象去获取当前主题信息,但是我们知道Application和Service是继承自ContextWrapper,没有实现关于主题的功能,然而Activity是继承自ContextThemeWrapper,该类是实现了关于主题功能的,因此创建Dialog的时候必须依附于Activity的Context引用。

结论:

Application、Service、Activity,它们本身对Resources资源处理方法的不同,造成了这个Resoucres最后表现出来的不一样,这么说大家就都懂了吧!

四:Context内存泄漏

关于Context的内存泄漏,找到一篇比较不错的文章分享给大家。

Android开发,中可能会导致内存泄露的问题

写在最后:

Context可能还有更多深层次的知识需要我们去了解,比如Context这些封装类,是具体如何通过Binder跟ContextImpl进行关联的;资源对象都被存储在ArrayMap,为什么ArrayMap中会有可能存在多个资源对象,如何访问其他应用程序的Context资源等等,剩下的这些就靠大家慢慢发掘了~

你足够了解Context吗?相关推荐

  1. 「技术综述」如何降低遮挡对人脸识别的影响

    https://www.toutiao.com/a6711562985187312131/ 作者 | 刘东平.谢远伦.言有三 编辑 | 言有三 人脸识别技术都是基于人脸关键特征提取对比完成识别的,其中 ...

  2. 如何降低遮挡对人脸识别的影响

    刘东平 重庆工商大学,本科 谢远伦 贵州大学,本科 言有三 毕业于中国科学院,计算机视觉方向从业者,有三工作室等创始人 作者 | 刘东平.谢远伦.言有三 编辑 | 言有三 人脸识别技术都是基于人脸关键 ...

  3. 吃瓜笔记 | 旷视研究院:被遮挡人脸区域检测的技术细节(PPT+视频)

    主讲人:袁野 | 旷视研究院研究员 屈鑫 编辑整理 量子位 出品 | 公众号 QbitAI 12月27日晚,量子位·吃瓜社联合Face++论文解读系列第四期开讲,本期中旷视(Megvii)研究院解读了 ...

  4. 声纹识别与声源定位(一)

    针对目前智能计算机及大规模数据的发展,依据大脑处理语音.图像数据方法的deep learning技术应运而生.deep learning技术是应用于音频信号识别,模仿大脑的语音信号学习.识别的模式.在 ...

  5. 怎样降低遮挡对人脸识别的影响,人脸图像算法研究(3)

    人脸识别技术都是基于人脸关键特征提取对比完成识别的,其中,特征的完整性是算法成败的一个极为关键的因素,但是当受到外界干扰较多,外界的条件发生明显变化的时候,由于传统方法过于依赖明显的.完整的图像特征, ...

  6. 操作系统,看这一篇足够了!

    [注]多谢原著作者 文章主要结构图如下 操作系统 现代计算机系统由一个或多个处理器.主存.打印机.键盘.鼠标.显示器.网络接口以及各种输入/输出设备构成. 然而,程序员不会直接和这些硬件打交道,而且每 ...

  7. 关于Block Formatting Context--BFC和IE的hasLayout

    转文请标明 --- 出处:穆乙 http://www.cnblogs.com/pigtail/ 一.BFC是什么? BFC(Block Formatting Context)直译为"块级格式 ...

  8. Android:字节跳动必备Context原理解析及使用

    Activity mActivity =new Activity() 作为Android开发者,不知道你有没有思考过这个问题,Activity可以new吗?Android的应用程序开发采用JAVA语言 ...

  9. [译]React高级话题之Context

    前言 本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读. 原文地址:Context Context提供了一种不需要手动地通过props来层层传递的方式来传递数据. 正文 在典型的Reac ...

最新文章

  1. OGG目标端复制Sequence时Hang住的问题
  2. 企业级Java应用最重要的4个性能指标
  3. HDFS多用户管理ACL机制other权限访问控制的理解
  4. SAP query生成的程序名
  5. 2021年网购大数据:哪些品类坑最多?什么价位的商品最有保障?
  6. js中的generator
  7. 计算机论文搜索技巧【二】
  8. OSGi 的核心配置、动态化及问题
  9. PS中rename-item的一些高级应用
  10. 3S基础知识:在VC++中嵌入MapX的集成二次开发
  11. android studio银联,android studio引入最新版银联支付功能
  12. 曲苑杂坛--DML操作中如何处理那些未提交的数据
  13. 基于Spring boot的云客服系统实现
  14. BT-3の蓝牙技术原理のBT协议の蓝牙核心协议の链路管理协议(LMP)
  15. Xbox 360抢鲜测评
  16. 发动机太热 请不要用冷水降温
  17. 两种模块化语法(module.exports,exports,require export,import)
  18. httpclient模拟post请求json封装表单数据
  19. 塔科夫为什么远程服务器返回错误,逃离塔科夫登陆错误解决教程
  20. 牛顿前插公式计算程序例题与matlab代码(计算方法)

热门文章

  1. [转载]SVN使用教程
  2. 【现代机器人学】基于指数积的机械臂正运动学
  3. 执行conda install tensorflow solving environment一直转圈圈,Anaconda无法打开的问题
  4. mysql导入报错1071_导入sql文件报错:1071 Specified key was too long; max key length is 767 bytes...
  5. java 2 %3c%3c 3_Shiro 实战(二)-授权
  6. python分布式开发容易吗_Python能实现分布式的进程吗?
  7. mysql questions_sql_mysql
  8. 在 ASP.NET 2.0 中创建 Web 应用程序主题(一)
  9. STL迭代器iterator
  10. shell sort result to self