首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android SQLite 查詢 `NullPointerException`?`moveToFirst()` vs `moveToNext()` 陷阱

     編輯:程式設計綜合問答

錯誤代碼

public User search(String username) {
    SQLiteDatabase sdb = helper.getReadableDatabase();
    Cursor cursor = sdb.rawQuery("SELECT * FROM user WHERE username=?", new String[]{username});

    if (cursor.moveToFirst()) {  // 若無資料返回 false,不會拋 NPE
        String uname = cursor.getString(cursor.getColumnIndex("username"));
        String pword = cursor.getString(cursor.getColumnIndex("password"));
        User user = new User(uname, pword);
        cursor.close();
        return user;
    }
    cursor.close();
    return null;
}

原提問者遇到 NPE,且建議改用 `cursor.next()`,**這是錯誤建議**。

真正的 NPE 來源

  1. `helper` 為 `null`:`SQLiteOpenHelper` 實例未初始化(忘記 `new MyHelper(context)`)。
  2. `getReadableDatabase()` 拋出 `SQLiteException`:資料庫檔案損壞、磁碟滿、權限不足,導致返回 `null` 或直接崩潰。
  3. `cursor.getColumnIndex()` 返回 `-1`:欄位名拼寫錯誤(如 `password` vs `passwd`),後續 `getString(-1)` 拋 `IllegalStateException`,非 NPE。
  4. `User` 建構子內部 NPE:參數為 `null` 時邏輯錯誤。

正確健寫模板

public User search(String username) {
    if (helper == null) {
        Log.e(TAG, "DBHelper not initialized");
        return null;
    }
    SQLiteDatabase db = null;
    Cursor cursor = null;
    try {
        db = helper.getReadableDatabase();
        cursor = db.rawQuery("SELECT username, password FROM user WHERE username=?", new String[]{username});
        if (cursor != null && cursor.moveToFirst()) {
            // 使用 getColumnIndexOrThrow 確保欄位存在
            String uname = cursor.getString(cursor.getColumnIndexOrThrow("username"));
            String pword = cursor.getString(cursor.getColumnIndexOrThrow("password"));
            return new User(uname, pword);
        }
        return null;
    } catch (SQLiteException e) {
        Log.e(TAG, "Query failed", e);
        return null;
    } finally {
        if (cursor != null) cursor.close();
        // db 由 helper 管理,不需手動 close
    }
}

關鍵點總結

  • `moveToFirst()` 是標準首行判斷,**不需改 `moveToNext()`**(後者用於迴圈遍歷)。
  • 永遠 `try-finally` 關閉 `Cursor`,防洩漏導致 `CursorFinalized` 錯誤。
  • 用 `getColumnIndexOrThrow` 讓欄位缺失快速暴露,而非返回 `-1` 造成後續難查錯誤。
  • 生產環境建議改用 Room / Flow / Coroutines,編譯期驗證 SQL、自動映射實體、生命週期感知。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved