Android 安卓SQLite中的多线程呢?
在我的应用程序中,我必须实现一些UI和同步服务。 它在后台运行并更新数据。同步服务不是很简单,它使用多线程 下面是我的故事: 当我开始开发这个应用程序时,我对sqlite一无所知,所以我没有在Java中使用任何线程同步。结果:我得到了很多异常,比如“SQLiteException:数据库被锁定:beginexclusive 然后,我用常规的Java synchronized(){}块同步了所有事务。一切都好了很多。但是我使用游标来实现列表的游标适配器。所以,有时我会得到相同的“SQLiteException:数据库被锁定:beginexclusive 我最后创建了一个小的线程安全sqlite实用程序,它处理所有线程安全的东西。此外,我还必须为我的UI使用ArrayAdapter(从游标读取所有数据,并在读取后关闭它,同时同步此块)之类的东西。所以,它工作正常 但是,我不喜欢这种处理UI的方式,因为使用这种解决方案,UI变得非常慢——从游标读取一些数据相当快,但比使用游标适配器慢 那么,谁得到了这个问题的答案?Android 安卓SQLite中的多线程呢?,android,multithreading,sqlite,Android,Multithreading,Sqlite,在我的应用程序中,我必须实现一些UI和同步服务。 它在后台运行并更新数据。同步服务不是很简单,它使用多线程 下面是我的故事: 当我开始开发这个应用程序时,我对sqlite一无所知,所以我没有在Java中使用任何线程同步。结果:我得到了很多异常,比如“SQLiteException:数据库被锁定:beginexclusive 然后,我用常规的Java synchronized(){}块同步了所有事务。一切都好了很多。但是我使用游标来实现列表的游标适配器。所以,有时我会得到相同的“SQLiteExc
谢谢SQLite实现了一个独占的写锁、共享的读锁模型。这意味着您可以同时在数据库中拥有活动的并发读卡器,也可以拥有一个编写器,但不能同时拥有这两个。如果使用WAL日志功能,则可以在数据库中同时激活一个writer和多个reader,但不能有多个writer。有关于SQLite并发性和并发性的优秀文档 <>你可能想考虑一下伯克利DB。提供与SQLite完全兼容的。事实上,我们所做的是在Berkeley DB存储层的顶部添加SQLite解析器、规划器和执行器。这为SQLite应用程序开发人员提供了一个与SQLite兼容的库,它具有额外的可伸缩性、并发性和可靠性(HA)以及其他特性。Berkeley DB支持同时访问数据库的多个读写器。《SQLite权威指南》的作者迈克·欧文斯(Mike Owens)写了两篇优秀的白皮书,比较了伯克利DB和SQLite(,)
免责声明:我是伯克利DB的产品经理之一,所以我有点偏见。但是,像您这样的请求(需要SQLite提供更多的并发性、可伸缩性和可靠性)正是我们决定提供一个组合库的原因,它将为您提供最好的两个方面 于是,终于找到了解决办法。给你 我阅读了一些论坛、谷歌群组,发现sqlite数据库应该只打开一次。所以,我使用singleton实现了这个 此外,我还实现了一些db代码来同步所有写操作(以防止多个线程同时执行写操作)。 我不在乎打开游标,从中读取 经过几天的测试,我没有收到来自用户的错误报告,所以我认为这是可行的
在我以前的工作中,我在整个应用程序中多次打开sqlite数据库,这就是问题所在。如果您只使用一个单例帮助器类访问数据库,您不需要自己进行同步,您可以使用来自多个读写器的帮助器,因为帮助器类本身管理同步
查看mor详细说明使用单例帮助程序打开连接 1) 打开尽可能多的可读连接,然后在完成操作后关闭 2) 对于可写连接,您应该只打开一个可写连接 尝试打开另一个可写连接时,返回已打开的连接。 如果不存在可写连接,请打开新的可写连接。 保留一个可写_连接计数器,并在所有线程都完成后关闭可写连接
我将要实现这一点,完成后会让大家知道它是否有效。好吧,阅读文档我之前的回答似乎不正确。 我有一个(吨)SQLiteOpenHelper,所以看起来所有连接都是相同的。 即使是可读连接也与可写连接相同 因此,我将跳过调用getReadableDatabase,而只使用getWritableDatabase。 然后我将保留一个计数器,以确保数据库关闭一次,并且只关闭一次 由于SQLiteOpenHelper序列化了写入操作,因此通过这种方式一切都应该很好。 我只需要跟踪连接,以确保最后不会打开一个 因此,在我的DbHelper类中,我现在有:
private int activeDatabaseCount = 0;
public synchronized SQLiteDatabase openDatabase() {
SQLiteDatabase connection = getWritableDatabase(); // always returns the same connection instance
activeDatabaseCount++;
return connection;
}
public synchronized void closeDatabase(SQLiteDatabase connection) {
activeDatabaseCount--;
if (activeDatabaseCount == 0) {
if (connection != null) {
if (connection.isOpen()) {
connection.close();
}
}
}
}
再见:
以下是解决方案:
我试图解释代码中的所有内容
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ScrollView;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class HelpUI {
final static class GetDataFromDBInAsyncMode extends AsyncTask<Object, Object, Object> {
private Context context;
private String SQL;
private final int xMin = -X_App.ScreenHeight; // put here your screen height (get it from code)
private final int xMax = X_App.ScreenHeight * 2; // put here your screen height (get it from code)
// this is the main view witch go to have all views
// Use this in onProgressUpdate to add other vies you get in runtime when you get data from database
private ViewGroup view_container;
// if you have a scrollview hide views witch isnot visible to user
private ScrollView scroll_view_container;
// this workaround make free processors in measureaments, your UI is more fluent
final ViewTreeObserver.OnScrollChangedListener onScrollChangedListener = () -> {
if (view_container == null || view_container.getChildCount() == 0) { return; }
int scrollY = scroll_view_container.getScrollY();
for (int i = 0; i < view_container.getChildCount(); i++) {
final View current = view_container.getChildAt(i);
final int topView = current.getTop(); //container_views.getChildAt(i).getTop();
final int diffTop = topView - scrollY;
if ((diffTop > xMin) && (diffTop < xMax)) {
current.setVisibility(View.VISIBLE);
} else {
current.setVisibility(View.INVISIBLE);
}
}
};
// constructor
GetDataFromDBInAsyncMode(Context ctx, String mSQL) {
this.context = ctx;
this.SQL = mSQL;
// put here the id of scrollViewContainer
scroll_view_container = X_App.getRootV().findViewById(R.id.scroll_view_container);
if (scroll_view_container != null) {
// add listener on scroll
scroll_view_container.getViewTreeObserver().addOnScrollChangedListener(onScrollChangedListener);
}
}
@Override
protected Object doInBackground(Object... objects) {
// All dirty things go to being in background
// Your cursor
final Cursor cursor = X_SqLite.get(X_App.getContext()).GeDataAsCursor(SQL);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
// The magic part
ScheduledExecutorService giveMeAsync = Executors.newSingleThreadScheduledExecutor();
// Give time to processor to do her tasks and do you dirty task here
// 50 millisec is enough per a single task
giveMeAsync.scheduleAtFixedRate(() -> {
ViewGroup viewInflated = ((Activity) this.context).findViewById(R.id.icon_view);
// Give your data from Database
// Do here all your things but take care some of these need to be done on UI thread (like adding view etc, you can do that on onProgressUpdate)
final String data1 = cursor.getString(cursor.getColumnIndex("data1"));
viewInflated.setTag(data1);
// Send this to UI thread
publishProgress(viewInflated, false);
// Here test to cursor if is finish or not
if (!cursor.moveToNext()) {
giveMeAsync.shutdownNow();
cursor.close();
publishProgress(null, true);
}
}, 1, 50, TimeUnit.MILLISECONDS);
}
// otherwise if cursor is emty close them
if (cursor != null && cursor.getCount() == 0) {
cursor.close();
publishProgress(null, true);
}
return null;
}
@Override
protected void onProgressUpdate(Object... values) {
final View viewInflated = (View) values[0];
final boolean isCursorEnded = (Boolean) values[0];
// Here you is in main thread
// You can do all what you do with the viewInflated
if (isCursorEnded) {
//raise your event to tell to your app reading data is finished
}
}
}
}
使用您的变量/对象更改变量只需注意-首选完整链接(而不是缩短的链接),以便我们可以看到它们指向何处。你有安卓系统实现的链接吗?干杯谢谢你的回答!但我需要我的解决方案在Android设备上工作。据我所知,您的解决方案无法在Android上运行。(sqlite有嵌入式和特殊版本)@Joseph,谢谢你的建议。我注意到使用了短链接和完整链接。现在,您可以为Android构建BDB,或者替换SQLite库,或者将BDB链接到应用程序中并在本地调用它。关于这一点,这里有一个很好的OTN帖子:你怎么说UI变慢了?是否需要更长的时间来响应触摸等,或只是更长的时间来显示您的数据库数据?如果您只是在读取数据库,那么不要使用事务-这允许您一次从数据库执行多次读取。@Joseph Earl我的意思是“显示数据的时间更长”。是的,我在读取数据时不使用事务。我只在写一些数据时才使用它们。。。如果只打开数据库一次。。。如何消除这个恼人的警告:
close()从未在d上显式调用过
import android.app.Activity;
import android.os.Bundle;
class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new HelpUI.GetDataFromDBInAsyncMode(this, "Select * FROM YourTable").execute();
}
}