Android之AIDL

来源:互联网 发布:淘宝玩具商城 编辑:程序博客网 时间:2024/06/06 01:51

本文只是记录一些零碎的东西

最近在做一些下载的东西,下载的过程放在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 1package slack.cl.com.aidl.bean;//这个文件的作用是引入了一个序列化对象 News 供其他的AIDL文件使用// 否则易报 couldn't find import for class ...  意思就是找不到这个类。//注意:News.aidl与News.java的 package 应当是一样的//注意parcelable是小写parcelable News;


第二个文件,类似接口文件啦,里面写方法,本例主要为


// IDownLoadAidlInterface.aidl  aidl 2package 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也没有想象中那么高大上嘛












0 0
原创粉丝点击