Android之AIDL

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

最近在做一些下载的东西,下载的过程放在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也没有想象中那么高大上嘛












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值