9.1、简介
Service
是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
9.2、非绑定模式
9.2.1、创建
按钮实现启停服务
主页面放置两个按钮分别用来启动非绑定服务和停止非绑定服务
主页面-Amain:
public class Amain extends Activity {
//非绑定服务跳转it1
//绑定服务跳转it2
Intent it1,it2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ly_main);
//非绑定服务
//非绑定服务的Intent
it1=new Intent(Amain.this,Srv1.class);
//非绑定事件监听器
View.OnClickListener Listener1 = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_start_unbind_service:{
startService(it1);
break;
}
case R.id.btn_stop_unbind_service:{
stopService(it1);
break;
}
}
}
};
findViewById(R.id.btn_start_unbind_service).setOnClickListener(Listener1);
findViewById(R.id.btn_stop_unbind_service).setOnClickListener(Listener1);
}
}
定义一个统一的事件监听器来处理按钮事件
通过switch来区分按钮
service端-Srv1:
public class Srv1 extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.i("mytag","Srv1:oncreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("mytag","Srv1:onStartCommand");
//服务
Log.i("mytag","unbind service providing!");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("mytag","Srv1:onDestroy");
super.onDestroy();
}
}
onBind是绑定模式下的生命周期函数,但无论是绑定模式还是非绑定模式服务器端都要重写onBind函数非绑定模式回传null即可
我们在非绑定模式中想要实现的功能在onStartCommand回调函数中编写
清单文件注册
activity组件和service使用不同的标签来注册
<activity android:name=".Amain" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service android:name=".Srv1"/>
9.2.2、生命周期
当我们点击启动非绑定服务时,会启动service端
service端在创建时会调用oncreate和onstartcommand两个回调函数
当activity退出时,service依然在运行,不会自动调用destory函数
我们只能手动销毁service端
方法:
-
通过系统跟后台来关闭
-
通过Actiivity的回调函数来销毁service端
//Activity端: @Override protected void onDestroy() { stopService(it1); super.onDestroy(); }
通过重写Activity的destory函数,当Activity关闭时顺便把service端销毁
-
通过按钮实现
即这里编写的
btn_stop_unbind_service
按钮,通过点击这个按钮来调用service端的ondestory回调函数,来销毁service
9.3、绑定模式
9.3.1、创建
主页面-Amain:
//全局变量
Srv2.binder binder;
//绑定服务
it2=new Intent(Amain.this,Srv2.class);
//3.
class Connection implements ServiceConnection {
//3.1
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i("mytag","Service Connected!");
binder=(Srv2.binder)iBinder;
}
//3.2
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i("mytag","Service Disconnected!");
}
}
//4.
Connection connection=new Connection();
//1.
View.OnClickListener Listener2=new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_start_bind_service:{
//2.
bindService(it2,connection,BIND_AUTO_CREATE);
break;
}
//6.
case R.id.btn_get_bind_service:{
binder.log();
break;
}
case R.id.btn_stop_unbind_service:{
//5.
unbindService(connection);
break;
}
}
}
};
-
使用统一监听器来处理按钮事件
-
调用bindService函数来开启绑定服务,bindService函数有三个参数:
//参考源码: public boolean bindService(Intent service, ServiceConnection conn, int flags) { throw new RuntimeException("Stub!"); }
para1:Intent对象
para2:ServiceConnection连接对象
para3:
BIND_AUTO_CREATE
表示在绑定同时自动创建服务对象,如果该服务对象已经存在,则不重复创建
Bindservice
启动后只进行service
的绑定,不执行服务的相关内容(不涉及binder内提供的服务) -
由于我们的
bindservice
函数需要一个ServiceConnection
参数,那么我们就去新建一个connetion内部类-
connetion
函数在服务端的onbind函数返回后调用,并返回服务端与启动端的连接service端onbind函数的返回值就从这里的iBinder参数拿到
-
服务接触绑定时,连接也会随之断开,只有非正常断开连接时才回调该函数
-
-
创建
connection
对象作为bindservice函数的一个参数 -
解绑service服务,绑定时使用的哪个连接对象,在解除绑定时就要关闭对应的绑定对象
-
通过binder对象来获取service端提供的服务
service端-Srv2:
public class Srv2 extends Service {
//2.
class binder extends Binder{
public void log(){
Log.i("mytag","binding service provided...");
}
}
@Override
public void onCreate() {
Log.i("mytag","Srv2:onCreate");
super.onCreate();
}
//3.
binder binder=new binder();
@Nullable
@Override
//1.
public IBinder onBind(Intent intent) {
Log.i("mytag","Srv2:onBind");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("mytag","Srv2:onCreate");
return super.onUnbind(intent);
}
@Override
//4.
public void onDestroy() {
Log.i("mytag","Srv2:onCreate");
super.onDestroy();
}
}
-
在绑定模式中,onbind函数起到了桥梁作用,调用onbind函数后,就会启动绑定的service服务,将service端提供的服务回传给activity端,通过一个IBinder对象,由我们创建
-
在当前service类中准备一个binder对象(Binder子类),IBinder是一个接口,Binder是一个实现该接口的类,binder子类中(内部类),可以定义service服务将要提供的功能,例如:函数的调用、对象的引用...
-
创建Binder对象作为onbind函数的返回值
-
当没有组件绑定service服务时,service对象会自动调用destory函数将自己销毁
9.3.2、生命周期
-
点击启动绑定服务时,service端调用
oncreate
,onbind
函数,这样onbind函数就在这是将服务移交给了connection对象,由binder对象获取 -
点击获取服务按钮时,会调用设置的binder对象的log方法,提示绑定服务提供
-
点击关闭绑定服务时,会调用service的
onunbind
方法接触绑定,ondestory
方法销毁
总结:
生命周期:
服务调用:
-
在非绑定模式中,我们的服务编写在onStartCommand回调函数中,当我们想要多次获取服务时,只能多次调用onStartCommand函数来获取
-
在绑定模式中,我们的服务编写在自定binder类中,由onbind对象回传,之后我们所有编写的服务就一直保存在Activity的变量中,当我们想要多次获取时,直接调用即可
服务销毁:
-
在非绑定模式中,我们的服务在start时定义,在service端销毁时,服务也随之销毁,没有数据残留
-
在绑定模式中,Activity端获取服务对象后,就一直保留着,当service端解除绑定时,服务对象仍存留在Activity端中,导致空间浪费
-
解决:
-
在停止绑定服务时,同时销毁服务对象
-
case R.id.btn_stop_bind_service:{ binder=null; unbindService(connection); break; }
-
代码健壮性考虑:
代码中调用了大量的对象,这些对象都要在对象非空时,才能发挥功能,否则可能会出现程序崩溃等错误
bindService参数:
Parameters | |
---|---|
service | Intent : Identifies the service to connect to. The Intent must specify an explicit component name. |
conn | ServiceConnection : Receives information as the service is started and stopped. This must be a valid ServiceConnection object; it must not be null. |
flags | int : Operation options for the binding. |
其中flag参数可以取以下几种,具体请参考官方文档
官网:bindService
-
BIND_AUTO_CREATE
-
BIND_DEBUG_UNBIND
-
BIND_NOT_FOREGROUND
-
BIND_ABOVE_CLIENT
-
BIND_ALLOW_OOM_MANAGEMENT
-
BIND_WAIVE_PRIORITY
-
BIND_IMPORTANT
-
BIND_ADJUST_WITH_ACTIVITY
-
BIND_NOT_PERCEPTIBLE
-
BIND_INCLUDE_CAPABILITIES
9.4、音乐播放服务案例
通过绑定模式实现音乐播放的相关功能
我们将播放器的功能封装成一个类music_util类
public class music_util {
MediaPlayer mp;
public music_util(Context context,int id) {
if(mp!=null) mp.release();
this.mp=MediaPlayer.create(context,id);
}
public music_util(Context context, Uri uri) {
if(mp!=null) mp.release();
this.mp=MediaPlayer.create(context,uri);
}
//播放音乐
public void start(){
if(mp!=null)
mp.start();
}
//暂停音乐
public void pause(){
if(mp!=null)
mp.pause();
}
//停止播放
public void stop(){
if(mp!=null)
mp.stop();
}
//设置单曲循环
public void setLooping(boolean bool){
if(mp!=null)
mp.setLooping(bool);
}
//判断是否在播放
public boolean isPlaying(){
if(mp!=null)
return mp.isPlaying();
else return false;
}
//释放资源
public void release(){
if(mp!=null)
mp.release();
}
}
使用绑定模式实现该程序
Amain:
public class Amain extends Activity {
Intent it;
Srv.binder binder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ly_main);
it=new Intent(Amain.this,Srv.class);
class connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i("mytag","service connected");
if(iBinder!=null)
binder=(Srv.binder)iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
binder=null;
}
}
connection connection=new connection();
View.OnClickListener listener=new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.service_start:{
it.putExtra("name",R.raw.hello);
if(connection!=null)
bindService(it,connection,BIND_AUTO_CREATE);
break;
}
case R.id.hello:{
binder.start(R.raw.hello);
break;
}
case R.id.remix: {
binder.start(R.raw.remix);
break;
}
case R.id.pause:{
Button button=((Button)findViewById(R.id.pause));
if(button.getText().equals("暂停")){
binder.pause();
button.setText("继续");
}
else if(button.getText().equals("继续")){
binder.continues();
button.setText("暂停");
}
break;
}
case R.id.stop:{
binder.stop();
break;
}
case R.id.loop:{
binder.setLooping(((Switch)findViewById(R.id.loop)).isChecked());
}
case R.id.service_terminal:{
unbindService(connection);
break;
}
}
}
};
findViewById(R.id.service_start).setOnClickListener(listener);
findViewById(R.id.hello).setOnClickListener(listener);
findViewById(R.id.remix).setOnClickListener(listener);
findViewById(R.id.pause).setOnClickListener(listener);
findViewById(R.id.stop).setOnClickListener(listener);
findViewById(R.id.loop).setOnClickListener(listener);
findViewById(R.id.service_terminal).setOnClickListener(listener);
}
}
布局文件:
<Button
android:id="@+id/service_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动服务"/>
<Button
android:id="@+id/hello"
android:layout_width="400px"
android:layout_height="200px"
android:text="明天你好"/>
<Button
android:id="@+id/remix"
android:layout_width="400px"
android:layout_height="200px"
android:layout_gravity=""
android:text="Remix"/>
<Button
android:id="@+id/pause"
android:layout_width="200px"
android:layout_height="200px"
android:text="暂停"/>
<Button
android:id="@+id/stop"
android:layout_width="200px"
android:layout_height="200px"
android:text="关闭"/>
<Switch
android:id="@+id/loop"
android:layout_width="400px"
android:layout_height="200px"
android:checked="false"
android:text="单曲循环:"/>
<Button
android:id="@+id/service_terminal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="终止服务"/>
service端-Srv:
public class Srv extends Service {
music_util mu;
int id;
class binder extends Binder{
public void start(int id){
mu=new music_util(Srv.this,id);
mu.start();
}
public void continues(){
mu.start();
}
public void pause(){
mu.pause();
}
public void stop(){
mu.stop();
}
public void release(){
mu.release();
}
public boolean isPlaying(){
return mu.isPlaying();
}
public void setLooping(boolean bool){
mu.setLooping(bool);
}
}
binder binder=new binder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
id=intent.getIntExtra("name",0);
mu=new music_util(this,id);
return binder;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public boolean onUnbind(Intent intent) {
mu.release();
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
清单文件注册这里就不赘述了
思路:
-
先将要实现的功能统一编写在music类中,使用自带的
MediaPlayer
对象来完成音频的播放 -
搭建绑定模式启动端与服务端的框架
-
设置布局文件,并在启动端中将各按钮的点击事件进行绑定
-
首先开启绑定模式服务,开启后会通过
bindservice
函数根据参数跳转到指定的Srv服务端,并调用其onbind
,create
函数在启动服务器时,我们可以通过service端的onbind方法中的intent参数来进行数值的传递,这里先把默认的第一个音频id传递 -
依次编写各个按钮的功能