AndroidService 深度解析(2)

来源:互联网 发布:linux复制为多个文件 编辑:程序博客网 时间:2024/06/11 22:26

AndroidService 深度解析(2)

上一篇文章我们对Service的生命周期进行了测试及总结。这篇文章我们介绍下绑定运行的Service的实现。

绑定运行的Service可能是仅为本应用提供服务,称为本地Service;也可能为其他应用提供跨进程服务,即远程Service。下面分别进行介绍:

本地Service

如果Service只服务于本应用,那么我们只需要继承Binder类,定义我们需要实现的方法即可,当发起绑定连接时,Service将会在onBind方法中返回这个继承类的对象,使得客户端与Service共享一个Binder对象。Binder就像一座桥梁,使客户端与Service能够互相联系。下面贴出本地Service的实现示例:

LocalService代码:

public classLocalService extends Service {    private String TAG =getClass().getSimpleName();    MyBinder myBinder = new MyBinder();    ServiceListener myServiceListener;    public LocalService() {    }     public interface ServiceListener {        public String getActivityInfo();    }     private void setListener(ServiceListenermyServiceListener) {        this.myServiceListener = myServiceListener;    }     //绑定成功后,Service就可以通过这个方法获得Activity的信息    private void getActivityInfo() {        String activityInfo =myServiceListener.getActivityInfo();        Log.d(TAG, TAG +"+activityInfo------>" + activityInfo);    }     private String getInfo() {        return "Hello,我是LocalService的方法,你可以通过它的对象访问我!";    }     public class MyBinder extends Binder {        public String getServiceInfo() {            return getInfo();        }         public void setServiceListener(ServiceListenermyServiceListener) {            setListener(myServiceListener);        }    }     @Override    public IBinder onBind(Intent intent) {        Log.d(TAG, TAG +"------>onBind()");        return myBinder;    }     @Override    public void onRebind(Intent intent) {        Log.d(TAG, TAG +"------>onRebind()");        super.onRebind(intent);    }     @Override    public boolean onUnbind(Intent intent) {        Log.d(TAG, TAG +"------>onUnbind()");        //return false;这里的返回值决定下一次绑定时是否执行onRebind        return true;    }}

LocalActivity代码:

public classLocalActivity extends ActionBarActivity {    private String TAG =getClass().getSimpleName();    Intent serviceIntent;     @Override    protected void onCreate(BundlesavedInstanceState) {        super.onCreate(savedInstanceState);       setContentView(R.layout.activity_local);        Log.d(TAG, TAG +"------>onCreate()");        serviceIntent = new Intent(this,LocalService.class);    }     public void bindService(View v) {        Log.d(TAG, TAG +"------>bindService()");        bindService(serviceIntent,serviceConnection, Service.BIND_AUTO_CREATE);    }     public void unBindService(View v) {        Log.d(TAG, TAG +"------>unBindService()");        unbindService(serviceConnection);    }     @Override    protected void onDestroy() {        Log.d(TAG, TAG +"------>onDestroy()");        super.onDestroy();    }     ServiceConnection serviceConnection = newServiceConnection() {        @Override        public voidonServiceConnected(ComponentName name, IBinder service) {            Log.d(TAG, TAG +"------>onServiceConnected()");            LocalService.MyBinder binder =(LocalService.MyBinder) service;            String localServiceInfo =binder.getServiceInfo();            Log.d(TAG, TAG +"+onServiceConnected------>" + localServiceInfo);            binder.setServiceListener(newLocalService.ServiceListener() {                @Override                public String getActivityInfo(){                    return "Hello,我在LocalActivity中,LocalService可以调用我获得LocalActivity的消息!";                }            });        }         @Override        public voidonServiceDisconnected(ComponentName name) {            Log.d(TAG, TAG +"------>onServiceDisconnected()");        }    };}

Activity对应的布局就是两个按钮,分别实现绑定和解绑功能,如图:

Activity与Service都是需要在Manifest文件中注册的哦。

 

我们启动Activity,先后绑定Service,输出日志如下:

03-17 10:10:58.525  D/LocalActivity﹕LocalActivity------>onCreate()03-17 10:11:00.955  D/LocalActivity﹕LocalActivity------>bindService()03-17 10:11:00.975  D/LocalService﹕LocalService------>onBind()03-17 10:11:00.995  D/LocalActivity﹕LocalActivity------>onServiceConnected()03-17 10:11:00.995  D/LocalActivity﹕ LocalActivity+onServiceConnected------>Hello,我是LocalService的方法,你可以通过它的对象访问我!03-17 10:11:16.345  D/LocalActivity﹕LocalActivity------>unBindService()03-17 10:11:16.345  D/LocalService﹕LocalService------>onUnbind()

       上面的日志显示,我们的确实现了Service的绑定与解绑工作,不仅如此,细心的你应该还发现了我们实现了Service与Activity中相互的调用吧。是的,在实际工作中我们不仅需要指示Service为我们提供服务,Service有时也需要获取到客户端的数据来更好地提供服务(LocalService中的getActivityInfo方法  通过回调实现)。

这里我总结下具体的实现过程:

1、在Service类中,设计继承Binder类的内部类MyBinder,添加需要向Activity提供的方法。本例中的getServiceInfo方法实现了获取Service信息的功能,当然有时候为了简便,我们直接提供方法返回Service对象,但是一般并不建议这样做;同时注意到setServiceListener方法,它是实现Service调用Activity提供方法的重要环节,我们通过回调的方法实现了Service对Activity的访问;

2、重写onBind方法,并返回MyBinder对象。至此,Service类的设计就完成了;

3、在Activity中,重写ServiceConnection接口的onServiceConnected与onServiceDisConnected方法;在onServiceConnected方法中,我们获得了onBinder方法返回的MyBinder对象,然后调用setServiceListener方法设置Service访问Activity所需要的回掉接口对象;
4、至此,Service与Activity之间的 “桥梁”搭建完毕。Service中我们可以通过getActivityInfo方法获得Activity的信息;而在Activity中,我们也可以通过getServiceInfo方法获得Service的信息。

远程Service

当我们的Service需要为其他应用提供服务的时候,我们就要用到远程Service了。远程Service有两种实现方式,分别是Messenger方式与AIDL(Andriod进程间接口描述语言)方式。下面分别进行介绍。

Messenger方式

在这种方式中,我们定义一个Handler来处理不同的Message对象。这个Handler是Messenger实现与客户端共享IBinder的基础,它允许客户端通过Message对象向Service发送命令。另外,客户端也可以定义一个Messenger,这样,Service也可以把消息发送给客户端。  这是实现进程间通信的最简单的方式,因为Messenger队列将会在单线程中执行,我们不需要去考虑线程安全。

使用Messenger实现进程间通信的步骤:

l  实现一个Handler,它用来处理传递的Message对象;

l  创建一个Messenger对象,将Handler对象作为构造参数;

l  使用Messenger对象创建一个IBinder对象,并通过onBind返回;

l  客户端将接收到的IBinder对象作为Messenger的构造参数,实例化一个Messenger对象,这个Messenger对象将拥有Handler的引用;

l  在客户端通过Handler发送Message对象,Service中就可以通过Handler的handleMessage处理这个Message。

下面提供一个示例:

我们在ServiceTest工程下新建MessengerService类:

public class MessengerService extendsService {   /**    * Command to the service to display a message    */   static final int MSG_SAY_HELLO = 1;   static final int MSG_GET_CLIENT_MESSENGER = 2;   static final int MSG_FROM_SERVICE = 3;    private String TAG = getClass().getSimpleName();   Messenger messengerToClient;    /**    * Handler of incoming messages from clients.    */   class ServiceIncomingHandler extends Handler {       @Override       public void handleMessage(Message msg) {            Log.d(TAG, TAG + "------>handleMessage()");            switch (msg.what) {                case MSG_SAY_HELLO:                    Log.d(TAG,"handleMessage------>MSG_SAY_HELLO!");                   Toast.makeText(getApplicationContext(), "hello!",Toast.LENGTH_SHORT).show();                    break;                case MSG_GET_CLIENT_MESSENGER:                    Log.d(TAG,"handleMessage------>Service收到Activity的messenger对象!");                    //此处获得可向客户端发送消息的Messenger对象                    messengerToClient =msg.replyTo;                    Message serviceMsg =Message.obtain(null, MSG_FROM_SERVICE, 0, 0);                    try {//向客户端发送消息                       messengerToClient.send(serviceMsg);                    } catch (RemoteException e){                        e.printStackTrace();                    }                    break;                default:                    super.handleMessage(msg);            }       }   }    /**    * 将这个serviceMessenger发送给客户端,客户端就可以通过它联系Service了    */   final Messenger serviceMessenger = new Messenger(newServiceIncomingHandler());    /**    * When binding to the service, we return an interface to our messenger    * for sending messages to the service.    */   @Override   public IBinder onBind(Intent intent) {       Log.d(TAG, TAG + "------>onBind()");       Toast.makeText(getApplicationContext(), "binding",Toast.LENGTH_SHORT).show();       return serviceMessenger.getBinder();   }}


新建MessengerTest工程,并创建ActivityMessenger类:

public classActivityMessenger extends Activity {    /**     * Messenger for communicating with theservice.     */    Messenger messengerToService = null;    /**     * Flag indicating whether we have calledbind on the service.     */    boolean mBound;    private String TAG =getClass().getSimpleName();    /**     * Command to the service to display amessage     */    static final int MSG_SAY_HELLO = 1;    static final intMSG_SEND_MESSENGER_TO_SERVICE = 2;    static final int MSG_FROM_SERVICE = 3;     /**     * Class for interacting with the maininterface of the service.     */    private ServiceConnection mConnection = newServiceConnection() {        public voidonServiceConnected(ComponentName className, IBinder service) {            // This is called when theconnection with the service has been            // established, giving us theobject we can use to            // interact with the service.  We are communicating with the            // service using a Messenger, sohere we get a client-side            // representation of that from theraw IBinder object.            Log.d(TAG, TAG +"------>onServiceConnected()");            messengerToService = newMessenger(service);            mBound = true;            Message msg = Message.obtain(null,MSG_SEND_MESSENGER_TO_SERVICE, 0, 0);            msg.replyTo = activityMessenger;            try {//Messenger对象发送消息,这个msg对象将交给Service类中的handleMessage处理                messengerToService.send(msg);            } catch (RemoteException e) {                e.printStackTrace();            }        }         public voidonServiceDisconnected(ComponentName className) {            // This is called when theconnection with the service has been            // unexpectedly disconnected --that is, its process crashed.            Log.d(TAG, TAG +"------>onServiceDisconnected()");            messengerToService = null;            mBound = false;        }    };     /**     * Handler of incoming messages fromservice.     */    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg){            Log.d(TAG, TAG + "------>handleMessage()");            switch (msg.what) {                case MSG_FROM_SERVICE:                    Log.d(TAG, TAG +"+MSG_FROM_SERVICE------>Activity收到Service回复的消息!");                   Toast.makeText(getApplicationContext(), "MSG_FROM_SERVICE!",Toast.LENGTH_SHORT).show();                    break;                default:                    super.handleMessage(msg);            }        }    }     /**     * Target we publish for service to sendmessages to IncomingHandler.     */    final Messenger activityMessenger = newMessenger(new IncomingHandler());     public void sayHello(View v) {        Log.d(TAG, TAG +"------>sayHello()");        if (!mBound) return;        // Create and send a message to theservice, using a supported 'what' value        Message msg = Message.obtain(null,MSG_SAY_HELLO, 0, 0);        try {//Messenger对象发送消息,这个msg对象将交给Service类中的handleMessage处理            messengerToService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }     @Override    protected void onCreate(BundlesavedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Log.d(TAG, TAG +"------>onCreate()");    }     @Override    protected void onStart() {        Log.d(TAG, TAG +"------>onStart()");        super.onStart();        // Bind to the service        bindService(newIntent("com.example.servicestudy.remoteservie.MessengerService"),mConnection,                Context.BIND_AUTO_CREATE);    }     @Override    protected void onStop() {        Log.d(TAG, TAG +"------>onStop()");        super.onStop();        // Unbind from the service        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }}

Acitivty与Service都需要在Manifest文件中注册。Service需要设置android:exported属性为true,并设置intent-filter。如下所示:

<service    android:name=".remoteservie.MessengerService"           android:enabled="true"           android:exported="true">           <intent-filter>                <actionandroid:name="com.example.servicestudy.remoteservie.MessengerService"/>           </intent-filter>       </service>

好了,编码工作我们已经完成了,下面我们进行一下测试,打印日志如下:

03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onCreate()03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onStart()03-1713:11:46.215  D/MessengerService﹕MessengerService------>onBind()03-17 13:11:46.235  D/ActivityMessenger﹕ActivityMessenger------>onServiceConnected()03-1713:11:46.275  D/MessengerService﹕MessengerService------>handleMessage()03-17 13:11:46.275 D/MessengerService﹕ handleMessage------>Service收到Activity的messenger对象!03-17 13:11:46.285  D/ActivityMessenger﹕ActivityMessenger------>handleMessage()03-17 13:11:46.285 D/ActivityMessenger﹕ ActivityMessenger+MSG_FROM_SERVICE------>Activity收到Service回复的消息!03-1713:11:55.425  D/ActivityMessenger﹕ ActivityMessenger------>sayHello()03-1713:11:55.425  D/MessengerService﹕MessengerService------>handleMessage()03-1713:11:55.425  D/MessengerService﹕handleMessage------>MSG_SAY_HELLO!03-1713:12:00.665  D/ActivityMessenger﹕ActivityMessenger------>onStop()

看见了吗?完美实现了Activity与Service互相发送消息。它们都是基于Messenger对象,Activity从Ibinder处获得一个可向Service发送消息的Messenger对象,接着Activity给Service发送了一个消息,并将可向Activity发送消息的Messenger对象携带过去,这样就实现了他们之间的交互。逻辑上和便马上都很简单易懂吧,点个赞。

好了,既然Messenger这么简单易用,为什么我们还需要继续看AIDL方式呢?不知道你有没有想过,Messenger方式在处理客户端发送的消息时,是将所有消息排成一个队列,然后依次处理,也就是单线程处理方式,这种处理方式的优点是简便不容易引起其他问题,比如线程安全;但是,对于一些即时性要求比较高的服务,这种方式可能就不够用了,也许我们需要采用多线程的方式,将接收到的请求尽快处理,这时候就可以直接使用AIDL方式了。

AIDL方式

其实我悄悄告诉你,Messenger方式的底层实现也是基于AIDL方式实现的,系统为了方便跨进程的服务,为我们提供了一个Messenger类来便利的实现,但是它可能无法满足我们的需求,这时候我们就需要直接基于AIDL方式实现了。

其实AIDL的实现不难,只是有很多细节需要注意。我这里也不过多描述推荐一篇文章,有代码和总结:http://blog.csdn.net/songjinshi/article/details/22918405

我把总结摘抄过来:

AIDL的创建方法
AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。由于远程调用的需要,这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型
1. 不需要import声明的简单Java编程语言类型(int,boolean
2.String, CharSequence不需要特殊声明 
3.List, MapParcelables类型,这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型. ( (另外:我没尝试Parcelables,Eclipse+ADT下编译不过,或许以后会有所支持). 
下面是AIDL语法
// 文件名: SomeClass.aidl //文件可以有注释, java的一样 //package以前的注释,将会被忽略. // 函数和变量以前的注释, 都会被加入到生产java代码中. package com.cmcc.demo; 
//import 引入语句 import com.cmcc.demo.ITaskCallback; 
interfaceITaskBinder { 
//函数跟java一样,可以有0到多个参数 ,可以有一个返回值 boolean isTaskRunning(); 
voidstopRunningTask(); //参数可以是另外的一个aidl定义的接口 void registerCallback(ITaskCallback cb); 
voidunregisterCallback(ITaskCallback cb); 
//参数可以是String,可以用in表入输入类型, out表示输出类型
intgetCustomerList(in String branch, out String customerList); 

实现接口时有几个原则
.抛出的异常不要返回给调用者.跨进程抛异常处理是不可取的
.IPC调用是同步的如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。也就是IPC调用会挂起应用程序导致界面失去响应.这种情况应该考虑单起一个线程来处理
.不能在AIDL接口中声明静态属性。 
IPC的调用步骤
1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。 
2. 实现ServiceConnection 
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递
4. ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service).调用YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。 
5. 调用接口中定义的方法。你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。 
6. 断开连接,调用接口实例中的ApplicationContext.unbindService() 

 

同时,我自己对AIDL实现也做了充分的测试,实现了Activity与Service之间互相调用方法,有兴趣的朋友可以下下来测试一下。Github地址:https://github.com/BBigBoy/AndroidServiceFULLStudy

 

 

至此,关于Service的全部学习均已完成。我们进行了Android Service的完整测试学习,主要包括生命周期测试,本地绑定运行Service实现、远程绑定运行Service的Messenger方式与AIDL方式实现 。  所有BOUND SERVICE的示例均实现了Service与客户端的交互功能,即Service可以调用客户端的方法,客户端也可以调用Service的方法。完整的项目见Github仓库:

https://github.com/BBigBoy/AndroidServiceFULLStudy

1 0
原创粉丝点击