Context

1.Context的继承关系和源码分析

Android 中 上下文Context理解_第1张图片Android 中 上下文Context理解_第2张图片

/** * 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. */

以上是api-28中google官方对context的注释,大概翻译如下:
1.是对应用上下文的描述;
2.是抽象类;
3、通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作。

Context类它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。ContextThemeWrapper类,其内部包含了与主题(Theme)相关的接口,只有Activity才需要主题,Service是不需要主题的,因为Service是没有界面的后台场景,所以Service直接继承于ContextWrapper,Application同理。而ContextImpl类则真正实现了Context中的所有函数,应用程序中所调用的各种Context类的方法,其实现均来自于该类。

一、Context相关类的继承关系
源代码(部分)如下:

public abstract class Context {           ...           //获得系统级服务           public abstract Object getSystemService(String name);            //通过一个Intent启动Activity           public abstract void startActivity(Intent intent);             //启动Service          public abstract ComponentName startService(Intent service);            //根据文件名得到SharedPreferences对象           public abstract SharedPreferences getSharedPreferences(String name,int mode); //Activity token  弹框中有用到          public IBinder getActivityToken();         //         public ApplicationInfo getApplicationInfo();         ...      }

ContextImpl.java类,该类实现了Context类的功能。请注意,该函数的大部分功能都是直接调用其属性mPackageInfo去完成.
源代码(部分)如下:

class ContextImpl extends Context{          //所有Application程序公用一个mPackageInfo对象         ActivityThread.PackageInfo mPackageInfo;                    @Override          public Object getSystemService(String name){              ...              else if (ACTIVITY_SERVICE.equals(name)) {                  return getActivityManager();              }               else if (INPUT_METHOD_SERVICE.equals(name)) {                  return InputMethodManager.getInstance(this);              }          }           @Override          public void startActivity(Intent intent) {              ...              //开始启动一个Activity              mMainThread.getInstrumentation().execStartActivity(                  getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);          }      }

ContextWrapper类是对Context类的一种包装,该类的构造函数包含了一个真正的Context引用,即ContextIml对象。

public class ContextWrapper extends Context {          Context mBase;  //该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值                    //创建Application、Service、Activity,会调用该方法给mBase属性赋值          protected void attachBaseContext(Context base) {              if (mBase != null) {                  throw new IllegalStateException("Base context already set");              }              mBase = base;          }          @Override          public void startActivity(Intent intent) {              mBase.startActivity(intent);  //调用mBase实例方法          }      }

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

public class ContextThemeWrapper extends ContextWrapper {           //该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值           private Context mBase;          //mBase赋值方式同样有一下两种           public ContextThemeWrapper(Context base, int themeres) {                  super(base);                  mBase = base;                  mThemeResource = themeres;           }                 @Override           protected void attachBaseContext(Context newBase) {                  super.attachBaseContext(newBase);                  mBase = newBase;           }      }

应用程序创建Context实例的:

