How to run SQLite query asynchronously on backgrou

2019-07-29 01:25发布

I have a big database which takes time to find needed information. So I decided to use RxJava to make this process asynchronous.

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

But query is running on main thread: EditText where I entering text is freezing.

My question is how to run SQLite query asynchronously on background thread?

1条回答
Melony?
2楼-- · 2019-07-29 01:59

Probably this https://developer.android.com/reference/android/app/LoaderManager.html will suite for you.

Besides, here is short implementation for you, but this is not RxJava.

Firstly, you need to implement LoaderManager.LoaderCallbacks<Cursor>, and usually this interface is implemented by Activity (or Fragment).

In onCreateLoader, a CursorLoader should be created and returned. Here is just an example with MyCursorLoader as descendant of CursorLoader, where you can perform connection to database and queries.

In onLoadFinished you have to treat cursor with results of query.

Please, consider the link to android.com, mentioned above.

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

}

}

Another way, is in using of ContentProvider https://developer.android.com/guide/topics/providers/content-providers.html .

In this way you can separate data layer and business logic. Your data access will be abstracted to uris. Using ContentProvider, you define your queries within it and load data using Uri:

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

This is convenient way if you have more than one or two customers of your data (Activities or Fragments) - you will use just predefined uris rather repeating sql queries or creating many CursorLoaders descendands.

Moreover, ContentProvider may be used from outside your app if you want.

查看更多
登录 后发表回答