Android进阶——使用远程服务AIDL实现进程间带远程回调接口较复杂通信小结(二)

文章大纲

  • 引言
  • 一、远程回调AIDL接口的应用
    • 1、封装基本的父类和一些工具类
    • 2、 创建服务端的AIDL
      • 2.1、定义回调AIDL接口
      • 2.2、定义业务AIDL接口
    • 3、实现服务端对应AIDL的带有回调功能的Service
    • 4、客户端调用服务端带有回调接口AIDL
  • 二、自定义 Parcelable类型AIDL的应用
    • 1、定义一个用于引入自定义Parcelable 类型的AIDL
    • 2、在Java层定义一个实现了Parcelable接口的JavaBean
    • 3、定义使用自定义Parcelable 类型的AIDL的AIDL
    • 4、使用自定义Parcelable 类型的AIDL的Service实现
    • 5、在服务端清单文件注册对应的Service
    • 6、客户端调用服务端自定义Parcelable的AIDL
    • 小结

引言

前一篇Android进阶——使用远程服务AIDL实现进程间简单通信小结(一)简单介绍了AIDL的由来和一些基本的语法知识,结合例子相信对于入门AIDL应该没有什么问题了,前篇讲到AIDL应用主要可以分为三大场景:普通AIDL、带有远程回调接口的AIDL和需要引用自定义Parcelable的AIDL,由于篇幅问题仅仅完成了第一个最简单的场景,这篇就完成剩下的两个场景的应用。

一、远程回调AIDL接口的应用

Android进阶——使用远程服务AIDL实现进程间带远程回调接口较复杂通信小结(二)_第1张图片

1、封装基本的父类和一些工具类

package com.crazymo.remoteserver.base;

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

/**
 * Auther: Crazy.Mo on 2018/5/7 13:09
 * Summary:所有远程服务必须继承的父类
 */
public abstract class AbstractService extends Service {
    protected IBinder mBinder;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        if(mBinder==null){
            mBinder=initBinder();
        }
        return mBinder;//与客户端成功连接上的时候返回给客户端使用的对象
    }

    protected abstract IBinder initBinder();
}

在Android5.1之后,仅仅通过setAction无法匿名启动服务了而且还会引发异常,需要做处理。

package com.crazymo.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.Log;
import java.util.List;

/**
 * Auther: Crazy.Mo on 2018/5/3 9:35
 * Summary:这是为了处理高版本下仅仅通过action 匿名启动服务引发的异常
 */
public class AIDLUtil {

    /**
     *
     * @param pContext
     * @param pConnection 实现ServiceConnection接口的类
     * @param action 要启动服务的action
     * @return
     */
    public static boolean bindAIDLService(Context pContext, ServiceConnection pConnection, String action){
        boolean isBind=false;
        if(pContext!=null && action!=null && pConnection!=null) {
            try {
                Intent intent = new Intent(getExplicitIntent(pContext, new Intent().setAction(action)));
                isBind = pContext.bindService(intent, pConnection, Context.BIND_AUTO_CREATE);
            }catch (Exception e){
                Log.e("AIDL",e.getMessage());
            }
        }
        return isBind;
    }

    private 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;
    }
}

2、 创建服务端的AIDL

和普通Java 回调接口语法不同,需要单独使用一个AIDL文件来定义回调接口。

2.1、定义回调AIDL接口

// IRemoteCallback.aidl
package com.crazymo.remoteserver.callback;

interface IRemoteCallback {
    void afterSpeak(String msg);
}

2.2、定义业务AIDL接口

和不需要回调的接口的AIDL不同,这里必须要定义注册和反注册回调的方法,同时通过import 引入对应的aidl回调

// IRemoteCallbackApi.aidl
package com.crazymo.remoteserver;
//引入回调接口
import com.crazymo.remoteserver.callback.IRemoteCallback;

interface IRemoteCallbackApi {
      void speak(String msg);
      void registerListener(IRemoteCallback callBack);//注册
      void unRegisterListener(IRemoteCallback callBack);//销毁
}

3、实现服务端对应AIDL的带有回调功能的Service

触发回调,需要借助**RemoteCallbackList(一些接口集合用于执行列表中对象的回调函数,主要用于为服务端和客户端通信)**来实现,所谓注册就是将回调接口添加到RemoteCallbackList里,销毁则是从RemoteCallbackList里移除,至于用于在回调的AIDL里做什么不应该由服务提供者来确定,所以无须实现具体业务逻辑。

  • 继承Service,重写onBind方法
  • 继承对应的Stub类,实现AIDL中定义的方法的具体的逻辑
  • onBind方法中返回我们自定义的Stub子类Binder对象
  • 实现AIDL的注册及反注册方法
