android的Bound Service----讲述进程间通信

来源:互联网 发布:刷赞软件下载 编辑:程序博客网 时间:2024/06/05 01:16

BoundServices

Bound Services是客户端-服务器接口的服务。Bound service允许应用组件(例如activity,下文用activity替代)绑定到serivce,发送请求,接收响应甚至实现进程间通信。Bound service只在当它服务另一个activity的时候存在而且不会在后台独立的运行。

这篇文章主要展示如何创建bound service,包括如何绑定其他activity创建的service。然而,有什么不明白的可以参考我的上一篇博客,可以对service有一个很好的理解。

基础知识

Bound service是service类的实现,它允许其他的应用程序绑定到service上并和它交互。想要给服务提供绑定,你就要实现onBind()回调方法。这个方法返回一个IBinder的对象,它用于定义应用与service进行交互的编程接口。

应用可以通过调用bindService()方法绑定到服务上。它一定要实现ServiceConnection,这个方法用于监听与服务的连通性。bindService()没有返回值并会立刻返回,但是当android系统创建应用于service的连接时,它会调用onServiceConnected()方法,然后传递IBinder对象给应用,这样应用就可和service交流了。

多个应用可以瞬时和service建立连接。然后,系统调用你的多个应用中的第一个service的onBind()方法来检索IBnder对象,对于其他应用就不会再次调用onBind()方法了。

当其他所有的activity都不绑定到这个服务至上,那么系统会掉毁掉这个服务,但是,如果service还被startService()方法调用启动过,那么就要使用stopSelf()或者stopService()来结束了。

 

当你实现了bound service,那么最重要的就是要定义接口接收onBind()回调方法的返回值。下面将会讲述一些比较关键的步骤:

创建Bound Service

想要给服务提供绑定,你就要实现onBind()回调方法。这个方法返回一个IBinder的对象,它用于定义应用与service进行交互的编程接口。有三种方法可以定义这个接口:

Extending theBinder class:如果你的服务是给你自己的应用程序去用,那么你应该继承Binder类,创建属于你自己的接口并接收onBind()方法的返回值。Activity接收Binder,然后直接可以通过公共方法使用这个Binder而不用实现Binder甚至是Service。这个服务当它在后台运行,且只给你运行的时候使用这个方法是最好的了,但是如果你的服务不是私有的,而是让其他的应用程序也会用到,那么你最好还是不要选择这个方法。因为它跨越了不同的进程。

Using a Messenger:如果你需要你的接口要在不同的进程中使用,你就应该使用Messenger为服务创建接口。在这种方式中,service定义Hander用于解决不同类型的Message对象。Handler是Messenger的基础,它可以分享IBinder对象给client,允许类通过Message对象发送命令到service。除此之外,你可以定义属于你自己的Messenger,这样service就能把消息发送回来了。

这个方式是实现IPC最简单的一种方式,因为Messenger quenes是一种单线程的东西而且你设计你服务的时候不用考虑线程安全问题。

Using AIDL:这个问题我们在AIDL那篇博客详细的说过,详细的内容请看那篇关于AIDL的叙述,这里就不赘述了。

(注:大部分的应用程序不应该使用AIDL创建bound service,因为它可能需要你有多线程并发的解决能力,同时,它也会使得程序变得很复杂)


继承Binder类

如果你的服务只是使用自己的应用程序而且不需要通过进程来操作其他应用程序,那么你就可以实现你自己的Binder类,在serivce中直接让类调用它的公共方法。

(注:这个情况只能是在应用程序和服务都在一个线程之中,尤其是在前台的service操作后台的播放动作这样的音乐应用上编辑最为有效果)

你只需要做以下几个步骤:

1.     在你的service中创建一个Binder的实例化对象,或者:

·包含一个其他类可以调用的方法;

·返回当前服务的实例,这样有公共方法的类就能够调用它;

·返回一个其他类创建服务的实例,这样有公共方法的类就能够调用它;

2.     从onBind()回调方法中返回Binder类的实例化对象;

3.     在类中,接收从onServiceConntected()回调方法的Binder然后通过使用提供的方法调用bound service;

(注:为什么非要让serivce和类都要在一个应用程序中,就是因为这样做的话类就能正确的收到传递过来的返回对象。Service和类也一定要在相同的进程中,因为这个方法不提供分发数据到其他进程的机制)

例如,下面这个方法就是通过Binder实现的与service连接

public class LocalServiceextends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we knowthis service always
     * runs in the same process as its clients, we don't need todeal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance ofLocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder给类提供getService()方法用于检索当前LocalService的实例。这允许类调用服务中公共的方法。例如,该类service中调用getRandomNumber()方法。

public class BindingActivityextends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layoutfile attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from theLocalService.
            // However, if this call weresomething that might hang, then this request should
            // occur in a separate thread toavoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number:" + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed tobindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentNameclassName,
                IBinder service) {
            // We've bound to LocalService, castthe IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder)service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentNamearg0) {
            mBound = false;
        }
    };
}

上面是一个简单的例子介绍bound service的使用方法,下面将会再进行详细的讨论。


Usinga Messenger

