【构建Android缓存模块】(三)Controller & 异步图片加载

转自: http://blog.csdn.net/floodingfire/article/details/8249122


声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) 

http://my.oschina.net/ryanhoo/blog/93432

    节课我们学习了缓存模块的实现, 缓存分做两份:Memory CacheFile Cache。方法也很简单,分别是:

  • 存储文件
  • 按唯一key值索引文件
  • 清空缓存

    区别在于内存缓存读取优先,应为它读写的速度更快。但是考虑到内存限制,退而选用文件存储,分担内存缓存的压力。

    原理非常简单,在第一课中已经详细分析了。那么要怎么才能将这个缓存模块与UI模块的显示关联起来呢?在这里我们需要一个控制器,掌管数据流向和读写,同时控制UI的显示。

    那么这个控制器需要以下的元素:

  • 内存缓存
  • 硬盘缓存
  • 异步任务处理
  • 控制UI显示
1 //caches
2 private MemoryCache memoryCache;
3 private FileCache fileCache;
4 //Asynchronous task
5 private static AsyncImageLoader imageLoader;
    Memory CacheFile Cache在上一课中有具体的实现,这里有一个异步的任务处理器—— AsyncImageDownloader,它用来在后台下载数据,完成下载后存储数据到缓存中,并更新UI的显示   。让我们来看看它是如何实现的:
