Android之Looper、Handler、Message、MessageQueue应用篇

来源:互联网 发布:一致性hash算法 编辑:程序博客网 时间:2024/05/29 18:30

简介

      上一篇文章介绍了Handler、Message、MessageQueue等Android线程交互方面的内容,Android之理解Looper、Handler、Message、MessageQueue。下面开始实践,学习如何去使用以及应用到程序里面。

实例

      在这里使用ListView作为异步下载图片的环境。

      1、Handle+Runnable

           实现思路是:

          

[java] view plaincopy
  1. package com.example.handlerloadiage;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.URL;  
  5. import android.app.Activity;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.util.Log;  
  10. import android.view.LayoutInflater;  
  11. import android.view.Menu;  
  12. import android.view.View;  
  13. import android.view.ViewGroup;  
  14. import android.widget.BaseAdapter;  
  15. import android.widget.ImageView;  
  16. import android.widget.ListView;  
  17.   
  18. public class Handler_Runnable_Mode extends Activity {  
  19.   
  20.     private ListView listview;  
  21.   
  22.     @Override  
  23.     protected void onCreate(Bundle savedInstanceState) {  
  24.         super.onCreate(savedInstanceState);  
  25.         setContentView(R.layout.activity_handlerimageloader);  
  26.   
  27.         listview = (ListView) findViewById(R.id.listview);  
  28.         listview.setAdapter(new MyAdapter());  
  29.     }  
  30.   
  31.     private class MyAdapter extends BaseAdapter {  
  32.   
  33.         public MyAdapter() {  
  34.   
  35.         }  
  36.   
  37.         @Override  
  38.         public int getCount() {  
  39.             // TODO Auto-generated method stub  
  40.             return 100;  
  41.         }  
  42.   
  43.         @Override  
  44.         public Object getItem(int position) {  
  45.             // TODO Auto-generated method stub  
  46.             return null;  
  47.         }  
  48.   
  49.         @Override  
  50.         public long getItemId(int position) {  
  51.             // TODO Auto-generated method stub  
  52.             return 0;  
  53.         }  
  54.   
  55.         @Override  
  56.         public View getView(int position, View convertView, ViewGroup parent) {  
  57.             if (convertView == null) {  
  58.                 convertView = LayoutInflater.from(getApplicationContext())  
  59.                         .inflate(R.layout.list_item, null);  
  60.             }  
  61.             final ImageView image = (ImageView) convertView  
  62.                     .findViewById(R.id.imageview);  
  63.             final String imageURL = "http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";  
  64.   
  65.             Handler handler = new Handler();  
  66.             handler.post(new Runnable() {  
  67.                 public void run() {  
  68.                     Drawable drawable = null;  
  69.                     try {  
  70.                         drawable = Drawable.createFromStream(  
  71.                                 new URL(imageURL).openStream(), "image.jpg");  
  72.                     } catch (IOException e) {  
  73.                         Log.d("test", e.getMessage());  
  74.                     }  
  75.                     if (drawable == null) {  
  76.                         Log.d("test""null drawable");  
  77.                     } else {  
  78.                         Log.d("test""not null drawable");  
  79.                     }  
  80.                     if (drawable == null) {  
  81.                         image.setImageResource(R.drawable.ic_launcher);  
  82.                     } else {  
  83.                         image.setImageDrawable(drawable);  
  84.                     }  
  85.                 }  
  86.             });  
  87.             return convertView;  
  88.         }  
  89.   
  90.     }  
  91.   
  92.     @Override  
  93.     public boolean onCreateOptionsMenu(Menu menu) {  
  94.         getMenuInflater().inflate(R.menu.main, menu);  
  95.         return true;  
  96.     }  
  97.   
  98. }  
      效果如下:

                                                          

      快速滑动的过程中,还是出现了ANR的现象。

                                                           


     这是因为handler.post(new Runnable()这个方法,并没有开启一个新的线程,他还是在UI主线程中,所以导致出现ANR现象。


     2、Handler+Runnable+Message

    实现思路:

           1:在UI线程中启动一个线程,让这个线程去下载图片。

           2:图片完成下载后发送一个消息去通知UI线程

           3:UI线程获取到消息后,更新UI。

     实现代码:

[java] view plaincopy
  1. package com.example.handlerloadiage;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.URL;  
  5. import android.app.Activity;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.os.Message;  
  10. import android.util.Log;  
  11. import android.view.LayoutInflater;  
  12. import android.view.Menu;  
  13. import android.view.View;  
  14. import android.view.ViewGroup;  
  15. import android.widget.BaseAdapter;  
  16. import android.widget.ImageView;  
  17. import android.widget.ListView;  
  18.   
  19. public class Handler_Runnable_Mode extends Activity {  
  20.   
  21.     private ListView listview;  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_handlerimageloader);  
  27.   
  28.         listview = (ListView) findViewById(R.id.listview);  
  29.         listview.setAdapter(new MyAdapter());  
  30.     }  
  31.   
  32.     private class MyAdapter extends BaseAdapter {  
  33.   
  34.         public MyAdapter() {  
  35.   
  36.         }  
  37.   
  38.         @Override  
  39.         public int getCount() {  
  40.             // TODO Auto-generated method stub  
  41.             return 100;  
  42.         }  
  43.   
  44.         @Override  
  45.         public Object getItem(int position) {  
  46.             // TODO Auto-generated method stub  
  47.             return null;  
  48.         }  
  49.   
  50.         @Override  
  51.         public long getItemId(int position) {  
  52.             // TODO Auto-generated method stub  
  53.             return 0;  
  54.         }  
  55.   
  56.         @Override  
  57.         public View getView(int position, View convertView, ViewGroup parent) {  
  58.             if (convertView == null) {  
  59.                 convertView = LayoutInflater.from(getApplicationContext())  
  60.                         .inflate(R.layout.list_item, null);  
  61.             }  
  62.             final ImageView image = (ImageView) convertView  
  63.                     .findViewById(R.id.imageview);  
  64.             final String imageURL = "http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";  
  65.   
  66.             final Handler handler = new Handler(){  
  67.   
  68.                 @Override  
  69.                 public void handleMessage(Message msg) {  
  70.                       
  71.                     super.handleMessage(msg);  
  72.                     Drawable d=(Drawable) msg.obj;  
  73.                     if (d == null) {  
  74.                         image.setImageResource(R.drawable.ic_launcher);  
  75.                     } else {  
  76.                         image.setImageDrawable(d);  
  77.                     }  
  78.                 }  
  79.                   
  80.             };  
  81.             handler.post(new Runnable() {  
  82.                 public void run() {  
  83.                     Drawable drawable = null;  
  84.                     try {  
  85.                         drawable = Drawable.createFromStream(  
  86.                                 new URL(imageURL).openStream(), "image.jpg");  
  87.                         Message message=handler.obtainMessage();  
  88.                         message.obj=drawable;  
  89.                         handler.sendMessage(message);  
  90.                           
  91.                     } catch (IOException e) {  
  92.                         Log.d("test", e.getMessage());  
  93.                     }  
  94.                     if (drawable == null) {  
  95.                         Log.d("test""null drawable");  
  96.                     } else {  
  97.                         Log.d("test""not null drawable");  
  98.                     }  
  99.                       
  100.                 }  
  101.             });  
  102.             return convertView;  
  103.         }  
  104.   
  105.     }  
  106.   
  107.     @Override  
  108.     public boolean onCreateOptionsMenu(Menu menu) {  
  109.         getMenuInflater().inflate(R.menu.main, menu);  
  110.         return true;  
  111.     }  
  112.   
  113. }  
    也是会出现ANR的现象,原因和之前一样。


     3、Handler+Thread+Message

    这种模式使用了线程,所以是异步加载。

