本文只是记录一些零碎的东西
最近在做一些下载的东西,下载的过程放在service里,其实开一个子线程也是可以的,不管怎么样,都是需要通信的
今天过来扒一扒AIDL,官方API:https://developer.android.com/guide/components/aidl.html
环境:android studio 2.1.2
这个说白了其实就是java接口通信,只是换了个样子,看看怎么实现,假设我现在有个下载的任务,需要显示下载进度,然后模拟一个新闻的app后台,查询所有新闻,新增一个新闻,模拟测试嘛,不要较真
项目地址:https://github.com/CL-window/aidl_example
先新建一个新闻类,实现Parcelable,需要实现的方法 alt+enter 快捷键搞定,完整的类,需要注意一下 in out 区别,看注释
package slack.cl.com.aidl.bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* <p>Description: 新闻类 需要序列化 Parcelable </p>
* 对参数 in out 的理解:aidl里会需要用到d
* in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,
* 而 inout 则表示数据可在服务端与客户端之间双向流通
* in out 都是相对服务端来说的
* Created by slack on 2016/9/27 11:34 .
*/
public class News implements Parcelable{
public String title;// 标题
public String author;// 作者
public String context;// 内容
public String time;// 时间
public News() {}
public News(String title, String author, String context, String time) {
this.title = title;
this.author = author;
this.context = context;
this.time = time;
}
protected News(Parcel in) {
title = in.readString();
author = in.readString();
context = in.readString();
time = in.readString();
}
public static final Creator<News> CREATOR = new Creator<News>() {
@Override
public News createFromParcel(Parcel in) {
return new News(in);
}
@Override
public News[] newArray(int size) {
return new News[size];
}
};
@Override
public int describeContents() {
return 0;
}
// 默认 in
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(title);
parcel.writeString(author);
parcel.writeString(context);
parcel.writeString(time);
}
/**
* 参数是一个Parcel,用它来存储与传输数据 out
* @param dest
*/
public void readFromParcel(Parcel dest) {
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
title = dest.readString();
author = dest.readString();
context = dest.readString();
time = dest.readString();
}
@Override
public String toString() {
return "News{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", context='" + context + '\'' +
", time='" + time + '\'' +
'}';
}
}
接下来是aidl文件的新建,IDE有提供新建
因为需要传一个序列化的对象,本例需要两个aidl文件
// News.aidl aidl 1
package slack.cl.com.aidl.bean;
//这个文件的作用是引入了一个序列化对象 News 供其他的AIDL文件使用
// 否则易报 couldn't find import for class ... 意思就是找不到这个类。
//注意:News.aidl与News.java的 package 应当是一样的
//注意parcelable是小写
parcelable News;
第二个文件,类似接口文件啦,里面写方法,本例主要为
// IDownLoadAidlInterface.aidl aidl 2
package slack.cl.com.aidl;
//导入所需要使用的非默认支持数据类型的包
import slack.cl.com.aidl.bean.News;
interface IDownLoadAidlInterface {
//所有的返回值前都不需要加任何东西,不管是什么数据类型
// activity --> service
void downLoadUpdate();// 模拟下载
News addNews(in News news);// in 相对服务端来说是数据流入,也可是使用inout
// service --> activity
int onDownLoadProgress();// 模拟下载进度
List<News> getNews();// 所有的新闻
}
reBuild一下项目,如果没有报错就会生成aidl的java文件
看一下这个生成的文件,
public static abstract class Stub extends android.os.Binder implements slack.cl.com.aidl.IDownLoadAidlInterface
原来Stub类就是继承于Binder类,只是所返回的IBinder对象比较特别,是一个实现了AIDL接口的Binder整个文件基于动态反射
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\slack\\demo\\MyApplication\\aidl\\src\\main\\aidl\\slack\\cl\\com\\aidl\\IDownLoadAidlInterface.aidl
*/
package slack.cl.com.aidl;
public interface IDownLoadAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements slack.cl.com.aidl.IDownLoadAidlInterface {
private static final java.lang.String DESCRIPTOR = "slack.cl.com.aidl.IDownLoadAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an slack.cl.com.aidl.IDownLoadAidlInterface interface,
* generating a proxy if needed.
*/
public static slack.cl.com.aidl.IDownLoadAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof slack.cl.com.aidl.IDownLoadAidlInterface))) {
return ((slack.cl.com.aidl.IDownLoadAidlInterface) iin);
}
return new slack.cl.com.aidl.IDownLoadAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_downLoadUpdate: {
data.enforceInterface(DESCRIPTOR);
this.downLoadUpdate();
reply.writeNoException();
return true;
}
case TRANSACTION_addNews: {
data.enforceInterface(DESCRIPTOR);
slack.cl.com.aidl.bean.News _arg0;
if ((0 != data.readInt())) {
_arg0 = slack.cl.com.aidl.bean.News.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
slack.cl.com.aidl.bean.News _result = this.addNews(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_onDownLoadProgress: {
data.enforceInterface(DESCRIPTOR);
int _result = this.onDownLoadProgress();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getNews: {
data.enforceInterface(DESCRIPTOR);
java.util.List<slack.cl.com.aidl.bean.News> _result = this.getNews();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements slack.cl.com.aidl.IDownLoadAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//所有的返回值前都不需要加任何东西,不管是什么数据类型
// activity --> service
@Override
public void downLoadUpdate() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_downLoadUpdate, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public slack.cl.com.aidl.bean.News addNews(slack.cl.com.aidl.bean.News news) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
slack.cl.com.aidl.bean.News _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((news != null)) {
_data.writeInt(1);
news.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addNews, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = slack.cl.com.aidl.bean.News.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int onDownLoadProgress() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_onDownLoadProgress, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.util.List<slack.cl.com.aidl.bean.News> getNews() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<slack.cl.com.aidl.bean.News> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNews, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(slack.cl.com.aidl.bean.News.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_downLoadUpdate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addNews = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_onDownLoadProgress = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getNews = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
//所有的返回值前都不需要加任何东西,不管是什么数据类型
// activity --> service
public void downLoadUpdate() throws android.os.RemoteException;
public slack.cl.com.aidl.bean.News addNews(slack.cl.com.aidl.bean.News news) throws android.os.RemoteException;
public int onDownLoadProgress() throws android.os.RemoteException;
public java.util.List<slack.cl.com.aidl.bean.News> getNews() throws android.os.RemoteException;
}
接着就到service类了,模拟了一下
service有两种启动,一种直接start,一种bind,需要了解bind的可以好好看看音乐播放器,那个主要是使用bind
service里 需要有实现aidl接口的类
package slack.cl.com.aidl.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import slack.cl.com.aidl.IDownLoadAidlInterface;
import slack.cl.com.aidl.bean.News;
public class DownLoadService extends Service {
public final String TAG = "slack"; // this.getClass().getSimpleName();
private List<News> newsList = new ArrayList<>();
// bindService 时调用,返回任何你想返回给activity的IBinder类型数据
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind...");
return stub;
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate...");
initData();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand...");
// 此处可以接收 data
return START_NOT_STICKY;
// 下面这个会 在service被kill 后自动重启
// return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy...");
super.onDestroy();
}
// test
private void initData(){
for (int i = 0;i < 5; i++){
newsList.add(new News("title"+i,"author"+i,"context"+i,"time"+i));
}
}
int progress = 0;
// 实现aidl Stub接口 build-->generated-->sources-->aidl-->debug
IDownLoadAidlInterface.Stub stub = new IDownLoadAidlInterface.Stub() {
@Override
public void downLoadUpdate() throws RemoteException {
Log.i(TAG,"downLoadUpdate...");
progress = 0;
}
@Override
public News addNews(News news) throws RemoteException {
Log.i(TAG,"addNews...");
newsList.add(0,news);
return news;
}
@Override
public int onDownLoadProgress() throws RemoteException {
// Log.i(TAG,"onDownLoadProgress...");
return progress += 5;
}
@Override
public List<News> getNews() throws RemoteException {
Log.i(TAG,"getNews...");
return newsList;
}
};
}
测试的activity
package slack.cl.com.aidl;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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 slack.cl.com.aidl.bean.News;
import slack.cl.com.aidl.service.DownLoadService;
public class MainActivity extends AppCompatActivity {
private final String TAG = "slack"; // getClass().getSimpleName();
private Intent serviceIntent;
private IDownLoadAidlInterface mAIDLmanager;
private boolean mIsBind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStop() {
super.onStop();
if(mIsBind){
unbindService(mServiceConnection);
mIsBind = false;
}
}
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "service connected...");
// 获得service里onBind 返回对象
mAIDLmanager = IDownLoadAidlInterface.Stub.asInterface(iBinder);
mIsBind = true;
if(mAIDLmanager == null){
return;
}
getNews(null);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "service disConnected...");
mIsBind = false;
}
};
// 显式启动service
public void startServices(View view) {
if(serviceIntent == null){
serviceIntent = new Intent(this,DownLoadService.class);
}
startService(serviceIntent);
}
// 通讯,肯定是建立在bind基础上,就跟打电话一样,先接通
public void bindServices(View view) {
if(!mIsBind) {
if(serviceIntent == null){
serviceIntent = new Intent(this,DownLoadService.class);
}
bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
public void addNews(View view) {
if(mAIDLmanager == null){
return;
}
try {
mAIDLmanager.addNews(new News("11","11","11","11"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void downLoad(View view) {
if(mAIDLmanager == null){
return;
}
try {
mAIDLmanager.downLoadUpdate();
// 获取下载进度
new Thread(new Runnable() {
@Override
public void run() {
int progress;
try {
while ((progress = mAIDLmanager.onDownLoadProgress()) < 100){
Log.i(TAG,"progress:" + progress );
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getNews(View view) {
if(mAIDLmanager == null){
return;
}
try {
Log.i(TAG ,"news:"+ mAIDLmanager.getNews().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
结果基本完成预期,本文只是简单模拟一下。
- - - - - - - - - - - - -分割线 - - - - - - - - - - - - - - -- - - - -
细细分析一下,基于动态代理生成的aidl 的 .java 文件
先看看在哪里使用的,在activity的bindService 的参数 ServiceConnection的onServiceConnected里面
mAIDLmanager = IDownLoadAidlInterface.Stub.asInterface(iBinder);
而asInterface 的返回值是 return new slack.cl.com.aidl.IDownLoadAidlInterface.Stub.Proxy(obj);
Proxy类,没错,返回的是这个代理类,代理类也实现里aidl接口
分析addNews
@Override
public slack.cl.com.aidl.bean.News addNews(slack.cl.com.aidl.bean.News news) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
slack.cl.com.aidl.bean.News _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((news != null)) {
_data.writeInt(1);
news.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addNews, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = slack.cl.com.aidl.bean.News.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
看看这句 mRemote.transact(Stub.TRANSACTION_addNews, _data, _reply, 0); Android Native层Binder.transact()函数调用 Binder.onTransact() 函数,native层代码,这里不分析,看看onTransact
case TRANSACTION_addNews: {
data.enforceInterface(DESCRIPTOR);
slack.cl.com.aidl.bean.News _arg0;
if ((0 != data.readInt())) {
_arg0 = slack.cl.com.aidl.bean.News.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
slack.cl.com.aidl.bean.News _result = this.addNews(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
1._arg0 = slack.cl.com.aidl.bean.News.CREATOR.createFromParcel(data); 调用我们的Bean对象news里实现序列化接口生成的CREATOR,新建了一个对象
2.slack.cl.com.aidl.bean.News _result = this.addNews(_arg0);// 调用addNews方法
3._result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);// 写序列化
如此看来,aidl也没有想象中那么高大上嘛