服务是android实现后台运行的解决方案,它适合去执行那些不需要和用户交互而且还要求长期运行的任务.服务的运行不依赖于任何用户界面,即便是程序被切换到后台,或者用户打开另外一个应用程序,服务依然能保持正常运行.
服务并不是运行在一个独立的进程中,而是依赖于创建服务时所在的应用程序的进程,当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止.
服务不会自动开启线程,所有的代码都是默认运行在主线程中的,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则有可能会出现主线程被堵塞住的情况.
android的多线程编程
一般常规写法:(采用声明匿名内部类的方法来定义一个类来实现Runnable接口,run()方法中处理具体的逻辑业务)
new Thread(new Runnable() {
@Override
public void run() {
Message message=new Message();
message.what=UPDATE_TEXT;
handler.sendMessage(message); }
}).start();
子线程中更新UI
Android的UI线程也是线程不安全的,如果想要更新应用程序中的UI元素,必须在主线程中进行,否则会出现异常(也就是若在子线程中更新UI元素会导致程序崩溃).但是有的时候我们需要在子线程中去执行一些耗时的任务,然后根据任务的执行结果来更新相应的UI控件.Android为我们提供了一套异步消息处理的使用方法-----------借助于Handler,完美解决了在子线程中进行UI操作的问题
因为之前我们已经讲了Hander的用法了,我们今天不进行过多的讨论,只是讲解大体的思路.即我们首先在主线程创建一个Handler对象,重写其handleMessage(Message msg)方法,然后加一个switch判断 根据msg.what的值来具体执行具体的逻辑,然后新建一个新的线程在新的线程里面声明Message message.what 然后借助于handler.sendMessage(message)方法来发送消息 ,发送的这条消息会被主线程中的Handler的handleMessage()方法接收,然后根据msg.what的不同来执行不同的逻辑.
![](https://img.haomeiwen.com/i7566707/7c759988b27076c1.png)
![](https://img.haomeiwen.com/i7566707/abaa952b102624ad.png)
Message:是线程之间传递消息,它可以内部携带少量的信息,用于在不同的线程之间交换数据
Handler:顾名思义也就是处理者的意思,主要用于发送和接收消息的,发送消息用的是handler.sendMessage()方法,发出的消息经过辗转处理后会传递到Handler的handMessage()方法中
MessageQueue:消息队列的意思,主要用于存放所有通过Handler发送的消息,每个线程只有一个MessageQueue对象
Looper:Looper是每个MessageQueue的管家,调用Looper的loop()方法后,会进入到一个无线的循环中去,然后每发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中.每个线程也只有一个Looper对象
几点说明:子线程中如果想要创建Handler对象那么必须首先要先创建一个Looper即用Looper.prepare()创建一个Looper实例,同时它会自动配套MessageQueue,然后便可以创建Hander对象(可以通过查看源码得知)最后启动Looper.loop()方法
在主线程中系统已经初始化好了一个Looper对象,我们直接创建Hander对象即可,然后可以通过Handler来发送处理消息了.
若Handler在主线程中创建的则此时handleMessage()也会在主线程中进行,于是我们便可在此进行UI操作了.
我们如何区分哪里是主线程哪里是子线程呢,系统默认所创建的东西比如A都是默认在主线程创建的,除非你特别的声明,你先声明了一个子线程然后在子线程中的run()方法中又声明了B.那么B便是在子线程中创建的.
使用AsyncTask
![](https://img.haomeiwen.com/i7566707/cc3f86b3a72ea21e.png)
AsynvTask背后的实现原理也是基于异步消息处理机制的,只是android已经帮我们做好了封装而已.
使用AsynvTask的诀窍是:在doInBackground()方法中执行具体的耗时任务;在onProgressUpdate()方法中进行UI操作;在onPostExecute()方法中执行一些任务的收尾工作.
服务(Serviec)
Android Studio中创建一个服务:
![](https://img.haomeiwen.com/i7566707/89d44b47ac453d95.png)
![](https://img.haomeiwen.com/i7566707/7d5c356d574a4103.png)
![](https://img.haomeiwen.com/i7566707/0260817404048585.png)
创建完Service可以发现创建的Service是继承Service的这和Activity有点类似只不过创建Activity时创建的Activity一般是继承AppcompatActivity.
![](https://img.haomeiwen.com/i7566707/806e66bee3a7fc2c.png)
onCreate onStartCommand onDestory是我们最常用的三个方法.
注意:每一个服务都需要在AndroidManifest.xml文件中注册才行,android四个组件都必须在AndroidManifest.xml文件中注册才行!!AndroidStudio会帮我们注册如果是傻瓜创建的话.
启动和停止服务
![](https://img.haomeiwen.com/i7566707/ec937d26e549780f.png)
证明服务已经成功启动或者停止的方法是在创建的服务中加入打印日志Log.d();
onCreat()和onStartCommand()方法的区别:前者是第一次创建服务的时候调用的,而后者是在每次启动服务的时候都会调用,当第一次创建服务时,由于服务还未创建过所以这两个方法都会执行,当服务创建好了之后在创建这个服务那么只有onStartCommand这个方法执行,而onCreat方法不执行!
活动和服务之间通信(最重要的地方)
![](https://img.haomeiwen.com/i7566707/520d9a9c8d1a32d3.png)
让服务和活动进行通信需要借助我们之前忽略的onBind()方法
比如说希望在创建的Service里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载的进度.实现这个的思路是在创建一个专门的Binder对象来对下载功能进行管理.其中查看源码不难看出IBinder是一个接口,而Binder实现了IBinder接口(实际上Binder是IBinder的实现类),我们创建的DownloadBinder又继承了Binder,即DownloadBinder也间接的实现了IBinder接口.
1.首先创建一个我们自己的Binder类(这里我们创建的是DownloadBinder) 让它继承Binder
class DownloadBinderextends Binder{}
2.在我们创建的Binder类里提供具体的方法来实现我们要实现的业务,注意定义的方法都是public因为这个方法之后是要在activity中调用的;在这里我们提供了开始下载和查看下载进度的方法
class DownloadBinderextends Binder {
public void startDownload() {
Log.d("MyService", "startDownload executed");}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return 0;}}
3.在我们创建的服务中创建了我们创建的这个Binder实例,然后在onBind()方法里返回了这个实例.但是我们并没有在DownloadBinder类中声明构造方法,当时我们DownloadBinder是继承了Binder我们在创建DownloadBinder实例时,其实是用的父类的构造器(即Binder的构造器).
private DownloadBinder mBinder =new DownloadBinder();
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
然后我们需要将一个活动和服务进行绑定绑定了之后我们便可以调用服务里的我们先前定义的Binder(本例中是mBinder)提供的方法了.
4.我们还要在活动中 创建一个ServiceConnection匿名类,并在里面重写onServiceConnected()方法和onServiceDisconnected()方法,这两个方法分别会在活动与服务成功绑定以及活动与服务的连接断开的时候调用.在onServiceConnected()方法中我们通过向下转型得到了DownloadBinder的实例,有了这个实例我们便可以调用DownloadBinder中我们写的方法了.
![](https://img.haomeiwen.com/i7566707/44ef11ccf6155547.png)
5.活动和服务的绑定,构建一个Intent对象然后调用bindService()方法将Activity和我们创建的Service进行绑定.bindService()方法接收三个参数:Intent对象;ServiceConnection实例;还有一个标志位.
6.解绑服务是用到unbindService()方法,unbindService()方法接收一个参数:ServiceConnection实例
需要注意的是:任何一个服务在整个程序范围内都是通用的,即MyService不仅可以和MainActivity进行绑定,还可以和任何一个其他的互动进行绑定,而且绑定完成后它们都可以获取到相同的我们创建的Binder实例(本例中为DownloadBinder实例)
服务的生命周期
![](https://img.haomeiwen.com/i7566707/ad4cb2ffe8c5ba7d.png)
![](https://img.haomeiwen.com/i7566707/2e93d51442a1df7f.png)
使用前台服务
![](https://img.haomeiwen.com/i7566707/68c8140790cda3e3.png)
上面的思维导图中的前台服务跟我们前面学的Notification很像!!都是创建一个PendingIntent(可以认为是一个延时的Intent),然后创建一个Notification实例(借助NotificationCompat.Builder().build();)跟通知不一样的地方在于它不用使用NotificationManager.notify()方法来将通知显示出来,而是调用StartForeground()方法.该方法接收两个参数第一个个id第二个是构建出的notification对象
前台服务和后台服务的最大区别在于:它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果.
使用IntentService
![](https://img.haomeiwen.com/i7566707/51cfdb9c9019f91f.png)
服务中的代码都是默认在主线程中运行的,如果在服务中处理一些耗时的逻辑那么非常容易出现ANR(Application Not Responding )的情况.
所以我们应该在服务中的每个具体的方法中开启一个新线程来处理一些耗时的逻辑,
![](https://img.haomeiwen.com/i7566707/dac16711487a1f15.png)
其实IntentService的用法和普通的服务没有什么两样.
![](https://img.haomeiwen.com/i7566707/514dab821514d46b.png)
另外不要忘记服务都是要在AndroidManifest.xml文件中注册的.因为我们是创建的java/class 所以需要我们在在AndroidManifest.xml文件中进行手动注册,注意创建java/class和在AndroidStudio中直接创建Service是不一样的系统前者不会自动给我们注册需要我们手动注册,而后者系统可以给我们自动注册!!
绑定本地Service并与之通信的更深层次理解
如果访问者和Service之间需要进行方法调用或者交换数据,则应该使用bindService()和unbindService()方法启动,关闭service.
首先我们先来看一下这两个方法(来源于API),拿bindService方法举例同样可以说明unbindService()方法
![](https://img.haomeiwen.com/i7566707/4cd9cfe925623773.png)
再来看一下Context的bindService()方法的完整方法签名.(来源于java API)
![](https://img.haomeiwen.com/i7566707/d37d8baf42ccb71f.png)
service:该参数通过Intent指定要启动的Service
conn:该参数是一个ServiceConnection对象,该对象用于监听访问者和Service之间的连接情况.当访问者与Service之间连接成功时将回调该ServiceConnection对象的onServiceConnected(ComponentName name, IBinderservice)方法;当Service所在的宿主进程由于异常中止或者其他原因中止,导致该Service与访问者之间断开连接时便回调该ServiceConnection对象的onServiceDisconnected(ComponentName name)方法.
注意:当调用者主动通过unBindService()方法断开与Service的连接时,ServiceConnection对象的onServiceDisconnected(ComponentName name)方法并不会被调用.
flags:指定绑定时是否自动创建Service(如果Service还未创建).该参数可指定为0(不自动创建)或BIND_AUTO_CREATE(自动创建)
当开发Service类时,该类必须提供一个IBinder onBind(Intent intent)方法,在绑定本地Service的情况下,onBind(Intent intent方法所返回的IBinder对象将会传给ServiceConnection对象里的onServiceConnected(ComponentName name, IBinder service)方法中的service 参数,这样访问者便可以通过该IBinder对象与Service进行通信了.
IBinder对象相当于Service组件的内部钩子,该钩子关联到绑定的Service组件,当其他应用程序组件绑定该Service时,Service将会把IBinder对象返回给其他程序组件,其他程序组件通过该IBinder对象即可与Service组件进行实时通信.
开发时通常会采用继承Binder(IBinder的实现类)的方式来实现自己的IBinder对象(因为IBinder是一个接口嘛当然不能返回一个接口了,接口是不能实例化的所以可以采用创建一个类来继承实现它的接口的Binder类的方法(当然在这个类里面我们可以写我们需要的功能),来返回一个IBinder对象).即通俗点来说通过继承Binder类实现了一个IBinder对象,这个MyBinder类是Service类中的内部类,这对于绑定本地Service并与之进行通信的场景是一种常见的情形.
对于Service的onBind()方法所返回的IBinder对象来说,它可以被当做是该Service组件所返回的代理对象,Service允许客户端通过该IBinder对象来访问Service内部的数据,这样即可实现客户端与Service之间的通信.
网友评论