getActivity()在Fragment函数中返回null


191

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

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

是的,当我从“活动”中调用它时,它为null ...

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

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


我不确定将其粘贴到本文中时是否只是一个错误,但是您需要在后面加上括号getActivity()。另外,您如何实例化片段?您的layout.xml中有吗?
CaseyB 2011年

第二个代码片段属于哪里?活动的oncreate()方法?并且您已经调用过setContentView()吗?
Franziskus Karsunke,2011年

R.id.upperPar是布局中的一个元素,因此应该将其替换为片段,但这不是我的问题。我不明白为什么我在自定义片段方法中调用getActivity()时会得到null,所以说onActivityCreated方法中的getActivity是实际的活动不是null
Lukap 2011年

问题不是在布局中,应用程序运行良好,但为什么我对getActivity却为null?顺便说一下,包括它呈现的片段在内的所有元素都不应该在这里发布
Lukap

1
您应该调用此方法:f1.asd(); 在onActivityCreated方法中,该方法将在您的片段类中覆盖。
Namrata Bagerwal '17

Answers:


164

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

我建议添加一个

onAttach(Activity activity)

方法Fragment并在其上放置一个断点,并查看相对于您对的调用何时调用该方法asd()。您会看到在调用asd()出口的方法之后调用了它。该onAttach呼叫在Fragment连接到它的活动,从这个角度getActivity()将返回非空(NB也有一个onDetach()呼叫)。


5
我不明白如何解决您的问题。如果我的getActivity()尚未准备好,如何获取FragmentActivity对象的引用?
CeccoCQ 2011年

2
@Vivek我不太清楚您想要实现什么。如果您需要片段直接显示对话框,则让其执行创建时所需的操作,例如在其onCreateViewonActivityCreated方法中。我质疑为什么在问题发布中需要调用asd()。
PJL 2012年

3
不推荐使用onAttach
abbasalim '16

6
onAttach(Activity mActivity)似乎已被贬值..对此的任何解决方法
ashish.n

4
引入了API 24commitNow()
Nicolas

92

最好的解决方法是在调用onAttach时保留活动参考,并在需要的地方使用活动参考,例如

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

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

34
我们应该设置mActivity = null onDetach()吗?
奥利弗·皮尔曼

5
@OliverPearmain如果您将在onDetach()中执行此操作,那么将没有任何利润。您必须在onDestory()中将其无效。此外,您必须将其保留在WeakRefernce中。
Kirill Popov 2015年

我在两者中都将其作废onDestroy()onDetach()因为onDestroy()不能保证会被调用。
Mohammed Ali

8
Activity如果不将其无效,是否泄漏了onDestroy()
Mohammed Ali

2
根据developer.android.com/intl/zh-tw/guide/components/…的说法,在调用onCreateView()之前先调用onAttach()。但是在onCreateView()中调用getActivity()时,我仍然收到NullPointerException。怎么会这样
Kimi Chiu

81

当您getActivity()在删除片段后调用另一个完成的线程时,就会发生这种情况。典型的情况是在HTTP请求结束时(例如)调用getActivity()(例如)。ToastonResponse

为避免这种情况,您可以定义一个字段名称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;
    }
}
}

编码愉快,


19
我们应该将mActivity = null设置为吗?在onDetach()中?
Bharat Dodeja

thucnguyen,如何为单个活动应用程序声明mActivity静态?
iamthevoid 2015年

我完全没有理由Activity从另一个线程进行访问。无论如何,您不能做任何事情,甚至不做吐司。因此,您要么应该先将工作转移到主线程,要么根本不使用Activity。
Dzmitry Lazerka

4
@BharatDodeja我们应该设置mActivity = null onDetach吗?你知道了吗
SLearner

5
这将在不使活动无效的情况下泄漏。
Darek Deoniziak '16

30

建议在onAttach中保留对活动的引用的其他答案仅是建议解决实际问题。当getActivity返回null时,这意味着该片段未附加到Activity。最常见的情况是当Activity因轮换而消失或Activity完成但Fragment仍然注册了某种回调侦听器时发生。如果您需要对Activity进行某些操作,但当Activity消失时,当调用侦听器时,您将无能为力。在您的代码中,您应该检查一下getActivity() != null如果没有,那就什么也不要做。如果您保留对已消失的活动的引用,则将防止对该活动进行垃圾收集。用户可能不会看到您可能尝试做的任何UI事情。我可以想象某些情况下,您可能希望在回调侦听器中为与UI不相关的内容提供一个Context,在这些情况下,获取Application上下文可能更有意义。请注意,该onAttach技巧不会造成大的内存泄漏的唯一原因是,通常在回调侦听器执行之后,就不再需要它了,并且可以将其与Fragment,其所有View和Activity上下文一起进行垃圾回收。如果你setRetainInstance(true) 内存泄漏的可能性更大,因为“活动”字段也将保留,但是在轮换之后可能是先前的“活动”而不是当前的“活动”。


1
这正是我的问题。我有一个片段来做一个过程->然后显示一个广告->然后过程继续。在某些设备中,从广告返回(通过侦听器到广告事件)后,getActivity()为null。但是我需要继续做另一部分工作才能完成工作。您是说没有解决办法吗?
Notbad

这正是我所面对的。我在一个片段中有一个“活动”接口,在其中执行一些计费工作。付款完成后,我想使用该界面执行某些操作,但是该界面为空。
弗雷迪

这似乎是针对该主题的数百个SO问题的正确通用答案。
曼努埃尔

最佳答案。SO上有很多针对Android的创可贴解决方案。
maxbeaudoin

因此,如果我想做一些操作,如何在getActivity()可用后执行(如果有的话)。
Sreekanth Karumanaghat

17

从Android API级别23开始,不推荐使用onAttach(Activity activity)。您需要使用onAttach(Context context)。http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

活动是上下文,因此,如果您可以简单地检查上下文是活动,并在必要时进行转换。

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

    Activity a;

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

}

如何使用它
ARR.s

10

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

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


3

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


首先,@ thucnguyen走在正确的轨道上

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

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

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

IMO的解决方案是防止在片段不再活动时发生回调(并且不仅限于Okhttp)。

解决方法:预防。

如果您查看片段的生命周期(更多信息,请参见此处),您会注意到其中的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();
    }
}

2

您在哪里调用此功能?如果在的构造函数中调用它Fragment,它将返回null

只要getActivity()onCreateView()执行该方法时调用即可。


1

进行如下操作。我认为这将对您有所帮助。

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

}

1

那些仍然对onAttach(Activity activity)有问题的人,它只是更改为Context-

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

在大多数情况下,保存上下文就可以满足您的需要-例如,如果您要执行getResources(),则可以直接从上下文中进行保存。如果您仍然需要将上下文放入“活动”中,请执行以下操作-

 @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所建议。


0

您可以使用onAttach,或者如果您不想将onAttach放在任何地方,则可以在主App类上放置一个返回ApplicationContext的方法:

public class App {
    ...  
    private static Context context;

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

    public static Context getContext() {
        return context;
    }
    ...
}

之后,您可以在项目的所有地方重复使用它,如下所示:

App.getContext().getString(id)

如果这不适合您,请告诉我。


0

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.