【问题标题】:NPE: CursorTreeAdapter$MyCursorHelper.changeCursor(Cursor, boolean)' on a null object referenceNPE:空对象引用上的 CursorTreeAdapter$MyCursorHelper.changeCursor(Cursor, boolean)'
【发布时间】:2015-06-17 14:15:59
【问题描述】:

我正在尝试以不同的游标从我的数据库中获取数据,并将其显示在按组分隔的 ExpandableListView 中。像下面这样:

  • 历史任务(到光标 0)
    • 历史 1
    • 历史2
  • 地理任务(到光标 1)
    • 地理 1
    • 地理 2
  • 外语任务(到光标 2)
    • 外语1
    • 外语2

我正在尝试使用 CursorTreeAdapter 来执行此操作,因为我将所有信息都保存在数据库中,它可以管理不同的游标并在 ExpandableListView 中正确显示信息。

我遇到的问题是我在代码的某个位置遇到了 NPE,但我无法检查它在哪里调试,因为它直接更改为另一个文件而不知道发生了什么。

当我在 CursorTreeAdapter::setChildrenCursor 方法中调试时,就在我踏入时

childrenCursorHelper.changeCursor(childrenCursor, false);

它失败了,所以我不知道这个方法里面发生了什么。

我的代码如下:

MtMainActivity.java

public class MtMainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    @InjectView(R.id.elvTaskList)
    protected ExpandableListView elvTaskList;
    private TaskCursorAdapter taskListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        taskListAdapter = new TaskCursorAdapter(null, this);
        elvTaskList.setAdapter(taskListAdapter);
        getSupportLoaderManager().initLoader(MtLoaders.HISTORY_TASK, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
            case MtLoaders.HISTORY_TASK:
                return mtLoaderFactory.createHistoryTaskLoader();
        }
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case MtLoaders.HISTORY_TASK:
                taskListAdapter.setChildrenCursor(0, cursor);
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case MtLoaders.HISTORY_TASK:
                taskListAdapter.setChildrenCursor(0, null);
        }
    }

TaskCursorAdapter.java

public class TaskCursorAdapter extends CursorTreeAdapter {

    private final Cursor cursor;

    public TaskCursorAdapter(Cursor cursor, Context context) {
        super(cursor, context);
        this.cursor = cursor;
    }

    @Override
    protected Cursor getChildrenCursor(Cursor groupCursor) {
        return cursor;
    }

    @Override
    protected View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
        final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        final TextView tvHeader = (TextView) layoutInflater.inflate(R.layout.mt_task_item_subtitle, parent, false);
        tvHeader.setText(">> Insert name here <<");
        return tvHeader;
    }

    @Override
    protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { /* nothing */ }

    @Override
    protected View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
        final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        final View view = layoutInflater.inflate(R.layout.mt_task_item, parent, false);

        return view;
    }

    @Override
    protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { /* nothing */ }
}

错误

E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: project.android, PID: 11592
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.CursorTreeAdapter$MyCursorHelper.changeCursor(android.database.Cursor, boolean)' on a null object reference
        at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:164)
        at project.android.ui.MtMainActivity.onLoadFinished(MtMainActivity.java:215)
        at project.android.ui.MtMainActivity.onLoadFinished(MtMainActivity.java:29)
        at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427)
        at android.support.v4.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:395)
        at android.support.v4.content.Loader.deliverResult(Loader.java:104)
        at android.support.v4.content.AsyncTaskLoader.dispatchOnLoadComplete(AsyncTaskLoader.java:223)
        at android.support.v4.content.AsyncTaskLoader$LoadTask.onPostExecute(AsyncTaskLoader.java:61)
        at android.support.v4.content.ModernAsyncTask.finish(ModernAsyncTask.java:461)
        at android.support.v4.content.ModernAsyncTask.access$500(ModernAsyncTask.java:47)
        at android.support.v4.content.ModernAsyncTask$InternalHandler.handleMessage(ModernAsyncTask.java:474)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

PS:Cursor 工作正常,我用一个唯一的 ListView 检查过,这没有问题。

【问题讨论】:

    标签: java android android-cursorloader simplecursortreeadapter


    【解决方案1】:

    我的项目中有同样的问题。好像是CursorTreeAdapter有问题:

    at android.widget.CursorTreeAdapter.setChildrenCursor(CursorTreeAdapter.java:164)
    

    查看 Android 源代码中的那个方法:

        public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
    
        /*
         * Don't request a cursor from the subclass, instead we will be setting
         * the cursor ourselves.
         */
        MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition, false);
    
        /*
         * Don't release any cursor since we know exactly what data is changing
         * (this cursor, which is still valid).
         */
        childrenCursorHelper.changeCursor(childrenCursor, false);
    }
    

    在某些情况下 getChildrenCursorHelper 会将 null 返回到 childrenCursorHelper。我通常在配置更改(例如屏幕旋转)后得到它。

    为了解决这个问题,我将 CursorFilter + CursorTreeAdapter 源添加到我的项目中,然后将 null 检查添加到 childrenCursorHelper

        public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
    
        /*
         * Don't request a cursor from the subclass, instead we will be setting
         * the cursor ourselves.
         */
        MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition, false);
    
        /*
         * Don't release any cursor since we know exactly what data is changing
         * (this cursor, which is still valid).
         */
        if (childrenCursorHelper != null)
            childrenCursorHelper.changeCursor(childrenCursor, false);
    }
    

    最后从固定的 CursorTreeAdapter 扩展我的适配器。
    详情请见this commit

    【讨论】:

      【解决方案2】:

      您不需要将 CursorTreeAdapter 源添加到项目中。

      只需覆盖 setChildrenCursor:

      public class MyAdapter extends CursorTreeAdapter {
      
          @Override
          public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
              try {
                  Method getChildrenCursorHelper = CursorTreeAdapter.class.getDeclaredMethod("getChildrenCursorHelper", int.class, boolean.class);
                  getChildrenCursorHelper.setAccessible(true);
                  Object childrenCursorHelper = getChildrenCursorHelper.invoke(this, groupPosition, false);
                  if (childrenCursorHelper != null) {
                      Method changeCursor = childrenCursorHelper.getClass().getDeclaredMethod("changeCursor", Cursor.class, boolean.class);
                      changeCursor.setAccessible(true);
                      changeCursor.invoke(childrenCursorHelper, childrenCursor, false);
                  }
              } catch (IllegalAccessException e) {
              } catch (InvocationTargetException e) {
              } catch (NoSuchMethodException e) {
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-09-26
        • 2017-07-16
        • 1970-01-01
        • 1970-01-01
        • 2017-12-02
        • 1970-01-01
        • 1970-01-01
        • 2021-05-04
        • 2016-06-12
        相关资源
        最近更新 更多