欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android使用Service实现IPC通信的2种方式

程序员文章站 2022-05-15 15:19:57
借助aidl实现ipc通信 一、代码实操---与远端进程的service绑定 上面的代码都是在当前进程内跟service通信,现在我们来实现一下,不同进程内se...

借助aidl实现ipc通信

一、代码实操---与远端进程的service绑定

上面的代码都是在当前进程内跟service通信,现在我们来实现一下,不同进程内service如何绑定。

aidl:android interface definition language,即android接口定义语言。

service跨进程传递数据需要借助aidl,主要步骤是这样的:

  1. 编写aidl文件,as自动生成的java类实现ipc通信的代理
  2. 继承自己的aidl类,实现里面的方法
  3. 在onbind()中返回我们的实现类,暴露给外界
  4. 需要跟service通信的对象通过bindservice与service绑定,并在serviceconnection接收数据。

我们通过代码来实现一下:

1、首先我们需要新建一个service

public class myremoteservice extends service {
 @nullable
 @override
 public ibinder onbind(intent intent) {
  log.e("myremoteservice", "myremoteservice thread id = " + thread.currentthread().getid());
  return null;
 }
}

2、在manifest文件中声明我们的service同时指定运行的进程名,这里并是不只能写remote进程名,你想要进程名都可以

<service
    android:name=".service.myremoteservice"
    android:process=":remote" />

3、新建一个aidl文件用户进程间传递数据。

aidl支持的类型:八大基本数据类型、string类型、charsequence、list、map、自定义类型。list、map、自定义类型放到下文讲解。

Android使用Service实现IPC通信的2种方式

里面会有一个默认的实现方法,删除即可,这里我们新建的文件如下:

package xxxx;//aidl所在的包名
//interface之前不能有修饰符
interface iprocessinfo {
 //你想要的通信用的方法都可以在这里添加
 int getprocessid();
}

4、实现我们的aidl类

public class iprocessinfoimpl extends iprocessinfo.stub {
 @override
 public int getprocessid() throws remoteexception {
  return android.os.process.mypid();
 }
}

5、在service的onbind()中返回

public class myremoteservice extends service {
 iprocessinfoimpl mprocessinfo = new iprocessinfoimpl();
 @nullable
 @override
 public ibinder onbind(intent intent) {
  log.e("myremoteservice", "myremoteservice thread id = " + thread.currentthread().getid());
  return mprocessinfo;
 }
}

6、绑定service

 mtvremotebind.setonclicklistener(new view.onclicklistener() {
    @override
    public void onclick(view v) {
      intent intent = new intent(mainactivity.this, myremoteservice.class);
      bindservice(intent, mremoteserviceconnection, bind_auto_create);
    }
  });


mremoteserviceconnection = new serviceconnection() {
    @override
    public void onserviceconnected(componentname name, ibinder service) {

      log.e("mainactivity", "myremoteservice onserviceconnected");
  // 通过aidl取出数据
      iprocessinfo processinfo = iprocessinfo.stub.asinterface(service);
      try {
        log.e("mainactivity", "myremoteservice process id = " + processinfo.getprocessid());
      } catch (remoteexception e) {
        e.printstacktrace();
      }
    }

    @override
    public void onservicedisconnected(componentname name) {
      log.e("mainactivity", "myremoteservice onservicedisconnected");
    }
  };

只要绑定成功就能在有log打印成myremoteservice所在进程的进程id。这样我们就完成了跟不同进程的service通信的过程。

二、代码实操---调用其他app的service

跟调同app下不同进程下的service相比,调用其他的app定义的service有一些细微的差别

1、由于需要其他app访问,所以之前的bindservice()使用的隐式调用不在合适,需要在service定义时定义action
我们在定义的线程的app a 中定义如下service:

<service android:name=".service.serverservice">
 <intent-filter>
 //这里的action自定义
   <action android:name="com.jxx.server.service.bind" />
   <category android:name="android.intent.category.default" />
  </intent-filter>
</service>

