为什么要扩展Android的Application类?

188

扩展的Application类可以声明全局变量。还有其他原因吗?


这只是我脑海中的一个想法,但你应该能够覆盖onCreate并显示一次性启动屏幕,而不是MainActivity,即在用户第一次打开应用程序时显示介绍屏幕。 - btse
13个回答

146

介绍:

enter image description here

  1. 如果我们在移动设备上考虑一个apk文件,它由多个有用的块组成,如ActivityService等。
  2. 这些组件不会定期彼此通信,不要忘记它们有自己的生命周期。这表明它们可能在某个时刻处于活动状态,而在另一个时刻处于非活动状态。

需求:

  1. 有时我们可能需要一种情况,其中需要访问整个Application中变量及其状态,而不管用户使用的是哪个Activity
  2. 例如,用户可能需要访问保存他的个人信息(例如姓名)的变量,该变量必须在整个Application中被访问,
  3. 我们可以使用SQLite,但创建Cursor并再次关闭它对性能不利,
  4. 我们可以使用Intent传递数据,但它很笨拙,并且活动本身可能在某些情况下不存在,这取决于内存可用性。

Application类的用途:

  1. 访问整个Application中的变量,
  2. 您可以使用Application启动某些内容,如分析等,因为应用程序类在ActivityService运行之前启动,
  3. 有一个重写的方法onConfigurationChanged(),当应用程序配置更改(从横向到纵向及其反之)时触发,
  4. 还有一个事件onLowMemory(),当Android设备内存不足时触发。

在您的需求部分,为什么不使用SharedPreferences呢? - user6685522
在第一个例子中,可以使用SharedPreferences来保存个人信息。但是你在最后部分给出的示例消除了我的疑虑。谢谢! - Saurabh Singh

68

Application类是您的应用程序的完整生命周期对象。它是作为应用程序的最高层级。可能使用示例:

  • 您可以通过覆盖Application类中的onCreate,在应用程序启动时添加所需内容。

  • 存储从Activity到Activity跳转的全局变量。例如AsyncTask。

    等等


4
将应用程序作为应用程序全局变量的垃圾场是一个很大的代码气味。你应该使用自己的自定义、更具体的类作为单例或使用静态变量来完成这个任务。 - Austin
6
@Austin 为什么会有气味? - Relm
2
是啊,为什么会出现这种情况呢?如前所述,Application类位于层次结构的顶部,我可以打赌我的午餐钱,一个自定义的单例类在它下面。因此,如果不得已而为之,当您的手机内存不足时,我会说自定义单例类是第一个被杀死的,而不是Application类(它本质上是您整个应用程序)。 - Starwave

37

有时您想要存储数据,例如全局变量,需要从多个Activities访问 - 有时需要在应用程序中随处访问。在这种情况下,Application对象将帮助您。

例如,如果您想要为每个http请求获取基本身份验证数据,则可以在应用程序对象中实现身份验证数据的方法。

之后,您可以在任何一个Activity中像这样获取用户名和密码:

MyApplication mApplication = (MyApplication)getApplicationContext();
String username = mApplication.getUsername();
String password = mApplication.getPassword();

最后,请记得将Application对象用作单例对象:

public class MyApplication extends Application {
    private static MyApplication singleton;

    public MyApplication getInstance(){
        return singleton;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        singleton = this;
    }
}

欲了解更多信息,请点击应用程序类


3
请您解释一下,为什么我们需要显式地创建一个应用程序类(Application Class)的单例对象,据我所知它本身就是一个单例。如果我们可以创建多个应用程序对象,那么如何创建?会有什么后果?请解释一下。 - Syed Raza Mehdi
不,可能只有一个应用程序类。https://developer.android.com/guide/topics/manifest/manifest-intro.html#filec - IntelliJ Amiya
1
那么为什么我们需要显式地维护它的单例对象呢?难道不是操作系统为我们维护吗?实际上,我遇到了一个代码,在活动类中创建了一个应用程序对象,并且在清单中没有提到它。我认为这是错误的,而且我很好奇为什么我们要制作静态单例对象。你认为最好的方法是什么?谢谢回复。 - Syed Raza Mehdi
1
谢谢,我在这个链接上找到了答案:https://developer.android.com/reference/android/app/Application.html - Syed Raza Mehdi
那里有 singleton 对象? - Cyph3rCod3r

33

一时之间,我无法想象出哪种情况下扩展 Application 会比其他方法更可取或者必要来完成某些事情。如果你有一个昂贵且经常使用的对象,可以在 IntentService 中初始化它,当你发现该对象当前不存在时。Application 本身在 UI 线程上运行,而 IntentService 在其自己的线程上运行。

我喜欢使用显示 Intent 将数据从 Activity 传递到 Activity,或使用 SharedPreferences。也有通过接口将数据从 Fragment 传递到其父 Activity 的方法。


45
扩展应用程序类有许多用途,其中一个非常有用的是捕获应用程序中所有未捕获的异常。因此,这是一件非常方便的事情。 - png
9
+1 for "prefer to pass data from Activity to Activity with explicit Intents, or use SharedPreferences". 我们应尽可能消除全局状态,并使用标准的Android全局状态管理工具,而不是静态变量/单例等。 - user1744056
10
为什么要这样做呢?为了在某些时候准备在不同进程或其他地方运行它们,并且每个组件都可以被任何应用程序重复使用,尽管有意限制了这一点。只传递数据对象而不对其进行序列化可以节省CPU和内存。将东西打包为内部处理-同一设备的交接是不理想的。我真的看不出来Intentservice像那样使用的意义(只需使用新的线程)。实际上,很多让程序员感到困惑的内容基本上都来自于谷歌添加的“帮助程序”,好像活动在分开的计算机上运行一样。 - Lassi Kinnunen

10

Application类是一个单例类,可以从任何活动或其他拥有Context对象的地方访问它。

你还获得了一些生命周期。

你可以使用Application的onCreate方法来实例化昂贵但经常使用的对象,例如分析助手。然后你可以在任何地方访问和使用这些对象。


7
你也会获得一点生命周期。你可能需要重新措辞一下。 - wtsang02
2
我的意思是,你可以得到一些生命周期的调用,但不像活动或片段那样多。例如,Application类没有onDestroy()方法。 - Jon F Hancock
这个类是自动创建的吗? - Konstantin Konopko
是的,只要在AndroidManifest.xml文件中正确指定即可。 - Jon F Hancock
不,它不是自动创建的。您必须先创建它,然后在清单文件中声明它。 - Ojonugwa Jude Ochalifu

8

最佳应用程序类的使用方法。 例如:假设您需要在启动完成后重新启动您的闹钟管理器。

public class BaseJuiceApplication extends Application implements BootListener {

    public static BaseJuiceApplication instance = null;

    public static Context getInstance() {
        if (null == instance) {
            instance = new BaseJuiceApplication();
        }
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();


    }

    @Override
    public void onBootCompleted(Context context, Intent intent) {
        new PushService().scheduleService(getInstance());
        //startToNotify(context);
    }

1
我想知道为什么我们需要创建应用程序对象的静态引用,而不是使用getApplication()获取并将其强制转换为应用程序类。据我所知,应用程序类是由操作系统自己创建的,它应该只有一个实例,由操作系统维护。请解释一下,谢谢。 - Syed Raza Mehdi
你是正确的。在你的应用程序中,从任何应用程序组件调用getApplication都会返回单个派生自Application的实例,即你的应用程序。这由Android框架在内部处理。你只需要将返回的实例转换为扩展Application的自定义类的类型。你还需要相应地设置你的清单文件,以便Android框架使用适当的类来实例化该实例。 - Matt Welke

5

我看到这个问题缺少答案。我扩展了Application,因为我使用了Bill Pugh的Singleton实现(参见参考文献),我的一些单例需要上下文。 Application类如下:

public class MyApplication extends Application {

    private static final String TAG = MyApplication.class.getSimpleName();

    private static MyApplication sInstance;

    @Contract(pure = true)
    @Nullable
    public static Context getAppContext() {
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() called");
        sInstance = this;
    }
}

单例看起来像这样:

public class DataManager {

    private static final String TAG = DataManager.class.getSimpleName();

    @Contract(pure = true)
    public static DataManager getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private DataManager() {
        doStuffRequiringContext(MyApplication.getAppContext());
    }

    private static final class InstanceHolder {
        @SuppressLint("StaticFieldLeak")
        private static final DataManager INSTANCE = new DataManager();
    }
}

这样一来,我在使用单例模式时不需要每次都要有一个上下文,并且只需编写极少量的代码即可进行懒汉式同步初始化。
提示:更新Android Studio单例模板可以节省大量时间。

5

这不是一个答案,而是一条观察:请记住,扩展应用程序对象中的数据不应该与活动实例绑定在一起,因为有可能同时运行两个相同的活动实例(一个在前台,一个未被显示)

例如,您通过启动器正常启动活动,然后“最小化”它。然后,您启动另一个应用程序(例如Tasker),它启动了您的活动的另一个实例,例如为了创建快捷方式,因为您的应用程序支持android.intent.action.CREATE_SHORTCUT。如果然后创建了快捷方式,并且此创建快捷方式调用了活动并修改了应用程序对象中的数据,则在将其带回前台时,后台运行的活动将开始使用此修改后的应用程序对象。


5

来源:https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class

在许多应用程序中,没有必要直接使用应用程序类。但是,以下是一些可以接受的自定义应用程序类的用途:

  • 需要在创建第一个活动之前运行的专业任务
  • 需要在所有组件之间共享的全局初始化(崩溃报告、持久化等)
  • 用于轻松访问静态不可变数据(例如共享网络客户端对象)的静态方法

您永远不应该在应用程序对象中存储可变实例数据,因为如果您假定数据会留在那里,您的应用程序最终将由于空指针异常而崩溃。应用程序对象不能保证永远留在内存中,它可能被销毁。与普遍的观念相反,应用程序不会从头开始重新启动。Android 将创建一个新的应用程序对象并启动用户之前所在的活动,以给出在第一次时似乎应用程序从未被杀死的错觉。


4
我认为您可以使用Application类来完成许多事情,但它们都与您需要在任何Activity或Service启动之前执行某些操作有关。例如,在我的应用程序中,我使用自定义字体。我不是从每个Activity调用Typeface.createFromAsset()方法来获取我的字体引用,而是在我的Application类的onCreate()方法中执行此操作:这样做的好处是避免了内存泄漏,因为您每次调用该方法时都会保留对assets的引用。
private App appInstance;
Typeface quickSandRegular;
...
public void onCreate() {
    super.onCreate();

    appInstance = this;
    quicksandRegular = Typeface.createFromAsset(getApplicationContext().getAssets(),
                       "fonts/Quicksand-Regular.otf");
   ...
   }

现在,我也有这样一个定义的方法:
public static App getAppInstance() {
    return appInstance;
}

还有这个:

public Typeface getQuickSandRegular() {
    return quicksandRegular;
}

因此,在我的应用程序中的任何位置,我只需要执行以下操作:
App.getAppInstance().getQuickSandRegular()

在我看来,Application类的另一个用途是在需要连接时,在活动和服务实际开始之前检查设备是否连接到Internet并采取必要措施。


2
说得好。非常好的分解。 - Oluwatobi Adenekan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接