[java] view plaincopy
  1. package com.example.handlerloadiage;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.URL;  
  5. import android.app.Activity;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.os.Message;  
  10. import android.util.Log;  
  11. import android.view.LayoutInflater;  
  12. import android.view.Menu;  
  13. import android.view.View;  
  14. import android.view.ViewGroup;  
  15. import android.widget.BaseAdapter;  
  16. import android.widget.ImageView;  
  17. import android.widget.ListView;  
  18.   
  19. public class HandlerThreadMessageActivity extends Activity {  
  20.   
  21.     private ListView listview;  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_handlerimageloader);  
  27.   
  28.         listview = (ListView) findViewById(R.id.listview);  
  29.         listview.setAdapter(new MyAdapter());  
  30.     }  
  31.   
  32.     private class MyAdapter extends BaseAdapter {  
  33.   
  34.         public MyAdapter() {  
  35.   
  36.         }  
  37.   
  38.         @Override  
  39.         public int getCount() {  
  40.             return 100;  
  41.         }  
  42.   
  43.         @Override  
  44.         public Object getItem(int position) {  
  45.             return null;  
  46.         }  
  47.   
  48.         @Override  
  49.         public long getItemId(int position) {  
  50.             return 0;  
  51.         }  
  52.   
  53.         @Override  
  54.         public View getView(int position, View convertView, ViewGroup parent) {  
  55.             if (convertView == null) {  
  56.                 convertView = LayoutInflater.from(getApplicationContext())  
  57.                         .inflate(R.layout.list_item, null);  
  58.             }  
  59.             final ImageView image = (ImageView) convertView  
  60.                     .findViewById(R.id.imageview);  
  61.             final String imageURL = "http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";  
  62.   
  63.             final Handler handler = new Handler() {  
  64.   
  65.                 @Override  
  66.                 public void handleMessage(Message msg) {  
  67.                     super.handleMessage(msg);  
  68.                     Drawable d = (Drawable) msg.obj;  
  69.                     if (d == null) {  
  70.                         image.setImageResource(R.drawable.ic_launcher);  
  71.                     } else {  
  72.                         image.setImageDrawable(d);  
  73.                     }  
  74.                 }  
  75.   
  76.             };  
  77.               
  78.             Thread thread = new Thread() {  
  79.                 @Override  
  80.                 public void run() {  
  81.                     Drawable drawable = null;  
  82.                     try {  
  83.                         drawable = Drawable.createFromStream(  
  84.                                 new URL(imageURL).openStream(), "image.jpg");  
  85.                     } catch (IOException e) {  
  86.                         Log.d("test", e.getMessage());  
  87.                     }  
  88.                     // 模拟网络延时  
  89.                     // SystemClock.sleep(2000);  
  90.                     Message message = handler.obtainMessage();  
  91.                     message.obj = drawable;  
  92.                     handler.sendMessage(message);  
  93.                 }  
  94.             };  
  95.             thread.start();  
  96.             thread = null;  
  97.             return convertView;  
  98.         }  
  99.   
  100.     }  
  101.   
  102.     @Override  
  103.     public boolean onCreateOptionsMenu(Menu menu) {  
  104.         getMenuInflater().inflate(R.menu.main, menu);  
  105.         return true;  
  106.     }  
  107.   
  108. }  
        运行程序,发现图片加载速度非常快,而且滑动过程中很顺畅。但是,这不是一个很好的解决方法,因为每次下载图片图片都需要新建一个线程,如果线程非常多,那么处理器显然不能承受。所以就需要对线程数量进行规划和维护,使其在一个合理的范围内运行。

          4、Handler+ExecutorService+Message

       因为能开线程的个数毕竟是有限的,不能开很多线程,对于手机更是如此。所以一个办法就是使用线程池。Android拥有与Java相同的ExecutorService实现。线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。下面的例子是创建一个可重用固定线程数的线程池。

看代码:

