从Fragment启动Activity导致NullPointerException

56

我在使用Fragment中的按钮启动Activity时遇到了问题。以下是我的代码:

ViewPagerAdapter.java

import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragments;

    public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        this.fragments = fragments;
    }

    @Override
    public int getCount() {
        return this.fragments.size();
    }

    @Override
    public Fragment getItem(int position) {
        return this.fragments.get(position);
    }

}

ViewPagerActivity.java

import java.util.List;
import java.util.Vector;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.util.Log;
import com.package.here.Class1Overview;
import com.package.here.Class1Overview;
import com.package.here.Class1Overview;

public class ViewPagerActivity extends FragmentActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.viewpager);

        ViewPager pager = (ViewPager)super.findViewById(R.id.my_viewpager);
        pager.setAdapter(initialisePaging());
    }

    private ViewPagerAdapter initialisePaging() {

        List<Fragment> fragments = new Vector<Fragment>();
        fragments.add(Fragment.instantiate(this, Class1Overview.class.getName()));
        fragments.add(Fragment.instantiate(this, Class2Overview.class.getName()));
        fragments.add(Fragment.instantiate(this, Class3Overview.class.getName()));
        return new ViewPagerAdapter(super.getSupportFragmentManager(), fragments);          
    }

}

Class1Overview.java(这里是按钮)

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import com.package.here.R;

public class FuelOverview extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            return null;
        }
        View v = (LinearLayout)inflater.inflate(R.layout.class_one_layout, container, false);
        View AddBtn = v.findViewById(R.id.btn_add);
        ((Button) AddBtn).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(getActivity(), ActivityToStart.class);
                startActivity(intent);
            }
        });

        return v;
    }
}

这是我的日志记录器(LogCat)

01-05 19:39:00.913: E/AndroidRuntime(10557): Uncaught handler: thread main exiting due to uncaught exception
01-05 19:39:01.034: E/AndroidRuntime(10557): java.lang.RuntimeException: Unable to pause activity {com.package.here/com.package.here.ViewPagerActivity}: java.lang.NullPointerException
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3162)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3119)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3102)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.access$2400(ActivityThread.java:119)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1870)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.os.Looper.loop(Looper.java:123)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.main(ActivityThread.java:4363)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at java.lang.reflect.Method.invokeNative(Native Method)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at java.lang.reflect.Method.invoke(Method.java:521)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at dalvik.system.NativeStart.main(Native Method)
01-05 19:39:01.034: E/AndroidRuntime(10557): Caused by: java.lang.NullPointerException
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.support.v4.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1576)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1617)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:481)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.Activity.performSaveInstanceState(Activity.java:1022)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1180)
01-05 19:39:01.034: E/AndroidRuntime(10557):    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3144)
01-05 19:39:01.034: E/AndroidRuntime(10557):    ... 12 more
01-05 19:39:01.423: I/dalvikvm(10557): threadid=7: reacting to signal 3

ViewPager本身工作正常。看起来是暂停FragmentActivity时出现了错误,但我无法找出原因。我找到的唯一一件事是Google上的这篇文章:http://code.google.com/p/android/issues/detail?id=19917

我还通过Google的示例代码来确保我主要使用相同的技术......

编辑:为了确保ClickListener有效,我在onClick方法中尝试了一个Toast - 完美地运行。


1
刚刚弄明白了以下问题:FragmentActivity 在以下情况下会崩溃:
  • 按下主页按钮
  • 改变设备方向
  • 启动一个 Activity
  • 调用 finish 方法可以正常工作
所以目前对我来说,这个方法可行:重写 onSaveInstanceState 方法时不要调用 super 方法,这样就没问题了。除了实例状态没有保存之外:D,所以这并不是真正的解决方法,但可能可以确定问题所在。
- Juke
我刚遇到了同样的问题 - 对我来说,问题是当我添加一个空布局的新片段到我的项目时引起的。似乎在代码的某个地方,如果在onSaveInstanceState期间没有将任何内容保存到bundle中,它必须会导致空指针异常。将一些视图添加到我的布局中解决了这个问题(在花费数小时敲打头部之后)。我正在使用android support library v4 revision 10。 - Wayne Uroda
@Juke,使用片段是否可以获得与TabViewActivity相同的行为?我想在选择任何选项卡时在片段区域打开一个新的活动。 - CoDe
@Juke,我能够在片段视图中的按钮点击时绘制一个新的片段(而不是活动),但如果我再次打开同一片段,则每次都会调用Fragment.onCreateView...我不想每次加载视图..有什么想法/建议吗? - CoDe
4个回答

