Android中Handler、HandlerThread、AsyncTask的应用

2024-02-04 10:18

本文主要是介绍Android中Handler、HandlerThread、AsyncTask的应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。而Handler、HandlerThread、AsyncTask类在消息处理中极其重要,它扮演者负责处理消息的角色。 

1. Handler :

废话不多说了,先看下面的代码:

package com.feixun.hu.hd;  import android.app.Activity;  
import android.os.Bundle;  
import android.os.Handler;  
import android.util.Log;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.EditText;  public class HandlerDemo extends Activity   
{  private int count = 0;  private EditText edit;  private Button start, stop;  private static final String TAG = "HandlerDemo";  /** Called when the activity is first created. */  @Override  public void onCreate(Bundle savedInstanceState)   {  super.onCreate(savedInstanceState);  setContentView(R.layout.main);  //打Log,查看主线程ID  Log.i(TAG, "MainThreadId" + Thread.currentThread().getId());  edit = (EditText)findViewById(R.id.edit);  start = (Button)findViewById(R.id.start);  stop = (Button)findViewById(R.id.stop);  //为按钮绑定监听  start.setOnClickListener(new OnClickListener()  {  @Override  public void onClick(View v)   {  //执行线程run方法  mHandler.post(r);  }  });  stop.setOnClickListener(new OnClickListener()  {  @Override  public void onClick(View v)   {  //移除线程run方法  mHandler.removeCallbacks(r);  }  });  }  private Handler mHandler = new Handler();  private Runnable r = new Runnable()   {  @Override  public void run()  {  // TODO Auto-generated method stub  count++;  edit.setText("" + count);  //每隔2秒执行一次run方法       mHandler.postDelayed(r, 2000);  //打Log查看线程所在ID  Log.i(TAG, "ThreadID" + Thread.currentThread().getId());      }  };  }  


上面的代码主要通过hanlder类是实现隔两秒count自增1的计数功能,然后在edit(EditText对象)中显示不断增加的计数值count;点击start按钮时会启动计数,点击stop按钮时停止计数;  上面的代码主要通过hanlder类是实现隔两秒count自增1的计数功能,然后在edit(EditText对象)中显示不断增加的计数值count;点击start按钮时会启动计数,点击stop按钮时停止计数;

 效果图如下:

  通过打Log可知,run方法所在的线程Id和onCreate方法所在的线程Id是相同的,也就是说,该run方法是在主线程中实现的。所以,Handler类的处理实现始终是在主线程上实现。但是,有的时候,需要开辟一个新的子线程处理一些超时的操作,避免你的应用程序主线程(UI线程)自己去处理这些超时的操作,从而出现ANR(Application Not Responding)异常。这时候就需要用到HandlerThread、AsyncTask来处理了。

2. HandlerThread :

在介绍HandlerThread之前,先给大家上代码,如下:

package com.feixun.hu.htd;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;public class HandlerThreadDemo extends Activity 
{private ProgressBar bar;private BarHandler mHandler;private Runnable BarThread;private static final String TAG = "HandlerThreadDemo";/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);//打log,查看主线程idLog.i(TAG, "Main Thread Id:" + Thread.currentThread().getId());//开启一个新的异步线程HandlerThread handlerThread = new HandlerThread("handler_thread");//在调用getLooper方法之前必须先调用start方法启动线程handlerThread.start();//异步线程加入循环消息队列处理机制mHandler = new BarHandler(handlerThread.getLooper());bar = (ProgressBar)findViewById(R.id.bar);Button start = (Button)findViewById(R.id.start);//为按钮绑定监听start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {bar.setVisibility(ProgressBar.VISIBLE);mHandler.post(BarThread);}});BarThread = new Runnable(){int count = 0;@Overridepublic void run() {//打log,查看该方法所在的线程idLog.i(TAG, "Runnable Thread Id:" + Thread.currentThread().getId());//进度条每秒增加5分值count += 5;//创建消息发送至handlerMessage方法处理Message msg = new Message();Bundle data = new Bundle();data.putInt("count", count);msg.setData(data);mHandler.sendMessage(msg);//线程休眠一秒try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();			}}};}private class BarHandler extends Handler{public BarHandler(Looper looper){super(looper);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);bar.setProgress(msg.getData().getInt("count"));mHandler.post(BarThread);if(msg.getData().getInt("count") >= 100){mHandler.removeCallbacks(BarThread);}}};
}


  上面的Demo主要实现点击start按钮,进度条展现出来,以5分值的进度不断增加显示,直到count大于等于100,才终止进度。

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" ><ProgressBar android:id="@+id/bar"android:layout_width="fill_parent"android:layout_height="wrap_content"android:max="100"style="@android:style/Widget.ProgressBar.Horizontal"android:visibility="gone"/><Button android:id="@+id/start"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="@string/start"/></LinearLayout>

效果图如下:

 上面的代码,通过打Log可知两个方法所在的线程Id不一样,也就是说,通过HandlerThread类实现开启一个新的异步线程处理循环消息队列机制,值得注意的是,handlerMessage方法不是在主线程中运行的,而是在新开辟的异步线程handlerThread中运行的。该方法可以接收来着BarThread的run()方法里发过来的消息(通过hanlder.senMessage()方法发送),然后通过接收的消息来设置ProgressBar的进度等级(亲们,要谨记,UI组件的相关处理操作只能在主线程中,如果在新开的异步线程对应的Handler的handlerMessage中处理UI组件,会出现问题)。

3. AsyncTask :

   我们开发应用程序的时候,经常中需要创建一个子线程来在后台执行一个特定的计算任务,而在这个任务计算的过程中,需要不断地将计算进度或者计算结果展现在应用程序的界面中。典型的例子是从网上下载文件,为了不阻塞应用程序的主线程,我们开辟一个子线程来执行下载任务,子线程在下载的同时不断地将下载进度在应用程序界面上显示出来,这样做出来程序就非常友好。由于子线程不能直接操作应用程序的UI,因此,这时候,我们就可以通过往应用程序的主线程中发送消息来通知应用程序主线程更新界面上的下载进度。因为类似的这种情景在实际开发中经常碰到,Android系统为开发人员提供了一个异步任务类(AsyncTask)来实现上面所说的功能,即它会在一个子线程中执行计算任务,同时通过主线程的消息循环来获得更新应用程序界面的机会。

 

 实现AsyncTask类的回调方法介绍如下:

    i. onPreExecute(),该回调函数在任务被执行之后立即由主线程(UI线程)调用。这个步骤通常用来建立任务,在用户接口(UI)上显示进度条。(准备运行)

  ii. doInBackground(Params...),该回调函数由后台线程在onPreExecute()方法执行结束后立即调用,即该函数不在主线程中,而是在开启的一个异步线程。通常在这里执行耗时的后台计算。计算的结果必须由该函数返回,返回值被传递到onPostExecute()中处理。在该函数内也可以使用publishProgress(Progress...)来发布一个或多个进度单位(unitsof progress)。这些值将会在onProgressUpdate(Progress...)中被发布到UI线程。(后台运行)

  iii.onProgressUpdate(Progress...),该函数由UI线程在publishProgress(Progress...)方法调用完后被调用。一般用于动态地显示一个进度条。实现进度更新。

  iv. onPostExecute(Result),当后台计算结束后调用。后台计算的结果会被作为参数传递给这一函数处理。实现完成后台任务。

  v.  onCancelled (),在调用AsyncTask的cancel()方法时调用,从而实现取消任务。

 

AsyncTask构造函数的三个模板参数:

   i. Params,传递给后台任务的参数类型。

   ii. Progress,后台计算执行过程中,进步单位(progress units)的类型。(就是后台程序已经执行了百分之几了。)

   ii. Result, 后台执行返回的结果的类型。

:AsyncTask并不总是需要使用上面的全部3种类型。标识不使用的类型很简单,只需要使用Void类型即可。

 

  ok,仅仅介绍回调方法还是不够,上代码,通过代码分析讲解才深刻,Demo代码如下:

 

package com.feixun.com.atd;import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;public class AsyncTaskDemo extends Activity 
{private ImageView image;private ProgressBar bar;private Button get;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);image = (ImageView)findViewById(R.id.image);bar = (ProgressBar)findViewById(R.id.bar);get = (Button)findViewById(R.id.btn);get.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {GetPictureTask task = new GetPictureTask();//执行下载网络图片,传入参数为图片所在的URL地址task.execute("http://csdnimg.cn/www/images/csdnindex_logo.gif");}});}private class GetPictureTask extends AsyncTask<String, Integer, Bitmap>{//在后台执行之前被调用,在UI线程中执行@Overrideprotected void onPreExecute() {image.setImageBitmap(null);bar.setProgress(0);}@Overrideprotected Bitmap doInBackground(String... params) {// TODO Auto-generated method stub//该方法执行后,将会调用onProgressUpdate(Integer... values) 方法publishProgress(0);//创建HttpCilent对象HttpClient hc = new DefaultHttpClient();publishProgress(30);//通过传入的参数URL字符串值,发送get请求HttpGet hg = new HttpGet(params[0]);Bitmap bm = null ;try {//响应请求HttpResponse hr = hc.execute(hg);//根据响应的内容获取Bitmap对象bm = BitmapFactory.decodeStream(hr.getEntity().getContent());} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}return bm;}@Overrideprotected void onProgressUpdate(Integer... values) {// TODO Auto-generated method stubsuper.onProgressUpdate(values);bar.setProgress(values[0]);}@Overrideprotected void onPostExecute(Bitmap result){// TODO Auto-generated method stubsuper.onPostExecute(result);if(result != null){//根据获取返回的位图Bitmap获取图片image.setImageBitmap(result);Toast.makeText(AsyncTaskDemo.this, "成功获取图片", Toast.LENGTH_LONG).show();}else{Toast.makeText(AsyncTaskDemo.this, "获取图片失败",Toast.LENGTH_LONG).show();}}//在UI线程执行@Overrideprotected void onCancelled() {// TODO Auto-generated method stubsuper.onCancelled();bar.setProgress(0);}@Overrideprotected void onCancelled(Bitmap result) {// TODO Auto-generated method stubsuper.onCancelled(result);}}
}

   上面的代码实现点击一个按钮下载网络图片,然后以进度条的形式显示图片下载的进度。

 

  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" ><ProgressBar android:id="@+id/bar"android:layout_width="fill_parent"android:layout_height="wrap_content"android:max="100"style="@android:style/Widget.ProgressBar.Horizontal"/><Button android:id="@+id/btn"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="@string/get"/><ImageView android:id="@+id/image"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>

 

注:最后,别忘了在manifest.xml文件里注册权限<uses-permission android:name="android.permission.INTERNET"/>

 

相关的Demo代码下载:http://download.csdn.net/detail/stevenhu_223/4653284

 

这篇关于Android中Handler、HandlerThread、AsyncTask的应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677145

相关文章

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络