美文网首页
Android中AIDL的使用

Android中AIDL的使用

作者: 0e7f5dc37bb5 | 来源:发表于2018-05-02 16:41 被阅读13次

AIDL,即Android 接口定义语言,平时代码中几乎没用到,有点忘了,今天拿出来复习一下使用方法。

我们知道Android中要实现IPC,有好多种方式:
1.在Intent中附加extras来传递信息。
2.共享文件。
3.SharedPreferences。
4.基于Binder的AIDL。
5.基于Binder的Messenger(其实Messenger本质上也是AIDL,只不过系统做了封装以方便上层调用)。
6.Socket。
7.ContentProvider。

先讲讲怎么使用AIDL,首先创建一个aidl文件夹,接着创建aidl文件,文件名和接口名称需一致,否则会报错(AS的rename功能有坑。。找了半天才发现问题)。以IPerson接口为例,定义接口如图。

aidl文件

接着Rebuild Project,IDE会帮我们实现相关的类(app/build/generated/source/aidl文件夹内,后续再分析),实现Person类,集成IPerson.Stub:

Person实现类
public class MainActivity extends AppCompatActivity {
    private IPerson iPerson;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            iPerson = IPerson.Stub.asInterface(iBinder);
            if (iPerson != null) {
                try {
                    iPerson.setName("SJC");
                    Toast.makeText(MainActivity.this, "赋值成功!", Toast.LENGTH_LONG).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                    Toast.makeText(MainActivity.this, "赋值失败!", Toast.LENGTH_LONG).show();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };


    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        intent.setAction("forServiceAidl");
        bindService(getExplicitIntent(this, intent), serviceConnection, Service.BIND_AUTO_CREATE);

        btn = findViewById(R.id.jump);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (iPerson != null) {
                    try {
                        iPerson.setName("SJC" + Math.random());
                        Toast.makeText(MainActivity.this, "赋值成功!", Toast.LENGTH_LONG).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Toast.makeText(MainActivity.this, "赋值失败!", Toast.LENGTH_LONG).show();
                    }
                }
            }
        });

    }


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


    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }
        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);
        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }



}

MainActivity代码贴出来了,这里因为Android5.0以上不允许隐式调用Service,调用了getExplicitIntent方法,将隐式调用改为显示调用(也可以直接指定包名),记得在Manifest中注册Service。MainActivity主要作用就是绑定Service。接下来可以创建客户端ApplicationB了,将aidl文件夹拷贝到ApplicationB中,如图:


客户端
public class MainActivityB extends AppCompatActivity {
    IPerson person;
    Button btn;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
        }
        //因为有可能有多个应用同时进行RPC操作,所以同步该方法
        @Override
        public synchronized void onServiceConnected(ComponentName arg0, IBinder binder) {
            //获得IPerson接口
            person = IPerson.Stub.asInterface(binder);
        }
    };

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

        btn = findViewById(R.id.btn);
        Intent intent = new Intent();
        intent.setAction("forServiceAidl");

        bindService(getExplicitIntent(this, intent), conn, Service.BIND_AUTO_CREATE);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                if(person != null){
                    try {
                        //RPC方法调用
                        String name = person.getName();
                        Toast.makeText(MainActivityB.this, "远程进程调用成功!值为 : "+ name, Toast.LENGTH_LONG).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Toast.makeText(MainActivityB.this, "远程进程调用失败! ", Toast.LENGTH_LONG).show();
                    }
                }
            }
        });

    }


    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }
        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);
        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }

}

MainActivityB 代码也贴出来,然后就可以愉快地调用远程服务了。


会用了,接下来分析一下原理吧。
废话不多说,直接把AS帮我们生成的代码贴出来再说

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\project\\MyApplication\\app\\src\\main\\aidl\\sjc\\zailingtech\\com\\myapplication\\IPerson.aidl
 */
package sjc.zailingtech.com.myapplication;

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

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

        /**
         * Cast an IBinder object into an sjc.zailingtech.com.myapplication.IPerson interface,
         * generating a proxy if needed.
         */
        public static sjc.zailingtech.com.myapplication.IPerson asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof sjc.zailingtech.com.myapplication.IPerson))) {
                return ((sjc.zailingtech.com.myapplication.IPerson) iin);
            }
            return new sjc.zailingtech.com.myapplication.IPerson.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_setName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.setName(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements sjc.zailingtech.com.myapplication.IPerson {
            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;
            }

            @Override
            public void setName(java.lang.String name) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String getName() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void setName(java.lang.String name) throws android.os.RemoteException;

    public java.lang.String getName() throws android.os.RemoteException;
}

emmmmm......不知道说什么。。重要的地方编译器都已经给了注释了。。

因此,简单的说,Android中的Stub是一个类,实现了远程服务的接口,以便你能使用它,就好像此服务是在本地一样。好比在本地打了一个远程服务的“桩”,你就可以用来造房子什么的。"

Stub继承了android.os.Binder并实现IPerson接口,asInterface 方法用于将服务端的Binder对象转换为客户端所需要的接口对象,根据obj.queryLocalInterface(DESCRIPTOR)方法区分进程,如果是在同一个进程中调用的,就返回服务端Stub对象本身,否则就返回封装后的Stub.Proxy对象供客户端使用。
onTransact方法是运行在服务端的Binder线程中的,_data即调用接口传入的参数,_reply为调用方法得到的返回值,mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);为调用过程。当客户端发起远程请求后,在底层封装后会交由此方法来处理。通过code来区分客户端请求的方法。

先讲这么多,写bug去了zzz。。。

参考文章:
https://www.jianshu.com/p/ef86f682a8f9

https://www.jianshu.com/p/9a13f1afbc6e

相关文章

  • Android中的AIDL

    Android中的AIDL使用详解 AIDL使用解析 Android进阶之AIDL的使用详解

  • AIDL

    Android中AIDL的基本用法Android 中AIDL的使用与理解Android AIDL使用详解彻底明白A...

  • Android Aidl 的使用

    Android Aidl 的使用 Binder框架 -- android AIDL 的使用 Binder框架 – ...

  • Android AIDL 进程间通信使用笔记

    Android AIDL 进程间通信使用笔记 AIDL AIDL是Android Interface defini...

  • Android中AIDL的工作原理

    在上一篇文章中Android中AIDL的使用详解介绍了AIDL的使用流程,这篇文章我们说说AIDL的工作原理。 I...

  • Android-服务(AIDL通讯)

    本文学习目标 学会使用AIDL与远程服务进行通讯 AIDL介绍 AIDL是Android中IPC(Inter-Pr...

  • IPC相关

    Android权限之sharedUserId、签名(实例说明) Android 中AIDL的使用与理解 Binde...

  • AIDL解析

    AIDL是Android中IPC方式中的一种。 AIDL的使用 创建aidl后缀的文件,服务端和客户端各保存一份,...

  • Android AIDL

    AIDL原理是什么?如何优化多模块都使用AIDL的情况? AIDL(Android Interface Defin...

  • AIDL的基本使用

    XSize的主页 AIDL的使用 AIDL是android中接口定义语言,用于进程间通信.这里大家其实应该好好理解...

网友评论

      本文标题:Android中AIDL的使用

      本文链接:https://www.haomeiwen.com/subject/zngqrftx.html