Android AIDL Service 跨进程传递复杂数据
2015-07-15 13:48
633 查看
黑夜
黑夜给了我黑色的眼睛,我却用它寻找光明~传值方式
AIDL是允许跨进程传递值的,一般来说有三种方式:- 广播;这种算是比较常见的一种方式了,传递小数据不错
- 文件;这个是保存到文件中,然后读取,传递大数据不错
- Service Bind模式;这个算是居中的一种方式,不过效率要高的多,唯一麻烦的是编写代码较为麻烦。特别是复杂类型数据传递麻烦。
其是,还有一些其他的办法进行数据传递,另外传递也并不是只可以使用一种,可以采用几种结合的方式进行。
今天要说的就是Service Bind进行复杂数据的传递。
传递类型
在AIDL中之所以使用Bind进行传递会比较麻烦是因为:其在跨进程的情况下只允许传递如下类型数据:- String
- CharSequence
- android.os.Parcelable
- java.util.List
- java.util.Map
虽然可以使用 List与Map但是其类型一样不能使用复杂类型;当然上面 int、long、bool就没有单独写出来了。
如过要进行复杂数据传递,如传递User类的实例,此时就要使用Parcelable来辅助完成。
简单流程
Created with Raphaël 2.1.0A进程数据User在A进程把User打包为Parcelable在B进程把Parcelable解包为UserB进程数据User其调用方式依然为:绑定服务->得到目标服务的Binder->调用对应方法。
跨进程传递数据麻烦就在于打包/解包Parcelable的操作。
目标:开启一个独立进程的服务,在主进程中绑定目标服务,调用服务的方法。
实现:将需要进行复杂传递的数据类,继承Parcelable,并实现其中的序列化与反序列化方法。
传递类
传递类包括两个:User.java、User.aidl其中java类是具体的实现,aidl文件仅仅只是用于对java文件的声明。告知进程其可以看作Parcelable处理。
User.Java
package net.qiujuer.sample.service.bean; import android.os.Parcel; import android.os.Parcelable; import java.util.UUID; /** * Created by qiujuer on 15/7/15. */ public class User implements Parcelable { private UUID id; private int age; private String name; public User(int age, String name) { this.age = age; this.name = name; this.id = UUID.randomUUID(); } protected User(Parcel in) { // Id long m = in.readLong(); long l = in.readLong(); id = new UUID(m, l); age = in.readInt(); name = in.readString(); } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // ID long m = id.getMostSignificantBits(); long l = id.getLeastSignificantBits(); dest.writeLong(m); dest.writeLong(l); dest.writeInt(age); dest.writeString(name); } @Override public String toString() { return "Id:" + id.toString() + " Age:" + age + " Name:" + name; } }
在类中,包含三个属性,两个基本类型,一个UUID,对于基本类型可以直接序列化,而UUID则不能,此时两个方案,一种是把UUID看作String进行处理,当解包时则把String转换为UUID即可。
第二种则是得到其中关键的数据,分别是两个long值,然后对long值进行传递,并反序列化。
在类中,我们实现了Parcelable接口,则需要完成两个方法与一个静态值操作。
-
describeContents为描述方法,通常返回0
-
writeToParcel具体的写入操作,在这里对需要传输的数据进行写入,请一定需要注意的是写入顺序则决定了读取顺序。
-
CREATOR此静态属性,则是为了反编译时使用,在Parcelable进行反编译时将会调用该属性,所以名称写法基本是固定不变的。
-
User在该类的构造函数中,我们对其进行了反序列化读取数据操作。
User.aidl
// User.aidl package net.qiujuer.sample.service.bean; parcelable User;
在该文件中,只需要两行代码就OK,一个指定包名,一个为parcelable申明。
# Service类
这个部分主要包含两个文件,一个AIDL申明文件
IServiceAidlInterface.aidl
// IServiceAidlInterface.aidl package net.qiujuer.sample.service; import net.qiujuer.sample.service.bean.User; // Declare any non-default types here with import statements interface IServiceAidlInterface { void addAge(); void setName(String name); User getUser(); }
在该文件中,定义了一个接口,其中有一个方法getUser(),该方法返回服务中的User类的实例;当然需要使用该类所以需要加上
import net.qiujuer.sample.service.bean.User;
IndependentService.java
package net.qiujuer.sample.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import net.qiujuer.sample.service.bean.User; public class IndependentService extends Service { private ServiceBinder mBinder; public IndependentService() { } @Override public IBinder onBind(Intent intent) { if (mBinder == null) mBinder = new ServiceBinder(); return mBinder; } class ServiceBinder extends IServiceAidlInterface.Stub { private User user; public ServiceBinder() { user = new User(21, "XiaoMing"); } @Override public void addAge() throws RemoteException { user.setAge(user.getAge() + 1); } @Override public void setName(String name) throws RemoteException { user.setName(name); } @Override public User getUser() throws RemoteException { return user; } } }
该类为具体的服务实现,在该服务中实现了服务接口,并进行了简单实现。
文件梳理与配置
文件结构
独立进程配置
由于我们需要让服务为独立进程,所以需要在AndroidManifest文件中Service申明的地方加上process属性:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.qiujuer.sample.service"> <application android:allowBackup="true" android:label="@string/app_name"> <service android:name=".IndependentService" android:enabled="true" android:exported="true" android:permission="1000" android:process=":AidlService" /> </application> </manifest>
在这里我设置的是:android:process=”:AidlService”
使用
在APP Model中,我建立了一个MainActivity,并在其中调用服务的方法。核心代码
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); private TextView mText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView) findViewById(R.id.txt_str); bindService(); } @Override protected void onDestroy() { super.onDestroy(); unBindService(); } private IServiceAidlInterface mService = null; private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mService = IServiceAidlInterface.Stub.asInterface(iBinder); if (mService != null) run(); else showText("Bind Error."); } @Override public void onServiceDisconnected(ComponentName componentName) { mService = null; } }; private void bindService() { // UnBind unBindService(); Intent intent = new Intent(this, IndependentService.class); bindService(intent, mConn, Context.BIND_AUTO_CREATE); } private void unBindService() { // Service IServiceAidlInterface service = mService; mService = null; if (service != null) { unbindService(mConn); } } private void run() { User user = null; try { user = mService.getUser(); showText(user.toString()); mService.addAge(); user = mService.getUser(); showText(user.toString()); mService.setName("FangFang"); user = mService.getUser(); showText(user.toString()); } catch (RemoteException e) { e.printStackTrace(); showText(e.toString()); } } private void showText(String str) { Log.d(TAG, str); mText.append("\n"); mText.append(str); } }
打印日志
MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:21 Name:XiaoMingMainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:XiaoMing
MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:FangFang
进程
可以看出,服务的进程的确是独立于主进程的。
引申
在这里,我们是传递的一个User,假如传递的User中又包含一个Account类呢?Account中又包含其他的类呢?这个该如何办?有两种办法:
第一种:使用上面UUID传递类似的方式,得到其中的核心数据,然后传递,在解包时进行还原。
第二种:将类也实现Parcelable接口,这样就能完美的解决了。
在这里简单写一下第二种的代码:
Account.java
public class Account implements Parcelable { private String name; protected Account(Parcel in) { name = in.readString(); } public static final Creator<Account> CREATOR = new Creator<Account>() { @Override public Account createFromParcel(Parcel in) { return new Account(in); } @Override public Account[] newArray(int size) { return new Account[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); } }
User.java
这里只写改动部分;其实这些改动的东西,完全可以借助编译工具自动生成,你只需要写好属性,然后继承接口;然后让编译工具帮助你完成接口对应方法就OK。
public class User implements Parcelable { private Account account; protected User(Parcel in) { ... account = in.readParcelable(Account.class.getClassLoader()); } @Override public void writeToParcel(Parcel dest, int flags) { ... dest.writeParcelable(account, flags); } }
另外需要注意的是,在aidl中申明类,仅仅只需要申明aidl接口中需要传递的类就OK,在这里直接传递的类只有User,所以只需要写一个User.aidl文件就OK,就算User中包含了Account类,但不需要写Account.aidl文件来申明。
代码
当然,源码肯定是会有的:AidlService 源码
写在最后
最近没事儿捣鼓了一个APP[UPMiss],一个简单的生日,纪念日提醒软件;欢迎大家尝鲜。{UPMiss} 思念你的夏天
下载地址:
魅族
百度 新版还在审核中!
豌豆荚 新版还在审核中!
========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:github.com/qiujuer/Genius-Android
开源库:github.com/qiujuer/Blink
转载请注明出处:/article/1531473.html
—— 学之开源,用于开源;初学者的心态,与君共勉!
========================================================
相关文章推荐
- 不翻墙一样更新Android SDK(转,已亲身测试确认)
- Android 常见知识整理(1)
- mac os使用lsusb命令和连接未知的Android设备
- Android获取本地图片和相册
- android ndk
- android访问php webservice简单一例
- Android对话框的生成
- android--面向开发者的即时通讯SDK对比分析
- android通过apktool之反编译 重新打包
- Android的缓存机制——图片的缓存
- android逆向分析之smali练习
- Android Studio Mac 10.5+快捷键使用
- 代码二次封装-xUtils(android)
- Android 最火的快速开发框架AndroidAnnotations使用详解
- android 开源框架xUtils
- Android 利用Theme自定义Activity间的切换动画
- Android移动view动画问题
- Android反编译代码和防止反编译
- Android入门(54)——第九章 使用GestureOverlayView进行手势识别
- android之XListview,上拉下拉出现BUG