Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL
服务端:
最终项目结构:

这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据。
Step 1:创建CustomData类
package com.ldb.android.example.aidl; import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log; import java.util.ArrayList;
import java.util.Date;
import java.util.List; /**
* Created by lsp on 2016/9/1.
*/
public class CustomData implements Parcelable { private static final String TAG = "CustomData"; private String mName;
private List<String> mReference;
private Date mCreated; public CustomData(){
mName = "";
mReference = new ArrayList<>();
mCreated = new Date();
} public String getName() {
return mName;
} public void setName(String name) {
mName = name;
} public List<String> getReference() {
return mReference;
} public void setReference(List<String> reference) {
mReference = reference;
} public Date getCreated() {
return mCreated;
} public void setCreated(Date created) {
mCreated = created;
} @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeStringList(mReference);
dest.writeLong(mCreated.getTime());
} @Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
CustomData that = (CustomData) o;
return mCreated.equals(that.mCreated) && mName.equals(that.mName);
} @Override
public int hashCode() {
int result = mName.hashCode();
result = 31 * result + mCreated.hashCode();
return result;
} public static final Parcelable.Creator<CustomData> CREATOR = new Parcelable.Creator<CustomData>(){
@Override
public CustomData createFromParcel(Parcel source) {
CustomData customData = new CustomData();
customData.mName = source.readString();
// customData.mReference = new ArrayList<>();
source.readStringList(customData.mReference);
Long created = source.readLong();
Log.d(TAG, "createFromParcel " + created);
customData.mCreated = new Date(created);
return customData;
} @Override
public CustomData[] newArray(int size) {
return new CustomData[size];
}
};
}
为了实现进程间传递,CustomData 需要实现接口Parcelable,writeToParcel()方法和CREATOR是不可少的。
Step 2:创建CustomData类对应的aidl文件, 不过aidl文件先任意命名,不能是CustomData,否则Android Studio不让继续执行。创建完之后再对aidl重命名为CustomData.aidl。注意此aidl文件的package与CustomData的package要保持一致。模块名app上右键-->new-->AIDL,生成文件后重命名,然后修改文件内容为:
// CustomData.aidl
package com.ldb.android.example.aidl; parcelable CustomData;
Step 3:继续生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件,修改文件内容为:
AidlCallback.aidl:
// AidlCallback.aidl
package com.ldb.android.example.aidl; // Declare any non-default types here with import statements
import com.ldb.android.example.aidl.CustomData; oneway interface AidlCallback {
void onDataUpdated(in CustomData[] data);
}
ApiInterfaceV1.aidl:
// ApiInterfaceV1.aidl
package com.ldb.android.example.aidl; // Declare any non-default types here with import statements
import com.ldb.android.example.aidl.CustomData;
import com.ldb.android.example.aidl.AidlCallback; interface ApiInterfaceV1 {
boolean isPrime(long value);
void getAllDataSince(long timestamp, out CustomData[] result);
void storeData(in CustomData data);
void setCallback(in AidlCallback callback);
}
Step 4:菜单 Build --> Make Project 或者 Rebuild Project,如果顺利的话,就能够自动生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件对应的.java文件。
在编译目录下,如我的目录是AidlService\app\build\generated\source\aidl\... 下有AidlCallback.java和ApiInterfaceV1.java两个文件。或者在创建服务的时候再进行验证。
Step 5:创建服务类AidlService:
package com.ldb.android.example.aidlservice; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log; import com.ldb.android.example.aidl.AidlCallback;
import com.ldb.android.example.aidl.ApiInterfaceV1;
import com.ldb.android.example.aidl.CustomData; import java.util.ArrayList;
import java.util.Date; /**
* Created by lsp on 2016/9/1.
*/
public class AidlService extends Service { private static final String TAG = "AidlService"; private ArrayList<CustomData> mCustomDataCollection;
private AidlCallback mCallback; @Override
public void onCreate() {
super.onCreate();
mCustomDataCollection = new ArrayList<>();
// TODO Populate the list with stored value...
} @Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
} @Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
} private static boolean isPrimeImpl(long number) {
// Implementation left out for brevity...
return false;
} private void getDataSinceImpl(CustomData[] result, Date since) {
int size = mCustomDataCollection.size();
Log.d(TAG, "getDataSinceImpl size = " + size);
Log.d(TAG, "since: " + since);
int pos = 0;
for (int i = 0; i < size && pos < result.length; i++) {
CustomData storedValue = mCustomDataCollection.get(i);
Log.d(TAG, "storedValue " + i + ": " + storedValue.getCreated());
if (since.before(storedValue.getCreated())) {
Log.d(TAG, "add " + i);
result[pos++] = storedValue;
}
}
} private void storeDataImpl(CustomData data) {
int size = mCustomDataCollection.size();
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < size; i++) {
CustomData customData = mCustomDataCollection.get(i);
if (customData.equals(data)) {
mCustomDataCollection.set(i, data);
return;
}
}
mCustomDataCollection.add(data);
} private final ApiInterfaceV1.Stub mBinder = new ApiInterfaceV1.Stub() {
@Override
public boolean isPrime(long value) throws RemoteException {
return isPrimeImpl(value);
} @Override
public void getAllDataSince(long timestamp, CustomData[] result) throws RemoteException {
getDataSinceImpl(result, new Date(timestamp));
} @Override
public void storeData(CustomData data) throws RemoteException {
Log.d(TAG, data.getName() + " -- " + data.getCreated());
storeDataImpl(data);
if(mCallback != null){
mCallback.onDataUpdated(new CustomData[]{data});
}
} @Override
public void setCallback(AidlCallback callback) throws RemoteException {
mCallback = callback;
mCallback.asBinder().linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG, "binderDied");
mCallback = null;
}
}, 0);
}
}; }
服务类中AidlCallback 和 ApiInterfaceV1 分别对应上一步的AidlCallback.java和ApiInterfaceV1.java,与客户端进行通信的就是mBinder,mBinder继承了ApiInterfaceV1.Stub,ApiInterfaceV1.Stub是上一步自动生成的一个类, 查看它的代码,ApiInterfaceV1.Stub实际就是一个Binder,同时它实现了接口ApiInterfaceV1,但没有实现ApiInterfaceV1具体的方法,因此它还是个抽象类,具体实现就得由我们在服务类中完成。而Binder在服务端正是通过onTransact(...)这个方法进行接收客户端的调用的(客户端则是调用transact(...)方法)。
因此服务端要完成的操作是:
1、定义Aidl文件。
2、IDE自动生成Aidl文件对应的java文件。
3、在服务类中定义一个成员变量,这个成员变量是上一步java文件中生成的Stub的一个实例,并且由我们实现Aidl文件中定义的接口方法。
4、在onBind()方法中返回此成员变量。
5、在AndroidManifest.xml文件中声明服务,并且在<inten-filter>中定义<action android.name="..." />,这样客户端可通过此action定位此服务。
客户端:
最终项目结构:
运行效果,三个按钮对应服务的三个方法。
Step 1:将服务端的Aidl文件和CustomData.java文件拷贝到客户端,注意保持package与服务端一致。
Step 2:菜单 Build --> Make Project 或者 Rebuild Project,如果顺利的话,就能够自动生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件对应的.java文件。
Step 3:实现回调接口AidlCallback.Stub,并定义一个此实现的变量作为客户端成员变量,用于给服务端设置回调。
// Implement the callback
mAidlCallback = new AidlCallback.Stub() {
@Override
public void onDataUpdated(final CustomData[] data) throws RemoteException {
Log.d(TAG, data[0].getName() + " was updated");
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, data[0].getName() + " was updated");
Toast.makeText(MainActivity.this, data[0].getName() + " was updated",
Toast.LENGTH_SHORT).show();
}
});
}
};
Step 4:实现接口ServiceConnection,这步是使用Binder进行服务通信必须做的一件事,因为服务端onBind()传出的Binder,最终作为onServiceConnected(ComponentName name, IBinder service)的参数传到客户端。在此方法的实现中,通过ApiInterfaceV1.Stub.asInterface(service)可得到服务端的代理对象。
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ApiInterfaceV1.Stub.asInterface(service);
try {
mService.setCallback(mAidlCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Step 5:bindService,通过Intent并指定action(与服务端设置的保存一致),来实现绑定,不过从Android 5.0(Lollipop)开始需要显示Intent才能完成bindService。
// Since Android 5.0(Lollipop), bindService should use explicit intent.
Intent intent = new Intent("com.ldb.android.example.aidlservice.AidlService");
bindService(
new Intent(createExplicitFromImplicitIntent(this, intent)),
this, BIND_AUTO_CREATE);
Step 6:unbindService。
以上是实现客户端与服务端进行通信的基本步骤。
客户端实例代码:
package com.ldb.android.example.aidlclient; import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast; import com.ldb.android.example.aidl.AidlCallback;
import com.ldb.android.example.aidl.ApiInterfaceV1;
import com.ldb.android.example.aidl.CustomData; import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List; public class MainActivity extends AppCompatActivity implements ServiceConnection{ private static final String TAG = "MainActivity"; private ApiInterfaceV1 mService;
private EditText mNumber;
private Button mPrime;
private Button mStore;
private Button mGet;
private AidlCallback.Stub mAidlCallback; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNumber = (EditText) findViewById(R.id.number_input);
mPrime = (Button) findViewById(R.id.prime);
mStore = (Button) findViewById(R.id.store);
mGet = (Button) findViewById(R.id.get); mPrime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onCheckForPrime();
}
});
mStore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final CustomData customData = new CustomData();
String name = mNumber.getText().toString();
customData.setName(name);
customData.getReference().add(name + "1");
customData.getReference().add(name + "2");
customData.getReference().add(name + "3");
// customData.setCreated(new GregorianCalendar(2016, 9, 1, 9, 0 ).getTime());
// try {
new Thread(new Runnable() {
@Override
public void run() {
try {
mService.storeData(customData);
Log.d(TAG, "mService.storeData1");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start(); Log.d(TAG, "mService.storeData2");
// } catch (RemoteException e) {
// e.printStackTrace();
// }
}
});
mGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CustomData[] result = new CustomData[10];
Date since = new GregorianCalendar(2016, 8, 1, 8, 0 ).getTime();
try {
mService.getAllDataSince(since.getTime(), result);
Log.d(TAG, "Result: " + result.length);
for(int i = 0; i < result.length; i++){
CustomData customData = result[i];
if(customData != null) {
Log.d(TAG, result[i].getName() + result[i].getCreated().toString());
for (String s : result[i].getReference()) {
Log.d(TAG, " -- " + s);
}
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
} @Override
protected void onResume() {
super.onResume();
// Since Android 5.0(Lollipop), bindService should use explicit intent.
Intent intent = new Intent("com.ldb.android.example.aidlservice.AidlService");
bindService(
new Intent(createExplicitFromImplicitIntent(this, intent)),
this, BIND_AUTO_CREATE); // Implement the callback
mAidlCallback = new AidlCallback.Stub() {
@Override
public void onDataUpdated(final CustomData[] data) throws RemoteException {
Log.d(TAG, data[0].getName() + " was updated");
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, data[0].getName() + " was updated");
Toast.makeText(MainActivity.this, data[0].getName() + " was updated",
Toast.LENGTH_SHORT).show();
}
});
}
};
} @Override
protected void onPause() {
super.onPause();
unbindService(this);
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ApiInterfaceV1.Stub.asInterface(service);
try {
mService.setCallback(mAidlCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
} public void onCheckForPrime() {
long number = Long.valueOf(mNumber.getText().toString());
boolean isPrime = false;
try {
isPrime = mService.isPrime(number);
} catch (RemoteException e) {
e.printStackTrace();
}
String message = isPrime ? "number_is_prime" : "number_not_prime";
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
} // Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit
explicitIntent.setComponent(component); return explicitIntent;
} }
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <EditText
android:id="@+id/number_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/prime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="prime"/>
<Button
android:id="@+id/store"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="store"/>
<Button
android:id="@+id/get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="get"/>
</LinearLayout>
Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL的更多相关文章
- Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper
前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝 ...
- Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger
Messenger类实际是对Aidl方式的一层封装.本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明.阅读Android Prog ...
- Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations
多屏幕 自定义View 多屏幕 @.Android 4.2 开始支持多屏幕. @.举例: public class SecondDisplayDemo extends Activity { priva ...
- Android Programming: Pushing the Limits -- Chapter 4: Android User Experience and Interface Design
User Stories Android UI Design 附加资源 User Stories: @.通过写故事来设计应用. @.每个故事只关注一件事. @.不同的故事可能使用相同的组件,因此尽早地 ...
- Android Programming: Pushing the Limits -- Chapter 3: Components, Manifests, and Resources
Android Components Manifest文件 Resource and Assets v\:* {behavior:url(#default#VML);} o\:* {behavior: ...
- Android Programming: Pushing the Limits -- Chapter 2: Efficient Java Code for Android
Android's Dalvik Java 与 Java SE 进行比较 Java代码优化 内存管理与分配 Android的多线程操作 Android’s Dalvik Java 与 Java SE ...
- Android Programming: Pushing the Limits -- Chapter 1: Fine-Tuning Your Development Environment
ADB命令 Application Exerciser Monkey Gradle ProGuard 代码重用 版本控制 静态代码分析 代码重构 开发者模式 ADB命令: @.adb help:查 ...
- Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks
什么时候使用Service 服务类型 开启服务 后台运行 服务通信 附加资源 什么时候使用Service: @.任何与用户界面无关的操作,可移到后台线程,然后由一个Service来控制这个线程. 服务 ...
- [iOS翻译]《iOS 7 Programming Pushing the Limits》系列:你可能不知道的Objective-C技巧
简介: 如果你阅读这本书,你可能已经牢牢掌握iOS开发的基础,但这里有一些小特点和实践是许多开发者并不熟悉的,甚至有数年经验的开发者也是.在这一章里,你会学到一些很重要的开发技巧,但这仍远远不够,你还 ...
随机推荐
- An error in projects
Error能使系统产生Failure从而导致系统不能达到所需的功能. 曾经,做一个关于酒店管理系统的项目.因为数据库表主外键的连接错误,当对页面的添加桌位功能进行测试时,不能正确的添加. 后通过逐行对 ...
- 为在韶大痛苦而不能用手机、Pad等上网的同志造福!
目标:共享咱们校园网,让更多的人或更多的设备冲浪去! 基本条件:一台带无线功能的笔记本,一个可以上网的账号与pwd,最好为Windows7以上的操作系统,如果是XP,则需要打个.net framewo ...
- Windows 10磁盘占用100%解决办法
开机后磁盘占用高,是因为 windows 10 默认启用了 superfetch 服务. 这个服务的主要功能是加快程序的启动速度.开机以后,系统将那些经常使用的程序,预先从硬盘加载到内存中,这样, ...
- hiho #1223 不等式
#1223 : 不等式 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定n个关于X的不等式,问最多有多少个成立. 每个不等式为如下的形式之一: X < C X ...
- 11.8---维护x的秩(CC150)
思路:比较easy.就是借助hashset让他有序然后就能够比较节省时间了. 答案: public static int[] getRankOfNumber(int[] a, int n){ int[ ...
- Docker内部存储结构(devicemapper)解析(续)
dm.fs 参数dm.fs可以指定容器的rootfs的文件系统,但只支持ext4/xfs: func NewDeviceSet(root string, doInit bool, options [] ...
- 【Linux】find grep 联合使用 过滤所有子目录、文件
find . -type f -name '*.*' | xargs grep --color -n 'Admin@123'find . -type f -name '*.*' | xargs sed ...
- PHP入门笔记
PHP是一种创建动态交互性站点的强有力的服务器端脚本语言.PHP其代码可以直接嵌入HYML代码.PHP语法非常类似于Perl和C,常常搭配Apache一起使用. 1.PHP是指超文本预处理器(Hype ...
- C++库(TinyXML)
C++库(TinyXML) 什么是XML? "当 XML(扩展标记语言)于 1998 年 2 月被引入软件工业界时,它给整个行业带来了一场风暴.有史以来第一次,这个世界拥有了一种用来结构化文 ...
- codeforces 501C. Misha and Forest 解题报告
题目链接:http://codeforces.com/problemset/problem/501/C 题目意思:有 n 个点,编号为 0 - n-1.给出 n 个点的度数(即有多少个点跟它有边相连) ...