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