利用CursorLoader讓你的數據加載快人一步
引子
前段時間做了一個類似通訊錄側邊導航功能,實現起來蠻簡單,網上也有大量現成的demo,但是最近突然啃爹的測試,說有些數據量大的用戶反映嚴重卡頓,我的實現跟網上的一樣,大都千遍一律,實際問題很多
問題描述
1,關于加載延遲,沒有圖片可能大家沒有直觀的感受,大家可以想象下,就是直接加載2000條以上數據,不加排序什么,iOS是打開頁面秒顯,Android即使放在內存中加載,也要延遲3秒左右,不同手機性能可能有差距。
經過分析,可能是以下兩點問題
1,可能是大量數據生成字母索引和排序導致的卡頓
2,一次性加載的數據量太多,導致展示緩慢
尋找解決方案
1,索引和排序問題 ,優化方案是存入數據庫前預先生成字母索引
2,排序,從數據庫查詢出來時搜索出來時預先排序
3,一次性加載太多導致延遲空白,可以做一次性分頁加載
一次性分頁加載過程中如果點擊頁面會出現延遲進入下個界面的問題。并且左邊的字母索引不能一次性全顯示,于是找了下官方通訊錄的源碼
官方實現方式
結果發現官方使用的是LoaderManager+cursor+cursorAdapter的方式,官方的源碼看上去很復雜,但是它是一次性加載完所有數據然后返回一個Cursor,cursorAdapter利用負責展示數據,腦海中展示2個疑問,
1,cursor一次性加載所有數據不會延遲嗎?
我有嘗試將所有數據放在內存中,adapter加載的時候還是會延遲。
但是官方的通訊錄在大數據量下加載非常順滑
原來數據庫返回Cursor的時候并沒有真正去查詢,打比方你普通加載99999條數據需要1000ms,返回cursor 只會耗費一秒。
CursorAdapter在顯示的時候才會去加載數據
/** * @see android.widget.ListAdapter#getView(int, View, ViewGroup) */
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
2,在查詢所有數據的同時,加載了所有字母索引,例如我的
select sortLetters,COUNT(*) as count from member GROUP BY sortLetters order by upper(sortLetters) asc
官方的有點復雜,這個索引還加了緩存
3,保存字母和字母所占數量
labels = new String[numLabels];counts = new int[numLabels];
for (int i = 0; i < numLabels; i++) {
cursor.moveToNext();
labels[i] = cursor.getString(0);
counts[i] = cursor.getInt(1);
}
4,根據字母和數量得到字母position
mSections = sections;
mPositions = new int[counts.length];
int size = counts.length;
int position = 0;
for (int i = 0; i < size; i++) {
if (TextUtils.isEmpty(mSections[i])) {
mSections[i] = "#";
}
mPositions[i] = position;
position += counts[i];}mCount = position;
4,根據sectionIndex 獲取postion
public int getPositionForSection(int sectionIndex) {
if (mSections == null || mPositions == null) {
return -1;
}
if (sectionIndex < 0 || sectionIndex >= mSections.length) {
return -1;
}
return mPositions[sectionIndex];
}
5,根據字母獲取Position
public int getPositionForTitle(String title){
for (int i = 0; i < mSections.length; i++) {
if(mSections[i].equals(title)){
int position = getPositionForSection(i);
MLog.e("test","getPositionForTitle" + position);
return position;
}
}
return -1;
}
根據postion獲取字母所在Section
@Override
public int getSectionForPosition(int position) {
if (mSections == null || mPositions == null) {
return -1;
}
if (position < 0 || position >= mCount) {
return -1;
}
int index = Arrays.binarySearch(mPositions, position);
return index;
}
修改過后,進入頁面就能立即看到數據,以前即使在內存中直接加載,也會有幾秒的空白時間
來自:http://www.jianshu.com/p/21bbd1c24b30