Android AIDL实现跨进程Activity与Service的通信

一、什么是AIDL?

AIDL:Android Interface Definition Language 用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication,IPC)的代码。

官方AIDL定义:

On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.

大概意思就是说Android进程之间不能直接通信,需要把对象转换成计算机能识别的原始语言,然后安排它跨越进程边界。但是做这些事很繁琐,于是Android提供了AIDL来做这件事。(换句话就是要实现跨进程需要编写很多复杂的代码,于是android提供了AIDL,通过编写简单的AIDL文件,编译器根据AIDL的规则生成那些复杂的代码)

二、为什么要有AIDL?

每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用进程的服务,而且经常需要在不同的进程间传递数据对象。

在Android平台,一个进程不能直接访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的单元,并且有序的跨越进程边界。

三、AIDL的使用?

使用AIDL跨进程通信,整体过程和单进程一样,都是通过一个Binder来通信的,区别在于单进程的Binder是自己通过继承Binder类来手动实现的,而跨进程的Binder是通过AIDL自动生成的。

首先①新建一个AIDL文件

右键 -> new -> AIDL -> AIDL File,然后输入文件名点击finish完成(这里的示例代码是IMyAidlInterface)

上面的操作不管右键哪个目录,完成之后都会在src/main目录下生成了一个aidl目录,新建的IMyAidlInterface.aidl文件就在这个目录下,注意和eclipse的不同。

打开这个文件发现就是一个接口(可能会默认生成一个basicTypes方法,这是示例方法,不用管,可以删掉),然后在里面定义一个自己的方法(需要其他的方法的话自己看着加)

// IMyAidlInterface.aidl
package com.example.wcystart.wcystart;

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

interface IMyAidlInterface {
//    /**
//     * 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);
void testMethod(String str);
}

②编译项目

Build -->Make Project

完成之后会在 app/build/generated/source/aidl/debug/ 目录下生成一个和AIDL文件同名的java文件 IMyAidlInterface.java

这个类文件就是用来提供进程间通信的,需要的Binder类就在这里面。

简单来说,AIDL就是一个用来生成代码的工具,最终的目的就是得到IMyAidlInterface.java这个类。这个和数据库框架GreenDao很像,都是通过一些简单的做法生成很多复杂而有用的代码,然后拿来直接用。

③分析IMyAidlInterface.java

生成的代码格式很乱,为了方便查看,ctrl+alt+L格式化一下。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\WcyStart\\app\\src\\main\\aidl\\com\\example\\wcystart\\wcystart\\IMyAidlInterface.aidl
 */
package com.example.wcystart.wcystart;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {
    //内部抽象类

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

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

        /**
         * Cast an IBinder object into an com.example.wcystart.wcystart.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.example.wcystart.wcystart.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.wcystart.wcystart.IMyAidlInterface))) {
                return ((com.example.wcystart.wcystart.IMyAidlInterface) iin);
            }
            return new com.example.wcystart.wcystart.IMyAidlInterface.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_testMethod: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.testMethod(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.wcystart.wcystart.IMyAidlInterface {
            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.
//     */
//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
//            double aDouble, String aString);

            @Override
            public void testMethod(java.lang.String str) 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(str);
                    mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_testMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
//    /**
//     * 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);

    public void testMethod(java.lang.String str) throws android.os.RemoteException;
}

IMyAidlInterface.java里面是一个接口,接口里面有一个内部抽象类和一个方法。这个方法就是我们在aidl文件里定义的那个方法。内部抽象类就是我们要的Binder类,类名Stub。到这里不难想象接下来的工作:(1)Service里面new一个Stub实例并在onBinder里面返回这个Stub(或者说Binder)的实例 。(2)Activity里面绑定Service的时候取到这个Binder(强转成Stub类型)。(3)调用这个Binder里面的testMethod方法实现Activity和Service的通信。大的思路是这样,不过细节上还是有很多不同的。

IMyAidlInterface.java里面的其他代码(主要是一些方法)暂时不用看,用到的时候会说。到这里只需要知道这个java文件里面有一个Stub类,有一个自定义的方法。

