Android跨进程通信之非AIDL(二)
16lz
2021-01-23
目录
Android跨进程通信之小例子(一)
Android跨进程通信之非AIDL(二)
Android跨进程通信之Proxy与Stub(三)
Android跨进程通信之AIDL(四)
从最常用的例子谈起
这篇文章围绕的核心主题就是
IBinder
,或许你早就在Service
的onBind
方法中见过。该方法就是返回一个IBinder
对象。一般我们的做法是这样的:
service代码
public class MusicService extends Service{ public IBinder onBind(Intent intent) { return new MyBinder(); } public class MyBinder extends Binder { public MyService getMyService() { return MusicService.this; } } public void play(){ Log.i("TAG", "play"); } public void pause(){ Log.i("TAG", "pause"); }}
获取service控制权代码
public class MainActivity extends Activity { Intent service; ServiceConnection conn; MyService myService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); service = new Intent(MainActivity.this, MusicService.class); conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder binder) { myService = ((MyBinder) binder).getMyService(); } }; bindService(service, conn, BIND_AUTO_CREATE); }}
我们通过
ServiceConnection
去绑定一个Service
然后绑定成功后返回一个IBinder
对象,其中IBinder
就充当与一个中介
。是的,中介这个词没有用错。再通过MyBinder
提供的getMyService
来获取服务进而控制Service
。
一个真相引发的血案
其实上面的例子跟文章要说的内容没什么卵关系,只是让你更好的回忆一下
IBinder
接口。这篇文章主要讲一下onTransact
方法。
IBinder跨进程通信图
ibinder下面就大概简单说明一下。
- 首先
myActivity
通过调用bindService
去绑定一个远程的服务(myService
),绑定成功后返回一个IBinder
对象。这时候双方就算是建立了连接了。 - 建立连接之后,双方就可以通过持有的
IBinder
进行通信。myActivity
使用IBinder
的transact
方法去给底层的Binder Driver
(Linux层)发送消息间接调用底层的IBinder
的execTransact
方法。 - 而
execTransact
导致的结果就是调用onTransact
方法。那么这时候事件的处理就可以在该环节进行了。
其实就是一个transact->onTransact的一个过程
事实上我们看到
transact
和onTransact
的参数是一模一样的。
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags);
public final boolean transact(int code, Parcel data, Parcel reply,int flags);
transact的参数
-
code
: 这是一个识别码,类似于Handler体系里面的Message的what属性。根据不同的code产生不同的响应。 -
data
: 调用transact的对象传送过去的参数。 -
reply
: 调用onTransact的对象返回的参数。 -
flags
: Java里面默认的native方法都是阻塞的,当不需要阻塞的时候设置为IBinder.FLAG_ONEWAY
,否则设置为0
一个小例子
环境:
- 一个叫做RemoteService的APP
- 一个叫做RemoteServiceDemo的APP
- RemoteServiceDemo中的Activity去控制RemoteService中的Service。
RemoteService
Service声明
别忘了加入,否则会抛出安全异常。
Service代码
public class MusicService extends Service { private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public IBinder onBind(Intent intent) { return new MyBinder(); } public class MyBinder extends Binder { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case 0: Log.i("TAG", data.readString()); play(); reply.writeString("Service play Music at " + sdf.format(new Date())); break; case 1: Log.i("TAG", data.readString()); pause(); reply.writeString("Service pause Music at " + sdf.format(new Date())); break; default: break; } return true; } } public void play() { Log.d("TAG", "play"); } public void pause() { Log.d("TAG", "pause"); }}
服务的代码很简单,也没什么需要说的。
RemoteServiceDemo
布局
代码
public class MainActivity extends Activity { private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { mBinder = service; } }; private IBinder mBinder = null; private Parcel data = Parcel.obtain(); private Parcel reply = Parcel.obtain(); private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void bind(View v) { if (isBinded()) { return; } /** * 通过ComponentName去定位意图 * 第一个参数是应用包名 * 第二个参数是Service或者Activity所在的包名+类名 */ bindService( new Intent().setComponent( new ComponentName("com.example.remoteservice", "com.example.remoteservice.MusicService")), mServiceConnection, Context.BIND_AUTO_CREATE); } public void unbind(View v) { if (!isBinded()) { return; } unbindService(mServiceConnection); mBinder = null; } public void play(View v) { if (!isBinded()) { return; } try { data.writeString("Activity request to play music at " + sdf.format(new Date())); mBinder.transact(0, data, reply, 0); Log.i("TAG", reply.readString()); } catch (RemoteException e) { e.printStackTrace(); } } public void pause(View v) { try { data.writeString("Activity request to pause music at " + sdf.format(new Date())); mBinder.transact(1, data, reply, 0); Log.i("TAG", reply.readString()); } catch (RemoteException e) { e.printStackTrace(); } } private boolean isBinded() { return mBinder != null; } }
因为我们需要使用
reply
,所以我们使用阻塞的transact
,否则我们调用了transact
,但是那边还未给reply写入字符串,最终导致NullPointException。
项目地址
Github地址
更多相关文章
- 布局中文件中【控件间距参数详解以及单位选择】
- androdi与服务器Socket通信原理
- Android—TextView的XML属性和方法
- 面向UDP的Android——PC双向通信(一):实现Android客户端和PC服务器
- Android来电铃声默认设置的实现方法与怎么设置语音来电的默认铃
- android使用socket使底层和framework通信
- NDK开发历程(一):android native code的调试方法
- Android ListView拖动时背景变黑的解决方法