app层的binder应用

2种:

1.使用service组件

这种间接使用的也是serviceManager


public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
    IChangeCallback mCallBack = null;
    IStudentInterface.Stub mBinder = new IStudentInterface.Stub() {

        @Override
        public int getStudentId(String name) throws RemoteException {
            if (name.equals( "helloworld")) {
                if (mCallBack!= null) {
                    Log.i("test"," mCallBack changeData result = " + mCallBack.changeData(1111));
                }
                return 1;
            } else {
                return  10;
            }
        }

    };
    @Override
    public void onCreate() {
        Log.i("test","MyService onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("test","MyService onStartCommand intent = " + intent );
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i("test","MyService onDestroy");
        super.onDestroy();
    }
}

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this,MyService.class);
                bindService(intent, new ServiceConnection() {
                    //bind后会调用这个onServiceConnected
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        //service这个对象就是服务端onBind返回的IBinder对象
                        IStudentInterface remoteInterface = IStudentInterface.Stub.asInterface(service);
                        try {
                            int helloworld = remoteInterface.getStudentId("helloworld");
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        Log.i("test","client onServiceDisconnected name = " +name);
                    }
                }, BIND_AUTO_CREATE);
            }
        });
2.直接使用serviceManager.getService

BatteryManager manager = (BatteryManager) getSystemService(BATTERY_SERVICE);

例如getSystemService实际上最终也是调用的binder 下面的是源码注册BatteryManager时把register(binder)对象返回出来

然后我们在使用BatteryManager manager时最终会调用到这个register 也就是说使用的是binder

特性 Binder AIDL
本质 一套 IPC 机制和内核驱动 一种接口定义语言和代码生成工具
在代码中看到的是(AIDL封装) 实际底层发生的是(Binder机制)
IStudentInterface.Stub 一个继承了 Binder
的类

AIDL 是基于 Binder 机制的一套高级封装,它通过自动生成代码的方式,让开发者能够以一种简单、直观的方式(类似于本地接口调用)来使用复杂的 Binder IPC。

AIDL

https://blog.csdn.net/learnframework/article/details/120103471

参考:详细https://blog.csdn.net/learnframework/article/details/119634022

what?aidl是一种语言,用来帮助写binder跨进程通信的接口

why:

1.如果要java语言实现那会导致要写很多重复的固定代码,写的代码量比较大,稍微不小心容易出错
2这一部分完全可以让脚本根据某些配置文件来生成对应的java代码

生成的IStudentInterface.java中主要了解的是Stub 类和Stub .Proxy

按照调用顺序:

Proxy就是remoteInterface=asInterface(iBinder);返回的类

然后remoteInterface.getStudentId(“helloworld”);

然后调用到 mRemote.transact(Stub.TRANSACTION_getStudentId, _data, _reply, 0);

然后会调用到Stub的onTransact

public static abstract class Stub的onTransact方法最终会调用到我们自己实现的匿名内部类的具体实现方法(getStudentId)

总结proxy在客户端 Stub在调用服务端方法

回忆

public IBinder onBind(Intent intent) {

    return mBinder;

}

IChangeCallback mCallBack = null;

IStudentInterface.Stub mBinder = new IStudentInterface.Stub() {

    @Override

    public int getStudentId(String name) throws RemoteException {

 }}

};

final IStudentInterface1 remoteInterface = IStudentInterface1.Stub.asInterface(service);

remoteInterface.getStudentId(“helloworld”);

AIDL写法

写个aidl文件

