Android日记之AIDL的基本使用

作者: 居居居居居居x | 来源:发表于2019-11-13 18:17 被阅读0次

    前言

    AIDL其实是属于IPC方面的东西,而Android的IPC是一个比较大的内容,主要涉及到Android的跨进程通信的内容,此篇文章会先介绍一下Android的IPC。

    Android IPC简介

    IPC是Inter-Process Communication的缩写,意思就是进程间通信或者跨进程的通信,就是两个进程之间交换数据的过程。说到进程,就不得不说到进程和线程这两个东西,这两个是完全不一样的概念,简单描述就是,一个进程可以包含多个线程,也就是一对多的关系。进程一般指一个执行单元,线程是CPU调度的最小单元。在Android里面,进程里可以只有一个线程,即主线程,在Android里面也可以叫作UI线程,一般在这个线程里面都是进行UI更新的操作,而耗时的操作都是交给子线程去进行,这样可以防止Android的ANR状态。

    Android的多进程模式

    再讲IPC之前我们先来讲讲Android的多进程模式,在Android中多进程是值一个应用中存在多个进程的情况,所以这里暂时不讨论两个应用之间的情况。在Android使用多进程的只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process属性。参数为你要自定义的包名,如果没有指定进程的话,那么就会运行在默认的进程中。

    <activity android:name=".MainActivity"
        android:process=":remote">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

    但是,看起来使用多进程模式很简单,但其实是会造成一些问题的,比如:

    • 静态成员和单例模式完全失效。
    • 线程同步机制完全失效。
    • SharedPreferences的可靠性下降。
    • Application会多次创建。

    虽然多进程会带来很多问题,但是不能因为就这样而不去正视它们,为了解决这个问题,系统提供了很多跨进程通信的方法,虽说不能直接的共享内存,但是通过跨进程通信我们还是可以实现数据的交互,实现跨进程的通信方式有很多种方法,我们后续会进行讲解。

    AIDL的使用

    刚刚也说过了,跨进程的通信方式有很多,比如通过BInder来进行跨进程通信,还有ContentProvider,它天生就是支持跨进程访问的,此外也可以使用Bundle,Messager等等方式,还有一种方式就是AIDL,AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码。


    Android的多进程方式如图,侵删

    而刚刚说的像Messager之类的底层其实就是AIDL,只是Messager做了一些封装。还有一个就是Binder,它也是Android里跨进程通信里的一种机制,而AIDL是对Binder的封装。因为要实现IBinder来支持远程调用,应从Binder类派生一个类。而使用AIDL,它会自动生成Binder的派生类,可以直接用,那么怎么用AIDL进行跨进程通信呢

    我们这里举一个例子,创建一个App,我们称之为服务端,然后先创建一个一个AIDL文件,然后自定义一个方法,创建后记住要build一下,build之后AIDL就会快速生成对应的java文件了。

    interface IAppServiceRemoteBinder {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
        
        
        //定义一个set方法
        void setData(String data);
    }
    
    AIDL文件创建如图

    这里说明一下,AIDL支持的数据类型有以下这些:

    • 基本数据类型(int、long、char、boolean、double)
    • String和CharSequence
    • List和Map集合
    • 集合内元素必须是AIDL支持的数据类型
    • 服务端具体使用的集合必须是ArrayList和HashMap
    • Parcelable:实现了Parcelable接口的对象
    • AIDL本身接口也可以在AIDL文件使用。

    接着我们创建一个服务,因为其实AIDL就是基于Binder,所以我们就直接在onBind()里面进行返回。

    public class AppService extends Service {
    
        private String data = "默认数据";
        private boolean running = false;
    
        public AppService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
    
    
            //我们这里直接返回这个接口
            return new IAppServiceRemoteBinder.Stub() {
                @Override
                public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
                }
    
                @Override
                public void setData(String data) throws RemoteException {
                    //客户端传过来的值赋值给data
                    AppService.this.data = data;
                }
            };
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d("TAG", "service started");
            //创建一个测试线程,把客户端传过来的数据进行循环输出。
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    running = true;
                    while (running) {
                        try {
                            Thread.sleep(1000);
                            Log.d("TAG", data);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                    }
                }
            }.start();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d("TAG", "service onDestroy");
            running = false;
        }
        
    }
    

    接着我们创建一个客户端,来远程启动这个服务,首先我们要把之前创建的那个AIDL文件给复制过来,这里需要注意的是,包名的路径是要相同的,然后记住要build,然后设置一下布局。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/btn_start_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 启动外部服务" />
    
        <Button
            android:id="@+id/btn_stop_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 停止外部服务" />
    
    
        <Button
            android:id="@+id/btn_bind_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 绑定外部服务" />
    
        <Button
            android:id="@+id/btn_unbind_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 解除外部服务" />
    
        <Button
            android:id="@+id/btn_edit_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 发送值给服务端" />
    
    </LinearLayout>
    

    然后在MainActivity里输入以下代码。

    //要实现ServiceConnection方法
    public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
    
    
        private Button btnStart;
        private Button btnStop;
        private Button btnBind;
        private Button btnUnBind;
        private Button btnEdit;
        private Intent serviceIntent;
        private IAppServiceRemoteBinder binder = null;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            serviceIntent = new Intent();
            //这里设置intent的包名,第一个参数为要启动app的包名,第二个为服务包名
            serviceIntent.setComponent(new ComponentName("com.ju.startservicefromanotherapp", "com.ju.startservicefromanotherapp.AppService"));
    
            btnStart = findViewById(R.id.btn_start_service);
            btnStart.setOnClickListener(this);
            btnStop = findViewById(R.id.btn_stop_service);
            btnStop.setOnClickListener(this);
            btnBind = findViewById(R.id.btn_bind_service);
            btnBind.setOnClickListener(this);
            btnUnBind = findViewById(R.id.btn_unbind_service);
            btnUnBind.setOnClickListener(this);
            btnEdit = findViewById(R.id.btn_edit_service);
            btnEdit.setOnClickListener(this);
    
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_start_service:
                    //远程启动服务
                    startService(serviceIntent);
                    break;
                case R.id.btn_stop_service:
                    //远程开始服务
                    stopService(serviceIntent);
                    break;
                case R.id.btn_bind_service:
                    //远程绑定服务
                    bindService(serviceIntent, this, Context.BIND_AUTO_CREATE);
                    break;
                case R.id.btn_unbind_service:
                    //远程解绑服务
                    unbindService(this);
                    binder = null;
                    break;
                case R.id.btn_edit_service:
                    //这里给服务端传值设置
                    if (binder != null) {
                        try {
                            binder.setData("hello");
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                default:
            }
        }
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("TAG", "Bind Service");
            Log.d("TAG", "IBinder:" + service);
            //这里进行实例化接口,不能强转。
            binder = IAppServiceRemoteBinder.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
        }
    
    
    }
    

    这里我们通过IAppServiceRemoteBinder.Stub.asInterface()方法来获取到Service里的Binder,然后就可以直接调用Binder里的setData()方法了。最后把这启动这个App,点击绑定外部服务按钮,服务就会运行起来了,然后点击发送给值客户端的按钮,就会远程发送一个值给服务端进行接收了。

    服务运行起来如图
    发送服务端给值效果如图
    如果说传输的是一个Object对象的话,就需要对Object进行序列化操作,Java的序列化是继承Serialzable接口,而Android也有一个自己的序列化,就是继承Parcelable接口。

    参考

    Android中AIDL的理解和使用
    [任玉刚] Android开发艺术探索

    相关文章

      网友评论

        本文标题:Android日记之AIDL的基本使用

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