【转】http://blog.csdn.net/ithomer/article/details/7364024
一、 Service简介
Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。
Service的启动有两种方式:context.startService() 和 context.bindService()
二、 Service启动流程
context.startService() 启动流程:
context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop
如果Service还没有运行,则android先调用onCreate(),然后调用onStart();
如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。
如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行,该Service的调用者再启动起来后可以通过stopService关闭Service。
所以调用startService的生命周期为:onCreate --> onStart (可多次调用) --> onDestroy
context.bindService()启动流程:
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
三、 Service生命周期
Service的生命周期并不像Activity那么复杂,它只继承了onCreate()、onStart()、onDestroy()三个方法
当我们第一次启动Service时,先后调用了onCreate()、onStart()这两个方法;当停止Service时,则执行onDestroy()方法。
这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。
它可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己,只要调用一次stopService()方法便可以停止服务,无论调用了多少次的启动服务方法。
四、 Service示例(startservice)
下面我做了一个简单的音乐播放的应用,分别使用startService和bindService来启动本地的服务。
Activity
[java] view plaincopyprint?
- public class PlayMusicService extends Activity implements OnClickListener {
- private Button playBtn;
- private Button stopBtn;
- private Button pauseBtn;
- private Button exitBtn;
- private Button closeBtn;
- private Intent intent;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.music_service);
- playBtn = (Button) findViewById(R.id.play);
- stopBtn = (Button) findViewById(R.id.stop);
- pauseBtn = (Button) findViewById(R.id.pause);
- exitBtn = (Button) findViewById(R.id.exit);
- closeBtn = (Button) findViewById(R.id.close);
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
- closeBtn.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- int op = -1;
- intent = new Intent("com.homer.service.musicService");
- switch (v.getId()) {
- case R.id.play: // play music
- op = 1;
- break;
- case R.id.stop: // stop music
- op = 2;
- break;
- case R.id.pause: // pause music
- op = 3;
- break;
- case R.id.close: // close activity
- this.finish();
- break;
- case R.id.exit: // stopService
- op = 4;
- stopService(intent);
- this.finish();
- break;
- }
- Bundle bundle = new Bundle();
- bundle.putInt("op", op);
- intent.putExtras(bundle);
- startService(intent); // startService
- }
- @Override
- public void onDestroy(){
- super.onDestroy();
- if(intent != null){
- stopService(intent);
- }
- }
- }
Service
[java] view plaincopyprint?
- public class MusicService extends Service {
- private static final String TAG = "MyService";
- private MediaPlayer mediaPlayer;
- @Override
- public IBinder onBind(Intent arg0) {
- return null;
- }
- @Override
- public void onCreate() {
- Log.v(TAG, "onCreate");
- Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
- if (mediaPlayer == null) {
- mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- }
- @Override
- public void onDestroy() {
- Log.v(TAG, "onDestroy");
- Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- @Override
- public void onStart(Intent intent, int startId) {
- Log.v(TAG, "onStart");
- if (intent != null) {
- Bundle bundle = intent.getExtras(); //通过intent传递的参数和调用者进行通信
- if (bundle != null) {
- int op = bundle.getInt("op");
- switch (op) {
- case 1:
- play();
- break;
- case 2:
- stop();
- break;
- case 3:
- pause();
- break;
- }
- }
- }
- }
- public void play() {
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- }
- public void pause() {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- public void stop() {
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- }
AndroidManifest.xml
注册activity
[css] view plaincopyprint?
- <activity
- android:name=".service.PlayMusicService"
- android:label="@string/app_name" />
注册service
[css] view plaincopyprint?
- <service
- android:name=".service.MusicService"
- android:enabled="true" >
- <intent-filter>
- <action android:name="com.homer.service.musicService" />
- </intent-filter>
- </service>
五、 代码解析
1、Activity中,PlayMusicService中通过重写OnClickListener 接口onClick()方法实现对播放音乐的控制,把音乐各种操作用数字通过Intent传递给service
然后通过构造一个Intent , intent = new Intent("com.homer.service.musicService");
其中,com.homer.service.musicService是 AndroidManifest.xml 对service的定义,即上面“注册service”
2、Activity中,音乐播放的控制,利用Bundle绑定数字op后,通过 startService(intent); 服务后发送出去
Bundle bundle = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);
startService(intent);
3、 Service中,会处理Activity启动的 startService(intent);服务,依次调用service的启动过程:onCreate --> onStart(可多次调用) --> onDestroy
onCreate(), 创建mediaPlayer
onStart(), 通过获取Bundle bundle = intent.getExtras();,提取int op = bundle.getInt("op");,然后执行响应的音乐播放操作
onDestroy(),停止并释放mediaPlayer音乐资源,如果当执行context.stopService()时调用此方法
4、Activity中,onClick()函数中close与exit是执行含义是不同的:
close : 只是执行了this.finish(); 关闭了本Activity窗体,service并没有被关掉,音乐依然会继续在后台播放
exit : 先调用了stopService(intent); 关闭了service服务,在Service中会调用3中的onDestroy()停止并释放音乐资源,后才执行this.finish(); 关闭了本Activity窗体
参考推荐:
Android BroadcastReceiver启动Service
Service (android developer)
六、Service示例(bindservice)【转】http://blog.csdn.net/ithomer/article/details/7366396
一、bindService简介
bindService是绑定Service服务,执行service服务中的逻辑流程。
service通过Context.startService()方法开始,通过Context.stopService()方法停止;也可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己。只要调用一次stopService()方法便可以停止服务,无论之前它被调用了多少次的启动服务方法。
客户端建立一个与Service的连接,并使用此连接与Service进行通话,通过Context.bindService()方法来绑定服务,Context.unbindService()方法来关闭服务。多个客户端可以绑定同一个服务,如果Service还未被启动,bindService()方法可以启动服务。
上面startService()和bindService()两种模式是完全独立的。你可以绑定一个已经通过startService()方法启动的服务。例如:一个后台播放音乐服务可以通过startService(intend)对象来播放音乐。可能用户在播放过程中要执行一些操作比如获取歌曲的一些信息,此时activity可以通过调用bindServices()方法与Service建立连接。这种情况下,stopServices()方法实际上不会停止服务,直到最后一次绑定关闭。
二、bindService启动流程
context.bindService() ——> onCreate() ——> onBind() ——> Service running ——> onUnbind() ——> onDestroy() ——> Service stop
onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。详见:Android Service 服务(一)—— Service
三、bindService生命周期
像一个activity那样,一个service有些可以用来改变状态的生命周期方法,但是比activity的方法少,service生命周期方法只有三个public
void onCreate()
void onStart(Intent intent)
void onDestroy()
通过实现这三个生命周期方法,你可以监听service的两个嵌套循环的生命周期:
1、整个生命周期
service的整个生命周期是在onCreate()和onDestroy()方法之间。和activity一样,在onCreate()方法里初始化,在onDestroy()方法里释放资源。例如,一个背景音乐播放服务可以在onCreate()方法里播放,在onDestroy()方法里停止。
2、活动的生命周期
service的活动生命周期是在onStart()之后,这个方法会处理通过startServices()方法传递来的Intent对象。音乐service可以通过开打intent对象来找到要播放的音乐,然后开始后台播放。注: service停止时没有相应的回调方法,即没有onStop()方法,只有onDestroy()销毁方法。
onCreate()方法和onDestroy()方法是针对所有的services,无论它们是否启动,通过Context.startService()和Context.bindService()方法都可以访问执行。然而,只有通过startService()方法启动service服务时才会调用onStart()方法。
如果一个service允许别人绑定,那么需要实现以下额外的方法:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
onBind()回调方法会继续传递通过bindService()传递来的intent对象
onUnbind()会处理传递给unbindService()的intent对象。如果service允许绑定,onBind()会返回客户端与服务互相联系的通信句柄(实例)。
如果建立了一个新的客户端与服务的连接,onUnbind()方法可以请求调用onRebind()方法。
记住: 任何服务无论它怎样建立,默认客户端都可以连接,所以任何service都能够接收onBind()和onUnbind()方法
四、bindService示例
Activity
[java] view plaincopyprint?
- public class PlayBindMusic extends Activity implements OnClickListener {
- private Button playBtn;
- private Button stopBtn;
- private Button pauseBtn;
- private Button exitBtn;
- private BindMusicService musicService;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.bind_music_service);
- playBtn = (Button) findViewById(R.id.play);
- stopBtn = (Button) findViewById(R.id.stop);
- pauseBtn = (Button) findViewById(R.id.pause);
- exitBtn = (Button) findViewById(R.id.exit);
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
- connection();
- }
- private void connection() {
- Intent intent = new Intent("com.homer.bind.bindService");
- bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
- }
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.play:
- musicService.play();
- break;
- case R.id.stop:
- if (musicService != null) {
- musicService.stop();
- }
- break;
- case R.id.pause:
- if (musicService != null) {
- musicService.pause();
- }
- break;
- case R.id.exit:
- this.finish();
- break;
- }
- }
- private ServiceConnection sc = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
- musicService = ((BindMusicService.MyBinder) (service)).getService();
- if (musicService != null) {
- musicService.play(); // play music ,通过MyBinder获取到service实例,调用service中定义的方法
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) { //disconnect Service
- musicService = null;
- }
- };
- @Override
- public void onDestroy(){
- super.onDestroy();
- if(sc != null){
- unbindService(sc);
- }
- }
- }
Service
[java] view plaincopyprint?
- public class BindMusicService extends Service {
- private MediaPlayer mediaPlayer;
- private final IBinder binder = new MyBinder();
- public class MyBinder extends Binder {
- BindMusicService getService() {
- return BindMusicService.this;
- }
- }
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
- if(mediaPlayer != null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- public void play() {
- if (mediaPlayer == null) {
- mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- }
- public void pause() {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- public void stop() {
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- }
AndroidManifest.xml
[css] view plaincopyprint?
- <service
- android:name=".bind.BindMusicService"
- android:enabled="true" >
- <intent-filter>
- <action android:name="com.homer.bind.bindService" />
- </intent-filter>
- </service>
五、代码解析
1、 Activity中,Intent intent = new Intent("com.homer.bind.bindService"); 构建一个service的action,然后bindService(intent, sc, Context.BIND_AUTO_CREATE); 绑定服务
2、 Activity中,通过private ServiceConnection sc = new ServiceConnection() 建立一个Service连接,onServiceConnected()获取Service实例,onServiceDisconnected()释放连接
3、 Service中,重载onBind(Intent intent)方法,返回Service实例(即BindMusicService)给Activity,然后执行onCreate()函数(注:bindService不执行onStart()函数)
4、 Activity中,通过返回的Service实例musicService,执行音乐播放的操作(play、pause、stop等)
六、Remote Service拓展
通常每个应用程序都在它自己的进程内运行,但有时需要在进程之间传递对象(IPC通信),你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在android平台中,一个进程通常不能访问其它进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在android为我们提供了AIDL工具可以来做这件事。
AIDL(android接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如在一个Activity中)访问另一个进程中(例如一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。
要使用AIDL,Service需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub服务桩类。Service的实现类需要去继承这个stub服务桩类。Service的onBind方法会返回实现类的对象,之后你就可以使用它了,参见下例:
IMusicControlService.aidl
[css] view plaincopyprint?
- package com.homer.remote;
- interface IMusicControlService{
- void play();
- void stop();
- void pause();
- }
使用eclipse的Android插件,会根据这个aidl文件生成一个Java接口类,生成的接口类中会有一个内部类Stub类,Service来继承该Stub类:
Service
[java] view plaincopyprint?
- public class RemoteMusicService extends Service {
- private MediaPlayer mediaPlayer;
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
- private final IMusicControlService.Stub binder = new IMusicControlService.Stub() {
- @Override
- public void play() throws RemoteException {
- if (mediaPlayer == null) {
- mediaPlayer = MediaPlayer.create(RemoteMusicService.this, R.raw.tmp);
- mediaPlayer.setLooping(false);
- }
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- }
- @Override
- public void pause() throws RemoteException {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- @Override
- public void stop() throws RemoteException {
- if (mediaPlayer != null) {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
- };
- @Override
- public void onDestroy() {
- super.onDestroy();
- if(mediaPlayer != null){
- mediaPlayer.stop();
- mediaPlayer.release();
- }
- }
- }
客户端(Activity)应用连接到这个Service时,onServiceConnected方法将被调用,客户端就可以获得IBinder对象。参看下面的客户端onServiceConnected方法:
Activity
[java] view plaincopyprint?
- public class PlayRemoteMusic extends Activity implements OnClickListener {
- private Button playBtn;
- private Button stopBtn;
- private Button pauseBtn;
- private Button exitBtn;
- private IMusicControlService musicService;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.remote_music_service);
- playBtn = (Button) findViewById(R.id.play);
- stopBtn = (Button) findViewById(R.id.stop);
- pauseBtn = (Button) findViewById(R.id.pause);
- exitBtn = (Button) findViewById(R.id.exit);
- playBtn.setOnClickListener(this);
- stopBtn.setOnClickListener(this);
- pauseBtn.setOnClickListener(this);
- exitBtn.setOnClickListener(this);
- connection();
- }
- private void connection() {
- Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");
- bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
- }
- @Override
- public void onClick(View v) {
- try {
- switch (v.getId()) {
- case R.id.play:
- musicService.play();
- break;
- case R.id.stop:
- if (musicService != null) {
- musicService.stop();
- }
- break;
- case R.id.pause:
- if (musicService != null) {
- musicService.pause();
- }
- break;
- case R.id.exit:
- this.finish();
- break;
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- private ServiceConnection sc = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) { //connect Service
- musicService = IMusicControlService.Stub.asInterface(service);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) { //disconnect Service
- musicService = null;
- }
- };
- @Override
- public void onDestroy(){
- super.onDestroy();
- if(sc != null){
- unbindService(sc); // unBindService
- }
- }
- }
Remote Service流程总结:
1、 Activity(客户端)中,Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");构建intent,然后bindService(intent, sc, Context.BIND_AUTO_CREATE);绑定服务
2、 Activity(客户端)中,通过ServiceConnection()重载onServiceConnected()建立连接,获取Service.Stub实例;onServiceDisconnected()释放连接(与bindService类似)
3、 Service中,通过重载onBind(Intent intent) 返回Service.Stub实例,但Service.Stub类是由aidl文件生成的接口类中的一个内部类Stub类,Service来继承该Stub类
4、 Activity中,通过操作Service实例(musicService),执行音乐播放操作(play、pause、stop等)
参考推荐:
Service (android developer)
Android Service 服务(一)—— Service
Android Service 服务(二)—— BroadcastReceiver
七、 组件的通信(remoteservice详细)
1. 以startService(Intent intent)的方式启动. 此时启动的Service与调用者之间没有关联, 即使调用者已经退出, Service仍然可以继续运行, 而且调用者和Service之间无法进行数据交换和通信. 如果需要停止Service的运行, 只能调用Context类的stopService(intent)方法, 或者由Service本身调用其stopSelf()等方法.(第一个startservice的例子实际上是通过intent携带的参数来进行通信的)
2. 以bindService(Intent service, ServiceConnection conn, int flags)的方式启动.
此时调用者与Service绑定在一起, 如果调用者退出, 则Service也随之退出, 而且调用者和Service之间可以进行数据交换或通信.
根据调用者和Service是否在一个应用程序内, 可以将调用者和Service之间的通信分为进程内通信和进程间通信.
a. 进程内通信. bindService(Intent service, ServiceConnection conn, int flags)方法的第二个参数为ServiceConnection对象, 最后一个参数通常可以是Service.BIND_AUTO_CREATE. ServiceConnection是一个接口, 该接口包含2个方法:
|-- onServiceConnected(ComponentName name, IBinder service): 该方法在调用者和Service成功绑定之后由系统回调.
方法中的第一个参数ComponentName是所绑定的Service的组件名称, 而IBinder对象就是Service中onBinder()方法的返回值. 要实现调用者和Service之间的通信, 只需要调用IBinder对象中定义的方法即可.
b.进程间通信. 调用者和Service如果不在一个进程内, 就需要使用android中的远程Service调用机制.(在一个进程定义了一个AIDL接口包,这个包里提供了一个静态的binder类,然后在另一个进程中引入这个包,通过得到这个binder来实现接口中定义的方法)
android使用AIDL定义进程间的通信接口. AIDL的语法与java接口类似, 需要注意以下几点:
1. AIDL文件必须以.aidl作为后缀名.
2. AIDL接口中用到的数据类型, 除了基本类型, String, List, Map, CharSequence之外, 其他类型都需要导包, 即使两种在同一个包内. List和Map中的元素类型必须是AIDL支持的类型.
3. 接口名需要和文件名相同.
4. 方法的参数或返回值是自定义类型时, 该自定义的类型必须实现了Parcelable接口.
5. 所有非java基本类型参数都需要加上in, out, inout标记, 以表明参数是输入参数, 输出参数, 还是输入输出参数.
6. 接口和方法前不能使用访问修饰符和static, final等修饰.
示例参考:【转】http://coolxing.iteye.com/blog/1222602
Service是android中的服务组件, 经常用来执行一些运行在后台的耗时操作. 使用一个Service需要继承Service类, 并根据需要重写生命周期方法. Service的生命周期如下:
|-- public abstract IBinder onBind (Intent intent): 该方法是一个抽象方法, 因此Service子类必须实现这个方法. 它返回一个IBinder对象, 应用程序可以通过这个对象与Service组件通信(关于这一点, 其后会有详细的讲解), 以bindService()方式启动Service时回调该方法.
|-- public void onCreate (): 当Service第一次被创建后回调的方法.
|-- public void onDestroy (): Service关闭之前回调的方法.
|-- public int onStartCommand (Intent intent, int flags, int startId): 以startService(Intent intent)的方式启动Service时, 系统都会回调该方法.
|-- public boolean onUnbind (Intent intent): 当所有绑定该Service的组件都断开连接时回调该方法.
从图中可以看出, Service可以有两种启动方式:
1. 以startService(Intent intent)的方式启动. 此时启动的Service与调用者之间没有关联, 即使调用者已经退出, Service仍然可以继续运行, 而且调用者和Service之间无法进行数据交换和通信. 如果需要停止Service的运行, 只能调用Context类的stopService(intent)方法, 或者由Service本身调用其stopSelf()等方法.
2. 以bindService(Intent service, ServiceConnection conn, int flags)的方式启动.
此时调用者与Service绑定在一起, 如果调用者退出, 则Service也随之退出, 而且调用者和Service之间可以进行数据交换或通信.
根据调用者和Service是否在一个应用程序内, 可以将调用者和Service之间的通信分为进程内通信和进程间通信.
a. 进程内通信. bindService(Intent service, ServiceConnection conn, int flags)方法的第二个参数为ServiceConnection对象, 最后一个参数通常可以是Service.BIND_AUTO_CREATE. ServiceConnection是一个接口, 该接口包含2个方法:
|-- onServiceConnected(ComponentName name, IBinder service): 该方法在调用者和Service成功绑定之后由系统回调.
方法中的第一个参数ComponentName是所绑定的Service的组件名称, 而IBinder对象就是Service中onBinder()方法的返回值. 要实现调用者和Service之间的通信, 只需要调用IBinder对象中定义的方法即可.
|-- onServiceDisconnected(ComponentName name): 该方法在调用者解除和Service的绑定之后由系统回调.
以下是利用Service实现进程内通信的一个例子.
首先自定义Service类:
Java代码
- public class MyService extends Service {
- public class MyBinder extends Binder {
- /**
- * 获取Service的运行时间
- * @return
- */
- public long getServiceRunTime() {
- return System.currentTimeMillis() - startTime;
- }
- }
- private long startTime;
- /**
- * MyBinder是Binder的子类, 而Binder实现了IBinder接口.
- */
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- super.onCreate();
- startTime = System.currentTimeMillis();
- }
- }
然后在activity中绑定上述的Service:
Java代码
- public class MainActivity extends Activity {
- private MyBinder binder = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 创建一个指向MyService的intent
- Intent intent = new Intent("cn.xing.action.my_service");
- this.bindService(intent, new MyServiceConnection(),
- Service.BIND_AUTO_CREATE);
- Button button = (Button) this.findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (binder != null) {
- Toast.makeText(getApplicationContext(), "MyService已经运行了" + binder.getServiceRunTime()
- + "毫秒", Toast.LENGTH_LONG).show();
- }
- }
- });
- }
- /**
- * 实现ServiceConnection接口
- *
- * @author xing
- *
- */
- private final class MyServiceConnection implements ServiceConnection {
- /**
- * 和MyService绑定时系统回调这个方法
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // MyService中的onBinder()方法的返回值实际上是一个MyBinder对象, 因此可以使用强制转换.
- binder = (MyBinder) service;
- }
- /**
- * 解除和MyService的绑定时系统回调这个方法
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // 解除和MyService的绑定后, 将binder设置为null.
- binder = null;
- }
- }
- }
b.进程间通信. 调用者和Service如果不在一个进程内, 就需要使用android中的远程Service调用机制.
android使用AIDL定义进程间的通信接口. AIDL的语法与java接口类似, 需要注意以下几点:
1. AIDL文件必须以.aidl作为后缀名.
2. AIDL接口中用到的数据类型, 除了基本类型, String, List, Map, CharSequence之外, 其他类型都需要导包, 即使两种在同一个包内. List和Map中的元素类型必须是AIDL支持的类型.
3. 接口名需要和文件名相同.
4. 方法的参数或返回值是自定义类型时, 该自定义的类型必须实现了Parcelable接口.
5. 所有非java基本类型参数都需要加上in, out, inout标记, 以表明参数是输入参数, 输出参数, 还是输入输出参数.
6. 接口和方法前不能使用访问修饰符和static, final等修饰.
下面通过一个例子来演示android远程Service调用机制的各个步骤:
1. 创建remoteService项目.
2. 在cn.xing.remoteservice包下定义aidl文件--IRemoteService.aidl:
Java代码
- package cn.xing.remoteservice;
- interface IRemoteService{
- int getServiceRunTime();
- }
Eclipse的ADT插件会在gen目录的cn.xing.remoteservice包下自动根据aidl文件生成IRemoteService接口.
接口中有一个static的抽象内部类Stub, Stub类继承了Binder类并实现了IRemoteService接口. Stub类有如下的静态方法:
public static cn.xing.remoteservice.IRemoteService asInterface(android.os.IBinder obj)
该方法接受一个IBinder对象, 返回值是IRemoteService的instance, 通过这个instance我们就可以调用IRemoteService中定义的方法了.
3. 在remoteService项目的cn.xing.remoteservice包下创建远程服务类RemoteService:
Java代码
- public class RemoteService extends Service {
- private long startTime;
- /**
- * IRemoteService.Stub类实现了IBinder和IRemoteService接口
- * 因此Stub的子类对象可以作为onBinder()方法的返回值.
- * @author xing
- *
- */
- public class MyBinder extends IRemoteService.Stub {
- @Override
- public long getServiceRunTime() throws RemoteException {
- return System.currentTimeMillis() - startTime;
- }
- };
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- super.onCreate();
- startTime = System.currentTimeMillis();
- }
- }
4. 创建一个新的android项目invokeRemoteService, 并复制remoteService项目中的aidl文件(连同包结构一起复制)到invokeRemoteService项目中.
5. 在invokeRemoteService项目中绑定远程服务, 并调用远程方法.
Java代码
- public class MainActivity extends Activity {
- private IRemoteService iRemoteService = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 创建一个指向RemoteService的intent
- Intent intent = new Intent("cn.xing.action.remote_service");
- this.bindService(intent, new MyServiceConnection(),
- Service.BIND_AUTO_CREATE);
- Button button = (Button) this.findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (iRemoteService != null) {
- try {
- Toast.makeText(getApplicationContext(), "MyService已经运行了" + iRemoteService.getServiceRunTime()
- + "毫秒", Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
- });
- }
- /**
- * 实现ServiceConnection接口
- *
- * @author xing
- *
- */
- private final class MyServiceConnection implements ServiceConnection {
- /**
- * 和RemoteService绑定时系统回调这个方法
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // 此处不能使用强制转换, 应该调用Stub类的静态方法获得IRemoteService接口的实例对象
- iRemoteService = IRemoteService.Stub.asInterface(service);
- }
- /**
- * 解除和RemoteService的绑定时系统回调这个方法
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // 解除和RemoteService的绑定后, 将iRemoteService设置为null.
- iRemoteService = null;
- }
- }
- }
附件中包含进程内通信和进程间通信例子的源码, 需要的朋友可自行下载查看.
- 进程内通信.rar (44.7 KB)
- 下载次数: 173
- 进程间通信.rar (92.9 KB)
- 下载次数: 279
八、 拓展知识(进程和声明周期)
Android操作系统尝试尽可能长时间的保持应用的进程,但当可用内存很低时最终要移走一部分进程。怎样确定那些程序可以运行,那些要被销毁,Android让每一个进程在一个重要级的基础上运行,重要级低的进程最有可能被淘汰,一共有5级,下面这个列表就是按照重要性排列的:
1 一个前台进程显示的是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
a 与用户正发生交互的。
b 它控制一个与用户交互的必须的基本的服务。
c 有一个正在调用生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())
d 它有一个正在运行onReceive()方法的广播接收对象。
只有少数的前台进程可以在任何给定的时间内运行,销毁他们是系统万不得已的、最后的选择——当内存不够系统继续运行下去时。通常,在这一点上,设备已经达到了内存分页状态,所以杀掉一些前台进程来保证能够响应用户的需求。
2 一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面两种情况发生时,可以称该进程为可用进程。
它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)这是可能发生的,例如:前台的activity是一个允许上一个activity可见的对话框,即当前activity半透明,能看到前一个activity的界面,它是一个服务于可用activity的服务。
3 一个服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据。所以系统保证他们的运行,直到不能保证所有的前台可见程序都正常运行时才会终止他们。
4 一个后台进程就是一个非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后台进程会有很多个在运行,所以他们维护一个LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。如果一个activity正确的实现了生命周期的方法,并且保存它当前状态,杀死这些进程将不会影响到用户体验。
5 一个空线程没有运行任何可用应用程序组,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
Android把进程里优先级最高的activity或服务,作为这个进程的优先级。例如,一个进程拥有一个服务和一个可见的activity,那么这个进程将会被定义为可见进程,而不是服务进程。
此外,如果别的进程依赖某一个进程的话,那么被依赖的进程会提高优先级。一个进程服务于另一个进程,那么提供服务的进程不会低于获得服务的进程。例如,如果进程A的一个内容提供商服务于进程B的一个客户端,或者进程A的一个service被进程B的一个组件绑定,那么进程A至少拥有和进程B一样的优先级,或者更高。
因为一个运行服务的进程的优先级高于运行后台activity的进程,一个activity会准备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个activity。例如后台播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保证这个操作至少运行在service 进程的优先级下,无论这个activity发生了什么,广播接收者应该作为一个空服务而不是简单的把耗时的操作单独放在一个线程里。
【补充】http://blog.csdn.net/p106786860/article/details/17885115 :intentservice的使用
关于AIDLservice的binder写法还有:(参考:http://blog.csdn.net/p106786860/article/details/17733223)
- /**
- * 继承Stub,也就是实现了ICat接口,并实现了IBinder接口
- *
- * @author pengcx
- *
- */
- public class CatBinder extends Stub {
- @Override
- public String getColor() throws RemoteException {
- return color;
- }
- @Override
- public double getWeight() throws RemoteException {
- return weight;
- }
- }