【问题标题】:Should an internal DialogFragment class be static or not?内部 DialogFragment 类是否应该是静态的?
【发布时间】:2013-03-14 16:30:05
【问题描述】:

这是我用来学习 Android 的项目中的一个 sn-p 代码:

private void enableLocationSettings() {
    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    startActivity(settingsIntent);
}

@SuppressLint("ValidFragment")
public class EnableGpsDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Tytuł")
            .setMessage("wiadomosc")
            .setPositiveButton("odpal", new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    enableLocationSettings();
                }

            })
            .create();
    }
} 

如您所见,我必须添加 @SuppressLint 才能使我的应用正常运行,但在 guide 上,此注释不是必需的。

我做错了什么?

这是我的导入:

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.DialogFragment;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import android.widget.ToggleButton;

【问题讨论】:

    标签: android android-dialogfragment


    【解决方案1】:

    该示例没有这些注释,因为该类位于其自己的文件中。这意味着它独立于使用 Fragment 的 Activity。

    在您的情况下,您的 Fragment 位于 Activity 内,并且不使用 static 修饰符。这意味着它与 Activity 实例相关联。

    让 Fragment 依赖于 Activity 实例是个坏主意,这两个类都有复杂的生命周期(特别是因为 Activity 经常被销毁和重新创建)并且应该彼此独立。

    您需要将EnableGpsDialogFragment 的修饰符设为static

    public static class EnableGpsDialogFragment extends DialogFragment {
    

    static 类不依赖于封闭类的实例,因此警告将消失。

    有关更多信息,请阅读nested classes 上的 Java 教程。

    编辑以响应您的编辑: 现在这些类不依赖于彼此的实例,您将不得不取出一个 YourActivity 的实例,以便您可以调用 enabledLocationSettings() 一种方法是通过强制转换,并且仅当 EnableGpsDialogFragment 仅由 @ 使用时才有效987654330@:

    @Override
    public void onClick(DialogInterface dialog, int which) {
      enableLocationSettings();
    }
    

    @Override
    public void onClick(DialogInterface dialog, int which) {
      ((YourActivity)getActivity()).enableLocationSettings();
    }
    

    如果此 Fragment 将被多个 Activity 使用,您应该create an interface to be implemented by each Activity instead

    【讨论】:

    • 谢谢,但是将静态修饰符添加到 EnableGpsDialogFragment 迫使我将静态修饰符添加到私有 void enableLocationSettings()。如果我添加它,则会出现一个新错误:Cannot make a static reference to the non-static method startActivity(Intent) from type Activity
    • @szpic 好吧,那是因为您对问题的 编辑 现在包括对 enableLocationSettings(); 的调用
    • 为了规避这一点,您可以使用对活动所需内容的弱引用。然后在使用之前,检查它是否仍然有效。每次销毁/创建活动时都应设置此项以确保其有效。添加关于静态片段的内容:如果不是静态创建,它们可能会在此上下文中泄漏内存,因为它们可能持有对不再可访问且应该被垃圾收集的旧活动的引用。
    • 谢谢你拯救了我的一天。我希望我能给你更多的赞成票!
    【解决方案2】:

    所有 DialogFragment 都应该是公共的,并且 - 如果是内部类 - 是静态的。它们还应该有一个公共的无参数构造函数,并且只依赖 setArguments() 进行参数传递。

    一段时间以来,未能遵守这一点会产生一个 Lint 警告,如果你真的想的话,你可以抑制它,但从 Android 支持库 v25 及更高版本开始,如果尝试显示 DialogFragment,你实际上会得到一个异常不符合这些规则的:

    java.lang.IllegalStateException: Fragment TestActivity$TestDialogFrament must be a public static class to be properly recreated from instance state.

    如前所述,原因是操作系统必须能够重新创建所有片段,以防在将应用程序置于后台时出现内存不足的情况迫使它销毁片段。当应用程序再次置于前台时,应该可以从序列化的应用程序状态重新创建片段,这对于非静态内部类来说是不可能的,而不是来自封闭外部类的实例,并且重新创建-创建不是从那个上下文完成的。

    不幸的是,这使得对话框的事情变得更加复杂,因为创建一个覆盖 onCreateDialog 的匿名子类通常非常方便。但是,这样的对话框根本无法重新创建。

    【讨论】:

    • 低内存情况是一个真正的威胁,因为我们在廉价智能手机中至少有 2GB RAM...:) 我不明白为什么如果 Google 不得不销毁我的对话片段,他们为什么要重新创建它们 - 我可以自己做并且已经这样做了(我使用 v23,不会升级)。
    • 嗯,部分原因是因为很多应用程序行为不端并且基本上一直在运行,或者由于频繁的广播意图而不断重启。这在 Android N 和 O 中得到了严格的解决。如果要一直运行 50 个应用程序,将会有大量的应用程序从内存中换入和换出。另外,请记住,在 1080p 屏幕上,一个位图是 8 MB。而且我不太确定我是否理解您为什么要完全手动重新创建状态,但我们都有我们的款待......
    【解决方案3】:

    不应该!

    从我的角度来看,我不希望我的 DialogFragment(您的 NetworkConnectionError)是静态的,因为我希望能够从中调用我的包含类(Activity)的变量或方法。
    所以它不会是静态的。 但我也不想产生内存泄漏。
    那么解决办法是什么呢?
    很简单,当你进入 onStop 时,确保你杀死你的 DialogFragment,就这么简单。 所以代码看起来像这样:

    public class CarActivity extends AppCompatActivity{
    
    /**
     * The DialogFragment networkConnectionErrorDialog 
     */
    private NetworkConnectionError  networkConnectionErrorDialog ;
    //...  your code ...//
    @Override
    protected void onStop() {
        super.onStop();
        //invalidate the DialogFragment to avoid stupid memory leak
        if (networkConnectionErrorDialog != null) {
            if (networkConnectionErrorDialog .isVisible()) {
                networkConnectionErrorDialog .dismiss();
            }
            networkConnectionErrorDialog = null;
        }
    }
    /**
     * The method called to display your dialogFragment
     */
    private void onDeleteCurrentCity(){
        FragmentManager fm = getSupportFragmentManager();
         networkConnectionErrorDialog =(DeleteAlert)fm.findFragmentByTag("networkError");
        if(networkConnectionErrorDialog ==null){
            networkConnectionErrorDialog =new DeleteAlert();
        }
        networkConnectionErrorDialog .show(getSupportFragmentManager(), "networkError");
    }
    

    这样您就可以避免内存泄漏(因为这很糟糕),并且您可以确保您没有一个他妈的静态片段无法访问您的活动的字段和方法。 从我的角度来看,这是处理该问题的好方法。

    【讨论】:

    • DialogFragments 应该是具有公共无操作构造函数的公共静态类,就这么简单。如果没有,系统将无法在内存不足的情况下重新创建片段等。此外,从支持库 v25 开始,如果尝试显示不满足上述要求的 DialogFragment,您的应用程序将崩溃约束:java.lang.IllegalStateException: Fragment TestActivity$TestDialogFrament must be a public static class to be properly recreated from instance state.
    • 哎呀,错字:no-op -> no-arg
    • 没有。他们不应该。我可以自己重新创建它们。
    • 再一次,为什么在大多数正常情况下您更喜欢这样做?
    猜你喜欢
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 2015-01-12
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    相关资源
    最近更新 更多