之前上一篇讲解到本地服务,本地服务只能在自身APP中Activity访问Service,调用Service里面到方法等操作
如果想A应用访问B应用里面的方法,属于跨进程调用,如果Android不特供这种跨进程间通讯等API,是不能实现的
Google Android 为了解决 A应用--->B应用 跨进程访问通讯,提供了一种机制,就是IBInder,这种IBinder机制是Google工程师加入进去的,以前的Linux里是没有这个机制的
bindService 会返回IBinder接口,IBinder就是Google工程师在为了解决这种跨应用跨进程通讯,IBinder还需要结合Aidl才能实现远程服务(跨进程跨应用通讯)
注意:⚠️以前本地服务是通过显示意图去绑定,现在远程服务由于无法拿到另外应用的字节码,只能隐士意图去绑定
Service2,作为服务端,需要把MyService暴露出去
<!-- android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。 而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。 --> <service android:name=".service.MyService" android:exported="true" android:enabled="true" android:process=":remote"> <!-- 允许对外输出 --> <!-- 想要其他应用程序可以访问我的访问,就必须要对外暴漏 --> <intent-filter> <action android:name="liudeli.service2.service.MyService" /> </intent-filter> </service>
Service2,把逻辑都卸载MyService
package liudeli.service2.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import liudeli.service2.IMusician; import liudeli.service2.service.inter.ISinger; public class MyService extends Service { @Override public IBinder onBind(Intent intent) { // return new MyBinder(); 以前这种方式只适合本地服务(自身APP) return new MyBinderAidl(); } /** * 以前本地服务的方式 */ /*class MyBinder extends Binder implements ISinger { *//** * 通过ID查询歌手姓名 * * @param id * @return *//* @Override public String querySingerNameByID(int id) { String result = null; switch (id) { case 1: result = "黄家驹"; break; case 2: result = "王杰"; break; case 3: result = "陈百强"; break; default: result = "张雨生"; break; } return result; } }*/ /** * AIDL的方式,只有这种方式才能完成远程服务 */ class MyBinderAidl extends IMusician.Stub { @Override public String querySingerNameByID(int id) throws RemoteException { String result = null; int result2 = 0; switch (id) { case 1: result = "黄家驹"; result2 = 210000; break; case 2: result = "王杰"; result2 = 180000; break; case 3: result = "陈百强"; result2 = 200000; break; default: result = "张雨生"; result2 = 200090; break; } return result; } } }
Service2,以前本地服务接口的定义:
package liudeli.service2.service.inter; import java.lang.String; public interface ISinger { /** * 通过ID查询歌手姓名 * @param id * @return */ public String querySingerNameByID(int id); }
Service2,现在远程服务的AIdl语言接口的定义:
// IMusician.aidl package liudeli.service2; // Declare any non-default types here with import statements interface IMusician { /** * 通过ID查询歌手姓名 * @param id * @return */ String querySingerNameByID(int id); }
----------------------------- 分割线 下面代码是 Service1 访问者相关的了
Service1,⚠️要把Service2 aidl 带包一起copy到Service1,保持aidl相关一模一样
Service1,绑定远程服务相关代码
package liudeli.service1; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.widget.Toast; import liudeli.service2.IMusician; public class RemoteActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_remote); bindServiceAction(); } /** * Activity初始化的时候绑定服务 */ private void bindServiceAction() { Intent intent = new Intent(); intent.setAction("liudeli.service2.service.MyService"); // android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Component intent.setPackage("liudeli.service2"); bindService(intent, connection, BIND_AUTO_CREATE); } private IMusician iMusician; /** * 定义服务连接对象,用于连接远程服务 */ private ServiceConnection connection = new ServiceConnection() { /** * 服务连接成功 * @param name 可以获取到服务到完整信息 * @param service 服务那边返回到IBinder接口 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 这里和本地服务不同,本地服务是强制类型转换,远程服务直接使用代理完成 iMusician = IMusician.Stub.asInterface(service); } /** * 服务断开连接 * @param name */ @Override public void onServiceDisconnected(ComponentName name) { } }; /** * 查询远程服务里面的数据 * @param view */ public void queryRemoteService(View view) { if (null == iMusician) { Toast.makeText(this, "绑定远程服务失败,无法查询", Toast.LENGTH_LONG).show(); return; } try { String result = iMusician.querySingerNameByID(1); Toast.makeText(this, "查询成功:" + result, Toast.LENGTH_LONG).show(); } catch (RemoteException e) { e.printStackTrace(); Toast.makeText(this, "远程服务失败 远程异常", Toast.LENGTH_LONG).show(); } } /** * Activity结束时,记得一定要解绑服务,否则会报连接资源异常 */ @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }
Layout相关代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="queryRemoteService" android:text="查询远程服务" /> </LinearLayout>
结果: