Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 安卓SQLite中的多线程呢?_Android_Multithreading_Sqlite - Fatal编程技术网

Android 安卓SQLite中的多线程呢?

Android 安卓SQLite中的多线程呢?,android,multithreading,sqlite,Android,Multithreading,Sqlite,在我的应用程序中,我必须实现一些UI和同步服务。 它在后台运行并更新数据。同步服务不是很简单,它使用多线程 下面是我的故事: 当我开始开发这个应用程序时,我对sqlite一无所知,所以我没有在Java中使用任何线程同步。结果:我得到了很多异常,比如“SQLiteException:数据库被锁定:beginexclusive 然后,我用常规的Java synchronized(){}块同步了所有事务。一切都好了很多。但是我使用游标来实现列表的游标适配器。所以,有时我会得到相同的“SQLiteExc

在我的应用程序中,我必须实现一些UI和同步服务。 它在后台运行并更新数据。同步服务不是很简单,它使用多线程

下面是我的故事: 当我开始开发这个应用程序时,我对sqlite一无所知,所以我没有在Java中使用任何线程同步。结果:我得到了很多异常,比如“SQLiteException:数据库被锁定:beginexclusive

然后,我用常规的Java synchronized(){}块同步了所有事务。一切都好了很多。但是我使用游标来实现列表的游标适配器。所以,有时我会得到相同的“SQLiteException:数据库被锁定:beginexclusive

我最后创建了一个小的线程安全sqlite实用程序,它处理所有线程安全的东西。此外,我还必须为我的UI使用ArrayAdapter(从游标读取所有数据,并在读取后关闭它,同时同步此块)之类的东西。所以,它工作正常

但是,我不喜欢这种处理UI的方式,因为使用这种解决方案,UI变得非常慢——从游标读取一些数据相当快,但比使用游标适配器慢

那么,谁得到了这个问题的答案?
谢谢

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();
   }
}