学习笔记——Services

Service(服务)是能够在后台执行长时间运行的操作,并且不提供用户界面的应用程序组件。其他应用程序能启动服务并且切换到另一个应用程序,服务还是可以在后台运行。此外,组件能绑定到服务并与之交互,甚至执行进程间通信(ipc)。

1.Service的分类

服务从本质上可以分为两种类型:
1.Started(启动):当应用程序组件(如Activity)通过调用startService方法启动服务时,服务处于started状态。一旦启动,服务能在后台无限期运行,即使启动它的组件被销毁了。通常,启动服务执行单个操作并且不会像调用者返回结果。例如,它可能通过网络下载或者上传文件。如果操作完成,服务需要停止自身。
2.Bound(绑定):当应用程序通过调用bindService方法绑定到服务时,服务处于bound状态。绑定服务停工客户端-服务端接口,以允许组件与服务交互、发送请求、获得结果,甚至使用进程间通信(IPC)跨进程完成这些操作。仅当其他应用程序与之绑定时,绑定服务才运行。多个组件可以一次绑定到一个服务上,但是当他们都接触绑定时,服务被销毁。

2.Service类中的重要方法

1.onStartCommand()
当其他组件,如Activity调用startService()方法请求服务启动时,系统调用该方法。一旦方法执行,服务就启动并在后台无限期运行。如果开发人员实现该方法,则需要在任务完成时调用stopSelf()或stopService()方法停止服务。
2.onBind()
当其他组件调用bindService()与服务绑定时,系统调用该方法。在该方法的实现中,开发人员必须通过返回IBinder提供客户端用来与服务通信的接口。该方法必须实现,如果不想允许绑定,可以返回null
3.onCreate()
当服务第一次创建时,系统调用该方法执行一次建立过程(在系统调用onStartCommand()或onBind()方法前)。如果服务已经运行,则该方法不调用。
4.onDestory()
当服务不再使用并即将销毁时,系统调用该方法。服务应该实现该方法来清理诸如线程、注册监听器、接收者等资源。

3.Service的声明

开发人员必须在配置文件中声明全部的Service。
<service android:enable=["true"|"false"]
     android:exported=["true"|"false"]
     android:icon="drawable resource"
     android:label="string resource"
     android:name="string"
     android:permission="string"
     android:process="string">
</service>
android:enabled
服务能否被系统实例化。默认为true。<application>标签也有自己的enabled属性,用于包括服务的全部应用程序组件。<application>和<service>属性必须同为true才能让服务可用

android:exported
其他应用程序组件能否调用服务或者与其交互,true表示可以,false表示不可以。当该值是false时,只有同一个应用程序的组件或者具有相同用户ID的应用程序能启动或者绑定到服务。默认值依赖于服务是否包含Intent过滤器。没有过滤器说明她仅能通过精确类名来调用。这意味着服务仅用于程序内部。此时,默认值是false。另一方面,存在至少一个过滤器暗示服务可以用于外部使用,因此默认值是true。
该属性不是限制其他应用程序使用服务的唯一方式。还可以使用permission属性来限制外部实体与服务的交互。

android:icon
表示服务的图标。该属性必须设置成包含图片定义的可绘制资源引用。如果没有设置,使用应用程序图标取代。

android:label
显示给用户的服务名称。如果没有设置,使用应用程序标签取代。
服务标签,不管在此设置还是<application>标签设置,都是所有服务的Intent过滤器默认图标。
标签应该设置为字符串资源引用,这样它能像用户界面的其他字符串那样本地化。

android:name
实现服务的Service子类名称。这应该是一个完整的类名(如com.example.bindservice)。

android:permission
实体必须包含的权限名称,以便启动或者绑定到服务。如果startService()、bindService()或stopService()方法调用者没有被授权,方法调用无效并且Intent对象也不会发送给服务。
如果该属性没有设置,使用<application>标签的permission属性设置给服务。乳沟<application>和<service>标签的permission属性都未设置,服务不受权限保护。

android:process
服务运行的进程名称。通常,应用程序的全部组件运行于应用程序创建的默认进程。它与应用程序的包名相同。<applicatoin>标签的process属性能为全部组件设置一个不同的默认值。但是组件能用自己的process属性重写默认值,从而允许应用程序跨越多个进程。
如果分配给该属性的名称以冒号(:)开头,仅属于应用程序新进程会在需要时创建,服务能在该进程中运行。 如果这个属性以"."开头,则为此服务开启一个全局的独立进程, 如果进程名称以小写字母开头,服务会运行在以此为名的全局进程,但需要提供相应的权限。这允许不同应用程序组件共享进程,减少资源使用。

4.创建Started Service