[java] view plaincopy
  1. package com.example.handlerloadiage;  
  2.   
  3. import java.net.URL;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. import android.app.Activity;  
  7. import android.graphics.drawable.Drawable;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.Message;  
  11. import android.view.LayoutInflater;  
  12. import android.view.Menu;  
  13. import android.view.View;  
  14. import android.view.ViewGroup;  
  15. import android.widget.BaseAdapter;  
  16. import android.widget.ImageView;  
  17. import android.widget.ListView;  
  18.   
  19. public class HandlerExecutorServiceActivity extends Activity {  
  20.   
  21.     private ListView listview;  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_handlerimageloader);  
  27.   
  28.         listview = (ListView) findViewById(R.id.listview);  
  29.         listview.setAdapter(new MyAdapter());  
  30.     }  
  31.   
  32.     private class MyAdapter extends BaseAdapter {  
  33.   
  34.         public MyAdapter() {  
  35.   
  36.         }  
  37.   
  38.         @Override  
  39.         public int getCount() {  
  40.             return 100;  
  41.         }  
  42.   
  43.         @Override  
  44.         public Object getItem(int position) {  
  45.             return null;  
  46.         }  
  47.   
  48.         @Override  
  49.         public long getItemId(int position) {  
  50.             return 0;  
  51.         }  
  52.   
  53.         @Override  
  54.         public View getView(int position, View convertView, ViewGroup parent) {  
  55.             if (convertView == null) {  
  56.                 convertView = LayoutInflater.from(getApplicationContext())  
  57.                         .inflate(R.layout.list_item, null);  
  58.             }  
  59.             final ImageView image = (ImageView) convertView  
  60.                     .findViewById(R.id.imageview);  
  61.             final String imageURL = "http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";  
  62.   
  63.             final Handler handler = new Handler() {  
  64.   
  65.                 @Override  
  66.                 public void handleMessage(Message msg) {  
  67.                     super.handleMessage(msg);  
  68.                     Drawable d = (Drawable) msg.obj;  
  69.                     if (d == null) {  
  70.                         image.setImageResource(R.drawable.ic_launcher);  
  71.                     } else {  
  72.                         image.setImageDrawable(d);  
  73.                     }  
  74.                 }  
  75.   
  76.             };  
  77.             ExecutorService executorService = Executors.newFixedThreadPool(5);  
  78.             executorService.submit(new Runnable() {  
  79.                 public void run() {  
  80.                     try {  
  81.                         final Drawable drawable = Drawable.createFromStream(  
  82.                                 new URL(imageURL).openStream(), "image.png");  
  83.                         // 模拟网络延时  
  84.                         // SystemClock.sleep(2000);  
  85.                         Message message = handler.obtainMessage();  
  86.                         message.obj = drawable;  
  87.                         handler.sendMessage(message);  
  88.                     } catch (Exception e) {  
  89.                         throw new RuntimeException(e);  
  90.                     }  
  91.                 }  
  92.             });  
  93.             return convertView;  
  94.         }  
  95.   
  96.     }  
  97.   
  98.     @Override  
  99.     public boolean onCreateOptionsMenu(Menu menu) {  
  100.         getMenuInflater().inflate(R.menu.main, menu);  
  101.         return true;  
  102.     }  
  103.   
  104. }<strong>  
  105. </strong>  
    这个方法的效果从表面上来看,其实和方法3差不太多,当然我说的是效果。那优势也是显而易见的,对线程的使用比较合理。但是,查看logcat就会发现,不管哪个方法都会出现GC_FOR_MALLOC freed 4843 objects / 473240 bytes in 64ms(感兴趣朋友可以看一下这个)。这个意思就是堆内部不足,如果有很多图片要加载的话,就可能出现OOM了,所以就需要进一步优化。

方案
     

     可以说对于下载网络图片,上面的方法只是一个开始。要想成为一个App的解决方案,其实还需要考虑更多的东西。先说一下可以进一步优化的地方:

     1、使用弱引用

     2、使用本地存储(SD卡)

     3、对图片进行缩放

     4、定时清除缓存

     5、存储到数据库

     下面看一个比较成熟的解决方案:

     ImageUtil.java  这是一个图片下载工具类

