活动生命周期vs View生命周期:如何避免NPE?

时间:2012-05-07 00:09:08

标签: android sqlite android-activity nullpointerexception lifecycle

我有一个ListView来调整游标中的数据。光标的记录位置通过View.setTag()存储,因此可以检索它以响应用户事件。

public class OrderActivity extends Activity {

  private ListView list;
  private CursorAdapter adapter;
  private SQLiteDatabase database;

  public void onResume() {
    database = new Database(this).getWriteableDatabase();
    // query the db and store the position of the cursor
    // as the tag while binding the view in the overridden
    // CursorAdapter.bindView()
    adapter = new SimpleCursorAdapter( /* code here */ );
    list.setAdapter(adapter);
  }

  public void onPause() {
    adapter.changeCursor(null);
    database.close();
  }

  private void onClickRowDumpOrder(View row) {
    int newPosition = (Integer) row.getTag();
    Cursor cursor = adapter.getCursor();
    int originalPosition = cursor.getPosition(); // Throws NPE

    cursor.moveToPosition(newPosition);
    Log.v("tag", "Description: " + cursor.getString(0));

    // restore the cursor
    cursor.moveToPosition(originalPosition);
  }

}

我将数据库和适配器存储在Activity的实例字段中,以在活动生命周期中分配/释放资源:我创建了一个新数据库onResume并关闭它onPause。不幸的是,我收到了很多来自我的用户的报告,NPE被引入上面伪代码中列出的行中,但是我无法重现它。

似乎cursornull,但我想知道如果只能在onClickRowDumpOrder之后调用方法onResume,这是可能的,因为它是对android:onClick的回调。 click事件(我通过cursor

在XML布局中设置了这个)

我做错了吗? API错误导致android:onClick为空?是否有一些文档描述了游标和适配器如何适应活动生命周期?

更新

我在我的XML文件中删除了SimpleCursorAdapter.bindView并在onPause内手动设置了侦听器。为了避免泄漏,我在自定义AbsListView.RecycleListener和我的活动android:onClick中删除了侦听器(我使用reclaimViews(List<View>)检索所有视图)。这似乎解决了这个问题,这是我的解释。

  1. 当我的活动首次启动时,新视图会膨胀
  2. 我的活动的实例#1在View的构造函数中被设置为该特定View的OnClickListener,当它解析AbsListView.RecycleBin属性时
  3. instance#1离开前台,因此onPause()将游标设置为null。请注意,此活动仍然是View的侦听器,因为视图和活动都没有标记为垃圾回收。这意味着它们在Android类的某些缓存中被引用
  4. 我的活动的实例#2已创建,其列表视图稍微回收已创建的视图。数据显示正确,但此视图仍然具有旧活动(使用空游标)作为侦听器
  5. 当用户单击我的视图时,将调用实例#1处理程序,但它具有空游标。这导致了NPE
  6. 这种解释是现实的,但我没有在Android类中找到相关代码({{1}}中有一个缓存,但ListView本身不会被重用)。此外,我从来没有能够重现这个错误,我只觉得我的修复工作,因为在过去的两天里我没有收到任何报告(通常我每天都会得到几十个)

    您是否了解Android堆栈中可以验证我的假设的任何代码?

2 个答案:

答案 0 :(得分:0)

你可以试试这种方式。 if (cursor.moveToFirst()) { for (int i = 0; i < cursor.getCount(); i++) { cursor.moveToPosition(i); } }

答案 1 :(得分:0)

我认为你是在Activity生命周期的错误点创建游标。 ListView sample onCreate()onResume()中的内容放入{{1}}。我不知道它一定是有害的,但你可能正在重新创造一些不需要的东西。