Один Cursor Loader на несколько фрагментов.

SQLite, Preferences, файлы, SD, Content Provider, XML, JSON
endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 13 окт 2014, 21:23

Уважаемые знатоки! Вот такая вот проблема у меня. Мое приложение работает с базой данных, состоящей из нескольких таблиц. Данные из базы выводятся на разные фрагменты с ListView'ами одним CursorLoaderom. У меня возникает такая ошибка:
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT _id, name FROM company GROUP BY name
Как я понимаю, лоадер пытается получить уже несуществующий курсор. Но беда в том, что ошибка проявляется нерегулярно. Может вылететь почти сразу, а может и нет. Ошибка проявляется при переходе из одного фрагмента в другой. Вот реализация лоадера:

Код: Выделить всё

@Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bndl) {
        return new MainCursorLoader(this);
    }

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

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

    static class MainCursorLoader extends CursorLoader {

        public MainCursorLoader(Context context) {
            super(context);
        }

        @Override
        public Cursor loadInBackground() {
            try {
                switch (category) {
                    case 0:
                        return dbHelper.getAllCursor();

                    case 1:
                        return dbHelper.getCompanyList();

                    case 4:
                        return dbHelper.getFavorite();

                    default:
                        return null;
                }

            } catch (SQLException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
switch я использую для того, чтобы лоадер загружал нужные мне данные.
Вот код класса DBHelper:

Код: Выделить всё

// Класс для работы с БД
public class DBHelper extends SQLiteOpenHelper{

    private AtomicInteger mOpenCounter = new AtomicInteger();
    private static DBHelper mInstance;
    private SQLiteDatabase mDatabase;

    private static Context context;

    private DBHelper(Context _context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(_context, name, factory, version);
        context = _context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {

    }

    public static synchronized void initializeInstance (Context _context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        if (mInstance == null) {
            mInstance = new DBHelper(_context.getApplicationContext(), name, factory, version);
            context = _context;
        }
    }

    public static synchronized DBHelper getInstance() {
        if (mInstance == null) {
            throw new IllegalStateException(DBHelper.class.getSimpleName() +
                    " is not initialized, call initializeInstance(..) method first.");
        }

        return mInstance;
    }

    public synchronized SQLiteDatabase openDataBase() {
        if(mOpenCounter.incrementAndGet() == 1) {
            // Opening new database
            File DB_PATH = context.getExternalFilesDir(null);
            File dbFile = new File(DB_PATH, DataBase.DB_NAME);
            //if (!mDatabase.isOpen())
                mDatabase = context.openOrCreateDatabase(dbFile.getAbsolutePath(), SQLiteDatabase.OPEN_READWRITE, null);
        }
        return mDatabase;
    }

    public synchronized void closeDataBase() {
        if(mOpenCounter.decrementAndGet() == 0) {
            // Closing database
            mDatabase.close();
        }
    }

    public Cursor getAllCursor() throws SQLException {
        SQLiteDatabase database = DBHelper.getInstance().openDataBase();
        Cursor cursor = database.query(DataBase.ProductList.TABLE_NAME, new String[] { DataBase.ProductList.COLUMN_ID,
                DataBase.ProductList.COLUMN_RUS_NAME, DataBase.ProductList.COLUMN_ZIP },
                null, null, DataBase.ProductList.COLUMN_RUS_NAME, null, null);
        //DBHelper.getInstance().closeDataBase();
        return cursor;
    }

    public Cursor getFavorite() throws SQLException {
        SQLiteDatabase database = DBHelper.getInstance().openDataBase();
        Cursor cursor = database.query(DataBase.ProductList.TABLE_NAME, new String[] { DataBase.ProductList.COLUMN_ID,
                        DataBase.ProductList.COLUMN_RUS_NAME, DataBase.ProductList.COLUMN_ZIP },
                DataBase.ProductList.COLUMN_FAVORITE  + " = 1", null,
                DataBase.ProductList.COLUMN_RUS_NAME, null, null);
        //DBHelper.getInstance().closeDataBase();
        return cursor;
    }

    public Cursor getCompanyList() throws SQLException {
        SQLiteDatabase database = DBHelper.getInstance().openDataBase();
        Cursor cursor = database.query(DataBase.Company.TABLE_NAME, new String[] { DataBase.Company.COLUMN_ID, DataBase.Company.COLUMN_NAME },
                null, null, DataBase.Company.COLUMN_NAME, null, null);
        //DBHelper.getInstance().closeDataBase();
        return cursor;
    }
}
После запуска нового фрагмента я делаю ресет лоадера:

Код: Выделить всё

getLoaderManager().restartLoader(0, null, this);
Фрагменты я не замещаю, а добавляю новый и скрываю старый. Вот так:

Код: Выделить всё

fragmentManager.beginTransaction()
                        .add(R.id.content_frame, fragment).addToBackStack(null).commit();
                fragmentManager.beginTransaction()
                        .hide(thisFragment).commit();
thisFragment - это просто сохраненный текущий фрагменты
Потом, когда возвращаюсь назад, или вызываю другой фрагмент, делаю в методе onDestroy следующее:

Код: Выделить всё

FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
                .show(getFragmentManager().findFragmentById(parent_id)).commit();
parent_id - это id родительского фрагмента
Я сделал так, чтобы при возврате на предыдущий фрагмент, ListView оставался на прежней позиции, а не сбрасывался на начало списка. Вот лог ошибки:

Код: Выделить всё

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT _id, name FROM company GROUP BY name
            at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
            at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:58)
            at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:152)
            at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:124)
            at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:214)
            at android.widget.CursorAdapter.getItemId(CursorAdapter.java:223)
            at android.widget.AbsListView.onSaveInstanceState(AbsListView.java:1764)
            at android.view.View.dispatchSaveInstanceState(View.java:12728)
            at android.view.ViewGroup.dispatchFreezeSelfOnly(ViewGroup.java:2629)
            at android.widget.AdapterView.dispatchSaveInstanceState(AdapterView.java:783)
            at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:2615)
            at android.view.View.saveHierarchyState(View.java:12711)
            at android.app.FragmentManagerImpl.saveFragmentViewState(FragmentManager.java:1577)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:951)
            at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)
            at android.app.BackStackRecord.run(BackStackRecord.java:641)
            at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1447)
            at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)
