Android高级第十讲之AIDL与JNI
博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved !
什么是AIDL,它有什么作用?
AIDL:Android Interface Defination Language
它是一种Android内部进程通信的描述语言,用它来定义通信接口。
AIDL是安卓的一个伟大设计,它保证不同项目之间资源、数据可以共享,减少冗余开发。
其实它也是RPC机制,客户端和服务端默认共用一套协议,Remote Procedure Call Protocol。
资源共享可以在项目的project.properties文件里写android.library.reference.1=../“项目名”
AIDL的简单实现(一方做服务端,一方做客户端):
目标项目:
1、首先可以在目标项目中声明一个Service A和一个.aidl结尾的接口文件B
2、在Service中实现一个IMyService.Stub实体类 C,其中C实现调用文件中的IInterface实现接口D
2、在Service的onBind()方法里返回B的实现
这样目标项目工作就完成了。
客户端:
1、首先实现一个序列化实体,一般用parcelable
2、然后完善D,写入一个抽象类,声明DESCRIPTOR变量为目标项目的B路径
3、在Activity中先绑定A,再获取A中所持久化的数据。
例:
创建一个ServiceConnection连接,从后台获得
private IAIDLServerService mIaidlServerService = null; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { mIaidlServerService = null; } public void onServiceConnected(ComponentName name, IBinder service) { mIaidlServerService = IAIDLServerService.Stub.asInterface(service); } };private void bindService() { // bindService Intent service = new Intent("com.chapter8.aidl.IAIDLServerService"); service.setPackage("com.blog.aidlserver"); bindService(service, mConnection, BIND_AUTO_CREATE); }private Book mBook; private void getTransferSingleData() { if (mIaidlServerService == null) { return; } // aidl通信 try { String mText = "Say hello: " + mIaidlServerService.sayHello() + "\n"; mBook = mIaidlServerService.getBook(); mText += "书名: " + mBook.getBookName() + "\n"; mText += "价格: " + mBook.getBookPrice(); mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } }
公共部分
Book:
package com.blog.aidl; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { private String bookName; private int bookPrice; public Book(){ } public Book(Parcel parcel){ bookName = parcel.readString(); bookPrice = parcel.readInt(); } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public int getBookPrice() { return bookPrice; } public void setBookPrice(int bookPrice) { this.bookPrice = bookPrice; } public int describeContents() { return 0; } public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(bookName); parcel.writeInt(bookPrice); } public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() { public Book createFromParcel(Parcel source) { return new Book(source); } public Book[] newArray(int size) { return new Book[size]; } }; }Book.aidlparcelable Book;IAIDLServerService.aidlpackage com.blog.aidl; import com.blog.aidl.Book; interface IAIDLServerService { String sayHello(); Book getBook(); List<Book> getBooks(); void getBookList(in List<Book> inBooks,out List<Book> outBooks); }
这里多解释一下,进程间通信的实体类都要实现parcelable接口,aidl里面也有一些格式规范主要是in和out关键字,in代表传入的参数,out代表输出的参数
当然看到这里,也清楚传入传出的参数类型,只能为实体类和容器类包含String,而不包含其他基本数据类型,即使封装类都不可以
服务端:
AidlServerService:
package com.blog.aidlserver; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.blog.aidl.Book; import com.blog.aidl.IAIDLServerService.Stub; import com.blog.aidl.IAIDLServerService; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class AidlServerService extends Service { private List<Book> mList = new ArrayList<Book>(); @Override public IBinder onBind(Intent intent) { return mBinder; } /** * 在AIDL文件中定义的接口实现。 */ private IAIDLServerService.Stub mBinder = new Stub() { public String sayHello() throws RemoteException { return "Hello"; } public Book getBook() throws RemoteException { Book mBook = new Book(); mBook.setBookName("Android应用开发"); Random random = new Random(); mBook.setBookPrice(50 + random.nextInt(50)); mList.add(mBook); return mBook; } @Override public void getBookList(List<Book> inBooks, List<Book> outBooks) throws RemoteException { // TODO Auto-generated method stub Book mBook = new Book(); mBook.setBookName("IOS应用开发"); mBook.setBookPrice(45); inBooks.add(mBook); outBooks.addAll(inBooks); } @Override public List<Book> getBooks() throws RemoteException { // TODO Auto-generated method stub return mList; } }; }客户端:package com.blog.aidlclient; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView; import com.blog.aidl.Book; import com.blog.aidl.IAIDLServerService; public class AidlClientActivity extends Activity { private TextView mTextView; private IAIDLServerService mIaidlServerService = null; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { mIaidlServerService = null; } public void onServiceConnected(ComponentName name, IBinder service) { mIaidlServerService = IAIDLServerService.Stub.asInterface(service); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 初始化控件 mTextView = (TextView) findViewById(R.id.textview); bindService(); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { public void onClick(View v) { getTransferSingleData(); } }); findViewById(R.id.button2).setOnClickListener(new OnClickListener() { public void onClick(View v) { getTransferMultiData(); } }); findViewById(R.id.button3).setOnClickListener(new OnClickListener() { public void onClick(View v) { getTransferTempData(); } }); } private void bindService() { // bindService Intent service = new Intent("com.chapter8.aidl.IAIDLServerService"); service.setPackage("com.blog.aidlserver"); bindService(service, mConnection, BIND_AUTO_CREATE); } private Book mBook; private void getTransferSingleData() { if (mIaidlServerService == null) { return; } // aidl通信 try { String mText = "Say hello: " + mIaidlServerService.sayHello() + "\n"; mBook = mIaidlServerService.getBook(); mText += "书名: " + mBook.getBookName() + "\n"; mText += "价格: " + mBook.getBookPrice(); mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } } private void getTransferMultiData() { if (mIaidlServerService == null) { return; } // aidl通信 try { String mText = "Say hello: " + mIaidlServerService.sayHello() + "\n"; List<Book> inBooks = new ArrayList<Book>(); if (mBook == null) { mBook = mIaidlServerService.getBook(); } inBooks.add(mBook); List<Book> outBooks = new ArrayList<Book>(); mIaidlServerService.getBookList(inBooks, outBooks); for (Book book : outBooks) { mText += "书名: " + book.getBookName() + "\n"; mText += "价格: " + book.getBookPrice() + "\n"; } mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } } private void getTransferTempData() { if (mIaidlServerService == null) { return; } // aidl通信 try { String mText = "Say hello: " + mIaidlServerService.sayHello() + "\n"; List<Book> books = mIaidlServerService.getBooks(); for (Book book : books) { mText += "书名: " + book.getBookName() + "\n"; mText += "价格: " + book.getBookPrice() + "\n"; } mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } } }按钮1:
按钮2:
按钮1再按钮2
要求客户端与服务端公用模块一样,最好写成依赖包的形式
注:直接启动服务端(只为Service自启动)再调用客户端就可以试验了!
关于JNI,主要用在java调用c或c调用java语言,应用场景分为3类
1、涉及到核心知识库,c的加密性会更好
2、c已经实现逻辑,且比较复杂
3、调用驱动,一般java做不到
如何区分Java类中哪些是调用c语言的方法呢?看到有native加为前缀的方法即是。当然这之前需要先把so库加载进来,否则会报错;例:
static { System.loadLibrary("webp"); }private native final void init();//初始化类的方法对应在c中的方法为(是一种编译规范,可改变)
static void android_content_AssetManager_init(JNIEnv* env,jobject clazz)
规范指,产生的c函数中,前者是JNIEnv对象指虚拟机运行环境,后者是调用函数的对象,指AssetManager;java方法在c中表现为包名+类包+方法名。
关于数据访问,java不可以直接访问c,c默认对java是私有的,可以使用get/set方法来达到访问目的。
有时c也会要访问java,因为一般情况下两者不会使用回调函数,不同于aidl,c语言返回值支持void、object和基本数据类型的封装类,调用分3步
1、获得Java对象的类
cls=env->GetObjectClass(object)
2、获得Java函数id
jmethodId mid= env ->GetMethodId(cls,"method_name","[Ljava/lang/String;)V")
3、调用函数
env->CallXXXMethod(jobject,mid,ret)
如果要获得数据,将上面的method改成field即可,比较奇怪的一个问题是这样,c库里主要进行逻辑运算,而结果的保存,如对象的声明,通常要保存在java中。