如何在Android中利用AIDL添加service

AIDL(Android Interface Definition Language,Android接口描述语言)用于定义Client与service进行跨进程通信的编程接口。AIDL简化了IPC通信时数据交换的流程,而程序员只需关心接口的实现即可。

注意:只有你需要其他应用程序可以通过IPC接口调用service或者需要service可以处理多线程的情况时,才去使用AIDL。若非如此,(1)不需要处理跨进程的并发访问,可通过实现 Binder 接口;(2)需要使用IPC,但无需处理多线程的情况,可以选择使用 Messager

使用AIDL进行IPC通信时,需要注意:

  • 当前进程调用时,service与调用者在同一线程执行(通常这种情况,一般不使用AIDL进程IPC,而是通过使用 Binder 接口进行调用)
  • 如果是其他进程进行IPC调用(系统维护的一个当前进程的Thread Pool),这时可能同时出现多个IPC调用,因此必须确保AIDL接口是支持多线程(thread-safe)
  • oneway(异步,不会阻塞当前进程)关键字用于修改远程调用的具体行为。使用 oneway 时,远程调用不会阻塞,只是发送交易数据(transaction data),然后立即返回,而实现该接口的service最终会将其当作一个来自 Binder 线程池(Thread pool)普通的调用(regular call);如果oneway用于本地调用,则不会有任何影响,该调用始终是同步的

接下来,通过一个具体的示例说明如何用AIDL定义一个service,如何绑定该service,如何进行IPC的调用.

定义AIDL接口

.aidl接口的语法跟JAVA语言一样,其定义的方式与JAVA中的interface基本一致,主要区别是AIDL只能定义methods,不能定义数据或者static的methods。每个.aidl文件只能定义一个接口(interface)。AIDL支持以下数据类型:

  • 所有JAVA中的基本数据类型(primitive types),例如int,long,char,boolean
  • String/CharSequence
  • List
  • Map
  • android.os.parcelable 用于自定义数据类型

以下示例均基于Android Studio

在AS,右击项目,new –> ADIL,对其进行命名,可以看到在 src 目录下,生成了一个AIDL的目录,里面包含了刚才新建的.aidl文件:

    package com.jason.test.systemserviceaddingtest;

    // Declare any non-default types here with import statements

    interface IMathService {
        /**
         * get current process PID
         */
         int getPID();

         /**
         * add two value
         */
         int add(int x, int y);

         /**
         * mutiple two value
         */
         int multiply(int x, int y);
    }*

实现AIDL接口

编译时,Android SDK工具会产生一个与 .aidl 同名的 .java 接口文件,该接口文件包含了一个名为 stub abstract class,其声明了 .aidl 文件里所有的methods,同时也定义一个 asInterface 的方法,它用 IBinder(作为参数传递给调用者的 onServiceConnected() 回调函数)作为参数,并返回一个stub接口的实例。我们可以通过扩展 .Stub 类,定义一个service:

    package com.jason.test.systemserviceaddingtest.service;

    import android.os.*;
    import android.os.Process;

    import com.jason.test.systemserviceaddingtest.IMathService;

    /**
     * Created by Jason on 2016/7/11.
     */
     public class MathServiceImpl extends IMathService.Stub {
        public static final String TAG = MathServiceImpl.class.getSimpleName();


        @Override
        public int getPID() throws RemoteException {
            return Process.myPid();
        }

        @Override
        public int add(int x, int y) throws RemoteException {
            return x + y;
        }

        @Override
        public int multiply(int x, int y) throws RemoteException {
            return x * y;
        }
    }

MathServiceImpl为 service 定义了一个RPC(Remote Process Call),接下来只需要将该实例传递给调用者,即可实现与service的远程通信。

使用AIDL的注意事项

  • 调用不一定在Main Thread中执行,因此开始就要考虑如何处理多线程的情况
  • RPC调用默认支持同步。如果一个service花比较长的时间完成一个request,不要在主线程(UI thread)中调用,以免发生ANR(Application Not Responding),而是应该在另一个线程中进行调用
  • 在service中抛出的任何异常都不会返回给调用者client

将接口提供给Clients

将上述实现了的接口提供给clients,以便clients进行绑定:

    package com.jason.test.systemserviceaddingtest.service;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;

    /**
     * Created by Jason on 2016/7/11.
     */
    public class MathService extends Service{
        public static final String TAG = MathService.class.getSimpleName();

        private final MathServiceImpl mServiceBinder =
                new MathServiceImpl();

        @Override
        public void onCreate(){
            Log.v(TAG,"onCreate()");
            super.onCreate();
        }

        @Override
        public IBinder onBind(Intent intent) {

            Log.v(TAG,"onBind()");
            return mServiceBinder;
        }
    }

