本系列分四篇文章详细介绍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 Studio系列(二)使用Android Studio开发/调试整个android系
  2. Android开发常用代码片段(三)
  3. Android 性能优化之Java(Android)代码优化 (三)
  4. Android常用代码之普通及系统权限静默安装APK
  5. android 常用代码
  6. 常用的android权限配置和常用工具代码
  7. Android终于公布源代码
  8. Android常用代码
  9. android 蓝牙打印程序源代码

随机推荐

  1. sql 语句插入结果为select和值混合示例
  2. 用SQL语句查询数据库中某一字段下相同值
  3. 显示同一分组中的其他元素的sql语句
  4. SQL Server 2005降级到2000的正确操作步
  5. sqlserver存储过程语法详解
  6. 省市县三级联动的SQL语句
  7. SQL Server 2005 数据库转 SQL Server 20
  8. Sql中将datetime转换成字符串的方法(CONV
  9. mssql和sqlite中关于if not exists 的写
  10. sql清空表数据后重新添加数据存储过程的