使用Android Studio编写AIDL的Demo及代码分析

最近在做android的时候需要用到AIDL,在网上搜资料发现还是一头雾水,基本都重点内容是Eclipse做的。所以就自己摸索着用Android Studio写了一个小demo。

我想使用aidl在一个app中调用另一个app的方法。实现的功能很简单,类似一个欢乐斗地主的app要买豆时调用另一个app的支付功能。

  • 首先新建一个支付的module,我取名为zhifubao:在里面新建一个aidl的文件,取名为Iservice,这个时候as会自动生成一个aidl的包,里面有一个Iservice.aidl的文件,在里面加一个callPay()方法,代码如下:
    使用Android Studio编写AIDL的Demo及代码分析_第1张图片
// Iservice.aidl
package com.leehour.aidldemo;

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

interface Iservice {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    boolean callPay(String name, String pwd, int money);
}
  • 随后关键的一步来了:点击Make Project(ctrl+F9),即可在project-zhifubao-build-generated-source-aidl-debug-包名下找到新生成的Iservice.java文件,这个文件不可修改,里面有句代码:

使用Android Studio编写AIDL的Demo及代码分析_第2张图片

public static abstract class Stub extends android.os.Binder implements com.leehour.aidldemo.Iservice

意味着我们在服务里面可以用到Stub类,这里先提一下,后面看具体用法。

  • 我们再新建一个服务PayService,注意在manifest里面声明(不过如果是使用的as新建的服务,在manifest里面已经自动加上声明了),在服务里面新建我们所要暴露给其他app的方法:pay()
package com.leehour.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

public class PaySerivce extends Service {

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

    public boolean pay(String name, String pwd, int money) {
        System.out.println("name:"+name+"pwd:"+pwd+"money:"+money);
        if ("abc".equals(name) && "123".equals(pwd) && money < 5000) {
            return true;
        } else {
            return false;
        }
    }

    private class MyBinder extends Iservice.Stub {
        @Override
        public boolean callPay(String name, String pwd, int money) throws RemoteException {
            return pay(name,pwd,money);
        }
    }

}

此时我们使用到了Stub类,这样我们可以通过新建的MyBinder类继承Stub来创建MyBinder对象,在onBind方法中返回一个MyBinder对象。

  • 到这里,支付端的app就已经完成了。再新建“欢乐斗地主”的module,在layout里面加一个按钮:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="买豆"
        android:onClick="click"/>
LinearLayout>
  • 新建一个aidl文件Iservice.aidl,包名要和上面的支付app的包名一致,这里都取为:”com.leehour.aidldemo”,里面的代码也和上面的app一致:
    使用Android Studio编写AIDL的Demo及代码分析_第3张图片
// Iservice.aidl
package com.leehour.aidldemo;

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

interface Iservice {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    boolean callPay(String name, String pwd, int money);
}
  • 随后也点击Make Project(ctrl+F9),此时在debug文件夹下也会生成Iservice.java文件。
    使用Android Studio编写AIDL的Demo及代码分析_第4张图片

在mainActivity中加入如下代码:

package com.leehour.happydoudizhu;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.leehour.aidldemo.Iservice;

/**
 * Created by leehour on 2016/9/9.
 */
public class MainActivity extends Activity {

    private MyConn conn;
    private Iservice iservice;
    private Intent intentService=new Intent();
    final String BOUNDSERVICE_PACKAGE = "com.leehour.aidldemo";
    final String BOUNDSERVICE_CLASS = ".PaySerivce";