[java] view plaincopy
  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.lang.ref.SoftReference;  
  7. import java.net.MalformedURLException;  
  8. import java.net.URL;  
  9. import java.net.URLConnection;  
  10. import java.text.DecimalFormat;  
  11. import java.util.Date;  
  12. import java.util.LinkedHashMap;  
  13. import android.annotation.SuppressLint;  
  14. import android.content.Context;  
  15. import android.database.Cursor;  
  16. import android.graphics.Bitmap;  
  17. import android.graphics.BitmapFactory;  
  18. import android.graphics.BitmapFactory.Options;  
  19. import android.graphics.Rect;  
  20. import android.os.Build;  
  21. import android.os.Environment;  
  22. import android.os.Handler;  
  23. import android.os.Message;  
  24. import android.util.Log;  
  25. import android.widget.ImageView;  
  26. import cn.eoe.app.R;  
  27. import cn.eoe.app.db.DBHelper;  
  28. import cn.eoe.app.db.ImageCacheColumn;  
  29.   
  30. public class ImageUtil {  
  31.     private static final String TAG = "ImageUtil";  
  32.     private static int DayCount = 15;// 天数  
  33.     private static final long CLEARTIME = DayCount * 24 * 60 * 60 * 1000;//秒  
  34.     /** 
  35.      * 默认图片 
  36.      */  
  37.     private final static int Default_Img = R.drawable.bg_load_default;  
  38.   
  39.     private static Object lock = new Object();  
  40.   
  41.     /** 
  42.      * 内存图片软引用缓冲 
  43.      */  
  44.     private static LinkedHashMap<String, SoftReference> imageCache = new LinkedHashMap<String, SoftReference>(  
  45.             20);  
  46.   
  47.     /** 
  48.      * 入口 
  49.      *  
  50.      * @param imageUrl 
  51.      * @param iv_item_image 
  52.      * @param context 
  53.      * @param callback 
  54.      * @param b 
  55.      */  
  56.     public static void setThumbnailView(String imageUrl,  
  57.             ImageView iv_item_image, Context context, ImageCallback callback,  
  58.             boolean b) {  
  59.         DBHelper dbHelper = DBHelper.getInstance(context);//获取数据库实例  
  60.         String md5 = ImageUtil.md5(imageUrl);  
  61.         String cachePath = context.getCacheDir().getAbsolutePath() + "/" + md5; // data里的缓存  
  62.         String imagePath = getExternalCacheDir(context) + File.separator + md5; // sd卡  
  63.         // 缓存目录  
  64.   
  65.         if (!CommonUtil.sdCardIsAvailable())/* true 为可用 */{  
  66.             setThumbnailImage(iv_item_image, imageUrl, cachePath, dbHelper,  
  67.                     callback, b);  
  68.             iv_item_image.setTag(cachePath);//SD卡不可用就是用data里面缓存的图片  
  69.         } else {  
  70.             setThumbnailImage(iv_item_image, imageUrl, imagePath, dbHelper,  
  71.                     callback, b);  
  72.             iv_item_image.setTag(imagePath);//  
  73.         }  
  74.     }  
  75.   
  76.     /** 
  77.      * 获得程序在sd开上的cahce目录 
  78.      *  
  79.      * @param context 
  80.      *            The context to use 
  81.      * @return The external cache dir 
  82.      */  
  83.     @SuppressLint("NewApi")  
  84.     public static String getExternalCacheDir(Context context) {  
  85.         // android 2.2 以后才支持的特性  
  86.         if (hasExternalCacheDir()) {  
  87.             return context.getExternalCacheDir().getPath() + File.separator  
  88.                     + "img";  
  89.         }  
  90.         // Before Froyo we need to construct the external cache dir ourselves  
  91.         // 2.2以前我们需要自己构造  
  92.         final String cacheDir = "/Android/data/" + context.getPackageName()  
  93.                 + "/cache/img/";  
  94.         return Environment.getExternalStorageDirectory().getPath() + cacheDir;  
  95.     }  
  96.   
  97.     public static boolean hasExternalCacheDir() {  
  98.         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;  
  99.     }  
  100.   
  101.     /** 
  102.      * 设置图片函数 
  103.      *  
  104.      * @param view 
  105.      * @param imageUrl 
  106.      * @param cachePath 
  107.      * @param callback 
  108.      * @param b 
  109.      */  
  110.     private static void setThumbnailImage(ImageView view, String imageUrl,  
  111.             String cachePath, DBHelper dbHelper, ImageCallback callback,  
  112.             boolean b) {  
  113.         Bitmap bitmap = null;  
  114.         bitmap = ImageUtil.loadThumbnailImage(cachePath, imageUrl, dbHelper,  
  115.                 callback, b);  
  116.         if (bitmap == null) {// 先查找数据库,再查找本地sd卡,若没有.再从网站加载,若网站上没有图片或错误时返回null  
  117.             // 设置默认图片  
  118.             view.setImageResource(Default_Img);  
  119.         } else {  
  120.             // 设置本地SD卡缓存图片  
  121.             view.setImageBitmap(bitmap);  
  122.         }  
  123.     }  
  124.   
  125.     private static Bitmap getImageFromDB(String imagePath, String imageUrl,  
  126.             DBHelper dbHelper) {  
  127.         Cursor cursor = queryFromDbByImgUrl(dbHelper, imageUrl);  
  128.         if (cursor.moveToFirst()) {  
  129.             long currTimestamp = (new Date()).getTime();  
  130.             long timestamp = cursor.getLong(cursor  
  131.                     .getColumnIndex(ImageCacheColumn.TIMESTAMP));  
  132.             long spanTime = currTimestamp - timestamp;  
  133.             int Past_time = cursor.getInt(cursor  
  134.                     .getColumnIndex(ImageCacheColumn.PAST_TIME));  
  135.             if (spanTime > Past_time * 24 * 60 * 60 * 1000) {  
  136.                 // 过期  
  137.                 // 删除本地文件  
  138.                 deleteImageFromLocal(imagePath);  
  139.                 return null;  
  140.             } else {  
  141.                 // 没过期  
  142.                 return getImageFromLocal(imagePath);  
  143.             }  
  144.         } else {  
  145.             return null;  
  146.         }  
  147.     }  
  148.   
  149.     private static Cursor queryFromDbByImgUrl(DBHelper dbHelper, String imageUrl) {  
  150.         // return dbHelper.query(ImageCacheColumn.TABLE_NAME, null,  
  151.         // ImageCacheColumn.Url + "=?", new String[] { imageUrl });  
  152.         return dbHelper.rawQuery("select * from " + ImageCacheColumn.TABLE_NAME  
  153.                 + "  where " + ImageCacheColumn.Url + "='" + imageUrl + "'",  
  154.                 null);  
  155.     }  
  156.   
  157.     /** 
  158.      * 保存图片到SD卡 
  159.      *  
  160.      * @param imagePath 
  161.      * @param buffer 
  162.      * @throws IOException 
  163.      */  
  164.     public static void saveImage(String imagePath, byte[] buffer)  
  165.             throws IOException {  
  166.         File f = new File(imagePath);  
  167.         if (f.exists()) {  
  168.             return;  
  169.         } else {  
  170.             File parentFile = f.getParentFile();  
  171.             if (!parentFile.exists()) {  
  172.                 parentFile.mkdirs();  
  173.             }  
  174.             f.createNewFile();  
  175.             FileOutputStream fos = new FileOutputStream(imagePath);  
  176.             fos.write(buffer);  
  177.             fos.flush();  
  178.             fos.close();  
  179.         }  
  180.     }  
  181.   
  182.     /** 
  183.      * 保存图片到缓存 
  184.      *  
  185.      * @param imagePath 
  186.      * @param bm 
  187.      */  
  188.     public static void saveImage(String imagePath, Bitmap bm) {  
  189.   
  190.         if (bm == null || imagePath == null || "".equals(imagePath)) {  
  191.             return;  
  192.         }  
  193.   
  194.         File f = new File(imagePath);  
  195.         if (f.exists()) {  
  196.             return;  
  197.         } else {  
  198.             try {  
  199.                 File parentFile = f.getParentFile();  
  200.                 if (!parentFile.exists()) {  
  201.                     parentFile.mkdirs();  
  202.                 }  
  203.                 f.createNewFile();  
  204.                 FileOutputStream fos;  
  205.                 fos = new FileOutputStream(f);  
  206.                 bm.compress(Bitmap.CompressFormat.PNG, 100, fos);  
  207.                 fos.close();  
  208.             } catch (FileNotFoundException e) {  
  209.                 f.delete();  
  210.                 e.printStackTrace();  
  211.             } catch (IOException e) {  
  212.                 e.printStackTrace();  
  213.                 f.delete();  
  214.             }  
  215.         }  
  216.     }  
  217.   
  218.     private static void saveImageByDb(String imageUrl, DBHelper dbHelper) {  
  219.         String sql = null;  
  220.         if (queryFromDbByImgUrl(dbHelper, imageUrl).moveToFirst()) {  
  221.             sql = "update " + ImageCacheColumn.TABLE_NAME + " set "  
  222.                     + ImageCacheColumn.TIMESTAMP + "='"  
  223.                     + (new Date().getTime()) + "' where "  
  224.                     + ImageCacheColumn.Url + "='" + imageUrl + "'";  
  225.         } else {  
  226.             sql = "insert into " + ImageCacheColumn.TABLE_NAME + "("  
  227.                     + ImageCacheColumn.Url + "," + ImageCacheColumn.TIMESTAMP  
  228.                     + "," + ImageCacheColumn.PAST_TIME + ") values('"  
  229.                     + imageUrl + "'," + (new Date().getTime()) + "," + DayCount  
  230.                     + ")";  
  231.         }  
  232.         dbHelper.ExecSQL(sql);  
  233.     }  
  234.   
  235.     /** 
  236.      * 从SD卡加载图片 
  237.      *  
  238.      * @param imagePath 
  239.      * @return 
  240.      */  
  241.     public static Bitmap getImageFromLocal(String imagePath) {  
  242.         File file = new File(imagePath);  
  243.         if (file.exists()) {  
  244.             Bitmap bitmap = BitmapFactory.decodeFile(imagePath);  
  245.             file.setLastModified(System.currentTimeMillis());  
  246.             return bitmap;  
  247.         }  
  248.         return null;  
  249.     }  
  250.   
  251.     /** 
  252.      * 从本地文件中删除文件 
  253.      *  
  254.      * @param imagePath 
  255.      */  
  256.     private static void deleteImageFromLocal(String imagePath) {  
  257.         File file = new File(imagePath);  
  258.         if (file.exists()) {  
  259.             file.delete();  
  260.         }  
  261.     }  
  262.   
  263.     /** 
  264.      * 从本地或者服务端异步加载缩略图图片 
  265.      *  
  266.      * @return 
  267.      * @param imagePath 
  268.      *            本地缓存路径 
  269.      * @param imgUrl 
  270.      *            拼接后的请求路径 
  271.      * @param callback 
  272.      *            得到数据后的处理方法回调 
  273.      * @throws IOException 
  274.      */  
  275.     public static Bitmap loadThumbnailImage(final String imagePath,  
  276.             final String imgUrl, final DBHelper dbHelper,  
  277.             final ImageCallback callback, final boolean b) {  
  278.         // 在软链接缓存中,则返回Bitmap对象  
  279.         if (imageCache.containsKey(imgUrl)) {  
  280.             SoftReference reference = imageCache.get(imgUrl);  
  281.             Bitmap bitmap = (Bitmap) reference.get();  
  282.             if (bitmap != null) {  
  283.                 return bitmap;  
  284.             }  
  285.         }  
  286.         // 若软链接缓存没有  
  287.         Bitmap bitmap = null;  
  288.         // 查询数据库 返回bitmap  
  289.         bitmap = getImageFromDB(imagePath, imgUrl, dbHelper);// 从本地加载  
  290.         if (bitmap != null) {  
  291.             return bitmap;  
  292.         } else {  
  293.             // 从网上加载  
  294.             final Handler handler = new Handler() {  
  295.                 @Override  
  296.                 public void handleMessage(Message msg) {  
  297.                     if (msg.obj != null) {  
  298.                         Bitmap bitmap = (Bitmap) msg.obj;  
  299.                         callback.loadImage(bitmap, imagePath);  
  300.                     }  
  301.                 }  
  302.             };  
  303.             Runnable runnable = new Runnable() {  
  304.                 @Override  
  305.                 public void run() {  
  306.                     try {  
  307.                         URL url = new URL(imgUrl);  
  308.                         URLConnection conn = url.openConnection();  
  309.                         conn.setConnectTimeout(5000);  
  310.                         conn.setReadTimeout(5000);  
  311.                         conn.connect();  
  312.                         InputStream in = conn.getInputStream();  
  313.                         BitmapFactory.Options options = new Options();  
  314.                         options.inSampleSize = 1;  
  315.                         Bitmap bitmap = BitmapFactory.decodeStream(in,  
  316.                                 new Rect(0000), options);  
  317.                         imageCache.put(imgUrl, new SoftReference(bitmap));  
  318.   
  319.                         Message msg = handler.obtainMessage();  
  320.                         msg.obj = bitmap;  
  321.                         handler.sendMessage(msg);  
  322.                         if (bitmap != null) {  
  323.                             // 保存文件到sd卡  
  324.                             saveImage(imagePath, bitmap);  
  325.                             // 保存到数据库  
  326.                             saveImageByDb(imgUrl, dbHelper);  
  327.                         }  
  328.                     } catch (MalformedURLException e) {  
  329.                         e.printStackTrace();  
  330.                         Log.e(ImageUtil.class.getName(), "图片url不存在");  
  331.                     } catch (IOException e) {  
  332.                         e.printStackTrace();  
  333.                     }  
  334.                 }  
  335.             };  
  336.             ThreadPoolManager.getInstance().addTask(runnable);  
  337.         }  
  338.         return null;  
  339.     }  
  340.   
  341.     /** 
  342.      * MD5 
  343.      *  
  344.      * @param paramString 
  345.      * @return 
  346.      */  
  347.     private static String md5(String paramString) {  
  348.         return MD5.encode(paramString);  
  349.     }  
  350.   
  351.     // ///////////////////////////////////////////////////////////////////////  
  352.     // 公共方法  
  353.   
  354.     public interface ImageCallback {  
  355.         public void loadImage(Bitmap bitmap, String imagePath);  
  356.     }  
  357.   
  358.     /** 
  359.      * 每次打开含有大量图片的activity时,开一个新线程,检查并清理缓存 
  360.      *  
  361.      * @param context 
  362.      */  
  363.     public static void checkCache(final Context context) {  
  364.         new Thread() {  
  365.             public void run() {  
  366.                 int state = 0;// 记录清除结果 0为都没清除, 1为只清除了sd卡, 2为只清除了rom Cache ,3  
  367.                                 // 为都清除了  
  368.                 String cacheS = "0M";  
  369.                 String cacheD = "0M";  
  370.                 File sdCache = new File(getExternalCacheDir(context)); // sd卡"mnt/sdcard/android/data/cn.eoe.app/cache/";  
  371.                 File cacheDir = context.getCacheDir(); // 手机data/data/com.mengniu.app/cache  
  372.                 try {  
  373.                     if (sdCache != null && sdCache.exists()) {  
  374.                         long sdFileSize = getFileSize(sdCache);  
  375.                         if (sdFileSize > 1024 * 1024 * 50) {  
  376.                             // SD需要清理  
  377.                             long clearFileSize = clear(sdCache);  
  378.                             state += 1;  
  379.                             cacheS = clearFileSize + "";  
  380.                         }  
  381.                     }  
  382.                     if (cacheDir != null && cacheDir.exists()) {  
  383.                         long cacheFileSize = getFileSize(cacheDir);  
  384.                         if (cacheFileSize > 1024 * 1024 * 50) {  
  385.                             // ROM需要清理  
  386.                             long clearFileSize = clear(cacheDir);  
  387.                             state += 2;  
  388.                             cacheD = clearFileSize + "";  
  389.                         }  
  390.                     }  
  391.                 } catch (Exception e) {  
  392.                     e.printStackTrace();  
  393.                 }  
  394.             };  
  395.         }.start();  
  396.     }  
  397.   
  398.     /** 
  399.      * 清除路径 
  400.      *  
  401.      * @param cacheDir 
  402.      * @return 
  403.      */  
  404.     public static long clear(File cacheDir) {  
  405.         long clearFileSize = 0;  
  406.         File[] files = cacheDir.listFiles();  
  407.         if (files == null)  
  408.             return 0;  
  409.         for (File f : files) {  
  410.             if (f.isFile()) {  
  411.                 if (System.currentTimeMillis() - f.lastModified() > CLEARTIME) {  
  412.                     long fileSize = f.length();  
  413.                     if (f.delete()) {  
  414.                         clearFileSize += fileSize;  
  415.                     }  
  416.                 }  
  417.             } else {  
  418.                 clear(f);  
  419.             }  
  420.         }  
  421.         return clearFileSize;  
  422.     }  
  423.   
  424.     /** 
  425.      * 取得文件大小 
  426.      *  
  427.      * @param f 
  428.      * @return 
  429.      * @throws Exception 
  430.      */  
  431.     public static long getFileSize(File f) throws Exception {  
  432.         long size = 0;  
  433.         File flist[] = f.listFiles();  
  434.         for (int i = 0; i < flist.length; i++) {  
  435.             if (flist[i].isDirectory()) {  
  436.                 size = size + getFileSize(flist[i]);  
  437.             } else {  
  438.                 size = size + flist[i].length();  
  439.             }  
  440.         }  
  441.         return size;  
  442.     }  
  443.   
  444.     /** 
  445.      * 转换文件大小 
  446.      *  
  447.      * @param fileS 
  448.      * @return 
  449.      */  
  450.     public static String FormetFileSize(long fileS) {  
  451.         DecimalFormat df = new DecimalFormat("#.00");  
  452.         String fileSizeString = "";  
  453.         if (fileS < 1024) {  
  454.             fileSizeString = df.format((double) fileS) + "B";  
  455.         } else if (fileS < 1048576) {  
  456.             fileSizeString = df.format((double) fileS / 1024) + "K";  
  457.         } else if (fileS < 1073741824) {  
  458.             fileSizeString = df.format((double) fileS / 1048576) + "M";  
  459.         } else {  
  460.             fileSizeString = df.format((double) fileS / 1073741824) + "G";  
  461.         }  
  462.         return fileSizeString;  
  463.     }  
  464.   
  465. }  
   DBHelper.java 和数据库有关的操作

