
版权声明:本文为博主原创文章,未经博主允许不得转载
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 的基本用法说说完,一定要亲自试试哦
网友评论