2、我们在需要bindservice的app b 中需要做这些处理

  • 首先要将a中定义的aidl文件复制到b中,比如我们在上面定义的iprocessinfo.aidl这个文件,包括路径在内需要原封不动的复制过来。
  • 在b中调用service通过显式调用
mtvserverbind.setonclicklistener(new view.onclicklistener() {
  @override
  public void onclick(view v) {
    intent intent = new intent();
    intent.setaction("com.jxx.server.service.bind");//service的action
    intent.setpackage("com.jxx.server");//app a的包名
    bindservice(intent, mserverserviceconnection, bind_auto_create);
  }
});

aidl中自定义对象的传递

主要步骤如下:

  1. 定义自定对象,需要实现parcelable接口
  2. 新建自定义对象的aidl文件
  3. 在传递数据的aidl文件中引用自定义对象
  4. 将自定义对象以及aidl文件拷贝到需要bindservice的app中,主要路径也要原封不动

我们来看一下具体的代码:

1、定义自定义对象,并实现parcelable接口

public class serverinfo implements parcelable {

public serverinfo() {

}

string mpackagename;

public string getpackagename() {
  return mpackagename;
}

public void setpackagename(string packagename) {
  mpackagename = packagename;
}

protected serverinfo(parcel in) {
  mpackagename = in.readstring();
}

public static final creator<serverinfo> creator = new creator<serverinfo>() {
  @override
  public serverinfo createfromparcel(parcel in) {
    return new serverinfo(in);
  }

  @override
  public serverinfo[] newarray(int size) {
    return new serverinfo[size];
  }
};

@override
public int describecontents() {
  return 0;
}

@override
public void writetoparcel(parcel dest, int flags) {
  dest.writestring(mpackagename);
}

//使用out或者inout修饰时需要自己添加这个方法
public void readfromparcel(parcel dest) {
  mpackagename = dest.readstring();
}
}

2、新建自定义对象的aidl文件

package com.jxx.server.aidl;
//注意parcelable 是小写的
parcelable serverinfo;

3、引用自定义对象

package com.jxx.server.aidl;
//就算在同一包下,这里也要导包
import com.jxx.server.aidl.serverinfo;
interface iserverserviceinfo {
 serverinfo getserverinfo();
 void setserverinfo(inout serverinfo serverinfo);
}

注意这里的set方法,这里用了inout,一共有3种修饰符
- in:客户端写入,服务端的修改不会通知到客户端
- out:服务端修改同步到客户端,但是服务端获取到的对象可能为空
- inout:修改都收同步的

当使用out和inout时,除了要实现parcelable外还要手动添加readfromparcel(parcel dest)

4、拷贝自定义对象以及aidl文件到在要引用的app中即可。

5、引用

mserverserviceconnection = new serviceconnection() {
    @override
    public void onserviceconnected(componentname name, ibinder service) {
      iserverserviceinfo serverserviceinfo = iserverserviceinfo.stub.asinterface(service);
      try {
        serverinfo serviceinfo = serverserviceinfo.getserverinfo();
        log.e("mainactivity", "serverservice packagename = " + serviceinfo.getpackagename());
      } catch (remoteexception e) {
        e.printstacktrace();
      }
    }

    @override
    public void onservicedisconnected(componentname name) {
      log.e("mainactivity", "serverservice onservicedisconnected");
    }
  };

list、map中引用的对象也应该是符合上面要求的自定义对象,或者其他的几种数据类型。

使用messenger实现ipc通信

步骤是这样的:

  1. 在server端新建一个messenger对象,用于响应client端的注册操作,并在onbind()中传递出去
  2. 在client端的serviceconnection中,将server端传递过来的messenger对象进行保存
  3. 同时client端也新建一个messenger对象,通过server传递过来的messenger注册到server端,保持通信用。
  4. 不管是否进行unbindservice()操作,只要client保有server端的messenger对象,仍然能和server端进行通信。

一、server端代码

public class messengerservice extends service {