当服务是started状态时,它的生命周期与启动它的组件无关并且可以再后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后,调用stopSelf()停止,或者由其他组件调用stopService()方法停止。
应用程序组件例如Activity能通过调用startService()方法和传递Intent对象来启动服务,在Intent对象中制定了服务,并且包含服务需要使用的全部数据。服务使用onStartCommand()方法接收Intent。

Android提供了很多类供开发者,下面介绍继承Service和IntentService来创建启动服务
Service:这是所有服务的基类。当继承该类时,创建新线程来执行服务的全部工作是非常重要的。因为服务默认使用应用程序主线程,这可能降低应用程序Activity的运行性能。
IntentService:这是Service的自雷,它每次使用一个工作线程来处理全部启动请求。在不必同时处理多个请求时,这是最佳选择。开发人员仅需要实现onHandleIntent()方法,它接收每次启动请求的Intent以便完成后台任务。

Service和Thread的区别

我们拿服务来进行一个后台长时间的动作,为了不阻塞线程,然而,Thread就可以达到这个效果,为什么我们不直接使用Thread去代替服务呢?(这个问题摘抄至网上,原文地址不是是哪个,所以没写上)

这里提下,

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有! 

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

4.1 继承IntentService类

因为多数启动服务不必同时处理多个请求(在多线程情境下会很危险),所以使用IntentService类实现服务是非常好的选择。IntentService完成如下任务:
1. 创建区别于应用程序主线程的默认工作线程来执行发送到onStartCommand()方法的全部Intent。
2. 创建工作队列每次传递一个Intent到onHandleIntent()方法实现,这样就不必担心多线程。
3. 所有启动请求处理完毕后停止服务,这样就不必调用stopSelf()方法。
4. 提供onBind()方法默认实现,其返回值是null。
5. 提供onStartCommand()方法默认实现,它先发送Intent到工作队列然后到onHandleIntent()方法。
所有这些加在一起说明开发人员仅需要实现onHandleIntent()方法来完成客户端提供的人物。由于IntentService类没有提供空参数的构造方法,因此需要提供一个构造方法。下面是例子:
public class HelloIntentService extends IntentService {

	public HelloIntentService() {
		super("HelloIntentService");
		// TODO Auto-generated constructor stub
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		// TODO Auto-generated method stub
		Bundle bundle = intent.getExtras();
		String hello = bundle.getString("hello");
		Log.v("hello", hello);
	}

	
}
这就是实现IntentService类必须的全部操作,没有参数的构造方法和onHandleIntent()方法。

4.2继承Service类

如果需要让服务处理多线程(取代使用工作队列处理启动请求),则可以继承Service来处理各个Intent。
public class CommonService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Bundle bundle = intent.getExtras();
		String hello = bundle.getString("hello");
		Log.v("hello", hello);
		return START_STICKY;
	}
	

}
onStartCommand()方法必须返回一个整数,该值用来描述系统停止服务后如何继续服务(如前所述,IntentService默认实现已经处理了这些,开发人员也可以进行修改)。onStartCommand()方法返回值必须是下面常量之一:

START_NOT_STICKY

如果系统在onStartCommand()方法返回之后杀死这个服务,那么直到接受到新的Intent对象,这个服务才会被重新创建。这是最安全的选项,用来避免在不需要的时候运行你的服务。

 

START_STICKY

如果系统在onStartCommand()返回后杀死了这个服务,系统就会重新创建这个服务并且调用onStartCommand()方法,但是它不会重新传递最后的Intent对象,系统会用一个nullIntent对象来调用onStartCommand()方法,在这个情况下,除非有一些被发送的Intent对象在等待启动服务。这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。

 

START_REDELIVER_INTENT

如果系统在onStartCommand()方法返回后,系统就会重新创建了这个服务,并且用发送给这个服务的最后的Intent对象调用了onStartCommand()方法。任意等待中的Intent对象会依次被发送。这适用于那些应该立即恢复正在执行的工作的服务,如下载文件。


4.3启动服务

组件使用显式Intent和startService方法启动,代码如下:
Intent intent = new Intent(this,CommonService.class);
Bundle bundle = new Bundle();
bundle.putString("hello", "This is a IntentService");
intent.putExtras(bundle);
startService(intent);
startService方法立即返回,然后Android系统调用服务的onStartCommand方法。如果服务还没有运行,系统首先调用onCreate()方法,接着调用onStartCommand()方法。
如果服务没有提供绑定,startService()方法发送的Intent是应用程序组件和服务组件之间唯一的通信模式。然后,如果开发人员需要服务返回结果,则可以再服务使用广播。 多个启动服务的请求导致服务的onStartCommand()方法,然后仅需要一个停止方法(stopSelf()或者stopService()方法)来停止服务。
protected void onHandleIntent(Intent intent) {
		// TODO Auto-generated method stub
		Bundle bundle = intent.getExtras();
		String hello = bundle.getString("hello");
		Log.v("hello", hello);
		Intent intent1 = new Intent();
		Bundle bundle1 = new Bundle();
		bundle1.putString("text", "IntentService");
		intent1.putExtras(bundle1);
		intent1.setAction("IntentService");
		sendBroadcast(intent1);
		
	}
在Activity中注册广播接收器即可:
private BroadcastReceiver receiver = new BroadcastReceiver(){
    	
    	@Override
    	public void onReceive(Context context, Intent intent) {
    		// TODO Auto-generated method stub
    		Bundle bundle = intent.getExtras();
    		String text = bundle.getString("text");
    		tv.setText(text);
    	}
    	
    };

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		IntentFilter filter = new IntentFilter();
		filter.addAction("IntentService");
		registerReceiver(receiver, filter);
	}


4.4停止服务

启动服务必须管理自己的生命周期。即系统不会停止或销毁服务,除非它必须回收系统内存而且在onStartCommand()方法返回后服务继续运行。因此,服务必须调用stopSelf()方法停止自身,或者其他组件调用stopService()方法停止服务。
然而,如果服务同时处理多个onStartCommand()方法调用请求,则处理完一个请求后,不应该停止服务,因为可能受到一个新的启动请求(在第一个情切结束后停止,会终止第二个请求)。为了避免这个问题,开发人员可以使用stopSelf(int)方法来确保停止服务的请求总是基于最近收到的启动请求。即当调用stopSelf(int)方法时,同时将启动请求的ID(发送给onstartCommand()方法的startID)传递给停止请求。这样如果服务在能够调用stopSelf(int)方法前接收到新启动请求,会因ID不匹配而不停止服务。

5.创建BoundService

绑定服务是允许其他应用程序绑定并且与之交互的Service类实现类。为了提供绑定,开发人员必须实现onBind()回调方法。该方法返回IBinder对象,它定义了客户端用来与服务交互的程序接口。
客户端能通过bindService()方法绑定到伏虎。此时,客户端必须提供ServiceConnection接口的实现类。它监视客户端与服务之间的链接。bindService()方法立即返回,但是当Android系统创建客户端与服务之间的连接时,它调用ServiceConnection接口的onServiceConnected()方法,来发送客户端用来与服务通信的IBinder对象。
多个客户端同时连接到服务,然后仅当第一个客户端绑定时,系统调用服务的onBind()方法来获取IBinder对象。系统接着发送同一个IBinder对象到其他绑定的客户端,但是不在调用onBinder()方法。
当最后的客户端与服务端接触绑定时,系统销毁服务。
在实现绑定服务时,最重要的是定义onBind()毁掉方法返回的接口,有一下3种方式可以定义这个接口。
1.继承Binder类
如果服务对应用程序私有并且客户端运行于相同的进程,则应该继承Binder类来创建接口并且从onBind()方法返回一个实例。客户端接收Binder对象并使用它来直接访问Binder实现类或者Service类中的可用公共方法。
当服务仅用于私有应用程序时,推荐使用该技术。只有当服务可以用于其他应用程序或者访问独立进程时,才不能使用该方法。
2.使用Messenger
如果开发人员需要接口跨不同的进程工作,则可以使用Messenger来为服务创建接口。此时,服务定义Handler对象来相应不同类型的Message对象。Handler是Messenger的基础,它能与客户端分享IBinder,允许客户端使用Messag对象想服务发送命令。此外,客户端能定义自己的Messenger对象,这样服务能发送回消息。
这是执行进程间通信(IPC)的最简单方式。因为Messenger类将所有请求队列化到单独的线程,这样开发人员就不必设计服务为线程安全。
3.使用AIDL
AIDL(Andriod接口定义语言)执行分解对象到原语的全部工作,以便操作系统能理解并且跨进程执行IPC。使用Messenger创建接口,实际上将AIDL作为底层架构。如上所述,Messenger再单个线程中将所有客户端请求队列化,这样服务每次就只会收到一个请求。如果开发人员希望服务能同时处理多个请求,则可以直接使用AIDL。此时服务必须能处理多线程且要保证线程安全。
为了直接使用AIDL,开发人员必须创建定义编程接口的.aidl文件。Android SDK工具使用该文件来生成抽象类,它实现接口并处理IPC,然后就可以在服务中使用。

5.1继承Binder类

