Android getActivity在为片段执行线程后一直返回null

Android getActivity在为片段执行线程后一直返回null,android,firebase,android-fragments,android-activity,firebase-realtime-database,Android,Firebase,Android Fragments,Android Activity,Firebase Realtime Database,在将问题标记为重复问题之前,请先阅读我的问题 我有一个map fragment,它使用firebase实时数据库执行了很多进程,我面临的问题是一些回调会进行密集的处理,这使得map非常滞后,有时会使应用程序崩溃,所以我创建了一个新线程来执行回调,其中有一些更新UI的代码,因此,我使用runOnUiThread运行它 当我第一次打开片段时,一切都很好,在我按下back并再次打开它之后,getActivity总是来null 我试过这个著名的解决办法 FragmentActivity mActivit

在将问题标记为重复问题之前,请先阅读我的问题

我有一个map fragment,它使用firebase实时数据库执行了很多进程,我面临的问题是一些回调会进行密集的处理,这使得map非常滞后,有时会使应用程序崩溃,所以我创建了一个新线程来执行回调,其中有一些更新UI的代码,因此,我使用
runOnUiThread
运行它

当我第一次打开片段时,一切都很好,在我按下back并再次打开它之后,
getActivity
总是来
null

我试过这个著名的解决办法

FragmentActivity mActivity;

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

  @Override
  public void onDetach() {
    super.onDetach();
    mActivity = null;
  }
并使用了
mActivity
而不是
getActivity
,但它不起作用
mActivity
也一直以
null
的形式出现

我不知道为什么会发生这种错误!当我再次打开片段时,它再次添加到backbackback上,并再次附加,因此活动不应为null,以及为什么它仅在第一次启动时添加才起作用

代码

  void updateStudents() {

    if (isRecursionEnable) {
      return;
    }

    isRecursionEnable = true;
    thread = new Thread(new Runnable() {
      @Override
      public void run() {

        if (!thread.isInterrupted()) {
          studentQuery.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(final DataSnapshot snapshot, String s) {
              Student_User.add(snapshot.getValue(TestUserStudent.class));
              if (getActivity() != null) { // NPE
                getActivity().runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                    showAllMarkers(snapshot);  // show custom markers on the map
                  }
                });
              }
            }

            @Override
            public void onChildChanged(final DataSnapshot snapshot, String s) {
              Student_User.add(snapshot.getValue(TestUserStudent.class));
              if (getActivity() != null) {
                getActivity().runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                    showAllMarkers(snapshot);
                  }
                });
              }
            }

            @Override
            public void onChildRemoved(DataSnapshot snapshot) {
            }

            @Override
            public void onChildMoved(DataSnapshot snapshot, String s) {
            }

            @Override
            public void onCancelled(DatabaseError error) {
            }

          });
        }

      }

    }, "");
    thread.start();

  }

此问题显示应用程序中存在一些依赖性问题。也许你应该找到其他方法来实现你的愿望

无论如何,请看一下:

.setRetainInstance(true)

这并不理想,你应该避免。但如果没有其他办法,它可能会解决你的问题


希望有帮助。

这个问题很难解决,因为UI代码与数据加载逻辑纠缠在一起。我认为最好的解决办法是让这些东西独立起来。这很可能会解决问题,或者至少会使问题更容易发现和解决。也将改善总体特征设计

这里是一个简短的代码狙击手的要求,只是为了理解我的想法。我不会写整件事,因为这是不可能的

创建状态类,例如
StudentState
。该类还将提供一个侦听器接口,该接口将向片段发送更新

public class StudentState {
    // You might want to store Student_User here instead
    // I'm not sure how your data is currently represented.
    private DataSnapshot dataSnapshot;
    // Can also be a list of listeners if needed.
    private StudentStateListener studentStateListener;

    public void registerListener(StudentStateListener listener) {
        studentStateListener = listener;
    }

    public void unregisterListener() {
        studentStateListener = null;
    }

    ...

    public void setDataSnapshot(DataSnapshot dataSnapshot) {
        this.dataSnapshot = dataSnapshot;
        if (studentStateListener != null) {
         studentStateListener.onDataSnapshotLoadFinished(dataSnapshot);
        }
    }

    public interface StudentStateListener {
        void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot);
    }
}
在片段中实现它:

public class StudentFragment implements StudentStateListener {

    // The state can be initialized in multiple ways.
    // It can be a singleton, or stored in Application class, or
    // defined in your base activity, etc. Up to you. Ideally
    // should be injected via dependency injection if you use one, such as Dagger.
    private StudentState state;

    @Override
    public void onCreate() {
       // initialize state
       // ...
    }

    @Override
    public void onResume() {
        state.registerListener(this);
        // If the data has already been loaded you might also want the following.
        // It's up to you.
        if (state.getDataSnapshot() != null) {
           showAllMarkers(state.getDataSnapshot());
        }
    }

    @Override
    public void onPause() {
        state.unregisterListener();
    }

    ...

    @Override
    public void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot) {
        if (!isAdded()) {
            return;
        }
        showAllMarkers(snapshot);
    }

    public void updateStudents() {
        // I'm not quite sure how to work with your API but basically the idea is to load a new data snapshot and store it in the state
        // This should happen in background thread.
        studentQuery.loadDataSnapshot(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot) {
                state.setDataSnapshot(dataSnapshot);
            }
        });
    }
}

我希望这有帮助。。祝你好运

从附加的代码中,我只能得出结论,您必须在尚未附加到任何内容的片段中运行它。但总的来说,没有足够的信息来说明其他问题。请检查这个答案。我认为这是最好的解决办法。请试一试,你需要更多的信息吗。!我在activitycreated中调用了这个方法。很难说到底出了什么问题,但我确信如果
getActivity()
返回null,这意味着片段被分离。它要么尚未连接,要么已经分离。与其尝试将数据加载与片段附件同步,不如将它们分开。在需要时加载数据,并将其存储在方便的位置。然后抛出加载已完成的事件。当附加片段时,它将捕获事件并更新UI。如果没有,它可以从存储数据的位置获取数据。因此,您可以避免这种回调依赖关系,因为它会导致问题。@DäñishShärmá也不起作用,一旦线程被执行,活动就会始终变为null。!我在
内部调用了activitycreated
,但没有任何更改。。一旦线程被执行,活动就会一直变为空。!为什么要将mActivity设置为null onDetach()?有什么特别的原因吗?如果没有,请尝试删除它。不,这只是一个变通办法,我试图解决这个问题,但没有奏效。如果您获取activity()?它还回什么吗?如果是,只需(FragmentActivity)getActivity()。如果它为null,则表示该片段已从活动中分离,我无法使用它来更新UI,因此我的问题是如何使getActivity始终不为null!