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

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

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

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

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

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

[html] view plaincopy

  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的代码如下:

[java] view plaincopy

  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的代码如下:

[java] view plaincopy

  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文件代码如下:

[html] view plaincopy

  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代码如下:

[java] view plaincopy

  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代码如下:

[java] view plaincopy

  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的代码如下:

[java] view plaincopy

  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

时间: 2024-07-29 16:11:22

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

Android性能优化之避免内存泄漏的建议

在android程序开发中,内存泄漏问题是比较常见的问题,相信有过一些android编程经历的程序猿都遇到过各种各样的内存泄漏.内存泄漏是造成应用程序OOM的主要原因之一,是编程中必须避免的问题.下面小编搜罗了几个避免内存泄漏的建议,分享给大家,一起来看看吧. 1.对于生命周期比Activity长的对象,如果需要应该使用ApplicationContext : 2.在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Co

android内存优化发展——使用软引用

整个Android开发者一定是遇到了内存溢出这个头疼的问题,一旦这个问题.很难直接决定我们的应用程序是哪里出了问题,为了找到问题的解决方案,必须累积发行通过一些内存分析工具高速定位和强大的体验,现在详细那里能力. 具有此功能基于手机开发,低内存消耗的原则.以及我近期遇到的内存堆积(偶尔溢出)问题,总结一下这次解决问题的经验. 问题源头:開始App功能没那么多的时候,是没有注意到这个问题的.后来功能越强越多.图片也越来越多的时候,用ADT自带的Allocation Tracker查看了一下内存分配

安卓android WebView Memory Leak WebView内存泄漏

Android WebView Memory Leak WebView内存泄漏 在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面后,即使在包含该webview的Activity的destroy()方法中,使用webview.destroy();webview=null;对内存占回收用还是没有任何效果.有人说,一旦在你的xml布局中引用了webview甚至没有使用过,都会阻碍重新进入Application之后对内存的gc.包括

java 谈谈引用(强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference))

简单谈谈引用(摘自java虚拟机第二版 ) 署名:wander   一.四种引用 在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference).软引用(Soft Reference).弱引用(Weak Reference).虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱. 二.引用介绍及回收时机 1.强引用 >>> 就是指在程序代码之中普遍存在的,类似"Object obj=new Object()&q

用Linkedhashmap的LRU特性及SoftReference软引用构建二级缓存

LRU: least recently used(近期最少使用算法).LinkedHashMap构造函数可以指定其迭代顺序:LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 设置accessOrder为true,则按照访问顺序迭代.当linkedhashmap调用put或者putall成功插入键值时,会调用removeEldestEntry方法,根据该方法的返回值决定是否删除最老对象(accessO

Java 理论与实践: 用弱引用堵住内存泄漏---转载

要让垃圾收集(GC)回收程序不再使用的对象,对象的逻辑 生命周期(应用程序使用它的时间)和对该对象拥有的引用的实际 生命周期必须是相同的.在大多数时候,好的软件工程技术保证这是自动实现的,不用我们对对象生命周期问题花费过多心思.但是偶尔我们会创建一个引用,它在内存中包含对象的时间比我们预期的要长得多,这种情况称为无意识的对象保留(unintentional object retention). 全局 Map 造成的内存泄漏 无意识对象保留最常见的原因是使用 Map 将元数据与临时对象(trans

Java 理论与实践: 用弱引用堵住内存泄漏

弱引用使得表达对象生命周期关系变得容易了 虽然用 Java™ 语言编写的程序在理论上是不会出现“内存泄漏”的,但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集.本月,负责保障应用程序健康的工程师 Brian Goetz 探讨了无意识的对象保留的常见原因,并展示了如何用弱引用堵住泄漏. 要让垃圾收集(GC)回收程序不再使用的对象,对象的逻辑 生命周期(应用程序使用它的时间)和对该对象拥有的引用的实际 生命周期必须是相同的.在大多数时候,好的软件工程技术保证这是自动实现的,不用我们对

Android WebView Memory Leak WebView内存泄漏

在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面后,即使在包含该webview的Activity的destroy()方法中,使用webview.destroy();webview=null;对内存占回收用还是没有任何效果.有人说,一旦在你的xml布局中引用了webview甚至没有使用过,都会阻碍重新进入Application之后对内存的gc.包括使用MapView有时一会引发OOM,几经周折在网上看到各种解决办法,在这里跟大

解决android中EditText导致的内存泄漏问题

开发中用到了LeankCanary,在一个简单的页面中(例如 :仅仅 包含Edittext),也会导致内训泄漏,为此,我在网上找了大量资料,最终解决.例如一个布局:<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:focusable="true"android:focusableInTouchMode=&qu