Android圖片選擇器

爬了某網(wǎng)站的圖片邪意,放到自己App上觀賞一波。發(fā)現(xiàn)自己寫的App中的一個(gè)獲取本地的相片功能直接崩了反砌。然后就想優(yōu)化優(yōu)化雾鬼。廢話不多說,上圖宴树。

test.gif

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) 不分手

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹃祖,一起剝皮案震驚了整個(gè)濱河市溪椎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恬口,老刑警劉巖校读,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異祖能,居然都是意外死亡歉秫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門养铸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雁芙,“玉大人,你說我怎么就攤上這事钞螟⊥酶剩” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鳞滨,是天一觀的道長(zhǎng)洞焙。 經(jīng)常有香客問我,道長(zhǎng)拯啦,這世上最難降的妖魔是什么澡匪? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮褒链,結(jié)果婚禮上唁情,老公的妹妹穿的比我還像新娘。我一直安慰自己碱蒙,他們只是感情好荠瘪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赛惩,像睡著了一般哀墓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喷兼,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天篮绰,我揣著相機(jī)與錄音,去河邊找鬼季惯。 笑死吠各,一個(gè)胖子當(dāng)著我的面吹牛臀突,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贾漏,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼候学,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了纵散?” 一聲冷哼從身側(cè)響起梳码,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伍掀,沒想到半個(gè)月后掰茶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜜笤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年濒蒋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片把兔。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沪伙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出县好,到底是詐尸還是另有隱情焰坪,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布聘惦,位于F島的核電站某饰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏善绎。R本人自食惡果不足惜黔漂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禀酱。 院中可真熱鬧炬守,春花似錦、人聲如沸剂跟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曹洽。三九已至鳍置,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間送淆,已是汗流浹背税产。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辟拷。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓撞羽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親衫冻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诀紊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容