Service作为Android四大组件之一,在每一个应用程序中都扮演着非常重要的角色。它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。
1. startedService
- 被开启的service通过其他组件调用 startService()被创建。
- 这种service可以无限地运行下去,必须调用stopSelf()方法或者其他组件调用stopService()方法来停止它。
- 当service被停止时,系统会销毁它。
2.boundService
- 被绑定的service是当其他组件(一个客户)调用bindService()来创建的。
- 客户可以通过一个IBinder接口和service进行通信。
- 客户可以通过 unbindService()方法来关闭这种连接。
注意:
一个service可以同时和多个客户绑定,当多个客户都解除绑定之后,系统会销毁service。
- 1.Service与Activity之间可以相互调用(bindService,拿到一个binder实例任意调用)
- 2.如果采用bindService Service实例生命周期与Activity绑定到一起了,如果Activity销毁,Service也会销毁,那么不能做到后台播放
- startService(Service实例与启动者的生命周期无关,除非调用stopService方法而消亡)
- bindService(Service实例与启动者的生命周期关联,用户手动调用unbindService或者Activity消亡会跟着消亡)
- 两种方式各有千秋,结合起来可以实现灵活控制
生命周期
service整体的生命时间是从onCreate()被调用开始,到onDestroy()方法返回为止。和activity一样,service在onCreate()中进行它的初始化工作,在onDestroy()中释放残留的资源。
比如,一个音乐播放service可以在onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。
onCreate() 和 onDestroy()会被所有的service调用,不论service是通过startService()还是bindService()建立。
BindService:
- 当绑定service和所有客户端解除绑定之后,Android系统将会销毁它,(除非它同时被onStartCommand()方法开启)。
- 因此,如果你的service是一个纯粹的绑定service,那么你不需要管理它的生命周期。然而,如果你选择实现onStartCommand()回调方法,那么你必须显式地停止service,因为service此时被看做是开启的。这种情况下,service会一直运行到它自己调用stopSelf()或另一个组件调用stopService(),不论它是否和客户端绑定。 另外,如果你的service被开启并且接受绑定,那么当系统调用你的onUnbind()方法时,如果你想要在下次客户端绑定的时候接受一个onRebind()的调用(而不是调用onBind()),你可以选择在onUnbind()中返回true。onRebind()的返回值为void,但是客户端仍然在它的 onServiceConnected()回调方法中得到 IBinder 对象。
简单的音乐播放器代码:
public class MainActivity extends Activity {
private Button mPlayBtn;
private Button mStopBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPlayBtn = (Button) findViewById(R.id.play_pause);
mPlayBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Started
Intent intent = new Intent();
intent.putExtra(“command”, “play”);
intent.setClass(MainActivity.this, MusicService.class);
startService(intent);
}
});
mStopBtn = (Button) findViewById(R.id.stop_play);
mStopBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Stop
stopService(new Intent(MainActivity.this, MusicService.class));
}
});
findViewById(R.id.next).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//切换下一首歌曲
Intent intent = new Intent();
intent.putExtra(“command”, “next”);
intent.setClass(MainActivity.this, MusicService.class);
startService(intent);
}
});
}
}
Service代码:
package com.example.lesson18_service_start;
import java.util.ArrayList;
import java.util.List;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.RemoteViews;
public class MusicService extends Service {
MediaPlayer mediaPlayer;
List<Integer> songList = new ArrayList<>();
List<String> songNameList = new ArrayList<>();
int playIndex;// 当前播放歌曲的下标
@Override
public void onCreate() {// Service实例被创建
super.onCreate();
Log.i("at22", "onCreate");
// 歌曲的资源集合
songList.add(R.raw.abc);
songList.add(R.raw.areyouok);
songList.add(R.raw.zxmzf);
// 歌曲的名字
songNameList.add("abc");
songNameList.add("areyouok");
songNameList.add("zxmzf");
mediaPlayer = MediaPlayer.create(this, songList.get(playIndex));
// 第一次播放的时候更新通知栏的歌曲名字
updateNotification();
}
/**
* 更新通知栏
*/
private void updateNotification() {
//创建能兼容v7以上的通知栏构建者对象
NotificationCompat.Builder builder = new NotificationCompat.Builder(
this);
// 通知栏三个必须的属性需要设置
builder.setSmallIcon(R.drawable.ic_launcher);
// builder.setContentText("abc");//自定义通知视图可以不写
// builder.setContentTitle("abc title");//自定义通知视图可以不写
//创建通知栏中自定义视图的类
RemoteViews views = new RemoteViews(getPackageName(),
R.layout.notification_layout);
//获取当前的歌曲名字
String songName = songNameList.get(playIndex);
// 更新RemoteView中的文本内容
views.setTextViewText(R.id.song, songName);
Intent intent = new Intent();//下一首的歌曲
intent.putExtra("command", "next");
intent.setClass(this, MusicService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 给RemoteView中的某个视图这是点击处理
views.setOnClickPendingIntent(R.id.next, pendingIntent);//第一个参数是下一首button视图的id
// 将自定义视图设置到通知栏中去
builder.setContent(views);
//启动一个通知与当前的服务“命运”关联
startForeground(111, builder.build());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String command = intent.getStringExtra("command");
if ("play".equals(command)) {
playOrPause();
} else if ("next".equals(command)) {
playNext();
}
Log.i("at22", "onStartCommand" + command);
return super.onStartCommand(intent, flags, startId);
}
private void playNext() {
if (mediaPlayer != null) {// 释放播放器
mediaPlayer.stop();
mediaPlayer.release();
}
playIndex++;
if (playIndex >= songList.size()) {// 切换下一首歌曲
playIndex = 0;
}
int songRawId = songList.get(playIndex);// 歌曲的资源id
mediaPlayer = MediaPlayer.create(this, songRawId);
mediaPlayer.start();
// 有歌曲名变化时通知栏更新
updateNotification();
}
private void playOrPause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {//如果正在播放就暂停
mediaPlayer.pause();//暂停
} else {
mediaPlayer.start();//播放
}
}
@Override
public void onDestroy() {// Service实例被销毁
super.onDestroy();
Log.i("at22", "onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
注意。由于Service是四大组建:一定要在清单文件中声明:
- android:name=”.MusicService”