前言
Service是Android的四大组件之一,在Android开发过程中是一个必不可少的组件。
Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。Service可以由Activity、Context等多种组件启动,而且还可以通过其他应用组件启动,而且即使用户切换到其他应用,Activity页面切换,Service一直都会保持在后台运行服务。此外,组件可以通过绑定到服务与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
一、Service的生命周期
Service的生命周期比Activity的生命周期简单,但是需要注意Service几个状态(创建、销毁等),因为Service一直处于后台,避免一直在后台运行。
Service的生命周期从创建到销毁遵循两种方式:
-
启动服务
启动服务在其他组件调用startService()时创建,然后无限期运行,必须通过调用stopSelf()来自行停止运行,或者是通过其他组件通过调用stopService() 来停止服务。服务停止后,系统会将其销毁。 -
绑定服务
该服务在其他组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。(服务不必自行停止运行。)
Service对应的两种启动方式的生命周期如下所示:
从上边的图上可以简单的了解到Service的生命周期的每个阶段,接下来会说明一下每个阶段对应的功能,在每个阶段怎么使用。
回调 | 描述 |
---|---|
onCreate() | 当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。如果service已处于运行中,调用startService()不会执行onCreate()方法。也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作 |
onStartCommand() | 其他组件(如活动)通过调用startService()来请求启动服务时,系统调用该方法。如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用 |
onBind | 当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。 |
onUnbind() | 当客户中断所有服务发布的特殊接口时,系统调用该方法。 |
onRebind() | 当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。 |
onDestroy() | 当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。 |
二、Service的应用
不管是哪一种的 service ,也都需要在 AndroidManifest.xml中声明,所以需要把自定义的Service配置如下:
<service android:name=".MyService"
android:enabled="true"
android:exported="true"
android:icon="@drawable/background_blue"
android:label="string"
android:process="string"
android:permission="string">
</service>
名称 | 介绍 |
---|---|
android:exported | 表示是否允许除了当前程序之外的其他程序访问这个服务 |
android:enabled | 表示是否启用这个服务 |
android:permission | 是权限声明 |
android:process | 是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。 |
android:isolatedProcess | 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。 |
1.启动服务
重写一个新的Service子类MyService,重写对应的方法。(其中IBinder onBind(Intent intent)方法并没有实现任何方法,但必须重写。)接下来用一个简单的例子来呈现启动服务
import android.app.Service;
import android.util.Log;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
@Override
public void onCreate() {
Log.i("Wds","onCreate - Thread ID = " + Thread.currentThread().getId());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Wds", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i("Wds", "onBind - Thread ID = " + Thread.currentThread().getId());
return null;
}
@Override
public void onDestroy() {
Log.i("Wds", "onDestroy - Thread ID = " + Thread.currentThread().getId());
super.onDestroy();
}
}
Activity中通过启动MyService–>销毁MyService,输出在整个Service的运行过程的流程。
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("Wds", "Thread ID = " + Thread.currentThread().getId());
Log.i("Wds", "before StartService");
//连续启动Service
Intent intentOne = new Intent(this, MyService.class);
startService(intentOne);
Intent intentTwo = new Intent(this, MyService.class);
startService(intentTwo);
Intent intentThree = new Intent(this, MyService.class);
startService(intentThree);
//停止Service
Intent intentFour = new Intent(this, MyService.class);
stopService(intentFour);
//再次启动Service
Intent intentFive = new Intent(this, MyService.class);
startService(intentFive);
Log.i("Wds", "after StartService");
}
}
Activity在运行中打印出来Service的Log输出:
I/Wds: Thread ID = 1
I/Wds: before StartService
I/Wds: after StartService
I/Wds: onCreate - Thread ID = 1
I/Wds: onStartCommand - startId = 1, Thread ID = 1
I/Wds: onStartCommand - startId = 2, Thread ID = 1
I/Wds: onStartCommand - startId = 3, Thread ID = 1
I/Wds: onDestroy - Thread ID = 1
I/Wds: onCreate - Thread ID = 1
I/Wds: onStartCommand - startId = 1, Thread ID = 1
从上述的输出可以看到:
- 1.Thread ID = 1说明是主线程,
- 2.Service在通过Activity启动,Service的运行在主线程上。
- 3.通过多次调用startService,onCreate只调用一次。每次调用startService都会再次回调onStartCommand方法,多次调用startService生成的startId是不同的Id值。
所以,在使用Service过程中,运行Service过程需要创建一个子线程,把需要的操作放在子线程中执行(避免主线程的堵塞)。
2.绑定服务
绑定服务(client-server模式):
- 调用者是client
- service则是server端
绑定服务有一些特征:
- 1.service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
- 2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
- 3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁。
接下来会用一些例子详细说明绑定服务的特征:
BindService的对象
import android.app.Service;
import android.os.IBinder;
import android.util.Log;
import android.content.Intent;
import android.os.Binder;
import java.util.Random;
public class BindService extends Service {
//client 可以通过Binder获取Service实例
public class MyBinder extends Binder {
public BindService getService() {
return BindService.this;
}
}
//通过binder实现调用者client与Service之间的通信
private MyBinder binder = new MyBinder();
private final Random generator = new Random();
@Override
public void onCreate() {
Log.i("Wds","BindService - onCreate - Thread = " + Thread.currentThread().getName());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Wds", "BindService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.i("Wds", "BindService - onBind - Thread = " + Thread.currentThread().getName());
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("Wds", "BindService - onUnbind - from = " + intent.getStringExtra("from"));
return false;
}
@Override
public void onDestroy() {
Log.i("Wds", "BindService - onDestroy - Thread = " + Thread.currentThread().getName());
super.onDestroy();
}
//getRandomNumber是Service暴露出去供client调用的公共方法
public int getRandomNumber() {
return generator.nextInt();
}
}
**Client对象 A **
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private BindService BBservice = null;
private boolean isBind = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBind = true;
BindService.MyBinder myBinder = (BindService.MyBinder) service;
BBservice = myBinder.getService();
Log.i("Wds", "MainActivity - onServiceConnected");
int num = BBservice.getRandomNumber();
Log.i("Wds", "MainActivity - getRandomNumber = " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("Wds", "MainActivity - onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.nextBtn).setOnClickListener(this);
findViewById(R.id.bindService).setOnClickListener(this);
findViewById(R.id.unBindService).setOnClickListener(this);
findViewById(R.id.finishBtn).setOnClickListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("Wds",
"-----------Main-------onDestroy-------------------");
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.bindService){
//单击了“bindService”按钮
Intent intent = new Intent(this, BindService.class);
intent.putExtra("from", "MainActivity");
Log.i("Wds", "----------------------------------------------------------------------");
Log.i("Wds", "MainActivity 执行 bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
} else if (v.getId() == R.id.unBindService){
if (isBind) {
Log.i("Wds",
"----------------------------------------------------------------------");
Log.i("Wds", "MainActivity 执行 unbind Service");
unbindService(conn);
}
} else if (v.getId() == R.id.finishBtn){
//单击了“Finish”按钮
Log.i("Wds",
"----------------------------------------------------------------------");
Log.i("Wds", "MainActivity 执行 finish");
this.finish();
} else{
Log.i("Wds",
"----------------------------------------------------------------------");
Log.i("Wds", "MainActivity 启动 NextActivity");
//下一个页面
Intent intent = new Intent(MainActivity.this,NextActivity.class);
startActivity(intent);
}
}
}
Client对象的页面XML文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/root"
>
<Button
android:id="@+id/nextBtn"
android:layout_width="200sp"
android:layout_height="50sp"
android:layout_marginStart="59dp"
android:layout_marginTop="20dp"
android:onClick="onClickNextBtn"
android:text="@string/MainBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bindService"
android:layout_marginStart="100dp"
android:layout_marginTop="100dp"
android:text="Bind Service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/unBindService"
android:layout_marginStart="100dp"
android:layout_marginTop="180dp"
android:text="unBind Service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/finishBtn"
android:layout_marginStart="100dp"
android:layout_marginTop="260dp"
android:text="Finish"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
</androidx.constraintlayout.widget.ConstraintLayout>
测试情况1:
- 1.点击MainActivity的bindService按钮
- 2.点击MainActivity的unbindService按钮
执行出来的log
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
I/Wds: MainActivity - getRandomNumber = -2070009866
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 unbind Service
I/Wds: BindService - onUnbind - from = MainActivity
I/Wds: BindService - onDestroy - Thread = main
从结果打印出来的log可以得出:
- 1.Client执行bindService(), 如果Service不存在,则Service执行onCreate(),onBind().
- 2.Client实例ServiceConnection执行onServiceConnected()方法。
- 3.Client实例解除绑定,Service检测是否还有其他client与其连接,如果没有Service执行onUnbind()和onDestroy()
测试情况2:
- 1.点击MainActivity 绑定bindService按钮
- 2.点击MainActivity的Finish按钮
执行出来的log
I/Wds: ----------------------------------------------------------------------
I/Wds:MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
I/Wds:MainActivity - getRandomNumber = 9346330
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 finish
I/Wds:-----------Main-------onDestroy-------------------
I/Wds: BindService - onUnbind - from = MainActivity
I/Wds: BindService - onDestroy - Thread = main
从结果打印出来的log可以得出:
- 如果Client销毁,那么Client上的ServiceConnection被销毁,ServiceConnection会自动与Service解除绑定
Client B的文件
public class NextActivity extends AppCompatActivity implements View.OnClickListener{
private BindService BBservice = null;
private boolean isBind = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBind = true;
BindService.MyBinder myBinder = (BindService.MyBinder) service;
BBservice = myBinder.getService();
Log.i("Wds", "NextActivity - onServiceConnected");
int num = BBservice.getRandomNumber();
Log.i("Wds", "NextActivity - getRandomNumber = " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("Wds", "NextActivity - onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
findViewById(R.id.bindService).setOnClickListener(this);
findViewById(R.id.unBindService).setOnClickListener(this);
findViewById(R.id.finishBtn).setOnClickListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("Wds",
"-----------Next-------onDestroy-------------------");
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.bindService){
//单击了“bindService”按钮
Intent intent = new Intent(this, BindService.class);
intent.putExtra("from", "NextActivity");
Log.i("Wds", "----------------------------------------------------------------------");
Log.i("Wds", "NextActivity 执行 bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
} else if (v.getId() == R.id.unBindService){
if (isBind) {
Log.i("Wds",
"----------------------------------------------------------------------");
Log.i("Wds", "NextActivity 执行 unbind Service");
unbindService(conn);
}
} else{
//单击了“Finish”按钮
Log.i("Wds",
"----------------------------------------------------------------------");
Log.i("Wds", "NextActivity 执行 finish");
this.finish();
}
}
}
Client B页面XML文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NextActivity"
android:id="@+id/nextActivity"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bindService"
android:layout_marginStart="100dp"
android:layout_marginTop="100dp"
android:text="Bind Service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/unBindService"
android:layout_marginStart="100dp"
android:layout_marginTop="180dp"
android:text="unBind Service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/finishBtn"
android:layout_marginStart="100dp"
android:layout_marginTop="260dp"
android:text="Finish"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
></Button>
</androidx.constraintlayout.widget.ConstraintLayout>
测试情况3:
多个Client,对应一个Service。
- 1.点击MainActivity的bindService按钮
- 2.点击MainActivity跳转到NextActivity页面
- 3.点击NextActivity的bindService按钮
- 4.点击NextActivity的unbind Service按钮
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
MainActivity - getRandomNumber = 1658998008
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 启动 NextActivity
I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 bindService
I/Wds: NextActivity - onServiceConnected
I/Wds: NextActivity - getRandomNumber = -1479193691
I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 unbind Service
I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 finish
从上边三个简单的例子,总结可以得出如下图的 “绑定服务” 的流程
总结
之前的Android开发序列已经讲述了Android的四大组件Activity和Intent,Activity的工作主要可以呈现给用户,可以看到的。Intent就像一个连接器,连接了底层环境,连接了各种组件,以及提供组件之间的数据。
Service就一个默默在后台功能强大具有生命周期的“Activity”,其中Service还有很多扩展的服务,例如音乐播放、视频播放等等都是Service。Service用途非常广泛,以上只是大概总结了一下Service的使用,以及简单的列举了一下Service的使用例子。其中有很多方面都没有讲到。本篇只是抛砖引玉……
持续更新中……