  static final int msg_register_client = 1;
  static final int msg_unregister_client = 2;
  static final int msg_set_value = 3;

  //这个是给client端接收参数用的
  static final int msg_client_set_value = 4;

  static class servicehandler extends handler {

    private final list<messenger> mmessengerlist = new arraylist<>();

    @override
    public void handlemessage(message msg) {
      switch (msg.what) {
        case msg_register_client:
          mmessengerlist.add(msg.replyto);
          break;
        case msg_unregister_client:
          mmessengerlist.remove(msg.replyto);
          break;
        case msg_set_value:
          int value = msg.arg1;
          for (messenger messenger : mmessengerlist) {
            try {
              messenger.send(message.obtain(null, msg_client_set_value, value, 0));
            } catch (remoteexception e) {
              e.printstacktrace();
            }
          }
          break;
        default:
          super.handlemessage(msg);
      }
    }
  }

  private messenger mmessenger = new messenger(new servicehandler());

  @nullable
  @override
  public ibinder onbind(intent intent) {
    return mmessenger.getbinder();
  }
}

二、client端代码

public class messengerclientactivity extends appcompatactivity {
 //这些类型要和server端想对应
  static final int msg_register_client = 1;
  static final int msg_unregister_client = 2;
  static final int msg_set_value = 3;
  static final int msg_client_set_value = 4;

  class clienthandler extends handler {

    @override
    public void handlemessage(message msg) {

      if (msg.what == msg_client_set_value) {
        mtvvalue.settext(msg.arg1 + "");
      } else {
        super.handlemessage(msg);
      }
    }
  }

  textview mtvserverbind;
  textview mtvserverunbind;
  textview mtvvalue;
  textview mtvsend;

  serviceconnection mserverserviceconnection;
  messenger mservermessenger;

  @override
  protected void oncreate(@nullable bundle savedinstancestate) {
    super.oncreate(savedinstancestate);

    setcontentview(r.layout.activity_messenger);

    mtvserverbind = findviewbyid(r.id.tv_server_bind);
    mtvserverunbind = findviewbyid(r.id.tv_server_unbind);
    mtvvalue = findviewbyid(r.id.tv_value);
    mtvsend = findviewbyid(r.id.tv_send);

    mtvserverbind.setonclicklistener(new view.onclicklistener() {
      @override
      public void onclick(view v) {
        intent intent = new intent();
        intent.setaction("jxx.com.server.service.messenger");
        intent.setpackage("jxx.com.server");
        bindservice(intent, mserverserviceconnection, bind_auto_create);
      }
    });

    mtvserverunbind.setonclicklistener(new view.onclicklistener() {
      @override
      public void onclick(view v) {
        //就算这里我们unbindservice,只要我们还保留有mservermessenger对象,
        //我们就能继续与server通信
        unbindservice(mserverserviceconnection);
      }
    });

    mtvsend.setonclicklistener(new view.onclicklistener() {
      @override
      public void onclick(view v) {
        if (mservermessenger != null) {
          try {
            //测试一下能否设置数据
            message test = message.obtain(null, msg_set_value, new random().nextint(100), 0);
            mservermessenger.send(test);
          } catch (remoteexception e) {
            e.printstacktrace();
          }
        }
      }
    });

    mserverserviceconnection = new serviceconnection() {
      @override
      public void onserviceconnected(componentname name, ibinder service) {
        //服务端的messenger
        mservermessenger = new messenger(service);

        //现在开始构client用来传递和接收消息的messenger
        messenger clientmessenger = new messenger(new clienthandler());

        try {
          //将client注册到server端
          message register = message.obtain(null, msg_register_client);
          register.replyto = clientmessenger;//这是注册的操作,我们可以在上面的server代码看到这个对象被取出
          mservermessenger.send(register);

          toast.maketext(messengerclientactivity.this, "绑定成功", toast.length_short).show();

        } catch (remoteexception e) {
          e.printstacktrace();
        }
      }

      @override
      public void onservicedisconnected(componentname name) {

      }
    };
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。