【问题标题】:getActivity() returns null in Fragment functiongetActivity() 在 Fragment 函数中返回 null
【发布时间】:2011-09-07 02:22:07
【问题描述】:

我有一个像这样的公共方法的片段 (F1)

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

是的,当我(从活动)调用它时,它是空的......

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

一定是我做错了什么,但我不知道那是什么。

【问题讨论】:

  • 我不确定您将它粘贴到这篇文章时是否只是一个错误,但您需要在getActivity() 后面加上括号。另外,你是如何实例化片段的?你的 layout.xml 中有吗?
  • 第二个代码片段属于哪里?到 Activity 的 oncreate() 方法?你已经调用 setContentView() 了吗?
  • R.id.upperPar 是布局中的一个元素,所以它应该被片段替换,但这不是我的问题。我不明白为什么当我在自定义片段方法中调用 getActivity() 时我得到 null,假设在 onActivityCreated 方法中 getActivity 是实际活动不是 null
  • 问题不在布局中,应用运行良好,但为什么我的 getActivity 为空?,顺便说一句,包括它呈现的片段在内的所有元素在这里不应该出现问题
  • 你应该调用这个方法:f1.asd();在要在片段类中覆盖的 onActivityCreated 方法中。

标签: android android-fragments android-activity null android-context


【解决方案1】:

commit 安排事务,即它不会立即发生,而是在下一次主线程准备好时安排为主线程上的工作。

我建议添加一个

onAttach(Activity activity)

对您的Fragment 设置一个断点并查看相对于您对asd() 的调用的调用时间。您会看到它是在您调用asd() 的方法退出之后调用的。 onAttach 调用是 Fragment 附加到其活动的位置,从此时起,getActivity() 将返回非空值(nb 还有一个 onDetach() 调用)。

【讨论】:

  • 我不明白您如何解决您的问题。如果我的 getActivity() 还没有准备好,如何获取 FragmentActivity 对象的引用?
  • @Vivek 我不太清楚你想要实现什么。如果您需要 Fragment 立即显示一个对话框,然后让它做它在创建时需要做的事情,例如在其onCreateViewonActivityCreated 方法中。我质疑为什么在发布问题时需要调用 asd()。
  • onAttach 已弃用
  • onAttach(Activity mActivity) 似乎已被贬值.. 任何解决方法
  • API 24 引入commitNow()
【解决方案2】:

最好的办法是在调用 onAttach 时保留活动引用,并在需要时使用活动引用,例如

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

【讨论】:

  • 我们应该设置 mActivity = null onDetach() 吗?
  • @OliverPearmain 如果您将在 onDetach() 中执行此操作,那么将没有利润。你必须在 onDestory() 中取消它。此外,您必须将其保存在 WeakReference 中。
  • 我在 onDestroy()onDetach() 中都取消它,因为不能保证调用 onDestroy()
  • 如果我们不在onDestroy() 中取消它,我们是否会泄露Activity
  • 根据developer.android.com/intl/zh-tw/guide/components/…,在调用onCreateView() 之前调用onAttach()。但是当我在 onCreateView() 中调用 getActivity() 时,我仍然得到 NullPointerException。怎么会这样?
【解决方案3】:

当您在另一个线程中调用getActivity() 时发生这种情况,该线程在删除片段后完成。典型情况是在 HTTP 请求完成时(例如在 onResponse 中)调用 getActivity()(例如 Toast)。

为避免这种情况,您可以定义一个字段名称mActivity 并使用它来代替getActivity()。该字段可以在 Fragment 的 onAttach() 方法中初始化如下:

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

在我的项目中,我通常为我的所有 Fragment 定义一个具有此功能的基类:

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

编码愉快,

【讨论】:

  • 我们应该设置 mActivity=null;在 onDetach() 中?
  • thucnguyen,如何为单活动应用声明 mActivity 静态?
  • 我看不出有任何理由从另一个线程访问Activity。无论如何,你不能用它做任何事情,甚至不能展示 Toast。所以你要么应该先将工作转移到主线程,要么根本不使用 Activity。
  • @BharatDodeja 我们应该设置 mActivity = null onDetach 吗?你发现了吗?
  • 这将在不取消活动的情况下泄漏。
【解决方案4】:

