android WeakReference(弱引用 防止内存泄漏)与SoftReference(软引用 实现缓存机制(cache))

在Android开发中,基本上很少有用到软引用或弱引用,这两个东东若用的很好,对自己开发的代码质量的提高有很大的帮助。若用的不好,会坑了自己。所以,在还没有真正的去了解它们之前,还是慎用比较好。

   下面将通过两个Demo来结识软引用和弱引用在开发中的运用。

   一. WeakReference:防止内存泄漏,要保证内存被虚拟机回收。

        下面以一个时间更新的Demo来说明弱引用的运用。

         1. main.xml文件代码如下:   

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <com.stevenhu.wrt.DigitalClock  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent"  
  10.         android:orientation="horizontal">  
  11.           
  12.         <TextView android:id="@+id/time"  
  13.             android:layout_width="wrap_content"  
  14.             android:layout_height="wrap_content"  
  15.             android:textSize="50pt"  
  16.             />  
  17.           
  18.         <TextView android:id="@+id/ampm"  
  19.             android:layout_width="wrap_content"  
  20.             android:layout_height="wrap_content"  
  21.             android:textSize="25pt"  
  22.             />  
  23.           
  24.     </com.stevenhu.wrt.DigitalClock>  
  25.   
  26. </LinearLayout>  

       2.自定义ViewGroup类DigitalClock的代码如下:

 

  1. package com.stevenhu.wrt;  
  2.   
  3. import java.lang.ref.WeakReference;  
  4. import java.text.DateFormatSymbols;  
  5. import java.util.Calendar;  
  6.   
  7. import android.content.BroadcastReceiver;  
  8. import android.content.Context;  
  9. import android.content.Intent;  
  10. import android.content.IntentFilter;  
  11. import android.database.ContentObserver;  
  12. import android.graphics.Canvas;  
  13. import android.os.Handler;  
  14. import android.provider.Settings;  
  15. import android.text.format.DateFormat;  
  16. import android.util.AttributeSet;  
  17. import android.view.View;  
  18. import android.widget.LinearLayout;  
  19. import android.widget.TextView;  
  20. import android.widget.Toast;  
  21.   
  22. public class DigitalClock extends LinearLayout {  
  23.     // 12小时、24小时制  
  24.     private final static String M12 = "h:mm";  
  25.     private final static String M24 = "kk:mm";  
  26.   
  27.     private Calendar mCalendar;  
  28.     private String mFormat;  
  29.     private TextView mDislpayTime;  
  30.     private AmPm mAmPm;  
  31.     private ContentObserver mFormatChangeObserver;  
  32.     private final Handler mHandler = new Handler();  
  33.     private BroadcastReceiver mReceiver;  
  34.     private Context mContext;  
  35.   
  36.     public DigitalClock(Context context, AttributeSet attrs) {  
  37.         super(context, attrs);  
  38.         mContext = context;  
  39.         // TODO Auto-generated constructor stub  
  40.     }  
  41.   
  42.     @Override  
  43.     protected void onFinishInflate() {  
  44.         // TODO Auto-generated method stub  
  45.         super.onFinishInflate();  
  46.         mDislpayTime = (TextView) this.findViewById(R.id.time);  
  47.         mAmPm = new AmPm(this);  
  48.         mCalendar = Calendar.getInstance();  
  49.         //设置时间显示格式  
  50.         setDateFormat();  
  51.     }  
  52.   
  53.     @Override  
  54.     protected void onAttachedToWindow() {  
  55.         // TODO Auto-generated method stub  
  56.         super.onAttachedToWindow();  
  57.           
  58.         //动态注册监听时间改变的广播  
  59.         if (mReceiver == null) {  
  60.             mReceiver = new TimeChangedReceiver(this);  
  61.             IntentFilter filter = new IntentFilter();  
  62.             filter.addAction(Intent.ACTION_TIME_TICK);  
  63.             filter.addAction(Intent.ACTION_TIME_CHANGED);  
  64.             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);  
  65.             mContext.registerReceiver(mReceiver, filter);  
  66.         }  
  67.   
  68.         //注册监听时间格式改变的ContentObserver  
  69.         if (mFormatChangeObserver == null) {  
  70.             mFormatChangeObserver = new FormatChangeObserver(this);  
  71.             mContext.getContentResolver().registerContentObserver(  
  72.                     Settings.System.CONTENT_URI, true, mFormatChangeObserver);  
  73.         }  
  74.   
  75.         //更新时间  
  76.         updateTime();  
  77.     }  
  78.   
  79.     @Override  
  80.     protected void onDetachedFromWindow() {  
  81.         // TODO Auto-generated method stub  
  82.         super.onDetachedFromWindow();  
  83.   
  84.         if (mReceiver != null) {  
  85.             mContext.unregisterReceiver(mReceiver);  
  86.         }  
  87.         if (mFormatChangeObserver != null) {  
  88.             mContext.getContentResolver().unregisterContentObserver(  
  89.                     mFormatChangeObserver);  
  90.         }  
  91.   
  92.         mFormatChangeObserver = null;  
  93.         mReceiver = null;  
  94.     }  
  95.   
  96.     static class AmPm {  
  97.         private TextView mAmPmTextView;  
  98.         private String mAmString, mPmString;  
  99.   
  100.         AmPm(View parent) {  
  101.             mAmPmTextView = (TextView) parent.findViewById(R.id.ampm);  
  102.             String[] ampm = new DateFormatSymbols().getAmPmStrings();  
  103.             mAmString = ampm[0];  
  104.             mPmString = ampm[1];  
  105.         }  
  106.   
  107.         void setShowAmPm(boolean show) {  
  108.             if (mAmPmTextView != null) {  
  109.                 mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);  
  110.             }  
  111.         }  
  112.   
  113.         void setIsMorning(boolean isMorning) {  
  114.             if (mAmPmTextView != null) {  
  115.                 mAmPmTextView.setText(isMorning ? mAmString : mPmString);  
  116.             }  
  117.         }  
  118.     }  
  119.   
  120.     /*时间刷新涉及到View的更新显示(特别是每秒刷新显示,这样的频率特别高),当然,此处的时间显示是每分钟更新一次 
  121.      * 所以在监听时间更新的广播中采用弱引用,防止在不断刷新当前界面View时产生内存泄露 
  122.      */  
  123.     private static class TimeChangedReceiver extends BroadcastReceiver {  
  124.           
  125.         //采用弱引用  
  126.         private WeakReference<DigitalClock> mClock;  
  127.         private Context mContext;  
  128.   
  129.         public TimeChangedReceiver(DigitalClock clock) {  
  130.             mClock = new WeakReference<DigitalClock>(clock);  
  131.             mContext = clock.getContext();  
  132.         }  
  133.   
  134.         @Override  
  135.         public void onReceive(Context context, Intent intent) {  
  136.               
  137.             // Post a runnable to avoid blocking the broadcast.  
  138.             final boolean timezoneChanged = intent.getAction().equals(  
  139.                     Intent.ACTION_TIMEZONE_CHANGED);  
  140.             //从弱引用中获取对象  
  141.             final DigitalClock clock = mClock.get();  
  142.             if (clock != null) {  
  143.                 clock.mHandler.post(new Runnable() {  
  144.                     public void run() {  
  145.                         if (timezoneChanged) {  
  146.                             clock.mCalendar = Calendar.getInstance();  
  147.                         }  
  148.                         clock.updateTime();  
  149.                     }  
  150.                 });  
  151.             } else {  
  152.                 try {  
  153.                     mContext.unregisterReceiver(this);  
  154.                 } catch (RuntimeException e) {  
  155.                     // Shouldn't happen  
  156.                 }  
  157.             }  
  158.         }  
  159.     };  
  160.   
  161.     // 监听时间显示的格式改变  
  162.     private static class FormatChangeObserver extends ContentObserver {  
  163.         // 采用弱应用  
  164.         private WeakReference<DigitalClock> mClock;  
  165.         private Context mContext;  
  166.   
  167.         public FormatChangeObserver(DigitalClock clock) {  
  168.             super(new Handler());  
  169.             mClock = new WeakReference<DigitalClock>(clock);  
  170.             mContext = clock.getContext();  
  171.         }  
  172.   
  173.         @Override  
  174.         public void onChange(boolean selfChange) {  
  175.             DigitalClock digitalClock = mClock.get();  
  176.             //从弱引用中取出对象  
  177.             if (digitalClock != null) {  
  178.                 //根据弱引用中取出的对象进行时间更新  
  179.                 digitalClock.setDateFormat();  
  180.                 digitalClock.updateTime();  
  181.             } else {  
  182.                 try {  
  183.                     mContext.getContentResolver().unregisterContentObserver(  
  184.                             this);  
  185.                 } catch (RuntimeException e) {  
  186.                     // Shouldn't happen  
  187.                 }  
  188.             }  
  189.         }  
  190.     }  
  191.   
  192.     // 更新时间  
  193.     private void updateTime() {  
  194.         Toast.makeText(mContext, "updateTime", Toast.LENGTH_SHORT).show();  
  195.         mCalendar.setTimeInMillis(System.currentTimeMillis());  
  196.   
  197.         CharSequence newTime = DateFormat.format(mFormat, mCalendar);  
  198.         mDislpayTime.setText(newTime);  
  199.         mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);  
  200.     }  
  201.   
  202.     private void setDateFormat() {  
  203.         // 获取时间制  
  204.         mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) ? M24  
  205.                 : M12;  
  206.         // 根据时间制显示上午、下午  
  207.         mAmPm.setShowAmPm(mFormat.equals(M12));  
  208.     }  
  209.       
  210.     @Override  
  211.     protected void onDraw(Canvas canvas) {  
  212.         // TODO Auto-generated method stub  
  213.         super.onDraw(canvas);  
  214.         //Toast.makeText(mContext, "ddd", Toast.LENGTH_SHORT).show();  
  215.     }  
  216. }  

      3.MainActivity的代码如下:  

 

  1. package com.stevenhu.wrt;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5.   
  6. public class MainActivity extends Activity {  
  7.     /** Called when the activity is first created. */  
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.main);  
  12.     }  
  13. }  

  
    二. SoftReference:实现缓存机制

     下面的Demo实现从网络上获取图片,然后将获取的图片显示的同时,通过软引用缓存起来。当下次再去网络上获取图片时,首先会检查要获取的图片缓存中是否存在,若存在,直接取出来,不需要再去网络上获取。

     1.main.xml文件代码如下:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.    <Button   
  8.        android:id="@+id/get_image"  
  9.        android:layout_width="fill_parent"  
  10.        android:layout_height="wrap_content"  
  11.        android:text="get Image"/>  
  12.      
  13.    <LinearLayout   
  14.        android:layout_width="match_parent"  
  15.        android:layout_height="match_parent"  
  16.        android:orientation="vertical">  
  17.     <ImageView   
  18.        android:id="@+id/one"  
  19.        android:layout_width="wrap_content"  
  20.        android:layout_height="wrap_content"/>  
  21.      
  22.     <ImageView   
  23.        android:id="@+id/two"  
  24.        android:layout_width="wrap_content"  
  25.        android:layout_height="wrap_content"/>  
  26.       
  27.      <ImageView   
  28.        android:id="@+id/three"  
  29.        android:layout_width="wrap_content"  
  30.        android:layout_height="wrap_content"/>  
  31.    </LinearLayout>  
  32.     
  33. </LinearLayout>  

    2.实现异步加载图片功能的类AsyncImageLoader代码如下:

 

  1. package com.stevenhu.lit;  
  2.   
  3. import java.lang.ref.SoftReference;  
  4. import java.net.URL;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7.   
  8. import android.graphics.drawable.Drawable;  
  9. import android.os.Handler;  
  10. import android.os.Message;  
  11.   
  12. //实现图片异步加载的类  
  13. public class AsyncImageLoader   
  14. {  
  15.     //以Url为键,SoftReference为值,建立缓存HashMap键值对。  
  16.     private Map<String, SoftReference<Drawable>> mImageCache =   
  17.         new HashMap<String, SoftReference<Drawable>>();  
  18.       
  19.     //实现图片异步加载  
  20.     public Drawable loadDrawable(final String imageUrl, final ImageCallback callback)  
  21.     {  
  22.         //查询缓存,查看当前需要下载的图片是否在缓存中  
  23.         if(mImageCache.containsKey(imageUrl))  
  24.         {  
  25.             SoftReference<Drawable> softReference = mImageCache.get(imageUrl);  
  26.             if (softReference.get() != null)  
  27.             {  
  28.                 return softReference.get();  
  29.             }  
  30.         }  
  31.           
  32.         final Handler handler = new Handler()  
  33.         {  
  34.             @Override  
  35.             public void dispatchMessage(Message msg)   
  36.             {  
  37.                 //回调ImageCallbackImpl中的imageLoad方法,在主线(UI线程)中执行。  
  38.                 callback.imageLoad((Drawable)msg.obj);  
  39.             }  
  40.         };  
  41.           
  42.         /*若缓存中没有,新开辟一个线程,用于进行从网络上下载图片, 
  43.          * 然后将获取到的Drawable发送到Handler中处理,通过回调实现在UI线程中显示获取的图片 
  44.          */  
  45.         new Thread()  
  46.         {         
  47.             public void run()   
  48.             {  
  49.                 Drawable drawable = loadImageFromUrl(imageUrl);  
  50.                 //将得到的图片存放到缓存中  
  51.                 mImageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
  52.                 Message message = handler.obtainMessage(0, drawable);  
  53.                 handler.sendMessage(message);  
  54.             };  
  55.         }.start();  
  56.           
  57.         //若缓存中不存在,将从网上下载显示完成后,此处返回null;  
  58.         return null;  
  59.           
  60.     }  
  61.       
  62.     //定义一个回调接口  
  63.     public interface ImageCallback  
  64.     {  
  65.         void imageLoad(Drawable drawable);  
  66.     }  
  67.       
  68.     //通过Url从网上获取图片Drawable对象;  
  69.     protected Drawable loadImageFromUrl(String imageUrl)  
  70.     {  
  71.         try {  
  72.             return Drawable.createFromStream(new URL(imageUrl).openStream(),"debug");  
  73.         } catch (Exception e) {  
  74.             // TODO: handle exception  
  75.             throw new RuntimeException(e);  
  76.         }  
  77.     }  
  78. }  

     3. 实现ImageCallback回调接口的类ImageCallbackImpl代码如下:

 

  1. package com.stevenhu.lit;  
  2.   
  3. import android.graphics.drawable.Drawable;  
  4. import android.widget.ImageView;  
  5.   
  6. import com.stevenhu.lit.AsyncImageLoader.ImageCallback;  
  7.   
  8. public class ImageCallbackImpl implements ImageCallback  
  9. {  
  10.   
  11.     private ImageView mImageView;  
  12.       
  13.     public ImageCallbackImpl(ImageView imageView)  
  14.     {  
  15.         mImageView = imageView;  
  16.     }  
  17.       
  18.     //在ImageView中显示从网上获取的图片  
  19.     @Override  
  20.     public void imageLoad(Drawable drawable)   
  21.     {  
  22.         // TODO Auto-generated method stub  
  23.         mImageView.setImageDrawable(drawable);  
  24.     }  
  25.   
  26. }  

     4.MainActivity的代码如下:

 

  1. package com.stevenhu.lit;  
  2.   
  3.   
  4. import android.app.Activity;  
  5. import android.graphics.drawable.Drawable;  
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10. import android.widget.ImageView;  
  11.   
  12. public class MainActivity extends Activity implements OnClickListener  
  13. {  
  14.     //创建异步加载图片类对象  
  15.     private AsyncImageLoader mImageLoader = new AsyncImageLoader();  
  16.       
  17.     /** Called when the activity is first created. */  
  18.     @Override  
  19.     public void onCreate(Bundle savedInstanceState)   
  20.     {  
  21.           
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.main);  
  24.         Button get = (Button)findViewById(R.id.get_image);  
  25.         get.setOnClickListener(this);  
  26.     }  
  27.       
  28.     private void loadImage(final String url, final int id)  
  29.     {  
  30.         ImageView imageView = (ImageView)findViewById(id);  
  31.         ImageCallbackImpl callbackImpl = new ImageCallbackImpl(imageView);  
  32.         Drawable cacheImage = mImageLoader.loadDrawable(url, callbackImpl);  
  33.         //若缓存中存在,直接取出来显示  
  34.         if (cacheImage != null)  
  35.         {  
  36.             imageView.setImageDrawable(cacheImage);  
  37.         }  
  38.     }  
  39.   
  40.     @Override  
  41.     public void onClick(View v) {  
  42.         // TODO Auto-generated method stub  
  43.         if (v.getId() == R.id.get_image)  
  44.         {  
  45.             //从网络上获取海贼王的三张图片显示  
  46.             loadImage("http://wenwen.soso.com/p/20111003/20111003194816-1615366606.jpg", R.id.one);  
  47.             loadImage("http://t10.baidu.com/it/u=2492256852,4267838923&fm=23&gp=0.jpg", R.id.two);  
  48.             loadImage("http://wenwen.soso.com/p/20100410/20100410102416-1948049438.jpg", R.id.three);  
  49.         }  
  50.           
  51.     }  
  52.           
  53. }        

 最后,对于这两者,作个小总结:

   1.  SoftReference<T>:软引用-->当虚拟机内存不足时,将会回收它指向的对象;需要获取对象时,可以调用get方法。

    2.  WeakReference<T>:弱引用-->随时可能会被垃圾回收器回收,不一定要等到虚拟机内存不足时才强制回收。要获取对象时,同样可以调用get方法。

    3. WeakReference一般用来防止内存泄漏,要保证内存被虚拟机回收,SoftReference多用作来实现缓存机制(cache);

note:转自http://blog.csdn.net/stevenhu_223/article/details/18360397

posted on 2015-06-02 11:33  jianrong.zheng  阅读(2972)  评论(0编辑  收藏  举报

导航