В общем борюсь уже долго. Не знаю, что и делать.

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 13 окт 2014, 21:51

придется переходить на контент-провайдеры
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 13 окт 2014, 22:21

Я и сам так думал. Меня все время останавливало предубеждение, что контент провайдерами разумно пользоваться только для передачи данных между приложениями. "A content provider is only required if you need to share data between multiple applications." Видимо, другого выхода нет.

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 14 окт 2014, 01:28

это утверждение уже примерно год как устарело.
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 14 окт 2014, 19:49

Перенес все на Content Provider. Все равно продолжает возникать та же ошибка. Может быть у меня неправильная логика работы лоадера?
Вот его полный текст:

Код: Выделить всё

@Override
        public Loader<Cursor> onCreateLoader(int id, Bundle bndl) {
            return new MainCursorLoader(this);
        }

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

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

        static class MainCursorLoader extends CursorLoader {

            public MainCursorLoader(Context context) {
                super(context);
            }

            @Override
            public Cursor loadInBackground() {
                Cursor cursor = null;
                switch (category) {
                    case 0:
                        if (search.isEmpty())
                            // Получаем все записи из product
                            cursor = getContext().getContentResolver().query(
                                    DBProvider.URI_PRODUCT,
                                    new String[] { DataBase.Product.COLUMN_ID, DataBase.Product.COLUMN_NAME,
                                            DataBase.Product.COLUMN_ZIP },
                                    null,
                                    null,
                                    DataBase.Product.COLUMN_NAME );
                        else
                            // Фильтрация по записям из product
                            cursor = getContext().getContentResolver().query(
                                    DBProvider.URI_PRODUCT,
                                    new String[]{DataBase.Product.COLUMN_ID, DataBase.Product.COLUMN_NAME,
                                            DataBase.Product.COLUMN_ZIP},
                                    DataBase.ProductList.COLUMN_NAME + " like ?",
                                    new String[]{search.toUpperCase(Locale.getDefault()) + "%"},
                                    DataBase.Product.COLUMN_NAME);
                        break;

                    case 1:
                        if (onResult) {
                            // Получаем список продуктов по производителю
                            Uri uri = ContentUris.withAppendedId(DBProvider.URI_PRODUCT_COMPANY, resultId);
                            cursor = getContext().getContentResolver().query( uri, null, null, null, null );

                        } else {
                            if (search.isEmpty())
                                // Получаем список из company
                                cursor = getContext().getContentResolver().query(
                                        DBProvider.URI_COMPANY,
                                        new String[] { DataBase.Company.COLUMN_ID, DataBase.Company.COLUMN_NAME },
                                        null,
                                        null,
                                        DataBase.Company.COLUMN_NAME );
                            else
                                // Поиск по записям в company
                                cursor = getContext().getContentResolver().query(
                                        DBProvider.URI_COMPANY,
                                        new String[]{DataBase.Company.COLUMN_ID, DataBase.Company.COLUMN_NAME},
                                        DataBase.Company.COLUMN_NAME + " like ?",
                                        new String[] {search.toUpperCase(Locale.getDefault()) + "%" },
                                        DataBase.Company.COLUMN_NAME );
                        }
                        break;

                    case 4:
						// Выводим избранное
                        cursor = getContext().getContentResolver().query(
                                DBProvider.URI_PRODUCT,
                                new String[]{DataBase.Product.COLUMN_ID, DataBase.Product.COLUMN_NAME,
                                        DataBase.Product.COLUMN_ZIP},
                                DataBase.Product.COLUMN_FAVORITE  + " = 1",
                                null,
                                DataBase.Product.COLUMN_NAME );
                }
                return cursor;
            }
        }
