在Android開發中,基本上很少有用到軟引用或弱引用,這兩個東東若用的很好,對自己開發的代碼質量的提高有很大的幫助。若用的不好,會坑了自己。所以,在還沒有真正的去了解它們之前,還是慎用比較好。
下面將通過兩個Demo來結識軟引用和弱引用在開發中的運用。
一. WeakReference:防止內存泄漏,要保證內存被虛擬機回收。
下面以一個時間更新的Demo來說明弱引用的運用。
1. main.xml文件代碼如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <com.stevenhu.wrt.DigitalClock
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="50pt"
- />
- <TextView android:id="@+id/ampm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="25pt"
- />
- </com.stevenhu.wrt.DigitalClock>
- </LinearLayout>
2.自定義ViewGroup類DigitalClock的代碼如下:
- package com.stevenhu.wrt;
- import java.lang.ref.WeakReference;
- import java.text.DateFormatSymbols;
- import java.util.Calendar;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.database.ContentObserver;
- import android.graphics.Canvas;
- import android.os.Handler;
- import android.provider.Settings;
- import android.text.format.DateFormat;
- import android.util.AttributeSet;
- import android.view.View;
- import android.widget.LinearLayout;
- import android.widget.TextView;
- import android.widget.Toast;
- public class DigitalClock extends LinearLayout {
- // 12小時、24小時制
- private final static String M12 = "h:mm";
- private final static String M24 = "kk:mm";
- private Calendar mCalendar;
- private String mFormat;
- private TextView mDislpayTime;
- private AmPm mAmPm;
- private ContentObserver mFormatChangeObserver;
- private final Handler mHandler = new Handler();
- private BroadcastReceiver mReceiver;
- private Context mContext;
- public DigitalClock(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- // TODO Auto-generated constructor stub
- }
- @Override
- protected void onFinishInflate() {
- // TODO Auto-generated method stub
- super.onFinishInflate();
- mDislpayTime = (TextView) this.findViewById(R.id.time);
- mAmPm = new AmPm(this);
- mCalendar = Calendar.getInstance();
- //設置時間顯示格式
- setDateFormat();
- }
- @Override
- protected void onAttachedToWindow() {
- // TODO Auto-generated method stub
- super.onAttachedToWindow();
- //動態注冊監聽時間改變的廣播
- if (mReceiver == null) {
- mReceiver = new TimeChangedReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- mContext.registerReceiver(mReceiver, filter);
- }
- //注冊監聽時間格式改變的ContentObserver
- if (mFormatChangeObserver == null) {
- mFormatChangeObserver = new FormatChangeObserver(this);
- mContext.getContentResolver().registerContentObserver(
- Settings.System.CONTENT_URI, true, mFormatChangeObserver);
- }
- //更新時間
- updateTime();
- }
- @Override
- protected void onDetachedFromWindow() {
- // TODO Auto-generated method stub
- super.onDetachedFromWindow();
- if (mReceiver != null) {
- mContext.unregisterReceiver(mReceiver);
- }
- if (mFormatChangeObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(
- mFormatChangeObserver);
- }
- mFormatChangeObserver = null;
- mReceiver = null;
- }
- static class AmPm {
- private TextView mAmPmTextView;
- private String mAmString, mPmString;
- AmPm(View parent) {
- mAmPmTextView = (TextView) parent.findViewById(R.id.ampm);
- String[] ampm = new DateFormatSymbols().getAmPmStrings();
- mAmString = ampm[0];
- mPmString = ampm[1];
- }
- void setShowAmPm(boolean show) {
- if (mAmPmTextView != null) {
- mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- }
- void setIsMorning(boolean isMorning) {
- if (mAmPmTextView != null) {
- mAmPmTextView.setText(isMorning ? mAmString : mPmString);
- }
- }
- }
- /*時間刷新涉及到View的更新顯示(特別是每秒刷新顯示,這樣的頻率特別高),當然,此處的時間顯示是每分鍾更新一次
- * 所以在監聽時間更新的廣播中采用弱引用,防止在不斷刷新當前界面View時產生內存泄露
- */
- private static class TimeChangedReceiver extends BroadcastReceiver {
- //采用弱引用
- private WeakReference<DigitalClock> mClock;
- private Context mContext;
- public TimeChangedReceiver(DigitalClock clock) {
- mClock = new WeakReference<DigitalClock>(clock);
- mContext = clock.getContext();
- }
- @Override
- public void onReceive(Context context, Intent intent) {
- // Post a runnable to avoid blocking the broadcast.
- final boolean timezoneChanged = intent.getAction().equals(
- Intent.ACTION_TIMEZONE_CHANGED);
- //從弱引用中獲取對象
- final DigitalClock clock = mClock.get();
- if (clock != null) {
- clock.mHandler.post(new Runnable() {
- public void run() {
- if (timezoneChanged) {
- clock.mCalendar = Calendar.getInstance();
- }
- clock.updateTime();
- }
- });
- } else {
- try {
- mContext.unregisterReceiver(this);
- } catch (RuntimeException e) {
- // Shouldn't happen
- }
- }
- }
- };
- // 監聽時間顯示的格式改變
- private static class FormatChangeObserver extends ContentObserver {
- // 采用弱應用
- private WeakReference<DigitalClock> mClock;
- private Context mContext;
- public FormatChangeObserver(DigitalClock clock) {
- super(new Handler());
- mClock = new WeakReference<DigitalClock>(clock);
- mContext = clock.getContext();
- }
- @Override
- public void onChange(boolean selfChange) {
- DigitalClock digitalClock = mClock.get();
- //從弱引用中取出對象
- if (digitalClock != null) {
- //根據弱引用中取出的對象進行時間更新
- digitalClock.setDateFormat();
- digitalClock.updateTime();
- } else {
- try {
- mContext.getContentResolver().unregisterContentObserver(
- this);
- } catch (RuntimeException e) {
- // Shouldn't happen
- }
- }
- }
- }
- // 更新時間
- private void updateTime() {
- Toast.makeText(mContext, "updateTime", Toast.LENGTH_SHORT).show();
- mCalendar.setTimeInMillis(System.currentTimeMillis());
- CharSequence newTime = DateFormat.format(mFormat, mCalendar);
- mDislpayTime.setText(newTime);
- mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
- }
- private void setDateFormat() {
- // 獲取時間制
- mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) ? M24
- : M12;
- // 根據時間制顯示上午、下午
- mAmPm.setShowAmPm(mFormat.equals(M12));
- }
- @Override
- protected void onDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- super.onDraw(canvas);
- //Toast.makeText(mContext, "ddd", Toast.LENGTH_SHORT).show();
- }
- }
3.MainActivity的代碼如下:
- package com.stevenhu.wrt;
- import android.app.Activity;
- import android.os.Bundle;
- public class MainActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
- }
二. SoftReference:實現緩存機制
下面的Demo實現從網絡上獲取圖片,然后將獲取的圖片顯示的同時,通過軟引用緩存起來。當下次再去網絡上獲取圖片時,首先會檢查要獲取的圖片緩存中是否存在,若存在,直接取出來,不需要再去網絡上獲取。
1.main.xml文件代碼如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <Button
- android:id="@+id/get_image"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="get Image"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <ImageView
- android:id="@+id/one"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <ImageView
- android:id="@+id/two"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <ImageView
- android:id="@+id/three"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </LinearLayout>
2.實現異步加載圖片功能的類AsyncImageLoader代碼如下:
- package com.stevenhu.lit;
- import java.lang.ref.SoftReference;
- import java.net.URL;
- import java.util.HashMap;
- import java.util.Map;
- import android.graphics.drawable.Drawable;
- import android.os.Handler;
- import android.os.Message;
- //實現圖片異步加載的類
- public class AsyncImageLoader
- {
- //以Url為鍵,SoftReference為值,建立緩存HashMap鍵值對。
- private Map<String, SoftReference<Drawable>> mImageCache =
- new HashMap<String, SoftReference<Drawable>>();
- //實現圖片異步加載
- public Drawable loadDrawable(final String imageUrl, final ImageCallback callback)
- {
- //查詢緩存,查看當前需要下載的圖片是否在緩存中
- if(mImageCache.containsKey(imageUrl))
- {
- SoftReference<Drawable> softReference = mImageCache.get(imageUrl);
- if (softReference.get() != null)
- {
- return softReference.get();
- }
- }
- final Handler handler = new Handler()
- {
- @Override
- public void dispatchMessage(Message msg)
- {
- //回調ImageCallbackImpl中的imageLoad方法,在主線(UI線程)中執行。
- callback.imageLoad((Drawable)msg.obj);
- }
- };
- /*若緩存中沒有,新開辟一個線程,用於進行從網絡上下載圖片,
- * 然后將獲取到的Drawable發送到Handler中處理,通過回調實現在UI線程中顯示獲取的圖片
- */
- new Thread()
- {
- public void run()
- {
- Drawable drawable = loadImageFromUrl(imageUrl);
- //將得到的圖片存放到緩存中
- mImageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
- Message message = handler.obtainMessage(0, drawable);
- handler.sendMessage(message);
- };
- }.start();
- //若緩存中不存在,將從網上下載顯示完成后,此處返回null;
- return null;
- }
- //定義一個回調接口
- public interface ImageCallback
- {
- void imageLoad(Drawable drawable);
- }
- //通過Url從網上獲取圖片Drawable對象;
- protected Drawable loadImageFromUrl(String imageUrl)
- {
- try {
- return Drawable.createFromStream(new URL(imageUrl).openStream(),"debug");
- } catch (Exception e) {
- // TODO: handle exception
- throw new RuntimeException(e);
- }
- }
- }
3. 實現ImageCallback回調接口的類ImageCallbackImpl代碼如下:
- package com.stevenhu.lit;
- import android.graphics.drawable.Drawable;
- import android.widget.ImageView;
- import com.stevenhu.lit.AsyncImageLoader.ImageCallback;
- public class ImageCallbackImpl implements ImageCallback
- {
- private ImageView mImageView;
- public ImageCallbackImpl(ImageView imageView)
- {
- mImageView = imageView;
- }
- //在ImageView中顯示從網上獲取的圖片
- @Override
- public void imageLoad(Drawable drawable)
- {
- // TODO Auto-generated method stub
- mImageView.setImageDrawable(drawable);
- }
- }
4.MainActivity的代碼如下:
- package com.stevenhu.lit;
- import android.app.Activity;
- import android.graphics.drawable.Drawable;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ImageView;
- public class MainActivity extends Activity implements OnClickListener
- {
- //創建異步加載圖片類對象
- private AsyncImageLoader mImageLoader = new AsyncImageLoader();
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Button get = (Button)findViewById(R.id.get_image);
- get.setOnClickListener(this);
- }
- private void loadImage(final String url, final int id)
- {
- ImageView imageView = (ImageView)findViewById(id);
- ImageCallbackImpl callbackImpl = new ImageCallbackImpl(imageView);
- Drawable cacheImage = mImageLoader.loadDrawable(url, callbackImpl);
- //若緩存中存在,直接取出來顯示
- if (cacheImage != null)
- {
- imageView.setImageDrawable(cacheImage);
- }
- }
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- if (v.getId() == R.id.get_image)
- {
- //從網絡上獲取海賊王的三張圖片顯示
- loadImage("http://wenwen.soso.com/p/20111003/20111003194816-1615366606.jpg", R.id.one);
- loadImage("http://t10.baidu.com/it/u=2492256852,4267838923&fm=23&gp=0.jpg", R.id.two);
- loadImage("http://wenwen.soso.com/p/20100410/20100410102416-1948049438.jpg", R.id.three);
- }
- }
- }
最后,對於這兩者,作個小總結:
1. SoftReference<T>:軟引用-->當虛擬機內存不足時,將會回收它指向的對象;需要獲取對象時,可以調用get方法。
2. WeakReference<T>:弱引用-->隨時可能會被垃圾回收器回收,不一定要等到虛擬機內存不足時才強制回收。要獲取對象時,同樣可以調用get方法。
3. WeakReference一般用來防止內存泄漏,要保證內存被虛擬機回收,SoftReference多用作來實現緩存機制(cache);
note:轉自http://blog.csdn.net/stevenhu_223/article/details/18360397