Форум программистов, компьютерный форум, киберфорум
Программирование Android
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
Android
245 / 242 / 52
Регистрация: 19.01.2013
Сообщений: 1,896
Записей в блоге: 3

Работа с SqlLite из нескольких потоков

19.10.2017, 10:26. Показов 1600. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть 5 фрагментов. В каждом фрагменте есть потоки работающие с сетью. При onPause делаю
Java
1
thread.interrupt();
В каждом потоке происходит обращение к SqlLite (чтение и запись).

экземпляр DataBaseHelper создается при onCreate фрагмента и используется внутри другого потока.

Но когда данных очень много и я перехожу между фрагментами в момент записи в БД, происходит ошибка :
XML
1
SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode

Как бы решить эту проблему?

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

Добавлено через 9 минут
+

Получаю данные из сервера
List<Data>
и отправляю их на запись в БД

Java
1
2
3
if (response.body()!=null && response.body().data!=null) {
  dataBaseHelper.updateData(response.body().data);
}
внутри метода updateData в цикле записывается полученный List<Data>

Добавлено через 11 минут
UP.
Запрос на сервер идет в цикле, оказывается в цикле не проверял
Java
1
if (Thread.interrupted()) { return; }
сейчас вроде работает без сбоев.

Но буду рад если подскажите как работаете с SqlLite, как лучше использовать.
Может стоит создавать один экземпляр SQLiteDatabase db = getWritableDatabase(); для экземпляра DataBaseHelper (у меня в каждом методе открывается и закрывается "getWritableDatabase")
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
19.10.2017, 10:26
Ответы с готовыми решениями:

Корректная работа нескольких потоков одновременно
Дорогие пользователи возникла проблема... Мне нужно запустить програму-чекер в многопотоке... Но как там много идёт время выполнения...

Отобразить ход нескольких потоков в нескольких ProgressBar
Возник вопрос как прикрепить ProgressBar к потокам, к примеру 100 потоков, нужно, чтобы ProgressBar правильно изменялся. Заранее спасибо

Работа с БД SQLlite стандартными средствами C#
Собственно есть тулза, состоящая из одного исполняемого файла. С недавних пор, понадобилась внести в ее функционал возможность считывать...

5
314 / 257 / 81
Регистрация: 31.10.2016
Сообщений: 619
19.10.2017, 13:16
Лучший ответ Сообщение было отмечено ILNAR_93 как решение

Решение

ILNAR_93, открытие базы данных каждый раз достаточно долгая операция по сравнению с записью. Я для себя делал отсрочку при закрытии. Т.е. есть Runnable, в котором только один метод закрытия базы. И есть Handler с postDelayed. И когда идет запись в БД, то я проверяю, открыт ли у меня инстанс бд. Если не открыт, то открываю его. Если открыт, то у handler удаляю задачу с закрытием. А после записываю туда транзакцию и ставлю задачу с закрытием.
По локам базы самый простой вариант это создать final Object lockObject и проводить записи в бд по синхронизации с ним либо через синхронизированные методы.

Я тут код накидал, чтоб показать

Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
 
 
class DatabaseHelper extends SQLiteOpenHelper {
 
    private static final class DATABASE {
        static final String NAME = "my_db.db";
        static final int VERSION = 1;
    }
 
    static final class TABLE {
        static final String PERSONS = "Persons";
    }
 
    static final class PERSON_FIELDS {
        static final String ID = "id";
        static final String FIRST_NAME = "fname";
        static final String LAST_NAME = "lname";
        static final String EMAIL = "email";
    }
 
    private static final class QUERY {
 
        static final String CREATE_TABLE_PERSONS = "CREATE TABLE "
                + TABLE.PERSONS
                + " ("
                + PERSON_FIELDS.ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                + PERSON_FIELDS.FIRST_NAME + " TEXT, "
                + PERSON_FIELDS.EMAIL + " TEXT, "
                + PERSON_FIELDS.LAST_NAME + " TEXT "
                + ");";
 
        static final String DROP_TABLE_PERSONS = "DROP TABLE IF EXISTS " + TABLE.PERSONS + ";";
    }
 
