【问题标题】:java.lang.IllegalStateException: Fragment not attached to Activityjava.lang.IllegalStateException:片段未附加到活动
【发布时间】:2015-04-24 17:21:51
【问题描述】:

我在进行 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));

标签: android android-fragments android-activity android-volley android-lifecycle


【解决方案1】:

此错误是由于两个因素的综合影响而发生的:

  • 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();
        }
    }
}

【讨论】:

  • Activity 中使用 Volley 请求和 AsyncTasks 时,没有万无一失的方法可以避免 NPE。当一个线程在后台做某事时,用户总是有可能离开当前的Activity,然后当线程完成并调用onPostExecute()onResponse()时,没有@ 987654337@。您所能做的就是检查代码中各个点的空引用,这不是防弹的:)
  • 如果您还没有以通用/全局方式解决此错误,那么 android monkey 测试 (adb shell monkey) 非常适合根除此错误。
  • isAdded() 就够了,final public boolean isAdded() { return mActivity != null && mAdded; }
  • @ruselli:检查added 布尔标志以及当前Activity 实例是否为null
  • @gauravjain 避免直接从 Fragment 发出异步请求(如 HTTP 调用)。从活动中执行它应该没问题。另外,从 FragmentManager 中清除 Fragment 引用,这是一种很好的做法,也是避免内存泄漏的最佳方法。
【解决方案2】:

Fragment 生命周期很复杂,漏洞百出,尝试补充:

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

【讨论】:

  • 我应该把它放在哪里?
  • @VaclovasRekašiusJr.似乎几乎可以从片段内部访问 Activity 的任何地方。好玩!
  • 如果 activity == null 我该怎么办。让我的应用程序保持活力@Miroslav
  • 查看isAdded(),你可能会发现“activity != null”不是多余的
  • @BertKing return mHost != null && mAdded; - 这就是 fragment.isAdded() 方法中的内容。如果你跟踪它,我认为 mHost 是一个 Activity,但似乎 mHost 在 FragmentActivity 内部。所以,可能,你是对的。有什么补充吗?
【解决方案3】:

我找到了非常简单的解决方案isAdded() 方法,它是用于识别当前片段是否附加到其 Activity 的片段方法之一。

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

if(isAdded())
{

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

}

【讨论】:

    【解决方案4】:

    异常: java.lang.IllegalStateException:片段

    DeadlineListFragment{ad2ef970} 未附加到 Activity

    类别:生命周期

    说明:在后台线程(例如AsyncTask)中进行耗时操作时,同时创建了一个新的Fragment,并在后台线程完成之前分离到Activity。 UI线程中的代码(例如onPostExecute)调用了一个分离的Fragment,抛出了这样的异常。

    修复解决方案:

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

    2. 使用 isAdded() 检查片段是否被附加 然后从活动中获取 getResources()。

    【讨论】:

      【解决方案5】:

      我可能会迟到,但可能会帮助某人..... 最好的解决方案是创建一个全局应用程序类实例并在未附加活动的特定片段中调用它

      如下图

      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;
          }
      }
      

      【讨论】:

      • 是的,这种方法也被广泛使用。
      • 这不是明智的选择。 FragmentContext 和 ApplicationContext 有不同的风格。片段上下文可能有深色主题、自定义样式、语言环境等。它们将从不同文件中提取颜色、字符串资源。虽然 ApplicationContext 可能无法提取正确的资源。如果您没有上下文,那么您不应该尝试渲染该资源。
      【解决方案6】:

      在片段中使用isAdded() 如果片段当前附加到 Activity,它将返回 true。

      如果你想查看Activity内部

       Fragment fragment = new MyFragment();
         if(fragment.getActivity()!=null)
            { // your code here}
            else{
             //do something
             }
      

      希望对大家有所帮助

      【讨论】:

        【解决方案7】:

        如果您正在实例化无法实例化的片段,则可能会发生此错误:

        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);
        

        【讨论】:

          【解决方案8】:

          我采用了以下方法来处理这个问题。创建了一个新类,作为此类活动方法的包装器

          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 为空,它会传递一个默认值(也由函数的调用者指定)。

          重要 这不是解决方案,这是一种有效的方法,您可以优雅地处理此崩溃。如果您将活动实例设置为空,您可能需要添加一些日志并尝试修复该问题(如果可能)。

          【讨论】:

            【解决方案9】:

            因此,基本想法是您正在对进入 onDetach 生命周期的片段运行 UI 操作

            当这种情况发生时,片段会离开堆栈并丢失 Activity 的上下文。

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

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

            【讨论】:

              【解决方案10】:

              当片段没有上下文时会发生这种情况,因此 getActivity() 方法返回 null。 在获取之前检查是否使用了上下文,或者 Activity 是否不再存在。在 fragment.onCreate 和 api 响应后使用上下文通常会出现此问题

              【讨论】:

                【解决方案11】:

                有时此异常是由支持库实现中的错误引起的。最近我不得不从 26.1.0 降级到 25.4.0 才能摆脱它。

                【讨论】:

                • 不,我没有,但也许我应该创建一个。
                【解决方案12】:

                当您调用一个不可用的上下文或调用它时为空时,就会出现此问题。当您在后台线程上调用主活动线程的上下文或在主活动线程上调用后台线程的上下文时,可能会出现这种情况。

                例如,我更新了我的共享首选项字符串,如下所示。

                editor.putString("penname",penNameEditeText.getText().toString());
                editor.commit();
                finish();
                

                然后在它之后调用finish()。现在它所做的是,当提交在主线程上运行并停止任何其他异步提交时,直到它完成。所以它的上下文是活动的,直到写入完成。因此,先前的上下文是 live ,导致错误发生。

                因此,如果某些代码存在此上下文问题,请确保重新检查您的代码。

                【讨论】:

                • 您是如何解决这个问题的?我在异步线程中调用它,我现在遇到了这个问题。
                • 只要确保写操作已经完成,那么只有上下文被杀死,而不是在写操作完成之前。
                猜你喜欢
                • 2018-01-05
                • 1970-01-01
                • 2014-04-13
                • 2012-12-01
                • 2021-09-02
                • 2018-09-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多