您的位置:首页 > 移动开发 > Android开发

Android中的软引用(SoftRefrerence)和弱引用(WeakReference)

2014-11-03 14:49 337 查看
/article/7858212.html

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

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

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

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

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

<span style="font-size:12px;"><?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></span>

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);

两个Demo的下载链接如下:http://download.csdn.net/detail/stevenhu_223/6855591
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