Android Framework框架之IBinder进程间通信

前面一篇介绍了Android中一个进程中有一个VM,一个主线程,一个Looper和一个MessageQueue,这一篇重点讲一下利用IBinder实现进程间通信。首先进程间通信肯定至少要有两个进程嘛。我们就模拟下这个场景,写一个Demo,声明这个Demo要用到两个进程。然后进程A放一个MainActivity,放几个按钮,用来控制播放音乐,另一个进程B用来实现播放音乐。通过这个Demo穿插这讲进程间通信IPC到底是个什么东西。

首先看MainActivity的代码。

public class MainActivity extends AppCompatActivity {
    private IBinder ib = null;
    private Button bt_1;
    private Button bt_2;
    private Button bt_3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt_1 = (Button) findViewById(R.id.bt_1);
        bt_2 = (Button) findViewById(R.id.bt_2);
        bt_3 = (Button) findViewById(R.id.bt_3);
        bt_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("play");
                try {
                    ib.transact(1, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        bt_2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("stop");
                try {
                    ib.transact(2, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        bt_3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        startService(new Intent(this, myService.class));
        bindService(new Intent(this, myService.class), conn, BIND_AUTO_CREATE);

    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ib = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

这里面很简单,只有三个按钮,每个按钮有一个点击事件,分别去做不同的事。(先不要管关于进程通信的内容,接下来会仔细的说明)

点击按钮bt_1让另一个进程的Service播放音乐,点击bt_2,让另一个进程的Service停止播放音乐,单击bt_3,finish掉Activity。然后根据上一篇讲的关于点击图标后怎么执行到MainActivity的onCreate()方法的问题,在此在重复一遍。App要启动,zygote孵化器就创建一个新的进程来执行这个App,进程里包含一个主线程,一个VM,Looper,MessageQueue。然后主线程在while循环里绕圈圈,框架把MainActivity对象new出来,发Message给主线程,让主线程执行MainActivity.onCreate();这个时候我们来看MainActivity.onCreate()最后两行,我们分别写了启动一个Service,绑定这个Service。然后根据前几篇讲的,App开发者是没有权利new一个Service对象的,App开发者只有在Manifest文件里声明一个Service,在我们通过Intent告知框架启动这个Service,然后框架根据我们在Manifest里写的Service的配置才知道要new一个什么样的Service。下面是我们对这个Service的配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.gray_dog3.ibindertest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".myService" android:process=":remote">
            <intent-filter>
                <action android:name="com.example.gray_dog3.ibindertest.PLAY_MUSIC_SERVICE"/>
            </intent-filter>
        </service>
    </application>

</manifest>

上面就是我们对Service的配置了,名字叫myService,而后面的
android:process=":remote"

这个则是告诉框架,我们想要启动的Service是在一个新的进程中的。所以框架便按照我们的要求,孵化出一个新进程,然后new一个myService放进去。然后这个进程的主线程在while循环里打转转,过一会框架给这个主线程一个Message,告诉他执行Service的onCreate方法。我们我们在在myService的写一些播放音乐的准备的代码,和前面的MainActivity.onCreate()一个性质。下面是我们的myService的代码。(先不要关注具体的业务逻辑后面会讲)

public class myService extends Service {
    private IBinder iBinder = null;
    private static ContrlSong contrlSong;

    @Override
    public void onCreate() {
        iBinder = new myBinder();
        contrlSong = new MySongContrler(getApplicationContext());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    public static void play() {
        contrlSong.play();
    }

    public static void stop() {
        contrlSong.stop();
    }
}

现在要注意一个问题,就是框架已经为我们启动两个进程,两个进程各自有一个自己的主线程,一个主线程执行完MainActivity.onCreate()之后在它的while循环里转圈圈,另一个主线程在执行完myService.onCreate()之后在它自己的while循环里转圈圈。

顺着刚刚的MainActivity的主线程往下分析,刚刚是执行到startService这一步了,执行完后两个进程也有了,Service也有了。然后MainActivity想在点击按钮后让myService播放音乐。(为了方便下面用MA代表MainActivity,MS代表myService)如果MA和MS在一个进程中,我们只需要在myService里写一个静态方法play()我们直接调myService.play()就可以了。即便是为了不引起ANR,我们也可以在MS里面开一个子线程让它去播放音乐。但是对于MA来说,我不管你怎么去实现,我只要调一个方法就完事了。但是现在问题是MA和MS不在同一个进程里。MA是不能直接调用MS的方法的。为什么不能直接调用呢?关于这个,在这里在补充一些进程的知识。

进程是一个独立的执行空间以及它所拥有的资源巴拉巴拉。。。总归记住一点就可以了,每个进程的空间都是独立的,这个很重要。此时的MA和MS就好像是在一个是FaceBook用户,一个是QQ用户,它们要想交流,是没办法直接加好友的。但是问题又来了,我是一个APP的两个进程,你TM不让我交流,我还玩个卵子。所以这时候就要通过一个统一接口来把它们两个连起来了,比如QQ邮箱,和FaceBook邮箱(不知道FaceBook有没有邮箱,假设有哈)。Android就为我们准备了一个叫IBinder,用来让两个进程通信。

现在明白了IBinder的用途,让我们来看看IBinder怎么用。接着前面的状态开始执行,前面说到两个进程有了,MS也启动了,然后要通信了,先把两个连接起来。这时候MS的那个主线程刚执行完startService,然后开始执行bindService,我们来看MainActivity.onCreate()的最后一行代码

bindService(new Intent(this, myService.class), conn, BIND_AUTO_CREATE);
第一个参数Intent,不奇怪,说明这个操作是要交个框架来完成的。第二个conn

private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ib = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

在MyActivity中,是类的属性,类加载后的时候就已经有这个对象了。这个ServiceConnection和OnClickListener是一个性质的东西。就是MA和MS绑定好了以后触发它执行它的绑定后的方法。前面说的bind动作是交由框架执行的,MA的主线程发出这个向框架的请求后就去转圈圈了。框架把bind这件事做好后再发一个Message告诉MA的主线程,我bindService已经bind好了,你执行bind后的动作把,然后MA的主线程就来执行conn.onServiceConnected了,注意这个时候Message里面还带的有一个IBinder的对象。这个IBinder的对象就是两个进程通信的工具 conn.onServiceConnected()里面的一行 ib = service;就是把这个对象的引用存下来。也就是bind成功后MA就拥有了回传的跟MS通信的工具了。看下MS里面的代码:

 @Override
    public void onCreate() {
        iBinder = new myBinder();
        contrlSong = new MySongContrler(getApplicationContext());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }
在MS创建的时候我们new了一个Binder对象。然后当MS被绑定onBind()的时候(这个就是在MA中发出bindService操作后框架在这边调用onBind()),我们把这个iBinder对象return给框架,框架交给交给MA,所以MA和MS就拥有了"同一个"对象,然后如果这个对象里有一个写方法,一个读方法,我写你读,或者你写我读,这样不就可以通信了么。这里我们为什么给"同一个"加上引号呢?实际上它们并不是同一个对象,而是利用一个是Binder,一个是BinderProxy。也就是一个是return回去的那个对象,一个是那个对象的分身。因为框架在进程间通信是通过底层Linux来完成的,MS这边return回去iBinder对象,框架就"new"出一个分身给MA,使他们看起来像是一个对象,操作的时候也像操作同一个对象一样,由框架和底层来保证分身和本尊的同步问题,对上面是透明的。既然我们MA和MS拿到了"同一个"对象,就可以利用这个对象进行通信了,一个写一个读。

懂了这个大概的原理我们再来看IBinder这个接口,是Android框架为我们写好的用来进程间通信的接口。同时Android也写了这个接口两个实现类类Binder和BinderProxy。其实叫什么名字,一点都不重要,不管它叫IBinder还是叫狗蛋,目的只有一个嘛,就是有一个对象,让MA和MS同时持有,然后这个对象有一个写数据的方法,一个读数据的方法,就可以完成通信了。MA持有的对象是BinderProxy类型的,MA持有的对象是Binder类型的。但是这些是框架做的事对我们是透明的,现在我们认为MA和MS拿到了同一个对象。在这里我们可以认为MainActivity.ib和myService.iBinder两个引用指向同一个对象。然后我们来看MA里的代码:

bt_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Parcel parcel = Parcel.obtain();
                Parcel parcelReply = Parcel.obtain();
                parcel.writeString("play");
                try {
                    ib.transact(1, parcel, parcelReply, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
我们前面讲的,点击bt_1要让MS播放音乐。我们让它播放音乐的信号就是:
ib.transact(1, parcel, parcelReply, 0);

这里的Parcel就是一个序列化的工具类。序列化又是各什么东东呢?刚刚我们说的MA和MS拥有"同一个"对象的引用,MA里面ib.transact()就相当于写的动作,MS里面的iBinder.onTransact()就相当于读的动作。当MA写了以后由框架通知MS去读。(iBinder.onTransact()的代码写在MyBinder里):

public class myBinder extends Binder {

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        if (code == 1)
            myService.play();
        if (code == 2)
            myService.stop();
        return true;
    }

}
这里我们相当于在myBinder里解析了传过来的内容。实际有用的就是1和2,MA传1就让MS唱歌,MA传2就让MS停止。现在来讲什么是序列化,序列化就是把要传的数据转成一个大家约定的格式,反正都是二进制0和1表示的,我只要规定String怎么表示,类怎么表示等等,就可以做一次转换,到接收方再反着翻译回来就行了。为什么要这么搞呢,我直接传数据不就行了?不行,因为前面已经说了,我们并不在操作统一对象,而真正的IPC也并不在Java层,底层代码鬼知道用什么语言写的,有什么样的数据结构定义,底层面对一堆乱码oo**xx怎么搞,而且以类或者对象什么的存储可能占的空间也比较大,不利于快速的读写啊。所以就搞出序列化这么各东东。把要传的数据转一种规定的格式,到那边再翻译过来就ok了。

到这里差不多就扯完了。整个执行逻辑就是这样咯,最后贴上播放音乐部分的业务逻辑。

播放音乐接口:

public interface ContrlSong {
    public void play();
    public void stop();
}


实现类。

public class MySongContrler implements ContrlSong {
    private MediaPlayer mediaPlayer = null;
    private Context context;

    MySongContrler(Context context) {
        this.context = context;
    }

    @Override
    public void play() {
        if (mediaPlayer != null) return;
        mediaPlayer = MediaPlayer.create(context, R.raw.song);
        mediaPlayer.start();
    }

    @Override
    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

}

有时间把代码传到GitHub,拜!



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,可以使用 Handler 进行进程间通信。以下是使用 Handler 进行进程间通信的基本步骤: 1. 在发送方进程中创建 Handler 对象,并在其构造函数中传递一个 Looper 对象。这个 Looper 对象可以通过调用 Looper.myLooper() 或 Looper.getMainLooper() 方法获取。 2. 在接收方进程中创建一个 Messenger 对象,将其作为参数传递给发送方进程的 Handler 对象的构造函数。 3. 在发送方进程中,通过 Messenger.send() 方法向接收方进程发送消息。在发送消息时,需要创建一个 Message 对象,并将其传递给 Messenger.send() 方法。 4. 在接收方进程中,需要创建一个 Handler 对象,并在其 handleMessage() 方法中处理消息。在创建 Handler 对象时,需要将当前线程的 Looper 对象作为参数传递给其构造函数。 5. 在接收方进程中,通过 MessengerbindService() 方法来连接发送方进程的服务,从而接收发送方进程发来的消息。 下面是一个简单的示例代码,用于演示如何在两个进程之间使用 Handler 进行通信: 在发送方进程中: ``` // 创建 Handler 对象 Handler handler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { // 处理接收到的消息 } }; // 创建 Messenger 对象 Messenger messenger = new Messenger(handler); // 发送消息 Message message = Message.obtain(); messenger.send(message); ``` 在接收方进程中: ``` // 创建 Handler 对象 Handler handler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { // 处理接收到的消息 } }; // 创建 Messenger 对象 Messenger messenger = new Messenger(handler); // 绑定发送方进程的服务 Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.sender", "com.example.sender.MyService")); bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 连接成功,保存 Messenger 对象 messenger = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { // 连接断开 } }, Context.BIND_AUTO_CREATE); ``` 注意:在进行进程间通信时,需要使用 AIDL 接口来定义消息的格式。具体的实现方式超出了本回答的范围,可以查阅相关文档进行学习。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值