从getApplicationContext和getApplication再次梳理Android的Application正确用法

Context

在Android开发的时候,不少地方咱们都会用上Context这个东西,好比咱们最经常使用的startActivity,之前也没怎么在乎这个到底有什么用,方法要参数就直接传过去,今天看到getApplicationContextgetApplication有点懵逼,我以为有必要去一探究竟了,首先看看什么是Context:android

Context,翻译为上下文,环境。多么直白又艹的翻译,想问啥又是上下文,啥又是环境,程序还有上下文。。。为了避免误人子弟,来Google的官方说法:app

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

翻译下:它是一个应用程序的全局环境,是Android系统的一个抽象类,能够经过它获取程序的资源,好比:加载Activity,广播,接收Intent信息等等。eclipse

总的来讲它就像是一个程序运行的时候的环境,若是Activity,Service这些是水里的鱼,那它就是水?(原谅个人理解能力,不知道怎么形容),好吧,理解不透就看代码(如下代码来自API-23):ide

public abstract class Context {}

首先它是个抽象类,那它提供了哪些方法,哎,太多了,随便看几个吧:工具

//[这个能够看看个人博客另一篇专门讲消息机制的](http://blog.csdn.net/ly502541243/article/details/52062179/)
public abstract Looper getMainLooper();
//获取当前应用上下文
public abstract Context getApplicationContext();
//开启activity
public abstract void startActivity(Intent intent);
//获取valus/strings.xml声明的字符串
public final String getString(@StringRes int resId) {
        return getResources().getString(resId);
    }
    
//获取valus/colors.xml声明的颜色    
public final int getColor(int id) {
    return getResources().getColor(id, getTheme());
}
//发送广播
public abstract void sendBroadcast(Intent intent);
//开启服务
public abstract ComponentName startService(Intent service);
//获取系统服务(ALARM_SERVICE,WINDOW_SERVICE,AUDIO_SERVICE、、、)
public abstract Object getSystemService(@ServiceName @NonNull String name);

咱们发现Context这个抽象类里面声明了不少咱们开发中经常使用一些方法,那有哪些类实现了这个抽象类呢(普及一个快捷键,eclipse下点击类后按F4能够看这个类的继承结构,AndroidStudio我设置的是eclipse的快捷键),结构以下oop

  • Contextui

    • ContextWrapperthis

      • TintContextWrapper
      • ContextThemeWrapper
      • IsolatedContext
      • MutableContextWrapper
      • ContextThemeWrapper.net

        • Activity
      • Service
      • RenamingDelegatingContext
      • Application
      • BackupAgent

咱们主要关注一下:ContextWrapper,Activity,Service,Application,先来看看Context的主要实现类ContextWrapper(剧透:其实这并非真正的实现类):看下官方注释,意思就是这是个简单的实现:翻译

Proxying implementation of Context that simply delegates all of its calls to another Context.

构造方法:

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    //设置BaseContext,同构造方法,多了个不为空的判断
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    
    .....
    .....
    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
    @Override
    public Looper getMainLooper() {
        return mBase.getMainLooper();
    }
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
}

注意方法是public的,因此继承类能够直接访问,看了方法的实现,咱们发现真是simply,就是都交给mBase来作相应的处理,关键就是构造方法或者attachBaseContext方法设置mBase而且进行操做。

来看看咱们最经常使用的Activity,主要看看getApplication

public class Activity extends ContextThemeWrapper implements ... {
       private Application mApplication; 
       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, String referrer, IVoiceInteractor voiceInteractor) {
            attachBaseContext(context); 
            ...
            mApplication = application;
        }
}
public final Application getApplication() {
        return mApplication;
}

咱们看到了在attach调用了咱们刚才说的attachBaseContext,还有给mApplication赋值。这里出现了另一个咱们关注的Application,到源码看看:

//构造方法传了个空,貌似没什么用
public Application() {
        super(null);
    }
//一样在attach中咱们看到了具体的东西
final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

