Android AIDL Service 跨進程傳遞復雜數據
黑夜
黑夜給了我黑色的眼睛,我卻用它尋找光明~
傳值方式
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來輔助完畢。
簡單流程
其調用方式依舊為:綁定服務->得到目標服務的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:XiaoMing
MainActivity﹕ 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} 思念你的夏天
下載地址:
- 魅族
docid=7821362&from=web_alad_5&f=search_app_UPMiss@list_1_title@1@header_app_input">百度 新版還在審核中!
- 豌豆莢 新版還在審核中!
========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
站點:www.qiujuer.net
開源庫:github.com/qiujuer/Genius-Android
開源庫:github.com/qiujuer/Blink
轉載請註明出處:http://blog.csdn.net/qiujuer/article/details/46885987
—— 學之開源,用於開源;剛開始學習的人的心態。與君共勉。
========================================================
Android AIDL Service 跨進程傳遞復雜數據