zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    (III)AOP:第六节:通知
    (III)AOP:第八节:多切面运行顺序
    (IV)JdbcTemplate
    (III)AOP:第三节:AOP配置与使用
    (III)AOP:第十节:AOP 总结
    (III)AOP:第二节:AOP概述
    (III)AOP:第四节:切入点表达式
    (III)AOP:第九节:XML配置切面
    (III)AOP:第五节:当前连接点
    7月3号
  • 原文地址:https://www.cnblogs.com/jianrong-zheng/p/4545996.html
Copyright © 2011-2022 走看看