Service之IPC远程通信


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

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>() {

		@Override
		public Student[] newArray(int size) {
			return new Student[size];
		}

		@Override
		public Student createFromParcel(Parcel source) {
			return new Student(source);
		}
	};

	// 直接返回0
	@Override
	public int describeContents() {
		return 0;
	}

	// 保存对象
	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeString(name);
		dest.writeInt(age);
	}

	// 获取对象
	public void readFromParcel(Parcel in) {

		name = in.readString();
		age = in.readInt();

	}

	@Override
	public 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() {

		@Override
		public List<Student> getStudent() throws RemoteException {
			return list;
		}

		@Override
		public void addStudent(Student student) throws RemoteException {
			list.add(student);
		}

		@Override
		public 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);
		};
	};

	@Override
	public IBinder onBind(Intent intent) {
		// 返回IMyService对象
		return mBinder;
	}

	@Override
	public 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 {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

	}

	private ServiceConnection sConn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {

		}

		@Override
		public 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) {
		// 绑定远程Service
		Intent intent = new Intent("com.xinxue.serviceserver.RemoteService");
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		bindService(intent, sConn, Service.BIND_AUTO_CREATE);
	}

	@Override
	protected 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值