getActivity()在ActionBar Fragment中返回null

时间:2014-01-23 11:48:31

标签: android nullpointerexception fragmentpageradapter

如果在getActivity()之前调用,片段中的

null将返回fragment.onAttach(activity)

fragment.onActivityCreated(savedInstanceState)之后调用

fragment.onAttach(activity)。有关片段生命周期的信息,请参阅developer.android

我的片段中有一个尝试实例化数据库连接的方法。最初调用该方法时,它可以正常工作。

public void onActivityCreated(Bundle savedInstanceState){
   super.onActivityCreated(savedInstanceState);
    Log.d("MyTag", "Activity being created");
    if (savedInstanceState == null){
        listAdapter.setContext(getActivity());
        execSearch(0);
    } else { listAdapter.setContext(getActivity()); }
}

public void execSearch(Long searchId) {
    MySQLiteHelper dbHelper = new MySQLiteHelper(getActivity());
    SQLiteDatabase database = dbHelper.getReadableDatabase();
    ... 
}

现在,当在列表视图中点击按钮后通过界面调用相同的方法时,getActivity()会返回null(在片段中。

以下是ListAdapter中的按钮回调:

private final View.OnClickListener editSearchListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Long search_id = (Long) v.getTag();
        ((MyInterface) context).searchCalled(search_id);
    }
};

MainActivity中的接口实现:

@Override
public void searchCalled(Long search_id) {
    fragment.execSearch(id);
}

第二次我在null内得到getActivity()的{​​{1}}回复。

我被困了!!这是否与来自UI操作的接口调用有关。我是否需要在UI线程???

上实现mainActivity.searchCalled作为runnable

1 个答案:

答案 0 :(得分:5)

由于这是我第一次开发Android应用程序,想要分享我学到的一些东西,以便为其他新的Android开发人员更直观地解决这个问题。我正在开发的背景:

  • 允许创建搜索,搜索列表和搜索结果列表的三个片段。这些都可以自己初始化,但可以从共享接口(即下面的SearchManager界面)发送消息和接收消息。
  • 一个FragmentPagerAdapter,用于将片段分配到各自的标签并在它们之间移动。这更适合我的设计,因为我只有三个标签可以在它们之间滑动。对于更多标签集,请使用FragmentStatePagerAdapter
  • 用于在片段之间进行通信的SearchManager接口。

为什么我遇到上述问题和许多类似的问题

基本上我要做的是保留用户在configuration changes之间所做的UI的任何更改(这对于新手了解活动创建与应用启动不同是有帮助的 - 重复创建活动当应用程序遇到配置更改或被其他应用程序替换时。这对我来说并不直观。)

有两种方法可以做到这一点。您可以在Fragment.onSaveInstanceState中保存必要的数据,然后在创建新活动时使用保存的数据重新创建片段,也可以在Fragment.onCreate中致电setRetainInstance

我最初使用的是savedInstanceState方法。由于这不会保留我的片段实例,因此只要我得到getActivity() == null,就是因为代码是从未附加到活动的旧片段实例运行的。 当从onClickListener调用一个接口时,可能会发生这种情况,该接口尚未更新为指向当前活动实例(即附加到当前活动的实例)。即监听器是在之前创建的配置更改,并在配置更改后调用,而不会被告知已创建新的Activity实例。

我最终选择了setRetainInstance(true)路线。这意味着:

    创建新活动时,不会调用
  • onCreate。如果活动被破坏,则不会调用onDestroy
  • savedInstanceState中的
  • onActivityCreated将始终为空
  • 将保留片段上的所有属性。

这使事情变得简单而困难。基本上我推荐这条路线,如果你有很多属性/数据不依赖于知道当前活动是什么(例如表格值,要显示的列表)。但是,在这种情况下,必须在创建新活动时告知需要了解当前活动的任何属性。您可以按如下方式执行此操作:

     @Override
     public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.activity = (MainActivity) activity;
        // custom method on list adapter, so any calls to SearchManager interface 
        // are properly routed to current activity
        myListAdapter.setContext(activity); 
     }

有助于了解的其他有用的东西

片段实例之间的通信

我听说过有关如何在片段之间进行通信的相互矛盾的建议。一方面,我听说你shouldn't have references to your fragment instances outside of the adapter。另一方面,您会听到fragment instances should not communicate directly with each other。这意味着使用FragmentPagerAdapter为片段实现通信接口。但是,即使在这种情况下,您也必须聪明地在适配器中获取片段实例(例如,您可以get the adapter's tag for your fragments或更好leverage the adapter's instantiateItem method)。

最终,因为你削减馅饼的任何方式你必须破解以获得你的片段实例,我走出适配器并创建了一个headless fragment来实现我的SearchManager接口。这使得接口与适配器分离,避免了我的功能片段与MainActivity之间的通信。

跨配置更改保留视图

最后一点。当我创建新的搜索表单时,我注意到所有输入都被删除了我的配置更改(即附加/创建新片段时)。这是因为即使setRetainInstance设置为true,仍会调用片段的onCreateView方法,要求您对视图进行充气并将其返回。正是这个过程消除了对视图的任何用户操作。所以我在我的所有片段的onCreateView方法中都执行了以下操作:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    if (view == null){
        view = inflater.inflate(R.layout.doc_list_fragment,
            container, false);
    } else {
        ((ViewGroup) view.getParent()).removeView(view);
    }
    setContext();
    return view;
}

因为setRetainInstance为真,所以视图在第一次充气后保留在片段实例中。 removeView业务可以防止此错误:

FATAL EXCEPTION: main java.lang.IllegalStateException: 
The specified child already has a parent. You must call removeView() on the child's parent first. 

此外,我在此处调用了我的自定义setContext方法,因为此时我既有我的观点又有我的活动。因此,如果任何点击监听器需要知道当前活动是什么,我现在有了我准备好的视图,并且可以创建监听器以正确地将它们指向当前活动。

已完成项目

您可以在GitHub上查看已完成的应用。