通过一个简单的音乐播放器探讨 Android Aidl 的实现原理

众所周知,音乐播放器的播放不应该在前台进程,而是要在另外一个进程的 Service 中进行,这样才能实现后台播放功能,同时不影响 UI 进程且不共用内存资源从而减少双方被 kill 的可能性。
由于不同进程间是无法直接通信的,因此在这种情况我们会使用 AIDL 去实现进程间通信。那么为什么使用 AIDL 就能实现进程间通信呢?AIDL 实质上做了什么呢?我们接下来通过实现一个音乐播放器来了解下。

我们先来写两个 AILD 接口,如下:

// IMusicServiceAidl.aidl
package com.android.mikechenmj.myapplication.aidl;

import com.android.mikechenmj.myapplication.aidl.ICallbackAidl;

interface IMusicServiceAidl {

    void startMusic();

    void pauseMusic();

    void switchMusic();

    oneway void setCallback(ICallbackAidl callback);
}
// ICallbackAidl.aidl
package com.android.mikechenmj.myapplication.aidl;


interface ICallbackAidl {

        void update();

        String getMusicPath();

}

IMusicServiceAidl.aidl 由后台服务实现,在对应的方法上实现播放音乐、暂停音乐以及切换音乐的操作。除此之外还通过 setCallback 方法保存客户端实现的 ICallbackAidl.aidl 引用,从而进行服务端向客户端的通信。

ICallbackAidl.aidl 由前台进程(主界面 Activity)实现,目的是接受服务端的消息,然后在主界面作出相应的逻辑操作。

后台服务 MusicService 的工作是通过 MediaPlayer 类控制音乐的播放暂停切换等操作,并通知 MusicListActivity 进行更新。

主界面活动 MusicListActivity 主要实现 UI 逻辑。在主界面活动 MusicListActivity 中,我们获取存储在手机的音乐列表,并将其展示在一个 ListView 上。并在用户按下播放、暂停、和切换音乐功能按键的时候,调用从 ServiceConnection 对象的 onServiceConnected 方法返回的 IMusicServiceAidl 引用的对应方法,控制音乐播放行为,实现跨进程调用。
上述所说就是实现基本的思路,具体实现可参考 github 上的代码:通过aidl实现跨进程通信的音乐播放器

接下来我们进入源码探讨下为什么 Aidl 能实现跨进程通信的。其实 Aidl 实现跨进程通信是通过 Binder 机制来实现的,我们查看 IMusicServiceAidl.java 或者 ICallbackAidl.java 的代码就能发现这一点。Aidl 对 Binder 进行了封装,让我们更加方便地跨进程通信。(注:阅读此文需要对 Binder 有基础的了解。)

以 IMusicServiceAidl.java 为例,我们可以发现抽象内部类 Stub 在实现 IMusicServiceAidl 接口的同时还继承了 android.os.Binder 类的,并实现了一个关键方法:onTransact。了解 Binder 机制的同学都知道,onTransact 方法会在 transact 方法中被调用,负责处理从 transact 方法传递过来的事件,因此实现了这个方法的 IMusicServiceAidl.Stub 类就充当了服务端的角色。代码如下:

        @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_startMusic: {
                    data.enforceInterface(DESCRIPTOR);
                    this.startMusic();
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_pauseMusic: {
                    data.enforceInterface(DESCRIPTOR);
                    this.pauseMusic();
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_switchMusic: {
                    data.enforceInterface(DESCRIPTOR);
                    this.switchMusic();
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_setCallback: {
                    data.enforceInterface(DESCRIPTOR);
                    com.android.mikechenmj.myapplication.aidl.ICallbackAidl _arg0;
                    _arg0 = com.android.mikechenmj.myapplication.aidl.ICallbackAidl.Stub.asInterface(data.readStrongBinder());
                    this.setCallback(_arg0);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

在 onTransact 方法中,判断 code 值执行相应的操作,并借助 Parcel 类实现对象的跨进程传递。 以开始音乐的方法为例,当 code 为 TRANSACTION_startMusic 时,会执行到 startMusic() 方法,而该方法的实现是在 MusicService.java 中的 IMusicServiceAidl.Stub() 的初始化中。

    private IMusicServiceAidl mIMusicServiceAidl = new IMusicServiceAidl.Stub() {

        @Override
        public void startMusic() throws RemoteException {
            mMediaPlayer.start();
        }

        @Override
        public void pauseMusic() throws RemoteException {
            mMediaPlayer.pause();
        }

        @Override
        public void switchMusic() throws RemoteException {
            String path = mCallback.getMusicPath();
            initMediaPlayer(path);

            if (DEBUG) {
                mMediaPlayer.seekTo((int) (mMediaPlayer.getDuration() * 0.9f));
            }

            mMediaPlayer.start();
        }

        @Override
        public void setCallback(ICallbackAidl callback) throws RemoteException {
            mCallback = callback;
        }
    };

IMusicServiceAidl.Stub() 充当了服务端的角色,那么谁充当了客户端的角色呢?我们继续查看 IMusicServiceAidl.java 类可以发现,在 Stub 类里面,有一个静态内部类 Proxy,且该类也实现了 IMusicServiceAidl 接口,那是不是这个类充当了客户端的角色呢?我们接下来再去查看下代码。

在 MusicListActivity.java 主活动中,我们调用 bindService 方法成功绑定服务之后,会回调 ServiceConnection 的 onServiceConnected 方法,如下:

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMusicServiceAidl = IMusicServiceAidl.Stub.asInterface(service);
            if (mIMusicServiceAidl == null || !service.isBinderAlive()) {
                Toast.makeText(MusicListActivity.this,"连接服务失败", Toast.LENGTH_SHORT).show();
                return;
            }

可以看到,我们是通过 IMusicServiceAidl.Stub.asInterface(service) 方法获取 mIMusicServiceAidl,并通过它去与服务跨进程通信,而查看 asInterface 方法可知,该方法实质上是返回了一个 IMusicServiceAidl.Stub.Proxy 对象,并把 IBinder 作为构造函数参数传入。

从上面的分析可知,IMusicServiceAidl.Stub.Proxy 确实是充当了客户端的角色,MusicListActivity 通过调用 Proxy 的对应方法,通知 MusicService 中的 Stub 进行对应的实际逻辑。还是以 startMusic 方法为例,Proxy 类的 startMusic 方法如下:

            @Override
            public void startMusic() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_startMusic, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

重点就在于 mRemote.transact(Stub.TRANSACTION_startMusic, _data, _reply, 0); 这一段代码。在这段代码中方法传入了 Stub.TRANSACTION_startMusic 作为标志 code,从而调用 Stub 的 onTransact 方法通知服务端 Stub 类调用实际逻辑的 startMusic 方法,从而实现跨进程通信。

一句话总结,Aidl 其实就是对于 Binder 机制的封装。运用代理机制,Aidl 让 Proxy 作为客户端发出信息,Stub 作为服务端接收信息,从而实现跨进程通信。

既然如此,我们是不是也可以模仿 Aidl 去实现自己的跨进程通信呢?答案自然是肯定的。修改后的代码已经上传到 github 上了:模仿 Aidl 实现跨进程通信的音乐播放器,修改的关键点在于把 Proxy 的逻辑移到客户端当中去,把 Stub 的逻辑移到服务端去。比如对于 IMusicServiceAidl 而言,MusicListActivity 为客户端,MusicService 为服务端,而对于 ICallbackAidl 而言,MusicService 为客户端,MusicListActivity 为服务端。

此篇博客到此结束,如有错漏欢迎提出,谢谢。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android AIDLAndroid Interface Definition Language)是一种用于在不同进程间进行通信的机制。在音乐播放器实践中,AIDL可以被用来实现音乐播放器服务与客户端应用之间的交互。 为了实现这一功能,首先需要创建一个MusicPlayerService服务类,该类需要实现AIDL接口中定义的方法。例如,可以在AIDL接口中定义“播放”、“暂停”、“停止”等功能的方法。服务类需要实现这些方法,并在方法中实现相应的播放器操作。 接下来,在客户端应用中,需要绑定MusicPlayerService服务并具备与其通信的能力。通过AIDL接口,客户端应用可以调用服务类中定义的方法,实现对音乐播放器的操控。比如,客户端应用可以调用“播放”方法将音乐播放器切换到播放状态。 需要注意的是,AIDL使用涉及到进程通信,需要在AndroidManifest.xml文件中进行相关配置,以确保服务和客户端应用能够正确地进行通信。另外,在使用AIDL之前,还需要定义所需的数据类型和参数,以便实现服务和客户端应用之间的数据传输。 在实际的音乐播放器实践中,AIDL能够提供可靠的进程间通信机制,使得服务和客户端应用之间能够进行数据交互和操控操作。通过AIDL,客户端应用可以调用服务的方法来控制音乐播放器的播放、暂停、停止等功能,同时还可以获取音乐的信息,如歌曲名称、歌手名等。这样,便实现了一个基于AIDL音乐播放器实践。 综上所述,Android AIDL音乐播放器实践中发挥了重要作用,通过它,服务和客户端应用能够高效地进行进程通信,实现音乐播放器的功能和操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值