在Android3.0之前,很多应用程序响应性能方面有缺陷,其中比较典型的错误行为是在UI线程中执行了查询数据操作,尤其是一次性从database查出大量数据并加载到ListView里,用这种方式载入数据是最差的选择,硬件偏弱的手机会假死会儿。 其实体验最好的还属手机自带通讯录App这类应用,滑动丝般顺滑。
在Android 3.0版本之前一般的做法是用Activity提供的startManagingCursor()和stopManagingCursor(), 已经deprecated的API我们就不谈了,3.0之后取而代之的是Loader,想必Loader的使用大家都有所知道:
public class CursorLoaderListFragment extends ListFragment {
SimpleCursorAdapter mAdapter;
SearchView mSearchView;
String mCurFilter;
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.CONTACT_STATUS,
};
private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created.
String select = "((" + ContactsContract.Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(),
ContactsContract.Contacts.CONTENT_URI,
CONTACTS_SUMMARY_PROJECTION, select, null,
ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No phone numbers");
setHasOptionsMenu(true);
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
LoaderManager.getInstance(this).initLoader(0, null, mLoaderCallback);
}
public boolean onQueryTextChange(String newText) {
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
if (mCurFilter == null && newFilter == null) {
return true;
}
if (mCurFilter != null && mCurFilter.equals(newFilter)) {
return true;
}
mCurFilter = newFilter;
LoaderManager.getInstance(this).restartLoader(0, null, mLoaderCallback);
return true;
}
}
不难看出只要实现三个回调函数就能创建出一个
LoaderCallbacks
,并将此丢给LoaderManager
去initLoader或者restartLoader,initLoader是第一次查询使用的,restartLoader是二次查询使用的。简单是简单不过有个东西不知有没有发现:在此demo 中onCreateLoader()方法返回值是CursorLoader
对象,它的构造函数必须是Uri,意味着必须是基于Content Provider
实现的数据库才可以使用,可是现实项目需要ContentProvider
的不多吧,很多是纯sqlite的,为了此场景硬生生将sqlite的实现改成ContentProvider
得不偿失。
当翻看onCreateLoader()方法定义时候,发现返回值不是CursorLoader
而是Loader
,CursorLoader
只是Loader
的一个子类而已,因此转机来了:定义一个类继承Loader
并内部用Cursor实现,最终返回自定义类的对象给onCreateLoader():
public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
private Cursor lastCursor;
private Cursor queryCursor;
public SQLiteCursorLoader(Context context, Cursor cursor) {
super(context);
queryCursor = cursor;
}
/**
* Runs on a worker thread, loading in our data. Delegates the real work to concrete subclass'
* buildCursor() method.
*/
@Override
public Cursor loadInBackground() {
final Cursor cursor = queryCursor;
if (cursor != null) {
// Ensure the cursor window is filled
cursor.getCount();
}
return cursor;
}
/**
* Runs on the UI thread, routing the results from the background thread to whatever is using
* the Cursor (e.g., a CursorAdapter).
*/
@Override
public void deliverResult(final Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
final Cursor oldCursor = lastCursor;
lastCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
/**
* Starts an asynchronous load of the list data. When the result is ready the callbacks will be
* called on the UI thread. If a previous load has been completed and is still valid the result
* may be passed to the callbacks immediately. Must be called from the UI thread.
*/
@Override
protected void onStartLoading() {
if (lastCursor != null) {
deliverResult(lastCursor);
}
if (takeContentChanged() || lastCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread, triggered by a call to stopLoading().
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Must be called from the UI thread, triggered by a call to cancel(). Here, we make sure our
* Cursor is closed, if it still exists and is not already closed.
*/
@Override
public void onCanceled(final Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
/**
* Must be called from the UI thread, triggered by a call to reset(). Here, we make sure our
* Cursor is closed, if it still exists and is not already closed.
*/
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (lastCursor != null && !lastCursor.isClosed()) {
lastCursor.close();
}
lastCursor = null;
}
}
事实上并没有直接继承Loader, 而是继承的
AsyncTaskLoader
,从名字看就知道它是类似AsyncTask的原理实现的,SDK的CursorLoader
也是基于AsyncTaskLoader
实现的,当有了SQLiteCursorLoader
我们就可以用它创建LoaderManager.LoaderCallbacks<Cursor>
了:
private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created.
String sql = "SELECT * FROM TABLE_XX";
return new SQLiteCursorLoader(getActivity(), sql);
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
// ...
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
// ...
}
};
关于Android数据库方面的开发用纯SQL的确有点累,可以考虑ORM的思路,以前我也写了一个轻量级的,也一直使用中,SQLiteCursorLoader其实我提供了2个构造方法,一个如上传Cursor,另外一个是传
BuilderSupport
, 它有2个实现,分别为ConditionBuilder
和MultiTableConditionBuilder
, 通过他们可以以面向对象方式来查询数据库,一个是用于单表查询,一个用于多表查询,具体可以参考下:Light-DAO
网友评论