您的位置:首页 > 大数据 > 人工智能

AIDL的简单使用,涵盖5.0之后系统及魅族手机调用问题的解决

2017-09-08 15:33 525 查看
最近在整理笔记,因为我很多东西都是放在印象笔记里面,写的也不是特别规范,有时候时间长了,自己回头看都有点懵圈,现在一点点的挪到博客里来,尽量写的规范一点,如果能帮到有需要的朋友,也是件好事。

AIDL,就是安卓跨进程通信的方式,其内部细节及Binder的实现暂时不表,只说怎样使用及一些注意点,还有我自己写demo的时候碰到的问题,比如5.0之后隐式调用Service出错啦,在魅族手机上无法绑定,在下面都会给出解决方式,demo已上传github:传送门。此示例为通过aidl获取服务端的一个List列表,里面存放的是Book这个实体类,服务端运行后,客户端“开启服务”,log日志打印出是否连接成功,然后点击“调用“,log打印出服务端给出的list列表中的实体内容。

一.服务端

1.建立服务端,构建AIDL文件

包名设置为com.crfchina.server,在服务端构建AIDL文件,在服务端可以右键通过studio来生成aidi,因为他的包名不需要修改,其文件结构如下:



来看一下主要的4个文件的书写,首先是aidl包下的两个aidl文件,因为我们使用到了Book类,所以也要建立Book的aidl文件。

Book.aidl
package com.crfchina.server;

parcelable Book;


注意parcelable 是一个类型,首字母是小写的。

IBookManager.aidl

package com.crfchina.server;
import com.crfchina.server.Book;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}

需要注意的是该接口使用到了Book文件,但是需要我们手动导入Book类,否则编译会报错的;在接口的定义中, 接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示,上面的代码我们用in标记,因为它是输入型参数。

Book实体类因为要在进程间传输,需要实现序列化接口Parcelable,写法如下,在构建这些需要序列化的类的时候,有一个小技巧,可以通过studio的自动生成功能来做,不用手写那么麻烦了,首先写成该类的成员变量,然后实现Parcelable接口,类名会报红,alt+/ 修复就自动生成对应的代码了。

package com.crfchina.server;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {

private int bookId;
private String bookName;

public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}

protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}

@Override
public int describeContents() {
return 0;
}

public static final 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 String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}


2.构建服务端Service

<service
android:name=".MyService"
android:enabled="true">
<intent-filter>
<action android:name="a.b.c" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
清单文件中必须配置过滤器,否则外部程序隐式调用该服务会出错。
package com.crfchina.server;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MyService extends Service {

private static final String TAG = "AIDL";
private List<Book> list = new ArrayList<>();

private Binder mBinder = new IBookManager.Stub() {

@Override
public List<Book> getBookList() throws RemoteException {
synchronized (MyService.class) {
return list;
}
}

@Override
public void addBook(Book book) throws RemoteException {
synchronized (MyService.class) {
if (!list.contains(book)) {
list.add(book);
}
}
}

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
};

@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "服务绑定");
list = new ArrayList<>();
list.add(new Book(0, "1984"));
list.add(new Book(1, "红楼梦"));
return mBinder;
}

@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "服务开启");

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "服务调用");
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "服务关闭");
}
}

二.客户端配置

1.客户端目录结构



客户端的aidl代码直接拷贝服务端的aidl包到客户端moudle下就可以了,由于我们使用到了Book文件,所以需要建立一个与服务端Book代码一致的Book,编译后,构建客户端代码调用文件。

2.客户端调用服务

客户端代码如下,在MainActivity中有两个按钮,点击开
4000
启服务后绑定服务端,点击调用,拿到服务端数据。
package com.crfchina.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.crfchina.server.Book;
import com.crfchina.server.IBookManager;

import java.util.List;

public class MainActivity extends AppCompatActivity {

private Button btBinder, btGet;
private IBookManager manager;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
manager = IBookManager.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
manager = null;
}

};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btBinder = (Button) findViewById(R.id.button2);
btGet = (Button) findViewById(R.id.button);
initListener();
}

private void initListener() {
btBinder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.crfchina.server", "com.crfchina.server.MyService"));
startService(intent);
boolean flag = bindService(intent, conn, Context.BIND_AUTO_CREATE);
Log.i("AIDL", flag + "");
}
});
btGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (manager != null) {
List<Book> books = manager.getBookList();
for (Book book : books) {
Log.i("AIDL", book.toString());
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}

  注意点:5.0之后不允许隐式开启服务,网上给出的办法是在Inent里面setPackage(服务包名);但是在我的魅族手机上怎么跑都不行,后来发现魅族的系统把这一块给改了,丫的费了我半天劲,最后找到一种方式,可以绑定服务,首先通过setComponent()的方式来指定服务,然后先startService,再bindService这样就能成功的绑定服务端的Service。

服务端Log输出如下:



客户端Log输出:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  aidl 手机 android
相关文章推荐