版权声明:本文为博主原创文章,未经博主允许不得转载
PS:转载请注明出处
作者: TigerChain
地址: http://www.jianshu.com/p/1443fa4036dc
本文出自 TigerChain 简书 Android 系列
教程简介
- 1、阅读对象
本篇教程适合新手阅读,老手直接略过 - 2、教程难度
初级 - 3、Demo 地址
稍后 提供
正文
Service 是一个神奇的东西,它可以执行长时间后台任务,并且在后台默默无闻的工作着,没有显示界面(也不露脸去表现自己),说到这里大家可能会想到,fk–Thread 也能干这样的事呀?那 Service 存在有必要吗?在这一节里这些问题都会一一解答
一、什么是 Service
在讲解 Service 之前我们先要知道什么是 Service,按照惯例,直接拿官方的解释来看
Service is an application component that can perform long-running operations in the
background and does not provide a user interface ...
大体就是说 Service 是一个 Android 系统的组件可以在后台执行一些长时间操作并且没有用户界面
Service一个进程吗?是一个线程吗?
Service 既不是一个进程,也不是一个线程,而且它默认是工作在主线程中的,有的同鞋会想运行在主线程?我们都知道在主线程进行耗时操作会 ANR 的,Service 是用来执行耗时操作的难道不会 ANR 吗,如果不会 难道 ANR 的机制还有两套,如果会,那么 Service 为什么还能执行耗时操作,别急这些疑问慢慢解答
我们从官方描述可以知道 Service 是不依赖 UI 运行有后台用来执行耗时操作的一个的系统组件,我们知道 Thread 就是用来执行耗时操作的,那么 Android 有必要要有 Service 这个东西吗?Thread 就够了呀,答案是非常有必要有,因为存在就有必要(靠~~ 说了等于没有说 _ )
Service 和 Thread 的关系
Service 和 Thread 没有半毛钱关系,如果非要说有关系,也就是组合使用的关系,我们对Service 和 Thread 产生混淆的主要一点是由于官方说了 Service 是后台任务来处理一些长时间的操作,这和 Thread 的功能非常类似,懂Handler的朋友尤其明白。其实 Thread 和 Service 的后台意思不太一样,前者是指不依赖UI后者运行在一个工作线程的后台任务,而且 Service 是运行在主线程中的,一个主线程一个子线程程肯定两者之间没有半毛钱关系(除非用于线程间通信),至于为什么 Service 运行在主线程,我们后面再说Service 的特点
(1)、优先级高于 Acitivity,一般情况下即使 Activity 销毁了 Service 任然可以运行,Service 优先级也高于 Activity 所创建的 Thread,优先级高就不会轻易被系统杀死,除非非常有必要
(2)、Service 有自己的生命周期,这样的话就很好控制,而 Thread 的生命周期一般是依赖它被启动的环境中
二、Service 的创建和启动方式
Service 的创建
/**
* Created by TigerChain.
*/
public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
@Override
public void onCreate() {
Log.e(TAG,"onCreate") ;
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand") ;
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.e(TAG,"onDestroy") ;
super.onDestroy();
}
}
Service 是系统组件,既然是系统四大组件之一,那么就要在 Mainfest 中去声明
<service android:name="com.jun.servicedemo.MyService"></service>
在MainActivity中声明一个按钮并用绑定事件
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = MainActivity.class.getSimpleName();
private Button start_service ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView() ;
}
private void initView() {
start_service = (Button) this.findViewById(R.id.start_service) ;
start_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent startServiceIntent = new Intent(MainActivity.this,MyService.class) ;
startService(startServiceIntent) ;
break ;
}
}
}
然后通过startService()来启动Service
Intent startServiceIntent = new Intent(MainActivity.this,MyService.class) ;
startService(startServiceIntent) ;
然后我们运行并查看结果,如下图所示
我们清楚的看到了,首次点击 startService 按钮的时候会依次调用 MyService 的 onCreate–>onStartCommand 方法
我们再点击 startService 按钮,图如下
以后不管再调用多少次 StartService 方法都只会调用 onStartCommand 方法
我们在 Myservice 和 MainActivity 的 onCreate 中分别加入一条 Log 信息
//用来获取当前类所在线程
Log.e(TAG,Thread.currentThread().getId()+"") ;
再次运行,查看 Log
神奇吧,居然 Service 和 Activity 是在同一个线程中,Activity 是在 UI 线程(主线程中),所以Service 默认也是在主线程中的,这就回答了上面的 Service 不是线程,也不是进程,它运行在主线程中,所以它执行耗时操作肯定会 ANR ,如何解决,答案是在 Service 中开启一个 Thread 来执行耗时操作,当然还可以有别的办法,我们后面再说
Service 的 ANR
我们来模拟一个 Service ANR 的效果,我们在 MyService 的 onStartCommand() 方法中添加如入代码
try {
//模拟耗时操作
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
当我们点击 startService 按钮的时候会发现先卡顿一小会作,然后就会报 ANR,典型的主线程进行耗时操作所带来的问题,如下图:
那么如何解决呢,肯定要开子线程去处理耗时操作,代码修改
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand") ;
//这里启用一个子线程用来处理耗时操作
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟耗时操作
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
这样处理以后,就永远不会 ANR 了
总结:Service 运行在主线程中,如果想要操作耗时操作必须在 Service 中开启子线程去处理。
调用 startService 以后,如果想要停止 Service 一定要手动调用 stopService 来停止,我们可看看 Service 的启动方式
Service的启动方式
1、
startService
通过上面的例子,我们已经了解了如何使用 startService 来启动一个 Service 了2、
bindService
按照官网的说法就是通过 bindService 可以创建一个客户端接口来和 Service 交互,并且还可以通过 aidl 来实现进程间通讯。
下面我们我们用实例来看看 bindService
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = MainActivity.class.getSimpleName();
private Button start_service ,stop_service ,bind_service,unbind_service;
private MyService.MyBinder myBinder ;
//服务是否绑定的标志位
private boolean isBind ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView() ;
Log.e(TAG,"所在的线程id:"+Thread.currentThread().getId()+"") ;
}
private void initView() {
start_service = (Button) this.findViewById(R.id.start_service) ;
start_service.setOnClickListener(this);
stop_service = (Button) this.findViewById(R.id.stop_service) ;
stop_service.setOnClickListener(this);
bind_service = (Button) this.findViewById(R.id.bind_service) ;
bind_service.setOnClickListener(this);
unbind_service = (Button) this.findViewById(R.id.unbind_service) ;
unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent startServiceIntent = new Intent(MainActivity.this,MyService.class) ;
startService(startServiceIntent) ;
break ;
case R.id.stop_service:
Intent stopServiceIntent = new Intent(MainActivity.this,MyService.class) ;
stopService(stopServiceIntent) ;
break ;
case R.id.bind_service:
Intent bindServiceIntent = new Intent(MainActivity.this,MyService.class) ;
bindService(bindServiceIntent,myServiceConnection, BIND_AUTO_CREATE) ;
break ;
case R.id.unbind_service:
if(isBind){
unbindService(myServiceConnection);
isBind = false;
}
break ;
}
}
private ServiceConnection myServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//和服务连接的时候调用
myBinder = (MyService.MyBinder) service ;
myBinder.progressLongTimeTask();
isBind = true ;
}
@Override
public void onServiceDisconnected(ComponentName name) {
//服务断开的时候调用(由于异常时断开) Service被停止或被系统杀死的时候调用
Log.e(TAG,"service 断开") ;
myBinder = null ;
}
} ;
}
从代码中可以看到,我们添加了两个按钮 bind_service 和 unbind_service 并添加相应的点击事件,我们在调用 bindService(Intent service, ServiceConnection conn,int flags)
的时候需要传入三个参数,第一个是 Intent,第二个是服务的连接类,第三个是标志位,这里传入 BIND_AUTO_CREATE 表示 Activty 和 Service 建立关联后自动创建 Service
相应的我们的 MyService 也要添加代码
/**
* @author TigerChain
**/
public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
private MyBinder myBinder = new MyBinder() ;
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG,"onCreate") ;
Log.e(TAG,"所在的线程id:"+Thread.currentThread().getId()+"") ;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand") ;
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟耗时操作
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,"IBinder") ;
return myBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG,"onDestroy") ;
}
class MyBinder extends Binder{
//这里模拟耗时任务
public void progressLongTimeTask(){
Log.e(TAG,"处理耗时任务") ;
}
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG,"onUnbind") ;
return true ;
}
@Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
Log.e(TAG,"unbindService") ;
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.e(TAG,"onRebind") ;
}
}
先来大概解释一下,在这里我们定义了一个 MyBinder 类来继承自 Binder,Binder 是 IBinder 的一个实现类,然后在 MyService 的 onBind() 方法中返回这个类的实例,当我们调用 bindService 的方法的时候就会触发 onBind() 方法把这个 MyBinder 类的实例返回去,返回到那里呢?就是MainActivity 中的 onServiceConnected 中的 IBinder 中,然后我们就可以在 Activity 中拿到 Binder 了,然后就可以为所欲为了…
private ServiceConnection myServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//和服务连接的时候调用
myBinder = (MyService.MyBinder) service ;
myBinder.progressLongTimeTask();
isBind = true ;
}
@Override
public void onServiceDisconnected(ComponentName name) {
//服务断开的时候调用(由于异常时断开) Service被停止或被系统杀死的时候调用
Log.e(TAG,"service 断开") ;
myBinder = null ;
}
} ;
废话少说,我们点击 bindService 和 unbindService 按钮来看看 Log 信息
从 gif 图中我们清楚的看到了点击 bindservice 按钮依次调用 MyService 的 oncreate()->onBind()
方法,而点击 unbindservice 按钮会依次调用 MyService 的 onUnbind()-> onDestroy()
方法,unbindservice 按钮如果我们点击多次就会报错,说没有注册 Service,我们是程序员当然对这种异常是0容忍的,解决办法上面代码中已经体现,在 MainActivity 中添加一个标志位 isBind 然后判断一下即可,当然你也可以有自己的解决方案
细心的朋友们发现我们重写了 Service 的 onRebind
方法,那么这个方法有什么卵用,何时调用,首先 onRebind
方法的调用必须满足两个条件,我们来看这个方法注释中杂说
/**
* Called when new clients have connected to the service, after it had
* previously been notified that all had disconnected in its
* {@link #onUnbind}. This will only be called if the implementation
* of {@link #onUnbind} was overridden to return true.
*
* @param intent The Intent that was used to bind to this service,
* as given to {@link android.content.Context#bindService
* Context.bindService}. Note that any extras that were included with
* the Intent at that point will <em>not</em> be seen here.
*/
public void onRebind(Intent intent) {
}
尼玛,啥求意思呀,总结起来就两点,也就是 onRebind() 调用满足的条件
- 服务被绑定后没有销毁
- onUnbind 方法必须返回值为 true
从上面的代码中可知,我们第二个条件是满足了,我们给 onUnbind
方法手动的返回了 true
,第一种情况就要配合 startService
了,我们来看这种情况,为了清楚的看日志信息,我把无关的 Log 注释掉了,如下:
如 gif 图我们依次调用了 bindService->startService
,然后再调用 unBindService
然后再调用 bindService
,这样就调用了 Service
的 onRebind
方法,以后只要不调用 stopService
方法,重复调用 unBindService 和 bindService
都会执行 onRebind
方法。在这里我们获取到了一个重要信息,就是当调用了 startServcie
再调用 bindService
的时候,如果再调用 unBindService
是没有销毁 SerVice
的,不然的话 onRebind
方法是不会调用的,关于 startService
和 bindService
调用同一个 Service
的情况我们后面讨论
三、当 bindService 遇上 startService
- 1、先看先
startService->bindService->unbindService->stopService
和startService->bindService->stopService->unBindService
这两种情况
无图无直相,直接上图
图中我们把上面说的两种情况都实现了:
(1)、首先看 startService->bindService->unbindService->stopService
这种情况:我们清楚的看到依次调用 Service 的
onCreate
、onStartCommand
、onBind
、onUnbind
和 onDestory
方法,以下分别是 startService
和 bindService
对应的方法
startService: onCreate,onStartCommand onDestory
bindService:onBind,onUnbind
先调用 unbindService
再调用 stopService
,会分别调用 Service
的 onUnbind
方法和 onDestory
方法
(2)、其次看 startService->bindService->stopService->unBindService
这种情况:分别依次调用了 Service 的 onCreate
、onStartCommand
、onBind
、onUnbind
和 onDestory
我肋个去和上面一毛一样,别急我们慢慢看,以下分别是 startService
和 bindService
对应的方法
startService: onCreate,onStartCommand
bindService:onBind,onUnbind onDestory
看到区别了没,如果先调用
stopService
再调用unBindService
前者任何 log 都不打,只是把 Service 暂停了,再调用unBindService
的时候会依次调用 Service 的onUnbind
和onDestory
方法
- 2、再看
bindService->startService->unbindService->stopService
和bindService->startService->stopService->unBindService
这两种情况,小二,上图:
图中我们看到两种情况都实现了
(1)、首先行看 bindService->startService->unbindService->stopService
情况会依次调用 Service 的 onCreate->onBind->onStardCommand->onUnbind->onDestory
以下分别是 bindService
和 startService
调用方法
bindService: onCreate, onBind ,onUnbind
startService: onStardCommand , onDestory
解释一下,先调用
bindService
会触发 Service 的onCreate
方法和onBind
方法,再调用startService
会调用 onStardCommand 方法,再调用unbindService
方法会调用 Service 的onUnbind
方法,最后调用stopService
会调用 Service 的onDestory
方法
(2)、再看 bindService->startService->stopService->unBindService
情况会依次调用 Service 的 onCreate->onBind->onStardCommand->onUnbind->onDestory
以下分别是 bindService
和 startService
调用方法
indService: onCreate, onBind ,onUnbind,onDestory
startService: onStardCommand
区别就是先调用 stopService
什么方法都没有调用没有日志信息,只是把 Service 暂停了,再调用 unBindService
方法会依次调用 onUnbind
和 onDestory
方法
四、总结:
- 1、
startService
和bindService
可以启动同一个 Service - 2、
startService
和bindService
启动同一个 Service 的时候如果想销毁 Service 就既要调用stopService
又要调用unBindService
方法,先后顺序无关,但是最后成对调用
到此为止,我们就就把 Service 的基本用法说说完,一定要亲自试试哦