一起简单写一下AIDL,入个门
前话
最近接触了Android开发的一个新知识,AIDL(¬_¬因为到现在都没用过)
因此不断谷歌找资料找Demo,自己尝试写一下。
因为用AndroidStudio作为开发环境,期间遇到过许多问题,因此写下来当作笔记,也给新接触这个知识点的同学们一个小指引。
这里推荐两篇文章:
什么是AIDL
什么是AIDL?这个谷歌一下有很多,我就摘抄一段 ↓
对于AIDL有一些人的浅显概念就是,AIDL可以跨进程访问其他应用程序,和其他应用程序通讯。
那我告诉你,很多技术都可以访问。
如广播(应用A在AndroidManifest.xml中注册指定Action的广播)应用B发送指定Action的广播,A就能收到信息,这样也能看成不同应用之间完成了通讯(但是这种通讯是单向的);
还如ContentProvider,通过URI接口暴露数据给其他应用访问;但是这种都算不上是应用之间的通讯。可能最让人迷惑的是Android推出来了Messager,它就是完成应用之间的通讯的。
那么为什么还要有AIDL呢,官方文档介绍AIDL中有这么一句话:
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
第一句最重要,“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。
可见AIDL是处理多线程、多客户端并发访问的。
而Messager是单线程处理。还是官方文档说的明白,一句话就可以理解为什么要有AIDL。
看到这里大家应该也简单知道什么是AIDL了,下面我们简单动手写一下AIDL,可以加深理解。
一起写AIDL
此次使用AIDL,我们就用来在两个不同进程间传输复杂数据吧。
首先我们定义一下需求,我们想实现这样的功能,就是一个服务端启动一个Service,这个Service用来接收客户端传来的数据,并将该数据打印出来。
客户端 –> 客户端传递数据(例如User类对象) –> 服务端 –>服务端打印接收到的数据
结合这样的功能需求,我们需要创建两个APP。
- 一个APP作为服务端用来启动Service接收并打印数据
- 一个APP作为客户端用来绑定服务端的Service并传递数据给该Service
服务端
最终项目结构
源码
User.aidl 和 User.java
因为要传递复杂数据,这里用User作为例子,因此我们要定义User的AIDL文件。
因为开发环境是AndroidStudio,所以我们要先在aidl文件夹内创建 User.aidl
User.aidl
package me.pwcong.aidlservice.model;
parcelable User;
然后我们再在java文件夹内创建User.java。
因为User作为复杂数据,在不同进程之间传递需要序列化,因此它需要继承Parcelable。
这里源码省略get、set和toString方法。
User.java
package me.pwcong.aidlservice.model;
import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
String name;
int age;
String gender;
@Override
public int describeContents() {
return 0;
}
public void readFromParcel(Parcel dest){
this.name=dest.readString();
this.age=dest.readInt();
this.gender=dest.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.age);
dest.writeString(this.gender);
}
public User() {
}
protected User(Parcel in) {
this.name = in.readString();
this.age = in.readInt();
this.gender = in.readString();
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
特别注意,User.aidl 和 User.java 和包名和路径必须要一样
例如上面User.aidl和User.java的包路径均为package me.pwcong.aidlservice.model;
MainInterface.aidl
接着我们定义一个接口AIDL文件,用作不同进程间传递数据。
MainInterface.aidl
package me.pwcong.aidlservice;
//引用其他类文件是否在同一包内都需要import
import me.pwcong.aidlservice.model.User;
interface MainInterface {
//注意这里的前缀in,表示数据流只能由客户端传到服务端
void introduce(in User user);
}
到这里我们就写好了服务端的AIDL,我们将该项目make一下,如果没有报错,则说明AIDL成功的编译导出了相关的java类文件,在generate文件夹内可以找到。
MainActivity和MainService
MainService.java
package me.pwcong.aidlservice.component.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import me.pwcong.aidlservice.MainInterface;
import me.pwcong.aidlservice.model.User;
public class MainService extends Service {
private final String TAG=getClass().getSimpleName();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mainInterface;
}
private final MainInterface.Stub mainInterface=new MainInterface.Stub() {
@Override
public void introduce(User user) throws RemoteException {
Log.i(TAG, "introduce: "+ user.toString());
}
};
}
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import me.pwcong.aidlservice.R;
public class MainActivity extends AppCompatActivity {
private final String TAG=getClass().getSimpleName();
ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: OK");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: OK");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent();
intent.setAction("me.pwcong.aidlservice.aidl");
startService(intent);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
}
最后在AndroidManifest.xml内进行Service的配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.pwcong.aidlservice">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".component.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".component.service.MainService">
<intent-filter>
<action android:name="me.pwcong.aidlservice.aidl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
</manifest>
启动服务端
我们点击运行,若logcat中输出如下这句说明服务端Service启动成功
09-13 02:44:36.888 1413-1413/? I/MainActivity: onServiceConnected: OK
客户端
最终文件结构
源码
User.java,User.aidl 和 MainInterface.aidl
因为客户端需要传递数据给服务端,因此它的AIDL文件必须和服务端的一样,所以我们将服务端的User.java,User.aidl 和 MainInterface.aidl 拷贝至相应目录。
特别注意,这三个文件的包名路径务必和服务端一致,如上图最终文件结构所示
接着我们再make一下项目,如果没有报错,我们就可以进行下一步了
MainActivity.java 和 AndroidManifest.xml
MainActivity.java
package me.pwcong.aidlclient;
import android.content.ComponentName;
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 android.widget.Button;
import me.pwcong.aidlservice.MainInterface;
import me.pwcong.aidlservice.model.User;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final String TAG=getClass().getSimpleName();
Button button;
MainInterface mainInterface;
ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//将绑定的service转成所定义的AIDL接口类
mainInterface= MainInterface.Stub.asInterface(service);
Log.i(TAG, "onServiceConnected: "+name);
Log.i(TAG, "onServiceConnected: OK");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: "+name);
Log.i(TAG, "onServiceDisconnected: OK");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent();
//特别注意这里设置了服务端APP的包名
intent.setPackage("me.pwcong.aidlservice");
//这里设置action为服务端APP对Service所定义的action值
intent.setAction("me.pwcong.aidlservice.aidl");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
button= (Button) findViewById(R.id.btn);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(mainInterface!=null){
User user=new User();
user.setName("Pwcong");
user.setAge(20);
user.setGender("Man");
try {
mainInterface.introduce(user);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
这个Activity里面简单实现了绑定服务端的Service,和通过点击按钮给服务端Service发送User类对象数据。
AndroidMainifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.pwcong.aidlclient">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
启动客户端
我们运行客户端,若输出如下说明客户端绑定服务端Service成功:
09-13 03:05:17.552 20379-20379/me.pwcong.aidlclient I/MainActivity: onServiceConnected: OK
接着我们点击按钮,若输出如下说明AIDL传输数据成功:
09-13 03:08:01.520 1413-1426/me.pwcong.aidlservice I/MainService: introduce: User{name='Pwcong', age=20, gender='Man'}
结束
到这里我们简单的AIDL入门就写完了。
如果此次入门项目书写过后仍懵懂的话我还是推荐大家认真阅读前面所推荐的两篇入门文章吧。
毕竟我也是刚入门哈。
一起简单写一下AIDL,入个门的更多相关文章
- webpack实践(一)- 先入个门
一.前言 webpack是个啥呢?看官网的这段描述. webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler) 在我以前做纯html.css. ...
- 如何寻找设计灵感?写给刚入行的设计师(转自UI中国)
如何寻找设计灵感?写给刚入行的设计师 如何寻找设计灵感? 这一次的文章,我想和大家聊聊年轻的设计师在没有那么多经验的情况下如何寻找设计师灵感.(希望这篇文章也能帮助感同身受的你) 每个设计师对设计都有 ...
- java————数组 简单写出一个管理系统
数组的特点 1, 数组是一块连续的空间,下标描述空间的位置. 2, 下标从0开始,最大下标为数组长度—1.(*.length-1) 3, 数组元素都是变量.(就是每个下标对应的内容).变量的类型 ...
- Android GIS开发系列-- 入门季(13)Gdal简单写个shp文件
Gdal是用来读写栅格与矢量数据的,在Gdal官网,可以下载相关的资源进行平台的编译.其实Arcgis底层也是用Gdal来读取shp文件的,那在Android中可以直接读写shp文件吗,是可以的.这里 ...
- EntityFramework 简单入个门
任何一个和数据相关的系统里,数据持久化都是一个不容忽视的问题. 一直以来,Java 平台出了很多 NB 的 ORM 框架,Hibernate.MyBatis等等..NET 平台上,ORM 框架这一块一 ...
- vuex , 简单入(liao)门(jie)
vuex什么 ? 官方的说法就是 vuex是专门为vue.js应用程序开发的 状态管理模式 .并采用集中式存储 , 管理应用的所有组件的状态 ,并以相同的规则保证状态以一种可预估的方式发生变化. 自己 ...
- 写给新入IT的新人们
IT=挨踢,这是IT人的自嘲,进入IT行业是有四五年了,也算得上是一个"老人"了吧,见了不少新人,面试了不少新人,也带了一些新人,多多少少还是有点发言权的. 关于书本 新人们经常会 ...
- Vue.js先入个门看看
使用vue.js原文介绍:Vue.js是一个构建数据驱动的web界面库.Vue.js的目标是通过尽可能简单的API实现响应式数据绑定和组合的视图组件.vue.js上手非常简单,先看看几个例子: 例一: ...
- 用c的数组简单的模拟了入栈
其实很简单,只要控制住输出时倒输出.且只输出一个 #include <stdio.h>#include <stdlib.h>int zhan[20];int n=-1;void ...
随机推荐
- PBOC2.0与3.0的区别
一.PBOC规范颁布的历程 1997年12月,PBOC V1.0 定义了五个方面的事项 电子钱包/电子存折应用(EP,ED) 卡片和终端的接口 卡片本身的技术指标 应用相关的交易流程 终端 ...
- Valid Palindrome
leetcode:https://oj.leetcode.com/problems/ 今天A了一个Easy类型的,主要是判断一个字符串是否是回文.东平西凑的还是给弄好了,具体可看下面的要求,或者直接去 ...
- 课堂练习:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。
题目 1 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. 2 要求: (1) 写一个函数 f(N) ,返回1 到 N 之间出现的“1”的个数.例如 f(12) ...
- dp--poj1458最长公共子序列
很水的一题 输入串a与串b: Dp[i][j]表示a串中1~i与b串中1~j的子串的最长公共子序列. Max{dp[i-1][j], dp[i][j-1]} (a[i]!=b[j]) Dp ...
- TLS学习总结
我们有知道 Immunity Debugger,OD 调试器,在调试程序时会设断在OEP(修改第一个字节0xcc).我在想,使用什么编程技术,代码可以在OEP前被执行.在网上找了些资料,在论坛上看到许 ...
- 利用Linq + Jquery + Ajax 异步分页的实现
在Web显示的时候我们经常会遇到分页显示,而网上的分页方法甚多,但都太过于消耗带宽,所以我想到了用Ajax来分页,利用返回的Json来处理返回的数据, 大大简化了带宽的压力. 先说下思路,无非就是异步 ...
- java输出流实现文件下载
//导出Excel try { HSSFWorkbook wb = carService.export(list); //调用service方法~! response.setContentType(& ...
- 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?
[编者按]本篇文章作者是Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于Parse特点,打造一款类似Instagram的应 ...
- IE8中能继续使用Expression的解决方案
在实际工作中,长的报表需要固定表头,比如DataGrid等控件. 过去在用IE8以前版本的时候,只需要在css中加上 position:relative ; top:expresion(this.of ...
- POJ 3233 Matrix Power Series (矩阵快速幂+二分求解)
题意:求S=(A+A^2+A^3+...+A^k)%m的和 方法一:二分求解S=A+A^2+...+A^k若k为奇数:S=(A+A^2+...+A^(k/2))+A^(k/2)*(A+A^2+...+ ...