建议在 onAttach 中保留对活动的引用的其他答案只是建议使用创可贴来解决真正的问题。当 getActivity 返回 null 时,表示 Fragment 未附加到 Activity。最常见的情况是当 Activity 由于旋转或 Activity 完成而消失但 Fragment 仍然注册了某种回调侦听器时。如果您需要对 Activity 做某事但 Activity 消失了,那么当侦听器被调用时,您无能为力。在您的代码中,您应该只检查getActivity() != null,如果它不存在则不要做任何事情。如果您保留对已消失的 Activity 的引用,则您将阻止 Activity 被垃圾收集。您可能尝试执行的任何 UI 操作都不会被用户看到。我可以想象在某些情况下,您希望在回调侦听器中为非 UI 相关的内容提供上下文,在这些情况下,获取应用程序上下文可能更有意义。请注意,onAttach 技巧不是大内存泄漏的唯一原因是因为通常在回调侦听器执行后不再需要它,并且可以与 Fragment、它的所有 View 和 Activity 上下文一起被垃圾收集.如果您setRetainInstance(true),则内存泄漏的可能性更大,因为 Activity 字段也将被保留,但在轮换之后可能是上一个 Activity 而不是当前的。

【讨论】:

  • 这正是我的问题。我有一个片段执行一个过程->然后显示一个广告->然后过程继续。在从广告返回后(通过广告事件的侦听器)的某些设备中,getActivity() 为空。但我需要继续做另一部分的工作才能完成工作。你的意思是没有办法解决这个问题?
  • 这正是我所面临的。我在片段中有一个 Activity 接口,我在其中执行一些计费操作。支付完成后,我想用接口做点什么,但是接口一直为空。
  • 这似乎是对该主题的 100 个 SO 问题的正确一般答案。
  • 最佳答案。 SO 上有很多适用于 Android 的创可贴解决方案。
  • 所以如果我想做一些操作,在getActivity()可用后如何执行(如果有的话)。
【解决方案5】:

自 Android API 级别 23 起,onAttach(Activity activity) 已被弃用。您需要使用 onAttach(Context context)。 http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Activity 是一个上下文,所以如果您可以简单地检查上下文是一个 Activity 并在必要时进行转换。

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}

【讨论】:

  • 如何使用它
【解决方案6】:

PJL 是对的。 我已经使用了他的建议,这就是我所做的:

  1. 为片段定义全局变量:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. 已实现

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3 .我在线程中封装了我需要调用 getActivity() 的函数,因为如果它在主线程上运行,我将使用步骤 4 阻塞线程。并且永远不会调用 onAttach()。

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4 .在我需要调用 getActivity() 的函数中,我使用它(在调用 getActivity() 之前)

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

如果您有一些 UI 更新,请记住在 UI 线程上运行它们。我需要更新 ImgeView,所以我这样做了:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});