当一个client通过调用 bindService() 连接这个service时,client对应的onServiceConnected() 的回调函数会接收到传自该service onBind() 函数的 mBinder 的实例(如果client与service在不同的应用中,则client所属的应用必须要有之前定义的.aidl文件的拷贝)。

调用IPC接口

调用service的IPC接口前,首先需要通过 ServiceConnection 将client与service进行绑定,具体参考下列代码:首先通过mServiceConnection将client(一个新的HandlerThread)与service进行绑定;作为示例,为避免APP出现ANR,创建了一个新的线程去执行service的调用。

  package com.jason.test.systemserviceaddingtest;

    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.os.IBinder;
    import android.os.Looper;
    import android.os.Message;
    import android.os.RemoteException;
    import android.support.v4.app.Fragment;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.widget.Toast;

    import com.jason.test.systemserviceaddingtest.service.MathService;

    /**
     * A placeholder fragment containing a simple view.
     */
    public class MainActivityFragment extends Fragment {
    public static final String TAG = MainActivityFragment.class.getSimpleName();

    public static final int EVENT_START_SERVICE = 0x01;
    public static final int EVENT_STOP_SERVICE = 0x02;
    public static final int EVENT_BIND_SUCCESS = 0x03;

    private Context mContext;
    private IMathService mMathService;
    private Handler mHandler;

    private Boolean mIsServiceBound = false;

    private Button mStartBtn;
    private Button mStopBtn;


    public MainActivityFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.v(TAG,"onCreateView()");

        mContext = getContext();
        View fragment = inflater.inflate(R.layout.fragment_main, container, false);
        mStartBtn = (Button)fragment.findViewById(R.id.start_service);
        mStopBtn = (Button)fragment.findViewById(R.id.stop_service);

        mStartBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // start service thread
                mServiceThread.start();

                Message msg = mHandler.obtainMessage(EVENT_START_SERVICE);
                mHandler.sendMessage(msg);
            }
        });

        mStopBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Message msg = mHandler.obtainMessage(EVENT_STOP_SERVICE);
                mHandler.sendMessage(msg);
                // stop the service thread
                if(!mIsServiceBound) {
                    mServiceThread.quitSafely();
                }
            }
        });

        return fragment;
    }

    // 连接service后,回调函数会将IBinder传回给client
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMathService = IMathService.Stub.asInterface(service);
            Log.d(TAG,"onServiceConnected(): success");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMathService = null;
            Log.d(TAG,"onServiceDisconnected(): success");
        }
    };

    private void getMathOperations(){
        int pid = 0;
        int[] val = {203,230};
        try{
            pid = mMathService.getPID();

            val[0] = mMathService.add(val[0],val[1]);
            val[1] = mMathService.multiply(val[0],val[1]);
        }catch (RemoteException e){
            Log.e(TAG,"remote calling exception");
        }

        Toast.makeText(mContext,"pid = " + pid + " result is " + val[0] + "," + val[1],
                Toast.LENGTH_LONG).show();
    }

    // 为防止APP出现ANR,启动新的线程调用service接口
    private HandlerThread mServiceThread = new HandlerThread("ProcessInfoGettingService"){

        @Override
        public void start(){
            //super.start();

            mHandler = new Handler(Looper.myLooper()){
                @Override
                public void handleMessage(Message msg){
                    switch (msg.what){
                        case EVENT_START_SERVICE:
                            bindService();
                            break;
                        case EVENT_STOP_SERVICE:
                            unbindService();
                            break;
                        case EVENT_BIND_SUCCESS:
                            getMathOperations();
                        default:
                            break;
                    }
                }
            };
        }

        @Override
        public void run(){
            Log.d(TAG,"MathService is running");
        }
    };

    // 绑定service
    private void bindService(){
        Log.v(TAG, "bindService()");
        Intent service = new Intent(getContext(), MathService.class);
        getContext().bindService(service, mServiceConnection, Context.BIND_AUTO_CREATE);
        mIsServiceBound = true;

        Toast.makeText(getContext(),"service is bound successfully",Toast.LENGTH_LONG).show();
        // notify to calling
        Message msg = mHandler.obtainMessage(EVENT_BIND_SUCCESS, Integer.valueOf(mIsServiceBound ? 1 : 0));
        mHandler.sendMessageDelayed(msg,1*1000);
    }

    // 解绑service
    private void unbindService(){
        if(mIsServiceBound) {
            mContext.unbindService(mServiceConnection);
            mIsServiceBound = false;
            Toast.makeText(getContext(),"service is unbound successfully",Toast.LENGTH_LONG).show();
        }
    }

}

更多关于利用IPC进行数据传递的介绍请参考[2]
如何添加如 Telephony/Alarm/Window Manager 系统服务,请参考[1]

参考文献

  1. http://processors.wiki.ti.com/index.php/Android-Adding_SystemService
  2. https://developer.android.com/guide/components/aidl.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值