AndroidHandler18.docx

上传人:b****9 文档编号:29162997 上传时间:2023-07-20 格式:DOCX 页数:15 大小:60.32KB
下载 相关 举报
AndroidHandler18.docx_第1页
第1页 / 共15页
AndroidHandler18.docx_第2页
第2页 / 共15页
AndroidHandler18.docx_第3页
第3页 / 共15页
AndroidHandler18.docx_第4页
第4页 / 共15页
AndroidHandler18.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

AndroidHandler18.docx

《AndroidHandler18.docx》由会员分享,可在线阅读,更多相关《AndroidHandler18.docx(15页珍藏版)》请在冰豆网上搜索。

AndroidHandler18.docx

AndroidHandler18

1.本节主题Handler+Message和多线程

2.知识回顾

2.1开发Android应用时必须遵守单线程模型的原则:

 

2.1.1.AndroidUI操作并不是线程安全的,并且这些操作必须在UI线程中执行。

2.2单线程模型中始终要记住两条法则:

 

2.2.1不要阻塞UI线程;

2.2.2确保只在UI线程中访问AndroidUI控件。

2.3UI线程的主要工作

2.3.1界面的刷新显示

2.3.2向各个UI组件分发事件、触发对应的回调处理

2.4两个主要的错误

2.4.1ANR错误

2.4.2Android4.0以上版本中,主线程中不允许访问网络。

涉及到网络操作的程序一般都是需要开一个新线程完成网络访问。

但是在获得页面数据后,又不能将数据返回到UI界面中。

因为子线程(WorkerThread)不能直接访问UI线程中的成员,也就是说没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:

CalledFromWrongThreadException。

2.4.3扩展对比一个错误OOM(内存溢出错误)

2.5android提供了几种在其他线程中访问UI线程的方法:

 

2.5.1Activity.runOnUiThread(Runnable),了解即可 

2.5.2View.post(Runnable) ,了解即可

2.5.3View.postDelayed(Runnable,long),了解即可 

2.5.4Timer定时;,理解即可

2.5.5ScheduledExecutorService调度任务;,理解即可

2.5.6AsyncTask异步,只能运行在主线程中;熟练掌握

2.5.7Handler消息传递机制熟练掌握并知其原理

2.6android两种事件类型:

 

2.6.1基于监听器回调的事件处理机制,以往的监听器

2.6.2基于消息回调的的事件处理机制,本节内容

3.Handler消息传递机制初步认识:

3.1什么是Handler?

handler通俗一点讲就是用来在【各个线程】之间【发送数据】的【处理对象】。

在任何线程中,只要获得了【另一个线程】的handler,则可以通过handler.sendMessage(message)方法向【另一个线程】发送消息数据。

基于这个机制,我们在处理多线程的时候可以新建一个thread,这个thread拥有UI线程中的一个handler。

当thread处理完一些耗时的操作后通过传递过来的handler向UI线程发送数据,由UI线程去更新界面。

 

 主线程:

运行所有UI组件,内部封装了存放message的消息队列(MessageQueue)。

设备会将用户的每项操作转换为消息,并将它们放入正在运行的消息队列中。

主线程位于一个循环中,通过Looper不断查询并处理处理每条消息。

如果任何一个消息用时超过5秒,Android将抛出ANR。

所以一个任务用时超过5秒,应该在一个独立线程中完成它,或者延迟处理它,当主线程空闲下来再返回来处理它。

3.2、常用类:

(Handler、Looper、Message、MessageQueue)

3.2.1Message:

消息

其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。

3.2.2Handler:

处理者

负责Message的发送及处理。

使用Handler时,需要实现handleMessage(Messagemsg)方法来对特定的Message进行处理,例如更新UI等。

Handler类的主要作用:

(1)、在工作线程中发送消息;

(2)、在主线程中获取、并处理消息。

3.2.3MessageQueue:

消息队列,

用来存放Handler发送过来的消息,并按照FIFO(先进先出)规则执行。

当然,存放Message并非实际意义的保存,而是将Message串联起来的,等待Looper的抽取。

