Android开发艺术探索之初探AIDL(一)

一、前言

       最近在学习Android开发艺术探索一书,因为也是开发刚入门,刚学习到AIDL这部分的时候,即使对着书本敲还是遇到许多书上没有总结的问题,故写此篇防止后忘,同时,也希望可以帮助初学的小伙伴贡献自己的一份力量吧。


二、概述

       那什么情况下才需要使用到AIDL呢?Android中IPC通信方式其实有很多啊,比如可以使用bundle、文件共享的方式、Messenger以及等等吧,但是再看看,使用Bundle,需要传递的数据可以被序列化,不能被序列化的数据就不能通过它在进程间传递数据了,对于文件共享,由于Android基于Linux,所以对于文件的并发读写没有限制,可能会导致多个线程,对于同一个文件同时进行写操作,这样就很容易导致问题的产生,至于Messenger,其实是对AIDL做了封装,底层实现还是AIDL。那什么情况必须要用AIDL呢,用官方文档中的一句话:“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”。那毕竟学好数理化,走遍天下都不怕!


三、书中实例走起

         实例简介:主要就是模仿实现服务端向客户端提供书籍,在服务端有新书的时候,将这个新书消息推送给客户端。
一.在main文件夹下新建一个aidl文件夹,建议将AIDL相关的所有东西都放在同一个文件夹下。这里依次创建Book类,IBookListener.aidl以及 IBookManager.aidl。
(1)Book类代码如下
package com.example.weixiao.myaidldemo;
import android.os.Parcel;
import android.os.Parcelable;
/**
 * 
 * Created by weixiao on 2017/8/27.
 */
public class Book implements Parcelable {
    private int bookId;
    private String bookName;//书名
    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }
    public int getBookId() {
        return bookId;
    }
    public void setBookId(int bookId) {
        this.bookId = bookId;
    }
    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }
    public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(bookId);
        parcel.writeString(bookName);
    }
}
        主要就是实现序列化,用于进程间数据的传递。

(2)IBookListener.aidl(新建aidl和新建activity类似,右键新建AIDL  new AIDL FILE,你会发现新建的aidl,接口里有一个basicTypes方法,其实没什么作用,可以直接删除)

// IOnBookListener.aidl
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
interface IOnBookListener {
    void NewBookArrived(in Book book);//新书到达通知回调
}

主要注意点:
       1.手动显示导入 import com.example.weixiao.myaidldemo.Book;
      2.AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out、inout,in表示输入型参数,out输出型参数,inout表示输入输出型参数,AIDL中只支持方法,不支持声明静态常量,是不同于一般接口的。

(3)IBookManager.aidl

// IBookManager.aidl
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IOnBookListener;
interface IBookManager {
   List<Book> getBookList();//获取书籍列表
   void addBook(in  Book book);//添加新书
   void registerListener(IOnBookListener listener);//注册新书通知监听
   void unregisterListener(IOnBookListener listener);//取消新书通知
}

注意点嘛,和上面一样,需要把Book以及IOnBookListener显示导进来,切记。

以上工作完成以后,就是编译AIDL文件了,这里可能会出现一些问题,我就说说我碰到的一些问题吧。


1.aidl unknown type  这个原因还是因为没有显示导入相关类或者AIDL。

2.Error:(101, 58) 错误: 找不到符号
符号:   类 Book
 这个需要在build.gradle中添加
android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.example.weixiao.myaidldemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
********************************************************************
    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
********************************************************************************************
}

以上就是我遇到的主要问题。

二、服务端代码
package service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IBookManager;
import com.example.weixiao.myaidldemo.IOnBookListener;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by weixiao on 2017/8/27.
 */

public class BookManagerService extends Service {
    private static final String TAG = "BMS";
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//查了下,主要是保证原子性,就是多线程的情况下,只有一个线程操作
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(
    );//CopyOnWriteArrayList支持并发读写,就是多个线程访问
    private RemoteCallbackList<IOnBookListener> mListenerList = new RemoteCallbackList<>();//保证关闭时取消新书消息推送
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }
        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
        @Override
        public void registerListener(IOnBookListener listener) throws RemoteException {
            mListenerList.register(listener);
        }
        @Override
        public void unregisterListener(IOnBookListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        //服务端添加一些新书
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestoryed.set(true);
    }
    private void onNewBookArrived(Book book) throws RemoteException {
        //新书到来了
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnBookListener listener = mListenerList.getBroadcastItem(i);
            if (listener!=null){
                listener.NewBookArrived(book);//将新书消息推给客户端
            }
            Log.d(TAG, "onNewBookArrived,notify listener:" + listener);
        }
        mListenerList.finishBroadcast();
    }
    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            //do background processing here
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);//5秒推送一次,5秒创建一本新书
                    int bookId = mBookList.size() + 1;
                    Book newBook = new Book(bookId, "new Book #" + bookId);
                    onNewBookArrived(newBook);//
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

当然记得在清单文件中注册哦,并设置为开启一个新的进程。
完成上面的两步,客户端就简单多了,如下

三、客户端

package com.example.weixiao.myaidldemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import java.util.List;

import service.BookManagerService;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE = 1;
    private IBookManager mIBookManager;
    private int i = 0;
    private IOnBookListener mBookListener = new IOnBookListener.Stub() {
        @Override
        public void NewBookArrived(Book book) throws RemoteException {
            mHandler.obtainMessage(MESSAGE, book).sendToTarget();
        }
    };
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE:
                    //收到新书
                    Book book = (Book) msg.obj;
                    i++;
                    Log.e(TAG, "receive book" + i + "::" + book.getBookName());
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    };
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager manager = IBookManager.Stub.asInterface(iBinder);
            try {
                mIBookManager = manager;
                List<Book> books = manager.getBookList();//获取服务端图片列表并toast
                for (Book book : books) {
                    Toast.makeText(MainActivity.this, book.getBookName(), Toast.LENGTH_SHORT).show();
                }
                manager.addBook(new Book(3, "Android开发艺术探索"));//添加新书
                manager.addBook(new Book(4, "Android开发艺术探索2"));//添加新书
                manager.addBook(new Book(5, "Android开发艺术探索3"));//添加新书
                List<Book> newList = manager.getBookList();
                manager.registerListener(mBookListener);//注册新书推送
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);//绑定服务
    }
    @Override
    protected void onDestroy() {
        if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
            try {
                Log.e(TAG, "unregister listener" + mBookListener);
                mIBookManager.unregisterListener(mBookListener);//取消新书推送消息
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}

至此就大功告成了,下面是测试结果:

09-16 21:16:55.907 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book2::new Book #7
09-16 21:17:00.909 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book3::new Book #8
09-16 21:17:05.911 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book4::new Book #9
09-16 21:17:10.912 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book5::new Book #10
09-16 21:17:15.914 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book6::new Book #11
09-16 21:17:17.873 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: unregister listener   com.example.weixiao.myaidldemo.MainActivity$1@e20bbf5

四、总结


      其实,因为是初学aidl,所以这点东西花费了我许多时间,但回过头,把之前的问题都梳理一遍,多少还是有些收获的,不要在乎每次进步多少,只要是再向前走,就别怕,就像蜗牛里面唱的那样不是嘛,我成为不了别人,我只能做好自己!加油。
最后附上代码下载地址: http://download.csdn.net/download/a734474820/9982255

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值