我们都知道安卓UI线程(Activity)里面禁止进行耗时操作,否则将会抛出异常。因此我们如需执行长时间耗时任务(比如上传、下载等任务)的话,还需的单独开一个任务线程来执行,这里我们可以选择IntentService或Service来在后台执行我们的耗时任务。如果任务处理结果要显示到UI界面上的话,那么选择IntentService比较合适。如果不需要在UI上显示,那么二者皆可。下面我主要讲解如何使用IntentService(我写博客理念是短小精悍,但是由于IntentService,此篇较长,请耐心服用)
1:他们是干什么的
service是后台服务
intentService是Service的子类,继承service,拥有service的全部生命周期,包含了service的全部特性;
他们都是为了保证应用程序被挂到后台后某些组件仍然可以工作
2 :service到底是什么?
service作为Android的四大组件之一,程序退出的时候,仍能在后台服务,即保活。在后台执行一系列计算任务,耗时的操作建议在单独的子线程中执行。
服务是一時 可以在后台长时间的运行,没有用界面的应用组件,即是activity被销毁也不会受到影响,service运行在主线程当中,不能做长时间的耗时操作。
Service服务分两种类:
启动状态:通过startService()
,绑定状态 通过bindService()
(英文理解:开启服务,绑定服务)。
因为service的启动方式有两种,所以说它的生命周期也有两种,一种是通过启动service,然后执行 onCreate()-->onStartCommand()-->onDestory()
另外一种是通过绑定service来启动的的时候,生命周期为onCreate()->onBind()->Service running-->onUnbind() -> onDestroy()
在启动方式中,启动的Service的组件不能获取Service对象的实例,因此无法调用Service中的任何函数,也不能够获取Service中的任何状态和数据信息.能够以启动方式使用的Service,需要具备自管理的能力,而且不需要通过函数调用获取Service的功能和数据.
在绑定方式中,Service是通过服务连接实现的,服务连接可以获取Service的对象实例,可以直接调用Service中的实现的函数,或者直接获得Service中的状态和数据信息,并且同一个Service可以绑定多个服务连接,可以同时为多个不同的组件提供服务.
3:service的弊端:
service既不是独立的进程也不是独立的线程,是依赖于主线程的,所以是不建议在service里面做过多的耗时操作的,避免ANR。
4:intentService的作用:
service没有自己独立的子线程他是依附于子主线程的 所以我们在直接在主线程做一些后台的耗时操作很容易ANR所以
他需要你手动创建子线程来解决这个问题 用来做一些耗时操作 防止泄露 防止ANR
而intentService在onCreat被执行时内部会默认自动开启一个线程,用于解决在后台服务中进行耗时操作;
例如:后台上传图片,批量操作数据库等;
5:IntentService对比service的优势
因为在Service中开线程进行耗时操作也不麻烦。所以大家也可以很少用IntentService。
但是IntentService还有一个特点,就是多次调用onHandleIntent函数(也就是有多个耗时任务要执行),多个耗时任务会按顺序依次执行。原理是其内置的Handler关联了任务队列,Handler通过looper取任务执行是顺序执行的 这个特点不能忽视;
比如在一个app中,有一个需求是下载某段时间用户保存的图片,下载完成后显示在imageView中;一般需要下载的图片有很多,每下一个图片就是一个线程,下载完后立即显示出来,所以肯定希望下载是按顺序依次下载,这样用户体验就比较好。我们就可以 直接开多个线程去下载,在前一个图片下载完成之前,其他线程必须等待;这样虽然也可以实现功能,但效率上不高,甚至可能出现ANR(具体代码逻辑是,如果后面的线程获得了cpu,而前面的图片还没下完,则等待;假设前一张图片没下完,而后面的线程一直获得cpu,就有问题 不安全稳妥)。
如果这个场景使用IntentService就非常方便,每个耗时任务都按顺序依次执行,不必担心出现逻辑上或性能上的问题,也是IntentService的价值所在。
他的使用
IntentService是继承自Service的抽象类,内部封装了HandlerThread和Handler,可以用来执行后台耗时的任务。它的优点是优先级高不容易被杀死,IntentService可以处理多个任务,只不过是一个接着一个的顺序来处理的,当任务完成后会自动停止,不需要主动调用stopSelft()来结束服务。onHandlerIntent是它的一个抽象方法,它可以从Intent中传入的参数来区分并执行任务。
Android编程过程中,经常涉及到后台程序,一个长时间运行的后台程序使用Android提供的Service将是一个很好的选择,然而在众多Service中,IntentService最为常用,也最为简单,但是在使用IntentService时,将服务产生的结果反馈给Activity却不是一件容易的事情。一般情况下,Activity与Service通信有两种方法,其一是通过绑定Binder对象,其二是通过broadcast(广播)的形式
Activity调用bindService (Intent service, ServiceConnection conn, int flags)方法,得到Service对象的一个引用,这样Activity可以直接调用到Service中的方法,如果要主动通知Activity,我们可以利用回调方法
Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好
Service后端的数据最终还是要呈现在前端Activity之上的,因为启动Service时,系统会重新开启一个新的进程,这就涉及到不同进程间通信的问题了(AIDL),
当我们想获取启动的Service实例时,我们可以用到bindService和onBindService方法,它们分别执行了Service中IBinder()和onUnbind()方法。
通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。
在android平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。
第一种 简单通信
直接通过Intent进行传值,我们在启动一个Service的时候通过Intent的对象向Service进行传值,这种方式传递值比较不方便,性能不是很高。
(1)在MainActivity中通过启动服务和终止服务的按钮分别调用startService(intent)和stopService(intent)来启动和停止服务
(2)在Myservice中,我们通过onStartCommand(finalIntent intent, int flags, intstartId)这个函数来接收从Activity传过来的值
第二种
(1)MyService 中我们创建一个Binder类,让其实现android.os.Binder类,并且定义一个方法setData,然后我们通过onBind()方法将其对象返回MainActivity。
(2)在MainActivity中,首先添加一个Binder对象,然后在ServiceConnection中获取MyService中返回的Binder对象,接着我们通过Binder对象调用它的方法setData向其传递数据
第三种,监听服务中的进程的变化
(1)添加一个公开的接口Callback
public static interface Callback{ void onDataChange(String data); }
(2)在MyService内部添加一个变量
private Callback callback; public void setCallback(Callback callback) { this.callback = callback; } public Callback getCallback() { return callback; }
(3)向外界派发信息
if (callback != null){ callback.onDataChange(str); }
(4)在Binder中返回一个当前的MyService对象,然也外部可以添加事件的绑定
public MyService getMyService(){ return MyService.this; }