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

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

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中有两个按钮,点击开启服务后绑定服务端,点击调用,拿到服务端数据。
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输出:




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值