6

我正在整合數據庫以保存和存儲稍後要檢索的基本聯繫人信息。然而,我的應用程序現在已經崩潰的啓動,所以如果是正在創建的表我甚至無法驗證等Android應用程序在啓動時崩潰:ContactsFragment中的SQLite NullPointerException

05-05 16:39:50.671 11631-11631/treehouse.greenlight E/AndroidRuntime﹕ FATAL EXCEPTION: main 
Process: treehouse.greenlight, PID: 11631 
java.lang.RuntimeException: Unable to start activity ComponentInfo{treehouse.greenlight/treehouse.greenlight.Home_screen}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2205) 
     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2255) 
     at android.app.ActivityThread.access$800(ActivityThread.java:142) 
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1203) 
     at android.os.Handler.dispatchMessage(Handler.java:102) 
     at android.os.Looper.loop(Looper.java:136) 
     at android.app.ActivityThread.main(ActivityThread.java:5118) 
     at java.lang.reflect.Method.invoke(Native Method) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:610) 
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference 
     at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224) 
     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) 
     at treehouse.greenlight.MyDBHandler.addContact(MyDBHandler.java:54) 
     at treehouse.greenlight.ContactsFragment.onCreateLoader(ContactsFragment.java:117) 
     at android.support.v4.app.LoaderManagerImpl.createLoader(LoaderManager.java:490) 
     at android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:499) 
     at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:553) 
     at treehouse.greenlight.ContactsFragment.onActivityCreated(ContactsFragment.java:71) 
     at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1797) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:979) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1138) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1120) 
     at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1929) 
     at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:547) 
     at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171) 
     at android.app.Activity.performStart(Activity.java:5285) 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2178)  

數據庫處理程序代碼:

public class MyDBHandler extends SQLiteOpenHelper { 

private static final int DATABASE_VERSION = 1; 
private static final String DATABASE_NAME = "contactsDB.db"; 
public static final String TABLE_CONTACTS = "contacts"; 
private Context context; 
public static final String COLUMN_ID = "_id"; 
public static final String COLUMN_NAME = "name"; 
public static final String COLUMN_PHONE = "phone"; 
public static final String COLUMN_STATUS = "status"; 
public static final String COLUMN_BLURB = "blurb"; 

public MyDBHandler(Context context, String name, 
        CursorFactory factory, int version) { 
    super(context, DATABASE_NAME, factory, DATABASE_VERSION); 

} 
    @Override 
    public void onCreate (SQLiteDatabase db){ 

     String CREATE_CONTACTS_TABLE = "CREATE TABLE " + 
       TABLE_CONTACTS + "(" 
       + COLUMN_ID + " INTEGER PRIMARY KEY," + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME 
       + " TEXT," + ContactsContract.CommonDataKinds.Phone.NUMBER + " INTEGER," 
       + COLUMN_STATUS + " TEXT," + COLUMN_BLURB 
       + " TEXT" + ");"; 
     db.execSQL(CREATE_CONTACTS_TABLE); 
    } 

    @Override 
    public void onUpgrade (SQLiteDatabase db,int oldVersion, int newVersion){ 
     db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS); 
     onCreate(db); 
    } 
public void addContact(ContactsDb contacts) { 

    ContentValues values = new ContentValues(); 
    values.put(COLUMN_NAME, contacts.getName()); 
    values.put(COLUMN_PHONE, contacts.getPhone()); 
    values.put(COLUMN_STATUS, contacts.getStatus()); 
    values.put(COLUMN_BLURB, contacts.getBlurb()); 

    SQLiteDatabase db = this.getWritableDatabase(); 
    db.insert(TABLE_CONTACTS, null, values); 

} 

的logcat的說,這個問題是SQLiteDatabase db = this.getWriteableDatabase();

這裏是我的問題的Contactsfragment還有:

public class ContactsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 

private CursorAdapter mAdapter; 
private Context context; 
TextView idView; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // create adapter once 
    Context context = getActivity(); 
    int layout = R.layout.activity_list_item_1; 
    Cursor c = null; // there is no cursor yet 
    int flags = 0; // no auto-requery! Loader requeries. 
    mAdapter = new SimpleCursorAdapter(context, layout, c, FROM, TO, flags); 

} 


public void ContactsDb(Context context) { 
    this.context=context; 


} 
Uri contentUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; 

String[] PROJECTION = { 
     ContactsContract.Contacts.HAS_PHONE_NUMBER, 
     ContactsContract.Contacts._ID, // _ID is always required 
     ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY, // that is what we want to display 
     Contacts.TIMES_CONTACTED, 
     ContactsContract.CommonDataKinds.Phone.NUMBER 

}; 

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 

    // each time we are started use our listadapter 
    setListAdapter(mAdapter); 
    // and tell loader manager to start loading 
    getLoaderManager().initLoader(0, null, this); 
} 

// columns requested from the database 


// and name should be displayed in the text1 textview in item layout 

public String[] has_phone = {ContactsContract.Contacts.HAS_PHONE_NUMBER}; 

    String phone = "0"; 
    int dbPhone = 0; 
private final String[] FROM = {Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.CommonDataKinds.Phone.NUMBER}; 
private final int[] TO = {android.R.id.text1, dbPhone}; 


