1 概述
Service服务能够在后台长期运行。其他应用程序组件能启动Service,即便用户切换到另一个应用程序,Service还是可以在后台运行。例如,Service可以在后台播放音乐,监控地理位置的变化等。
1.1 Service分类
Service按照启动方式分为两种类型:
(1)Started Service:
startService()后Service处于启动状态。
(2)Bound Service:
bindService()后Service处于绑定状态。
Started Service与Bound Service的区别:
Started Service | Bound Service |
调用startService()方法启动 | 调用bindService()方法绑定 |
无返回值 | 有返回值 |
启动Service的组件与Service之间无关联,即使关闭改组件,Service会一直运行 | 启动Service的组件与Service绑定在一起,如果关闭该组件,Service就会停止 |
回调onStartCommand()方法,允许组件启动Service | 回调onBind()方法,允许组件绑定Service |
注意:可以在配置文件中将Service声明为私有的,从而阻止其他应用程序访问。
1.2 Service生命周期
Service生命周期按照Service分类分为两种:
(1)通过startService()方法启动Service
需要自己调用stopSelf()或其他组件调用stopService()来停止Service。
(2)通过bindService()方法启动Service
客户端通过IBinder接口与Service通信。客户端通过unbindService()方法关闭连接。多个客户端能绑定到同一个Service。
Service的生命周期分类如下:
2 Started Service
应用程序组件(如Activity)能通过调用startService()来启动Service,参数传递Intent。Service使用onStartCommand()方法来接收Intent。
Android有两个类可以继承以创建Service:
(1)Service。
默认使用应用程序主线程来处理业务;不会自动停止。
(2)IntentService。
新建工作线程来处理业务,需要实现onHandleIntent()方法;当IntentService运行结束后会自动停止。
举例,使用Service播放背景音乐:MusicService
https://github.com/hanyuhang-hz/android-demos
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 设置全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
ImageButton btn_play = (ImageButton) findViewById(R.id.btn_play);
// 启动服务与停止服务,实现播放背景音乐与停止播放背景音乐
btn_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MusicService.isplay == false) {
// 启动服务,从而实现播放背景音乐
startService(new Intent(MainActivity.this, MusicService.class));
((ImageButton) v).setImageDrawable(getResources().getDrawable(R.drawable.play, null));
} else {
// 停止服务,从而实现停止播放背景音乐
stopService(new Intent(MainActivity.this, MusicService.class));
((ImageButton) v).setImageDrawable(getResources().getDrawable(R.drawable.stop, null));
}
}
});
}
@Override
protected void onStart() {
// 进入界面时,启动背景音乐服务
startService(new Intent(MainActivity.this, MusicService.class));
super.onStart();
}
}
MusicService.java
public class MusicService extends Service {
public MusicService() {
}
static boolean isplay;
MediaPlayer player;
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
// 创建MediaPlayer对像并加载播放的音乐文件
player = MediaPlayer.create(this, R.raw.music);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!player.isPlaying()) {
player.start();
isplay = player.isPlaying();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
player.stop();
isplay = player.isPlaying();
player.release();
super.onDestroy();
}
}
注意:应用程序应该在任务完成后停止Service,以免浪费系统资源。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hyh.musicservice">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MusicService">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MusicService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
android:enabled="true"
用于指定Service能否被实例化。
android:exported="true"
用于指定其他应用程序组件能否调用Service或者与其交互。当值为false时,只有同一个应用程序的组件能启动或绑定到Service。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageButton
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/play"
android:background="@color/btn_bg"
android:layout_marginTop="@dimen/btn_marginTop"
android:layout_marginLeft="@dimen/btn_marginLeft"
/>
</RelativeLayout>
3 Bound Service
应用程序组件能调用bindService()方法绑定到Service,接口如下:
bindService(Intent service, ServiceConnection conn, int flags)
service:要启动的service
conn:一个ServiceConnection对象,用于监听访问者与Service之间的连接情况
flags:指定绑定时是否自动创建Service
bindService()后,系统调用Service的onBind()方法,返回IBinder对象与Service通信。
注意:只有Activity,Service和ContentProvider能绑定到Service,BroadcastReceiver不能绑定到Service。
举例,模拟大乐透彩票选号:RandomService
https://github.com/hanyuhang-hz/android-demos
MainActivity.java
public class MainActivity extends Activity {
BinderService binderService;
// 文本框组件ID
int[] tvid = {R.id.textView1, R.id.textView2, R.id.textView3, R.id.textView4, R.id.textView5,
R.id.textView6, R.id.textView7};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_random = (Button) findViewById(R.id.btn);
btn_random.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取BinderService类中的随机数数组
List number = binderService.getRandomNumber();
for (int i = 0; i < number.size(); i++) {
TextView tv = (TextView) findViewById(tvid[i]);
String strNumber = number.get(i).toString();
tv.setTextColor(Color.GRAY);
tv.setText(strNumber);
}
}
});
}
@Override
protected void onStart() {
// 设置启动Activity时与后台Service进行绑定
super.onStart();
Intent intent = new Intent(this, BinderService.class);
// 绑定指定Service
bindService(intent, conn, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
// 设置关闭Activity时解除与后台Service的绑定
super.onStop();
unbindService(conn);
}
// 设置与后台Service进行通讯
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 获取后台Service
binderService = ((BinderService.MyBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
当Service与绑定它的组件连接成功时将回调onServiceConnected()方法;当Service与绑定它的组件断开连接时将回调onServiceDisconnected()方法。
BinderService.java
public class BinderService extends Service {
public BinderService() {
}
// 创建MyBinder内部类并获取服务对象与Service状态
public class MyBinder extends Binder {
public BinderService getService() { //创建获取Service的方法
return BinderService.this;
}
}
// 返回MyBinder服务对象
@Override
public IBinder onBind(Intent intent) { // 必须实现的绑定方法
return new MyBinder();
}
// 创建获取随机号码的方法
public List getRandomNumber() {
List resArr = new ArrayList();
String strNumber="";
// 将随机获取的数字转换为字符串添加到ArrayList数组中
for (int i = 0; i < 7; i++) {
int number = new Random().nextInt(33) + 1;
//把生成的随机数格式化为两位的字符串
if (number<10) {
strNumber = "0" + String.valueOf(number);
} else {
strNumber = String.valueOf(number);
}
resArr.add(strNumber);
}
return resArr;
}
@Override
public void onDestroy() { //销毁该Service
super.onDestroy();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hyh.randomservice">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RandomService">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".BinderService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView1_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView2_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView3_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView4_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView5_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView6_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/textView7_marginLeft"
android:layout_marginTop="@dimen/textView_marginTop"
android:textColor="@color/textColor"
/>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_marginTop="@dimen/btn_marginTop"
android:text="随机选号"
android:background="@color/btn_Color"
android:textColor="@color/textColor"
android:layout_marginLeft="@dimen/btn_marginLeft"
android:layout_marginRight="@dimen/btn_marginRight"
/>
</RelativeLayout>