Я думаю, дело в следующем. У меня есть одно активити и пять фрагментов. Три из них со списком, наполняются одним и тем же лоадером. Струтура такая: три фрагмента с ListView открываются из меню, в них содержаться списки продуктов, производителей и избранного.
1) Фрагмент продуктов. Если кликнуть на элемент в фрагменте с продуктами, открывается новый фрагмент с подробной информацией.
2) Фрагмент избранного. Тут то же самое, как и с фрагментом продуктов.
3) Фрагмент с производителями. Тут пока просто список.
На этом этапе все работает замечательно!
Но.
После того, как я реализую метод OnItemClickListener в третьем фрагменте, который вызывает подобие первого фрагмента - фрагмент с результатами, содержащий продукты выбранного производителя. И вот с этим и возникает описанная выше ошибка. Проявляется она, как я уже писал, нерегулярно. Но практически всегда при переходе с фрагмента результатов во фрагмент продуктов. Я уже перепробовал все советы со stackoverflow по этой ошибке, ничего не помогает.

Текст ошибки ничем не отличается от данного выше

Код: Выделить всё

10-14 19:29:34.313  24409-24409/ru.endid.sqliteproject E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: ru.endid.sqliteproject, PID: 24409
    java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT _id, name FROM company ORDER BY name
            at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
            at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:58)
            at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:152)
            at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:124)
            at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:214)
            at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162)
            at android.widget.CursorAdapter.getItemId(CursorAdapter.java:223)
            at android.widget.AbsListView.onSaveInstanceState(AbsListView.java:1764)
            at android.view.View.dispatchSaveInstanceState(View.java:12728)
            at android.view.ViewGroup.dispatchFreezeSelfOnly(ViewGroup.java:2629)
            at android.widget.AdapterView.dispatchSaveInstanceState(AdapterView.java:783)
            at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:2615)
            at android.view.View.saveHierarchyState(View.java:12711)
            at android.app.FragmentManagerImpl.saveFragmentViewState(FragmentManager.java:1577)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:951)
            at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)
            at android.app.BackStackRecord.run(BackStackRecord.java:641)
            at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1447)
            at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 14 окт 2014, 20:10

