服务的两种启动方式及实现调用服务中方法的测试程序

本文介绍了Android Service的启动方式,包括开启和绑定,并详细解析了服务的生命周期。通过示例代码展示了如何在Activity中通过绑定服务调用Service的方法,包括绑定、解绑、启动和停止服务的顺序影响。同时,提供了测试代码实现,帮助读者深入理解Service的使用。
 

最近开始学习Android四大组件之一的Service组件,所以想把自己的一些总结写成博客,共同学习。

 

下面开始主要内容:

Service:长期在后台运行的没有界面的组件,而且是配合Intent意图一起使用的。下面先介绍一下器两种启动方式

 

启动方式有两种:开启和绑定

 

1.对于开启:有两个对应的方法

startService()调用onCreate方法
stopService() 调用onDestory方法


2.对于绑定:也有两个对用的方法

bindService()  直接调用onBind()方法 同时还会检测服务是否已被 如果没有会先调用onCreate()方法
unBindService()  直接调用onUnbind()方法 如果该解绑是最后一个绑定 同时还会调用onDestory()方法

      
其中bindService()绑定服务   可以得到服务的代理人对象,间接地调用服务里面的方法

 

另外要注意下面两点:

绑定服务:可以间接调用服务里的方法。
                  如果调用者activity被销毁了,服务也会跟着被销毁


开启服务:不可以调用服务里的方法。
                  如果调用者activity退出了,服务还会长期的在后台运行

 

下面再简单介绍一下service的生命周期:

1.单独调用

 

startService()-----onCreate();

                             ...中间还有startCommand()
                                               start()

stopService ()    -----ondestory();

 

-------------------------------------------------------

 

bindService()        -----onCreate() -> onBind();

                                ...中间还有startCommand()
                                                  start()

unBindService()   -----onUnbind() -> onDestory();

 


2.混合调用

 

需求:既要保证服务长期的在后台运行,又想要去调用服务里的里面方法

技巧:1.先开启服务(实现长期运行) 2.绑定服务(可实现调用服务中的方法)
           3.解除绑定 4.停止服务

步骤:1.开启服务 startService()     -onCreate();
           2.绑定服务 bindService()     -onBind();
           3.解绑服务 unBindService() -onUnbind();
           4.停止服务 stopService()     -onDestory();

 

如果在混合使用是不是按照这样的顺序去执行 ,可能会出现不是上面顺序的生命周期

 

比如说:如果先绑定服务,再开启服务,会发现如果这个时候点击stopService的动作按钮不会有任何 

              反应,但如果接着点击解除绑定服务,这是会打印出onUnbind 接着是onDestory
 
              又如如果在开启服务,绑定服务后,在没有解绑之前就按了模拟器的返回键,这时抛出表示 

              还有与服务之间的绑定存在的异常,之后会有onUnbind 和onDestory出现

 

总之,综合上面的现象,在以后实际应用中,最好是start与stop 和 bind与unbind 分别都是成对出现。

 

通过上面简单的讲解,下面给出具体的测试代码,测试代码通过使用服务主activity——MainActivity和一个模拟唱歌的服务——SingService分别实现了服务的两种开启方式,另外在测试代码中还实现了通过绑定调用服务中方法的功能;这里是调用了调用了SingService中的changeSong()方法。

下面详细说明各部分代码的功能。

 

首先是布局文件,可以看到这里是定义了五个按钮,分别对用于开启服务,关闭服务,绑定服务,解绑服务以及调用服务中的方法的五个按钮,代码如下:

<LinearLayout 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"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testservice.MainActivity" >
    <Button
        android:onClick="start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启服务" />
    <Button
        android:onClick="stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭服务" />
     <Button
        android:onClick="bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务" />
    <Button
        android:onClick="unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务" />
    <Button
        android:onClick="change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="调用服务里的方法" />

</LinearLayout>

 

下面再来看一下MainActivity的代码,首先可以清楚的看到,代码中分别实现了五个按钮事件start(),stop(),bind(),unbind()以及change();前四个事件分别条用系统提供的api来实现服务的开启,停止,绑定与解绑,具体实现代码大家应该也能很容易就看懂,不过要注意的是绑定和解绑使用的conn一定要是同一个,不然会报错。对于change()方法的实现,就涉及到SingService的实现,下面通过SingService的代码一起讲解

package com.wang.testservice1;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

/**
 * 调用唱歌服务的activity
 * 同时通过按钮事件来控制服务的生命周期
 * @author Administrator
 *
 */
public class MainActivity extends Activity {

	//步骤四:在activity中得到IBinder的引用对象
	//private SingService.MyBinder mybinder;
	private IService mybinder;
	private MyConn conn;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	/**
	 * 开始服务
	 * @param v
	 */
	public void start(View v){
		Intent intent = new Intent(this,SingService.class);
		//采用 api 创建服务  服务是被系统(框架)创建出来的
		startService(intent);
	}
	
	/**
	 * 停止服务
	 * @param v
	 */
	public void stop(View v){
		Intent intent = new Intent(this,SingService.class);
		stopService(intent);
	}
	
	
	public void bind(View v){
		Intent intent = new Intent(this,SingService.class);
		//intent:激活服务的意图
		//conn:获取服务的中间人 一定不能为空
		//flags( BIND_AUTO_CREATE) 如果绑定的服务没有被创建  系统会创建服务
		//步骤一:采用绑定的的方式取开启服务
		conn = new MyConn();
		bindService(intent, conn,  BIND_AUTO_CREATE);
	}
	
	public void unbind(View v){
		//注意解除绑定的连接一定要是和绑定时的是同一个
		unbindService(conn);
	}
	
	private class MyConn implements ServiceConnection{
		
