代码之家  ›  专栏  ›  技术社区  ›  Bo Z

未附加到上下文的片段

  •  80
  • Bo Z  · 技术社区  · 6 年前

    在工具栏中的活动中,我得到了一个按钮,需要从片段中调用方法并更新该片段中的列表。现在这是一个错误。 调用活动

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case  R.id.menu_sort:
                ListFragment listFragment = new ListFragment();
                listFragment.sortByPopularity();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
    

    片段代码。我发现未附加活动时出错。但与上下文无关

    public class ListFragment extends Fragment implements ListAdapter.ItemClickListener {
    
        /**
         * Needed
         */
        RecyclerView recyclerView;
        View view;
        List<BasePojo.Result> list;
        ListAdapter listAdapter;
    
        public ListFragment() {
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
    
            /**
             * Main Initialization
             */
            view = inflater.inflate(R.layout.fragment_list, container, false);
            recyclerView = view.findViewById(R.id.recycler_list_detailed);
            recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
            list = new ArrayList<>();
            listAdapter = new ListAdapter(list, setOnItemClickCallback());
            recyclerView.setAdapter(listAdapter);
    
            RetrofitClient.getApiService().getPhotosList(getString(R.string.api_key)).enqueue(new Callback<BasePojo>() {
                @Override
                public void onResponse(Call<BasePojo> call, Response<BasePojo> response) {
                    BasePojo basePojo = response.body();
                    list.addAll(basePojo.getResults());
                    recyclerView.getAdapter().notifyDataSetChanged();
                }
    
                @Override
                public void onFailure(Call<BasePojo> call, Throwable t) {
                    Log.d("tag", "Response failed" + t.toString());
    
                }
            });
    
    
            return view;
        }
    
        @Override
        public void onItemClick(View view, int position) {
            Log.v("in on click", "value " + position);
    
        }
    
        private OnItemClickListener.OnItemClickCallback setOnItemClickCallback() {
            OnItemClickListener.OnItemClickCallback onItemClickCallback = new OnItemClickListener.OnItemClickCallback() {
                @Override
                public void onItemClicked(View view, int position) {
                    BasePojo.Result itemClicked = list.get(position);
                    Bundle bundle = new Bundle();
                    bundle.putString("title", itemClicked.getOriginalTitle());
                    bundle.putString("overview", itemClicked.getOverview());
                    bundle.putString("release_date", itemClicked.getReleaseDate());
                    bundle.putString("vote_average", itemClicked.getVoteAverage().toString());
                    bundle.putString("poster_path", itemClicked.getPosterPath());
                    DetailedFragment detailedFragment = new DetailedFragment();
                    detailedFragment.setArguments(bundle);
                    FragmentManager manager = getActivity().getSupportFragmentManager();
                    FragmentTransaction transaction = manager.beginTransaction();
                    transaction.replace(R.id.main_frame_list, detailedFragment);
                    Log.d("tag", "title is 111 " + bundle.get("title"));
    
                    transaction.commit();
                }
    
            };
            return onItemClickCallback;
        }
    
        @Override
        public void onAttachFragment(Fragment childFragment) {
            super.onAttachFragment(childFragment);
    
        }
    
        public void sortByPopularity() {
            RetrofitClient.getApiService().getPopularList(getString(R.string.api_key)).enqueue(new Callback<BasePojo>() {
                @Override
                public void onResponse(Call<BasePojo> call, Response<BasePojo> response) {
                    BasePojo basePojo = response.body();
                    list.addAll(basePojo.getResults());
                    recyclerView.getAdapter().notifyDataSetChanged();
                }
    
                @Override
                public void onFailure(Call<BasePojo> call, Throwable t) {
                    Log.d("tag", "Response failed" + t.toString());
    
                }
            }); }
    
    }
    

    这里有一个错误

    05-09 12:48:26.915 5775-5775/com.borisruzanov.popularmovies E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.borisruzanov.popularmovies, PID: 5775
    java.lang.IllegalStateException: Fragment ListFragment{6dbd6de} not attached to a context.
        at android.support.v4.app.Fragment.requireContext(Fragment.java:614)
        at android.support.v4.app.Fragment.getResources(Fragment.java:678)
        at android.support.v4.app.Fragment.getString(Fragment.java:700)
        at com.borisruzanov.popularmovies.ListFragment.sortByPopularity(ListFragment.java:110)
        at com.borisruzanov.popularmovies.MainActivity.onOptionsItemSelected(MainActivity.java:47)
        at android.app.Activity.onMenuItemSelected(Activity.java:3204)
        at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:407)
        at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:195)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
        at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:63)
        at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:203)
        at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:780)
        at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
        at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:171)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:973)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:963)
        at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:624)
        at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:150)
        at android.view.View.performClick(View.java:5610)
        at android.view.View$PerformClick.run(View.java:22265)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
    

    非常感谢您的时间和帮助。如果我的问题看起来不太好,请记下来,我会教你如何更好地提问

    12 回复  |  直到 6 年前
        1
  •  92
  •   Garg    4 年前

    就我而言,这个问题发生在我打电话的时候 getString()

    将此呼叫更改为 getActivity().getString() 解决了问题。

        2
  •  23
  •   Shawn Wong    6 年前

    使用 commit() 如果不能解决这个问题,我们应该尝试在源代码片段中找到解决方案。

    因此,从您提供的错误堆栈来看 requireContext() 在片段中为:

        public final Context requireContext() {
            Context context = getContext();
            if (context == null) {
                throw new IllegalStateException("Fragment " + this + " not attached to a context.");
            }
            return context;
        }
    

    这意味着系统将检查 Context 从…起 getContext() ,如果为null,将引发异常。

    因此,为了避免此问题,我们可以检查 getContext() 在做我们的生意之前。

        3
  •  20
  •   Peter Samokhin Daniel    6 年前

    创建片段实例是不够的。
    需要通过事务将其附加到活动:

    getFragmentManager()
        .beginTransaction()
        .replace(R.id.container_layout, fragment)
        .commit();
    

    成功提交后, onAttach 方法,创建视图,然后您可以与其视图交互。

    在您的情况下,创建片段实例并将其附加到活动中 onCreate ,然后致电 sortByPopularity 稍后在单击事件中。

    了解有关碎片生命周期的更多信息: https://developer.android.com/guide/components/fragments

        4
  •  17
  •   Ben    4 年前

    科特林:

    我的问题发生在 getString()

    将其更改为 context.getString() 解决了它

        5
  •  10
  •   oguzhan dbyrne    4 年前

    如果您正在使用 倒计时器 ,您可能会得到在完成计时器之前分离片段的错误原因。如果在中执行ui更改 onFinish(完成) 回调时,应检查上下文是否为null或与下面的内容不同;

        timer = object : CountDownTimer(startTimeInMillis, 1000) {
            override fun onTick(millisUntilFinished: Long) {
    
            }
    
            override fun onFinish() {
                context?.let {
                  //perform ui changes here 
                }
            }
        }
        timer?.start()
    

    或者,在分离片段之前,应该取消计时器,如下所示;

    override fun onDestroy() {
        super.onDestroy()
        timer?.cancel()
    }
    
        6
  •  5
  •   CoolMind    4 年前

    如果未显示(未添加)或删除片段,则 context == null 。在这种情况下,获取资源将导致此异常。 getString(R.string.some_string) 需要 context 还有车祸。

    您可以检查片段是否存在,以便:

    if (isAdded) {
        // Print getString(R.string.some_string).
    }
    

    但您可能需要打印字符串,即使在片段发布时也是如此,例如,在LogCat、analytics中,或者向服务器发送请求。在这种情况下,您需要一个应用程序上下文来获取字符串资源。

    class MyApplication : Application() {
    
        override fun onCreate() {
            super.onCreate()
    
            instance = this
        }
    
    
        companion object {
            lateinit var instance: MyApplication private set
        }
    }
    
    object Strings {
        fun get(@StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
            return instance.getString(stringRes, *formatArgs)
        }
    }
    

    然后设定 MyApplication 在AndroidManifest中使用: Strings.get(R.string.some_string)

        7
  •  3
  •   Saurabh Padwekar    4 年前

    Kotlin:使用惰性初始化

    override val contentMessage by lazy {
         getString(R.string.message)
    }
    
        8
  •  2
  •   I_tea_shey    5 年前

    我知道这是一篇老帖子,但我刚刚知道你能做些什么。的确,创建片段实例是不够的,它需要通过事务附加到活动。然而,您可以首先添加这两个片段,然后将它们从片段管理器中分离出来。这样,它们在fragmentManager中都是“活动的”,您可以稍后根据需要调用附加和分离这些片段。

    i、 e类

    .add(container, fragment1).detach(fragment1).add(container, fragment2).commit();
    .
    .
    .
    .
    .
    ft.detach(fragment2)
    ft.attach(fragment1
    
        9
  •  1
  •   Dhiraj Gupta    4 年前

    此断言可以发生在您有 requireContext() 调用以从片段访问Android上下文。在使用之前,请仔细查看呼叫站点 requireContext() .我只使用 requireContext() 当我确定片段将在当时附加到活动,或者用例非常重要,因此使用此断言比使用任何其他操作过程更好时。

    如果出于任何原因,片段可能碰巧未连接,您可以在调用站点通过避免或提前返回来处理它,那么更好的方法是空检查从 getContext() 只有这样才能继续前进。

    空检查的典型Kotlin代码如下所示:

    fun myFragmentFunction(){
        val context = getContext() ?: return // early return using Elvis operator
        context.whatever() // guaranteed non-null context at this point
    }
    
        10
  •  0
  •   Manoj Mohanty    4 年前

    在这个场景中,如果您没有任何依赖于上下文的类级属性,那么验证,因为片段没有提交,它就没有上下文,我们可能会出现这个异常。

        11
  •  0
  •   Qamar khan    3 年前

    对于kotlin开发者

    lifecycleScope.launchWhenResumed {
    // do your work here 
    }
    
        12
  •  0
  •   AngelesVP    3 年前

    正如Tam Huynh所说,当我们的碎片没有连接时,就会发生这种崩溃。我遇到了与您相同的问题(但有一个底部),现在它工作正常。

    我们收到这种错误的原因有两个:

    • requireContext() 如果上下文为null,则可能直接崩溃
    • 使命感 getString(R.string.xxx_xxx_xxx) 如果片段被分离,from fragment将崩溃(因为我们需要上下文,而上下文为null)。

    对我来说,通过这段代码,我们可以检查片段是否被附加,然后我就解决了崩溃问题。

    fun checkIfFragmentAttached(operation: Context.() -> Unit) {
            if (isAdded && context != null) {
                operation(requireContext())
            }
        }
    

    更多信息=> https://weidianhuang.medium.com/android-fragment-not-attached-to-a-context-24d00fac4f3d