Android Service的学习,AIDL传递对象
Service有两种:
1,本地服务(Local Service):用于应用程序内部
2,远程服务(Remote Service):用于android系统内部的应用程序之间 。
使用区别:
本地服务:主要是平时做一些耗时,或者要长时间运行,影响UI线程的时候到到。如,播放音乐,下载等。
远程服务:则用于多应用之间的相互访问。比如做个天气预报的,做个健康数据的。
一开始我们学习使用Service可能会是因为播放多音乐的时候,用户启动了其他Activity这个时候程序要在后台继续播放。我们把MediaPlayer在Local Service里。然后通过bindService()在ServiceConnection里通过IBinder返回一个Service的实例。通过Service实例去操作MediaPlayer的开始和停止等等。例如这样:
Intent intent = new Intent(mContext, TestService.class);
mContext.bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mTestService = ((TestService.MyBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mTestService = null;
}
};
当某天用到远程Service的时候,上面这种方式报错了。
将一个普通的Service转换成远程Service其实非常简单,只需要在注册Service的时候将它的android:process属性指定成:remote就可以了。当然这个remote是自定义的。
为什么这里用个“:”呢?
如果被设置的进程名是以一个冒号开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以共享一个进程,从而减少资源的占用。
当一个Service被设置为Remote Service时候我们又怎么通信呢?
我们再复习下线程通信与进程通信
线程通信:
- 1)共享变量(内存)
- 2)管道
- 3)handle机制 平时熟悉的runOnUiThread,view.post(Runnable)都是Handle机制
进程通信:
- 1)bind机制(IPC->AIDL)
- 2)Content Provider
- 3)Broadcast
- 4)Intent (有没试过用Intent打开别的应用。。)
这篇文章主要学习AIDL,AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。关于AIDL是怎么实现跨进程通信的,网上不少资料,关心的小伙伴可以自己查阅。强大的Android Studio为开发者省了很大的工夫。
第一步:创建AIDL:右键New-选择AIDL,输入你的接口名如IMyAidlInterface,点击Finish。
IDE会在src->main目录下新建一个aidl目录与java同级的。
第二步:自定义方法
interface IMyAidlInterface {
void addStudent(inout Student student);
void showTip(String tip);
}
第三步:Rebuild Project
在项目的 build/generated/source/aidl/debug/包名/下能找到IDE帮你生成的接口
第四步:在Service里返回一个IMyAidlInterface.Sub
“`
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
private class MyBinder extends IMyAidlInterface.Stub{
@Override
public void add(Student student) throws RemoteException {
}
}
第五步:在BindService()
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyAidlInterface = null;
}
};
“`
通过上边的五步我们就拿到了一个IMyAidlInterface实例。这时候可能欢呼了。
当你通过这个接口传一个对象过去的时候,崩了!!!!
查看资料发现AIDL默认支持的数据类型包括:
Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
String 类型。
CharSequence类型。
List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
AIDL传送对象
由于Aidl只支持Java基本类型数据传递,因此是不能直接传递一个复杂类型对象的,所以为了解决这个问题,Android提供了一套机制—-将需要传递的对象序列化,然后在反序列化。
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
第一步:创建一个Student类 implements Parcelable。按提示添加所有方法。(鼠标移动类名ALT+Enter–>add Parcelable implementation)
public class Student implements Parcelable {
int id;
String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
protected Student(Parcel in) {
id = in.readInt();
name = in.readString();
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
public void readFromParcel(Parcel source) {
id = source.readInt();
name = source.readString();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name=" + name +
'}';
}
}
第二步:在AIDL目录下里和你类放的路径一样的地方创建一个Student.AIDL
package com.example.yang.testaidl;
parcelable Student; //parcelable 要小写,网上说的,我也不明真相。
- 注意目录一定要相同
例如,你在java/包名/beans/下创建的Student 你在aidl/包名/下再创建一个beans文件夹再这个beans下创建Student.aidl。要不然之前定义的IMyAidlInterface引用Student会报错。。。。
第三步:在IMyAidlInterface里加上
import com.example.yang.testaidl.Student;
interface IMyAidlInterface {
void addStudent(inout Student student);
void showTip(String tip);
}
这里传对象用的是in/out/inout
Parcelable 还有这个方法是隐式的要自己写上来。这样就实现out了
public void readFromParcel(Parcel source) {
id = source.readInt();
name = source.readString();
}
好了。到了这里基本学习完毕。边学边写,有不足之处望批评指正。附Demo
https://github.com/YangKee/TestAIDL.git