④修改Service代码

到这AIDL相关的代码已经完成,接下来就是使用AIDL为我们生成的代码。首先修改MyService只需把MyBinder替换成Stub,但是Stub是个抽象类,需要我们自己实现,那么新建一个继承自Stub的类,类名随意,这里取名AidlBinder,然后仿照同进程下的MyBinder实现testMethod()方法,代码如下:

package com.example.wcystart.wcystart;

import android.os.RemoteException;

/**
 * Created by ${wcystart}
 * date:on 2019/1/10
 * description:自定义Binder类继承自Stub抽象类
 */

public class AidlBinder extends IMyAidlInterface.Stub {
    private MyService mService;

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMothod(str);

    }
}

上面代码中没有回调相关的代码,因为跨进程的回调和同进程下是不一样的,后面会说到。另外,这里为了方便讲解,专门定义了AidlBinder类作为Stub 的实现类,另一种在Service里面直接使用匿名内部类的方式实现Stub 也是很常见的。

然后MyService里面使用AidlBinder即可,代码如下:

package com.example.wcystart.wcystart;

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

public class MyService extends Service {
    private static final String TAG="wcy";
    //实例化一个AidlBinder对象
    //private MyBinder myBinder=new MyBinder(this);
    AidlBinder mAidlBinder=new AidlBinder(this);

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mAidlBinder;
    }
    public void serviceMothod(String str){
        Log.i(TAG,"receive msg from activity:"+str);
    }
}

和同进程基本一样。

总结一下:到这里为止,除去理论的分析之外,实际的操作只有两步:(1)新建一个AIDL文件用来生成一些代码。(2)实现抽象类Stub,实现类是AidlBinder。(3)Service里面使用Stub的实现类AidlBinder替换原来的MyBinder。

⑤修改Activity代码

Actviity如何使用传过来的Binder呢?

AIDL生成的代码中提供了一个静态方法asInterface(IBinder),可以将IBinder转换成Aidl接口,所以可以这样做:IMyAidlInterface mService = IMyAidlInterface.Stub.asInterface(service);

asInterface()用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。Binder是BinderProxy类型的不能转换成Stub类型(因为Stub不是BinderProxy的子类而是Binder的子类)

所以同进程下,Activity有以下3种方式使用Service传过来的Binder:

  • IMyAidlInterface mService = IMyAidlInterface.Stub.asInterface(service);

  • IMyAidlInterface.Stub mBinder = (IMyAidlInterface.Stub)service;

  • IMyAidlInterface.Stub mService = (IMyAidlInterface.Stub)IMyAidlInterface.Stub.asInterface(service)

而跨进程只能使用一种方式,见Activity代码:

package com.example.wcystart.wcystart;

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

public class FirstActivity extends Activity implements View.OnClickListener {
    private static String TAG = "FirstActivity";
    private Button mBtnMain;
    public IMyAidlInterface mAidlService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mAidlService=IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnMain = findViewById(R.id.btn_main);
        //跳转到Service
        Intent intent=new Intent(this,MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);
        mBtnMain.setOnClickListener(this);
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        //当Activity销毁的时候解服务
        if(mConnection!=null){
            unbindService(mConnection);
            mConnection=null;
        }
    }
 
    @Override
    public void onClick(View v) {
        try {
            mAidlService.testMethod("hi,service.");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

   
}

打印Log信息

01-10 17:03:47.719 18038-18038/com.example.wcystart.wcystart I/wcy: receive msg from activity:hi,service.

至此,实现了跨进程Activity给Service发送消息。

⑥跨进程回调接口的实现,实现Service向Activity发消息。

首先,回调接口需要定义成aidl接口而不是普通接口,所以新建一个IMyCallbackListener.aidl文件,里面定义一个onRespond方法作为回调函数。

File ->new ->AIDL

// IMyCallbackListener.aidl
package com.example.wcystart.wcystart;

interface IMyCallbackListener {
   void onResponse(String str);
}

扩展IMyAidlInterface.aidl,里面定义一个注册回调监听的方法.

// IMyAidlInterface.aidl
package com.example.wcystart.wcystart;
import com.example.wcystart.wcystart.IMyCallbackListener;

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

interface IMyAidlInterface {
void testMethod(String str);
  //定义一个注册回调监听的方法
 void  registerListener(IMyCallbackListener listener);
}

这里注意需要手动导包IMyCallbackListener,aidl的语法规则,非系统的类即使在同一个包下也要import,比如上面代码的IMyCallbackListener,而系统的类String就不用import。如果不手动导包的话,就会报一个错:

这时编译会提示AidlBinder实现父类的抽象方法registerListener(),仿照同进程下的MyBinder里面的回调相关的代码,修改AidlBinder如下:

public class AidlBinder extends IMyAidlInterface.Stub{
    private MyService mService;
    private IMyCallbackListener mListener;

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMothod(str);
        mListener.onResponse("service response activity:"+"hello activity");
    }

    @Override
    public void registerListener(IMyCallbackListener listener) throws RemoteException {
        mListener=listener;
    }
}

