前言:和有梦想的人一起奋斗,那也就不会觉得孤独了。


以前开发都是在别人搭完框架的情况下进行开发,今天遇到一个很头疼的问题,想要在做一个很常见的功能,当接收到极光推送的消息,就在当前页面显示一个dialog通知用户账号在别处已经登录,需要重新登录。这个问题真的是难倒我了,因为极光推送过来的时候,我们并不知道用户操作到了哪一个Activity,而Dialog初始化的时候需要一个Activity或者一个context作为实例化的参数。当我一脸懵逼的时候,还是万能的艳芳姐给我提供了解决的思路。也让我在一定程度上对Application有一定的认知。

android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。因此在安卓中我们可以避免使用静态变量来存储长久保存的值,而用Application。

这也给我们提供了解决思路,在我们需要获取到当前Context或者Activity对象的时候,我们就可以通过重写这个Application来保存当前的Context或者Activity。

public class BaseApp extends Application {
    private static BaseApp mApp;
    private static Activity sActivity;
    @Override
    public void onCreate() {
        super.onCreate();
        mApp = this;
        this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                Log.d("YWK",activity+"onActivityCreated");
            }

            @Override
            public void onActivityStarted(Activity activity) {
                Log.d("YWK",activity+"onActivityStarted");
                sActivity=activity;

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }

    public static Context getAppContext() {
        return mApp;
    }

    public static Resources getAppResources() {
        return mApp.getResources();
    }

    public static Activity getActivity(){
        return sActivity;
    }
}

这个就是我们重新写的Application,其实我们在做任何项目的时候,应该都会去重写这个Application,去实现一些全局变量的保存,向上面这段代码,我们保存了Activity对象和Context对象,然后重写Application的Oncreate,mApp=this,这里我们保存了一个Application Context,为什么说Application Context呢?我们在下面讨论。

然后我们通过Application调用registerActivityLifecycleCallbacks的方法,注册一个监听器,对Activity的生命周期进行监听,这样我们只要在一个Activity在start的时候,使sActivity=这个Activity就好了。

这样在初始化Dialog的时候 AlertDialog.Builder dialog = new AlertDialog.Builder(BaseApp.getActivity());我们将我们Application中获取到的最新的Activity传进去,这样不管用户在哪一个界面,我们都能获取到栈最顶层的Activity。

最后我们要给我们的应用设置上我们自定义的Application:

<application
        android:name=".base.BaseApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:largeHeap="true"

        android:theme="@style/AppTheme">

我们通过设置AndroidManifest.xml文件,在application中设置name属性就好了。



Application Context 和Activity Context的区别:

为什么我上面说是Application Context而不是说Activity Context呢?在解决上面那个功能的时候,我最开始就在初始化Dialog的时候我是这样写的:

AlertDialog.Builder dialog = new AlertDialog.Builder(BaseApp.getAppContext());

结果报错,一直定位在这一行。一直不解,后来才知道Application中获取的Context是一个全局的Application,它和Activity Context的使用场景有很大的不同。



大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)

注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。

android constraintLayout显示最上层 android 顶层窗口_ide

好了,这里我们看下表格,重点看Activity和Application,可以看到,和UI相关的方法基本都不建议或者不可使用Application,并且,前三个操作基本不可能在Application中出现。实际上,只要把握住一点,凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。

以上这段话是引用了鸿洋大神的博客,这么一看,我们也就很清楚了Application Context和Activity Context的区别。