2014-01-12 2 views
0

Я возникли проблемы с обновления списка контактов для фрагмента, каждый раз, когда я пытаюсь вызвать метод, который обновляет список, я получаю эту ошибку:Android NullPointerException базы данных при открытии второй раз

01-11 17:55:48.215: W/System.err(25594): java.lang.NullPointerException 
01-11 17:55:48.235: W/System.err(25594):at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224) 
01-11 17:55:48.235: W/System.err(25594): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) 
01-11 17:55:48.235: W/System.err(25594): at com.Spit.pocketbook.DatabaseManager.<init>(DatabaseManager.java:48) 
01-11 17:55:48.235: W/System.err(25594): at com.Spit.pocketbook.ContactsFragment.getContacts(ContactsFragment.java:105) 
01-11 17:55:48.235: W/System.err(25594): at com.Spit.pocketbook.ContactsFragment.fillContactList(ContactsFragment.java:70) 
01-11 17:55:48.235: W/System.err(25594): at com.Spit.pocketbook.SwipeActivity.onFinishCreateContactDailog(SwipeActivity.java:96) 
01-11 17:55:48.235: W/System.err(25594): at com.Spit.pocketbook.ContactCreateDialog$PositiveButtonListener.onClick(ContactCreateDialog.java:129) 
01-11 17:55:48.235: W/System.err(25594): at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167) 
01-11 17:55:48.235: W/System.err(25594): at android.os.Handler.dispatchMessage(Handler.java:99) 
01-11 17:55:48.235: W/System.err(25594): at android.os.Looper.loop(Looper.java:137) 
01-11 17:55:48.235: W/System.err(25594): at android.app.ActivityThread.main(ActivityThread.java:4950) 
01-11 17:55:48.235: W/System.err(25594): at java.lang.reflect.Method.invokeNative(Native Method) 
01-11 17:55:48.235: W/System.err(25594): at java.lang.reflect.Method.invoke(Method.java:511) 
01-11 17:55:48.245: W/System.err(25594): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1004) 
01-11 17:55:48.245: W/System.err(25594): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:771) 
01-11 17:55:48.245: W/System.err(25594): at dalvik.system.NativeStart.main(Native Method) 

Так что это происходит каждый раз, когда я использую этот метод в ContactsFragment

private ArrayList<Contact> getContacts(){ 
    DatabaseManager manager = new DatabaseManager(getActivity()); 
    ArrayList<Contact> contacts = manager.getAllContacts(); 
    manager.close(); 
    return contacts; 
} 

, который создает новый DatabaseManager (который я создал, чтобы сделать тяжелую работу в базе данных и завернуть хорошо для остальной части моего приложения):

public DatabaseManager(Context context){ 
    mDatabase = new DatabaseHelper(context).getWritableDatabase(); //Line 48 (where the error occurs) 
    mContext = context; 
} 

и вот getAllContacts() метод для справки:

public ArrayList<Contact> getAllContacts(){ 
    ArrayList<Contact> contacts = new ArrayList<Contact>(); 

    //query the database for all the contacts 
    String[] projections = {ContactsTable._ID, 
      ContactsTable.COLUMN_CONTACT_NAME, 
      ContactsTable.COLUMN_LOOKUP_KEY, 
      ContactsTable.COLUMN_SCOPE}; 
    Cursor C = mDatabase.query(ContactsTable.CONTACTS_TABLE_NAME, projections, 
      null, null, null, null, ContactsTable.COLUMN_CONTACT_NAME + " ASC"); 

    //iterates through the results and adds them all to the ArrayList 
    while(C.moveToNext()){ 
     Contact contact = new Contact(C.getLong(0), 
       C.getString(1), 
       C.getString(2), 
       C.getString(3)); 
     contacts.add(contact); 
     } 

    C.close(); //save from memory leakage 

    return contacts; 
} 

Примечание: «M» переменные представляют собой глобальные переменные

Самое интересное во всем этом является то, что этот же метод (.getContacts()) вызывается при создании ContactFragment, который также вызывает конструктор DatabaseManager таким же образом. В дополнение ко всему этому, когда мое приложение загружается, открывается ViewPager (SwipeActivity для тех, кто действительно читает журнал), который загружает два фрагмента. The ContactsFragment и другой SummaryFragment, который использует очень похожий метод для .getContact(), чтобы получить другую информацию в базе данных. Но мое приложение работает хорошо, когда оно загружается. Вся информация извлекается и отображается очень хорошо. Только когда я попробую обновить его во второй раз в любом фрагменте жизни, я получу и ошибку. Единственное исправление, которое я нашел, это повернуть экран, который в основном сбрасывает и воссоздает все в Activity.

Поскольку он работает, когда я поворачиваю экран, это заставляет меня думать, что это проблема с тем фактом, что, хотя я закрыл базу данных, она как-то остается открытой, и именно поэтому я не могу назвать ее второй раз обновите списки. Если это проблема, я не знаю, как ее исправить, и буду признателен за любую помощь в этом, но опять же, это может не иметь никакого отношения к этому.

