爬了某網(wǎng)站的圖片邪意,放到自己App上觀賞一波。發(fā)現(xiàn)自己寫的App中的一個(gè)獲取本地的相片功能直接崩了反砌。然后就想優(yōu)化優(yōu)化雾鬼。廢話不多說,上圖宴树。
The application may be doing too much work on its main thread.
這個(gè)坑應(yīng)該是最容易去填的策菜,但是還是要說。因?yàn)橛羞@個(gè)坑酒贬,然后會(huì)升級(jí)成另一個(gè)坑又憨。還是要說,這個(gè)坑是什么锭吨。怎么造成的蠢莺。怎么解決。
這個(gè)坑是什么零如?
The application may be doing too much work on its main thread躏将,就是提示你在主線程里面干了太多事情了锄弱。簡(jiǎn)單,就像網(wǎng)絡(luò)請(qǐng)求一樣把耗時(shí)間的任務(wù)放在異步線程去做祸憋。
為什么會(huì)造成這個(gè)坑会宪?
造成這個(gè)坑是因?yàn)椋阍谥骶€程里面做了太多的事情了蚯窥。而我這里在主線程里面做的耗時(shí)任務(wù)就是如下代碼:
public static Map<String, List<Picture>> getPicturs(Context context) {
Map<String, List<Picture>> maps = new HashMap<>();
Cursor mCursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projections,
MediaStore.Images.Media.MIME_TYPE + " = ? or " + MediaStore.Images.Media.MIME_TYPE + " = ?",
new String[]{IMAGE_JPEG, IMAGE_PNG},
MediaStore.Images.Media.DATE_ADDED + " desc");
if (mCursor == null) return maps;
try {
mCursor.moveToFirst();
while (mCursor.moveToNext()) {
int pictureID = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media._ID));
String picturePath = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
String thumbPath = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA));
Picture picture = new Picture(pictureID, picturePath, thumbPath);
String floderName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
List<Picture> datas = maps.get(floderName);
if (datas == null) {
datas = new ArrayList<>();
maps.put(floderName, datas);
}
datas.add(picture);
maps.put(floderName, datas);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
mCursor.close();
}
return maps;
}
這個(gè)方法是使用ContentProvider將手機(jī)中的圖片掃描出來掸鹅。然后放到Map里面。因?yàn)槭謾C(jī)里面的圖片比較多拦赠。所以掃描的時(shí)間比較慢巍沙。所以就造成這個(gè)warning。
怎么解決這個(gè)坑矛紫。
簡(jiǎn)單赎瞎,直接在子線程里面去處理不就好了嗎?OK 改造一下在子線程里面去處理一下颊咬。然后用Handler來試試看务甥。
new Thread(new Runnable() {
@Override
public void run() {
maps = PictruesResolver.getPicturs(PicturesActivity.this);
handler.sendEmptyMessage(1);
}
}).start();
這個(gè)時(shí)候The application may be doing too much work on its main thread是解決了,但是如果你不斷的切換界面喳篇,在進(jìn)去圖片展示的界面敞临。這樣重復(fù)操作。Leakcanary就會(huì)提示內(nèi)存泄露麸澜。這也就是剛才說的挺尿,另一個(gè)坑升級(jí)版的坑。內(nèi)存泄露介紹比較給力的文章http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1123/2047.html
就像文章里面所說的炊邦,在onDestroy中清理掉所有Messages编矾,這樣就可以解決內(nèi)存泄露.
下面提供一下我所處理的一種方案:使用IntentService進(jìn)行異步操作,操作成功就發(fā)一個(gè)廣播出來給自定義廣播馁害。然后廣播在回調(diào)自己的業(yè)務(wù)邏輯.
IntentService代碼:
public class PictureService extends IntentService {
public PictureService(String name) {
super(name);
}
public PictureService() {
this(PictureService.class.getName());
}
@Override
protected void onHandleIntent(Intent intent) {
Intent broadcastIntent = new Intent();
HashMap<String, List<Picture>> maps = (HashMap<String, List<Picture>>) PictruesResolver.getPicturs(getApplicationContext());
broadcastIntent.setAction(PictureReceiver.PictureReceiver_ACTION);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
SPUtils.put(getApplicationContext(),"picturs",new Gson().toJson(maps));
sendBroadcast(broadcastIntent);
}
}
廣播代碼:
public class PictureReceiver extends BroadcastReceiver {
public static final String PictureReceiver_ACTION = "PICTUREACTION";
private PictureProxy.PictureCallBack callBack;
public PictureReceiver(PictureProxy.PictureCallBack callBack) {
this.callBack = callBack;
}
@Override
public void onReceive(Context context, Intent intent) {
try {
String datas = (String) SPUtils.get(context, "picturs", "");
SPUtils.put(context, "picturs", "");
if (TextUtils.isEmpty(datas)) {
callBack.onFail("圖片獲取失敗");
} else {
Map<String, List<Picture>> maps = new Gson().fromJson(datas, new TypeToken<Map<String, List<Picture>>>() {
}.getType());
callBack.onSuccess(maps);
}
} catch (Exception e) {
e.printStackTrace();
callBack.onFail("圖片獲取失敗");
} finally {
}
}
}
核心代理類
public class PictureProxy {
private Context context;
private PictureReceiver receiver;
private boolean isRegister = false;
public PictureProxy(Context context) {
this.context = context;
}
/**
* 開始獲取圖片
**/
public void startPictureProxy(PictureCallBack callBack) {
Intent intent = new Intent(context, PictureService.class);
context.startService(intent);
//接受者
receiver = new PictureReceiver(callBack);
IntentFilter filter = new IntentFilter(PictureReceiver.PictureReceiver_ACTION);
filter.addCategory(Intent.CATEGORY_DEFAULT);
context.registerReceiver(receiver, filter);
isRegister = true;
}
public interface PictureCallBack {
void onSuccess(Map<String, List<Picture>> maps);
void onFail(String meesage);
}
public void destroyProxy() {
if (receiver == null || !isRegister) return;
context.unregisterReceiver(receiver);
}
}
在這里踩了一個(gè)坑:
FAILED BINDER TRANSACTION !!! (parcel size = 9655008)
本來是直接用intent傳輸數(shù)據(jù)的窄俏,但是又踩了一個(gè)地雷。因?yàn)镮ntent里面不能存放數(shù)據(jù)量>1M碘菜。然后實(shí)在沒辦法凹蜈。代碼中會(huì)有一些序列化和反序列的代碼(別打臉)
SPUtils.put(getApplicationContext(),"picturs",new Gson().toJson(maps));
Map<String, List<Picture>> maps = new Gson().fromJson(datas, new TypeToken<Map<String, List<Picture>>>() { }.getType());
后來,上了個(gè)廁所忍啸,想了一下仰坦。感覺自己有點(diǎn)小題大做了。其實(shí)我只要控制線程在Activity結(jié)束的時(shí)候计雌。把它給cancel掉不就OK了嗎悄晃?于是又有了下面的方案。
compositeSubscription = new CompositeSubscription();
Subscription subscription = Observable.create(new Observable.OnSubscribe<Map<String, List<Picture>>>() {
@Override
public void call(Subscriber<? super Map<String, List<Picture>>> subscriber) {
subscriber.onNext(PictruesResolver.getPicturs(context));
}
}).compose(RxUtils.<Map<String, List<Picture>>>transformerShedule())
.subscribe(new Action1<Map<String, List<Picture>>>() {
@Override
public void call(Map<String, List<Picture>> maps) {
onSuccess(maps);
}
});
compositeSubscription.add(subscription);
記得在onDestory中調(diào)用compositeSubscription.unsubscribe()停止異步操作凿滤。
@Override
protected void onDestroy() {
super.onDestroy();
// pictureProxy.destroyProxy();
if (compositeSubscription != null) {
compositeSubscription.unsubscribe();
}
RefWatcher refWatcher = BaseApplication.getRefWatcher(this);
refWatcher.watch(this);
}
附上git鏈接:https://github.com/BelongsH/Pictures
寫完的時(shí)候妈橄,突然聽到了by2的歌鼠渺。真棒!>煜浮!
當(dāng)我 緊握你的手
你是耀眼的星火
就讓夢(mèng)想編織王者世界
照亮整個(gè)宇宙
當(dāng)你 握緊我的手
我變勇敢的星火
和你一起閃耀 到世界盡頭
和你一起到永遠(yuǎn) 不分手