如果你需要使用服务交流远程的进程,那么你就可以使用Messenger为服务提供这个接口,它使你最简单的实现进程间通信。

下面说一说如何使用Messenger:

·service实现了Handler类用于接收从其他类发来的回调请求;

·Handler用于创建Messenger对象;

·Messenger创建IBinder类用于接收onBind()方法中发送过来的返回值;

·某个类使用IBinder类来实例化Messenger,用于发送Message对象给service;

·service在Handler中接收每一个Message,在handleMessage()方法中;

在这种情况中,没有类调用方法给service。取而代之的是,类传递“messages”然后service用它的Handler接收。

public class MessengerServiceextends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(),"hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages toIncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface toour messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding",Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

 

值得注意的是,在Handler中使用handleMessage()方法接收即将到来的Message,是使用what关键字的;

 

你只要在类中创建一个基于被service返回的IBinder的Messenger然后通过send()方法发送一条message。例如,下面这个例子就是绑定到service上然后给服务发送MSG_SAY_HELLO消息;

public class ActivityMessengerextends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service.*/
    boolean mBound;

    /**
     * Class for interacting with the main interface of theservice.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentNameclassName, IBinder service) {
            // This is called when the connectionwith the service has been
            // established, giving us the objectwe can use to
            // interact with the service. We are communicating with the
            // service using a Messenger, so herewe get a client-side
            // representation of that from theraw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentNameclassName) {
            // This is called when the connectionwith the service has been
            // unexpectedly disconnected -- thatis, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, usinga supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO,0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class),mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

值得注意的是,这个例子中没有展示service如何对类响应。如果你想让service对其进行响应,那你需要在你的类中创建一个Messenger对象,然后当该类接收到onServiceConnected()回调方法的值时,它会给service发送一条Message,这条Message包含该类的Messenger,这个Messenger的类型是send()方法中的replyTo参数。

此外,你还需要好好看看MessengerService.java和MessengerServiceActivities.java两个类

Binding to aService

类可以通过调用bindService()方法绑定到service上,然后android系统调用service的onBind()方法,返回一个IBinder类的实例化对象用于与服务交互。

这个绑定是异步的。bindService()会立刻返回而且不会把IBinder返回给类。为了接收这个IBinder,类必须创建一个ServiceConnection的实例化对象然后把它传递给bindService()。然后ServiceConnection包含了一个系统调用传送IBinder的回调方法。

(注:只有activites、services、content providers可以绑定服务,你不能使用contentprovider绑定服务)

所以,你的类要想绑定service,你一定要:

1.     实现ServiceConnection

你实现的时候一定要重载以下两个回调方法:

OnServiceConnected():系统调用这个方法来传递被服务的onBind()方法返回的IBinder

onServiceDisconnected():当正在连接的服务发生异常时系统调用这个方法

2.     调用bindService()方法,传递Serviceconnection的实现实例

3.     当系统调用你的onServiceConnected()回调方法,你可以使用你定义的接口的方法开始调用服务了。

4.     断开与service的连接,调用unbindService()方法。

例如,下面这段代码就是通过继承了Binder类把该类与服务连接,所以你必须要传递一个IBinder的返回值给LocalService类然后实例化LocalService

 

LocalServicemService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinderservice) {
        // Because we have bound to an explicit
        // service that is running in our own process, wecan
        // cast its IBinder to a concrete class anddirectly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnectsunexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

使用Serviceconnection,类可以绑定到服务上通过bindService,例如:

Intent intent = newIntent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

·bindService的第一个参数是一个intent

·第二个参数时Serviceconnection对象

·第三个参数是一个声明,通常是BIND_AUTO_CREATE,为了创建不存在的serivce。


额外的一些注意事项

·你可能经常遇到DeadObjectException,就是当远程连接时发生中断会出现的异常

·多个进程,对象都被当成了引用

·如果你想要你的activity接受回应当它在后台停止的时候,那么你就绑定onCreate()而在onDestory()中释放绑定,这就意味着你的activity需要在整个运行期一直使用服务,所以如果service在另一个进程中,你就会增加进程的负重从而出现被系统杀掉的风险;

(注:你不应该经常绑定和取消绑定在你activty调用onResume()和onPause()方法)


管理Bound Service的生命周期:

当service没有绑定时,系统就会销毁它。这样,如果service纯粹是一个纯粹的Bound Service,你不必管理服务的生命周期。

然而,如果你选择实现onStartCommand()回调方法,然后你必须明确的停止service,因为这个服务被认为已经开启了。在这种情况下,service会一直运行直到service停止自己或者调用stopService(),不管它是否绑定到了任何类。

除此之外,如果你的service已经开启了并且接受binging,然后当系统调用你的onUnbind()方法,你就可以返回true如果想要接受onRebind()的调用。OnRebind()返回void,但是类依然接收IBinder在它的onServiceConnected()回调方法。上面可能说的有点乱,下面看看这个图吧:



笔者个人理解:通过对bound Service的学习,这样就会发现,我们完全可以通过里面的Messenger去实现,接下来会做一个通过Bound Service

实现的Demo。本人个人翻译能力有限,如有不足,欢迎斧正。

0 0
原创粉丝点击