[java] view plaincopy
  1. import android.content.ContentValues;  
  2. import android.content.Context;  
  3. import android.database.Cursor;  
  4. import android.database.sqlite.SQLiteDatabase;  
  5. import android.database.sqlite.SQLiteDatabase.CursorFactory;  
  6. import android.database.sqlite.SQLiteOpenHelper;  
  7. import android.provider.BaseColumns;  
  8.   
  9. public class DBHelper extends SQLiteOpenHelper {  
  10.   
  11.     private static final String DB_NAME = "cn";  
  12.     private static final int DB_VERSION = 2;  
  13.   
  14.     private SQLiteDatabase db;  
  15.       
  16.     private static DBHelper mdbHelper;  
  17.       
  18.     public static DBHelper getInstance(Context context)  
  19.     {  
  20.         if(mdbHelper==null)  
  21.         {  
  22.             mdbHelper=new DBHelper(context);  
  23.         }  
  24.         return mdbHelper;  
  25.     }  
  26.   
  27.     private DBHelper(Context context) {  
  28.         super(context, DB_NAME, null, DB_VERSION);  
  29.     }  
  30.   
  31.     private DBHelper(Context context, String name, CursorFactory factory,  
  32.             int version) {  
  33.         super(context, name, factory, version);  
  34.         // TODO Auto-generated constructor stub  
  35.     }  
  36.   
  37.     @Override  
  38.     public void onCreate(SQLiteDatabase db) {  
  39.         this.db = db;  
  40.         operateTable(db, "");  
  41.     }  
  42.   
  43.     @Override  
  44.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  45.         // TODO Auto-generated method stub  
  46.         if (oldVersion == newVersion) {  
  47.             return;  
  48.         }  
  49.         operateTable(db, "DROP TABLE IF EXISTS ");  
  50.         onCreate(db);  
  51.     }  
  52.   
  53.     public void operateTable(SQLiteDatabase db, String actionString) {  
  54.         Class<DatabaseColumn>[] columnsClasses = DatabaseColumn.getSubClasses();  
  55.         DatabaseColumn columns = null;  
  56.   
  57.         for (int i = 0; i < columnsClasses.length; i++) {  
  58.             try {  
  59.                 columns = columnsClasses[i].newInstance();  
  60.                 if ("".equals(actionString) || actionString == null) {  
  61.                     db.execSQL(columns.getTableCreateor());  
  62.                 } else {  
  63.                     db.execSQL(actionString + columns.getTableName());  
  64.                 }  
  65.             } catch (Exception e) {  
  66.                 e.printStackTrace();  
  67.             }  
  68.         }  
  69.   
  70.     }  
  71.   
  72.     public long insert(String Table_Name, ContentValues values) {  
  73.         if (db == null)  
  74.             db = getWritableDatabase();  
  75.         return db.insert(Table_Name, null, values);  
  76.     }  
  77.   
  78.     /** 
  79.      *  
  80.      * @param Table_Name 
  81.      * @param id 
  82.      * @return 影响行数 
  83.      */  
  84.     public int delete(String Table_Name, int id) {  
  85.         if (db == null)  
  86.             db = getWritableDatabase();  
  87.         return db.delete(Table_Name, BaseColumns._ID + "=?",  
  88.                 new String[] { String.valueOf(id) });  
  89.     }  
  90.   
  91.     /** 
  92.      * @param Table_Name 
  93.      * @param values 
  94.      * @param WhereClause 
  95.      * @param whereArgs 
  96.      * @return 影响行数 
  97.      */  
  98.     public int update(String Table_Name, ContentValues values,  
  99.             String WhereClause, String[] whereArgs) {  
  100.         if (db == null) {  
  101.             db = getWritableDatabase();  
  102.         }  
  103.         return db.update(Table_Name, values, WhereClause, whereArgs);  
  104.     }  
  105.   
  106.     public Cursor query(String Table_Name, String[] columns, String whereStr,  
  107.             String[] whereArgs) {  
  108.         if (db == null) {  
  109.             db = getReadableDatabase();  
  110.         }  
  111.         return db.query(Table_Name, columns, whereStr, whereArgs, nullnull,  
  112.                 null);  
  113.     }  
  114.   
  115.     public Cursor rawQuery(String sql, String[] args) {  
  116.         if (db == null) {  
  117.             db = getReadableDatabase();  
  118.         }  
  119.         return db.rawQuery(sql, args);  
  120.     }  
  121.   
  122.     public void ExecSQL(String sql) {  
  123.         if (db == null) {  
  124.             db = getWritableDatabase();  
  125.         }  
  126.         db.execSQL(sql);  
  127.     }  
  128.   
  129.     public void closeDb() {  
  130.         if (db != null) {  
  131.             db.close();  
  132.             db = null;  
  133.         }  
  134.     }  
  135.   
  136. }  
    CommonUtil.java

