ThreadActivity
package org.wp.activity;
import java.io.InputStream;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
public class ThreadActivity extends Activity {
private Context mContext = null;
private Handler iHandler = null;
/** 开始加载图片按钮 **/
private Button myButton = null;
/** 显示内存信息 **/
private TextView miTextView = null;
/** 显示加载信息 **/
private TextView liTextView = null;
/** 获得手机内存信息 **/
private ActivityManager activityManager = null;
private MemoryInfo mi = null;
/** 加载图片进度 **/
private static final int LOAD_PROGRESS = 0;
/** 加载图片结束 **/
private static final int LOAD_COMPLETE = 1;
/** 加载图片开始时间 **/
private Long mLoadStartTime = 0L;
/** 加载图片结束时间 **/
private Long mLoadEndTime = 0L;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 去掉标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 设置横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.thread);
mContext = this;
addMessageHandler();
activityManager = (ActivityManager) mContext
.getSystemService(Context.ACTIVITY_SERVICE);
mi = new MemoryInfo();
miTextView = (TextView) this.findViewById(R.id.miTextView);
liTextView = (TextView) this.findViewById(R.id.liTextView);
myButton = (Button) this.findViewById(R.id.myButton);
myButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 加载图片
loadImage();
}
});
}
private void addMessageHandler() {
iHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LOAD_PROGRESS:
// 加载过程中
liTextView.setText("当前读取到第" + msg.arg1 + "张图片");
activityManager.getMemoryInfo(mi);
miTextView.setText("剩余内存:" + (mi.availMem >> 10) + "K"
+ "======" + (mi.availMem >> 20) + "M" + "======"
+ mi.lowMemory);
break;
case LOAD_COMPLETE:
// 记载结束
liTextView.setText("读取结束一共耗时" + msg.arg1 + "毫秒");
break;
}
}
};
}
private void loadImage() {
new Thread() {
public void run() {
// 加载图片的开始时间
mLoadStartTime = System.currentTimeMillis();
// 循环加载2000张图片
for (int i = 0; i < 2000; i++) {
readBitmap(mContext, R.drawable.one_piece);
// 发送加载个数
Message msg = iHandler.obtainMessage();
msg.what = LOAD_PROGRESS;
msg.arg1 = i + 1;
msg.sendToTarget();
}
// 加载图片的结束时间
mLoadEndTime = System.currentTimeMillis();
// 发送加载完成信息
Message msg = iHandler.obtainMessage();
msg.what = LOAD_COMPLETE;
msg.arg1 = (int) (mLoadEndTime - mLoadStartTime);
msg.sendToTarget();
}
}.start();
}
/**
* 尽量不要使用setImageBitmap或setImageResource
* 或BitmapFactory.decodeResource来设置一张大图,
* 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,
* 需要消耗更多内存。
* 因此,改用先通过BitmapFactory.decodeStream方法,
* 创建出一个bitmap,再将其设为ImageView的 source,
* decodeStream最大的秘密在于其直接调用 JNI >> nativeDecodeAsset() 来完成decode,
* 无需再使用java层的createBitmap,从而节省了java层的空间。
*
* 如果在读取时加上图片的Config参数,可以更有效减少加载的内存,
* 从而有效阻止抛出out of Memory异常
* 另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应,
* 使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,
* 否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。
*
* BitmapFactory.Options.inPreferredConfig
*
* ALPHA_8:数字为8,图形参数应该由一个字节来表示,应该是一种8位的位图
* ARGB_4444:4+4+4+4=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.
* ARGB_8888:8+8+8+8=32,图形的参数应该由四个字节来表示,应该是一种32位的位图.
* RGB_565:5+6+5=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.
*
* ALPHA_8,ARGB_4444,ARGB_8888都是透明的位图,也就是所字母A代表透明。
* ARGB_4444:意味着有四个参数,即A,R,G,B,每一个参数由4bit表示.
* ARGB_8888:意味着有四个参数,即A,R,G,B,每一个参数由8bit来表示.
* RGB_565:意味着有三个参数,R,G,B,三个参数分别占5bit,6bit,5bit.
*
*
* BitmapFactory.Options.inPurgeable;
*
* 如果 inPurgeable 设为True的话表示使用BitmapFactory创建的Bitmap
* 用于存储Pixel的内存空间在系统内存不足时可以被回收,
* 在应用需要再次访问Bitmap的Pixel时(如绘制Bitmap或是调用getPixel),
* 系统会再次调用BitmapFactory decoder重新生成Bitmap的Pixel数组。
* 为了能够重新解码图像,bitmap要能够访问存储Bitmap的原始数据。
*
* 在inPurgeable为false时表示创建的Bitmap的Pixel内存空间不能被回收,
* 这样BitmapFactory在不停decodeByteArray创建新的Bitmap对象,
* 不同设备的内存不同,因此能够同时创建的Bitmap个数可能有所不同,
* 200个bitmap足以使大部分的设备重新OutOfMemory错误。
* 当isPurgable设为true时,系统中内存不足时,
* 可以回收部分Bitmap占据的内存空间,这时一般不会出现OutOfMemory 错误。
*
*
* inInputShareable 是否深拷贝
* This field works in conjuction with inPurgeable.
* If inPurgeable is false, then this field is ignored. If inPurgeable is
* true, then this field determines whether the bitmap can share a reference
* to the input data (inputstream, array, etc.) or if it must make a deep copy.
*
* @param context
* @param resId
* @return Bitmap
*/
public Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Config.RGB_565;
opts.inPurgeable = true;
opts.inInputShareable = true;
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opts);
}
}
thread.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:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Thread循环加载100张图片" android:id="@+id/myButton" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/miTextView" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/liTextView" /> </LinearLayout>