2012-11-26 4 views
4

Android에서 SQLite 테이블에 값을 삽입 할 때 NullPointerException이 발생하며 그 이유를 알 수 없습니다. 나는 ContentValues와 null에 대한 데이터베이스 인스턴스를 테스트하고있다. SQLite에 데이터를 삽입 할 때 간헐 NPE

는 삽입 코드 :

public void insertOrIgnore(ContentValues values) { 

    SQLiteDatabase db = this.dbHelper.getWritableDatabase(); 

    try { 

     //I added these null value checks to stop NPE, but doesn't help. 
     if (values != null && db != null) { 
      db.insertWithOnConflict(TABLE, null, values, SQLiteDatabase.CONFLICT_IGNORE); 
     } 
    } catch (SQLiteException e) { 

    } finally { 
     if (db != null) { 
      db.close(); 
     } 
    } 
} 

public static final String TABLE = "albums"; 

이 코드는 예상대로 데이터베이스에 추가 데이터와 함께 작동 대부분의 시간. 그러나 때로는 이며, 드물게은 아래 오류를 생성합니다. 스택 추적은 ACRA에서 왔으며이 오류가 발생하는 조건에서 격리 할 수 ​​없었습니다. 왜 이런 일이 일어나고 조건이 무엇인지에 대한 포인터를 찾고 있습니다. 내 SQLite에 대한 지식은 초보자 수준입니다.

java.lang.NullPointerException 
    at android.database.sqlite.SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290) 
    at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96) 
    at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:2025) 
    at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1965) 
    at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:690) 
    at android.database.sqlite.SQLiteDatabase.beginTransactionNonExclusive(SQLiteDatabase.java:605) 
    at android.database.sqlite.SQLiteStatement.acquireAndLock(SQLiteStatement.java:247) 
    at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:112) 
    at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1844) 
    at com.mydomain.myapp.albums.AlbumsData.insertOrIgnore(AlbumsData.java:89) 

줄 89는 위에 표시된 db.insertWithOnConflict (...) 호출입니다.

필자는 완전한 코드로 답변을 찾는 것이 아니라 오히려 포인터가 잘못되어 무엇이 잘못되었는지에 대한 설명을 찾고 있기 때문에 직접 고칠 수 있습니다.

편집 :

setNativeHandle(mDatabase.mNativeHandle); 

은 그래서 데이터베이스 인스턴스가 null 보인다 스택 추적은 NPE가 SQLiteStatement (V 4.03)의 라인 (290)에서 유래 보여줍니다. 트랜잭션이 시작될 때 null을 테스트했을 때 트랜잭션 중에 null이 될 수 있습니까?

+0

"TABLE"이란 무엇입니까? – Simon

+0

TABLE가 null인지 확인 했습니까? –

+0

또한 89 행은 무엇입니까? –

답변

2

여기 SQLiteDatabase close() function causing NullPointerException when multiple threads

언급 한 바와 같이 버그에 대한 이유는 당신이 어떤 점에서 데이터베이스를 닫는 것이 될 수있다. 아마도 동시에 실패한 작업이 완료되지 않은 동안 동시에 진행됩니다.

나는 스택 트레이스 조금 따랐다이 대략 무슨이다 : 다음의 값과 함께 그 래핑 당신은 결과 SQL 문자열 ("INSERT OR IGNORE INTO...")를 구축 insertWithOnConflict를 호출

  • AlbumsData.insertOrIgnore(AlbumsData.java:89)
    당신의 을 SQLiteStatement으로 변환합니다.
  • SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1844)은 - 결과 문은 지금
  • SQLiteStatement.executeInsert(SQLiteStatement.java:112) 실행하는 것입니다 - 실제 삽입이 발생하기 전에, 데이터베이스는 잠금을 획득 할 필요가있다.
  • SQLiteStatement.acquireAndLock(SQLiteStatement.java:247) - 일부 검사는 여기에서 발생합니다. 데이터베이스 객체는 해당 시점에서 null이 아닌 한 확인할 수 있습니다. 코드는 트랜잭션을 시작해야한다고 결정합니다. 데이터베이스 객체 자체는 그 시점에서 잠겨 있지 않은 것을 볼 수 있습니다.
  • SQLiteDatabase.beginTransactionNonExclusive(SQLiteDatabase.java:605) - 단지
  • SQLiteDatabase.beginTransaction(SQLiteDatabase.java:690)
  • 전달 - 단지 앞으로
  • SQLiteDatabase.executeSql(SQLiteDatabase.java:2025) - - 일부 검사 (데이터베이스가 여기에 존재하는 경우 확실하지) 후에는 execSQL("BEGIN IMMEDIATE;")
  • SQLiteDatabase.execSQL(SQLiteDatabase.java:1965) 실행하려고합니다 "BEGIN IMMEDIATE;에서 다른 SQLiteStatement을 구축합니다. 이 하나가 지금 실행되어야합니다
  • SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96) - 데이터베이스 잠금 검사로 시작, 이건 괜찮아 보이는 데이터베이스는 여기에 null이 있어서는 안됩니다. 그런 다음 명령문이 실행되고 마지막으로 데이터베이스가 다시 잠금 해제됩니다.
  • SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290) - 데이터베이스가 null이므로 일부 항목을 정리하고 결국 NPE와 함께 실패합니다.