interface IStudentInterface {
    /**
       * 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);

    int getStudentId(String name);
}

这个aidl文件写好后,点击make编译一下工程,在编译过程就会被aidl.exe转成对应的java文件

在Service写个Stub的匿名实现类,在onbind方法中返回出去

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
    IChangeCallback mCallBack = null;
    IStudentInterface.Stub mBinder = new IStudentInterface.Stub() {

        @Override
        public int getStudentId(String name) throws RemoteException {
            if (name.equals( "helloworld")) {
                if (mCallBack!= null) {
                    Log.i("test"," mCallBack changeData result = " + mCallBack.changeData(1111));
                }
                return 1;
            } else {
                return  10;
            }
        }

act调用方:

@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,MyService.class);
bindService(intent, new ServiceConnection() {
    //bind后会调用这个onServiceConnected
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //service这个对象就是服务端onBind返回的IBinder对象
        IStudentInterface remoteInterface = IStudentInterface.Stub.asInterface(service);
        try {
            int helloworld = remoteInterface.getStudentId("helloworld");
aidl的几个关键字

默认aidl是同步的, 默认in类型

in out inout修饰的是传参数,和返回值没有关系,不影响return的值

1.oneway

立即返回,没有callback,异步的,不能有返回值

2.in(以服务端角度)

in 表示数据只能由客户端流向服务端。(表现为服务端修改此参数,不会影响客户端的对象)

3.out(以服务端角度)

out 表示数据只能由服务端流向客户端。(表现为服务端收到的参数是空对象,并且服务端修改对象后客户端会同步变动)

4.inout

inout 则表示数据可在服务端与客户端之间双向流通。(表现为服务端能接收到客户端传来的完整对象,并且服务端修改对象后客户端会同步变动)

binder双向通信

setCallback把Ibinder对象传到服务端,服务端作为新的客户端调用这个Ibinder对象的方法

客户端:

IStudentInterface remoteInterface = IStudentInterface.Stub.asInterface(service);
try {
    remoteInterface.setCallback(new CallbackeService());

服务端

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
    IChangeCallback mCallBack = null; //是个adil的接口实际上就是这里把IChangeCallback的实现类传过来了,
    之后就可以调用客户端提供的方法了
    IStudentInterface.Stub mBinder = new IStudentInterface.Stub() {
        @Override
        public void setCallback(IChangeCallback callback) throws RemoteException {
            mCallBack = callback;
            mCallBack.asBinder().linkToDeath(new DeathRecipient() {
                @Override
                public void binderDied() {
                    Log.i("test","linkToDeath binderDied");
                }
            },0);
        }

4、linktodeath介绍

bindSevices,当服务端crash时,客户端service会回调OnSeviceDisconnected

那为什么还需要linktodeath?

在双向通信时setCallback()这个实现是在客户端,这个时候如果服务端调用客户端的方法是感知不到客户端是否crash,(也就是上图的第3步)

死亡通知是为了让Bp端(客户端进程)进能知晓Bn端(服务端进程)的生死情况,当Bp端进程死亡后能通知到Bn端。

定义:AppDeathRecipient是继承IBinder::DeathRecipient类,主要需要实现其binderDied()来进行死亡通告。

注册:binder->linkToDeath(AppDeathRecipient)是为了将AppDeathRecipient死亡通知注册到Binder上。

Bp端只需要覆写binderDied()方法,实现一些后尾清除类的工作,则在Bn端死掉后,会回调binderDied()进行相应处理。

@Override
        public void setCallback(IChangeCallback callback) throws RemoteException {
            mCallBack = callback;
            //监听客户端(上图左边)的进程是否死了
            mCallBack.asBinder().linkToDeath(new DeathRecipient() {
                @Override
                public void binderDied() {
                    Log.i("test","linkToDeath binderDied");
                }
            },0);
        }

当然双向通信时 你可以在两边都加上linktodeath

Messenger

Message  消息 
Messenger 消息者 邮递员 

Messenger是基于AIDL实现的轻量级IPC方案。

这里立马就会有疑问为什么要它呢?明明有了aidl

毕竟通信什么的要写aidl,而且客户端和服务端都需要aidl文件,两个过程里面都需要,相对来说还是比较麻烦,对于项目过程中可能就是一些简单的跨进程数据传递,就是调用几个非常非常简单的方法,很多觉得都要写aidl成本比较大,那么有没有更加简单的方案来解决这个问题呢?

那就是Messenger。

https://blog.csdn.net/learnframework/article/details/119845833

1、服务端对Messenger的使用

服务端需要实现一个Handler用于处理客户端发来的跨进程通信信息:

 Handler messengerHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case  1:
                    Log.i("test","MessengerService Messenger handleMessage msg = " + msg + " bundle  key value = " + msg.getData().getString("bundleKey"));
                    Messenger clientSend = msg.replyTo;
                    Message toClientMsg = Message.obtain();
                    toClientMsg.what = 2;
                   // toClientMsg.obj = "I am replay from Server";
                    try {
                        clientSend.send(toClientMsg);
                    }catch (Exception e) {
                        Log.i("test","MessengerService clientSend  error ",e);
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };

其次服务端构造出对应的Messenger:
服务端构造:
Messenger messenger = new Messenger(messengerHandler);
注意这里参数是messengerHandler即一个Handler

最后,当服务端的onBinder回调时候要返回Messenger的IBinder对象给客户端

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return messenger.getBinder();
}

2、客户端的使用

客户端还是和以前一样通过bindService在ServiceConnection类的onServiceConnected获取到服务端的返回的IBinder,从而获取到服务端的Messenger代理类,调用send函数发送Message。所以Messenger能发送的信息只有Message能携带的信息。

Intent intent = new Intent(MainActivity.this,MessengerService.class);
                Log.i("test","MessengerService  onClick ");
                bindService(intent, new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        try {
                            Log.i("test","MessengerService  onServiceDisconnected name = " +name);
                            messengerServer = new Messenger(service);
                            sendMessageToServer();
                        } catch (Exception e) {
                            e.printStackTrace();
                            Log.i("test","error " ,e);
                        }
                    }
                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        Log.i("test","client onServiceDisconnected name = " +name);
                    }
            }, BIND_AUTO_CREATE);

void sendMessageToServer() throws RemoteException {
    Message toServer = Message.obtain();//构造Message
    toServer.replyTo = messengerClientSend;//这个也是一个messengerServer发信东西。对面可以回复
    toServer.what = 1;
    //toServer.obj = "hello I send from client"; 注意不可以 传递非parcel的对象,这个只能给obj赋值为parcel类型对象否则报错
    Bundle bundle = new Bundle();
    bundle.putString("bundleKey","bundleValue Client");
    toServer.setData(bundle);
    messengerServer.send(toServer); //messengerServer发信
}

大家这里注意客户端获取了服务端IBinder对象后,用它来构造客户端的Messenger,
messengerServer = new Messenger(service);
这里注意是和服务端不一样地方
有了服务端Messenger后,那么就可以通过它与服务端进行通信了,通信的内容载体是我们最为属性Message,对他就是和Handler搭配的Message,它就是具体消息体,即你需要发送什么消息,都是把内容转换成Message对象既可以,这里我们案例中传递一个Bundle的对象,这个Bundle对象可以利用键值对方式装载各种各样类型数据。和Intent传递数据Bundle是一样的。注意这里message对象还有一个属性是replyTo ,这个是Messenger类型的,字面意思就是说这个消息发送过去,如果对方需要回复,就可以通过消息中的replyTo 的Messenger对象来进行回复,这里是不是也和我们上节课讲的binder双向通信一样,所以说Messenger这种方式本身就相当于自带了双向通信
3 Messenger本质原理

Messenger其实本质上也是使用aidl进行实现了,只是这个aidl是在Framework层面进行写好了,不需要你写,你也就没有在意,没有看到。这里对他的源码进行分析一下:

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;
/**
 * Create a new Messenger pointing to the given Handler.  Any Message
 * objects sent through this Messenger will appear in the Handler as if
 * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
 * been called directly.
 * 
 * @param target The Handler that will receive sent messages.
 */
public Messenger(Handler target) {
    mTarget = target.getIMessenger();//这个是handler对象获取IMessenger接口
}

/**
 * Send a Message to this Messenger's Handler.
 * 
 * @param message The Message to send.  Usually retrieved through
 * {@link Message#obtain() Message.obtain()}.
 * 
 * @throws RemoteException Throws DeadObjectException if the target
 * Handler no longer exists.
 */
public void send(Message message) throws RemoteException {
    mTarget.send(message);//这其实调用是IMessenger接口的send
}
。。。。。省略
}

代码中注释提到的handler对象获取IMessenger接口,IMessenger 接口到底又是什么呢?
目前我没没有看到有aidl啊。。。

Handler的getIMessenger

@UnsupportedAppUsage
final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}
private final class MessengerImpl extends IMessenger.Stub {
    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);
    }
}

大家看这里其实就是MessengerImpl ,是继承IMessenger.Stub,大家看到IMessenger.Stub是不是和aidl里面的接口和很熟悉啊。对它其实就是IMessenger.aidl文件生成的

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