Странная у тебя какая-то реализация лоадера..не знаю, может она и правильная, но
у меня все проще примерно так
http://code.tutsplus.com/tutorials/andr ... obile-5673
пункт 4.
При этом хочу отметить, что для разных списков на 1 активити используют разные лоадеры, для этого у них есть ID, который фигурирует в данном методе и других, обозначаемый константой, и по которому в данном методе грузишь тот или иной лоадер.
Loader<Cursor> onCreateLoader(int id, Bundle args) {...

вот еще ссылки
http://www.vogella.com/tutorials/Androi ... activities
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 11:54

Огромное спасибо за подсказку! Стало работать на порядок лучше!

Код: Выделить всё

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Uri uri = Uri.parse(args.getString("uri"));
        String[] projection = args.getStringArray("projection");
        String selection = args.getString("selection");
        String[] selectionArgs = args.getStringArray("selectionArgs");
        String orderBy = args.getString("orderBy");

        return new CursorLoader(this, uri, projection, selection, selectionArgs, orderBy);
    }

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

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }
Чтобы запускался нужный лоадер, я дополнительно реализовал методы init и restart, которые в зависимости от id передают нужные аргументы.
Но все равно ошибка не исчезла, хоть и стала появляться реже. Однако, появились и другие проблемы. Непосредственно перед вылетом стали вылетать пачки таких сообщений:

Код: Выделить всё

10-15 11:05:30.887    8955-8964/ru.endid.sqliteproject W/SQLiteConnectionPool﹕ A SQLiteConnection object for database '/storage/emulated/0/Android/data/ru.endid.sqliteproject/files/database' was leaked!  Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
И еще таких:

Код: Выделить всё

10-15 11:05:24.737    8955-8964/ru.endid.sqliteproject W/CursorWrapperInner﹕ Cursor finalized without prior close()
Сперва принялся решать последнюю ошибку, но так как курсор у меня должен идти на выход метода, я не нашел способа вызвать cursor.closse(); перед закрытием базы. К слову, и ошибка с утечкой вызывала у мня недоумение. Я всегда закрывал базу командой dbHelper.close() после использования.
В общем, последние две ошибки я исправил, запретив создавать новые экземпляры класса DBHelper:

Код: Выделить всё

public class DBHelper extends SQLiteOpenHelper{

    private static DBHelper mInstance = null;
    private static Context context;