Application和Activity的attach方法感受都差很少,都调用了attachBaseContext(context),成为了一个Context。

这里还看到了ContextImpl,其实它才是Context的真正实现类(看名字也看出来了),但是刚才咱们看Context的继承结构时没看到这个类啊,原来它跟ActivityThread同样,并无在sdk中,因此是看不到的。这个类的具体实现就不仔细看了,再看要晕了,后面有时间再细细品味...

可是咱们知道了mApplicationcontext是两个不一样的东西,因此严格意义上来讲getApplicationContextgetApplication是不同的,虽然不少时候他们返回的都是同一个对象,可是getApplication只存在于Activity或者Service中,咱们要注意具体的状况,这个咱们后面再说

Application

为什么物

看到这里咱们发现,Application和Activity都继承自Context,他们都是环境,只不过Application是随着咱们的应用(或者包)启动的时候就存在的环境,Activity是一个界面的环境

使用方法

既然Application是在应用一建立就初始化了,并且是在应用运行时一直存在的,那咱们能够把它当作是一个全局变量来使用,能够保存一些共享的数据,或者说作一些工具类的初始化工做。要本身来使用Application的话咱们须要先新建一个类来继承Application

public class MyApplication extends Application {}

而后重写它的onCreate作一些工具的初始化:

@Override
    public void onCreate() {
        super.onCreate();
        ToastUtils.register(this);
        //LeakCanary检测OOM
        LeakCanary.install(this);
    }

最后一个关键的工做是要在manifest里面作一下声明(无关代码我忽略了)

<application
    android:name=".MyApplication"
    ...
</application>

而后说说Application的获取问题,一个方法是咱们直接 (MyApplication)getApplication(),可是还有一种更常见的作法,要在其余没有Context的地方也能拿到怎么办呢?能够这样,仿照单例的作法(只是仿照!),在MyApplication声明一个静态变量

public class MyApplication extends Application {
    private static MyApplication instance;
}
@Override
    public void onCreate() {
        super.onCreate();
        instance = this;
}
 // 获取ApplicationContext
    public static Context getMyApplication() {
        return instance;
}

至此咱们拿到了MyApplication实例,注意这跟咱们常见的单例不同,不要自做聪明去在getMyApplication里面作一下空的判断,Application在应用中原本就是一个单例,因此每次返回的都是同一个实体,原文以下:

There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way.

总结

ApplicationActivityService都是继承自Context,是应用运行时的环境,咱们能够把Application看作是应用,Activity看作是一个界面,至于getApplicationContextgetApplication,在Activity和Service中他们返回的对象是同样的,可是在某些特殊状况下并不保证都是同样,因此若是想要拿到在manifest里面声明的那个Application,务必用getApplication,贴下原文:

getApplication() is available to Activity and Services only. Although in current Android Activity and Service implementations, getApplication() and getApplicationContext() return the same object, there is no guarantee that this will always be the case (for example, in a specific vendor implementation). So if you want the Application class you registered in the Manifest, you should never call getApplicationContext() and cast it to your application, because it may not be the application instance (which you obviously experienced with the test framework).

还有就是由于他们都继承自Context,好比在打开Dialog的时候好像是均可以,其实否则,好比咱们大多数状况:

AlertDialog.Builder builder = new Builder(Activity.this);//能够
 AlertDialog.Builder builder = new Builder(getApplicationContext());//内存泄漏

若是把this换成getApplicationContext(),不会报错,可是就如咱们刚才所说,getApplicationContext() 返回的上下文会随着应用一直存在,而这里的Dialog应该属于Activity,Activity关闭了咱们没法销毁上下文(Dialog持有全局的上下文)。

因此在使用的时候要注意具体的使用场景,避免内存泄漏问题。

这里顺便附上一个stackoverflow对于这个问题的连接


这篇文章还有个续集和补充
到底getApplicationContext和getApplication是否是返回同一个对象?

相关文章
相关标签/搜索