    DatabaseHelper(@NonNull Context context) {
        super(context, DATABASE.NAME, null, DATABASE.VERSION);
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(QUERY.CREATE_TABLE_PERSONS);
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(QUERY.DROP_TABLE_PERSONS);
        db.execSQL(QUERY.CREATE_TABLE_PERSONS);
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.util.Log;
 
import com.example.test22.model.Person;
 
import java.util.ArrayList;
 
 
final class WriteDatabaseThread extends Thread {
    private static final String TAG = "WriteDatabaseThread";
    static final int WHAT_SAVE_PERSONS = 1000;
    static final String KEY_SAVE_PERSONS = "key_save_persons";
 
    private Handler databaseHandler;
 
    @NonNull
    private final Handler closeHandler;
    @NonNull
    private final Runnable closeRunnable;
 
    @NonNull
    private final DatabaseHelper databaseHelper;
    private SQLiteDatabase database;
 
    WriteDatabaseThread(@NonNull DatabaseHelper databaseHelper) {
        Log.d(TAG, "WriteDatabaseThread() called");
        this.databaseHelper = databaseHelper;
        this.closeHandler = new Handler(Looper.myLooper());
        this.closeRunnable = new Runnable() {
            @Override
            public void run() {
                close();
            }
 
            private synchronized void close() {
                Log.d(TAG, "close() called");
                if (database != null && database.isOpen()) {
                    if (database.inTransaction()) {
                        closeHandler.removeCallbacks(closeRunnable);
                        closeHandler.postDelayed(closeRunnable, 60_000);
                    } else {
                        database.close();
                        database = null;
                    }
                }
            }
        };
 
        databaseHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (isInterrupted()) return;
                switch (msg.what) {
                    case WHAT_SAVE_PERSONS:
                        Log.d(TAG, "handleMessage() called with: msg = [" + WHAT_SAVE_PERSONS + "]");
                        Bundle bundle = msg.getData();
                        if (bundle != null && bundle.containsKey(KEY_SAVE_PERSONS)) {
                            ArrayList<Person> persons = (ArrayList<Person>) bundle.getSerializable(KEY_SAVE_PERSONS);
                            if (persons != null) {
                                savePersons(persons);
                            } else {
                                Log.d(TAG, "handleMessage() persons is null");
                            }
                        } else {
                            Log.d(TAG, "handleMessage() bundle is null");
                        }
                        break;
                    default:
                        Log.d(TAG, "handleMessage() called with: msg = [ DEFAULT ]");
                        break;
                }
            }
        };
    }
 
 
    @Override
    public void run() {
        Log.d(TAG, "run() called");
        Looper.prepare();
 
        Looper.loop();
    }
 
    Handler getDatabaseHandler() {
        return databaseHandler;
    }
 