【讨论】:

    【解决方案7】:

    在 commit() 之后调用回调的顺序:

    1. 无论你在 commit() 之后手动调用什么方法
    2. onAttach()
    3. onCreateView()
    4. onActivityCreated()

    我需要做一些涉及一些视图的工作,所以 onAttach() 对我不起作用;它崩溃了。因此,我移动了在 commit() (1.) 之后调用的方法中设置一些参数的部分代码,然后在 onCreateView() (3.) 中处理视图的另一部分代码。

    【讨论】:

      【解决方案8】:

      我正在使用 OkHttp,我刚刚遇到了这个问题。


      对于第一部分@thucnguyen was on the right track

      当您在另一个线程中调用 getActivity() 时发生这种情况,该线程在片段被删除后完成。典型情况是在 HTTP 请求完成时(例如在 onResponse 中)调用 getActivity()(例如 Toast)。

      即使在活动已关闭之后仍会执行一些 HTTP 调用(因为完成 HTTP 请求可能需要一段时间)。然后,我通过HttpCallback尝试更新一些Fragment字段并在尝试getActivity()时得到null异常。

      http.newCall(request).enqueue(new Callback(...
        onResponse(Call call, Response response) {
          ...
          getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already
      

      IMO 的解决方案是防止在片段不再存在时发生回调(这不仅适用于 Okhttp)。

      修复:预防。

      如果您查看fragment lifecycle(更多信息here),您会注意到有onAttach(Context context)onDetach() 方法。这些在 Fragment 属于一个活动之后和停止之前分别被调用。

      这意味着我们可以通过在onDetach 方法中控制它来防止该回调发生。

      @Override
      public void onAttach(Context context) {
          super.onAttach(context);
      
          // Initialize HTTP we're going to use later.
          http = new OkHttpClient.Builder().build();
      }
      
      @Override
      public void onDetach() {
          super.onDetach();
      
          // We don't want to receive any more information about the current HTTP calls after this point.
          // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
          for (Call call : http.dispatcher().queuedCalls()) {
              call.cancel();
          }
          for (Call call : http.dispatcher().runningCalls()) {
              call.cancel();
          }
      }
      

      【讨论】:

        【解决方案9】:

        你在哪里调用这个函数?如果在Fragment的构造函数中调用,会返回null

        当方法onCreateView()被执行时,只需调用getActivity()

        【讨论】:

          【解决方案10】:

          请按以下步骤操作。我想这对你会有帮助。

          private boolean isVisibleToUser = false;
          private boolean isExecutedOnce = false;
          
          
          @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              View root = inflater.inflate(R.layout.fragment_my, container, false);
              if (isVisibleToUser && !isExecutedOnce) {
                  executeWithActivity(getActivity());
              }
              return root;
          }
          
          @Override
          public void setUserVisibleHint(boolean isVisibleToUser) {
              super.setUserVisibleHint(isVisibleToUser);
              this.isVisibleToUser = isVisibleToUser;
              if (isVisibleToUser && getActivity()!=null) {
                  isExecutedOnce =true;
                  executeWithActivity(getActivity());
              }
          }
          
          
          private void executeWithActivity(Activity activity){
              //Do what you have to do when page is loaded with activity
          
          }
          

          【讨论】:

          • setUserVisibleHint 被贬低
          【解决方案11】:

          onAttach(Activity activity)还有问题的人,改成Context了——

              @Override
          public void onAttach(Context context) {
              super.onAttach(context);
              this.context = context;
          }
          

          在大多数情况下,保存上下文对您来说就足够了 - 例如,如果您想要执行 getResources(),您可以直接从上下文中执行。如果您仍然需要将上下文添加到您的 Activity 中,请这样做 -

           @Override
          public void onAttach(Context context) {
              super.onAttach(context);
              mActivity a; //Your activity class - will probably be a global var.
              if (context instanceof mActivity){
                  a=(mActivity) context;
              }
          }
          

          根据 user1868713 的建议。

          【讨论】:

            【解决方案12】:

            另一个好的解决方案是使用带有 MVVM 架构的 Android 的 LiveData。 您将在 ViewModel 中定义一个 LiveData 对象并在您的片段中观察它,并且当 LiveData 值更改时,它只会在您的片段处于活动状态时通知您的观察者(在这种情况下为片段),因此可以保证您只有当您的片段处于活动状态时,您的 UI 才能正常工作并访问活动。这是LiveData 带来的优势之一

            当然,当第一次提出这个问题时,并没有 LiveData。我将这个答案留在这里,因为正如我所见,仍然存在这个问题,它可能对某人有所帮助。

            【讨论】:

              【解决方案13】:

              在onActivityCreated()中调用getActivity()方法

              【讨论】:

                【解决方案14】:

                我已经通过这种方式解决了我的问题。我已经从已经访问 getApplicationContext 的上一个类传递了 getApplicationContext。我已经将 Inputstream 对象传递给了我的新类 Nutrients。

                try{
                                    InputStream is= getApplicationContext().getAssets().open("nutrient_list.json");
                                    Nutrients nutrients=Nutrients.getNutrients(topRecognition,is);
                
                                  } catch (IOException e) {
                                    e.printStackTrace();
                                  }
                

                【讨论】:

                  【解决方案15】:

                  编写一个通用方法,确保您永远不会得到 null Activity。

                  public class BaseFragment extends Fragment {
                      private Context contextNullSafe;
                  
                       @Override
                      public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
                          super.onViewCreated(view, savedInstanceState);
                           /*View creation related to this fragment is finished here. So in case if contextNullSafe is null
                           * then we can populate it here.In some discussion in - https://stackoverflow.com/questions/6215239/getactivity-returns-null-in-fragment-function
                           * and https://stackoverflow.com/questions/47987649/why-getcontext-in-fragment-sometimes-returns-null,
                           * there are some recommendations to call getContext() or getActivity() after onCreateView() and
                           * onViewCreated() is called after the onCreateView() call every time */
                          if (contextNullSafe == null) getContextNullSafety();
                      }
                  
                  
                     @Override
                      public void onAttach(@NonNull Context context) {
                          super.onAttach(context);
                          contextNullSafe = context;
                      }
                  
                      /**CALL THIS IF YOU NEED CONTEXT*/
                      public Context getContextNullSafety() {
                                  if (getContext() != null) return getContext();
                                  if (getActivity() != null) return getActivity();
                                  if (contextNullSafe != null) return contextNullSafe;
                                  if (getView() != null && getView().getContext() != null) return getView().getContext();
                                  if (requireContext() != null) return requireContext();
                                  if (requireActivity() != null) return requireActivity();
                                  if (requireView() != null && requireView().getContext() != null)
                                      return requireView().getContext();
                                  
                                  return null;
                              
                          }
                  
                      /**CALL THIS IF YOU NEED ACTIVITY*/
                      public FragmentActivity getActivityNullSafety() {
                          if (getContextNullSafety() != null && getContextNullSafety() instanceof FragmentActivity) {
                              /*It is observed that if context it not null then it will be
                               * the related host/container activity always*/
                              return (FragmentActivity) getContextNullSafety();
                          }
                          return null;
                      }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-09-05
                    • 1970-01-01
                    • 2012-07-25
                    • 2013-12-29
                    • 2019-04-04
                    相关资源
                    最近更新 更多