    //Bound Service Connection
    /*private ServiceConnection conn = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder boundService) {
            iservice = Iservice.Stub.asInterface(boundService);

        }

        public void onServiceDisconnected(ComponentName name) {

        }
    };*/
    private class MyConn implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            iservice = Iservice.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentService.setClassName(BOUNDSERVICE_PACKAGE,
                BOUNDSERVICE_PACKAGE + BOUNDSERVICE_CLASS);
        conn=new MyConn();
        bindService(intentService, conn, BIND_AUTO_CREATE);
    }

    public void click(View view) {
        try {
            boolean result = iservice.callPay("abc", "123", 100);
            if (result) {
                Toast.makeText(getApplicationContext(), "买豆成功", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(getApplicationContext(), "买豆失败", Toast.LENGTH_LONG).show();

            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

其中BOUNDSERVICE_PACKAGEBOUNDSERVICE_CLASS是第一个app的包名和服务的类名,这里我们使用的是MyConn直接实现的ServiceConnection接口,也可以使用上面注释里面的方法。

这样就做好了两个app,将第一个部署到模拟机上,再部署第二个,点击“买豆”按钮,就会弹出吐司“买豆成功”,大功告成!


以上展示了如何编写demo,之前对于生成的Iservice.java文件我们还没有分析,接下来对于该文件进行详细解析。

首先,来看一下生成的整个Iservice.java文件,代码如下:

    public interface Iservice extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.developmentarts.Iservice {
        private static final java.lang.String DESCRIPTOR = "com.example.developmentarts.Iservice";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.developmentarts.Iservice interface,
         * generating a proxy if needed.
         */
        public static com.example.developmentarts.Iservice asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.developmentarts.Iservice))) {
                return ((com.example.developmentarts.Iservice) iin);
            }
            return new com.example.developmentarts.Iservice.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_callPay: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    int _arg2;
                    _arg2 = data.readInt();
                    boolean _result = this.callPay(_arg0, _arg1, _arg2);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.developmentarts.Iservice {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public boolean callPay(java.lang.String name, java.lang.String pwd, int money) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    _data.writeString(pwd);
                    _data.writeInt(money);
                    mRemote.transact(Stub.TRANSACTION_callPay, _data, _reply, 0);
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_callPay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public boolean callPay(java.lang.String name, java.lang.String pwd, int money) throws android.os.RemoteException;
}

刚开始看整个java文件内心是拒绝的,里面方法很多,看起来显得杂乱无章。但是仔细分析下来发现逻辑还是很清晰的,一步步来看:

  • 首先是Stub类:
public static abstract class Stub extends android.os.Binder implements com.example.developmentarts.Iservice

该类继承自Binder并实现了Iservice接口,但未具体实现该接口的callPay()方法,而是放到子类里面去实现。本例中在PayService中实现了callPay()方法:

private class MyBinder extends Iservice.Stub {
        @Override
        public boolean callPay(String name, String pwd, int money) throws RemoteException {
            return pay(name,pwd,money);
        }
    }
  • asInterface:将一个IBinder对象转换为一个Iservice接口,如果客户端和服务端不在同一个进程则返回代理类Stub.Proxy
  • asBinder:返回当前对象
  • onTransact:该方法较为关键:
 @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_callPay: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    int _arg2;
                    _arg2 = data.readInt();
                    boolean _result = this.callPay(_arg0, _arg1, _arg2);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

首先会使用code来判断客户端请求的是什么方法,随后从data中读取客户端传入的参数,然后调用this.callPay方法,此callPay()方法会调用服务端实现的callPay()方法,从而完成方法的调用。最终得到返回值_result,将其写入_reply中。
注意到此方法会返回一个boolean类型的值,当返回false时,表示客户端请求失败。

  • Proxy:这个类实现了Iservice,前文有讲过如果客户端和服务端不在同一进程,则返回的是此代理类。里面有callPay()方法,该方法运行在客户端中。
            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public boolean callPay(java.lang.String name, java.lang.String pwd, int money) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    _data.writeString(pwd);
                    _data.writeInt(money);
                    mRemote.transact(Stub.TRANSACTION_callPay, _data, _reply, 0);
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

当客户端调用服务端的callPay()方法时,会创建一个Parcel对象_data,将参数写入_data对象中,然后调用mRemote.transact()方法,该方法会使服务端调用onTransact()方法,从而完成客户端对服务端方法的调用。
至此完成了对于生成的Iservice.java文件的分析,实际上该java文件完全可以读者自己写出来,使用Android Studio的AIDL来自动编译生成Iservice.java文件方便了我们编写跨进程程序。

工程如下:android studio aidldemo

你可能感兴趣的:(android开发)