利用服务实现进程之间的通信,aidl 安卓接口定义语言的使用 与 Messenger信使的使用
AIDL: android interface definition language 安卓接口定义语言 作用:可以调用另一个工程中的方法
在Service总结里面使用绑定服务的方法暴露了MyBind里面的方法
为了不暴露服务类中发方法,就需要一个中间人去调用服务里的方法,而我们就只需要这个中间人的对象,代码如下:
public class MyService extends Service { private class MyBind extends Binder implements InServiceBinder{ private void method(){ System.out.println("我是服务里面的方法"); } @Override public void inMethod() { method(); } } @Override public IBinder onBind(Intent intent) { return new MyBind(); } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } }
接口的定义:
public interface InServiceBinder { public void inMethod(); }
调用服务:
public class MainActivity extends Activity implements OnClickListener { public InServiceBinder binder;//调用服务方法的对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bind=(Button) findViewById(R.id.bind); Button unbind=(Button) findViewById(R.id.unbind); Button method=(Button) findViewById(R.id.method); bind.setOnClickListener(this); unbind.setOnClickListener(this); method.setOnClickListener(this); } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn);//避免了退出程序时因为没有取消绑定而程序报错 } public ServiceConnection conn=new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { System.out.println("杀死进程时调用"); } @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("连接了服务"); binder = (InServiceBinder) service;//转换成接口对象,去调用该接口的方法,service对象就是MyBind返回的对象 } }; @Override public void onClick(View v) { Intent intent=new Intent(this,MyService.class); switch (v.getId()) { case R.id.bind://绑定服务 bindService(intent, conn, BIND_AUTO_CREATE); break; case R.id.unbind://解除绑定 unbindService(conn); break; case R.id.method://调用服务里的方法 binder.inMethod();//这样就没有暴露MyService里的方法,而是通过接口来调用MyService里的方法 break; } } }
如果把MyBind类改为public,那么在Activity里面可以通MyBind对象调用该类的所有方法,可是通常我们不想把所有方法都暴露出来,或者不想暴露具体实现的步骤,就可以通过定义接口来实现这样的功能,该接口也就相当于中间人的作用,这样使得MyService类中的方法没有暴露出来,在上面的代码中还有一个问题,就是在解除绑定后还能调用服务中的方法,所以可以在解除绑定的时候把bind对象赋值为null并在使用时进行判断就可以了.
前面说的为本地服务,下面介绍远程服务
远程服务:调用者和服务在不同的工程代码里面。
本地服务:调用者和服务在同一个工程代码里面。
进程间进行通讯都是需要一块公共的空间的
绑定本地服务调用方法的步骤:
1.在服务的内部创建一个内部类 提供一个方法,可以间接调用服务的方法
private class MiddlePerson extends Binder implements IMiddlePerson{}
2.实现服务的onbind方法,返回的就是中间人 MiddlePerson
3.在activity 绑定服务。bindService();
4.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
5.强制类型转化 调用接口里面的方法。
绑定远程服务调用方法的步骤:
1.在服务的内部创建一个内部类 提供一个方法,可以间接调用服务的方法
2.把暴露的接口文件的扩展名改为aidl文件 去掉访问修饰符 public
private class MiddlePerson extends IMiddlePerson.Stub{} IPC的子类
3.实现服务的onbind方法,返回的就是中间人 IMiddlePerson
4.在activity 绑定服务。bindService();
5.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
6.IMiddlePerson.Stub.asInterface(binder) 调用接口里面的方法。
aidl文件都是公有的,没有访问权限修饰符
IPC: inter process communication 进程间通讯,在生成的类中里面就是利用了IPC实现两个工程的通讯
上面的那个例子是在自己的工程中创建一个服务,称为本地服务
具体实现两个工程通讯的代码:
1.先创建远程服务工程:在远程工程中创建一个服务
/* 定义远程服务的关键: 1.创建一个接口作为中间人,然后修改该接口的类型为.aidl文件,系统并会在gen目录中自动生成这个类,该类就是实现远程通信的关键 2.在另一个程序中需要创建一个和这个服务同包名,同一个aidl文件 3.在注册服务的时候需要定义一个action,这样才可以在不同的工程中绑定该服务 */ public class RemoteService extends Service { private class RemoteBind extends InRemote.Stub{ private void method(){ System.out.println("远程服务中的方法"); } @Override public void InRemotemethod() { method(); } } @Override public IBinder onBind(Intent intent) { return new RemoteBind(); } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } }
说明:InRemote.Stub的Stub为自定生成的类,那么怎么生成的?按照第一个例子创建接口,然后修改扩展名为.aidl文件
InRemote.aidl文件:
package com.example.ts.yuanc; interface InRemote { /** * 远程服务的接口方法 */ void InRemotemethod(); }
AndroidManifest.xml文件中注册服务
<service android:name=".RemoteService"> <intent-filter > <action android:name="com.ts.remoteService"/> //为了别的工程也能绑定该服务 </intent-filter> </service>
2.创建一个工程去绑定上面这个工程中的服务:
1.首先需要在该工程中创建一个与上面.aidl同名同包名同内容的文件 (复制上面的就是)
2.绑定远程服务:
public class MainActivity extends Activity implements OnClickListener { public InRemote binder;//远程服务通讯的对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bind=(Button) findViewById(R.id.bind); Button unbind=(Button) findViewById(R.id.unbind); Button method=(Button) findViewById(R.id.method); bind.setOnClickListener(this); unbind.setOnClickListener(this); method.setOnClickListener(this); } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn);//避免了退出程序时因为没有取消绑定而程序报错 } public ServiceConnection conn=new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { System.out.println("杀死进程时调用"); } @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("连接了服务"); binder=InRemote.Stub.asInterface(service);//得到远程服务的中间人对象 } }; @Override public void onClick(View v) { Intent intent=new Intent(); intent.setAction("com.ts.remoteService");//指定开启远程服务 switch (v.getId()) { case R.id.bind://绑定服务 bindService(intent, conn, BIND_AUTO_CREATE); break; case R.id.unbind://解除绑定 unbindService(conn); break; case R.id.method://调用远程服务里的方法 try { binder.InRemotemethod();//调用远程服务中的方法 break; } catch (RemoteException e) { e.printStackTrace(); } } } }
如果是使用studio实现:可以在module项目上直接右键添加aidl文件
跨进程不同应用之间的通信2-Messenger(信使)实现的:
服务端(注册了服务的)代码如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
上面只是用来显示个界面而已,下面看服务类:MyService
public class MyService extends Service { private final int service_message=0; private final int client_message=1; /** * messenger(信使)处理进程的通讯,handler处理线程的通信 * 声名一个服务端的Meesnger用来接收客服端的跨进程消息,通过 handler去处理消息 */ public Messenger serviceMessenger=new Messenger(new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case client_message://代表接收到的为客服端消息 int arg1 = msg.arg1; Log.i("tag","获取到客服端的数据:"+arg1); Messenger clientMessenger = msg.replyTo;//获取客服端对象 Message message = Message.obtain(); message.what=service_message;//代表这是服务端发过去的 message.arg1=100;//发送的数据 try { clientMessenger.send(message);//利用客服端对象发送消息到客服端的handler } catch (RemoteException e) { e.printStackTrace(); } break; } } }); @Override public IBinder onBind(Intent intent) { return serviceMessenger.getBinder(); } }
注册信息:
<service android:name=".MyService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.messenger.service"/> </intent-filter> </service>
客服端类:用来绑定上面的服务来实现数据之间的传递:
/** * 利用Messenger(信使)在进程中通信的原理: * 在客服端绑定服务,连接服务成功就能得到服务端的Messenger对象,就能利用这个对象发送数据给服务端 * 注意:在发送数据给服务端的时候需要携带自己客服端的Messenger对象,这样服务端才能拿到这个对象发送数据给客服端 * 也就是Messenger处理进程的通信,各自的handler再进行处理 */ public class MainActivity extends AppCompatActivity { private final int service_message=0; private final int client_message=1; private Messenger clientMessenger=new Messenger(new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case service_message://接收到服务端的消息 Log.i("tag","接收到服务端的消息:"+msg.arg1); break; } } }); private ServiceConnection serviceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //service为服务端的serviceMessenger.getBinder() Messenger serviceMessenger = new Messenger(service);//获取服务端的Messenger对象 Message message = Message.obtain(); message.what=client_message;//代表是客服端发过去的 message.arg1=1;//向服务端发送数据1 //需要携带客户端的 Messenger,这样服务端才能通过msg获取客服端对象发送消息 message.replyTo = clientMessenger;//切记要给个回应,服务那边才能收到这个对象 try { serviceMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent=new Intent(); intent.setAction("com.messenger.service"); bindService(intent,serviceConnection,BIND_AUTO_CREATE);//绑定另一个程序的服务 } @Override protected void onDestroy() { super.onDestroy(); unbindService(serviceConnection);//取消绑定,不然退出时会有异常 } }
总结:Messenger其实是封装了一下AIDL
1. Messenger本质也是AIDL,只是进行了封装,开发的时候不用再写.aidl文件。
结合我自身的使用,因为不用去写.aidl文件,相比起来,Messenger使用起来十分简单。但前面也说了,Messenger本质上也是AIDL,故在底层进程间通信这一块,两者的效率应该是一样的。
2. 在service端,Messenger处理client端的请求是单线程的,而AIDL是多线程的。
使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程(非主线程)去执行相应的操作。而Messenger,service收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。
3. client的方法,使用AIDL获取返回值是同步的,而Messenger是异步的。
Messenger只提供了一个方法进行进程间通信,就是send(Message msg)方法,发送的是一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的,而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。