package com.crazymo.remoteserver.service;

import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.callback.IRemoteCallback;

/**
 * Auther: Crazy.Mo on 2018/5/7 17:27
 * Summary:
 */
public class RemoteCallApiService extends AbstractService {
    private final static String TAG="RemoteService";
    /**
     * 和普通AIDL的Service实现不同,添加的代码中比较关键的有两点:注册和反注册远程回调接口
     * 即将回调对象元素加入或移 除mRemoteCallbackList这个对象中的一个集合,执行回调,将回调方法放在需要执行的地方
     **/
    private static RemoteCallbackList<IRemoteCallback> mRemoteCallbackList = new RemoteCallbackList<>();

    @Override
    protected IBinder initBinder() {
        if(mBinder==null){
            mBinder=new RemoteCallBinder();
        }
        return mBinder;
    }

    private static final class RemoteCallBinder extends IRemoteCallbackApi.Stub{

        @Override
        public void speak(String msg) throws RemoteException {
            Log.e(TAG,"【服务端】 线程"+Thread.currentThread().getName()+"接到客户端的字符串:"+msg);
            try {
                Thread.sleep(100);
                int count = 0;
                Log.e(TAG, "mRemoteCallbackList: " +     mRemoteCallbackList+",mRemoteCallbackList.mCallBack:"+mRemoteCallbackList);
                /**
                 *准备开始调用当前注册的回调,这将创建一个回调列表的副本,你可以从使用getBroadcastItem检索条目,而且
                 * 一次只能激活一个广播,所以你必须确保总是从同一个线程调用这个或者是自己做同步。完成后必须调用finishBroadcast。
                 * 返回值:callbacks的大小;如果 mBroadcastCount > 0 说明之前调用过beginBroadcast(),则会抛出异常;
                 **/
                count = mRemoteCallbackList.beginBroadcast();
                if (count == 0) {
                    return;
                }
                try {
                    for (int i = 0; i < count; i++) {
                        Log.e(TAG,"【服务端】 线程"+Thread.currentThread().getName()+"触发回调方法");
                        mRemoteCallbackList.getBroadcastItem(i).afterSpeak("今天晴,23℃");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                } finally {
                    mRemoteCallbackList.finishBroadcast();//必须执行否则有可能会出现状态不合法异常,mBroadcastCount <0 说明之前掉用过finishBroadcast() 则会抛出异常。
                }
            } catch (InterruptedException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void registerListener(IRemoteCallback callBack) throws RemoteException {
            if (mRemoteCallbackList == null) {
                return;
            }
            mRemoteCallbackList.register(callBack);//注册回调添加到mRemoteCallbackList 里
        }

        @Override
        public void unRegisterListener(IRemoteCallback callBack) throws RemoteException {
            if(mRemoteCallbackList!=null) {
                mRemoteCallbackList.unregister(callBack);
            }
        }
    }
}

  • 清单中声明自定义的服务,指定相应的Action和进程
 <service android:name=".service.RemoteCallApiService"
                 android:process=":remotecall">
            <intent-filter>
                <action android:name="com.crazymo.aidl.callback"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

4、客户端调用服务端带有回调接口AIDL

无论是本地服务还是远程服务,本质上都是Service机制,按照匿名启动Service的步骤来就可以了,所以AIDL的是使用步骤都是:先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法

  • 引入服务端的AIDL即把服务端AIDL全部文件复制到客户端的aidl目录下
  • 声明远程服务对象实例(类型名为AIDL的名称)
  • 实现并创建远程回调接口对象
  • 声明ServiceConnection对象实例,最好通过继承ServiceConnection实现具体的子类的形式(不要通过匿名内部类的形式创建,因为取消绑定unbindService(ServiceConnection conn)也需要传入ServiceConnection)
  • 通过上下文的bindService(@RequiresPermission Intent service,@NonNull ServiceConnection conn, @BindServiceFlags int flags)匿名启动远程服务
  • ServiceConnection的onServiceConnected完成远程服务对象实例的初始化及对应远程回调接口的注册
  • ServiceConnection的onServiceDisconnected完成远程服务对象实例的销毁及对应远程回调接口的反注册或者重连
  • 通过远程服务对象实例调用远程接口
package com.crazymo.client;

import android.app.Activity;
import android.content.ComponentName;
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.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;

import java.util.List;

//为了节约篇幅,我没有做重连处理,也省掉了一些逻辑,实际应用中为了健壮性应该根据具体情况参照前一篇普通AIDL应用的例子进行完善
public class MainActivity extends Activity {
    private final static String TAG="RemoteService";
    private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
    private boolean isUnbindRemoteApiConn=false;
    private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是进程通信都需要实现,因为系统是在这个ServiceConnecttion类的相关方法通过回调返回Ibinder对象的
    private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();

    private IRemoteCallbackApi mRemoteCallbackApi;

	//实现远程回调接口
    private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {

        @Override
        public void afterSpeak(String msg) throws RemoteException {
            Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"远程回调返回的信息:"+msg);
        }
    };

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

    public void connCallbackAIDL(View view) {
        boolean isBind;
        isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
        Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程带有回调的远程接口(成功true,失败false):  "+isBind);
    }

    public void testCallbackAIDL(View view) {
        if(mRemoteCallbackApi!=null){
            try {
                Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"调用带有回调的远程接口,发送:今天天气怎么样");
                mRemoteCallbackApi.speak("今天天气怎么样");
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }
    }

    private final class  RemoteCallbackApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
            try {
                mRemoteCallbackApi.registerListener(mIRemoteCallback);
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(mRemoteCallbackApi!=null){
                try {
                    mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
                } catch (RemoteException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }
}

这里写图片描述

二、自定义 Parcelable类型AIDL的应用

Android进阶——使用远程服务AIDL实现进程间带远程回调接口较复杂通信小结(二)_第2张图片
Android 中进程之间的通信最终都是基于Binder机制的,而AIDL是Binder机制中的一部分,进程之间要想通过AIDL 进行数据传输,发送进程方就必须先把数据进行序列化操作,Android 中序列化没有采用Java 传统的Serializer 方案,而是使用了一种性能更高的独有方案Parcelable(因为传统的Serializer方案的序列化和反序列化操作都是由JVM去完成的,对于开发者来说是黑盒的,在做反序列化时需要去遍历根据字节等判断边界,而Parcelable 则是直接提供序列化和反序列化接口方法,这些接口直接通过JNI 调用本地方法存储到Parcel中,最终存储到Native层),总之AIDL传输非基本类型及集合类型数据时都需要通过Parcelable类型

1、定义一个用于引入自定义Parcelable 类型的AIDL

为了引入实现了Parcelable 的JavaBean到AIDL层,得先建立一个和JavaBean 同名的AIDL文件(一对一的关系),如果需要引入多个JavaBean就得建立多个对应的AIDL文件,这些没有Interface的AIDL文件在编译时就不会生成对应的Java文件,仅仅是起描述Parcelable 类型的作用(不能定义Interface,有多少个Parcelable类型就需要有多少个这种类型的描述AIDL文件)。而且必须要先建立AIDL文件,再去Java 同一包名下建立同名的JavanBean

// User.aidl
package com.crazymo.remoteserver.bean;

//为了引入JavaBean到AIDL层,得先建立一个和JavaBean 同名的aidl接口(一对一的关系),如果需要引入多个JavaBean就得建立对应的aidl接口
//这个文件的作用是引入了一个序列化对象 User 供其他的AIDL文件使用
//注意:User.aidl与User.java的包名应当是一样的
parcelable User;

2、在Java层定义一个实现了Parcelable接口的JavaBean

package com.crazymo.remoteserver.bean;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable {
    private String mName;
    private int mAge;

    public User() {
    }

    public void setName(String pName) {
        mName = pName;
    }

    public void setAge(int pAge) {
        mAge = pAge;
    }

    protected User(Parcel in) {
        mName = in.readString();
        mAge = in.readInt();
    }
	/**
	* 这里new User 对象和下面writeToParcel方法的对象不在同一个进程内,不是同一个对象实例,
	* 这里是接收进程的User对象实例
	*/
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
	
	/**
	* 序列化时,写的顺序要和下面读的顺序一致
	*/
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mName);
        dest.writeInt(mAge);
    }

    public void readFromParcel(Parcel dest) {
        mName = dest.readString();
        mAge = dest.readInt();
    }

    @Override
    public String toString() {
        return "姓名:" + mName + "    " + "年龄:" + mAge;
    }
}

3、定义使用自定义Parcelable 类型的AIDL的AIDL

使用自定义类型前得先通过import 手动引入且作为参数时必须使用修饰符修饰,一般使用in

// IUserMgrApi.aidl
package com.crazymo.remoteserver;
import com.crazymo.remoteserver.bean.User;//引入
// Declare any non-default types here with import statements

interface IUserMgrApi {
    void registUser(inout User user);
    List<User> getUsers();
}

4、使用自定义Parcelable 类型的AIDL的Service实现

