2014-10-25 2 views
4

나는 listview에서 인터넷에서 이미지를 다운로드하고 표시하려면 Volley NetworkImageView을 사용합니다. 이제 사용할 수있는 네트워크가 없을 때 Volley NetworkImageView에 저장된 이미지를 표시하려고합니다. 내가Volley NetworkImageView를 오프라인으로 만드는 법

Entry entry = SingletonRequestQueue.getInstance(context).getRequestQueue().getCache().get(imageURL); 

entry.data가 null가 아닌 사용하는 경우 때문에 Volley 이미 키로 URL에 의해 이미지를 캐시합니다. 하지만 내 문제는 그 이미지 해상도가 높은하고 지연을 많이 만들기 때문에 내가

Bitmap b = BitmapFactory.decodeByteArray(entry.data, 0, entry.data.length); 

을 사용할 수 없습니다 나는 다시 내가 디코딩을 취소 스크롤 때 listviewasynctask 볼 작성해야하기 때문에 바퀴를 재발견 할 필요가있다

그래서 더 나은 아이디어가 단지 Volley NetworkImageView 더 네트워크가 없을 때를 보여주기 위해 자신의 DiskLRUCache을 사용할 수 있도록 몇 가지 트릭을 할 수 있습니다 ... 최고의 insample 가치를 발견하고, 메모리 캐시에 창조하는 bitmap 재활용.

아이디어가 있으십니까?

내 코드 :

public class SingletonRequestQueue { 


    private static SingletonRequestQueue mInstance; 
    private RequestQueue mRequestQueue; 
    private ImageLoader mImageLoader; 
    private static Context mCtx; 
    private LruBitmapCache mLruBitmapCache; 

    private SingletonRequestQueue(Context context) { 
     mCtx = context; 
     mRequestQueue = getRequestQueue(); 
     mLruBitmapCache = new LruBitmapCache(LruBitmapCache.getCacheSize(context)); 
     mImageLoader = new ImageLoader(mRequestQueue,mLruBitmapCache); 

    } 

    public static synchronized SingletonRequestQueue getInstance(Context context) { 
     if (mInstance == null) { 
      mInstance = new SingletonRequestQueue(context); 
     } 
     return mInstance; 
    } 

    public RequestQueue getRequestQueue() { 
     if (mRequestQueue == null) { 

      // getApplicationContext() is key, it keeps you from leaking the 
      // Activity or BroadcastReceiver if someone passes one in. 

      mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(),new OkHttpStack()); 
//   mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext()); 
     } 
     return mRequestQueue; 
    } 

    public <T> void addToRequestQueue(Request<T> req) { 
     getRequestQueue().add(req); 
    } 

    public ImageLoader getImageLoader() { 
     return mImageLoader; 
    } 

    public LruBitmapCache getLruBitmapCache() { 
     return mLruBitmapCache; 
    } 

    public void setLruBitmapCache(LruBitmapCache lruBitmapCache) { 
     mLruBitmapCache = lruBitmapCache; 
    } 


} 

내 어댑터 : 나는 DiskLRUCacheVolley을 사용하여 프로젝트를 만든

public IssueListAdapter(Context context, int resource, List<Issue> objects) { 
     super(context, resource, objects); 
     this.context = context; 
     this.mIssueList = objects; 
     mImageLoader = SingletonRequestQueue.getInstance(context).getImageLoader(); 
} 

public static class ViewHolder{ 

    public NetworkImageView mNetworkImageView; 
    public TextView mFee; 
    public TextView mName; 
} 


