《移动应用开发-郑贵锋08-服务与多线程教学提纲.ppt》由会员分享,可在线阅读,更多相关《移动应用开发-郑贵锋08-服务与多线程教学提纲.ppt(47页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、移动应用开发-郑贵锋08-服务与多线程通过Service可以使程序在退出之后仍然能够对事件或用户操作做出反应,或者在后台继续运行某些程序功能。例如在退出播放器界面之后,继续在后台播放音乐,还有浏览器在后台下载网络数据。Android赋予Services比处于不活动(inactivity)的Activities更高的优先级,所以它们的进程不会轻易被系统杀掉。Service 简介在某些极端的情况下(例如为前台Activity提供资源),Service可能会被杀掉,但是只要有足够的资源,系统会自动重启Service。在某些需要确保用户体验的情况下,(例如播放音乐)Service也可以被提高到与前台进
2、程相同的优先级。Service是由其他Service,Activity或者Broadcast Receiver开始,停止和控制。Service 简介启动模式一:通过startService启动通过startService启动的Service的生命周期状态(一旦启动,各自无关)通过调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf()或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,需要调用一次stopService()来停止服务。
3、Service 简介onCreateonDestroy用于实现应用程序的耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验较好。startService()启动和stopService()关闭服务,Service与访问者之间基本不存在太多关联,因此Service和访问者之间无法通讯和数据交换。Service 简介开启Service在Activity中可以通过startService(Intent)开启一个Service。与Activity跳转类似。Intent intent=new Intent(this,MyService.class
4、);startService(intent);其中MyService类是开发者自定义的继承Service的子类。当第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法。若Service已经启动,当再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。模式一:开始和停止Service关闭Service在Activity中通过stopService(Intent)关闭Service;Intent intent=new Intent(this,MyService.
5、class);stopService(intent);或者在Service中通过stopSelf()关闭自身。模式一:开始和停止Service通过Context.bindService()方法启动服务,通过 Context.unbindService()关闭服务。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。bindService启动的Service的生命周期模式二:通过bindService启动onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定
6、,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。模式二:通过bindService启动两种启动模式对比建立Android工程Activity:ServiceApplication.java 程序入口,例程将在这个Activity中启动Service。Service:MyService.java(继承Service的
7、子类)如何实现一个Service在大多情况,需要重写onCreate和onStartCommand方法onStartCommand方法在使用startService方法启动Service时被调用,在Service的生命周期内会多次被调用。onStartCommand方法代替了Android 2.0之前一直使用的onStart方法。通过onStartCommand方法,可以明确告诉操作系统,在用户调用stopService或者stopSelf方法显式停止之前被操作系统杀死的Service重启的时候要执行的操作。如何实现一个ServiceMyService.javapublic class MyS
8、ervice extends Service Overridepublic IBinder onBind(Intent intent)/必须实现的接口return null;Overridepublic int onStartCommand(Intent intent,int flags,int startId)return Service.START_STICKY;Overridepublic boolean onUnbind(Intent intent)return super.onUnbind(intent);Overridepublic void onDestroy()super.on
9、Destroy();如何实现一个ServiceServiceApplication.javapublic class ServiceApplication extends Activity Override public void onCreate(Bundle savedInstanceState)super.onCreate(savedInstanceState);setContentView(R.layout.main);Intent intent=new Intent(this,MyService.class);startService(intent);如何实现一个Service在An
10、droidManifest.xml中注册这个Service如果没有在此定义服务名称、访问权限,服务就无法被正确运行。如何实现一个Service由于Service没有界面,所以用户控制Service需要通过另外一个Activity来接收用户输入。通过绑定Activity与Service,可以实现Activity与Service之间的交互。例如在Activity中控制音乐播放Service对音乐进行开始/停止,快进/快退等操作。绑定Activity和Service如果Service运行在本地进程,即与Activity属于同一进程,则只要在Activity中获得指向Service的引用,就可以像调用
11、成员对象的方法一样去调用Service的方法。如果Service运行在远程进程中,则Activity与Service的交互属于进程间通讯。跟其他操作系统一样,Android操作系统也对进程空间进行保护,一个进程不能直接访问另一个进程的内存空间。所以进程间数据交互需要利用Android操作系统提供的进程间通讯(IPC)机制来实现。绑定Activity和ServiceIBinder是远程对象的基本接口,是为高效率进行进程间通讯设计的轻量级远程过程调用机制的核心。该接口描述了与远程对象交互的抽象协议。通常我们使用的时候并不是直接实现这个接口,而是继承自Binder父类。IPC机制IBinderBin
12、der实质上是以IPC(Inter-Process Communication,进程间通信)框架为基础。可以简单按下图理解,其实质就是通过共享内存实现进程间的通讯。关于消息传递Android SDK进程1进程2Linux kernel共享内存Java层C+层Activity和Service交互示意图绑定Activity和ServiceActivityBinderServiceIBinderMyService.javapublic class MyService extends Service private IBinder mBinder=new MyBinder();Override pub
13、lic IBinder onBind(Intent intent)/必须实现的接口 return mBinder;public class MyBinder extends BinderOverrideprotected boolean onTransact(int code,Parcel data,Parcel reply,int flags)throws RemoteException return super.onTransact(code,data,reply,flags);绑定Activity和ServiceServiceApplication.javapublic class Se
14、rviceApplication extends Activity private IBinder mBinder;private ServiceConnection mConnection;Override public void onCreate(Bundle savedInstanceState)super.onCreate(savedInstanceState);setContentView(R.layout.main);mConncetion=new ServiceConnection()Overridepublic void onServiceConnected(Component
15、Name name,IBinder service)mBinder=service;Overridepublic void onServiceDisconnected(ComponentName name)mConnection=null;Intent intent=new Intent(this,MyService.class);bindService(intent,mConnection,Context.BIND_AUTO_CREATE);绑定Activity和ServiceonServiceConnection的输入参数IBinder就是Service中onBind返回的Binder对象
16、。通过IBinder对象就可以实现Activity和Service进程间通讯。通过配对的IBinder的transact()方法和Binder的onTransact()方法,可以实现从IBinder接口发送消息,在Binder对象中接收消息,处理消息,再返回的过程。绑定Activity和Servicetransact()和onTransact()是一个同步调用过程,意味着线程A调用transact()后,在线程B完成onTransact()整个处理过程之前,线程A都是阻塞着。所以,如果在onTransact()中需要进行大规模计算时,通常需要在线程A中开启新线程调用transact()。绑定A
17、ctivity和ServiceonTransact(int code,Parcel data,Parcel reply,int flags)transact(int code,Parcel data,Parcel reply,int flags)通过onTransact和transact交互的数据都被封装在Parcel中由于IBinder只提供了一个消息传递接口,只能通过int类型的输入参数code对消息进行识别和判断绑定Activity和ServiceAIDL(Android Interface Description Language)弥补了IBinder接口单一的缺点。AIDL接口描述文
18、件,语法与普通的Java接口一样,只是文件扩展名为.aidl。ADT会根据这个描述文件自动生成一个底层基于IBinder机制,表层提供描述文件所定义方法的接口类。AIDL简介以下例程中自定义了set、get两个方法ITest.aidlinterface IAidlTest/可以看到,aidl文件语法与普通接口一致void set(int i);int get();AIDL简介ADT会根据这个.aidl文件,自动生成一个.java接口文件,其中代码结构如下:public interface IAidlTest extends android.os.IInterface/部分省略public st
19、atic abstract class Stub extends android.os.Binder implements com.book.ServiceApplication.IAidlTest /部分省略private static class Proxy implements com.book.ServiceApplication.IAidlTest/部分省略public void set(int i)throws android.os.RemoteException/implement./public int get()throws android.os.RemoteExceptio
20、n/implement./需要在服务端重写set、get函数,实现具体操作public void set(int i)throws android.os.RemoteException;public int get()throws android.os.RemoteException;AIDL简介AIDL接口代码结构如下,可以看出这里采用了COM组件开发的Proxy/Stub结构,这种代理设计模式多用于远程对象的调用。AIDL简介AIDL interfaceProxyStubServiceActivityProcess AProcess BMyService.java部分代码通过匿名类,实现服
21、务端(stub端)的方法private final IAidlTest.Stub binder=new IAidlTest.Stub()Overridepublic void set(int i)throws RemoteException/在这里实现set方法Overridepublic int get()throws RemoteException/在这里实验get方法;Overridepublic IBinder onBind(Intent intent)return binder;AIDL使用ServiceApplication.java部分代码ServiceConnection mS
22、rcConn=new ServiceConnection()Overridepublic void onServiceConnected(ComponentName name,IBinder service)/这里stub.asInterface其实就是返回一个代理(Proxy)对象IBinder mBinder=IAidlTest.Stub.asInterface(service);Overridepublic void onServiceDisconnected(ComponentName name)/省略部分代码;AIDL使用1.为了提供良好的用户体验,我们必须保证程序有高响应性,所以不
23、能在UI线程中进行耗时的计算或IO操作。2.Android操作系统在下面情况下会强制关闭程序UI线程在5秒内没有响应广播对象不能再10秒内完成onReceive方法多线程应用-背景因此需要采用多线程的方法,将大规模的计算放在后台线程中进行计算,然后将计算结果再显示到前台。例如,在后台下载网络图片,下载完成后显示在屏幕上。多线程应用-应用场景Java基础Thread mThread=new Thread()Override public void run()timeConsumingProcess();mThread.start()新建用户线程但是由于Android操作系统的线程安全机制,不能在
24、非UI线程中重绘UI,所以在用户线程中进行类似更改进度条,修改TextView文字等操作都会造成程序强制关闭(FC)Android提供了两种方法解决上述问题:HandlerAsyncTask新建用户线程Android操作系统在UI线程中,缺省维护该MessageQueue和一个Looper。Looper伪码Handler机制 while(true)Msg=getFirstMessage(MessageQueue)if(Msg!=null)processMessage()Looper通过一个死循环,当有消息Message加入队列时,通过FIFO的顺序处理消息。一个Message中包括了处理Mes
25、sage的Handler对象还有消息内容。这种机制对应这设计模式中的命令模式Handler与UI线程是同一个线程,所以我们在用户线程中完成计算之后,可以通过向消息队列加入一个消息,通知特定的Handler去更改UI。Handler机制/*ServiceApplication.java*/Handler mHandler=new Handler()Overridepublic void handleMessage(Message msg)super.handleMessage(msg);switch(msg.what)/在此处判断消息类型并更新UI ;Thread mThread=new Thr
26、ead()Overridepublic void run()timeConsumingProcess();/进行耗时操作/定义接收消息的Handler对象,并将消息加入队列mHandler.obtainMessage(type).sendToTarget();Handler实现与与UI同一线程的消息同一线程的消息处理器处理器Handler,专门,专门负责处理非负责处理非UI线程发线程发送过来的各种消息,送过来的各种消息,更新更新UI。非非UI线程负责耗时工线程负责耗时工作,将不同类型的消作,将不同类型的消息发送给上面定义的息发送给上面定义的Handler。AsyncTask是在Android
27、SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类。AsyncTask的内部实现是一个线程池,每个后台任务会提交到线程池中的线程执行,然后使用Thread+Handler的方式调用回调函数(对程序员透明)。AsyncTask抽象出后台线程运行的五个状态,分别是:1、准备运行,2、正在后台运行,3、进度更新,4、完成后台任务,5、取消任务,对于这五个阶段,AsyncTask提供了五个回调函数。AsyncTask概述1、准备运行:onPreExecute(),该回调函数在任务被执行之后立即由UI线程调用。这个步骤通常用来建立任务,在用户接口(UI)上显示进度条。2、正在后台运行:d
28、oInBackground(Params.),该回调函数由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算。计算的结果必须由该函数返回,并被传递到onPostExecute()中。在该函数内也可以使用publishProgress(Progress.)来发布一个或多个进度单位(unitsof progress)。这些值将会在onProgressUpdate(Progress.)中被发布到UI线程。AsyncTask可重载的回调函数3.进度更新:onProgressUpdate(Progress.),该函数由UI线程在publishProgress(Pr
29、ogress.)方法调用完后被调用。一般用于动态地显示一个进度条。4.完成后台任务:onPostExecute(Result),当后台计算结束后调用。后台计算的结果会被作为参数传递给这一函数。5、取消任务:onCancelled(),在调用AsyncTask的cancel()方法时调用AsyncTask可重载的回调函数三个模板参数1.Params,传递给后台任务的参数类型。2.Progress,后台计算执行过程中,进度单位(progressunits)的类型。(就是后台程序已经执行了百分之几了。)3.Result,后台执行返回的结果的类型。AsyncTask并不总是需要使用上面的全部3种类型。
30、标识不使用的类型很简单,只需要使用Void类型即可。AsyncTask的构造函数ServiceApplication.javaclass MyTask extends AsyncTaskOverrideprotected Integer doInBackground(Integer.params)/重写该函数,实现后台处理大规模计算return null;Overrideprotected void onProgressUpdate(Integer.values)super.onProgressUpdate(values);/重写该回调函数,更新UI很清晰的一套模板方法设计模式,只需要重写关键
31、事件,不用去了解底层的多线程并发的实现机制AsyncTask使用1.在布局里放置进度条,在代码里定义进度条ProgressBarmProgBar;2.定义class MyTask extends AsyncTask3.在MyTask 的doInBackground里执行耗时操作,在适当的时候调用publishProgress(n)来设置进度条进度。AsyncTask更新进度条的例子4.在MyTask的onProgressUpdate(Integer.progress)里具体设置进度条进度,例如mProgBar.setProgress(progress0);注意:这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数,但是这里取到的是一个数组,所以要用progesss0来取值,第n个参数就用progressn来取值。.定义并运行:MyTask mt=new MyTask();mt.execute(100);AsyncTask更新进度条的例子Questions?此课件下载可自行编辑修改,仅供参考!此课件下载可自行编辑修改,仅供参考!感谢您的支持,我们努力做得更好!谢谢感谢您的支持,我们努力做得更好!谢谢
限制150内