3.2.4Looper:

消息泵,

不断地从MessageQueue中抽取Message执行。

因此,一个MessageQueue需要一个Looper。

3.2.5Thread:

线程

负责调度整个消息循环,即消息循环的执行场所。

3.3、Handler、Looper、Message、MessageQueue之间的关系:

         

3.3.1Looper和MessageQueue【一一对应】,创建一个Looper的同时,会创建一个MessageQueue;

3.3.2而Handler与它们的关系,只是【简单的聚集关系】,即Handler里会引用当前线程里的特定Looper和MessageQueue;

3.3.3在一个线程中,【只能有一个Looper和MessageQueue】,但是【可以有多个Handler】,而且这些Handler可以【共享】一个Looper和MessageQueue;

3.3.4Message被存放在 MessageQueue的队列中,通过Looper按照FIFO规则存取。

3.4主线程和子线程中异同

2.4.1异同点

Looper对象用来为一个线程开启一个消息循环,从而操作MessageQueue;

默认情况下,Android创建的线程没有开启消息循环Looper,但是主线程例外。

系统自动为主线程创建Looper对象,开启消息循环;所以主线程中使用new来创建Handler对象。

而子线程中不能直接new来创建Handler对象就会异常。

3.4.1子线程中创建Handler对象,步骤如下:

【考点】APILooper

classLooperThreadextendsThread{

 privateHandlermHandler;

 publicvoidrun(){

//第1步通过Looper的静态函数,

//生成一个Looper对应的MessageQueue消息队列

  Looper.prepare();

//第2步生成一个Handler

  mHandler=newHandler(){

   publicvoidhandleMessage(Messagemsg){

//接着处理线程的其它事务

......

}

};

//第3步启动Looper内部的消息循环处理机制,自动触发mHandler

Looper.loop();

//线程在Looper.loop()处阻塞,因此下面执行不到,需要处理的程序只能在上面的handleMessage内处理

 }

//为外部应用提供一个访问mHandler的方法

PublicHandlergetHandlerByRunNew(){

returnmHandler;

}

}

4.Handler和Message中常用方法、属性:

4.1Handler中常用的方法

4.1.1handleMessage(),用在主线程中,构造Handler对象时,重写handleMessage()方法。

该方法根据工作线程返回的消息标识,来分别执行不同的操作。

4.1.2sendEmptyMessage(),用在工作线程中,发送空消息。

4.1.3sendMessage(),用在工作线程中,立即发送消息。

4.2、Message消息类中常用属性:

4.2.1arg1 用来存放整型数据

4.2.2arg2 用来存放整型数据

4.2.3what 用于指定用户自定义的消息代码,这样便于主线程接收后,根据消息代码不同而执行不同的相应操作。

4.2.4obj 需要在消息中携带Object类型的数据时使用。

例如String、Bitmap、自定义的数据实体等。

4.3使用Message需要注意4点:

【重点】

4.3.1、Message虽然也可以通过new来获取,但是通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源;

4.3.2、如果一个Message只需要携带简单的int型数据,应优先使用arg1和arg2属性来传递数据,这样比其他方式节省内存;

4.3.3、尽可能使用Message.what来标识信息,以便用不同的方式处理Message;

4.3.4、如果需要从工作线程返回很多数据信息,可以借助Bundle对象将这些数据集中到一起,然后存放到obj属性中,再返回到主线程。

5.常见的几种应用方式

5.1子线程向主线程的Handler发送Message,主线程处理消息;【大量使用,必须掌握】

5.2主线程向子线程的Handler发送Message,子线程处理消息;【少量使用,熟悉】

5.3一个子线程向另一个子线程的Handler发送Message,另一个子线程处理消息;【很少使用,了解】

6.常用的格式化套路模板

6.1子线程向主线程的Handler发送Message:

第1步,在Activity中定义一个成员变量privateHandlermHandler=null;

第2步,在Activity中为message的what特定值,定义一组常量,后续根据常量操作,不易出错。

第3步,在Activity的onCreate()中new一个Handler的实例,并重写其handleMessage()方法;

