Androidネットワーク画像リクエスト+2次キャッシュ実装


序文
android学習者にとって、ネットワークリクエストに対しては必ずこのようなプロセスを経験し、HttpClientまたはHttpUrlConnectionを通じてリクエストを送信し、Handlerを通じてデータの伝達を行い、非常に面倒で、その後、Volley、OKHttpがあることを知って、私たちにネットワークリクエストの小さなツールを書いてみましょう.
画像リクエストネットワークフレームワーク
画像の要求に対して、私達は1つのキャッシュを設定する必要があって、キャッシュの策略を通じて(通って)ネットの要求を減らして、それによって電量の消耗と流量の消耗を減らして、キャッシュの策略は2級のキャッシュの策略を通じて(通って)、メモリは1級のキャッシュとして、ディスクは2級のキャッシュとして、キャッシュはLRUの方式を採用して管理して、指定の内容が得られない後に、画像を取得するためにネットワークに要求します.そう聞くと簡単そうですね.私たちは穴を一つ一つ踏んで行きます.
今URLに基づいて画像を探していますが、まずメモリから取ります.
 public Bitmap loadBitmap(String uri,int reqWidth,int reqHeight){
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if(bitmap!=null)
            return bitmap;
        try{
            bitmap = loadBitmapFromDiskCache(uri,reqWidth,reqHeight);
            if(bitmap!=null)
                return bitmap;
            bitmap = loadBitmapFromHttp(uri,reqWidth,reqHeight);
        }catch (IOException e){
            e.printStackTrace();
        }
        if(bitmap==null&&mIsDiskLruCacheCreated){
            bitmap = downloadBitmapFromUrl(uri);
        }
    }
  • loadBitmapFromMemCache:まずメモリから
  • を引き出します.
    private Bitmap loadBitmapFromMemCache(String url){
            final String key = hashKeyFormUrl(url);
            Bitmap bitmap = getBitmapFromMemCache(key);
            return bitmap;
        }

    Urlでメモリのキャッシュを検索すると、まずurlをハッシュします.ハッシュの原因は明らかです.urlには特殊な文字があり、私たちの使用に影響を与える可能性があります.だから、私たちは一般的にMD 5値をkeyとして採用しています.この関数では見ないで、今その役割を知っていればいいです.私たちが関心を持っているポイントはgetBitmapFromMemcache()であり,この方法をどのように実現するかである.
     private Bitmap getBitmapFromMemCache(String key){
            return mMemoryCache.get(key);
        }

    あるオブジェクトから取りに来ました.これがメモリを管理するためのLruCacheです.このオブジェクトを作成するにはどうすればいいですか.作成するときは、sizeOfメソッドを書き換える必要があります.
    mMemoryCache = new LruCache(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getRowBytes() * value.getHeight() / 1024;
                }
            };

    明らかに、putメソッドで私たちの画像をキャッシュすることができます.ここで、メモリキャッシュからどのように画像を取るかについて説明します.そして、メモリに画像が見つからないときにディスクのキャッシュから画像を検索します.
  • loadBitmapFromDiskCacheディスクから
  • をロード
     private Bitmap loadBitmapFromDiskCache(String url,int reqWidth,int reqHeight) throws IOException{
            if(Looper.myLooper()==Looper.getMainLooper()){
                Log.w(TAG,"load bitmap from the UI Thread is not recommended!");
            }
            if(mDiskLruCache ==null){
                return null;
            }
            Bitmap bitmap = null;
            String key = hashKeyFormUrl(url);
            DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
            if(snapshot!=null){
                FileInputStream fileInputStream = (FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                bitmap = mBitmapHelper.decodeBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
                if(bitmap!=null){
                    addBitmapToMemoryCache(key,bitmap);
                }
            }
            return bitmap;
        }

    まず、メインスレッドであるかどうかを判断し、ディスクからファイルを読み取り、メインスレッドでの読み取りを推奨せず、ANRを招きやすい.次に私たちのDisKLruCacheの実現を見て、次の文章で分析します.ディスクから画像が見つからない場合は、ネットワークリクエストを開始します.
  • loadBitmapFromHttpネットワークから画像
  • を取得
    private Bitmap loadBitmapFromHttp(String url,int reqWidth,int reqHeight) throws IOException{
            if(Looper.myLooper()==Looper.getMainLooper()){
                throw new RuntimeException("can not visit network from UI thread");
            }
            if(mDiskLruCache == null){
                return null;
            }
            String key = hashKeyFormUrl(url);
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);
            if(editor!=null){
                OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
                if(downloadUrlToStream(url,outputStream)){
                    editor.commit();
                }else{
                    editor.abort();
                }
                mDiskLruCache.flush();
            }
            return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
        }
    

    まず、スレッドの検出を行い、メインスレッドにあるかどうかを判断し、downloadUrlToStream()を呼び出してネットワークからデータストリームを取得し、そのデータストリームをDiskLruCacheに渡します.つまり、ピクチャファイルをディスクキャッシュに書き込み、ディスクキャッシュからピクチャをロードします.提供されたurlに基づいてデータストリームを取得する方法を見てみましょう.
  • downloadUrlToStream
  •  //          
        public boolean downloadUrlToStream(String urlString,OutputStream outputStream){
            HttpURLConnection urlConnection = null;
            BufferedOutputStream out = null;
            BufferedInputStream in = null;
            try{
                final URL url = new URL(urlString);
                urlConnection = (HttpURLConnection)url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
                out = new BufferedOutputStream(outputStream,IO_BUFFER_SIZE);
                int b;
                while((b=in.read())!=-1){
                    out.write(b);
                }
                return true;
            }catch (IOException e){
                Log.e(TAG,"downloadBitmap failed"+e);
            }finally {
                if(urlConnection!=null){
                    urlConnection.disconnect();
                }
                IOUtils.close(out);
                IOUtils.close(in);
            }
            return false;
        }

    私たちはこの関数に必要な2つのパラメータを伝えました.url、1つは出力ストリームです.私たちはUrlConnectionを通じてネットワークとの接続を取得し、データストリームを取得し、ストリームに基づいて読み取り、その後、私たちの出力ストリームに書き込みました.Javaの研究が深くなければ、ストリームが聞こえるかもしれません.書き込みがぼやけています.その底辺の細部も知りたいので、焦らないで、後続の文章は続けて話します.これにより、ディスクキャッシュにデータを書き込むことができます.私たちの最初に戻って、私たちは今回の湖の画像の後ろについて判断しました.あなたは疑問を感じるかもしれませんが、どうしてこのような操作をしなければならないのですか.100%でネットから画像を手に入れることができるのではないでしょうか.実はそうではありません.この時、私たちはネットワークのロードなどの各方面の問題で状況が発生した可能性があります.この時、私たちはネットワークからロードを再開することを選択しました.
    //  Url    
        private Bitmap downloadBitmapFromUrl(String urlString){
            Bitmap bitmap = null;
            HttpURLConnection urlConnection = null;
            BufferedInputStream in = null;
            try{
                final URL url = new URL(urlString);
                urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);
                bitmap = BitmapFactory.decodeStream(in);
            }catch (final IOException e){
    
            }finally {
                if(urlConnection!=null){
                    urlConnection.disconnect();
                }
                IOUtils.close(in);
            }
            if(bitmap!=null)
            
            return bitmap;
        }

    このような画像キャッシュのフレームワークは終わりました.もちろん、ネットワークからロードされた画像は、メモリにすべてロードすることはできません.お腹が空いている人は、そのサイズに応じて表示の処理、処理方法をする必要があります.画像の幅の高さを取得し、必要な幅の高さに合わせてスケーリングし、そのプロパティを変更した後、Bitmapのプロパティを設定します.ボリュームを小さくします.
    public static Bitmap decodeBitmapFromFileDescriptor(FileDescriptor fd,int reqWidth,int reqHeight){
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFileDescriptor(fd,null,options);
            options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFileDescriptor(fd,null,options);
        }
    
        public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
            if(height>reqHeight||width>reqHeight){
                final int halfHeight = height/2;
                final int halfWidth = width/2;
                while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
                    inSampleSize *=2;
                }
            }
            return inSampleSize;
        }

    ここでは、ImageViewがViewをバインドすることをサポートする別の方法を提供します.
     //      
        public void bindBitmap(final String uri,final ImageView imageView,final int reqWidth,final int reqHeight){
            imageView.setTag(uri);
            Bitmap bitmap = loadBitmapFromMemCache(uri);
            if(bitmap!=null){
                imageView.setImageBitmap(bitmap);
                return;
            }
            Runnable loadBitmapTask = new Runnable() {
                @Override
                public void run() {
                    Bitmap bitmap = loadBitmap(uri,reqWidth,reqHeight);
                    if(bitmap!=null){
                        LoaderResult result = new LoaderResult(imageView,uri,bitmap);
                        //get the message from messge pool avoid to create new message
                        mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result).sendToTarget();
                    }
                }
            };
            THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
        }

    この方法は、まずメモリから検索し、見つかったら設定し、見つからなかったらネットワークから要求を開始し、Runnableを作成し、上記のメモリ、ネットワークからの要求をカプセル化し、スレッドプールに捨てて処理します.このとき問題が発生します.スレッドプールに捨てた後、UIスレッドとはスレッドではありません.このとき、スレッドの切り替えを行い、viewを操作したり、結果を伝えたりする方法が必要です.結果クラスをカプセル化し,我々のviewと結果を結果クラスにカプセル化した.
    private static class LoaderResult{
            public ImageView imageView;
            public String uri;
            public Bitmap bitmap;
    
            public LoaderResult(ImageView imageView,String uri,Bitmap bitmap){
                this.imageView = imageView;
                this.uri = uri;
                this.bitmap = bitmap;
            }
        }

    結果として,メッセージ送信方式でメインスレッドに渡し,Handlerで処理する.処理方式
    //  handler    
        private Handler mMainHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                LoaderResult result = (LoaderResult)msg.obj;
                ImageView imageView = result.imageView;
                //imageView.setImageBitmap(result.bitmap);
                String uri = (String) imageView.getTag();
                if (uri.equals(result.uri)) {
                    imageView.setImageBitmap(result.bitmap);
                }else{
                    //set a default background
                }
            }
        };

    ここでは、ImageViewにタグを付けるタグを設定し、それを判断して画像を上に設定します.これで私たちの画像リクエストのフレームワークが書き終わりました.もちろん、いくつかの最適化が必要です.次に,通常のネットワーク要求(非ピクチャ)ライブラリのパッケージ化を行う.次に、2つのキャッシュツールクラスのプロファイルです.