我们都知道Service的主要的作用是后台运行跨进程访问
关于Service后台运行请查看鄙人的另外一篇文章Service基础

本篇博文主要探讨的是跨进程访问~

什么是AIDL

Android系统中的进程之间是不能共享内存,因此,需要提供一些机制在不同的进程之间进行数据通信,Activity BroadCast 和 Content Provider都可以跨进程通信,Service同样也可以跨进程通信。

其中Activity可以跨进程调用其他应用程序的Activity 看看这里;还有这里

Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;

Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播;

Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。

为了使其他应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。 与很多其他基于RPC的解决方案一样,Android使用了一种接口定义语言(Interface Definition Lanuage)来公开服务的接口,因此可以将这种跨进程访问的服务称为 AIDL (Android Interface Definition Language);


建立AIDL的步骤

建立AIDL服务要比建立普通服务的步骤要复杂一些,工具:AS
具体步骤如下
看看这里
看看这里
1. New —-AIDL—-AIDL File ,建立AIDL文件
2. 如果aidl文件正确,Build–Rebulild Project之后,会自动生成一个Java接口文件


3. 建立一个服务类(Service子类)
4. 实现有aidl文件生成的java接口
5. 在AndroidManifest.xml中配置AIDL服务,尤其要注意的是,action标签中android:name的属性值就是客户端要引用该服务的id,也就是Intent类构造方法的参数值。

   <service
            android:name=".activity.service.aidl.AIDLService"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.turing.base.activity.service.aidl.AIDLService" /></intent-filter></service>

建立AIDL服务

首先需要明确,两个工程。ProjectAIDL 和ProjectAIDLClient 。这样就可以实现跨进程访问啦。


功能说明:

建立一个简单的AIDL服务,这个AIDL服务只有一个getValue的方法,改方法返回一个字符串, 在安装完服务后,会在客户端调用这个getValue方法,并将返回值在TextView控件显示。


ProjectAIDL:

A. 建立AIDL文件

// IMyService.aidl
package com.turing.base.activity.service.aidl;// Declare any non-default types here with import statementsinterface IMyService {String getValue();
}

但是此时并没有AIDL的java文件产生,其实android studio也是带有自动生成的,只不过需要确认一些信息后才能生成。此时,我们可以在目录 build–>generated–>source–>aidl–>test–>debug下面发现还没有任何文件

此时,打开AndroidManifest.xml,确认package的值,
关键性的一步,确认aidl文件所在的包名和AndroidMainifest.xml的package名是否一致。如果一致,点击
Build–>Make Project,生成相应的java文件。

经验证,貌似不一样也没问题


同样生成了IMyService.java文件

B. 编写Service子类,在子类中定义一个内部类,该内部类继承自 IMyService.Stub

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;public class AIDLService extends Service {public class MyServiceImpl extends IMyService.Stub {@Overridepublic String getValue() throws RemoteException {return "AIDL.....";}}@Overridepublic IBinder onBind(Intent intent) {return new MyServiceImpl();}
}

注意事项:
I: IMyService.Stub是根据IMyService.aidl文件自动生成的,一般不需要了解这个类的内容,只需要编写一个继承自IMyService.Stub的类即可
II:onBind方法必须返回MySeviceImpl对象,否则客户端无法获取服务对象。

C: 在AndroidManifest.xml中配置MyService类

  <service
            android:name=".activity.service.aidl.AIDLService"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.turing.base.activity.service.aidl.AIDLService" /></intent-filter></service>

其中com.turing.base.activity.service.aidl.AIDLService是客户端访问AIDL服务的ID

至此 ,AIDL服务端的工作完成。


ProjectAIDLClient:

A. 建立AIDLClient工程,并将服务端自动生成的IMyService.java文件连通同包目录一起复制到该工程的src目录下。

首先要拷贝AIDL文件,这里要保证文件的内容一模一样,包括包的名称,比如本例子中服务器端AIDL文件所在包的名称是com.sysu.aidlclient.aidlcilent,如何做到这一点,先新建一个项目,然后在:项目文件夹/app/src/main目录下建立一个aidl文件夹,与java文件夹同级,在Android Studio中就可以看到这个目录,在这个目录上右键New>Package,建立一个com.sysu.aidlclient.aidlclient的包,再将aidl文件拷进去。这样才能保证生成的java接口文件完全一样,否则会提示找不到接口。