然后Activity里面在合适的地方注册回调,用来接收服务端的消息:

package com.example.wcystart.wcystart;

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

public class FirstActivity extends Activity implements View.OnClickListener {
    private static String TAG = "FirstActivity";
    private Button mBtnMain;
   //private MyBinder myBinder;
    public IMyAidlInterface mAidlService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, final IBinder service) {
            mAidlService=IMyAidlInterface.Stub.asInterface(service);
            try {
                mAidlService.registerListener(new IMyCallbackListener.Stub() {
                    @Override
                    public void onResponse(String str) throws RemoteException {
                     Log.i(TAG,"receive message from service:"+str);
                    }

                    @Override
                    public IBinder asBinder() {
                        return service;
                    }
                });
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnMain = findViewById(R.id.btn_main);

        //跳转到Service
        Intent intent=new Intent(this,MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);

        mBtnMain.setOnClickListener(this);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //当Activity销毁的时候解服务
        if(mConnection!=null){
            unbindService(mConnection);
            mConnection=null;
        }
        Log.i(TAG, "执行了FirstActivity的onDestroy()");
    }

   

    @Override
    public void onClick(View v) {
        try {
            mAidlService.testMethod("hi,service.");
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }
}

运行日志打印:

01-11 10:11:26.393 23902-23902/com.example.wcystart.wcystart I/wcy: receive msg from activity:hi,service.
01-11 10:11:26.393 23902-23902/com.example.wcystart.wcystart I/FirstActivity: receive message from service:service response activity:hello activity

至此,跨进程下Activity与Service的双向通信就完成了。

使用adb命令查看下当前应用的进程信息。

可以使用adb shell ps | grep "本应用的包名"命令查看进程信息,会看到如下两个进程:

这个命令会报错,我也不知道是什么原因,知道的大佬麻烦告诉我喔~~系,先谢谢啦~

请教参考文章的大佬给我的回复:

这个命令可以拆分成两个命令
adb shell 和 ps | grep "xxx"
adb shell是进入手机的shell,之后就可以运行手机上带的一些命令了。android手机是基于linux内核的,所以手机上会带一些linux命令,比如常见的ls,cd等。

ps是linux中查看进程的命令,单独执行ps会列出设备上的所有进程。
如果你的是linux系统,执行命令ps,就会列出电脑上的所有进程;执行adb shell ps(或先执行adb shell再执行ps),会列出手机上的所有进程。

grep也是linux的一个命令,起一个过滤的作用,用法例如:ps | grep "abc" 只会列出结果带abc字符串的进程

看你的问题是你的那部手机上没有grep命令,这也是正常的,不同手机厂商可能会在手机上去掉一些linux的指令,比如linux获取root权限的命令su,一般手机都会去掉的。

所以你要查看手机的进程,可以用adb shell ps命令列出所有进程,然后手动去找。
AndroidStudio也可以间接的看进程的,比如看log那个窗口,可以选择看哪个进程的log,有哪些进程可选就说明当前就有哪些进程

接下来看下跨进程解注册回调

Service回应Activity消息通过注册回调接口实现的,那怎么实现解注册呢,和同进程的解注册不同,多进程需要借助RemoteCallbackList来完成,所以注册回调的相关方法也要改一下,改成使用RemoteCallbackList来注册回调,AidlBinder代码修改如下:

package com.example.wcystart.wcystart;

import android.os.RemoteCallbackList;
import android.os.RemoteException;

/**
 * Created by ${wcystart}
 * date:on 2019/1/10
 * description:自定义Binder类继承自Stub抽象类
 */

public class AidlBinder extends IMyAidlInterface.Stub {
    private MyService mService;
    // private IMyCallbackListener mListener;
    private RemoteCallbackList<IMyCallbackListener> mListener=new RemoteCallbackList<>();

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMothod(str);
        // mListener.onResponse("service response activity:"+"hello activity");
        //调用mListener里面所有已注册的监听
        int count = mListener.beginBroadcast();
        for (int i = 0; i < count; i++) {
            mListener.getBroadcastItem(i).onResponse("service response activity:"+"hello activity");
        }
        mListener.finishBroadcast();
    }

    @Override
    public void registerListener(IMyCallbackListener listener) throws RemoteException {
       // mListener = listener;
        mListener.register(listener);
    }

    @Override
    public void unregisterListener(IMyCallbackListener listener) throws RemoteException {
        mListener.unregister(listener);
    }

}