  • 继承Service,重写onBind方法
  • 继承对应的Stub类,实现AIDL中定义的方法的具体的逻辑
  • onBind方法中返回我们自定义的Stub子类Binder对象
package com.crazymo.remoteserver.service;

import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.base.AbstractService;
import com.crazymo.remoteserver.bean.User;

import java.util.ArrayList;
import java.util.List;

/**
 * Auther: Crazy.Mo on 2018/5/7 15:44
 * Summary:
 */
public class RemoteUserApiService extends AbstractService {
    private final static String TAG="RemoteService";
    private List<User> mUserList;
    @Override
    protected IBinder initBinder() {
        if(mBinder==null){
            mBinder=new RemoteUserServiceBinder();
        }
        return mBinder;
    }

    /**
     * 继承AIDL文件里的Stub封装IBinder对象,可以理解为AIDL接口的实现
     */
    private final class RemoteUserServiceBinder extends IUserMgrApi.Stub{

        @Override
        public void registUser(User user) throws RemoteException {
            synchronized (this){
                if(mUserList==null){
                    mUserList=new ArrayList<>();
                }
                if(user!=null){
                    mUserList.add(user);
                    Log.e(TAG, "【服务端】 线程"+Thread.currentThread().getName()+"处理客户端的注册请求"+user.toString());
                }
            }
        }

        @Override
        public List<User> getUsers() throws RemoteException {
            synchronized (this){
                if(mUserList!=null){
                    Log.e(TAG, "【服务端】 线程"+Thread.currentThread().getName()+"处理客户端的获取用户集请求返回List集合");
                    return mUserList;
                }else{
                    return null;
                }
            }
        }
    }
}

5、在服务端清单文件注册对应的Service

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.crazymo.remoteserver">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service android:name=".service.RemoteApiService"
                 android:process=":remote">
            <intent-filter>
                <action android:name="com.crazymo.aidl.comm"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
        <service android:name=".service.RemoteUserApiService"
                 android:process=":remoteuser">
            <intent-filter>
                <action android:name="com.crazymo.aidl.user"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
        <service android:name=".service.RemoteCallApiService"
                 android:process=":remotecall">
            <intent-filter>
                <action android:name="com.crazymo.aidl.callback"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
    </application>
</manifest>

6、客户端调用服务端自定义Parcelable的AIDL

无论是本地服务还是远程服务,本质上都是Service机制,按照匿名启动Service的步骤来就可以了,所以自定义Parcelable的AIDL的是使用步骤都是:先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法

  • 引入服务端的AIDL即把服务端AIDL全部文件复制到客户端的aidl目录下
  • 引入服务端定义的Parcelable,把服务端定义的实现了Parcelable接口的JavaBean 复制到客户端相同的路径下
  • 声明远程服务对象实例(类型名为AIDL的名称)
  • 声明ServiceConnection对象实例,最好通过继承ServiceConnection实现具体的子类的形式(不要通过匿名内部类的形式创建,因为取消绑定unbindService(ServiceConnection conn)也需要传入ServiceConnection)
  • 通过上下文的bindService(@RequiresPermission Intent service,@NonNull ServiceConnection conn, @BindServiceFlags int flags)匿名启动远程服务
  • ServiceConnection的onServiceConnected完成远程服务对象实例的初始化
  • ServiceConnection的onServiceDisconnected完成远程服务对象实例的销毁或者重连
  • 通过远程服务对象实例调用远程接口
//这里把三种场景使用的完整代码全都贴在一起,做个对比
package com.crazymo.client;

import android.app.Activity;
import android.content.ComponentName;
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.crazymo.remoteserver.IRemoteApi;
import com.crazymo.remoteserver.IRemoteCallbackApi;
import com.crazymo.remoteserver.IUserMgrApi;
import com.crazymo.remoteserver.bean.User;
import com.crazymo.remoteserver.callback.IRemoteCallback;

import java.util.List;

public class MainActivity extends Activity {
    private final static String TAG="RemoteService";
    private final static String ACTION_COMM="com.crazymo.aidl.comm";
    private final static String ACTION_PARCE="com.crazymo.aidl.user";
    private final static String ACTION_CALLBACK="com.crazymo.aidl.callback";
    private boolean isUnbindRemoteApiConn=false;