B 调用AIDL服务,首先要绑定服务,然后才可以获得服务对象


import android.app.Service;
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.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import com.turing.base.R;public class AIDLActivityDemo extends AppCompatActivity implements View.OnClickListener {private Button btn_bindAIDL, btn_callAIDL;private TextView tv_aidlResult;private IMyService myService  ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_aidlactivity_demo);initView();initEvents();}/*** 初始化组件*/private void initView() {btn_bindAIDL = (Button) findViewById(R.id.id_btn_aidl_bind);btn_callAIDL = (Button) findViewById(R.id.id_btn_aidl_call);// 现将调用AIDL按钮设置为灰色禁用,等初始化AIDL服务之后在设置为可点击btn_callAIDL.setEnabled(false);tv_aidlResult = (TextView) findViewById(R.id.id_tv_aidl_result);}/*** 按钮注册监听事件*/private void initEvents() {btn_bindAIDL.setOnClickListener(this);btn_callAIDL.setOnClickListener(this);tv_aidlResult.setOnClickListener(this);}/*** 按钮监听事件** @param v*/@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.id_btn_aidl_bind:bindService(new Intent("com.turing.base.activity.service.aidl.AIDLService"),serviceConnection,Service.BIND_AUTO_CREATE);break;case R.id.id_btn_aidl_call:// 调用服务端getValue方法try {tv_aidlResult.setText(myService.getValue().toString());} catch (RemoteException e) {e.printStackTrace();}break;case R.id.id_tv_aidl_result:Toast.makeText(this,"闹着玩",Toast.LENGTH_SHORT).show();break;}}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 获取服务对象myService = IMyService.Stub.asInterface(service);btn_callAIDL.setEnabled(true);}@Overridepublic void onServiceDisconnected(ComponentName name) {}} ;
}

注意事项:

  1. 使用bindService方法绑定AIDL服务,其中需要使用Intent对象指定AIDL服务的ID,也就是action标签中android:name属性的值
  2. 在绑定时需要一个ServiceConnection对象,创建ServiceConnection对象的过程中如果绑定成功,系统会调用ServiceConnection.onServiceConnected方法,通过改方法的service参数值可以获得AIDL服务对象。

运行效果演示:

首先,运行AIDL服务程序,然后运行客户端程序,单击绑定AIDL服务按钮,如果绑定成功,调用AIDL按钮 会变成可点击状态,单击此按钮,输出getValue方法的返回值,


传递复杂数据的AIDL服务

AIDL服务只支持有限的数据类型,因此如果使用AIDL传递复杂的数据就需要做进一步的处理。

AIDL服务支持的数据类型

  • Java简单类型(int 、char 、boolean等),无需import
  • String 和 CharSequence,无需import
  • List 和 Map,但是List和Map对象的元素类型必须是AIDL服务支持的数据类型,不需要import
  • AIDL指定生成的接口,需要import
  • 实现android.os.Parcelable接口的类,需要import

工程目录:


传递不需要import的数据类型值的方式相同,传递一个需要import的数据类型值(例如实现android.os.Parceable接口的类)的步骤略显复杂,除了要建一个实现android.os.Parceable接口的类外,还需要为这个类单独建立一个aidl文件,并使用parceable关键字进行定义,具体的实现步骤如下:

ComplexTypeAIDL:

建立一个IMyService.aidl文件

IMyService.aidl

package mobile.android.ch12.complex.type.aidl;
import mobile.android.ch12.complex.type.aidl.Product;interface IMyService
{  Map getMap(in String country, in Product product);Product getProduct();
}  

注意事项:

  • Product是一个实现了android.os.Parcelable接口的类,需要使用import导入这个类
  • 如果方法的类型是非简单类型,例如String、List或者自定义的类,需要使用in 、out或者inout 进行修饰,其中in表示这个值被客户端设置,out表示这个值被服务端设置;inout表示这个值既被客户端设置,又要被服务端设置。
  • -

