标题:Android 四大组件 —— 服务
作者:猫猫、有点乖
文章目录
-
- 1 Service 简介
- 2 创建、配置 Service
- 3 startService 方式示例 —— 电话监听器
- 4 startService 方式实例 —— 计时器
- 5 绑定本地 Service 并与之通信
- 6 Service 的生命周期
1 Service 简介
Service 是 Android 四大组件中与 Activity 最相似的组件,它们都代表可执行程序,Service 与 Activity 的区别在于:Service 一直在后台运行,它没有用户界面,所以它绝不会到前台上来。一旦 Service 被启动起来之后,它就与 Activity 一样,它完全具有自己的生命周期。那二者如何选择呢?
如果某个程序组件需要在运行时向用户呈现某种界面,或者该程序需要与用户交互,就需要使用 Activity,否则就应该考虑使用 Service 了。
开发 Service 的步骤与开发 Activity 的步骤很像,开发 Service 组件需要先开发一个 Service 的子类,然后在
AndroidManifest.xml
文件中配置该 Service,配置时可通过
<intent-filter.../>
元素指定它可被哪些 Intent 启动。
2 创建、配置 Service
开发 Service 需要两个步骤:
- 定义一个继承 Service 的子类;
- 在
文件中配置该 Service;AndroidManifest.xml
Service 与 Activity 还有一点相似之处,它们都是从 Context 类派生出来的,因此它们都可调用 Context 里定义的如
getResources()、getContentResolver()
等方法。
与 Activity 相似的是,Service 中也定义了系列生命周期方法,如下所示:
-
:该方法是 Service 子类必须实现的方法。该方法返回一个 IBinder 对象,应用程序可通过该对象与 Service 组件通信;abstract IBinder onBind(Intent intent)
-
:当该 Service 第一次被创建后将立即回调该方法;void onCreate()
-
:当该 Service 被关闭之前将会回调该方法;void onDestroy()
-
:该方法的早期版本是void onStartCommand(Intent intent, int flags, int startId)
,每次客户端调用void onStart(Intent intent, int startId)
方法启动该 Service 时都会回调该方法。startService(Intent)
-
:当 Service 上绑定的所有客户端都断开连接时将会回调该方法。boolean onUnbind(Intent intent)
下面举一个简单的例子来说明下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLUFGWwN0MyYzNIZkWTpmUxsUc612UK9UWjFjQUh0LcZ2avwVbvNmLuR2YpxWYuEDMlF2Lc9CX6MHc0RHaiojIsJye.png)
在 Android Studio 快速创建 Service,如上所示,这样做的好处它会自动在
AndroidManifest.xml
中生成
<Service>...<service />
。
MyService.java
public class MyService extends Service {
public MyService() {
}
// 必须实现的方法,该方法将返回 IBinder 对象
// 可用该对象与 Service 组件进行通信
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
// 当该 Service 第一次被创建后将立即回调该方法
@Override
public void onCreate() {
super.onCreate();
Log.v("-fanfanblog.cn-","Service is Created");
}
// Service 被启动时回调该方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("-fanfanblog.cn-","Service is Started");
return START_STICKY;
}
// Service 被关闭之前回调
@Override
public void onDestroy() {
super.onDestroy();
Log.v("-fanfanblog.cn-","Service is Destroyed");
}
}
上面这个 Service 什么也没干 —— 它只是重写了 Service 组件的
onCreate、onStartCommand、onDestroy、onBind
等方法,重写这些方法只是简单地在 Logcat 中输出一些信息。如果希望 Service 组件做某些事情,那么只要在
onCreate
或
onStartCommand
方法中定义相关业务逻辑代码即可。
定义了上面的 Service 之后,接下来需要在
AndroidManifest.xml
文件中配置该 Service,配置 Service 使用
<service>...</service>
元素,也可以使用
<intent-filter>
子元素,用于说明该 Services 可被哪些 Intent 启动。
AndroidManifest.xml
<!-- 配置一个 Service 组件 -->
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.fanfanblog.services.MYSERVICE" />
</intent-filter>
</service>
当 Service 开发完成之后,接下来可在程序中运行该 Service 了,Android 系统中运行 Service 有如下两种方式:
- 通过 Context 的
方法:通过该方法启用 Service,访问者与 Service 之间没有关联,即使访问者退出了,Service 仍然运行;startService()
- 通过 Context 的
方法:使用该方法启用 Service,访问者与 Service 绑定在了一起,访问者一旦退出,Service 也就终止;bindService()
在界面上添加两个按钮,用于开启服务和关闭服务。
activity_main.xml
<Button
android:text="启动服务"
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:text="关闭服务"
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button start, stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取程序界面中的 start、stop 两个按钮
start = (Button) findViewById(R.id.start);
stop = (Button)findViewById(R.id.stop);
// 创建启动 Service 的 Intent
final Intent intent = new Intent();
// 为 Intent 设置 Action 属性
intent.setAction("cn.fanfanblog.services.MYSERVICE");
// 不加这句,Android 5.0 以上会报 Service Intent must be explitict 错误
intent.setPackage(getPackageName());
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动指定 Service
startService(intent);
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 停止指定 Service
stopService(intent);
}
});
}
}
运行之后,在 Logcat 下输出日志如下:
从上图中可以看出,当首次创建 Service 时,如果服务不关闭,再点启动,那么就不会再
Service is Created
,也就是说只回调一次
onCreate()
方法,而连续点击启动服务按钮则连续回调
onStart()
方法。
3 startService 方式示例 —— 电话监听器
在第二小节的最后用 startService 方式创建了一个服务,然而什么都没做,这一小节将给出一个小应用 —— 电话监听器。下面来说下它的原理:
- 在创建服务时获取电话管理器,注册监听器,监听电话状态;
- 电话状态:闲置、响铃、通话、接听;
- 监听电话需要权限;
实现步骤:
- 继承 Service,编写自定义 Service,定义必要的成员变量;
- 重写 onCreate 方法获取电话管理器,创建监听器;
- 重写 onDestroy 方法,取消监听;
- 清单文件中增加 标签;
- 添加监听电话状态权限;
- 使用 startService 启动服务;
首先用 Android Studio 快速创建 Service:
MyService.java
public class MyService extends Service {
private final String TAG = "MyService";
private TelephonyManager tm;
private MyPhoneStateListener listener;
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
// 获取电话管理器
tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
// 创建电话监听器
listener = new MyPhoneStateListener();
// 监听电话的呼叫状态
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
// 取消电话状态接听
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
}
private class MyPhoneStateListener extends PhoneStateListener {
// 重写 onCallStateChanged 方法,监听电话状态
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Log.d(TAG, "电话状态:闲置");
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d(TAG, "电话状态:响铃");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d(TAG, "电话状态:接听");
break;
}
}
}
}
在清单文件中注册 MyService 服务,添加读取电话状态权限。如下:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.fanfanblog.services.MYSERVICE" />
</intent-filter>
</service>
最后在 Activity 中启动服务,关闭服务:
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button start, stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button)findViewById(R.id.start);
stop = (Button)findViewById(R.id.stop);
final Intent intent = new Intent();
intent.setAction("cn.fanfanblog.services.MYSERVICE");
intent.setPackage(getPackageName());
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(intent);
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(intent);
}
});
}
}
运行后结果如下:
4 startService 方式实例 —— 计时器
在上一小节给出了一个非常简单 startService 案例,这节也同样给出一个,但这个例子会与之前的广播相结合。下面来说下它的原理:
- 在创建服务时在 onStartCommand 方法中启动定时器,获取时间,定时发送广播;
- 在创建 Activity 时注册广播接收者,接收时间广播,更新控件;
实现步骤:
- 继承 Service,编写自定义 Service,定义必要的成员变量;
- 重写 onCreate 方法启动定时器,获取时间;
- 重写 onStartCommand,重置时间;
- 重写 onDestroy 方法,关闭定时器;
- 清单文件中增加 标签;
- 在 Activity 中创建内部类广播接收者,重写 onReceive 方法;
- 在 Activity onCreate 中动态注册广播接收者;
- 启动服务;
创建服务:TimerService.java
public class TimerService extends Service {
private Timer timer = null;
private int mCount = 0;
public TimerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
// 创建一个定时器
timer = new Timer();
// 创建一个定时器任务
TimerTask task = new TimerTask() {
@Override
public void run() {
mCount++;
Log.d("-fanfanblog.cn-", "计时:" + mCount);
Intent intent = new Intent();
intent.setAction("action.timer");
intent.putExtra("count", mCount);
sendBroadcast(intent);
}
};
// 设置定时器任务的触发间隔
timer.schedule(task, 0, 1000);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 重置定时器
mCount = 0;
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
timer.cancel();
}
}
TimerReceiver.java
public class TimerReceiver extends BroadcastReceiver {
Activity mContext;
public TimerReceiver(Activity mContext) {
this.mContext = mContext;
}
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
// 获取 定时器的数据
int count = intent.getIntExtra("count", 0);
// 更新到控件上
Log.d("-fanfanblog.cn-", "当前计时:" + count);
Activity activity = mContext;
TextView textView = activity.findViewById(R.id.tv1);
textView.setText("当前计时: " + count);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initReceiver();
}
private void initReceiver() {
// 1. 创建广播接收者
TimerReceiver timerReceiver = new TimerReceiver(this);
// 2. 指定这个广播接收者的动作
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("action.timer");
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
// 3. 注册广播接收者到系统中
registerReceiver(timerReceiver, intentFilter);
}
boolean mIsClick = false;
public void btnStart(View view) {
Intent intent = new Intent();
intent.setClass(this,TimerService.class);
startService(intent);
mIsClick = true;
if(mIsClick){
Button button = findViewById(R.id.btn1);
button.setText("重新开始计时");
}
}
public void btnStop(View view) {
Intent intent = new Intent();
intent.setClass(this,TimerService.class);
stopService(intent);
mIsClick = false;
Button button = findViewById(R.id.btn1);
button.setText("开始计时");
}
}
activity_main.xml
<Button
android:id="@+id/btn1"
android:text="开始计时"
android:onClick="btnStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<Button
android:text="关闭计时"
android:onClick="btnStop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:text="当前计时未开始"
android:textSize="16dp"
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
运行后如下所示:
5 绑定本地 Service 并与之通信
当程序通过
startService()
和
stopService()
启动、关闭 Service 时,Service 与访问者之间基本上不存在太多的关联,因此 Service 和访问者之间也无法进行通信、数据交换。
如果 Service 和访问者之间需要进行方法调用或数据交换,则应该使用
bindService()
和
unbindService()
方法启动、关闭服务。
bindService()
函数原型为
bindService(Intent service, ServiceConnection conn, int flags)
该方法中的三个参数解释如下:
- servicce:该参数通过 Intent 指定要启动的 Service;
- conn:该参数是一个
对象,该对象用于监听访问者与 Service 之间的连接情况。当访问者与 Service 之间连接成功时将回调该ServiceConnection
对象的ServiceConnection
方法;当访问者与 Service 之间断开连接时将回调该onServiceConnected()
对象的ServiceConnection
方法;onServiceDisconnected()
- flags:指定绑定时是否自动创建 Service(如果 Service 还未创建)。该参数可指定为 0(不自动创建)或
(自动创建);BIND_AUTO_CREATE
还记得那个
onBind()
方法吗?这个方法在开发 Service 类必须实现的一个方法,在绑定本地 Service 的情况下,
onBind()
方法所返回的 IBinder 对象将会传给
ServiceConnection
对象里的
onServiceConnected()
方法的 service 参数,这样访问者就可以通过该 IBinder 对象与 Service 进行通信。
实际开发过程时通常会采用继承 Binder(IBinder 的实现类)的方式实现自己的 IBinder 对象。下面将会演示下如何在 Activity 中绑定本地 Service,并获取 Service 的运行状态。
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button bind, unbind, getServiceStatus;
// 保持所启动的 Service 的 IBinder 对象
BindService.MyBinder binder;
// 定义一个 ServiceConnection 对象
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("-fanfanblog.cn-","--Service Connected--");
// 获取 Service 的 onBind 方法所返回的 MyBinder 对象
binder = (BindService.MyBinder)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("-fanfanblog.cn-","--Service Disconnected--");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind = (Button)findViewById(R.id.bind);
unbind = (Button)findViewById(R.id.unbind);
getServiceStatus = (Button)findViewById(R.id.getServiceStatus);
final Intent intent = new Intent();
intent.setAction("cn.fanfanblog.service.BIND_SERVICE");
intent.setPackage(getPackageName());
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
}
});
getServiceStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,
"Service 的 count 值为:" + binder.getCount(),
Toast.LENGTH_SHORT).show();
}
});
}
}
BindService.java
public class BindService extends Service {
private int count;
private boolean quit;
// 定义 onBinder 方法所返回的对象
private MyBinder binder = new MyBinder();
public class MyBinder extends Binder {
public int getCount() {
// 获取 Service 的运行状态:count
return count;
}
}
public BindService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.d("-fanfanblog.cn-","Service is Binded");
// 返回 IBinder 对象
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("-fanfanblog.cn-","Service is Created");
// 启动一个线程、动态地修改 count 状态值
new Thread() {
@Override
public void run() {
while(!quit) {
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
}
count++;
}
}
}.start();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("-fanfanblog.cn-","Service is Unbinded");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
this.quit = true;
Log.d("-fanfanblog.cn-","Service is Destroyed");
}
}
AndroidManifest.xml
<service
android:name=".BindService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.fanfanblog.service.BIND_SERVICE" />
</intent-filter>
</service>
activity_main.xml
<Button
android:id="@+id/bind"
android:text="bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/unbind"
android:text="unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/getServiceStatus"
android:text="getServiceStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
运行后结果如下:
6 Service 的生命周期
以一张图来说明,如下:
(本文完)