    private DBHelper(Context _context) {
        super(_context, DataBase.DB_NAME, null, DataBase.DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {

    }

    public static DBHelper getInstance(Context _context) {
        if (mInstance == null) {
            context = _context;
            mInstance = new DBHelper(context.getApplicationContext());
        }
        return mInstance;
    }

    public SQLiteDatabase openDatabase() {
        File DB_PATH = context.getExternalFilesDir(null);
        File dbFile = new File(DB_PATH, DataBase.DB_NAME);
        return  SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
    }
}
Ошибка с утечкой базы исчезла, но приложение стало работать заметно медленнее, и что самое главное, ошибка

Код: Выделить всё

IllegalStateException: attempt to re-open an already-closed object
стала проявляться чаще. Я уже ума не приложу, в чем проблема.
И еще, может дело в этом:

Код: Выделить всё

10-15 11:05:35.127    8955-8955/ru.endid.sqliteproject E/MessageQueue-JNI﹕ Exception in MessageQueue callback: handleReceiveCallback
10-15 11:05:35.137    8955-8955/ru.endid.sqliteproject E/MessageQueue-JNI﹕
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT _id, name FROM company ORDER BY name 
Что означают первые две строчки?

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 12:05

нет, что-то у тебя где-то мусор какой-то есть.
ничего подобного не должно быть, синглтон из хэлпера тоже делать не нужно! закрывать тоже не нужно каждый раз. Медленно на каком наборе данных работает? у меня на 100 тыс записей сложные запросы просто летали.
Советую рассмотреть исходники повнимательнее, или туториалы, которые я скидывала - тоже сравнить со своими.
Курсор не закрыт у тебя - ну надо его закрывать после использования всегда, но вообще сборщик мусора его потом собирает. Но это не влияет особо, только на чистоту кода.
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 12:23

Спасибо, буду внимательно пересматривать код. А вот насчет курсора. Вот мой метод query в реализации Content Provider'а:

Код: Выделить всё

public Cursor query(Uri uri, String[] columns, String selection, String[] selectionArgs, String orderBy) {
        String id;
        Uri fullUri;
        String raw = "";		// некоторые сложные запросы делаю rawQuery
        Boolean isRaw = false;	// чтобы не использовать query несколько раз
        switch (uriMatcher.match(uri)) {
            
			... тут проверяю поступивший uri

            default:
                throw new IllegalArgumentException("Wrong URI: " + uri);
        }

        database = dbHelper.openDatabase();
        Cursor cursor;
        if (isRaw)
            cursor = database.rawQuery(raw, selectionArgs);
        else
            cursor = database.query(uri.getLastPathSegment(), columns, selection,
                                    selectionArgs, null, null, orderBy);
        cursor.setNotificationUri(getContext().getContentResolver(), fullUri);
        //dbHelper.close();
        return cursor;
    }
Как мне в таком случае закрыть курсор? Или его нужно закрывать в том месте, где я вызываю метод query? Например:

Код: Выделить всё

Cursor cursor = getActivity().getContentResolver().query(
                DBProvider.URI_PRODUCT_LIST,
                new String[] { ..массив столбцов.. },
                DataBase.Product.COLUMN_ID + " = ?",
                new String[] { String.valueOf(product_id) },
                null );
        if (cursor != null) {
            cursor.moveToFirst();
            ... получаю из курсора все, что нужно ...
        }
		cursor.close();
Так?
P.S. не то, чтобы медленно, на заметно медленнее работает, когда dbHelper создается в единственном экземпляре. База большая, под 100 мб.

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 13:17

Открой любой туториал на нормальном сайте и сравни свой код, еще раз повторяю.
вот я тебе ссылки давала, плюс допустим тут http://www.grokkingandroid.com/android- ... -provider/
ну где ты там видишь, чтоб база открывалась? там пишут
первая же строка
SQLiteDatabase db = mHelper.getReadableDatabase();
!!!
и так далее....
rawQuery делают не по условию. Нужно завести отедльный URI под это дело и писать не условие, а отдельную ветку switch в данном методе query
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 13:18

медленно не должно работать, оптимизируй, значит, свои запросы. Индексы ставь, если вообще критично.
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 13:37

Спасибо за информацию!
У меня база данных на внешней памяти, поэтому SQLiteDatabase db = mHelper.getReadableDatabase(); не работает. Вместо этого я возвращаю
SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);

с rawQuery я так и делаю. У меня отдельный Uri, на отдельную ветку switch в котором и включается условие. Либо я в каждой ветке прописываю query, или rowQuery, в зависимости от того, что нужно сделать, либо в нужном месте поднимаю флаг и в конце выполняю query, или rawQuery по условию. Мне показалось, так будет меньше кода.

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 13:49

в общем ясно.
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 14:29

Все-таки работает. В первый раз у меня не получилось реализовать через getReadableDatabase, я пошел за ответом на stackoverflow. Там популярным ответом было использовать SQLiteDatabase.openDatabase(..).
Сейчас убрал метод openDatabase. В конструкторе dbHelper сделал так:

Код: Выделить всё

public DBHelper(Context _context, String dbName) {
        super(_context, dbName, null, DataBase.DB_VERSION);
        context = _context;
    }
и вызываю
dbHelper.getReadableDatabase(); вместо SQLiteDatabase.openDatabase(...);
Но, изменений вообще нет. Лоадер у меня реализован как в примерах по ссылкам выше, контент провайдер сделал по уроку (да, добавил условие). В общем, буду дальше искать.

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 15:46

ты изменил лоадер, т.к. раньше ты показывал код вовсе не такой, как по ссылкам выше.
И интерсно, что там за код, где у тебя написано "получаю из курсора что нужно"? может, в нем дело?
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 16:08

там я создаю экземпляр своего класса Product, куда просто складываю значения с курсора.

Код: Выделить всё

if (cursor != null) {
    cursor.moveToFirst();
    product = new Product(cursor.getInt(cursor.getColumnIndex(DataBase.Product.COLUMN_ID)),
                    cursor.getString(cursor.getColumnIndex(DataBase.Product.COLUMN_NAME)),
                    cursor.getString(cursor.getColumnIndex(DataBase.Product.COLUMN_GROUPS)),
                    cursor.getString(cursor.getColumnIndex(DataBase.Product.COLUMN_COMPANY)),
                    cursor.getInt(cursor.getColumnIndex(DataBase.Product.COLUMN_INFO_ID)),
                    (cursor.getInt(cursor.getColumnIndex(DataBase.Product.COLUMN_FAVORITE)) != 0));
    cursor.close();
}
Потом вытягиваю по мере надобности.

Я все-таки локализировал ошибку. Проблема заключалась в том, что я описал еще в первом посте. Вот в этом:

Код: Выделить всё

fragmentManager.beginTransaction() .add(R.id.content_frame, fragment).addToBackStack(null).commit(); 
         fragmentManager.beginTransaction().hide(thisFragment).commit(); 
Сделал все проще

Код: Выделить всё

fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).addToBackStack(null).commit();
Неудачный я выбрал способ, чтобы сохранять состояние предыдущего фрагмента.
Вам огромное спасибо за помощь и полезную информацию!

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 16:32

