服务(Service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
不过需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。与某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。另外.也不要被服务的后台概念所迷惑,实际上服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
1.service用于在后台完成用户指定的操作。service分为两种:
(1)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
(2)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
2.需要在应用程序配置文件中声明全部的service,使用标签。
3.Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
服务的两种启动方式
两种方式的生命周期
4.startService()与bindService()区别:
(1)started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。
(2)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
startService 启动服务
startService生命周期:onCreate() ->onStartCommand() -> onDestroy()
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
android:gravity="center_horizontal">
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="30dp"
android:background="#F08080"
android:onClick="stopClick"
android:text="关闭服务"
android:textColor="#6C6C6C"
android:textSize="18sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_above="@id/btn_stop"
android:layout_marginBottom="110dp"
android:background="#B0E0E6"
android:onClick="startClick"
android:text="开启服务"
android:textColor="#6C6C6C"
android:textSize="18sp" />
</RelativeLayout>
定义(启动)一个Service
核心步骤如下:
- 创建一个类继承android.app.Service类,实现抽象方法onBind(),重写onCreate()、onStartCommand()、onDestry();
- 在清单文件中配置Service。
新建一个JAVA类
MySerice.java
onCreate():首次启动服务的时候,系统会调用这个方法, 方法中做一些初始化的操作,比如要执行耗时的操作,可以 在这里创建线程
onStartCommand(): 当通过startService 方法来启动服务的时候,在onCreate 方法 之后就会回调这个方法,此方法调用后,服务就启动起来了,将会在后台无限期 的运行,直到通过stopService 或者 stopSelf 方法来停止服务。
onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。实现onDestroy方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最 后一个调用。
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.i("liubotao", "创建服务,执行onCreate()方法");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("test", "开启服务,执行onStartCommand()方法");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("test", "关闭服务,执行onDestroy()方法");
}
}
MainActivity.java
当应用组件通过startService方法来启动Service 时,Service 则会处于启动状态,一旦服务启动,它就会在后台无限期的运行,生命周期独立于启动它的组件,即使启动它的组件已经销毁了也不受任何影响,由于启动的服务 长期运行在后台,这会大量消耗手机的电量,因此,应该在任务执行 完成之后调用stopSelf()来停止服务,或者通过其他应用组件调用stopService 来停止服务。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startClick(View view) { //开启服务
Log.i("test", "执行startClick()方法");
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
}
public void stopClick(View view) { //关闭服务
Log.i("test", "执行stopClick()方法");
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
}
}
但是点击启动服务和停止服务并没有反应,,没有调用MyService里的方法。
然后找到AndroidManifest.xml,发现MyService没有被注册,所以要加入注册信息。
再次运行
首次启动onCreate()之后在onStartCommand()之前服务已经启动起来了,startService()方法启动服务后,通过onCreate()的回调调用onStartCommand()之后在后台无限期运行,使用stopService 或者 stopSelf 方法来停止服务。关闭服务调用 onDestroy()销毁资源。
通过startService 方式启动的服务,服务会无限期的在后台运行,直到通 过stopService 或 stopSelf 来终止服务。服务独立于启动它的组件,当组件启动服务后,组件和服务就再也没有关系了,就算启动它的组件被销毁了,服务照样在后台运行。所以startService 方式启动的服务与组件之间通信不友好。
bindService启动服务
如果是多个组件绑定到一个服务上,当绑定到该服务的所有组件都被销毁时,服务才会停止。 多个组件可以绑定到同一个服务上,如果只有一个组件绑定服务,当绑 定的组件被销毁时,服务也就会停止了。如果是多个组件绑定到一个服 务上,当绑定到该服务的所有组件都被销毁时,服务才会停止。
bindServie 的生命周期:onCreate -> onBind -> onUnbind ->onDestroy
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg">
<Button
android:id="@+id/btn_unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="85dp"
android:layout_marginBottom="80dp"
android:background="#F5F5DC"
android:onClick="unbindClick"
android:text="解绑服务"
android:textSize="16sp" />
<Button
android:id="@+id/btn_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_unbind"
android:layout_alignLeft="@id/btn_unbind"
android:layout_marginBottom="55dp"
android:background="#F5F5DC"
android:onClick="callClick"
android:padding="10dp"
android:text="调用服务中的方法"
android:textSize="16sp" />
<Button
android:id="@+id/btn_bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_call"
android:layout_alignLeft="@id/btn_call"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="bindClick"
android:text="绑定服务"
android:textSize="16sp" />
</RelativeLayout>
MyService.java
通过bindSerivce 方式与绑定组件的生命周期是有关的。即多个组件可以绑定到同一个服务上,如果只有一个组件绑定服务,当绑定的组件被销毁时,服务也就会停止了。
onCreate():首次启动服务的时候,绑定会调用这个方法, 方法中做一些初始化的操作,比如要执行耗时的操作,可以在这里创建线程。
onBind() :当其他组件想通过bindService 与服务绑定时,系统将会回调这个方法,在实现中,必须返回一个IBinder接口,供客户端与服务进行通信,必须实现此方法,这个方法是Service 的一个抽象方法,但是如果不允许绑定的话,返回null 就可以了。
onUnbind() :当所有与服务绑定的组件都解除绑定时,就会调用此方法。
onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。实现onDestroy方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最 后一个调用。
methodInBinder():通过接口暴露到外部(通过绑定MyBinder类),回调method()。
public class MyService extends Service {
class MyBinder extends Binder { //创建服务的代理,调用服务中的方法
public void methodInBinder() {
Log.i("test", "执行MyBinder中的methodInBinder()方法");
method();
}
}
public void method() {
Log.i("test", "执行MyService中的method()方法");
}
@Override
public void onCreate() {
Log.i("test", "创建服务,执行onCreate()方法");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.i("test", "绑定服务,执行onBind()方法");
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("test", "解绑服务,执行onUnbind()方法");
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("test", "call onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("test", "call onDestroy...");
}
}
MainActivity.java
第1个参数Intent:用于指定要启动的Service;
第2个参数conn:用于监听调用者(服务绑定的组件)与Service直接的 连接状态,当调用者与Service连接成功时,程序会回调conn的 onServiceConnected()方法;
第3个参数flags:表示组件绑定服务时,是否会自动创建Service(若 Service还没创建的话),该参数可设为0,表示不自动创建;也可设置 为【BIND_AUTO_CREATE】,表示自动创建Service。
public class MainActivity extends AppCompatActivity {
private MyService.MyBinder myBinder;
private MyConn myconn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindClick(View v) {
Log.i("test", "bindClick()");
if (myconn == null) {
myconn = new MyConn(); //创建连接服务的对象
}
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, myconn, BIND_AUTO_CREATE); //绑定服务
}
public void callClick(View v) {
Log.i("test", "callClick()");
myBinder.methodInBinder(); //调用服务中的方法
}
public void unbindClick(View v) {
Log.i("test", "unbindClick()");
if (myconn != null) {
unbindService(myconn); //解绑服务
myconn = null;
}
}
private class MyConn implements ServiceConnection {
//创建MyConn类,用于实现连接服务
// 当成功绑定服务时调用的方法,该方法获取MyService中的Ibinder对象
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myBinder = (MyService.MyBinder) iBinder;
Log.i("test", "服务成功绑定, 内存地址为:" + myBinder.toString());
}
//当服务失去连接时调用的方法
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i("test", "服务成功解绑");
}
}
}
绑定
调用服务
解绑
onServiceDisconnected() 在连接正常关闭的情况下是不会被调用的 该方法只在Service 被破坏了或者被杀死的时候调用. 例如, 系统资源不足, 要关闭一 些Services, 刚好连接绑定的 Service 是被关闭者之一, 这个时候 onServiceDisconnected() 才会被调用。
Service 是支持多个组件绑定在同一个服务上的,第一个组件绑定是会回调 onCreate 生命周期方法,后续的绑定只会调用onBind方法,返回IBinder给客户端。
当绑定在服务上的组件都调用unbindService 解除绑定服务或者组件本身就已经被系统回收,那么服务也就会被停止回收了,会回调onUnbind 和 onDestroy 方法。
验证
再新建一个Activity,实现两个页面的跳转,及绑定在同一个服务上。
在AndroidManifest.xml中注册Activity2,采用singleInstance-全局唯一模式,给Activity1加上android:launchMode=“singleInstance” 属性,实现页面跳转。
在MainActivity.java和Main2Activity.java中的onCreate()里添加匿名内部类,实现页面间的跳转。注意new Intent()的参数,是从MainActivity调到Main2Activity,再从Main2Activity调到MainActivity 。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button Btn = (Button) findViewById(R.id.to2);
Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("test", "Activity1跳转到Activity2");
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);
}
});
}
还有一个重要的是,在 bindClick()中在绑定服务前添加intent的类型,不然服务会认为两个Activity的intent类型是一样的,不能区分,最后会导致不管怎么绑定服务都只执行一次onBind()
intent.setType(“main”);//只需要参数不一样即可。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg">
<Button
android:id="@+id/btn_unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="85dp"
android:layout_marginBottom="80dp"
android:background="#F5F5DC"
android:onClick="unbindClick"
android:text="解绑服务"
android:textSize="16sp" />
<Button
android:id="@+id/btn_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_unbind"
android:layout_alignLeft="@id/btn_unbind"
android:layout_marginBottom="55dp"
android:background="#F5F5DC"
android:onClick="callClick"
android:padding="10dp"
android:text="调用服务中的方法"
android:textSize="16sp" />
<Button
android:id="@+id/btn_bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_call"
android:layout_alignLeft="@id/btn_call"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="bindClick"
android:text="绑定服务1"
android:textSize="16sp" />
<Button
android:id="@+id/to2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/btn_bind"
android:layout_alignLeft="@+id/btn_bind"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="onClick"
android:text="跳转到页面2"
android:textSize="16sp"/>
</RelativeLayout>
activity_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- android:background="@drawable/bg">-->
<Button
android:id="@+id/btn_unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="85dp"
android:layout_marginBottom="80dp"
android:background="#F5F5DC"
android:onClick="unbindClick"
android:text="解绑服务"
android:textSize="16sp" />
<Button
android:id="@+id/btn_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_unbind"
android:layout_alignLeft="@id/btn_unbind"
android:layout_marginBottom="55dp"
android:background="#F5F5DC"
android:onClick="callClick"
android:padding="10dp"
android:text="调用服务中的方法"
android:textSize="16sp" />
<Button
android:id="@+id/btn_bind2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn_call"
android:layout_alignLeft="@id/btn_call"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="bindClick2"
android:text="绑定服务2"
android:textSize="16sp" />
<Button
android:id="@+id/to1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/btn_bind2"
android:layout_alignLeft="@+id/btn_bind2"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="onClick"
android:text="跳转到页面1"
android:textSize="16sp" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private MyService.MyBinder myBinder;
private MyConn myconn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button Btn = (Button) findViewById(R.id.to2);
Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("test", "Activity1跳转到Activity2");
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);
}
});
}
public void bindClick(View v) {
Log.i("test", "bindClick()");
if (myconn == null) {
myconn = new MyConn(); //创建连接服务的对象(接口)
}
Intent intent = new Intent(MainActivity.this, MyService.class);
intent.setType("main");
bindService(intent, myconn, BIND_AUTO_CREATE); //绑定服务
}
public void callClick(View v) {
Log.i("test", "callClick()");
Log.i("test", myBinder.toString());
myBinder.methodInBinder(); //调用服务中的方法
}
public void unbindClick(View v) {
Log.i("test", "unbindClick()");
if (myconn != null) {
unbindService(myconn); //解绑服务
myconn = null;
}
}
protected class MyConn implements ServiceConnection {
//创建MyConn类,用于实现连接服务(接口类)
// 当成功绑定服务时调用的方法,该方法获取MyService中的Ibinder对象
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myBinder = (MyService.MyBinder) iBinder;
Log.i("test", "服务成功绑定, 内存地址为:" + myBinder.toString());
}
//当服务失去连接时调用的方法
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i("test", "服务成功解绑");
}
}
}
Main2Activity.java
public class Main2Activity extends AppCompatActivity {
private MyService.MyBinder myBinder;
private MyConn myconn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button Btn = (Button) findViewById(R.id.to1);
Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("test", "Activity2跳转到Activity1");
Intent intent = new Intent(Main2Activity.this, MainActivity.class);
startActivity(intent);
}
});
}
public void bindClick2(View v) {
Log.i("test", "bindClick2()");
if (myconn == null) {
myconn = new MyConn(); //创建连接服务的对象(接口)
}
Intent intent = new Intent(Main2Activity.this, MyService.class);
intent.setType("child");
bindService(intent, myconn, BIND_AUTO_CREATE); //绑定服务
}
public void callClick(View v) {
Log.i("test", "callClick()");
myBinder.methodInBinder(); //调用服务中的方法
}
public void unbindClick(View v) {
Log.i("test", "unbindClick()");
if (myconn != null) {
unbindService(myconn); //解绑服务
myconn = null;
}
}
private class MyConn implements ServiceConnection {
//创建MyConn类,用于实现连接服务(接口类)
// 当成功绑定服务时调用的方法,该方法获取MyService中的Ibinder对象
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myBinder = (MyService.MyBinder) iBinder;
Log.i("test", "服务成功绑定, 内存地址为:" + myBinder.toString());
}
//当服务失去连接时调用的方法
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i("test", "服务成功解绑");
}
}
}
MyService.java不变
结果