AIDL详解
AIDL:Android Interface Definition Language,即Android接口定义语言。
从定义上看,这个AIDL有两个特点:1、是用来定义接口的 2、是另一种“语言”,也不完全算一种语言。3、实现了远程接口
为什么有ADIL来定义接口,直接public interface不好吗?
在线程间通信的时候,用Bound Service(什么是Bound Service?先接着看。。),通过继承Binder的方式,会获得Service的实例,然后调用Service里所有的方法。的那是当有些方法不想被公开的时候,当然你可以将方法声明为private,但是倘若到后期,又需要公开这些方法,你是不是还要将声明改为public?当方法较多时,显然不符合松耦合的思想。
这时,可以将MyBinder继承Binder的类私有,用MyBinder里的方法,调用Service中的私有方法(这些方法是不公开的,但是可以通过这种方法调用)。这时候,我们如果将MyBider里的方法私有,外部就访问部了Service的方法了。
那么如何公开这些方法呢?这时候,可以声明一个接口,接口里声明想要公开的方法,然后让MyBinder extends Binder implements Inter,这样就必须要重写接口里公开的方法。
如何创建AIDL?
将上面的接口文件Inter.java拷贝到桌面,更改后缀为Inter.aidl,再把这个文件拷贝回工程里。删除原来的Inter.java。双击进去,看到有报错,这是因为接口用了public修饰,在adil语言里,没有public,所以只需要删除掉前面的修饰符就好。
工程里还会报错。因为已经没有了Inter.java,所以implements那里会报错。这时候,打开工程里的gen文件,找到Inter.java文件(自动生成的)
打开Inter.java发现,有一个Stub方法,实现了Binder和Inter。所以在
所以做如下修改即可(注释的是修改之前的),这样就不会报错,可以运行。
远程访问:
远程访问时,需要事先知道哪个接口是公开的,只需要拷贝这个文件到src文件里,但是有一个条件,那就aidl文件所在的包名必须和原来的一样。
和上图里那个包名一样。
所以要在目标工程里这么写:
这样就把接口公开出去了。可以通过给Service添加隐式意图来实现远程访问。
有什么好处?
这些东西的设计,都是为了更好的适应高内聚低耦合的思想。android整体的设计,和web那一套MVC三层架构一样,例如Broadcast,就是类UDP通信。而这里的AIDL,就实现远程(不同线程之间)的访问,这个远程访问和Activity的隐式意图启动一样,在Activity的Intent里,setAction()方法可以指定要启动的Activity。同理在Service里,startService()方法里也是传一个Intent,可以通过setAction来指定要访问的uri。然后后台在你看不到的情况下开启了一个服务。服务可以当做没有界面的Activity来理解。
一个小Demo
服务端:(在服务端的代码里,也自己访问了一次自己)
献上代码,注释没有去掉,有些地方值得思考,为什么不能那样做,又为什么可以这样写。
下面是Mainactivity
import com.example.aidldemo.Inter.Stub;
import android.app.Activity;
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.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends Activity {
private Conn conn;
private boolean isConn;
private Inter inter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new Conn();
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 调用服务提供出来的方法
try {
inter.m1();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
// 绑定上Service----》调用Service提供出来的方法
bindService(new Intent(MainActivity.this, Service01.class), conn,
Context.BIND_AUTO_CREATE);
}
class Conn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConn = true;
// inter = (Inter) service;
inter = Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// if(isConn){
// unbindService(conn);
isConn = false;
// }
}
}
@Override
protected void onDestroy() {
if (isConn) {
unbindService(conn);
isConn = false;
}
super.onDestroy();
}
}
下面是Service
import com.example.aidldemo.Inter.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;
public class Service01 extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private class MyBinder extends Stub{
public void get02() {
methodService02();
}
public void m2() {
}
@Override
public void m1() {
// Toast.makeText(getApplicationContext(), "调用成功", 0).show();
System.out.println("打印信息 服务端");
}
}
private void methodService01() {
}
private void methodService02() {
}
}
AIDL
package com.example.aidldemo;
interface Inter {
void m1();
}
Mainfest,在application节点下添加
<service android:name="com.example.aidldemo.Service2406">
<intent-filter >
<action android:name="service01aidl"/>
</intent-filter>
</service>
===================================================
模拟远程客户端访问
import com.example.aidldemo.Inter;
import com.example.aidldemo.Inter.Stub;
import android.app.Activity;
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.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class MainActivity extends Activity {
private Conn conn;
private boolean isConn;
private Inter inter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new Conn();
//绑定远程方法
bindService(new Intent("service01aidl"), conn, Context.BIND_AUTO_CREATE);
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//调用远程服务中的方法
try {
inter.m1();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
class Conn implements ServiceConnection{
//Service连接上的时候
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConn = true;
inter = Stub.asInterface(service);
Toast.makeText(getApplicationContext(), "连接上了", 0).show();
}
//意外断开连接的时候
@Override
public void onServiceDisconnected(ComponentName name) {
isConn =false;
}
}
@Override
protected void onDestroy() {
if(isConn){
unbindService(conn);
isConn=false;
}
super.onDestroy();
}
}
客户端里还有一个这个东东
其他作用:电话监听
这个AIL还可以实现电话监听,手机黑名单的制作。
权限:
<uses-permission android:name="android.permission.CALL_PHONE"/>
<span style="white-space:pre"> </span> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
注册reciver节点(因为电话的信息是通过ContentProvider提供出来的)
<receiver android:name="com.example.endcall.Reciver02">
<intent-filter >
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
思路:通过BroadcastReciver接收信息,判断是打出去电话还是打进来电话。如果是打进来电话,判断是挂断,接通和响铃。
通过反射,获得ServiceManager的getService方法,这个方法invoke返回的是一个Binder类。通过Stub.asInterface(binder)得到了endCall的接口,进而调用endCall挂断电话。
package com.example.day24demo08endcall;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephony.Stub;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
/***
* 监听--》电话
* 电话挂断
* 电话响铃
* 电话接通
* @author cj
*
*/
public class Receiver01 extends BroadcastReceiver{
//接收到广播的时候,会执行
@Override
public void onReceive(Context context, Intent intent) {
//打进来电话的时候,挂断
if(Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())){//打出去电话
}else{
TelephonyManager manager=(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//获取电话状态
int state = manager.getCallState();
//判断状态
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://挂断
break;
case TelephonyManager.CALL_STATE_OFFHOOK://接通
break;
case TelephonyManager.CALL_STATE_RINGING://响铃--->挂断电话
System.out.println("电话响了~~~");
//得到ITelephony对象,调用挂断电话的方法
// "android.os.ServiceManager"
try {
// Method[] methods = Class.forName("android.os.ServiceManager").getDeclaredMethods();
//
// for (Method method : methods) {
// System.out.println(method);
// }
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
//执行方法
IBinder ibinder=(IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});
ITelephony itelephony=Stub.asInterface(ibinder);
itelephony.endCall();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
}
AIDL文件,及其包名