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. <com.stevenhu.wrt.DigitalClock
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:orientation="horizontal">
  10. <TextView android:id="@+id/time"
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. android:textSize="50pt"
  14. />
  15. <TextView android:id="@+id/ampm"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:textSize="25pt"
  19. />
  20. </com.stevenhu.wrt.DigitalClock>
  21. </LinearLayout>

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

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

3.MainActivity的代码如下:

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

二. 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. <Button
  7. android:id="@+id/get_image"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="get Image"/>
  11. <LinearLayout
  12. android:layout_width="match_parent"
  13. android:layout_height="match_parent"
  14. android:orientation="vertical">
  15. <ImageView
  16. android:id="@+id/one"
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"/>
  19. <ImageView
  20. android:id="@+id/two"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"/>
  23. <ImageView
  24. android:id="@+id/three"
  25. android:layout_width="wrap_content"
  26. android:layout_height="wrap_content"/>
  27. </LinearLayout>
  28. </LinearLayout>

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

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

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

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

4.MainActivity的代码如下:

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

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

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

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

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

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

上一篇:Android相关


下一篇:java – Maven – 在当前项目中找不到前缀’wildfly’的插件