服务端:

最终项目结构:

这个项目中,我们将用到自定义类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的更多相关文章

  1. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper

    前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝 ...

  2. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger

    Messenger类实际是对Aidl方式的一层封装.本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明.阅读Android Prog ...

  3. Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations

    多屏幕 自定义View 多屏幕 @.Android 4.2 开始支持多屏幕. @.举例: public class SecondDisplayDemo extends Activity { priva ...

  4. Android Programming: Pushing the Limits -- Chapter 4: Android User Experience and Interface Design

    User Stories Android UI Design 附加资源 User Stories: @.通过写故事来设计应用. @.每个故事只关注一件事. @.不同的故事可能使用相同的组件,因此尽早地 ...

  5. Android Programming: Pushing the Limits -- Chapter 3: Components, Manifests, and Resources

    Android Components Manifest文件 Resource and Assets v\:* {behavior:url(#default#VML);} o\:* {behavior: ...

  6. 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 ...

  7. Android Programming: Pushing the Limits -- Chapter 1: Fine-Tuning Your Development Environment

    ADB命令 Application Exerciser Monkey Gradle ProGuard 代码重用 版本控制 静态代码分析 代码重构 开发者模式   ADB命令: @.adb help:查 ...

  8. Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks

    什么时候使用Service 服务类型 开启服务 后台运行 服务通信 附加资源 什么时候使用Service: @.任何与用户界面无关的操作,可移到后台线程,然后由一个Service来控制这个线程. 服务 ...

  9. [iOS翻译]《iOS 7 Programming Pushing the Limits》系列:你可能不知道的Objective-C技巧

    简介: 如果你阅读这本书,你可能已经牢牢掌握iOS开发的基础,但这里有一些小特点和实践是许多开发者并不熟悉的,甚至有数年经验的开发者也是.在这一章里,你会学到一些很重要的开发技巧,但这仍远远不够,你还 ...

随机推荐

  1. Android开发学习笔记--计时器的应用实例

    为了了解安卓计时器的用法,写了一个秒表的应用,正是这个秒表,让我对Android应用的速度大跌眼镜,我设置了一个计时器,10ms更新一次显示的时间,然后更标准的时间一比较发现,跑10s就有一秒的时间误 ...

  2. http://five-js.envylabs.com/

    一个很有意思的播报javascript最新资讯的网站http://five-js.envylabs.com/

  3. caffe学习系列(7):Blob,layer,Net介绍

    参考:http://www.cnblogs.com/denny402/p/5073427.html

  4. GR32 TImage32的图层绘制原理

    转载:http://blog.sina.com.cn/s/blog_491aced20100ded4.html TImage32的继承顺序如下:TCustomControl->TCustomPa ...

  5. ubuntu14 谷歌输入法

    sudo apt-get install ibus-googlepinyin 装完重启即可: (在右上角语言处右键,添加text entry)

  6. 在Navicat for MySQL中打开视图时,提示视图没有主键的问题

    一直把视图理解为一个select语句而已,视图一般就是用于查询,不会通过视图来更新表或视图本身的数据,所以视图根本不需要什么主键.今天自己建了一个视图view_test: drop view if e ...

  7. c# Dictionary的遍历和排序

    c# Dictionary的遍历和排序 c#遍历的两种方式 for和foreach for: 需要指定首位数据.末尾数据.数据长度: for遍历语句中可以改变数据的值: 遍历规则可以自定义,灵活性较高 ...

  8. 【微服务】SpringBoot、SpringCloud相关

    深入学习微框架:Spring Boot:   http://www.infoq.com/cn/articles/microframeworks1-spring-boot/ Spring Boot--2 ...

  9. ASP.NET Web API与Rest web api

    ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of ...

  10. 《oracle每天一练》Oracle冷备份与数据恢复

    相关帖子 转自http://blog.csdn.net/nsj820/article/details/5611361 备份 直接拷贝oracle目录下的admin.oradata(datafile,  ...