[java] view plaincopy
  1. import java.io.File;  
  2. import android.content.Context;  
  3. import android.os.Environment;  
  4. import android.os.StatFs;  
  5.   
  6. public class CommonUtil {  
  7.   
  8.     /** 
  9.      * 检测sdcard是否可用 
  10.      *  
  11.      * @return true为可用,否则为不可用 
  12.      */  
  13.     public static boolean sdCardIsAvailable() {  
  14.         String status = Environment.getExternalStorageState();  
  15.         if (!status.equals(Environment.MEDIA_MOUNTED))  
  16.             return false;  
  17.         return true;  
  18.     }  
  19.   
  20.     /** 
  21.      * Checks if there is enough Space on SDCard 
  22.      *  
  23.      * @param updateSize 
  24.      *            Size to Check 
  25.      * @return True if the Update will fit on SDCard, false if not enough space on SDCard Will also return false, if the SDCard is 
  26.      *         not mounted as read/write 
  27.      */  
  28.     public static boolean enoughSpaceOnSdCard(long updateSize) {  
  29.         String status = Environment.getExternalStorageState();  
  30.         if (!status.equals(Environment.MEDIA_MOUNTED))  
  31.             return false;  
  32.         return (updateSize < getRealSizeOnSdcard());  
  33.     }  
  34.   
  35.     /** 
  36.      * get the space is left over on sdcard 
  37.      */  
  38.     public static long getRealSizeOnSdcard() {  
  39.         File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath());  
  40.         StatFs stat = new StatFs(path.getPath());  
  41.         long blockSize = stat.getBlockSize();  
  42.         long availableBlocks = stat.getAvailableBlocks();  
  43.         return availableBlocks * blockSize;  
  44.     }  
  45.   
  46.     /** 
  47.      * Checks if there is enough Space on phone self 
  48.      *  
  49.      */  
  50.     public static boolean enoughSpaceOnPhone(long updateSize) {  
  51.         return getRealSizeOnPhone() > updateSize;  
  52.     }  
  53.   
  54.     /** 
  55.      * get the space is left over on phone self 
  56.      */  
  57.     public static long getRealSizeOnPhone() {  
  58.         File path = Environment.getDataDirectory();  
  59.         StatFs stat = new StatFs(path.getPath());  
  60.         long blockSize = stat.getBlockSize();  
  61.         long availableBlocks = stat.getAvailableBlocks();  
  62.         long realSize = blockSize * availableBlocks;  
  63.         return realSize;  
  64.     }  
  65.       
  66.     /** 
  67.      * 根据手机分辨率从dp转成px 
  68.      * @param context 
  69.      * @param dpValue 
  70.      * @return 
  71.      */  
  72.     public static  int dip2px(Context context, float dpValue) {    
  73.         final float scale = context.getResources().getDisplayMetrics().density;    
  74.         return (int) (dpValue * scale + 0.5f);    
  75.     }    
  76.         
  77.     /**  
  78.      * 根据手机的分辨率从 px(像素) 的单位 转成为 dp  
  79.      */    
  80.     public static  int px2dip(Context context, float pxValue) {    
  81.         final float scale = context.getResources().getDisplayMetrics().density;    
  82.         return (int) (pxValue / scale + 0.5f)-15;    
  83.     }    
  84.   
  85.       
  86. }  
    ThreadPoolManager.java  线程池管理类