줄 번호가 일치하지 않아 해당 코드에 공급 업체가 수정/추가되었을 수 있습니다.

실제로 알 수 있듯이 실제로 제공된 데이터를 사용하기 전에 코드가 충돌합니다. 그것은하려고하고 있었다

BEGIN TRANSACTION IMMEDIATE; -- crash 
INSERT INTO table (...) VALUES (...); 
-- (end transaction) 

그것이 나의 의견으로는 프레임 워크 버그가된다. 내부적으로 처리되는 데이터베이스 객체는 어딘가에서 null이 될 수 없어야합니다. 특히 스택에서 더 이상 null이 아닌 것으로 보이는 경우가 있습니다.

다른 숨겨진 예외가이 문제의 근본 원인 일 수 있다고 생각합니다. 코드 내에 try { /* do stuff */ } finally { /* clean up */ } 블록이 많이 있고 부분이 예외를 throw하더라도 finally 부분이 실행됩니다. 이제 finally 블록은 또 다른 예외를 일으킬 수 있으며 결과는 원래 예외가 finally 블록의 새 예외로 대체된다는 AFAIK입니다. 데이터베이스가 실패 할 수있는 점, acquireAndLock 또는 try 부분에 어떤 코드를 폐쇄하고 다시 실패 할 releaseAndUnlock 원인 null에서 데이터베이스 개체를 떠날 수 있다면

는 특히 executeUpdateDelete()

try { 
    acquireAndLock(WRITE); 
    // actual statement execution 
} finally { 
    releaseAndUnlock(); 
} 

같다. 동일한 스택 추적을 얻어야합니다.

그 외에도 catch (SQLiteException e) { /* empty */ }과 같은 빈 캐치 블록을 사용하지 마십시오. 가능한 경우 ACRA로 기록하십시오/이미 그렇게하지 마십시오.

+0

그런 상세하고 철저한 대답에 감사드립니다. 그것은 링크를 요약 할뿐만 아니라 새로운 가치있는 관점을 제공합니다. 빈 catch 블록에 대한 조언을 주셔서 감사합니다. –

1

이 NPE는 Android source code이 LogCat에서받은 것과 다른 방법을 가리키며 맞춤 ROM에있는 것으로 보입니다. 이러한 경우에 예외는 다음과 같습니다. 이러한 예외의 비율이 매우 드문 경우 전화에서 실행되는 사용자 지정 ROM을 알기가 어렵고이 사용자 지정 ROM의 소스 코드를 알기가 더 어려워서 무시합니다. 문제가있는 곳.

맞춤 ROM을 사용하는 사용자가 많지 않으므로 다른 SDK가있는 다른 휴대 전화에서 앱을 광범위하게 테스트하고 얻을 수있는 예외 비율이 그다지 중요하지 않은 경우 무시할 수 있습니다. 그렇지 않으면 어둠 속에서 촬영을하고 NPE의 원인이되는이 맞춤 ROM에 무엇이있을 수 있는지 추측 할 수 있습니다 (개인적으로는 노력할 가치가 없다고 생각합니다).

+0

답변 해 주셔서 감사합니다. 앱에서 웹 사이트에서 앨범 데이터를 다운로드하는 동안 발생하는 오류는 내 휴대 전화에서 발생합니다. 휴대 전화가 Android 4.03을 실행 중입니다. 늘어나는만큼 스택 추적을 안드로이드 소스를 통해 추적 할 수 있습니다 - 내 질문에 편집 참조하십시오. –

+0

다른 클래스 (android.database.sqlite.SQLiteDatabase)의 경우 Logcat의 메소드를 가리키고 있지 않습니다 (예 :'SQLiteDatabase.java : 1965'는 LogCat에서'public boolean isReadOnly() {'을 가리 킵니다. 'execSQL'을 가리키고 있습니다 (다른 것들과 동일합니다). 사용자 정의 ROM에 휴대폰이 있습니까? –

+0

그것은 맞춤 ROM이 아니지만 SQLiteDataBase의 라인 번호에 대해 확실히 맞습니다. 클래스와 소스 코드 라인 번호 중 아무 것도 내 것과 일치하지 않습니다. 얼마나 호기심이십니까. 알아 차리면 +1 할 수 있습니다.이 라인 번호 퍼즐이 발생할 수있는 방법에 대한 관점은 높이 평가됩니다 (또는 아마도 자신의 SO 질문을 보증해야합니다 ...) –

관련 문제