第4步,在handleMessage()方法中根据what值对应的含义,进行对应的处理业务逻辑处理;

第5步,按需生成线程或线程的子类,同时把Activity中的mHandler通过构造或者方法函数传递给子线程,并保存到子线程的成员变量mHandler中;

第6步,在子线程的run处理中,根据业务需要,生成一个有特定含义的message,然后通过子线程中的mHandler发送消息,发送后Activity中的mHandler就能收到消息,并触发handleMessage()。

6.2示例代码1:

进度百分比:

【重点】

思路:

利用多线程,子线程每隔1秒发送一个当前的进度给主线程,主线程中Handler接收消息,更新进度显示。

6.3、示例代码2:

图片定时切换:

【重点】

思路:

利用多线程,子线程每隔2秒发送一个消息给主线程,主线程中Handler接收消息,并更新ImageView中的图片。

这样就实现了循环切换的动态效果。

6.4、示例代码3:

打地鼠:

思路:

主界面中放置9副地鼠的照片,在子线程中每隔2秒,利用Random().nextInt(9)产生一个随机数,并把随机数发送到主线程,主线程收到消息后根据随机数显式对应的图片,隐藏其它的图片。

6.5、示例代码4:

从网上下载4副图片,并显示到界面的4副图片上:

【重点】

思路:

利用多线程,子线程中依次下载4副图片,并把每副图片的Bitmap对象发送到主线程中,主线程中Handler接收消息,并更新ImageView中的图片。

6.6、【扩展内容】主线程跟子线程之间消息互相传递

7、Handler:

7.1、Handler的概念:

7.1.1Handler是用于发送和处理消息和一个线程的MessageQueue相关联的Runable对象。

 

7.1.2每个Handler实例关联到一个单一线程和线程的messagequeue。

 

7.1.3当您创建一个Handler,从你创建它的时候开始,它就绑定到创建它的线程以及对应的消息队列,handler将发送消息到消息队列,并处理从消息队列中取出的消息。

7.1.4Handler的主要用途有两个:

(1)、在将来的某个时刻执行消息或一个runnable;

(2)、为运行在不同线程中的多个任务排队。

7.2主要依靠以下方法来完成消息调度:

∙post(Runnable)、 

∙postAtTime(Runnable,long)、 

∙postDelayed(Runnable,long)、 

∙sendEmptyMessage(int)、 

∙sendMessage(Message)、 

∙sendMessageAtTime(Message)、 

∙sendMessageDelayed(Message,long) 

【备注:

∙post方法是当到Runable对象到达就被插入到消息队列; 

∙sendMessage方法允许你把一个包含有信息的Message插入消息队列,它会在Handler的handlerMessage(Message)方法中执行(该方法要求在Handler的子类中实现)。

∙当Handlerpost或者send消息的时候,可以在消息队列准备好的时候立刻执行,或者指定一个延迟处理或绝对时间对它进行处理,后两个是实现了timeout、ticks或者其他timing-based的行为。

∙当你的应用创建一个进程时,其主线程(UI线程)会运行一个消息队列,负责管理优先级最高的应用程序对象(Activity、广播接收器等)和任何他们创建的windows。

你也可以创建自己的线程,通过handler与主线程进行通信,在新创建的线程中handler通过调用post或sendMessage方法,将传入的Runnable或者Message插入到消息队列中,并且在适当的时候得到处理。

 

7.3、Handler的用法:

当你实例化一个Handler的时候可以使用Callback接口来避免写自定义的Handler子类。

这里的机制类似与Thread与runable接口的关系。

在Handler里面,子类要处理消息的话必须重写handleMessage()这个方法,因为在handler里面它是个空方法:

7.4、源码分析:

7.4.1A、Handler.java:

(3个属性,9个方法)

7.4.2代码中的3个属性:

∙final MessageQueuemQueue;

∙final LoopermLooper;

∙final CallbackmCallback;

7.4.3代码中的9个方法:

∙publicboolean handleMessage(Messagemsg); 

∙publicfinalMessage obtainMessage()

∙publicfinalboolean sendMessage(Messagemsg)

∙publicfinalboolean sendEmptyMessage(intwhat)

∙publicfinalboolean post(Runnabler)

∙publicfinalbooleanpostAtTime(Runnabler,longuptimeMillis)

∙publicvoiddispatchMessage(Messagemsg)

∙publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis)

∙publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)

8、Looper的源代码分析:

8.1、Looper.JAVA:

(4个属性,4个方法)

每个ThreadLocal中只能有一个Looper,也就是说一个Thread中只有一个Looper

8.2源代码中4个属性:

∙staticfinal ThreadLocal sThreadLocal =newThreadLocal();

∙final MessageQueue mQueue;

∙final Thread mThread;

∙privatestatic Looper mMainLooper =null;

8.3源代码中4个方法:

∙publicstaticvoidprepare()

∙publicstaticvoidprepareMainLooper()

∙publicstaticvoidloop()

∙publicstaticLoopermyLooper()

9.Message的源代码

9.1、Message.java:

(8个属性,5个方法)

9.2源代码中的8个属性:

∙publicint what;

∙publicint arg1;

∙publicint arg2;

∙publicObject obj;

∙Handler target; 

∙Message sPool;

∙int sPoolSize;

∙int MAX_POOL_SIZE=10;

9.2源代码中的5个方法:

∙publicstaticMessage obtain()

∙publicvoid recycle()

∙publicvoid setTarget(Handlertarget)

∙publicHandler getTarget()

∙publicvoid sendToTarget()

10.扩展1【定时器】

Timer与ScheduledExecutorService定时周期执行指定的任务的区别:

10.1、Timer当任务抛出异常时的缺陷:

如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行;但是ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行。

10.2、Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。

10.3、因此建议使用ScheduledExecutorService来取代Timer。

11扩展2在非UI线程中,更新UI问题的方法.

publicvoidupdate(Viewv){

//创建一个非UI线程,也就是工作线程.

newThread(){

publicvoidrun(){

while(count<1000){

count++;

//以下三个方法,都是用来处理,在非UI线程中,更新UI问题的方法.

//android.view.ViewRootImpl$CalledFromWrongThreadException:

//Onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews.

//如果直接在非UI线程中更新UI线程中的组件,那么就会爆上面那个异常.

//tv.setText("count="+count);

//CausestheRunnabletobeaddedtothemessagequeue.

//Therunnablewillberunontheuserinterfacethread.

//这个post方法,会将这个Runnable给添加到一个消息队列中.这个Runnable就会在ui线程中调用.

//tv.post(newRunnable(){//此时newRunnable()可以看做主线程.

//@Override

//publicvoidrun(){

//tv.setText("count="+count);

//}

//});

//也是将一个Runnable给添加到消息队列中,这个runnable对象也是在UI线程中运行.但是是在指定的时间之后去运行.

//CausestheRunnabletobeaddedtothemessagequeue,

//toberunafterthespecifiedamountoftimeelapses.

//Therunnablewillberunontheuserinterfacethread.

//tv.postDelayed(newRunnable(){

//

//@Override

//publicvoidrun(){

//tv.setText("count="+count);

//}

//},3000);

//RunsthespecifiedactionontheUIthread.

//在UI线程中执行一个指定的动作(任务)

//IfthecurrentthreadistheUIthread,

//thentheactionisexecutedimmediately.

//IfthecurrentthreadisnottheUIthread,

//theactionispostedtotheeventqueueoftheUIthread.

MainActivity.this.runOnUiThread(newRunnable(){

@Override

publicvoidrun(){

tv.setText("count="+count);

}

});

try{

Thread.sleep(500);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

};

}.start();

}

11扩展思考3

11.1场景模拟一个中转任务调度。

11.2需求

11.2.1点击Activity中的按钮1时,如果子线程mTA1没有运行则启动运行;mTA1没有接到停止运行时,每隔500毫秒休眠一次,持续运行。

11.2.2点击Activity中的按钮2时,获取mTA1中属于mT1的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 韩语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1