62

快速解决方法是

class MyFragment extends Fragment {

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        setUserVisibleHint(true);
    }

等待在安全模式下进行更新 :)


3
顺便提一句,问题已经在2012年3月的支持程序更新中修复了。 - Vadim
1
截至2012年4月23日,我仍然遇到了这个问题...但是这个答案完美地解决了它。 - Steven Elliott
1
哎呀,这个修复在少数情况下仍然有效!我已经回来并继续使用它。 - Vadim
1
@BryanDenny 哇,这个修复了令人烦恼的ABS的FragmentTabPager示例!只需要将其放置在所有SherlockFragment声明中即可 :) - Yman
我曾经遇到过同样的问题。现在完美解决了。必须将这段代码放入所有片段中。谢谢 :) - kushdilip
显示剩余3条评论

48

这是一个众所周知的问题,我很惊讶地发现没有人在公共错误跟踪器上报告此问题。

你的问题源于以下原因:

if (f.mSavedViewState != null) {
    if (result == null) {
        result = new Bundle();
    }
    result.putSparseParcelableArray(
            FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
}
if (!f.mUserVisibleHint) {
    // Only add this if it's not the default value
    result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
}

你需要注意,如果在进入这段代码之前resultnull,并且第一个if没有被触发,那么如果我们进入第二个if,你将会获得一个NullPointerException异常。

应该修改为:

if (f.mSavedViewState != null) {
    if (result == null) {
        result = new Bundle();
    }
    result.putSparseParcelableArray(
            FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
}
if (!f.mUserVisibleHint) {
    if (result == null) {
        result = new Bundle();
    }
    // Only add this if it's not the default value
    result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
}

我一两个星期之前提交了一个补丁:https://android-review.googlesource.com/31261


你能发布重新编译的步骤吗? - Pankaj Kumar
3
很简单,只需在Eclipse上创建一个新的Java项目,使用 SDK/extras/android/support/v4/src/ 的源代码,将API 14的SDK库 SDK/platforms/android-14/android.jar 添加到项目库中,并将其导出为JAR文件。完成! :) - Mokkun
13
我已经按照Jake上面概述的更改,编译了一个新的支持库,可在Google发布修复程序之前使用。http://ara.sh/android/android-support-v4.jar - Arash Payan
@ArashPayan 非常感谢你,伙计。没有这个jar文件,我就陷入了困境。现在我通过使用这个jar文件解决了我的问题。你为我做了一件了不起的事情。谢谢! - RajeshVijayakumar
如果我使用 [android-support-v13.jar] 而不是 [android-support-v4.jar],这会有帮助吗? - RRTW
显示剩余5条评论

32
根据这里的帖子,你应该使用FragmentStatePagerAdapter而不是FragmentPagerAdapter。我曾经遇到过你描述的同样问题,转换后问题得以解决。

2
问题对我也解决了。 - starkej2
一样啊。你知道为什么吗? - user1545072
3
即使使用v13支持库的FragmentStatePagerAdapter,仍然存在这个问题。 - gardarh
我使用v13支持库时遇到了类似的问题。我正在使用FragmentStatePagerAdapter,一切都很好,直到我尝试添加第三个页面,添加另一个页面导致应用程序崩溃,并在问题上方出现NPE。具有讽刺意味的是,解决方法是切换回FragmentPagerAdapter - pkk

4

我刚遇到了同样的问题 - 这个问题是由于我在我的项目中添加了一个空布局的新片段时引起的。似乎如果在Fragment.onSaveInstanceState(Bundle outState)期间没有保存任何内容到bundle中,它会导致FragmentManagerImpl.saveFragmentBasicState中发生空指针异常。

将一些视图添加到我的布局中即可解决此问题(在花费数小时撞头后)。使用android support库v4修订版10。

此外,当将ListView用作片段布局的根元素时,如果listview中没有元素,则也会崩溃。在片段类中像这样覆盖onSaveInstanceState以将一些数据放入bundle中:

@Override
public void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    outState.putString("DO NOT CRASH", "OK");
}

你和Vadim的解决方案都对我有用。我正在使用support v13 FragmentStatePageAdapter。虽然你的更有趣。 - Joey Harwood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接