




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Android项目课程的设计与实施(国培)第8天Android线程机制项目涉及技能#技能项重点难点隶属课程1Activity组件√Android程序设计基础2Boradcastreceiver组件√√Android程序设计基础3线程√√Android程序设计基础4Service组件√Android程序设计基础5自定义组件(button)√√Android高级程序设计6文件读写与数据存储√Android高级程序设计主要内容认识线程线程的基本用法线程间通信机制AsyncTask子线程更新UI线程同步认识线程Android是单线程模型,我们创建的Service、Activity以及Broadcast均是在一个主线程处理,这里我们可以理解为UI线程。
但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们考虑使用Thread线程来解决。认识线程进程与线程线程是指进程内的一个执行单元,也是进程内的可调度实体,与进程的区别:
(1)地址空间:进程内的一个执行单元;进程至少有一个线程,它们共享进程的地址空间;而进程有自己独立的地址空间;
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是.
(4)二者均可并发执行.
基本用法实现继承java.lang.Thread类
实现Runnable接口启动Thread类代表线程类,它的两个最主要的方法是:
run()——包含线程运行时所执行的代码
Start()——用于启动线程通信Handler机制,它是Runnable和Activity交互的桥梁,在run方法中发送Message,在Handler里,通过不同的Message执行不同的任务。线程同步简介当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题。实现方法同步代码块:
synchronized(同一个数据){}
同一个数据:就是N条线程同时访问一个数据。同步方法:
publicsynchronized
数据返回类型
方法名(){}子线程更新UI子线程更新UI的四种方法handle.post(Runnabler)handle.handleMessage(Messagemsg)runOnUiThread(Runnabler)View.post(Runnabler)线程间通讯
Handler
:在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。
Looper:负责管理线程的消息队列和消息循环
。Message
:线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息。
MessageQueue:消息队列,先进先出,它的作用是保存有待线程处理的消息。
用于线程间通讯的类Android多线程机制
Android多线程3.1使用线程任何耗时的处理过程都会降低用户界面的响应速度,甚至导致用户界面失去响应,当用户界面失去响应超过5秒钟,Android系统会允许用户强行关闭应用程序较好的解决方法是将耗时的处理过程转移到子线程上,这样可以避免负责界面更新的主线程无法处理界面事件,从而避免用户界面长时间失去响应本地服务3.1使用线程线程是独立的程序单元,多个线程可以并行工作在多处理器系统中,每个中央处理器(CPU)单独运行一个线程,因此线程是并行工作的在单处理器系统中,处理器会给每个线程一小段时间,在这个时间内线程是被执行的,然后处理器执行下一个线程,这样就产生了线程并行运行的假象无论线程是否真的并行工作,在宏观上可以认为子线程是独立于主线程,且能与主线程并行工作的程序单元3本地服务3.1使用线程Android中的线程是基于Java定义的线程,其内部结构如图所示:…消息队列消息队列线程1run(){…}线程Nrun(){…}3本地服务3.1使用线程一个应用程序中可能会包含多个线程(Thread),每个线程中都有一个run()方法,run()方法内部的程序执行完毕后,所在的线程就自动结束。每个线程都有一个消息队列,用于不同的线程之间传递消息。在run()方法内部,如果不主动去读取消息队列中的消息,这些消息就是一些无用的消息,因为它们没有被处理。3本地服务3.1使用线程在Android系统中,读取消息和处理消息是两个步骤,并由两个不同的部分完成,先读取消息,然后才能处理消息。无论是本线程的还是其它线程,都不能直接处理消息队列中的消息,而是需要通过在线程内部定义一个Handler类对象来处理消息队列。一个Thread只能包含一个Handler对象。在实际应用中,读取消息队列一般需要循环执行,即不断地从消息队列中获取消息并进行相应处理,这就又需要一个Looper对象。3本地服务3.1使用线程Looper对象用于循环读取消息队列的值,并回调Handler对象中定义的消息处理函数,同时,Looper对象还可以将读取的消息从队列中移除,执行完一次消息处理后,再循环从消息队列中读取下一个消息,直到Looper对象调用stop()方法退出循环。如果消息队列中没有消息,Looper对象则会等待,线程不会退出。为了更方便地从线程中使用Looper功能,Android又定义了一个HandlerThread类,该类基于Thread,并且内部已经添加了Looper功能,使用者只需重写其onLooperPrepared()方法,添加具体应用代码即可。Android中一个Activity就是一个线程,多个Activity之间的切换是在同一个线程中。Android中Activity的调用流程publicclassPseudoActivityextendsThread{ voidrun(){ while(1){ //如果定义了Handler对象,则进行消息派发
if(hasHandler()){ if(MessageQueue.getMessage()!=null) Handler.handleMessage(); //执行完一个消息后,接着执行与用户界面相关的响应。
activity(){ //以下是Activity的核心代码,包括用户按键消息、菜单、对话框等处理。 //经过ifelse等程序控制语句,回调一些接口函数,如onXXX()。 if..else.. switch...case...for...onCreate...,,,onStart(); }}}} //以下为一些回调函数定义
publicvoidonCreate(){ } publiconStart(){ } ....}
3本地服务3.1使用线程以上伪代码中,while(1)循环用于指定该Thread一直执行,永不退出,直到被操作系统杀掉。if(hasHandler())语句判断该Thread中是否定义了Handler对象,该Handler对象是由应用程序定义的(也可不定义);如果有该对象,那么就会读取Thread中消息队列的值,并做一定的处理。执行完一个消息后,接着需要执行Activity中的用户界面响应,例如是否有按键按下、触摸屏是否按下等。处理完一次用户消息响应后,则继续循环读取Thread中消息队列的值。MessageQueue、Looper、Handler调用关系在以上这个大循环中,Handler对象对应的就是Handler.handleMessage()部分完成的功能,Looper对象对应的就是整个while(1)循环控制和MessageQueue.getMessage()完成的功能。一个ThreadHandler接口对象Looper对象MessageQueue(消息队列)其它功能函数3本地服务3.1使用线程Looper对象负责从线程的消息队列中循环读取消息值,再将这些消息传递给Handler对象,Handler对象中定义的消息处理函数会根据消息类型再调用Thread中定义的其它函数。如果Thread中没有Looper对象,那么Thread的执行体就无法读取消息队列;如果Thread中没有Handler对象,则不会处理任何消息。一般情况下,Handler和Looper是同时使用的,要么同时有,要么同时没有。Android多线程定义
4线程定义4.1线程定义Android中定义线程的方法与Java相同,可以使用两种方法:一种是Thread类,另一种是Runnable接口。Thread是一个类,根据Java继承风格,一个类只能有一个父类,继承了Thread的子类不能再继承其它类,这是一个缺陷。于是,出现了Runnable,其作用和Thread相同,都是启动另一个线程,不同的是,Runnable是一个接口(interface),因此可以同时实现多个接口。4线程定义4.1线程定义Android中使用Thread与Java基本相同,所不同的是,Android抛弃了Java线程中一些不安全的做法。比如:终止一个Thread,在Java中可以调用线程名字.stop()、线程名字.destroy()等;而在Android中,这些方法都没有实现,即不能使用。新建一个Thread对象,需要实现两个方法:4线程定义4.1线程定义第一个是定义构造方法。在Android程序中,新建的线程多为Activity、Service等程序片断服务,而在线程的内部执行过程中,很多时候都需要使用应用程序内部的Context对象,因此,在实际应用中,线程的构造方法往往会传递应用程序的Context对象,从而在线程的内部可以调用Context相关的系统服务。当然,这不是必须的。第二个是run()方法,该方法是Thread对象中必须实现的方法,用于完成具体的任务。启动线程时,不能直接调用线程名字.run()方法,而是调用线程名字.start()方法启动,start()方法是Thread内部使用的,该方法包含初始化线程的工作,然后回调run()方法,这些对应用程序都是不可见的。4线程定义4.1线程定义停止线程时,不能调用线程名字.destroy()方法或者线程名字.stop()方法。run()方法执行完毕后,线程默认会自动停止。因此,如果需要线程循环执行run()方法内部的代码,可以在线程内部增加一个状态变量,run()方法内部通过检查该状态变量,决定是否继续执行;同时可在线程外设置该状态变量的值,从而终止该线程。4线程定义4.1线程定义线程定义Thread继承Thread类,并重写run()方法。在run()中放置代码的主体部分classThread1extendsThread{privateContextmCtx; Thread1(Contextcontext){ mCtx=context; }@Override publicvoidrun(){
//过程代码}4线程定义4.1线程定义以上代码包括3个基本方法:Thread1()为构造方法,用于保存调用者的Context对象,供以后可能使用;run()方法内部是应用代码;setToStop()用于设置全局变量mRunState的值,run()内部循环执行时会判断该值,决定是否退出run()方法,即终止该线程。要在Activity启动Thread1,首先需要定义一个Thread1对象,并使用构造方法将Activity的Context对象传递给Thread1,然后调用线程的start()方法启动Thread1线程。要终止Thread的运行,可调用自定义的Thread1的setToStop()方法。以上代码中,id值为action_stop的按钮,用于停止Thread1的运行。这是Android系统建议的启动线程和退出线程的方法。4线程定义4.1线程定义RunnableRunnable的作用和Thread基本相同,都是用于定义一个线程,但两者本质上有重要区别。第1:Runnable只是一个接口(interface),其内部没有定义任何已实现的方法。因此,要使用与线程有关的方法,只能使用Thread的静态方法,比如:不能直接调用sleep(),而要调用Thread.sleep()方法。4线程定义4.1线程定义Runnable第2:定义一个Thread对象,就意味着创建了一个新线程,而定义一个Runnable对象,只是定义了一个可以当作线程运行的代码对象,并没有创建新线程。因此,如果调用Runnable对象的run()方法,仅相当于把Runnable对象当作普通类对象进行调用,并没有启动一个新线程,Runnable对象和调用者在同一个线程中运行。如果要创建一个新线程,则还需要将Runnable对象传入Thread的构造方法,从而创建一个新线程,新线程的执行码就是Runnable所定义的。4线程定义4.1线程定义Runnable第3:Runnable对象经常被当作参数传递给一些与线程有关的方法,用于启动一个新的线程。4线程定义4.1线程定义线程定义Runnable实现Java的Runnable接口,并重载run()方法。在run()中放置代码的主体部分privateRunnablebackgroudWork=newRunnable(){ @Override publicvoidrun(){ //过程代码 } };4线程定义4.1线程定义线程定义实现Java的Runnable接口,并重载run()方法。在run()中放置代码的主体部分。privateRunnablebackgroudWork=newRunnable(){ @Override publicvoidrun(){ //过程代码 } };4线程定义4.1线程定义线程定义创建Thread对象,并将上面实现的Runnable对象作为参数传递给Thread对象Thread的构造函数中,第1个参数用来表示线程组第2个参数是需要执行的Runnable对象第3个参数是线程的名称调用start()方法启动线程privateThreadworkThread;workThread=newThread(null,backgroudWork,"WorkThread");workThread.start();4线程定义4.1线程定义线程在run()方法返回后,线程就自动终止了;不推荐使用调用stop()方法在外部终止线程最好的方法是通知线程自行终止,一般调用interrupt()方法通告线程准备终止,线程会释放它正在使用的资源,在完成所有的清理工作后自行关闭interrupt()方法并不能直接终止线程,仅是改变了线程内部的一个布尔字段,run()方法能够检测到这个布尔字段,从而知道何时应该释放资源和终止线程在run()方法的代码,一般通过Terrupted()方法查询线程是否被中断workTerrupt();4线程定义4.1线程定义下面的代码是以1秒为间隔循环检测断线程是否被中断第4行代码使线程休眠1000毫秒当线程在休眠过程中被中断,则会产生InterruptedException在中断的线程上调用sleep()方法,同样会产生InterruptedExceptionpublicvoidrun(){ while(!Terrupted()){ //过程代码 Thread.sleep(1000); }}4线程定义4.1线程定义Terrupted()方法功能判断线程是否应被中断通过捕获InterruptedException判断线程是否应被中断,并且在捕获到InterruptedException后,安全终止线程publicvoidrun(){ try{ while(true){ //过程代码 Thread.sleep(1000); } }catch(InterruptedExceptione){ e.printStackTrace(); }}Handler5
Handler5.1使用HandlerHandler用于处理线程中的消息队列。当Looper对象从消息队列中获取消息后,会把消息派发给Handler对象。一个线程中只能有一个Handler对象,可以通过该对象向所在线程发送消息。因此,只要拥有其它线程中Handler对象的引用,就可以向其发送消息;除了给别的线程发送消息外,还可以给本线程发送消息。5
Handler5.1使用HandlerHandler一般有两种用途:实现一个定时任务。这个有点类似于Windows中的定时器功能,可以通过Handler对象向所在线程发送一个延时消息。当消息指定的时间到达后,通过Handler对象的消息处理方法完成指定任务。在线程间传递数据。5
Handler5.1使用HandlerHandler完成定时任务在一个Activity内部,经常需要做一些定时器的功能,比如周期性更新某个视图的内容、在指定时间后结束某个操作等。完成定时任务,可以通过Handler对象的延迟发送消息方法来实现。在介绍发送消息之前,需要先了解一下消息Message的数据结构。Message是一个描述消息的数据结构类,Message包含很多成员变量和方法,但对于简单的消息处理,一般仅需了解3项,分别是:intwhat这是用户自定义的一个整型值,用于区分消息类型。intarg1这是额外消息参数intarg2同arg1对于需要包含更多数据的消息,可以使用message.setData()和getData()方法。setData()方法用于把一个Bundle类数据对象加入到Message中,而getData()则是取出该Bundle数据。Bundle数据类型就是包含“键值对”数据的类型。Handler发送消息的方式:一类是postXXX()方法,该方法用于把一个Runnable对象发送到消息队列。从而当消息被处理时,能够执行Runnable对象;另一类是sendXXX()方法,该方法用于发送一个Message类型的消息到消息队列,当消息被处理时,系统会调用Handler对象定义的handleMessage()方法处理该消息。实现定时任务则主要使用sendXXX()类,该类具体包含如下方法:sendEmptyMessage(intwhat),空消息是指该消息仅包含what值。sendEmptyMessageAtTime(intwhat,longuptimeMilis),在指定时间点发送空消息,uptimeMilis是指从本次开机开始运行的时间点,不包含系统休眠的时间,单位为毫秒。参照SystemClock类。sendEmptyMessageDelayed(intwhat,longdelayMillis),在指定时间后发送空消息,指定的时间以毫秒为单位。sendMessage(Message),发送Message指定的消息。sendMessageAtTime(Message,long),在指定时间点发送该消息。sendMessageDelayed(Message,long),在指定的时间后发送该消息。实现定时任务时,一般使用sendMessageAtDelayed()或者sendEmptyMessageAtDelayed()方法,即在指定的时间后发送消息。当收到该消息后,系统会调用Handler对象实现的消息处理接口handlhandleMessage()的参数是Message对象,可以通过Message的相关方法获得Message的具体值,并根据其消息完成不同的任务。Handle定时任务案例演示1)、handleMessage用于处理Activity所在线程接收到的消息,此处是把当前时间显示在文本框中。obtainMessage()方法用于从全局的消息池中获得一个已有的Message对象,系统为了加速线程间的消息传递,创建了一些全局的消息对象供各线程使用,这些全局消息对象称为全局消息池,使用该方法比重新创建一个消息对象的效率高。该方法的第一个参数用于指定初始化返回消息的what值。sendMessageDelayed()方法用于在1000毫秒后发送what值为100的消息,即在显示完当前时间后的1秒,再发送一次消息,从而可以每过1秒更新一次文本框的时间。此处使用100代表该消息类型。2)、需要注意的是:在应用程序运行时,当用户按Back键返回后,尽管Activity进入了暂停或者停止的状态,但是消息的发送会依然在后台执行,因此,程序员需要根据情况决定是否要停止消息发送。例如可以在onPause()方法内将消息队列中的消息移除,并在onResume()方法中重新开始消息发送。removeMessage(100)方法用于移除消息队列中what值为100的全部消息。课堂练习:跳动的时钟HandlerHandler完成线程间传递数据使用Handler对象不但可以给本线程发送消息,还可以给其它线程发送消息,前提是需要获取其它线程中的Handler对象。线程之间传递数据在GUI应用中十分广泛,比如后台线程正在执行具体的数据处理,前台界面需要显示出处理的进度,典型的就是进度对话框。在这种应用中,前台线程(一般是指Activity)创建一个后台线程,并把前台线程的Handler对象传递给后台线程,后台线程就可以通过该Handler对象向前台线程发送消息,报告后台数据处理的进度。Looper6
Handler6.1使用LooperThread在默认情况下,只要run()方法执行完毕,线程就结束。简单控制线程不主动退出的方法是:在run()方法内部加一个while()循环,这的确也能解决一些问题,对于那些不需要接收消息而言,基本上够用了。但在另一些情况下,新建的线程需要接收消息并处理,因此,在新线程中,除了需要添加一个Handler对象外,还需要从线程的消息队列中取出消息,并负责分发消息,这就需要Looper了。事实上,Activity内部就有一个Looper,只是Activity是一个特殊的Thread,操作系统已经将其封装了而已。Looper往往和Handler同时使用,案例如下所示:6
Handler6.1使用LooperLooper.prepare()用于给该线程创建一个Looper对象;Looper.loop()用于开始执行Looper对象,所谓的执行就是让Looper对象开始读取线程的消息队列,并派
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 邻里火灾纠纷协议书
- 非自愿签婚内协议书
- 装修安全保证协议书
- 销售车辆合同协议书
- 首付付款比例协议书
- 餐厨垃圾合同协议书
- 苗圃现金收购协议书
- 转让药厂设备协议书
- 加入俱乐部合同协议书
- 协会副会长合同协议书
- 2025-2030年中国腰果酚市场竞争格局及发展前景研究报告
- 智能制造对融资租赁行业影响-全面剖析
- 2025年新高考语文【语言运用新题型】考前训练试卷附答案解析
- GB 29743.2-2025机动车冷却液第2部分:电动汽车冷却液
- 安全人机工程学-人因事故分析与预防报告课件
- 生物有机肥试验方案
- 2025年小升初语文《分析人物形象》教学讲义及专项练习题(附答案)
- 超星尔雅学习通《中华文化才艺(中国海洋大学)》2025章节测试附答案
- 大数据与人工智能在财务管理中的深度应用研究
- 《AI技术术语解析》课件
- 康姿百德入职培训
评论
0/150
提交评论