    private synchronized void open() {
        try {
            if (database == null || !database.isOpen()) {
                Log.d(TAG, "open() called");
                database = databaseHelper.getWritableDatabase();
            }
            closeHandler.removeCallbacks(closeRunnable);
            closeHandler.postDelayed(closeRunnable, 60_000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
 
    private synchronized void savePersons(@NonNull ArrayList<Person> persons) {
        Log.d(TAG, "savePersons() called with: persons = [" + persons.size() + "]");
        long startTime = System.currentTimeMillis();
        open();
        SQLiteStatement stmt = database.compileStatement(
                String.format("INSERT INTO %s ('%s', '%s', '%s') VALUES(?1, ?2, ?3)",
                        DatabaseHelper.TABLE.PERSONS,
                        DatabaseHelper.PERSON_FIELDS.FIRST_NAME,
                        DatabaseHelper.PERSON_FIELDS.LAST_NAME,
                        DatabaseHelper.PERSON_FIELDS.EMAIL));
        database.beginTransaction();
 
        for (Person person : persons) {
            stmt.clearBindings();
            stmt.bindString(1, person.getFirstName());
            stmt.bindString(2, person.getLastName());
            stmt.bindString(3, person.getEmail());
            stmt.executeInsert();
        }
 
        database.setTransactionSuccessful();
        database.endTransaction();
        Log.d(TAG, "savePersons() returned with time " + (System.currentTimeMillis() - startTime) + " ms");
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import android.content.Context;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
 
import com.example.test22.model.Person;
 
import java.util.ArrayList;
 
 
public final class StorageDatabase {
 
    @NonNull
    private final WriteDatabaseThread writeDatabaseThread;
 
    public StorageDatabase(@NonNull Context applicationContext) {
        DatabaseHelper databaseHelper = new DatabaseHelper(applicationContext);
        writeDatabaseThread = new WriteDatabaseThread(databaseHelper);
        writeDatabaseThread.start();
    }
 
    public void savePersons(@Nullable ArrayList<Person> persons) {
        if (persons == null || persons.isEmpty()) return;
 
        Bundle bundle = new Bundle(1);
        bundle.putSerializable(WriteDatabaseThread.KEY_SAVE_PERSONS, persons);
 
        Message message = new Message();
        message.what = WriteDatabaseThread.WHAT_SAVE_PERSONS;
        message.setData(bundle);
 
        writeDatabaseThread.getDatabaseHandler().sendMessage(message);
    }
 
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import android.support.annotation.NonNull;
 
import java.io.Serializable;
 
public class Person implements Serializable {
    private int id;
    private String firstName;
    private String lastName;
    private String email;
 
    public Person() {
    }
 
    public Person(@NonNull String firstName, @NonNull String lastName, @NonNull String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getFirstName() {
        return firstName;
    }
 
    public String getLastName() {
        return lastName;
    }
 
    public String getEmail() {
        return email;
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
 
        Person person = (Person) o;
 
        if (id != person.id) return false;
        if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null)
            return false;
        if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null)
            return false;
        return email != null ? email.equals(person.email) : person.email == null;
 
    }
 
    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        result = 31 * result + (email != null ? email.hashCode() : 0);
        return result;
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import android.app.Application;
 
import com.example.test22.database.StorageDatabase;
 
public class App extends Application {
 
    private StorageDatabase storageDatabase;
 
    @Override
    public void onCreate() {
        super.onCreate();
        storageDatabase = new StorageDatabase(this);
    }
 
    public StorageDatabase getStorageDatabase() {
        return storageDatabase;
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
 
import com.example.test22.model.Person;
 
import java.util.ArrayList;
import java.util.Random;
 
public class MainActivity extends AppCompatActivity {
 
    String[] firstNames = new String[]{
            "Alice", "Bob", "Carol", "Chloe", "Dan", "Emily", "Emma", "Eric", "Eva",
            "Frank", "Gary", "Helen", "Jack", "James", "Jane",
            "Kevin", "Laura", "Leon", "Lilly", "Mary", "Maria",
            "Mia", "Nick", "Oliver", "Olivia", "Patrick", "Robert",
            "Stan", "Vivian", "Wesley", "Zoe"};
    String[] lastNames = new String[]{
            "Hall", "Hill", "Smith", "Lee", "Jones", "Taylor", "Williams", "Jackson",
            "Stone", "Brown", "Thomas", "Clark", "Lewis", "Miller", "Walker", "Fox",
            "Robinson", "Wilson", "Cook", "Carter", "Cooper", "Martin"};
    Random random = new Random();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btInsert).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ArrayList<Person> people = new ArrayList<>();
                for (int i = 0; i < 3000; i++) {
                    Person person = new Person();
                    String first = firstNames[random.nextInt(firstNames.length)];
                    String last = lastNames[random.nextInt(lastNames.length)];
                    person.setFirstName(first);
                    person.setLastName(last);
                    person.setEmail(Character.toLowerCase(first.charAt(0)) + "." +
                            last.toLowerCase() + "@gmail.com");
                    people.add(person);
                }
                ((App) getApplication()).getStorageDatabase().savePersons(people);
            }
        });
    }
 
}
1
Android
245 / 242 / 52
Регистрация: 19.01.2013
Сообщений: 1,896
Записей в блоге: 3
19.10.2017, 16:46  [ТС]
demixdn, спасибо. Попробую переварить)

Добавлено через 29 минут
А можно как то прервать операцию

Java
1
2
3
 private synchronized void savePersons(@NonNull ArrayList<Person> persons) {
//....
}
Например осталось сохранить еще 1000 persons, а нам это уже не нужно т.к. в приоритете другая операция по сохранению других данных, а к persons`ам мы вернемся в след раз когда они понадобятся .
0
314 / 257 / 81
Регистрация: 31.10.2016
Сообщений: 619
19.10.2017, 17:33
savePersons лучше не разбивать, но можно заливать порциями.
Например нам прилетел список из 3000, мы его разбиваем в Queue<ArrayList<Person>> и в каждом itemQueue хранить по 100...1000 персон. И потом циклом while(!queue.isEmpty) брать порции и применять savePersons. И если у вас прилетел приоритетная порция, то мы ее либо в начало добавляете, либо еще как то разрулить ситуацию.
0
Android
245 / 242 / 52
Регистрация: 19.01.2013
Сообщений: 1,896
Записей в блоге: 3
23.10.2017, 11:07  [ТС]
demixdn, есть вопрос)
При записи понятно, открываем транзакцию, а при чтении не подскажешь как делать?
я чуть по другому сделал, но с одним подключением к DB. и получается хендл закрытия может закрыть подключение в момент чтения
Java
1
2
3
4
5
6
7
8
9
if (database != null && database.isOpen()) {
                    if (database.inTransaction()) {
                        closeHandler.removeCallbacks(closeRunnable);
                        closeHandler.postDelayed(closeRunnable, 60_000);
                    } else {
                        database.close();
                        database = null;
                    }
                }
0
314 / 257 / 81
Регистрация: 31.10.2016
Сообщений: 619
23.10.2017, 13:26
ILNAR_93, для записи берется getWritableDatabase. Для чтения берите getReadableDatabase, он может читаться из нескольких потоков. Это главная особенность. Поэтому читать можно из нескольких потоков, БД сама манипулирует запросами на чтение.

Я для чтения из базы в свое время написал простой класс и Callback. Класс выполняет указанную работу в другом потоке и возвращает результат по коллбэку в главный поток. Этот класс подходит для мелких асинхронных задач.
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
public interface Callback<T> {
    void onSuccess(T result);
 
    void onFailure(Throwable throwable);
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import android.util.Log;
 
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
 
public abstract class SimpleTask<T> implements Runnable {
    private static final String TAG = "SimpleTask";
    private static Executor executor = Executors.newCachedThreadPool();
    private final WeakReference<Callback<T>> callbackReference;
    private static Handler handlerInstance;
 
    public SimpleTask(@NonNull Callback<T> callback) {
        callbackReference = new WeakReference<>(callback);
    }
 
    @Override
    public void run() {
        Log.d(TAG, "run on thread [" + Thread.currentThread().getName() + "]");
        try {
            T result = doWork();
            Callback<T> callback = callbackReference.get();
            if (callback != null) getUIHandler().post(new ResultRunnable<>(callback, result));
        } catch (Exception e) {
            Callback<T> callback = callbackReference.get();
            if (callback != null) getUIHandler().post(new FailureRunnable(callback, e));
        }
    }
 
    @WorkerThread
    public abstract T doWork() throws Exception;
 
    public void start() {
        executor.execute(this);
    }
 
    private static synchronized Handler getUIHandler() {
        if (handlerInstance == null) handlerInstance = new Handler(Looper.getMainLooper());
        return handlerInstance;
    }
 
    private static final class ResultRunnable<T> implements Runnable {
 
        @NonNull
        private final Callback<T> callback;
        private final T result;
 
        private ResultRunnable(@NonNull Callback<T> callback, T result) {
            this.callback = callback;
            this.result = result;
        }
 
        @Override
        public void run() {
            callback.onSuccess(result);
        }
    }
 
    private static final class FailureRunnable implements Runnable {
 
        @NonNull
        private final Callback callback;
        private final Throwable throwable;
 
        private FailureRunnable(@NonNull Callback callback, Throwable throwable) {
            this.callback = callback;
            this.throwable = throwable;
        }
 
        @Override
        public void run() {
            callback.onFailure(throwable);
        }
    }
}


После этого просто расширяете класс StorageDatabase методом и небольшим внутренним классом
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
 
import com.example.test22.model.Person;
 
import java.util.ArrayList;
import java.util.List;
 
 
public final class StorageDatabase {
 
    @NonNull
    private final WriteDatabaseThread writeDatabaseThread;
 
    private SQLiteDatabase readableDatabase;
 
    public StorageDatabase(@NonNull Context applicationContext) {
        DatabaseHelper databaseHelper = new DatabaseHelper(applicationContext);
        writeDatabaseThread = new WriteDatabaseThread(databaseHelper);
        writeDatabaseThread.start();
        readableDatabase = databaseHelper.getReadableDatabase();
    }
 
    public void savePersons(@Nullable ArrayList<Person> persons) {
        if (persons == null || persons.isEmpty()) return;
 
        Bundle bundle = new Bundle(1);
        bundle.putSerializable(WriteDatabaseThread.KEY_SAVE_PERSONS, persons);
 
        Message message = new Message();
        message.what = WriteDatabaseThread.WHAT_SAVE_PERSONS;
        message.setData(bundle);
 
        writeDatabaseThread.getDatabaseHandler().sendMessage(message);
    }
 
    public void getPersons(@NonNull Callback<List<Person>> callback) {
        new GetDatabaseContentTask(readableDatabase, callback).start();
    }
 
    private static class GetDatabaseContentTask extends SimpleTask<List<Person>> {
 
        @NonNull
        private final SQLiteDatabase readableDatabase;
 
        private GetDatabaseContentTask(@NonNull SQLiteDatabase readableDatabase, @NonNull Callback<List<Person>> callback) {
            super(callback);
            this.readableDatabase = readableDatabase;
        }
 
        @Override
        public List<Person> doWork() throws Exception {
            return getPersons();
        }
 
        private List<Person> getPersons() {
            List<Person> persons = new ArrayList<>();
            Cursor cursor = readableDatabase.rawQuery("SELECT * FROM " + DatabaseHelper.TABLE.PERSONS, null);
            if (cursor.getCount() == 0)
                return persons;
            while (cursor.moveToNext()) {
                int id = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.PERSON_FIELDS.ID));
                String firstName = cursor.getString(cursor.getColumnIndex(DatabaseHelper.PERSON_FIELDS.FIRST_NAME));
                String lastName = cursor.getString(cursor.getColumnIndex(DatabaseHelper.PERSON_FIELDS.LAST_NAME));
                String email = cursor.getString(cursor.getColumnIndex(DatabaseHelper.PERSON_FIELDS.EMAIL));
                persons.add(new Person(id, firstName, lastName, email));
            }
            if (!cursor.isClosed())
                cursor.close();
            return persons;
        }
    }
 
}
И запускаете из Activity|Fragment. Он естественно должен имплементить тот интерфейс.
Java
1
((App) getApplication()).getStorageDatabase().getPersons(MainActivity.this);
Добавлено через 17 минут
Но ошибка с закрытием базы все равно будет. Там тоже нужно проверять, если база открыта то вытягивать записи, а если закрыта, то сначала открыть.

Добавлено через 3 минуты
Java
1
2
3
4
5
public void getPersons(@NonNull Callback<List<Person>> callback) {
        if (readableDatabase == null || !readableDatabase.isOpen())
            readableDatabase = databaseHelper.getReadableDatabase();
        new GetDatabaseContentTask(readableDatabase, callback).start();
    }
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
23.10.2017, 13:26
Помогаю со студенческими работами здесь

Запуск нескольких потоков
По отдельности каждый поток запускается, как мне сделать так, чтобы при нажатии кнопки 1 у меня запускались уже 3 созданных потока ( я их...

Синхронизация нескольких потоков
Здравствуйте. После нескольких лет пользования tidHttp появилось желание вынести загрузку (а точнее команду http.get(...)) в поток, а...

Выполнение нескольких потоков
Объясните каким образом выполняется данный код: class DaughterThread implements Runnable{ String DTname; DaughterThread(String...

Создание нескольких потоков
как сделать что было много потоков, я не совсем понимаю как это?

Создание нескольких потоков
Суть проблемы таковая - мне нужно создать несколько потоков (их количество задаётся в командной строке) и вывести созданным потоком его...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Подстановка значения реквизита справочника в табличную часть документа
Maks 10.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеПерсонала", разработанного в конфигурации КА2. Задача: при выборе сотрудника (справочник Сотрудники) в ТЧ документа. . .
Очистка реквизитов документа при копировании
Maks 09.04.2026
Алгоритм из решения ниже применим как для типовых, так и для нетиповых документов на самых различных конфигурациях. Задача: при копировании документа очищать определенные реквизиты и табличную. . .
модель ЗдравоСохранения 8. Подготовка к разному выполнению заданий
anaschu 08.04.2026
https:/ / github. com/ shumilovas/ med2. git main ветка * содержимое блока дэлэй из старой модели теперь внутри зайца новой модели 8ATzM_2aurI
Блокировка документа от изменений, если он открыт у другого пользователя
Maks 08.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа, разработанного в конфигурации КА2. Задача: запретить редактирование документа, если он открыт у другого пользователя. / / . . .
Система безопасности+живучести для сервера-слоя интернета (сети). Двойная привязка.
Hrethgir 08.04.2026
Далее были размышления о системе безопасности. Сообщения с наклонным текстом - мои. А как нам будет можно проверить, что ссылка наша, а не подделана хулиганами, которая выбросит на другую ветку и. . .
Модель ЗдрввоСохранения 7: больше работников, больше ресурсов.
anaschu 08.04.2026
работников и заданий может быть сколько угодно, но настроено всё так, что используется пока что только 20% kYBz3eJf3jQ
Дальние перспективы сервера - слоя сети с космологическим дизайном интефейса карты и логики.
Hrethgir 07.04.2026
Дальнейшее ближайшее планирование вывело к размышлениям над дальними перспективами. И вот тут может быть даже будут нужны оценки специалистов, так как в дальних перспективах всё может очень сильно. . .
Горе от ума
kumehtar 07.04.2026
Эта мне ментальная установка, что вот прямо сейчас, мол, мне для полного счастья не хватает (нужное вписать), и когда я этого достигну - тогда и полный кайф. Одна из самых сильных ловушек на пути. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru