java.lang.IllegalStateException: Fragment未附加到Activity

181

在调用API时,我很少遇到这个错误。

java.lang.IllegalStateException: Fragment  not attached to Activity

我尝试将代码放入 isAdded() 方法中,以检查片段是否已添加到其活动中,但仍很少出现这种错误。我不明白为什么我仍然会出现这个错误。我该如何避免?

它显示错误在以下行上-

cameraInfo.setId(getResources().getString(R.string.camera_id));

下面是我正在进行的样本API调用。

SAPI.getInfo(getActivity(),
                new APIResponseListener() {
                    @Override
                    public void onResponse(Object response) {


                        cameraInfo = new SInfo();
                        if(isAdded()) {
                            cameraInfo.setId(getResources().getString(R.string.camera_id));
                            cameraInfo.setName(getResources().getString(R.string.camera_name));
                            cameraInfo.setColor(getResources().getString(R.string.camera_color));
                            cameraInfo.setEnabled(true);
                        }


                    }

                    @Override
                    public void onError(VolleyError error) {
                        mProgressDialog.setVisibility(View.GONE);
                        if (error instanceof NoConnectionError) {
                            String errormsg = getResources().getString(R.string.no_internet_error_msg);
                            Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                        }
                    }
                });

cameraInfo.setId(getActivity().getResources().getString(R.string.camera_id)); - Ashwin H
13个回答

237

这个错误是由两个因素的联合作用引起的:

  • 当HTTP请求完成时,它会调用要么onResponse()要么onError()(它们在主线程中运行),而不知道Activity是否仍处于前台。如果Activity已经消失了(用户导航到其他地方),getActivity()将返回null。
  • Volley的Response被表达为匿名内部类,隐式地持有对外部Activity类的强引用。这会导致一个典型的内存泄漏。

为了解决这个问题,您应该始终执行以下操作:

Activity activity = getActivity();
if(activity != null){

    // etc ...

}

还有,在onError()方法中也要使用isAdded()

@Override
public void onError(VolleyError error) {

    Activity activity = getActivity(); 
    if(activity != null && isAdded())
        mProgressDialog.setVisibility(View.GONE);
        if (error instanceof NoConnectionError) {
           String errormsg = getResources().getString(R.string.no_internet_error_msg);
           Toast.makeText(activity, errormsg, Toast.LENGTH_LONG).show();
        }
    }
}

2
当在Activity中使用Volley请求和AsyncTask时,没有绝对可靠的方法来避免NPE。用户可能会在其中一个线程在后台执行某些操作时导航离开当前Activity,然后当线程完成并调用onPostExecute()onResponse()时,就没有了Activity。你所能做的就是在代码的各个点上进行空引用检查,但这并不是百分之百可靠的 :) - Yash Sampat
2
Android Monkey测试(adb shell monkey)非常擅长在您尚未以通用/全局方式解决该错误时找出它。 - Groovee60
8
isAdded()方法足够了,最终的公共布尔型返回值为isAdded()函数: 如果mActivity不为null且mAdded为真,则返回真。 - lannyf
2
@ruselli:检查added布尔标志以及当前的Activity实例是否为null - Yash Sampat
1
这怎么算是一个解决方案呢?当然,你不再会收到“IllegalStateException”并且应用程序也不会崩溃,但是当“Activity”为空或“Fragment”未附加到它时,我们实际上应该做什么呢?你的解决方案基本上是说“添加一个空检查”,但它并没有说明我们应该做什么来避免“Activity”变为空,我们应该做什么来避免“Fragment”未附加到“Activity”,或者在这些情况发生时我们应该做什么。这个答案更像是一个解决方法而不是真正的解决方案。当“if”语句中的条件为false时,我们应该怎么办? - Vratislav Jindra
显示剩余10条评论

74

片段生命周期非常复杂并且充满了错误,请尝试添加:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}

2
我应该把它放在哪里? - Vaclovas Rekašius Jr.
1
@VaclovasRekašiusJr。似乎你可以在片段内的任何地方访问Activity。很有趣! - TylerJames
3
如果activity == null,我该怎么办才能使我的应用程序保持活动状态?@Miroslav - pavel
1
请查看isAdded()函数,你可能会发现"activity != null"并不是多余的。 - BertKing
@BertKing return mHost != null && mAdded; - 这就是fragment.isAdded()方法内部的内容。我原以为如果你追踪它,mHost是一个Activity,但似乎mHost在FragmentActivity中。所以,你可能是对的。还有其他补充吗? - Johnny Five
我也是这样做的。但不知怎么的,有时候还是会出现相同的错误... - chitgoks

26

我找到了非常简单的解决方案:isAdded() 方法是用来判断当前 Fragment 是否已经附加到它的 Activity 上的 Fragment 方法之一。

我们可以在 Fragment 类中的任何地方使用它,例如:

if(isAdded())
{

// using this method, we can do whatever we want which will prevent   **java.lang.IllegalStateException: Fragment not attached to Activity** exception.

}

16

异常: java.lang.IllegalStateException: Fragment DeadlineListFragment{ad2ef970} 没有附加到 Activity

类别: 生命周期

描述: 当在后台线程执行耗时操作(例如 AsyncTask)时,同时创建了一个新的 Fragment,并在后台线程完成之前将其分离到 Activity 中。在 UI 线程中的代码(例如 onPostExecute)调用了已分离的 Fragment,抛出此异常。

修复方案:

  1. 在暂停或停止 Fragment 时取消后台线程

  2. 使用 isAdded() 检查 Fragment 是否已连接并从 Activity 中获取资源。


13

我可能会迟到,但可能会帮助别人......

最好的解决方案是创建一个全局应用程序类实例,并在你的活动未被附加的特定片段中调用它。

就像下面这样:

icon = MyApplication.getInstance().getString(R.string.weather_thunder);

这里是应用程序类

public class MyApplication extends Application {

    private static MyApplication mInstance;
    private RequestQueue mRequestQueue;

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

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
}

1
是的,这种方法也被广泛使用。 - CoolMind
2
这不是一个明智的选择。FragmentContext和ApplicationContext具有不同的样式。FragmentContext可以具有深色主题,自定义样式,区域设置等,这将从不同的文件中提取颜色、字符串资源等。而ApplicationContext可能无法正确地拉取资源。如果您没有上下文,那么您就不应该尝试呈现该资源。 - Jemshit Iskenderov

5
在Fragment中使用isAdded()方法,如果该Fragment当前已与Activity连接,则返回true。
如果您想在Activity中进行检查,请使用该方法。
 Fragment fragment = new MyFragment();
   if(fragment.getActivity()!=null)
      { // your code here}
      else{
       //do something
       }

希望这能帮助到某些人


4

如果您实例化了一个无法实例化的片段,可能会出现此错误:

Fragment myFragment = MyFragment.NewInstance();


public classs MyFragment extends Fragment {
  public void onCreate() {
   // Some error here, or anywhere inside the class is preventing it from being instantiated
  }
}

在我的情况下,当我尝试使用时遇到了这个问题:
private String loading = getString(R.string.loading);

4

基本思路是你正在一个 UI 操作 上运行一个片段,这个片段正在进入 onDetach 生命周期。

当发生这种情况时,片段将从堆栈中移除并且失去Activity的上下文。

因此,当您调用与UI相关的函数,例如调用进度条,并且您想要离开该片段时,请检查该片段是否已添加到堆栈中,如下所示:

if(isAdded){ progressBar.visibility=View.VISIBLE }


2
我采用了以下方法来处理这个问题。创建一个新类,充当活动方法的包装器,如下所示:
public class ContextWrapper {
    public static String getString(Activity activity, int resourceId, String defaultValue) {
        if (activity != null) {
            return activity.getString(resourceId);
        } else {
            return defaultValue;
        }
    }

    //similar methods like getDrawable(), getResources() etc

}

现在,无论何时我需要从片段或活动中访问资源,我都使用这个类来代替直接调用方法。如果活动的context不为null,它将返回资产的值。而如果context为null,则传递一个默认值(也由函数的调用者指定)。

重要提示:这不是解决方案,而是一种有效的方式,您可以优雅地处理此崩溃。如果可能的话,您会想在获取活动实例为空的情况下添加一些日志并尝试修复该问题。


2
这将解决你的问题。
在你的Fragment中添加此内容。
Activity activity;
@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    activity = context instanceof Activity ? (Activity) context : null;
}

然后将 getContext(),getActivity(),requireActivity()或requireContext() 替换为 activity


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