Service之IPC远程通信

来源:互联网 发布:中青宝网络 编辑:程序博客网 时间:2024/04/29 11:18


一篇好的文章势必是可以用简短的文字就可以讲透一个知识点,所以我一般写文章都是把最本质的原理体现出来,如果你要阅读哪些详细的说明可以百度这个相关的技术点。跟着我的步骤操作我可以用最少的时间和精力让你用起这个技术点,看到效果,只有自己操作一遍之后看到效果了才可以学好一个技术点,我会把技术点的精华和本质给大家说明,同学们可以根据自己已有的知识去体会,融汇到以前学的知识中去,看看你现在学的知识点和以前的知识点有什么联系和不同,只有多思考这些才可以熟练的使用学到的知识点。

ok,阅读这篇文章前我希望你是对service有所了解的,起码要知道如何start一个Service和bind一个Service,如果你对这块知识不是很熟练,可以查看我的另外一篇文章:Service的基本使用,本篇文章是Service的高级应用,用来实现android的IPC远程通信,所谓远程通信就是不同的应用进程之间通信,一般android中一个app就是一个应用进程,所以本篇文章的效果是一个app调用另一个app中的Service方法并且被调用的app之前不是出于正在运行的状态。所以开始之前请你新建两个工程,一个用于被调用一个用于调用,这里我新建的被调用的app叫ServiceServer,调用者为ServiceClient。

一、Serviceserver

我们在这个app的src文件夹下新建一个包,这里我新建的包名为:com.xinxue.aidl,在这个包下面新建一个扩展名为aidl的文件,我的这个文件名为:IMyService.aidl,文件中的内容为:

package com.xinxue.aidl;import com.xinxue.aidl.Student;interface IMyService{List<Student> getStudent();void addStudent(in Student student);void printString( String msg);}
上面的package和import都是需要手动写的,这里的Student也是我在这个包下面新建的一个类。IMyService为一个接口,里面定义了3个方法,方法里面的参数如果不是基本数据类型需要使用in和out来标示这个参数是传入的还是传出的,显然我们这里是一个传入的参数,所以使用in来标示,如果是基本数据类型就不需要用这个来标示。ok,既然student是我们需要传输的对象,而我们知道java不允许直接传递一个自定义的对象除非这个对象实现了parcelable接口,很显然我们的student类也需要这么做。下面我们在这个包下面新建一个student类,实现parcelable接口,具体代码如下:

public class Student implements Parcelable {public int age;public String name;/** * 两个构成方法,一个私有化的只能内部使用 */private Student(Parcel in) {readFromParcel(in);}public Student() {}// 用来穿件对象的时候使用public static final Parcelable.Creator<Student> CREATOR = new Creator<Student>() {@Overridepublic Student[] newArray(int size) {return new Student[size];}@Overridepublic Student createFromParcel(Parcel source) {return new Student(source);}};// 直接返回0@Overridepublic int describeContents() {return 0;}// 保存对象@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(age);}// 获取对象public void readFromParcel(Parcel in) {name = in.readString();age = in.readInt();}@Overridepublic String toString() {return "Student [age=" + age + ", name=" + name + "]";}}
该类中readFromParcel方法和CREATOR是我们自己写的,其余的方法是重写方法,类里都有注释而且很简单这里就不啰嗦了,相信你可以看懂如果哪里不懂可以给我留言。

创建好了这个对象之后我们发现IMyService.aidl提示找不到Student类,这里我们还需要定义一个aidl文件用于描述我们的Student对象。在同一个包下面新建一个Student.aild,里面的代码如下:

package com.xinxue.aidl;parcelable Student;
非常简单的两行代码,其中第二行的parcelable为小写,用来描述Student对象时parcelable的子类。

做好以上的工作之后eclipse的编译工具就会自动给我们在gen文件下创建一个IMyService.java类,看下面截图:




然后在主包下面新建一个供远程调用的Service类,这里就是在上面图中的com.xinxue.serviceserver包下面,新建的文件为RemoteService.java,里面代码如下:

public class RemoteService extends Service {private List<Student> list = new ArrayList<Student>();// 这里使用到的就是自动生成的在gen文件夹下的java文类,重写里面的方法private IMyService.Stub mBinder = new Stub() {@Overridepublic List<Student> getStudent() throws RemoteException {return list;}@Overridepublic void addStudent(Student student) throws RemoteException {list.add(student);}@Overridepublic void printString(String msg) throws RemoteException {Log.e("需要输出的消息:", msg);}// 用来控制指定的应用才可以绑定,可以不重写public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)throws RemoteException {String[] packages = getPackageManager().getPackagesForUid(getCallingUid());if (packages != null && packages.length > 0) {String name = packages[0];if (!"com.example.serviceclient".equals(name)) {return false;}}return super.onTransact(code, data, reply, flags);};};@Overridepublic IBinder onBind(Intent intent) {// 返回IMyService对象return mBinder;}@Overridepublic void onCreate() {super.onCreate();// 初始化数据for (int i = 0; i < 6; i++) {Student student = new Student();student.age = i * 2 + 1;student.name = "小新";list.add(student);}}}

里面注释都很详细,代码也和Service的使用代码一直就不多说了,唯一需要指出的是onTransact()这个方法我们可以用来控制服务只能被指定的app调用。最后记得在manifest文件中注册:

        <service android:name="com.xinxue.serviceserver.RemoteService" >            <intent-filter>                <action android:name="com.xinxue.serviceserver.RemoteService" >                </action>                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </service>

到这里我们远程的app就写好了,下面就是写测试用的app了。

二、ServiceClient

这个类用来绑定远程的那个app,首先把我们在上面的app中新建的包和下面的所有文件都拷贝到本app的src文件夹下,然后只需要在MainActivity里面写上下面的代码就好了:

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}private ServiceConnection sConn = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 因为远程的onBind方法返回的是一个IMyService对象,所以这里可以直接转换为该对象IMyService myService = IMyService.Stub.asInterface(service);try {// 调用对象里面定义的方法myService.printString(myService.getStudent().get(0).toString());} catch (RemoteException e) {e.printStackTrace();}}};public void bindRemoteService(View view) {// 绑定远程ServiceIntent intent = new Intent("com.xinxue.serviceserver.RemoteService");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);bindService(intent, sConn, Service.BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();// 解绑if (sConn != null)unbindService(sConn);}}
代码其实和bindService的使用差不多,唯一不同的是在bind上后使用自动生成的IMyService.Stub.asInterface方法把ibinder对象转换为IMyService对象,然后就可以调用这个对象里面的方法了是不是很简单呢??下面把两个文件都按照到手机里面,打开ServiceClient这个app,点击按钮我们就可以看到效果了:


其实我们是传递自定义对象所以代码才有点多,要是只是传递基本数据类型简单的几行代码就可以搞定的。

扫描关注我的微信公众号:


总结:

Service的使用方式有两种,一种是本地服务,一种是远程服务,今天我们使用的就是远程服务这种情况,其实要实现IPC远程通信我们有两种方式,一种是现在说的还有一种方式是使用handler发消息去实现。远程服务使用其实很简单,我们只需要自定义一个aidl文件,在这个文件里面写代码其实和java的写法一致,而aidl文件里面我们只需要定义一个接口,接口里面定义的方法就是我们提供给远程调用的方法,远程服务和本地服务还有一个不同就是需要在manifest文件定义的时候指定一个action用于远程隐式的调用,这块知识使用起来不是很难的,如果还有什么问题可以给我留言,最后附上demo:下载



1 0
原创粉丝点击