编写Product类,该类用于传递的数据类型

Produt.java

package mobile.android.ch12.complex.type.aidl;import android.os.Parcel;
import android.os.Parcelable;public class Product implements Parcelable
{private int id;private String name;private float price;public static final Parcelable.Creator<Product> CREATOR = new Parcelable.Creator<Product>(){public Product createFromParcel(Parcel in){return new Product(in);}public Product[] newArray(int size){return new Product[size]; }};public Product(){}private Product(Parcel in){readFromParcel(in);}@Overridepublic int describeContents(){// TODO Auto-generated method stubreturn 0;}public void readFromParcel(Parcel in){id = in.readInt();name = in.readString();price = in.readFloat();}@Overridepublic void writeToParcel(Parcel dest, int flags){dest.writeInt(id);dest.writeString(name);dest.writeFloat(price);}public int getId(){return id;}public void setId(int id){this.id = id;}public String getName(){return name;}public void setName(String name){this.name = name;}public float getPrice(){return price;}public void setPrice(float price){this.price = price;}}

注意事项:

  • Product类必须实现android.os.Parcelable接口。该接口用于序列化对象。在Android中之所以使用Parcelable接口序列化,而不是使用java.io.Serializable接口,主要是为了提高效率。
  • 在Product类中必须有一个静态常量,常量名必须是CREATOR,而且CREATOR常量的数据类型必须是Parcelable.Creator.
public static final Parcelable.Creator<Product> CREATOR = new Parcelable.Creator<Product>(){public Product createFromParcel(Parcel in){return new Product(in);}public Product[] newArray(int size){return new Product[size]; }};
  • 在writeToParcel方法中需要将序列化的值写入Parcel对象
public void readFromParcel(Parcel in){id = in.readInt();name = in.readString();price = in.readFloat();}@Overridepublic void writeToParcel(Parcel dest, int flags){dest.writeInt(id);dest.writeString(name);dest.writeFloat(price);}

建立一个Proudct.aidl

Proudct.aidl

parcelable Product; 

编写MySevice类

MyService.java


import java.util.HashMap;
import java.util.Map;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;public class MyService extends Service
{ public class MyServiceImpl extends IMyService.Stub{@Overridepublic Product getProduct() throws RemoteException{Product product = new Product();product.setId(1234);product.setName("汽车");product.setPrice(31000); return product;}@Overridepublic Map getMap(String country, Product product)throws RemoteException{Map map = new HashMap<String, String>();map.put("country", country);map.put("id", product.getId());map.put("name", product.getName());map.put("price", product.getPrice());map.put("product", product);return map;}}@Overridepublic IBinder onBind(Intent intent){       return new MyServiceImpl();}}

在AndroidManifest.xml文件中配置MyService类

    <service android:name=".MyService" ><intent-filter> <action android:name="mobile.android.ch12.complex.type.aidl.IMyService" /></intent-filter></service>

至此,服务端的AIDL服务已经完成,下面看下客户端的操作


ComplexTypeAIDLClient:

将IMyservice.java和Product.java文件连同目录一起复制到客户端工程

绑定AIDL服务,并获取AIDL服务,最后调用AIDL服务中的方法

Main.java

package mobile.android.ch12.complex.type.aidlclient;import mobile.android.ch12.complex.type.aidl.IMyService;
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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;public class Main extends Activity implements OnClickListener
{private IMyService myService = null;private Button btnInvokeAIDLService;private Button btnBindAIDLService;private TextView textView;private ServiceConnection serviceConnection = new ServiceConnection(){ @Overridepublic void onServiceConnected(ComponentName name, IBinder service){   // 获取AIDL服务对象myService = IMyService.Stub.asInterface(service);btnInvokeAIDLService.setEnabled(true);}@Overridepublic void onServiceDisconnected(ComponentName name){// TODO Auto-generated method stub}};@Overridepublic void onClick(View view){switch (view.getId()){case R.id.btnBindAIDLService:// 绑定AIDL服务bindService(new Intent("mobile.android.ch12.complex.type.aidl.IMyService"),serviceConnection, Context.BIND_AUTO_CREATE);break;case R.id.btnInvokeAIDLService:try{String s = "";// 调用AIDL服务中的方法s = "Product.id = " + myService.getProduct().getId() + "\n";s += "Product.name = " + myService.getProduct().getName()+ "\n";s += "Product.price = " + myService.getProduct().getPrice()+ "\n";s += myService.getMap("China", myService.getProduct()).toString();textView.setText(s);}catch (Exception e){}break;}}@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);btnInvokeAIDLService = (Button) findViewById(R.id.btnInvokeAIDLService);btnBindAIDLService = (Button) findViewById(R.id.btnBindAIDLService);btnInvokeAIDLService.setEnabled(false);textView = (TextView) findViewById(R.id.textview);btnInvokeAIDLService.setOnClickListener(this);btnBindAIDLService.setOnClickListener(this);}
}

运行效果演示:

首选运行服务端,在运行客户端,即可在客户端获取如下信息


AIDL与来去电自动挂断

真机亲测有效

概述

虽然可以通过Activity Action来拨打电话,但是使用常规的方法却无法挂断电话,不过我们可以利用反射,使用AIDL文件自动生成接口来实现。

在Android SDK 源码中可以找到如下接口

com.android.internal.telephony.ITelephony

这个接口在外部是无法访问的,只有将程序嵌入到Android SDK 内部才可以访问,这个接口提供了一个endCall方法可以挂断电话,现在我们就想办法来调用ITelephony.endCall方法。

尽管不能直接访问ITelephony接口,但是我们发现在TelephonyManager类中有一个getITelephhony方法,可以返回一个ITelephony对象,不过改方法是private方法,so..我们可以通过反射来调用改方法

  private ITelephony getITelephony() {return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));}

在调用getITelephony方法获得ITelephony对象之前,我们需要在SDK源码中找到 NeighboringCellInfo.aidl和 ITelephony.aidl,并将这两个文件连同所在的包复制到我们自己的工程中来。

目录如下:

ADT会根据ITelephony.aidl文件自动生成ITelephony.java文件,在gen目录下。

下面我们编写一个接收来电的广播接收器,并在这个广播中自动挂断指定号码的来电,

Code

InCallReceiver.java

package mobile.android.ch12.call.aidl;import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;public class InCallReceiver extends BroadcastReceiver
{@Overridepublic void onReceive(Context context, Intent intent){TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);switch (tm.getCallState()){case TelephonyManager.CALL_STATE_RINGING: // 响铃// 获得来电的电话号String incomingNumber = intent.getStringExtra("incoming_number");if ("1234576".equals(incomingNumber)){try{//  获取TelephoneManager对象TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);//  获取TelephoneManager的class对象Class<TelephonyManager> telephonyManagerClass = TelephonyManager.class;//  获得getITelephony方法Method telephonyMethod = telephonyManagerClass.getDeclaredMethod("getITelephony",(Class[]) null);// 允许访问private方法telephonyMethod.setAccessible(true);// 调用getITelephony方法返回ITelephony对象ITelephony telephony = (com.android.internal.telephony.ITelephony) telephonyMethod.invoke(telephonyManager, (Object[]) null);// 挂断电话telephony.endCall();}catch (Exception e){Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();}}break;}}}

配置权限

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/><uses-permission android:name="android.permission.CALL_PHONE"/>

小结

服务除了可以在内部调用,还可以使用AIDL服务实现跨应用的调用,其中的AIDL文件应用很广泛,可以利用AIDL文件自动生成接口文件,并可以将相应的对象转换成指定的接口,这大大方便了服务的调用。

跨进程访问(AIDL服务)相关推荐

  1. Android 跨进程通信: AIDL

    概念 在Android上一个进程通常是无法访问另一个进程的内存,而AIDL翻译过来就是Android 接口定义语言,你可以你利用它定义客户端和服务端进程间通信相互认可的编程接口. 适用范围 只有允许不 ...

  2. 极路由设置共享磁盘密码、跨网访问samba服务

    极路由插上移动硬盘后会自动建立samba服务器,但我们没法去配置哪些盘符需要密码,这样只要在同一个wifi下的电脑都能去访问这些东西了,比较弱智.另外我还想再公司中去读写这个移动硬盘. 设置密码 首先 ...

  3. Android开发之跨进程通讯-AIDL实现方法 (附源码)

    先看效果图,下面是广播和AIDL跨进程的方法 我们先创建AIDL文件定义接口方法 定义好接口方法如下图: // ITokenAidlInterface.aidl package com.example ...

  4. 跨进程通信AIDL使用

    1.Messenger 1.1 定义 通过Messenger在不同进程中传递Message对象,在Message中放入需要传递的数据.底层实现是AIDL 构造方法 public Messenger ( ...

  5. Android中的跨进程调用技术AIDL

    什么是AIDL Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用 ...

  6. Android进阶——Android跨进程通讯机制之Binder、IBinder、Parcel、AIDL

    前言 Binder机制是Android系统提供的跨进程通讯机制,这篇文章开始会从Linux相关的基础概念知识开始介绍,从基础概念知识中引出Binder机制,归纳Binder机制与Linux系统的跨进程 ...

  7. Android跨进程通讯机制之Binder、IBinder、Parcel、AIDL

    https://blog.csdn.net/qq_30379689/article/details/79451596 前言 Binder机制是Android系统提供的跨进程通讯机制,这篇文章开始会从L ...

  8. WCF服务以控制台程序为宿主时的跨域访问问题(附源码)

    前几天研究SilverLight时碰到了这个问题,本以为很轻松的试验结果很不顺利,查了很多资料才解决了这个问题,在此把解决问题的方法写出来,也方便其它朋友借鉴. 问题是这样产生了,我在测试Silver ...

  9. 实现Android跨进程组件通信能有多简单?

    实现Android跨进程组件通信能有多简单? 作为一个Android开发,都要会点组件化知识.组件化的主要的特点,是剥离依赖,但组件间不直接依赖后,通信问题要怎么解决呢. 通常我们用的一下这种类似Bi ...

最新文章

  1. 虚拟机centos7繁忙关不了机的解决方法(转载)
  2. aspx页面引用html页面
  3. 亚信安全中标民生银行防病毒项目 为“互联网+金融”革新防护能力
  4. thinkphp phpexcel导出
  5. [C#]我自己写的一个对字节中每位进行修改值的函数
  6. 从测试流程角度,阿里P8大佬对产品质量的一些总结思考...
  7. oj运行时错误_FME服务器链接运行时错误(由于缺少适当的权限,用户未授权的请求)...
  8. Python实战从入门到精通第九讲——字符串与文本3之字符串匹配和搜索
  9. Alpine Linux:如何配置GUI的图形桌面环境:x Desktop Environment
  10. LVM逻辑卷管理总结
  11. linux -- CW8.8 编译 提示缺少libstdc++.so.5的error
  12. c语言0x00如何不截断_数组越界及其避免方法,C语言数组越界详解
  13. 无线路由器服务器连接线,无线路由器连接有线路由器怎么设置?
  14. 速记TCP/IP五层模型
  15. 网易互联网(网易严选)测试开发工程师
  16. U盘读不出来的解决办法
  17. 多态的含义、多态的作用及多态的详细代码实现
  18. Ubuntu下使用ss-local+simple-obfs
  19. Pg报错: HikariPool-1 - Connection is not available, request timed out after 30040ms.问题处理
  20. 操作系统--(Linux)LinuxThread vs NPTL

热门文章

  1. C 库函数 int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中
  2. git commit如何修改默认编辑器为vim
  3. C++将派生类赋值给基类(向上转型)(一)
  4. 使用pytorch动手实现LSTM模块
  5. java jni.h_java-如何使jni.h被找到?
  6. 错误处理:IndexError: index out of range in self
  7. 从C语言的角度重构数据结构系列(十二)-C语言判断语法详解(ifswitch)
  8. tableau可视化数据分析60讲(十三)-tableau常用可视化视图(条形图折线图饼图)
  9. 隐性语义索引(LSI)
  10. c语言数字的拆解_C语言解决变态推理题