public void newContact (View view) { 
    MyDBHandler dbHandler = new MyDBHandler(context, null, null, 1); 

    String name = Contacts.DISPLAY_NAME_PRIMARY; 
    int dbPhone = 
    Integer.parseInt(phone); 

    String status =""; 
    String blurb =""; 
    ContactsDb contacts = 
      new ContactsDb(name, dbPhone, status, blurb); 
    dbHandler.addContact(contacts); 

} 


@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    Context context = this.context; 
    // load from the "Contacts table" 
    MyDBHandler dbHandler = new MyDBHandler(context, null, null, 1); 

    String name = Contacts.DISPLAY_NAME_PRIMARY; 
    int phone = Integer.parseInt(ContactsContract.CommonDataKinds.Phone.NUMBER); 

    String status ="Busy"; 
    String blurb ="N/A"; 

    ContactsDb contacts = 
      new ContactsDb(name, dbPhone, status, blurb); 
    dbHandler.addContact(contacts); 

    // no sub-selection, no sort order, simply every row 
    // projection says we want just the _id and the name column 
    return new CursorLoader(getActivity(), 
      contentUri, 
      PROJECTION, 
      ContactsContract.Contacts.HAS_PHONE_NUMBER + " =? AND " + Contacts.TIMES_CONTACTED + ">=?", // This is selection string, we're looking for records that HAS_PHONE_NUMBER is 1 
      new String[]{"1", "0"}, // 1 means that contact has a phone number & 60 is the amount of times contacted (arbitrary - needs to be fixed before release) 
      null); 
} 

最後,這裏是ContactsDB類:

public ContactsDb(String name, int phone, String status, String blurb) { 

    this._name = name; 
    this._phone = phone; 
    this._status = status; 
    this._blurb = blurb; 

任何幫助非常感謝 - 我認爲問題在於我的上下文爲空。我只是不確定爲什麼它會在我的MyDBHandler文件中定義它。截至目前,我無法驗證數據庫是否正在創建,更不用說保存數據了。

乾杯&感謝您的時間和耐心, 希亞姆

回答

12

它看起來就像你偶然形成的,而不是使用實例變量context局部變量。

因此,context在您將其傳遞到MyDBHandler時爲空,因此當您撥打this.getWritableDatabase()時,this爲空。其結果是,你得到的NullPointerException,你可以在日誌中看到:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference 
     at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224) 
     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) 

嘗試將所有相關的代碼onActivityCreated()Context,以確保你有一個有效的Context,並確保使用的實例變量而不是本地變量:

public class ContactsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 

private CursorAdapter mAdapter; 
private Context context; //this is the Context you will use 
TextView idView; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // create adapter once 
    //Context context = getActivity(); //Here was the problem 
    //int layout = R.layout.activity_list_item_1; 
    //Cursor c = null; // there is no cursor yet 
    //int flags = 0; // no auto-requery! Loader requeries. 
    //mAdapter = new SimpleCursorAdapter(context, layout, c, FROM, TO, flags); 

} 


public void ContactsDb(Context context) { 
    this.context=context; 


} 
Uri contentUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; 

String[] PROJECTION = { 
     ContactsContract.Contacts.HAS_PHONE_NUMBER, 
     ContactsContract.Contacts._ID, // _ID is always required 
     ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY, // that is what we want to display 
     Contacts.TIMES_CONTACTED, 
     ContactsContract.CommonDataKinds.Phone.NUMBER 

}; 

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 

    //Add this here: 
    context = getActivity(); //use the instance variable 
    int layout = R.layout.activity_list_item_1; 
    Cursor c = null; // there is no cursor yet 
    int flags = 0; // no auto-requery! Loader requeries. 
    mAdapter = new SimpleCursorAdapter(context, layout, c, FROM, TO, flags); 

    // each time we are started use our listadapter 
    setListAdapter(mAdapter); 
    // and tell loader manager to start loading 
    getLoaderManager().initLoader(0, null, this); 
} 

// columns requested from the database 


// and name should be displayed in the text1 textview in item layout 

public String[] has_phone = {ContactsContract.Contacts.HAS_PHONE_NUMBER}; 

    String phone = "0"; 
    int dbPhone = 0; 
private final String[] FROM = {Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.CommonDataKinds.Phone.NUMBER}; 
private final int[] TO = {android.R.id.text1, dbPhone}; 


public void newContact (View view) { 

    //context should now be valid: 
    MyDBHandler dbHandler = new MyDBHandler(context, null, null, 1); 

    String name = Contacts.DISPLAY_NAME_PRIMARY; 
    int dbPhone = 
    Integer.parseInt(phone); 

    String status =""; 
    String blurb =""; 
    ContactsDb contacts = 
      new ContactsDb(name, dbPhone, status, blurb); 
    dbHandler.addContact(contacts); 

} 
//........ 
+0

嗨丹尼爾,感謝您的及時迴應。我已經做了這個更正,但logcat仍然給我同樣的錯誤。這對我來說很有意義,但是我從來沒有打電話給ContactsFragment(它讓我知道,說這樣的名字的方法很容易被混淆爲構造函數)。也許我保留我的ContactsDb,並創建一個不同的ContactsFragment構造函數? – Shyam

+0

哇,丹尼爾,很棒......這就是問題所在。從現在開始,我會將它鎖定在我的頭骨上,這個頭骨應該在onCreate中聲明!乾杯 – Shyam