		//步骤三:IBinder对象传递到conn接口的回调方法onServiceConnected
		//在服务成功绑定的时候 执行的方法
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			System.out.println("唱歌服务的中间人(Ibinder service)被返回回来了....");
			//mybinder = (MyBinder) service;
			mybinder = (IService) service;
		}

		//在服务被解除绑定时执行的方法  只有程序异常终止或进程被杀死时才会被调用
		@Override
		public void onServiceDisconnected(ComponentName name) {
			System.out.println("绑定被异常解除了.....");
		}
		
	}
	
	/**
	 * 调用服务的方法,换首歌
	 * @param v
	 */
	public void change(View v){
		//由于系统框架在创建服务的时候 会创建与之对应的上下文
		//而下面的操作是直接new 对象 是没有上下文的 所以changeSong()方法中的toast会抛出空指针异常
		/*SingService service = new SingService();
		service.changeSong("我爱你!");*/
		
		//步骤五:利用IBinder间接地条用服务里的方法
		mybinder.callChangeSong("我爱你");
	}

	@Override
	protected void onDestroy() {
		//这里是一个小技巧,防止在activity退出时还有绑定着的服务没解绑而抛出异常
		try {
			unbindService(conn);
		} catch (Exception e) {
			e.printStackTrace();
		}
		super.onDestroy();
	}	
}

 

在讲解change()之前,我们先来看一下如何实现主activity中调用服务中的方法,在前面我们简要说过,直接通过startService开启服务,是无法实现的,在下面的代码中我们也可以看出,在change()试图通过new 一个SingService实例对象来调用callChangeSong()是不可行的,具体原因在代码注释中已经说的很清楚了,这里也不再赘述。下面主要通过绑定服务来讲解如何实现,我归纳出了五个步骤:

1.采用绑定的的方式取开启服务

2.在service中发现有activity通过绑定的方式开启服务,调用onBind()方法,并返回一个中间人IBinder

3.在主activity中IBinder对象传递到conn接口的回调方法onServiceConnected()中

4.在activity中得到IBinder的引用对象

5.利用IBinder间接地调用服务里的方法

同时每个步骤也在代码中明确的标了出来。下面是SingService的代码

package com.wang.testservice1;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;

/**
 * 模拟一个唱歌服务
 * 同时实现了通过中间人提供供外界调用服务的方法
 * @author Administrator
 *
 */
public class SingService extends Service {
	//步骤二:有activity通过绑定的方式开启服务,调用onBind()方法,并返回一个中间人IBinder
	//当采用绑定方式开启服务时 被调用,同时会返回一个Ibinder对象(中间人)供启动服务的activity使用
	@Override
	public IBinder onBind(Intent intent) {
		System.out.println("onBind 服务被绑定了....");
		//返回自定义的代理人
		return new Mybinder();
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
		System.out.println("onUnbind 所有的绑定都被解除了");
		return super.onUnbind(intent);
	}
	
	//这里把中间人定义成共有的话,在绑定服务的activity中可以直接拿到中间人的对象
	//这样就可以直接调用中间人中的方法
	//但实际应用中是不会把中间人直接暴露出来的,而是定义成服务的私有类
	//但是这样的话,其他的activity就无法利用中间人去调用服务中的方法
	//我们会采用让中间人继承一个接口,同时让中间人中的一些方法是实现了接口中的方法
	//这样我们就抽取出来了中间人中的几个方法去供外界调用
	/*public class Mybinder extends Binder implements IService{
		
		//间接利用中间人去调用更换歌曲的方法
		public void callChangeSong(String songName){
			changeSong(songName);
		}
		
		public void callDeleteSong(String songName){
			deleteSong(songName);
		}
	}*/
	
	

	//这里是将中间人的访问权限定义成private,这样外界就无法直接得到中间人的对象
	//所以这里就让中间人去继承IService接口,同时实现接口中的callChangeSong()方法
	//这样外界就可以通过返回的IBinder和接口去调用callChangeSong()方法
	//而其他的方法像callDeleteSong()还是私有的,无法被外界访问
	private class Mybinder extends Binder implements IService{
		
		//间接利用中间人去调用更换歌曲的方法
		public void callChangeSong(String songName){
			changeSong(songName);
		}
		
		public void callDeleteSong(String songName){
			deleteSong(songName);
		}
	}
	

	@Override
	public void onCreate() {
		System.out.println("onCreate 服务开始 开始唱歌了.....");
		super.onCreate();
	}
	
	/**
	 * 更改唱的歌曲
	 * @param songName
	 */
	public void changeSong(String songName){
		Toast.makeText(getApplicationContext(), "开始唱"+songName, 0).show();
	}
	
	public void deleteSong(String songName){
		Toast.makeText(getApplicationContext(), "删除"+songName, 0).show();
	}
	
	@Override
	public void onDestroy() {
		System.out.println("onDestory 服务销毁  停止唱歌了.....");
		super.onDestroy();
	}
	
	
}

 

从上面的代码可以看出,通过两种方式都可以实现调用服务中的方法,

一种是将service中的IBinder类(中间人)做成public,这样在其他类中可以直接生成其实例对象,就可以调用任何一个方法;

 

另一种是也是实际中使用的将IBinder类做成private,这样实现了更好的封装性,但是在访问上却更加麻烦了,这时就需要借助接口来实现,具体实现代码中也给出了,下面给出接口IService的实现;

package com.wang.testservice1;

/**
 * 一个接口
 * 配合服务中的私有类MyBinder实现外界间接调用服务的方法
 * @author Administrator
 *
 */
public interface IService {
	public void callChangeSong(String songName);
}

 

由于每个人的理解不同,在表达上可能不是太清晰,大家将就着看吧!

另外,这里附上源码下载路径http://download.youkuaiyun.com/detail/wangwenan1993/8696711



 



 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值