[java] view plaincopy
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3.   
  4. public class ThreadPoolManager {  
  5.     private ExecutorService service;  
  6.       
  7.     private ThreadPoolManager(){  
  8.         int num = Runtime.getRuntime().availableProcessors();  
  9.         service = Executors.newFixedThreadPool(num*2);  
  10.     }  
  11.       
  12.     private static ThreadPoolManager manager;  
  13.       
  14.       
  15.     public static ThreadPoolManager getInstance(){  
  16.         if(manager==null)  
  17.         {  
  18.             manager= new ThreadPoolManager();  
  19.         }  
  20.         return manager;  
  21.     }  
  22.       
  23.     public void addTask(Runnable runnable){  
  24.         service.submit(runnable);  
  25.     }  
  26.       
  27. }  
      MD5.java

[java] view plaincopy
  1. import java.security.MessageDigest;  
  2. import java.security.NoSuchAlgorithmException;  
  3.   
  4. import org.apache.http.impl.auth.UnsupportedDigestAlgorithmException;  
  5.   
  6. import android.util.Log;  
  7.   
  8. /** 
  9.  * @version 1.0 
  10.  */  
  11. public final class MD5 {  
  12.     private static final String LOG_TAG = "MD5";  
  13.     private static final String ALGORITHM = "MD5";  
  14.   
  15.     private static char sHexDigits[] = {  
  16.         '0''1''2''3''4''5''6''7''8''9''a''b''c''d''e''f'  
  17. };  
  18.     private static MessageDigest sDigest;  
  19.   
  20.     static {  
  21.         try {  
  22.             sDigest = MessageDigest.getInstance(ALGORITHM);  
  23.         } catch (NoSuchAlgorithmException e) {  
  24.             Log.e(LOG_TAG, "Get MD5 Digest failed.");  
  25.             throw new UnsupportedDigestAlgorithmException(ALGORITHM, e);  
  26.         }  
  27.     }  
  28.   
  29.     private MD5() {  
  30.     }  
  31.   
  32.       
  33.     final public static String encode(String source) {  
  34.         byte[] btyes = source.getBytes();  
  35.         byte[] encodedBytes = sDigest.digest(btyes);  
  36.   
  37.         return Utility.hexString(encodedBytes);  
  38.     }  
  39.   
  40. }  
      ImageCacheColumn.java

[java] view plaincopy
  1. import java.util.HashMap;  
  2. import java.util.Map;  
  3. import android.net.Uri;  
  4.   
  5. public class ImageCacheColumn extends DatabaseColumn {  
  6.   
  7.     public final static String TABLE_NAME = "imageCache";  
  8.     public final static String TIMESTAMP = "timestamp";  
  9.     public final static String Url = "url";  
  10.     /** 
  11.      * 单位:天 
  12.      */  
  13.     public final static String PAST_TIME = "past_time";  
  14.       
  15.     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY  
  16.             + "/" + TABLE_NAME);  
  17.     private static final Map<String, String> mColumnMap = new HashMap<String, String>();  
  18.     static {  
  19.   
  20.         mColumnMap.put(_ID, "integer primary key autoincrement");  
  21.         mColumnMap.put(TIMESTAMP, "TimeStamp");  
  22.         mColumnMap.put(Url, "text");  
  23.         mColumnMap.put(PAST_TIME, "TimeStamp");  
  24.     }  
  25.       
  26.   
  27.     @Override  
  28.     public String getTableName() {  
  29.         // TODO Auto-generated method stub  
  30.         return TABLE_NAME;  
  31.     }  
  32.   
  33.     @Override  
  34.     public Uri getTableContent() {  
  35.         // TODO Auto-generated method stub  
  36.         return CONTENT_URI;  
  37.     }  
  38.   
  39.     @Override  
  40.     protected Map<String, String> getTableMap() {  
  41.         // TODO Auto-generated method stub  
  42.         return mColumnMap;  
  43.     }  
  44.   
  45. }