    private RemoteApiConn mRemoteApiConn=new RemoteApiConn();//只要是进程通信都需要实现,因为系统是在这个ServiceConnecttion类的相关方法通过回调返回Ibinder对象的
    private RemoteUserApiConn mRemoteUserApiConn=new RemoteUserApiConn();
    private RemoteCallbackApiConn mCallbackApiConn=new RemoteCallbackApiConn();

    private IRemoteApi mRemoteApi;
    private IUserMgrApi mUserMgrApi;
    private IRemoteCallbackApi mRemoteCallbackApi;

    private IRemoteCallback mIRemoteCallback=new IRemoteCallback.Stub() {

        @Override
        public void afterSpeak(String msg) throws RemoteException {
            Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"远程回调返回的信息:"+msg);
        }
    };

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

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

    private void disconnectRemoteApiComm() {
        unbindService(mRemoteApiConn);
        isUnbindRemoteApiConn=true;
    }

    public void connAIDL(View view) {
        connectAIDL();
    }

    private void connectAIDL() {
        boolean isBind;
        isBind= AIDLUtil.bindAIDLService(this,mRemoteApiConn,ACTION_COMM);
        Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程接口(成功true,失败false):  "+isBind);
        if(!isBind){
            Toast.makeText(this,"连接远程服务发生异常",Toast.LENGTH_SHORT).show();
        }
    }

    public void useAIDL(View view) {
        if(mRemoteApi==null){
            connectAIDL();
        }
        try {
            if(mRemoteApi!=null) {//这里有必要
                Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程接口,客户端:小鸡炖蘑菇");
                String result=mRemoteApi.getRemoteStr();
                Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"服务端响应请求返回:" +result );
            }
        } catch (RemoteException pE) {
            pE.printStackTrace();
        }
    }

    public void connUserAIDL(View view) {
        boolean isBind;
        isBind= AIDLUtil.bindAIDLService(this,mRemoteUserApiConn,ACTION_PARCE);
        Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程用户管理接口(成功true,失败false):  "+isBind);
        if(!isBind){
            Toast.makeText(this,"连接远程用户管理服务发生异常",Toast.LENGTH_SHORT).show();
        }
    }

    public void testUserAIDL(View view) {
        try {
            User user=new User();
            user.setName("CrazyMo");
            user.setAge(1);
            Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程用户管理接口注册用户:"+user.toString());
            mUserMgrApi.registUser(user);
            Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"通过AIDL调用远程用户管理接口查询用户集");
            List<User> list=mUserMgrApi.getUsers();
            if(list!=null){
                for (User tmp :list){
                    Log.e(TAG, "【客户端】线程"+Thread.currentThread().getName()+"远程用户管理接口返回的用户数据:"+tmp.toString());
                }
            }
        } catch (RemoteException pE) {
            pE.printStackTrace();
        }
    }

    public void connCallbackAIDL(View view) {
        boolean isBind;
        isBind=AIDLUtil.bindAIDLService(this,mCallbackApiConn,ACTION_CALLBACK);
        Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"绑定远程带有回调的远程接口(成功true,失败false):  "+isBind);
    }

    public void testCallbackAIDL(View view) {
        if(mRemoteCallbackApi!=null){
            try {
                Log.e(TAG,"【客户端】线程"+Thread.currentThread().getName()+"调用带有回调的远程接口,发送:今天天气怎么样");
                mRemoteCallbackApi.speak("今天天气怎么样");
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }
    }

    private final class RemoteApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteApi=IRemoteApi.Stub.asInterface(service);//初始化远端服务对象实例
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(isUnbindRemoteApiConn){
                mRemoteApi=null;//主动断开直接置为null
            }else{
                while(mRemoteApi==null) {
                    connectAIDL();//做重连
                }
            }
        }
    }

    private final class RemoteUserApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mUserMgrApi= IUserMgrApi.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mUserMgrApi=null;
        }
    }

    private final class  RemoteCallbackApiConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteCallbackApi=IRemoteCallbackApi.Stub.asInterface(service);
            try {
                mRemoteCallbackApi.registerListener(mIRemoteCallback);
            } catch (RemoteException pE) {
                pE.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if(mRemoteCallbackApi!=null){
                try {
                    mRemoteCallbackApi.unRegisterListener(mIRemoteCallback);
                } catch (RemoteException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }
}


Android进阶——使用远程服务AIDL实现进程间带远程回调接口较复杂通信小结(二)_第3张图片

小结

AIDL的使用到此基本结束了,要记住AIDL本质上也是一种特殊的Service,所以使用它的基本流程和使用本地服务基本一致先连接——>在连接回调里初始化远程接口对象——>再通过远程接口对象调用远程方法。源码传送门

你可能感兴趣的:(Android,进阶)