  • 创建Application对象时,而且整个App共一个Application对象
  • 创建Service对象时
  • 创建Activity对象时

所以App共有的Context数目公式为:
总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context实例)

1、创建Application对象  每个应用程序在第一次启动时,都会首先创建Application对象。如果对应用程序启动一个Activity(startActivity)流程比较清楚的话,创建Application的时机在创建handleBindApplication()方法中,该函数位于 ActivityThread.java类中 ,如下:    //创建Application时同时创建的ContextIml实例      private final void handleBindApplication(AppBindData data){          ...          ///创建Application对象          Application app = data.info.makeApplication(data.restrictedBackupMode, null);          ...      }            public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {          ...          try {              java.lang.ClassLoader cl = getClassLoader();              ContextImpl appContext = new ContextImpl();//创建一个ContextImpl对象实例              appContext.init(this, null, mActivityThread);//初始化该ContextIml实例的相关属性              ///新建一个Application对象               app = mActivityThread.mInstrumentation.newApplication(                      cl, appClass, appContext);             appContext.setOuterContext(app);  //将该Application实例传递给该ContextImpl实例                   }           ...      }
2、创建Activity对象  通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调onCreate(),onStart()方法等,函数都位于 ActivityThread.java类 ,如下:    private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {          ...          Activity a = performLaunchActivity(r, customIntent);  //启动一个Activity      }      private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {          ...          Activity activity = null;          try {              //创建一个Activity对象实例              java.lang.ClassLoader cl = r.packageInfo.getClassLoader();              activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);          }          if (activity != null) {              ContextImpl appContext = new ContextImpl();      //创建一个Activity实例              appContext.init(r.packageInfo, r.token, this);   //初始化该ContextIml实例的相关属性              appContext.setOuterContext(activity);            //将该Activity信息传递给该ContextImpl实例              ...          }          ...          }
3、创建Service对象     通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:    //创建一个Service实例时同时创建ContextIml实例      private final void handleCreateService(CreateServiceData data){          ...          //创建一个Service实例          Service service = null;          try {              java.lang.ClassLoader cl = packageInfo.getClassLoader();              service = (Service) cl.loadClass(data.info.name).newInstance();          } catch (Exception e) {          }          ...          ContextImpl context = new ContextImpl(); //创建一个ContextImpl对象实例          context.init(packageInfo, null, this);   //初始化该ContextIml实例的相关属性          //获得我们之前创建的Application对象信息          Application app = packageInfo.makeApplication(false, mInstrumentation);          //将该Service信息传递给该ContextImpl实例          context.setOuterContext(service);          ...      }

通常我们想要获取Context对象,主要有以下四种方法

  1. View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
  2. Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
  3. ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
  4. Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使ApplicationContext也可以。
    区别:getApplication()和getApplicationContext()

Context引起的一些问题(网络上的例子)
错误的单例模式

public class Singleton {        private static Singleton instance;        private Context mContext;            private Singleton(Context context) {                this.mContext = context;    }                public static Singleton getInstance(Context context) {               if (instance == null) {  instance = new Singleton(context);        }               return instance;    }}

这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象,其中也包含Activity,假如Activity A去getInstance获得instance对象,传入this,常驻内存的Singleton保存了你传入的Activity A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。

View持有Activity引用public class MainActivity extends Activity {    private static Drawable mDrawable;        @Override    protected void onCreate(Bundle saveInstanceState) {                super.onCreate(saveInstanceState);        setContentView(R.layout.activity_main);                ImageView iv = new ImageView(this);        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);        iv.setImageDrawable(mDrawable);    }}

有一个静态的Drawable对象当ImageView设置这个Drawable时,ImageView保存了mDrawable的引用,而ImageView传入的this是MainActivity的mContext,因为被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,MainActivity被销毁时,也不能被GC掉,所以造成内存泄漏。
正确使用Context
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
自己的经验:
1:如果我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式不推荐使用,Service同Application。
2:在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。
总结:凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。

更多相关文章

  1. android抓log方法
  2. android 登陆右上角 的关闭按钮的使用方法
  3. Android AESUtils 工具类与调用方法
  4. Android wifi的WifiInfo对象详解
  5. sqlit导入外部数据库查找数据方法
  6. Android NDK打印log到logcat的方法
  7. Android之JAVASe基础篇-面向对象-IO(九)
  8. android用户界面详尽教程实例
  9. [置顶] 我的Android进阶之旅------>Android Widget 桌面数字时钟

随机推荐

  1. Android移动应用开发_基础页面布局总结
  2. Android Battery 分析
  3. 轻松学Android开发
  4. Android(安卓)7.0 启动篇 — init原理(一)(
  5. Error generating final archive: Debug
  6. 推送功能,(服务器向android客户端推送信息,
  7. Android 菜单(OptionMenu)
  8. android 笔记 02
  9. Android的StatusBar资料收集
  10. Android属性之build.prop生成过程