最后在Activity里边添加个按钮,当点击按钮的时候,调用远程的解注册方法。

package com.example.wcystart.wcystart;

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

public class FirstActivity extends Activity implements View.OnClickListener {
    private static String TAG = "FirstActivity";
    private Button mBtnRegister;
    private Button mBtnUnRegister;
    //private MyBinder myBinder;
    public IMyAidlInterface mAidlService;

    private IMyCallbackListener.Stub mListener = new IMyCallbackListener.Stub() {
        @Override
        public void onResponse(String str) throws RemoteException {
            Log.i(TAG, "receive message from service" + str);
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, final IBinder service) {
            mAidlService = IMyAidlInterface.Stub.asInterface(service);
            try {
                //注册回调
                mAidlService.registerListener(mListener);
                Log.i(TAG,"注册回调调用了");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnRegister = findViewById(R.id.btn_register);
        mBtnUnRegister = findViewById(R.id.btn_unregister);

        //跳转到Service
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        mBtnRegister.setOnClickListener(this);
        mBtnUnRegister.setOnClickListener(this);
      
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_register: //注册
                try {
                    mAidlService.testMethod("hi,service.");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.btn_unregister://解注册
                try {
                    //解注册回调
                    mAidlService.unregisterListener(mListener);
                    Log.i(TAG,"解注册回调调用了");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }

    }

}

看下log日志:

I/FirstActivity: 注册回调调用了
I/wcy: receive msg from activity:hi,service.
I/FirstActivity: receive message from service service response activity:hello activity
I/FirstActivity: 解注册回调调用了

整个代码最终的功能是:启动Activity的时候绑定Service并注册一个回调,点击register按钮后Activity向Service发送消息"hi, service",然后Service收到消息后log打印 "receive message from activity: hi, service",并恢复一个消息 " service response activity:hello activity",Activity收到消息后log打印 "receive message from service: hi, activity"。然后点击unregisterListener按钮解注册回调监听,再点击 send message 后就只打印log "receive message from activity: hi, service",说明解注册成功。

这里总结一下:同进程下自定义MyBinder可以轻松实现Activity与Service通信,跨进程的话需要使用AIDL生成可以跨进程的Binder。至于Activity与Service里面的代码,流程套路基本相同,不相同的只是一些很简单的细节。

通过代码可以看到,Activity和Service之间是通过一个binder对象来通信的。

所有的执行代码都在文章中,如果你在执行的过程中有行不通的地方,还请将问题发到评论区,我一定会帮你解决的~~~

参考文章:关于Android Service的全面解析

猜你喜欢

转载自blog.csdn.net/qq_37982823/article/details/86238019