zoukankan      html  css  js  c++  java
  • 【起航计划 037】2015 起航计划 Android APIDemo的魔鬼步伐 36 App->Service->Remote Service Binding AIDL实现不同进程间调用服务接口 kill 进程

    本例和下个例子Remote Service Controller 涉及到的文件有RemoteService.java ,IRemoteService.aidl, IRemoteServiceCallback.aidl 及ISecondary.aidl。

    Android Interface Definition Language(AIDL)和其它一些支持远程方法调用RMI的系统的IDL类似,它定义了Service和Client 之间的使用接口约定,这种远程调用一般需要通过进程间通信机制(IPC)来实现。在Android系统,一个进程(Process)通常不能直接访问其它 进程的内存空间,Android系统支持使用AIDL来实现使用不同进程间调用服务接口。

    在设计AIDL接口之前,要意识到使用AIDL接口是通过直接函数调用的方法来进行的,但这种远程调用所发生的线程Thread随调用者是否和Service提供者属于同一进程Process的不同而不同:

    • 如果调用者与Service属于同一个进程(可以称为Local Process),那么AIDL Call将使用与调用者同一线程执行。因此如果你的应用只使用Local Process来访问AIDL Call,那么根本就无必要使用AIDL接口,使用Binder即可,参见Android ApiDemo示例解析(39):App->Service->Local Service Binding
    • 如 果使用Remote Process方式来调用AIDL ,Android将会使用由本进程管理的线程池(Thread pool)来分发函数调用。因此你的Service需要能够处理多进程触发的AIDL Call,换句话来说,AIDL接口的实现必须是Thread-safe的。
    • 关键字oneway 可以修改远程调用的的行为,当使用oneway关键字时,remote call调用后立即返回,有点类似异步调用。

    定义AIDL 接口的步骤如下:

    AIDL接口定义使用Java Interface语法定义在 .aidl文件中,然后必须同时放在Service和Client 的 src目录下。 当使用Eclipse 编译时,Android SDK工具会根据 .aidl的接口定义自动生成对应的IBinder接口定义 (定义在gen目录下) Service必须实现由这个IBinder接口定义。 Client然后可以通过绑定Service来访问这些方法。

    1. 创建. aidl 文件

    AIDL接口定义使用和Java Interface定义同样的语法,每个.aidl文件只能定义一个调用接口,而且只能定义接口方法,不能含有静态变量定义。AIDL缺省支持 int ,long, char, boolean, String, CharSequence, List ,Map 变量类型,也可以引用其它 .aidl中定义的类型。

    下面是IRemoteService.aidl 的定义:

    package com.example.android.apis.app;
    import com.example.android.apis.app.IRemoteServiceCallback;
    /**
     * Example of defining an interface for calling on to a remote service
     * (running in another process).
     */
    interface IRemoteService {
     /**
     * Often you want to allow a service to call back to its clients.
     * This shows how to do so, by registering a callback interface with
     * the service.
     */
     void registerCallback(IRemoteServiceCallback cb);
     /**
     * Remove a previously registered callback interface.
     */
     void unregisterCallback(IRemoteServiceCallback cb);<br />
    }

    编译时,Android SDK 工具自动在gen目录下生成对应的 IRemoteService.java。

    2. 实现这个AIDL接口

    编译时,Android SDK 工具自动在gen目录下生成对应的 IRemoteService.java,这个文件中会定义一个子类Stub (IRemoteService.Stub),Stub定义了由.aidl 定义的抽象方法实现。除此之外,Stub还定义了几个Help 方法,比如asInterface() 参数为IBinder对象(通常由Client中的onServiceConnected()传入),返回一个Stub实例供Client使用。

    下面是IRemoteService.Stub 在 RemoteService中的实现:

        /**
         * The IRemoteInterface is defined through IDL
         */
        private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
            public void registerCallback(IRemoteServiceCallback cb) {
                if (cb != null) mCallbacks.register(cb);
            }
            public void unregisterCallback(IRemoteServiceCallback cb) {
                if (cb != null) mCallbacks.unregister(cb);
            }
        };

    3. Expose the interface to Clients

    在定义.aidl 和实现AIDL接口定义后,就需要将这个接口提供给Client使用。方法是派生Service并提供onBind方法返回上面实现的Stub的一个实例。

    public class RemoteService extends Service {
    ...
    @Override
    public IBinder onBind(Intent intent) {
     // Select the interface to return.  If your service only implements
     // a single interface, you can just return it here without checking
     // the Intent.
     if (IRemoteService.class.getName().equals(intent.getAction())) {
     return mBinder;
     }
     if (ISecondary.class.getName().equals(intent.getAction())) {
     return mSecondaryBinder;
     }
     return null;
    }

    RemoteService 中定义了两个.aidl 接口可供Client使用IRemoteService.aidl和ISecondary.aidl。

    有了AIDL定义并在Service中定义了可供Client使用的AIDL实现。下面再来看看Client的实现步骤:

    1. 将. aidl定义包含着 src目录下,由于本例Service ,Client 都包含在ApiDemos中,.aidl已在src中定义了。
    2. 根据.aidl接口生成IBinder接口定义(编译时由Android SDK工具自动生成)。
    3. 实现ServiceConnection接口
    4. 调用Context.bindService 来绑定需调用的Service。
    5. 在ServiceConnection 的onServiceConnected方法中,根据传入的IBinder对象(被调用的Service),使用 YourInterfaceName.Stub.asInterface((IBinder)service)) 将 service转换为YourInterfaceName类型。
    6. 调用YourInterfaceName定义的方法,这里需要捕获DeadObjectException 异常,DeadObjectException会在链接断裂时抛出。
    7. 使用完Service,使用Context.unbindService断开与Service之间的绑定。

    Remote Service Binding 例子中 Service 端实现了两个Service:

    • IRemoteService :提供registerCallback,unregisterCallback用来在RemoteCallbackList 中注册或注销一个Client的Callback。
    • ISecondary: 实际Client会调用的服务,getPid返回当前进程Process ID。basicTypes 介绍了一般参数类型用法,本例中Client为使用。
            /**
             * Class for interacting with the main interface of the service.
             */
            private ServiceConnection mConnection = new ServiceConnection() {
                public void onServiceConnected(ComponentName className,
                        IBinder service) {
                    // This is called when the connection with the service has been
                    // established, giving us the service object we can use to
                    // interact with the service.  We are communicating with our
                    // service through an IDL interface, so get a client-side
                    // representation of that from the raw service object.
                    mService = IRemoteService.Stub.asInterface(service);
                    mKillButton.setEnabled(true);
                    mCallbackText.setText("Attached.");
    
                    // We want to monitor the service for as long as we are
                    // connected to it.
                    try {
                        mService.registerCallback(mCallback);
                    } catch (RemoteException e) {
                        // In this case the service has crashed before we could even
                        // do anything with it; we can count on soon being
                        // disconnected (and then reconnected if it can be restarted)
                        // so there is no need to do anything here.
                    }
                    
                    // As part of the sample, tell the user what happened.
                    Toast.makeText(Binding.this, R.string.remote_service_connected,
                            Toast.LENGTH_SHORT).show();
                }
    
                public void onServiceDisconnected(ComponentName className) {
                    // This is called when the connection with the service has been
                    // unexpectedly disconnected -- that is, its process crashed.
                    mService = null;
                    mKillButton.setEnabled(false);
                    mCallbackText.setText("Disconnected.");
    
                    // As part of the sample, tell the user what happened.
                    Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                            Toast.LENGTH_SHORT).show();
                }
            };

    在RemoteService 的主线程中定义了一个mHandler,它每隔1秒钟将value值加1,并通过IRemoteServiceCallback 将这个新值发给注册过的Client。

                        // Up it goes.
                        int value = ++mValue;
                        
                        // Broadcast to all clients the new value.
                        final int N = mCallbacks.beginBroadcast();
                        for (int i=0; i<N; i++) {
                            try {
                                mCallbacks.getBroadcastItem(i).valueChanged(value);
                            } catch (RemoteException e) {
                                // The RemoteCallbackList will take care of removing
                                // the dead object for us.
                            }
                        }
                        mCallbacks.finishBroadcast();
                        
                        // Repeat every 1 second.
                        sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);

    当运行这个示例时,如果没有事先运行Remote Service Controller 的start Service ,这个值会从1开始,每隔1秒钟加1.

    再来看看Client如何绑定Service的。

    bindService(new Intent(IRemoteService.class.getName()),
     mConnection, Context.BIND_AUTO_CREATE);
    bindService(new Intent(ISecondary.class.getName()),
     mSecondaryConnection, Context.BIND_AUTO_CREATE);

    注意这里的Context.BIND_AUTO_CREATE,这意味这如果在绑定的过程中,如果Service由于某种原因被Destroy 了,Android还会自动重新启动被绑定的Service。你可以点击Kill Process 杀死Service看看结果 :-)。

            private OnClickListener mKillListener = new OnClickListener() {
                public void onClick(View v) {
                    // To kill the process hosting our service, we need to know its
                    // PID.  Conveniently our service has a call that will return
                    // to us that information.
                    if (mSecondaryService != null) {
                        try {
                            int pid = mSecondaryService.getPid();
                            // Note that, though this API allows us to request to
                            // kill any process based on its PID, the kernel will
                            // still impose standard restrictions on which PIDs you
                            // are actually able to kill.  Typically this means only
                            // the process running your application and any additional
                            // processes created by that app as shown here; packages
                            // sharing a common UID will also be able to kill each
                            // other's processes.
                            Process.killProcess(pid);
                            mCallbackText.setText("Killed service process.");
                        } catch (RemoteException ex) {
                            // Recover gracefully from the process hosting the
                            // server dying.
                            // Just for purposes of the sample, put up a notification.
                            Toast.makeText(Binding.this,
                                    R.string.remote_call_failed,
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            };

    在RemoteService.Binding 中也定义了一个mHandler,是用来显示从Service发回的当前的Value值,Servcie发回Client的接口 IRemoteServiceCallback 同样也需要对应的AIDL定义。使用mHandler是因为valueChanged 需要更新UI。Android 系统Handler用法简介

    本例按unBind Service 会解除与Service之间的绑定,同样如果没有事先运行Remote Service Controller 的start Service,这个操作会导致Destory Service,因为”Bound” Service 在没有与之绑定的Service是自动退出,下次绑定时,Value又会从1开始计数。

    此外本例中用到了Android.os.RemoteCallbackList 这个类是为了方便管理Remote调用时向Client发送Callback而提供的,具体不详述了,可以参见http://developer.android.com/reference/android/os/RemoteCallbackList.html

  • 相关阅读:
    自定义CollectionView实现流式布局和动态图片的展现
    Java设计模式之观察者模式
    HashMap工作原理
    SpringBoot 实现多线程
    十大排序算法
    IDEA集成 plant uml 画图工具
    解决国内访问github速度慢的问题
    SpringBoot整合JWT Token
    SpringBoot在idea中配置热部署
    Spring-Security教程【一】简单的登录认证
  • 原文地址:https://www.cnblogs.com/dongdong230/p/4359012.html
Copyright © 2011-2022 走看看