я бы так не переписывала весь огромный курсор в объекты. Тоже работаю с аналогичными таблицами товаров, но как-то не было такой потребности. Что с ним делать-то? отобразить - тут достаточно курсора и адаптера на курсоре, который быстро работает, чтоб доставать оттуда данные? сразу я эти данные беру опять из таблиц, чтоб сохранять? я сразу в базу пишу - ничего не тормозит нигде. И память экономится.
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 15 окт 2014, 17:04

Согласен. Удалил этот класс, и теперь вывожу напрямую из курсора. По сути, все сразу идет на view элементы и пару переменных

Аватара пользователя
Foenix
Сообщения: 4201
Зарегистрирован: 20 окт 2012, 12:01

Re: Один Cursor Loader на несколько фрагментов.

Сообщение Foenix » 15 окт 2014, 17:23

и везде нужно так делать (если возможно), и почаще пользоваться запросами, запрашивая только самое необходимое, ничего не обрабатывая в java.
Ну представь ты себе противоречишь? у тебя такая большая бд что ты запихнул ее на карту памяти, а потом качаешь ее в оперативку!
R.id.team

NullPointerException - что делать???
viewtopic.php?f=33&t=3899&p=28952#p28952
Где моя ошибка?
viewtopic.php?f=60&t=3198

endid13
Сообщения: 16
Зарегистрирован: 26 сен 2014, 16:34

Re: Один Cursor Loader на несколько фрагментов.

Сообщение endid13 » 16 окт 2014, 20:38

Мне все таки не дает покоя эта ошибка. Хоть она и не вылетает больше, но я прям чувствую, что она все еще осталась. Откатил назад и пытаюсь ее поймать. И у меня уже есть серьезный подозрения, что проблема не в моем приложении, а в чем то другом.
Я окружил все проблемные места try - catch и ничего.
Судя по логу, ошибка связана с базой. Сделал try catch в методах query и update/
В методе query контент провайдера:

Код: Выделить всё

try {
            database = dbHelper.getReadableDatabase();
            Cursor cursor;
            if (isRaw)
                cursor = database.rawQuery(raw, selectionArgs);
            else
                cursor = database.query(uri.getLastPathSegment(), columns, selection,
                        selectionArgs, null, null, orderBy);
            cursor.setNotificationUri(getContext().getContentResolver(), fullUri);
            return cursor;
        } catch (IllegalStateException e) {
            Log.d("myDebug", "ISE query");
            e.printStackTrace();
        }
и в методе update:

Код: Выделить всё

try {
            database = dbHelper.getReadableDatabase();
            int count = 0;
            database.beginTransaction();
            try {
                count = database.update(DataBase.ProductList.TABLE_NAME, contentValues, selection, selectionArgs);
                database.setTransactionSuccessful();
            } finally {
                database.endTransaction();
            }
            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        } catch (IllegalStateException e) {
            Log.d("myDebug", "ISE update");
            e.printStackTrace();
        }
Все! это единственные места в коде, через которые осуществляется доступ к БД. И ничего. Ошибка вылетает, но не здесь.
Так же сделал то же самое со строкой adapter.swapCursor(cursor) в методе onLoadFinish. Тоже ничего не словил. Еще проверил методы getLoaderManager.initLoader() и getLoaderManager.restartLoader(). Все чисто. А ошибка вылеает.
Может у вас будут какие-то советы, где можно еще попробовать поискать?

Ответить