利用 HandlerThread 创建一个后台工作线程( AsyncTask 的缺点),处理图片缩放时内存溢出问题
来源:互联网 发布:ip地址添加网络打印机 编辑:程序博客网 时间:2024/06/07 03:31
在前面的博客中有提到 HandlerThread 的简单使用:http://blog.csdn.net/antimage08/article/details/50473788
HandlerThread 可以帮助创建一个拥有有效 Looper 的后台线程,该 Looper 会关联一个 Handler,而 Handler 中的 MessageQueue 会处理所有的任务。Android 中最常用的后台技术之一就是 AsyncTask,这个了类很好用。但是,它也有一些缺点。一个缺点就是 AsyncTask 只能执行一次且受限,如果想要在 Activity 或服务这样的组件生命周期中执行重复或无限期的任务,AsyncTask 显示有些工作繁重。通常,需要创建多个AsyncTask 实例才能完成这些任务。AsyncTask执行(调用 execute() 方法)之后就不容易停止了,但是可以将其放入 WeakReference (弱引用)中,这样就能很好的停止AsyncTask 的执行。
本例是通过后台线程任务用两个按钮来实现图片的缩放,裁剪。本例中在加载了一个 233kb 大小的 bg_04 之后就出现的内存溢出的情况。通过优化后,解决了该问题。关于 Android 图片内存优化可以参考我以前的博客《Android 图片的内存优化》http://blog.csdn.net/antimage08/article/details/50445820
显示 ic_launcher 时的效果 (没有优化图片)
加载 bg_04 图片时的内存溢出:
优化后的效果:
利用 HandlerThread 创建后台线程的 ImageProcessor.java 该类实现了 Handler.Callback 接口,该接口在前面的 《SurfaceView 实现高性能的绘制》和 《TextureView 的使用 》中都有用到 :
ImageProcessor.java :
package com.crazy.handlerthreadtest;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import java.io.InputStream;/** * Created by antimage on 2016/1/7. */public class ImageProcessor extends HandlerThread implements Handler.Callback { public static final int MSG_SCALE = 0; public static final int MSG_CROP = 1; private Context mContext; private Handler mReceiver, mCallback; public ImageProcessor(Context context) { this(context, null); } public ImageProcessor(Context context, Handler callback) { super("AndroidRecipesWorker"); mCallback = callback; mContext = context.getApplicationContext(); } @Override protected void onLooperPrepared() { mReceiver = new Handler(getLooper(), this); } @Override public boolean handleMessage(Message msg) { Bitmap source, result; // 从传入的消息中解析参数 int scale = msg.arg1; switch (msg.what) { case MSG_SCALE: source = readBitMap(mContext, R.drawable.bg_04); // 加载图片较小时可以这么做,较大时则会 内存溢出 // source = BitmapFactory.decodeResource(mContext.getResources(), // R.drawable.bg_04); // 创建一张新的、缩放的图片 result = Bitmap.createScaledBitmap(source, source.getWidth() * scale, source.getHeight() * scale, true); break; case MSG_CROP: source = readBitMap(mContext, R.drawable.bg_04); // source = BitmapFactory.decodeResource(mContext.getResources(), // R.drawable.ic_launcher); int newWidth = source.getWidth() / scale; // 创建一张新的、横向裁剪的图片 result = Bitmap.createBitmap(source, (source.getWidth() - newWidth) / 2, 0, newWidth, source.getHeight()); break; default: throw new IllegalArgumentException("Unknown Worker Request"); } // 将图片返回给注线程 if (mCallback != null) { mCallback.sendMessage(Message.obtain(null, 0, result)); } return true; } // 添加、删除 回调 Handler public void setCallback(Handler callback){ mCallback = callback; } /* 队列操作相关方法 */ // 缩放图标为特定的值 public void scaleIcon(int scale) { Message msg = Message.obtain(null, MSG_SCALE, scale, 0, null); mReceiver.sendMessage(msg); } // 居中裁剪图标,然后缩放为特定的值 public void cropIcon(int scale) { Message msg = Message.obtain(null, MSG_CROP, scale, 0, null); mReceiver.sendMessage(msg); } /** * 以最省内存的方式读取本地资源的图片 */ public static Bitmap readBitMap(Context mContext, int resId){ BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; // 获取资源图片 InputStream is = mContext.getResources().openRawResource(resId); return BitmapFactory.decodeStream(is,null,opt); }}
HandlerThread 是一个线程,和外部 Handler 一起创建一个后台线程。所以我们必须自己实现一个 Handler 来真正处理我们想要执行的工作。本例自定的处理器实现了 Handler.Callback 接口并传入了线程拥有的新 Handler。这样做避免了使用 Handler 的子类。在 onLooperPrepared() 回调之后接收器 Handler 才会被创建,这是因为我们需要用 HandlerThread 所创建的 Looper 对象将要执行的任务发送到后台线程。
MainActivity.java :
package com.crazy.handlerthreadtest;import android.graphics.Bitmap;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.widget.ImageView;public class MainActivity extends AppCompatActivity implements Handler.Callback{ private ImageProcessor mWorker; private Handler mResponseHandler; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); imageView = (ImageView)findViewById(R.id.image_result); // 该 MainActivity 关联的后台回调 Handler mResponseHandler = new Handler(this); } @Override protected void onResume() { super.onResume(); // 启动一个新的工作线程 mWorker = new ImageProcessor(this, mResponseHandler); mWorker.start(); } @Override protected void onPause() { super.onPause(); // 终止工作线程 mWorker.setCallback(null); mWorker.quit(); mWorker = null; } /** * 后台执行结果的回调方法,运行在 UI 线程上 */ @Override public boolean handleMessage(Message msg) { Bitmap result = (Bitmap)msg.obj; imageView.setImageBitmap(result); return true; } /* 发送后台工作线程的动作方法 */ public void onScaleClick(View v) { for (int i = 1; i < 10; i++) { mWorker.scaleIcon(i); } } public void onCropClick(View v) { // i 的初始值部位 0 ,否则参数传递过去后会报异常(除数不能为0) for (int i = 1; i < 10; i++) { mWorker.cropIcon(i); } }}
启动后台线程只需要调用 HandlerThread 的 start() 方法,,这时会设置 Looper 和 Handler ,然后等待任务的输入。终止后台线程也只需要调用 quit() 方法就可以停止 Looper 并立即移除队列中未处理的消息。将回调设置为 null ,这样此时可能正在运行的任务就不会再通知 Activity le 。
所需的布局文件,content_main.xml :
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.crazy.handlerthreadtest.MainActivity" tools:showIn="@layout/activity_main"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Scale Icon" android:onClick="onScaleClick" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Crop Icon" android:onClick="onCropClick" /> <ImageView android:id="@+id/image_result" android:scaleType="center" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
0 0
- 利用 HandlerThread 创建一个后台工作线程( AsyncTask 的缺点),处理图片缩放时内存溢出问题
- HandlerThread 创建一个异步的后台线程
- 图片内存溢出时的处理
- HandlerThread处理线程实现复杂的事物工作
- Android中处理加载图片时内存溢出问题
- Android中处理加载图片时内存溢出问题
- 安卓图片处理内存溢出问题
- 缩放图片,解决bitmap 内存溢出out of memory的问题
- 图片内存溢出处理
- 彻底解决Java处理图片时,BufferedImage内存溢出的解决方案
- android 下的图片内存溢出处理
- 用HandlerThread创建一个启动线程
- Android 的线程(AsyncTask、HandlerThread、IntentService详解)和线程池
- Android 的线程(AsyncTask、HandlerThread、IntentService详解)和线程池
- 图片缩放处理的一个示例
- 线程相关——HandlerThread、IntentService、ResultReceiver:结果接收者、AsyncTask:异步任务、Android中处理线程间通信的方式
- 处理图片缩放遇到的问题
- 解决Android加载图片时内存溢出的问题
- 数据流图的画法
- 遍历方法
- 活动被回收,怎么保存数据
- 绑定自定义类到Runtime(Lua-binding)
- 实况记录网站被k后采取的措施
- 利用 HandlerThread 创建一个后台工作线程( AsyncTask 的缺点),处理图片缩放时内存溢出问题
- 归档
- POJ 1474 ZOJ 1248 HDU 1469 Video Surveillance 半平面交
- Spring 4 MVC入门实例
- Shell脚本实例
- 为什么自定义集合类
- Linux信号捕捉及其处理
- 圆的面积
- 归档