仅当客户端与服务位于同一个应用程序和进程时才有效。
步骤如下:
1.在服务中,创建BInder类实例来完成下列操作之一:
a.包含客户端能调用的公告方法。
b.返回当前Service实例,其中包含客户端能调用的公共方法
c.返回服务管理的其他类的实例,其中包含客户端能调用的公共方法。
2.从onBind()回调方法中返回Binder实例
3.在客户端,从onServiceConnected()毁掉方法接收Binder实例,并且使用提供的方法调用绑定服务。
PS:服务和客户端必须位于同一个应用程序的原因是,客户端能转型返回对象并且适当地调用其方法。服务和客户端必须有位于同一个进程,因为该技术不支持跨进程。
public class LocalService extends Service {
	private final IBinder binder = new LocalBinder();
	private final Random generator = new Random();
	
	public class LocalBinder extends Binder{
		LocalService getService(){
			return LocalService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return binder;
	}
	
	public int getRandomNumber(){
		return generator.nextInt(100);
	}

}

下面Activity绑定到LocalService:
public class MainActivity extends Activity {
	private LocalService localService;
	boolean bound =false;
	private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = (Button)findViewById(R.id.button1);
        btn.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				if(bound){
					int num = localService.getRandomNumber();
					Toast.makeText(getApplicationContext(), "获得随机数:"+num, Toast.LENGTH_LONG).show();
				}
			}
        	
        });
    }

	@Override
	protected void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		Intent intent = new Intent(this,LocalService.class);
		bindService(intent, connection, Context.BIND_AUTO_CREATE);
	}

	@Override
	protected void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		if(bound){
			unbindService(connection);
			bound = false;
		}
	}
	
	private ServiceConnection connection = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			LocalBinder binder = (LocalBinder) service;
			localService = binder.getService();
			bound = true;
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			bound = false;
		}
		
	};
    
}

5.2使用Messenger类

如果开发人员需要服务于远程进程通信,则可以使用Messenger来为服务提供接口。该技术允许不适用AIDL执行进程间通信(IPC)。
下面是关于如何使用Messenger的总结:
1.实现Handler的服务因为每次从客户端调用而收到回调。
2.Handler用于创建Messenger对象(它是Handler的引用。)
3.Messenger创建IBinder,服务从onBind方法将其返回到客户端。
4.客户端使用IBinder来实例化Messenger,然后使用它来发送Message对象到服务。
5.服务在其Handler的handleMessage()方法接收Message。
此时,没有客户端在服务上调用的方法。相反,客户端发送“消息“(Message对象)到服务的Handler方法。
public class MessageService extends Service {
	
	class IncomingHandler extends Handler{

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			switch(msg.what){
			case 1 :
				Toast.makeText(getApplicationContext(), "Hello World", Toast.LENGTH_LONG).show();
				break;
			}
		}
		
	}
	final Messenger messenger = new Messenger(new IncomingHandler());

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Toast.makeText(getApplicationContext(), "Binding", Toast.LENGTH_LONG).show();
		return messenger.getBinder();
	}

}

Handler中的handleMessage()方法时服务接收Message对象的地方,并且根据Message类的what成员比那辆决定如何操作。
客户端需要完成的全部工作就是根据服务返回的IBinder创建Messenger并且使用send()方法发送消息。例如,
public class MainActivity extends Activity {
	Messenger messenger = null;
	boolean bound;
	private Button btn;
	private ServiceConnection connection = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			messenger = new Messenger(service);
			bound = true;
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			messenger = null;
			bound = false;
		}
		
	};
	public void sayHello(){
		if(!bound)
			return;
		Message msg = Message.obtain(null, 1, 0, 0);
		try {
			messenger.send(msg);
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = (Button) findViewById(R.id.button1);
        btn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				sayHello();
			}
		});
    }

	@Override
	protected void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		bindService(new Intent(this,MessageService.class), connection, Context.BIND_AUTO_CREATE);
	}

	@Override
	protected void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		if(bound){
			unbindService(connection);
			bound=false;
		}
	}
    
}

5.3绑定到服务

应用程序组件(客户端)能调用bindService()方法绑定到服务。Android系统接下来调用服务的onBind()方法,它返回IBinder来与服务通信。

绑定是异步的。bindService()方法立即返回并且不返回IBinder到客户端。为了接受IBinder,客户端必须创建ServiceConnection实例,然后将其传递给bindService()方法。ServiceConnection包含系统调用发送IBinder的回调方法。

只有Activity,Service和ContentProvider能绑定到服务,BroadcastReceiver不能绑定到服务。

如果需要从客户端绑定服务,需要完成以下操作:

1.实现ServiceConnection,这需要重写onServiceConnected()和onServiceDisconnected()两个回调方法。

2.调用bindService()方法,传递ServiceConnection实现。

3.当系统调用onServiceConnected()回调方法时,就可以使用接口定义的方法调用服务,

4.调用unbindService()方法解绑。

当客户端销毁时,会将其从服务商解绑,但是当与服务完成交互或者Activity暂停时,最好解绑以便系统能计时停止不用的服务。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值