01 class AsyncImageDownloader extends AsyncTask<Void, Void, Bitmap>{
02     private ImageView imageView;
03     private String fileName;
04      
05     public AsyncImageDownloader(ImageView imageView, String fileName){
06         this.imageView = imageView;
07         this.fileName = fileName;
08     }
09      
10     @Override
11     protected void onPreExecute() {
12         super.onPreExecute();
13         imageView.setImageResource(R.drawable.placeholder);
14     }
15      
16     @Override
17     protected Bitmap doInBackground(Void... arg0) {
18         String url = Utils.getRealUrlOfPicture(fileName);
19         HttpResponse response = new HttpRetriever().requestGet(url, null);
20         Log.i(TAG, "url: " + url);
21         Log.i(TAG, "respone: " + response);
22         InputStream in = null;
23         try {
24             if(response != null && response.getEntity() != null)
25                 in = response.getEntity().getContent();
26         catch (IllegalStateException e) {
27             e.printStackTrace();
28             return null;
29         catch (IOException e) {
30             e.printStackTrace();
31             return null;
32         }
33          
34         //TODO to be optimized: adjust the size of bitmap
35         return BitmapFactory.decodeStream(in);
36     }
37      
38     @Override
39     protected void onPostExecute(Bitmap result) {
40         super.onPostExecute(result);
41         if(result != null && imageView != null)
42             imageView.setImageBitmap(result);
43          
44         //TODO cache the bitmap both in sdcard & memory
45         memoryCache.put(fileName, result);// key is a unique token, value is the bitmap
46          
47         fileCache.put(fileName, result);
48     }
49 }

    可以看到这个类的构造函数需要两个参数,分别是文件名和对应要显示的ImageView,那么在任务开始的时候,可以为该ImageView设置未下载状态的图片,然后下载完成后更新UI。

    需要提醒的是,这里的唯一key值,我使用的是文件名,因为我接收到的文件名是唯一的。猿媛们也可以根据自己的需求,设计自己的唯一key值算法。

    接下来,我们需要读用key值索引相应的Bitmap:

01 public Bitmap getBitmap(String key){
02     Bitmap bitmap = null;
03     //1. search memory
04     bitmap = memoryCache.get(key);
05      
06     //2. search sdcard
07     if(bitmap == null){
08         File file = fileCache.getFile(key);
09         if(file != null)
10             bitmap = BitmapHelper.decodeFile(file, null);
11     }
12      
13     return bitmap;
14 }

    读取到Bitmap后进行显示:

01 public void displayBitmap(ImageView imageView, String fileName){
02     //no pic for this item
03     if(fileName == null || "".equals(fileName))
04         return;
05      
06     Bitmap bitmap = getBitmap(fileName);
07     //search in cache, if there is no such bitmap, launch downloads
08     if(bitmap != null){
09         imageView.setImageBitmap(bitmap);
10     }
11     else{
12         Log.w(TAG, "Can't find the file you required.");
13         new AsyncImageDownloader(imageView, fileName).execute();
14     }
15 }
    到这里,一个简单的缓存框架就搭建成功了。它简洁有效,但是非常单薄,似乎不够强大,需要你们根据自己的需求进行修改。另外它本来的目的就是用于演示,理解这个以后,我们再来看Google的BitmapFun。

    不过,我将它应用在一个小项目中,性能还不错。对于小项目的需求,应该是够的。

    最后,附上使用方法,以及整个类的源码。

    使用方法:

1 AsyncImageLoader imageLoader = AsyncImageLoader.getInstance(this);、
2 imageLoader.displayBitmap(imageView, fileName);

    源码:

001 public class MainActivity extends Activity {
002  
003     private static final String TAG = "MainActivity";
004      
005     private static final boolean DEBUG = true;
006      
007     private static String url = Config.SERVER_URL + Config.REQUEST_UPDATE;
008      
009     //views
010     private ListView listViewNews;
011      
012     //controller
013     private AsyncImageLoader imageLoader;
014     private NewsAdapter newsAdapter;
015      
016     /**
017      * Adapter for the pictures
018      * */  
019     private class ViewHolder{
020         public ImageView imageViewPic;
021         public TextView textViewNewsEN;
022         public TextView textViewNewsZN;
023         public TextView textViewTags;
024         public TextView textViewLikeIt;
025         public TextView textViewCreationDate;
026     }
027      
028     class NewsAdapter extends ArrayAdapter<SoftNews>{
029  
030         private ArrayList<SoftNews> newsList;
031         private LayoutInflater inflater;
032          
033         private boolean notEmpty(String str){
034             if(str != null && !"".equals(str))
035                 return true;
036              
037             return false;
038         }
039          
040         public NewsAdapter(Context context, int resource, ArrayList<SoftNews> newsList) {
041             super(context, resource, newsList);
042             this.newsList = newsList;
043             this.inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
044         }
045  
046         @Override
047         public View getView(int position, View convertView, ViewGroup parent) {
048             ViewHolder holder = null;
049             if (convertView == null) {    //Prepare the view if doesn t exits
050                 convertView = inflater.inflate(R.layout.news_item, null);
051                 holder = new ViewHolder();
052                  
053                 holder.imageViewPic = (ImageView)convertView.findViewById(R.id.imageViewPic);
054                 holder.textViewNewsEN = (TextView)convertView.findViewById(R.id.textViewNewsEN);
055                 holder.textViewNewsZN = (TextView)convertView.findViewById(R.id.textViewNewsZN);
056                 holder.textViewTags = (TextView)convertView.findViewById(R.id.textViewTags);
057                 holder.textViewLikeIt = (TextView)convertView.findViewById(R.id.textViewLikeIt);
058                 holder.textViewCreationDate = (TextView)convertView.findViewById(R.id.textViewCreationDate);
059                  
060                 convertView.setTag(holder);
061             else
062                 holder = (ViewHolder)convertView.getTag();
063              
064             //set data
065             SoftNews news = newsList.get(position);
066             if(news != null){
067                 if(notEmpty(news.getPicture())){
068                     holder.imageViewPic.setVisibility(View.VISIBLE);
069                     imageLoader.displayBitmap(holder.imageViewPic, news.getPicture());
070                 }
071                 else
072                     holder.imageViewPic.setVisibility(View.GONE);
073                 if(notEmpty(news.getNews_en()))
074                     holder.textViewNewsEN.setText(news.getNews_en());
075                 if(notEmpty(news.getNews_zn()))
076                     holder.textViewNewsZN.setText(news.getNews_zn());
077                 if(news.getCreationDate() != null)
078                     try {
079                         holder.textViewCreationDate.setText(Utils.dateToString(news.getCreationDate(), Config.NEWS_ITEM_DATE_FORMAT));
080                     catch (Exception e) { e.printStackTrace();    }
081                 if(notEmpty(news.getTags()))
082                     holder.textViewTags.setText(news.getTags());
083             }
084             return convertView;
085         }
086     }
087      
088     private void testUpdates() throws IllegalStateException, IOException {
089         InputStream in = null;
090         if(DEBUG){
091             //for test
092             in = Utils.readSampleJson(getResources());
093         }else{
094             // for real function
095             HttpRetriever httpRetriever = new HttpRetriever();
096             HttpResponse httpResponse = httpRetriever.requestGet(url, null);
097             in = httpResponse.getEntity().getContent();
098         }
099         ArrayList<SoftNews> newsList = Utils.readNewsFromJsonStream(in);
100         for(int i=0; newsList != null && i<newsList.size(); i++){
101             //Log.i(TAG, "news " + i + ": " + newsList.get(i).toString());
102             newsAdapter.add(newsList.get(i));
103         }
104          
105         //refresh ui
106         newsAdapter.notifyDataSetChanged();
107     }
108      
109     @Override
110     public boolean onCreateOptionsMenu(Menu menu) {
111         getMenuInflater().inflate(R.menu.activity_main, menu);
112         return true;
113     }
114      
115     @Override
116     public boolean onOptionsItemSelected(MenuItem item) {
117         switch (item.getItemId()) {
118         case R.id.menu_clear_cache:
119             imageLoader.clearCache();
120             break;
121  
122         default:
123             break;
124         }
125         return super.onOptionsItemSelected(item);
126     }
127      
128     /** Activity life cycle */
129     @Override
130     public void onCreate(Bundle savedInstanceState) {
131         super.onCreate(savedInstanceState);
132         setContentView(R.layout.news_list);
133          
134         imageLoader = AsyncImageLoader.getInstance(this);
135          
136         //views
137         listViewNews = (ListView) findViewById(R.id.listViewNews);
138          
139         newsAdapter = new NewsAdapter(this, R.layout.news_item, new ArrayList<SoftNews>());
140         listViewNews.setAdapter(newsAdapter);
141          
142         try {
143             testUpdates();
144         catch (IllegalStateException e) {
145             e.printStackTrace();
146         catch (IOException e) {
147             e.printStackTrace();
148         }
149     }
150  
151 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值