@Override 
public View getView(int position, View convertView, ViewGroup parent) { 


    ViewHolder holder; 

    if(convertView == null){ 
     holder = new ViewHolder(); 
     LayoutInflater inflater =  
       (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 
     convertView = inflater.inflate(R.layout.gridview_issuelist_item, parent, false); 

     holder.mNetworkImageView = (NetworkImageView)convertView.findViewById(R.id.NetworkImageView_MainActivity_issue_image); 
     holder.mName = (TextView)convertView.findViewById(R.id.TextView_MainActivity_name); 
     holder.mFee = (TextView)convertView.findViewById(R.id.TextView_MainActivity_fee); 
     Utility.settingTypfaceFont(context, holder.mName); 
     Utility.settingTypfaceFont(context, holder.mFee); 

     convertView.setTag(holder); 

    }else{ 
     holder = (ViewHolder)(convertView.getTag()); 
    } 

    final Issue issue = mIssueList.get(position); 
    holder.mName.setText(issue.getTitle()); 
    holder.mFee.setText(String.valueOf(issue.getFee())); 
    String imageURL = issue.getPublicCover(); 

    holder.mNetworkImageView.setImageUrl(imageURL, mImageLoader); 
    holder.mNetworkImageView.setDefaultImageResId(R.drawable.placeholder2);; 

    /* 
    Entry entry = SingletonRequestQueue.getInstance(context).getRequestQueue().getCache().get(imageURL); 
    if(entry != null && entry.data != null){ 
     byte[] imageByte = entry.data; 
     loadBitmap(imageByte, holder.mNetworkImageView,imageURL); 
    }else{ 
     holder.mNetworkImageView.setImageUrl(imageURL, mImageLoader); 
    }*/ 

    return convertView; 
} 

@Override 
public int getCount() { 
    if(mIssueList != null){ 
     return mIssueList.size();   
    } 
    else{ 
     return 0; 
    } 
} 

public List<Issue> getIssueList() { 
    return mIssueList; 
} 

} 
+0

' NetworkImageView'는 개발자가 만든 메모리 캐시와 Volley 자체에서 만든 디스크 캐시의 2 단계 캐시를 사용합니다. 이미지가 메모리 캐시에서 먼저 체크 된 다음 디스크 캐시가 발견되지 않으면 네트워크 요청 만 배치됩니다. 따라서 오프라인 모드에서 작동하게하려면 추가 작업이 필요 없습니다. 문제가 있는지 코드를 공유 할 수 있습니까? –

+0

@ManishMulimani 귀하의 도움에 많은 감사드립니다, 내 질문을 업데이 트했습니다. 문제는 내가 애플 리케이션을 닫고 이미지의 자리 표시 자만 다시 열면 이미지 아래의 텍스트가 모두 제대로로드되지만 이미지는 그렇지 않다는 것입니다. 감사합니다. – mmlooloo

+0

ImageLoader가 이미 getRequestQueue(). getCache()와 같지 않은 캐시를 체크 한 다음 ImageLoader를 다시 구현하면 Context 멤버를 추가하고 캐시 된 이미지를 확인하면 ImageLoader는 이미 isCached (String requestUrl, int maxWidth, int maxHeight) 귀하의 질문에 첫 번째 줄을 사용하십시오. src code ImageLoader : https://android.googlesource.com/platform/frameworks/volley/+/master/src/com/android/volley/toolbox/ImageLoader.java – Yazan

답변

2

은 그냥 자신의 HttpHeaderParser 여기

를 사용하여 Cached.Entry을 변경할 수 있습니다 만료 BasicNetwork 클래스

if (!ConnectivityUtils.isNetworkEnabled(CardApplication.getContext()) && request instanceof ImageRequest) { 
      VolleyLog.e("Cached response", "No Network Connectivity for Url=", request.getUrl()); 
      return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, 
        request.getCacheEntry().data, responseHeaders, true); 
     } 

과 데이터 요청이 줄을 추가 구체적으로 일을 설명 Link입니다

+0

링크는 어디에 있습니까? – mmlooloo

+0

http://mobilewebwizard.in/2015/01/offline-caching-with-volley-using-cache-controlled-header/ –

+0

사실 환영합니다. 데이터 요청을 캐시해서는 안됩니다. 요청 instanceof ImageRequest가 추가 된 이유는 –

1

안녕 @mmlooloo. 내 저장소 DiskLRUCache using Volley의 링크가 있습니다. 그것이 당신이 저장된 이미지를 보여주는 데 도움이되기를 바랍니다. 감사.

+1

도움을 주셔서 감사합니다 (+1).하지만 Volley는 이미 내 이미지를 캐싱했습니다. ** ** DiskLRUCache에서 가져온 이미지를 가져 오려고합니다. 나는 NetworkImageView를 사용하여리스트 뷰와 디코딩을위한 모든 문제를 처리하지만 ... 오프라인 일 때는 효과가 없다. – mmlooloo

+1

발리는 메모리 캐시 만 사용합니다. 따라서 오프라인으로 표시하려면 자체 DiskLRU 캐시를 구현해야합니다. 내 솔루션 코드를 확인하십시오. – androidcodehunter

+2

아니, 그것은'DiskLRU' 소스 코드를 확인했습니다 :-) – mmlooloo

3

