本系列分四篇文章详细介绍Context, 这是第四篇

  • Android深入理解Context–Application中Context的创建过程
  • Android深入理解Context–Activity中Context的创建过程
  • Android深入理解Context–Service中Context的创建过程
  • Android深入理解Context–Context使用的误区

getApplication()和getApplicationContext()的区别?

  • 我们先在我们的Activity中打印信息,代码如下(代码使用的是Kotlin,比较简单)

      class MainActivity : AppCompatActivity() {      val TAG: String by lazy {          MainActivity::class.java.simpleName      }      override fun onCreate(savedInstanceState: Bundle?) {          super.onCreate(savedInstanceState)          setContentView(R.layout.activity_main)          Log.i(TAG, "" + application)          Log.i(TAG, "" + applicationContext)      }  }
  • 结果如下:

    MainActivity: com.oman.dependency.MyApplication@841a27MainActivity: com.oman.dependency.MyApplication@841a27
  • 我们可以看到getApplication()getApplicationContext()返回的对象是一样的,地址值都一样。

  • 我们现在分析getApplication():

    public final Application getApplication() {    return mApplication;}
  • 这里的mApplication由前面第二篇 Android深入理解Context 知道,它其实是由ActivityThread.performLaunchActivity中的makeApplication创建的,并且由activity.attach(...,application,...)传递到Activity的。

  • 我们接着分析Activity.getApplicationContext()(其实在Activity启动流程源码分析中分析过这个), 因为Activity继承ContextWrapper, 发现最终会调用如下:

    public class ContextWrapper extends Context {    @Override    public Context getApplicationContext() {        return mBase.getApplicationContext();    }}
  • 这里的mBase,如果你看了之前的几篇文章,很清楚这里的mBase其实就是ContextImpl,所以我们进入如下:

    class ContextImpl extends Context {    @Override    public Context getApplicationContext() {        return (mPackageInfo != null) ?                mPackageInfo.getApplication() : mMainThread.getApplication();    }}
  • 这里mPackageInfoLoadedApk, 这里肯定不为null, 因为在ActivityThread.performLaunchActivity中开始部分,先判断if (r.packageInfo == null)的话就会给其赋值,而这里的mPackageInfo就是在创建ContextImpl的时候从那里传过来的。这样我们就进入到mPackageInfo.getApplication()中:

    Application getApplication() {    return mApplication;}
  • 这里的mApplication就是我们在LoadedApk.makeApplication中创建的application,这样我们通过getApplicationContext()就得到了全局唯一的mApplication

  • 也就是说LoadedApk.makeApplication中创建的application,首先在LoadedApk中将其赋值给LoadedApk的成员变量mApplication, 还是同一个application对象通过activity.attach往activity中存了一份,所以获取的结果自然是一样的(Service中这两个方法获取的值也是一样的)。

  • 那为什么既然结果一样,还要存在两个API供我们使用呢?其实这是因为getApplication方法仅仅限于ActivityService中有,其它的类没有,那么如果其它的类想要获取Application的实例的话,这么办呢?就可以通过context.getApplicationContext来获取了,也就是说这个方法的使用范围更加大,便于使用而已。

Context的使用误区:

  • 首先我们在自定义的MyApplication中写如下代码:运行后会crash:Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference;

    class MyApplication : Application() {    init {        Log.i("aaa", packageName)    }}
  • 经过调查发现是因为ContextWrapper中的mBase没有初始化导致的。那么mBase是什么时候初始化的呢,看过前面的文章,我想你应该很清楚了,下面根据源码再具体分析一下:

    @UnsupportedAppUsage    public Application makeApplication(boolean forceDefaultAppClass,            Instrumentation instrumentation) {        if (mApplication != null) {            return mApplication;        }        try {            java.lang.ClassLoader cl = getClassLoader();            if (!mPackageName.equals("android")) {                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,                        "initializeJavaContextClassLoader");                initializeJavaContextClassLoader();                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);            }            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);            //-----1-----            app = mActivityThread.mInstrumentation.newApplication(                    cl, appClass, appContext);            appContext.setOuterContext(app);        } catch (Exception e) {            if (!mActivityThread.mInstrumentation.onException(app, e)) {                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                throw new RuntimeException(                    "Unable to instantiate application " + appClass                    + ": " + e.toString(), e);            }        }        mActivityThread.mAllApplications.add(app);        mApplication = app;        if (instrumentation != null) {            try {                //-----2-----                instrumentation.callApplicationOnCreate(app);            } catch (Exception e) {                if (!instrumentation.onException(app, e)) {                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                    throw new RuntimeException(                        "Unable to create application " + app.getClass().getName()                        + ": " + e.toString(), e);                }            }        }        return app;    }
  • 上面1的代码会进入newApplication,如下:

    public Application newApplication(ClassLoader cl, String className, Context context)        throws InstantiationException, IllegalAccessException,        ClassNotFoundException {    Application app = getFactory(context.getPackageName())            .instantiateApplication(cl, className);    app.attach(context);    return app;}
  • 接着会调用app.attach:

    @UnsupportedAppUsage/* package */ final void attach(Context context) {    attachBaseContext(context);    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;}
  • 接着会调用attachBaseContext(context),到这里为mBase赋值了,然后才会调用上面注释2处的方法,进入application.onCreate()

    protected void attachBaseContext(Context base) {    if (mBase != null) {        throw new IllegalStateException("Base context already set");    }    mBase = base;}
  • 也就是说之所以上面会抛出异常,肯定是因为我们的mBase还没有初始化完成,就开始执行任务,所以想要保证不出问题,就最少要在attachBaseContext之后调用。

    class MyApplication : Application() {    override fun attachBaseContext(base: Context) {        // Log.i("aaa", packageName) 会报错        super.attachBaseContext(base)        Log.i("aaa", packageName)    }}

关于Application的单例

  • 其实从源码中我们就能看到Application是一个全局唯一的存在,如果我们想在应用中获取它的实例,直接像下面一样就可以了, 不能像我们常规的单例模式去判断为空然后new一个对象,那样的话就不具备我们的Context功能,所以这一点一定要注意。
    class MyApplication : Application() {    override fun onCreate() {        super.onCreate()        app = this    }    companion object {        private lateinit var app: MyApplication        fun getApplication(): MyApplication {            return app        }    }}

好了,到此为止,关于Context一系列就全部讲完了。

更多相关文章

  1. Android笔记 - Android启动之Launcher启动
  2. [原创] Android(安卓)Activity onNewIntent() 详解
  3. Android(安卓)NDK开发实例教程
  4. 转:Android(安卓)AsyncTask
  5. Android(安卓)LayoutInflater
  6. android 环境搭建helloworld
  7. 【android】Android(安卓)SDK 配置
  8. Android(安卓)Service AIDL 远程调用服务 【简单音乐播放实例】
  9. Android(安卓)ListView 去除边缘阴影、选中色、拖动背景色等

随机推荐

  1. Android虚拟导航键的显示隐藏实例
  2. Android快速开发(2)
  3. 集成easaui报错解决方案
  4. list滑动删除item
  5. Android腾讯微博客户端开发三:多账号管理
  6. http通信,Android(安卓)Gzip压缩解压
  7. Android(安卓)连接.net WebService 工具
  8. java.lang.RuntimeException: Unable to
  9. 日志工具类
  10. Android组件 文字标签(TextView)