如何在后台线程上异步运行SQLite查询?

时间:2017-07-02 15:07:18

标签: android multithreading sqlite asynchronous android-sqlite

我有一个大型数据库,需要时间来查找所需信息。所以我决定使用RxJava使这个过程异步。

            @Override
        public void afterTextChanged(final Editable s) {
            final String query = s.toString();
            Observable.create(new Observable.OnSubscribe<Cursor>() {
                @Override
                public void call(Subscriber<? super Cursor> subscriber) {
                    subscriber.onNext(database.search(query));
                }
            }).subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Cursor>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {
                    e.printStackTrace();
                }

                @Override
                public void onNext(Cursor cursor) {
                    scAdapter.swapCursor(cursor);
                }
            });
        }

但是查询正在主线程上运行:我输入文本的EditText正在冻结。

我的问题是如何在后台线程上异步运行SQLite查询?

1 个答案:

答案 0 :(得分:0)

可能是https://developer.android.com/reference/android/app/LoaderManager.html 会适合你。

此外,这里有一个简短的实现,但这不是RxJava。

首先,您需要实现LoaderManager.LoaderCallbacks<Cursor>,通常此接口由Activity(或Fragment)实现。

onCreateLoader中,应创建并返回CursorLoader。以下是MyCursorLoader作为CursorLoader后代的示例,您可以在其中执行与数据库和查询的连接。

onLoadFinished中,您必须将光标视为查询结果。

请考虑上面提到的android.com链接。

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
protected void onResume() {
    super.onResume();

    // start loading data using LoaderManager of Activity
    // third argument only has sense in this case
    getLoaderManager().initLoader(0, null, this);
}

private static final String ACTIVITY_NAME = "main_activity";

private void treatCursorRow(Cursor cursor){
    // treat record from cursor
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // this callback is called by LoaderManager in order to obtain CursorLoader
    // here a new one loader is created
    // created loader will be processed by LoaderManager
    return new MyCursorLoader(this, ACTIVITY_NAME);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // this callback is called when loader finishes load cursor
    // you don't need to destroy loader - just tread the data
    if(data != null)
        while(data.moveToNext())
            treatCursorRow(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    // here you can do something
    // relevant to cancelling of loading data
    // in example, when you have an event that cancels current
    // loading and restarts new one
}

class MyCursorLoader extends CursorLoader {

    private static final String DATABASE_NAME = "my_database";
    private static final int DATABASE_VERSION = 1;
    private String name_param;

    public MyCursorLoader(Context context, String activity_name) {
        super(context);
        name_param = activity_name;
    }

    @Override
    public Cursor loadInBackground() {
        // assuming, that we have implemented SQLiteOpenHelper
        // to treat sqlite-database
        MyDatabaseHelper dbh = new MyDatabaseHelper(
                MainActivity.this,
                DATABASE_NAME,
                null,
                DATABASE_VERSION
        );
        return dbh.getWritableDatabase().rawQuery(
                "SELECT * FROM some_table WHERE name=?",
                new String[]{ name_param }
        );
    }

}

}

另一种方法是使用ContentProvider https://developer.android.com/guide/topics/providers/content-providers.html

通过这种方式,您可以分离数据层和业务逻辑。您的数据访问将被抽象为uris。 使用ContentProvider,您可以在其中定义查询并使用Uri加载数据:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    return getContentResolver().query(
            YourContentProvider.SOME_URI,
            null,
            null,
            null,
            null
    );
}

如果您拥有多个或两个数据客户(活动或片段),这是很方便的方法 - 您将使用预定义的uris而不是重复sql查询或创建许多CursorLoaders descendands

此外,如果您愿意,可以在您的应用外部使用ContentProvider