Volley/retrofit을 Android-Universal-Image-Loader /Picasso으로 사용하는 것을 선호합니다. 그림 로더 라이브러리는 실제로 이미지를로드하고 캐싱하는 데 큰 역할을했습니다.

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView); 

은 또한 당신이 애니메이션 이미지 크기를 조정하고 그들이로드하는 동안 자리를 추가 할 수 있습니다

그들은 기본적으로 한 줄의 코드로 모든 것을 처리합니다. 그것을 구현하고 캐시 구현 (같은이 LruCache)에서 그것을 확인

  • "인터넷 연결을 확인하는 방법을 안드로이드"에 대한

  • +0

    고마워 (+1) @Pedram 나는 이미 그들 모두를 알고 있었지만, 지금은 다른 도서관을 사용하고 싶지 않고 내 이미지도 3000 * 2500이고이 도서관들이이 해상도로 곤란을 겪고 있다고 생각한다 :-) – mmlooloo

    +0

    나는 생각한다. 'ListView'에 표시하기위한 미리보기 이미지와 원본 크기로 된 이미지의 두 버전이 필요합니다. 예를 들어 이미지의 미리보기 이미지를 생성하고 Picasa와 함께로드 한 다음 원하는 크기로 실제 크기 이미지를 다운로드하십시오. DiskLruCache https://github.com/JakeWharton/DiskLruCache를 사용하여 캐시하면 Volley의 DiskBasedCache 소스를 살펴 보았지만 재미 있지는 않습니다.이 스레드를 읽으십시오 http://stackoverflow.com/questions/20916478/performance-issue- with-volleys-diskbasedcache 행운을 빌어 요 – Pedram

    +0

    @mmlooloo 방법을 통해 이것을 알 수 있습니다. http://stackoverflow.com/a/26565940/2970351 – Pedram

    0
    1. 검색 인터넷. if(networkAvailable()){ getFromNetwork()} else { getFromCache()}

    논리는 괜찮아? 그럼 그냥 시도해.
    캐시 impl 클래스가 LruBitmapCache 인 것 같습니다.

    그런 다음 해당 클래스의 연결을 확인하는 방법에 대해 알려주십시오.

    public Bitmap getBitmap(String url) { 
        if(networkAvailable()/* this is your impl */){ 
        // dont use cache 
        return null; 
        } 
        return getFromCache(); // or something like that; 
    } 
    
    +0

    인터넷이 켜져 있지 않기 때문에 캐시를 사용하지 마십시오. 캐시는 성능을 높이고 가능한 경우 느린 네트워크 호출을 줄이는 것입니다. –

    2

    오프라인에서 앱, 당신은 단지 디스크 캐시 (즉. DiskBasedCache)에 의존 할 수있는 마지막 일을 다시 시작합니다. 발리의 로컬 캐시는 네트워크 데이터와 응답 헤더로 구성됩니다. 그러나이 상황에서는 Cache-Control 헤더에 초점을 맞추기 만하면됩니다.예를 들어 서버 측에서 헤더를 "Cache-Control : max-age = 604800"으로 반환하면 Volley에게 응답 리소스를 604800 초 동안 캐시합니다 (소스는 HttpHeaderParser.parseCacheHeaders()). 다음에 같은 URL의 데이터를 검색 할 때 다음에 캐시 만료 시간을 초과했는지 확인하고 네트워크 또는 로컬에서 검색을 결정합니다.

    귀하의 설명에 따라 서버 측에서 Cache-Control:must-revalidate|proxy-revalidate|no-cache|no-store과 같은 값을 제공한다고 가정합니다. 따라서 오프라인 일 때 마지막으로 검색 한 데이터를 다시 사용할 수 없습니다.

    지금은 질문이 있습니다. 캐시 만료 시간을 조정할 수있게되면 그 시간을 충분히 큰 값으로 늘릴 수 있으므로 오프라인에서 데이터를 사용할 수 있습니다.

    불행히도 Volley는이 작업을 지원하지 않습니다. 따라서 서버 측에서 실현 가능한 최대 연령을 제공하도록 만들 수 있다면?

    그렇지 않은 경우 원하는대로 다른 라이브러리로 변경하십시오. 실제로 친구가 될 수있는 사람이 있다면 Netroid입니다. 그것은 Volley에 기반을두고 몇 가지 개선점을 제시했기 때문에 현재 코드를 변경하지 않을 것입니다. 이를 통해 만료 시간을 훨씬 더 쉽게 제어 할 수 있으며 더 많은 기능이 제공 될 것입니다.

    mImageLoader = new SelfImageLoader(mRequestQueue, mLruBitmapCache) { 
        @Override 
        public void makeRequest(ImageRequest request) { 
         // you can manipulate the cache expire time with one line code. 
         request.setCacheExpireTime(TimeUnit.DAYS, 10); 
    
         // you can even according to the different request to 
         // set up the corresponding expire time. 
         if (request.getUrl().contains("/for_one_day/")) { 
          request.setCacheExpireTime(TimeUnit.DAYS, 1); 
         } else { 
          request.setCacheExpireTime(TimeUnit.DAYS, 10); 
         } 
        } 
    }; 
    

    전체 코드가 프로젝트의 샘플 모듈에 있었기 때문에 도움이 될 수 있기를 바랍니다.

    +0

    고마워요.하지만 제 질문을 신중하게 읽지는 않았다고 생각합니다. Bitmap b = BitmapFactory.decodeByteArray (entry.data, 0, entry.data.length);로 이미지를 가져올 수 있기 때문에 데이터가 이미'disckcache '에 있습니다. ' – mmlooloo

    +0

    아, 그래, 정말 죄송합니다. 정답을 얻으시기 바랍니다. ;-) – VinceStyling

    +0

    도와 주셔서 감사합니다. (+1) – mmlooloo

    0

    내가 제대로 이해 경우 NetworkImageView에서 사용 년대 ImageLoader 클래스에 제공되는 메모리 캐시는이 메모리 캐시 있다는 사실을 잃지 않고, 응용 프로그램 실행 사이에 지속 될 경우, 당신이 도움이 될.

    메모리 캐시는 네트워크가 다운 된 경우에도 정상적으로 작동 할 수 있도록 올바른 크기의 비트 맵을 유지합니다.

    아이디어는 다음과 같습니다. 앱을 종료 할 때마다 캐시의 이미지를 계속 저장하십시오. 다음에 응용 프로그램을로드 할 때 메모리 캐시를 만들 때 - 디스크에 저장된 버전이 있는지 확인하고 사용 가능한 경우 디스크에서 메모리 캐시를 채 웁니다.

    이미지를 유지할시기와 삭제할시기를 결정할 수있는 몇 가지 방법이 있습니다.

    하이브리드 메모리/디스크 캐시를 만드는 방법은 하나입니다. 그것은 정확히 메모리 캐시는 다음과 같은 차이점이 지금 작동과 동일하게 작동합니다 :

    1. 정상적인 작동과 함께 호출 될 때마다 putBitmap(), 백그라운드 스레드에서 디스크에 비트 맵의 ​​인코딩 된 버전을 저장/AsyncTask.
    2. 비트 맵이 캐시에서 제거 될 때마다 (캐시에 일종의 공간 제약이 있다고 가정 할 때) 배경 스레드의 디스크에서 해당 파일을 삭제하십시오/AsyncTask.
    3. 메모리 캐시를 만들 때마다 (응용 프로그램 당 한 번씩) 백그라운드에서 수행되어 메모리 캐시에 디스크의 사용 가능한 이미지가 채워지도록 "loadFromDisk"작업을 만듭니다.

    비트 맵의 ​​디코딩은 피할 수 없지만 크기를 줄이고 큰 비트 맵의 ​​크기를 조정해야합니다.

    이 방법이 도움이됩니까?

    +0

    "이게 도움이됩니까?" 당신이 내 질문을주의 깊게 읽지 않았기 때문에 아니오, Volley는 ** DiskLRUCache **와 메모리 캐시를 사용합니다. 내 데이터는 ** DiskLRUCache **에 있습니다. – mmlooloo

    +0

    질문을 읽었습니다. 디스크 캐시에 저장된 이미지를 너무 많이 썼기 때문에 이미지를 디코딩하고 크기를 재조정하는 것을 원하지 않습니다. 크기 조정 부분을 건너 뛰는 방법을 제안했습니다. 비트 맵을 저장하는 유일한 방법은 인코딩하는 것입니다. 따라서 디코딩은 지속성을 원한다면 벗어날 수 없습니다. 아마도 당신은 내 대답을 이해하지 못했을 것입니다. –

    +0

    내가 심하게 댓글을 달았지만 직접 해독하고 싶지는 않습니다. 해적판을 해독하길 원합니다. – mmlooloo