代码之家  ›  专栏  ›  技术社区  ›  USER249

调用AndroidViewModel会使应用程序崩溃

  •  0
  • USER249  · 技术社区  · 7 年前

    我有一个片段(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();
    

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

    0 回复  |  直到 5 年前
        1
  •  171
  •   Milad Faridnia Alexey Zakharov    10 年前

    commit 安排事务,也就是说,它不会立即发生,但在下一次主线程准备就绪时,它被安排为主线程上的工作。

    我建议加一个

    onAttach(Activity activity)
    

    你的方法 Fragment 在它上面放一个断点,看看它什么时候被调用,相对于你对它的调用 asd() . 您将看到它是在调用的方法之后调用的 建筑署() 出口。这个 onAttach 碎片 getActivity() 将返回非null(注意,还有一个 onDetach()

        2
  •  96
  •   Pawan Maheshwari    8 年前

    @Override
    public void onAttach(Context context) {
        super.onAttach(activity);
        mContext = context;
    }
    
    @Override
    public void onDetach() {
        super.onDetach();
        mContext = null;
    }
    
        3
  •  88
  •   thanhbinh84    7 年前

    这是你打电话的时候发生的 getActivity() 在片段被移除后完成的另一个线程中。典型的例子是打电话 (例如 Toast )HTTP请求完成时(在 onResponse

    为了避免这种情况,可以定义字段名 mActivity 用它来代替 getActivity() . 此字段可以在片段的onAttach()方法中初始化,如下所示:

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

    在我的项目中,我通常使用以下功能为我的所有片段定义一个基类:

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

        4
  •  47
  •   miguel    6 年前

    其他的答案建议保留一个关于Nantach中活动的参考,只是建议给真正的问题贴上绷带。当getActivity返回null时,表示片段未附加到活动。最常见的情况是,当活动因旋转而消失或活动正在完成但 . 如果您需要对活动执行某些操作,但活动已停止,则调用侦听器时,您将无能为力。在代码中,您只需检查 getActivity() != null 如果没有,那就什么都不要做。如果保留对已消失活动的引用,则会阻止该活动被垃圾收集。用户不会看到您可能尝试执行的任何UI操作。我可以想象一些情况,在回调侦听器中,您希望为与UI无关的内容创建上下文,在这些情况下,获取应用程序上下文可能更有意义。请注意 onAttach 这个技巧并不是一个大内存泄漏,因为通常在回调侦听器执行之后,就不再需要它了,它可以与片段、其所有视图和活动上下文一起被垃圾收集。如果你 setRetainInstance(true)

        5
  •  19
  •   Yuliia Ashomok    10 年前

    自Android API级别23以来,OnAtach(活动)已被弃用。您需要使用onAttach(上下文)。 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;
        }
    
    }
    
        6
  •  10
  •   zajac.m2    13 年前

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

        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
  •  7
  •   Bogdan Zurac    13 年前

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

    1. 在commit()之后手动调用的任何方法
    2. onActivityCreated()

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

        8
  •  4
  •   zurfyx    8 年前

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


    第一部分 @thucnguyen was on the right track .

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

    甚至在活动结束之后 HttpCallback 试图更新一些片段字段,但得到一个 null 尝试创建时出现异常 getActivity()

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

    国际海事组织的解决办法是 再也没有了(不仅仅是Okhttp)。

    解决办法:预防。

    fragment lifecycle (更多信息 here onAttach(Context context) onDetach() 方法。它们分别在片段属于某个活动之后和停止之前被调用。

    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
  •  2
  •   Dev-iL    9 年前

    Fragment ,它会回来的 null .

    getActivity() 当方法 onCreateView() 被执行。

        10
  •  1
  •   Vinil Chandran    9 年前

    做如下事情。我想这对你会有帮助的。

    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
    
    }
    
        11
  •  1
  •   Shai    9 年前

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

    根据用户1868713的建议。

        12
  •  0
  •   Serdar Samancıoğlu    7 年前

    您将在ViewModel中定义一个LiveData对象,并在片段中观察它,当LiveData值更改时,它将仅在片段处于活动状态时通知您的观察者(在本例中为片段),因此,可以保证,只有当片段处于活动状态时,您的UI才能工作并访问活动。这是随之而来的一个优势 LiveData

    当然,当第一次问这个问题时,没有现场数据。我把这个答案留在这里,因为正如我所看到的,仍然存在这个问题,它可能会对某些人有所帮助。

        13
  •  0
  •   MuM6oJuM6o    6 年前

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

        14
  •  0
  •   RAHUL KUMAR    6 年前

    try{
                        InputStream is= getApplicationContext().getAssets().open("nutrient_list.json");
                        Nutrients nutrients=Nutrients.getNutrients(topRecognition,is);
    
                      } catch (IOException e) {
                        e.printStackTrace();
                      }
    
        15
  •  0
  •   Gk Mohammad Emon J. Jefferson    5 年前

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

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