Вот мой призыв к manager.close() который закрывает базу данных, что DatabaseManager открывается при его создании:

public void close(){ 
    mDatabase.close(); 
} 

Я боролся с этой проблемой в течение нескольких дней в настоящее время. Я провел некоторое исследование (без видимых результатов) и посмотрел на различные способы получения ошибки, но каждое исправление, которое я придумал, не имеет никакого эффекта.

Спасибо впереди времени для любого и всех советов/помощи,

Давид (Кос)

+3

Извините, что знаю, что это не ответ, но я бы настоятельно рекомендовал рефакторинг этого кода. Вы делаете запрос базы данных в потоке пользовательского интерфейса, который является большим нет-нет. Вы также передаете активность, которая просто требует утечки памяти. Вы должны использовать API-интерфейсы Loader (мне нравится, когда мои Fragments реализуют интерфейс LoaderManager.LoaderCallbacks), который заботится не только о проблемах с потоками, но также позволяет открывать/закрывать базу данных, которая решит исключение, которое у вас есть. –

+0

@Sam_D Я попробую поблагодарить вас. – Spit

ответ

2

Вы получаете NullPointerException, потому что вы закрыли базу данных, а затем последующие попытки вновь открыть его не удается по какой-то причине.

Ваш лучший выбор - не закрывать базу данных. Как говорит инженер Google FW Диана Хакборн, вам не нужно закрывать базу данных вручную, так как она будет автоматически закрыта после завершения процесса. Обратитесь к link #1 и link #2 за несколько сообщений, где она комментирует это.

Кроме того, если вы идете по этому пути, вы должны сделать однотонный DatabaseHelper, так что у вас будет только один экземпляр БД в любой момент времени (в противном случае это тоже станет проблематичным).Реализовать что-то подобное для ContentProvider и вложенных DatabaseHelper класса:

public class CustomDbProvider extends ContentProvider { 
    private static final String DATABASE_NAME = "myapp.db"; 
    private static final int DATABASE_VERSION = 4; 

    static class DatabaseHelper extends SQLiteOpenHelper { 
     private static final String TAG = "DBHelper"; 
     private static DatabaseHelper sInstance = null; 

     private DatabaseHelper(Context context) { 

      // calls the super constructor, requesting the default cursor factory. 
      super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     } 

     public static DatabaseHelper getInstance(Context c) { 
      if (sInstance == null) sInstance = new DatabaseHelper(c); 
      return sInstance; 
     } 

и тогда ваш Fragment вы можете сделать следующее (см также dev page here) (Кроме того, этот код предполагает, что вы используете некоторый тип List и Adapter но если нет, то как только вы получите в onLoadFinished()Cursor вы можете делать все, что вам нужно с ним):

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

    ... 

    @Override 
    public void onCreate(Bundle savedInstance) { 
     ... 
     // initialize the loader with the loader callbacks 
     getLoaderManager().initLoader(SOME_UNIQUE_ID, getArguments(), this); 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int arg0, Bundle fragargs) { 
     Uri uri = /* FIXME some URL defined in your Provider */; 
     String selection = null; 
     String[] selectionArgs = null; 
     return new CursorLoader(mContext, uri, projection, selection, selectionArgs, ContactsTable.COLUMN_CONTACT_NAME + " ASC"); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor c) { 
     mAdapter.swapCursor(c); 
    } 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     mAdapter.swapCursor(null); 
    } 
+0

Хм, это то, о чем я думал, тоже была проблемой. Но я думаю, что стоит попробовать, что @Sam_D сказал в комментарии к моему сообщению, потому что я чувствую, что знание послужит мне хорошо в будущем. Спасибо за ваше предложение, но это очень ценится. – Spit

+1

Возможно, вам могут понадобиться оба. Обычно я использую DbHelper Singleton в своем ContentProvider, а затем использую CursorLoader для запроса необходимого курсора. – anddev84

+0

Теперь, когда вы упомянули об этом, я предполагаю, что я собираюсь реализовать аналогичный метод. У вас будет некоторый пример кода, который поможет мне начать с этого, так как я явно не использовал этот метод раньше. В частности, у меня возникли проблемы с пониманием того, как установить наблюдателя загрузчика на данные (мои данные поступают из личной базы данных, и все, что я видел при поиске, это база данных Android). – Spit

0

StackTrace говорит, что контекст, переданное в SQLiteOpenHelper является null.

Поскольку фрагмент getActivity() вы передаете как контекст, фрагмент не был присоединен к активности на момент создания вашего менеджера баз данных.

+0

Я заглянул в это, и я не думаю, что это проблема, потому что, когда я пытаюсь сделать работу, это решит проблему (скорее, как тест, чтобы увидеть, если это была проблема), не было никакого влияния. – Spit

Смежные вопросы