跨进程访问(AIDL服务)

简介: 跨进程访问(AIDL服务)

我们都知道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接口文件

20160216193204156.png


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:

20160216200130398.png20160216200130398.png

A. 建立AIDL文件


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


20160216200130398.png


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


此时,打开AndroidManifest.xml,确认package的值,

关键性的一步,确认aidl文件所在的包名和AndroidMainifest.xml的package名是否一致。如果一致,点击

Build–>Make Project,生成相应的java文件。


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


20160216201442798.png

20160216201512213.png

同样生成了IMyService.java文件

20160216201603845.png

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 {
        @Override
        public String getValue() throws RemoteException {
            return "AIDL.....";
        }
    }
    @Override
    public 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  ;
    @Override
    protected 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
     */
    @Override
    public 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() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 获取服务对象
            myService = IMyService.Stub.asInterface(service);
            btn_callAIDL.setEnabled(true);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    } ;
}


注意事项:


使用bindService方法绑定AIDL服务,其中需要使用Intent对象指定AIDL服务的ID,也就是action标签中android:name属性的值

在绑定时需要一个ServiceConnection对象,创建ServiceConnection对象的过程中如果绑定成功,系统会调用ServiceConnection.onServiceConnected方法,通过改方法的service参数值可以获得AIDL服务对象。


运行效果演示:


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


20160216210531749.gif


传递复杂数据的AIDL服务


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


AIDL服务支持的数据类型

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

工程目录:

20160302145038106.png

20160302145146701.png


传递不需要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);
    }
    @Override
    public int describeContents()
    {
        // TODO Auto-generated method stub
        return 0;
    }
    public void readFromParcel(Parcel in)
    {
        id = in.readInt();
        name = in.readString();
        price = in.readFloat();
    }
    @Override
    public 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();
    }
    @Override
    public 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
    {
        @Override
        public Product getProduct() throws RemoteException
        {
            Product product = new Product();
            product.setId(1234);
            product.setName("汽车");
            product.setPrice(31000); 
            return product;
        }
        @Override
        public 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;
        }
    }
    @Override
    public 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()
    { 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {   
            // 获取AIDL服务对象
            myService = IMyService.Stub.asInterface(service);
            btnInvokeAIDLService.setEnabled(true);
        }
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            // TODO Auto-generated method stub
        }
    };
    @Override
    public 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;
        }
    }
    @Override
    public 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);
    }
}

运行效果演示:

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


20160302145539814.gif


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,并将这两个文件连同所在的包复制到我们自己的工程中来。

目录如下:

20160302160642032.png

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
{
    @Override
    public 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文件自动生成接口文件,并可以将相应的对象转换成指定的接口,这大大方便了服务的调用。

相关文章
|
1月前
|
Java 关系型数据库 MySQL
java控制Windows进程,服务管理器项目
本文介绍了如何使用Java的`Runtime`和`Process`类来控制Windows进程,包括执行命令、读取进程输出和错误流以及等待进程完成,并提供了一个简单的服务管理器项目示例。
39 1
|
2月前
|
存储 Linux Docker
CentOS 7.6安装Docker实战案例及存储引擎和服务进程简介
关于如何在CentOS 7.6上安装Docker、介绍Docker存储引擎以及服务进程关系的实战案例。
144 3
CentOS 7.6安装Docker实战案例及存储引擎和服务进程简介
|
5月前
|
开发框架 安全 .NET
【权限提升】Win本地用户&进程注入&令牌窃取&AT&SC&PS服务命令
【权限提升】Win本地用户&进程注入&令牌窃取&AT&SC&PS服务命令
|
4月前
|
Python
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
|
6月前
|
安全 Linux 应用服务中间件
操作系统引导过程 与 服务进程的控制
操作系统引导过程 与 服务进程的控制
|
6月前
|
Linux 开发者
【亮剑】Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件
【4月更文挑战第30天】本文介绍了Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件。flock适合脚本,fcntl提供底层灵活性,lockfile用于管理锁定文件,flockfile则结合两者功能。选择锁定策略时需考虑应用场景,如脚本可选flock,复杂需求则用fcntl。理解并正确使用这些工具对保证系统稳定性和数据一致性至关重要。
382 1
|
测试技术 API
【OS Pintos】Project1 项目要求说明 | 进程中止信息 | 参数传递 | 用户内存访问 | 有关项目实现的建议
【OS Pintos】Project1 项目要求说明 | 进程中止信息 | 参数传递 | 用户内存访问 | 有关项目实现的建议
148 0
|
6月前
|
负载均衡 网络协议 中间件
掌握 SOME/IP :访问进程数据 构建高效通信系统的关键技术
掌握 SOME/IP :访问进程数据 构建高效通信系统的关键技术
358 2
|
6月前
|
监控 Java Linux
linux下监控java进程 实现自动重启服务
linux下监控java进程 实现自动重启服务
|
6月前
|
数据采集 Java Python
python并发编程:Python在FastAPI服务中使用多进程池加速程序运行
python并发编程:Python在FastAPI服务中使用多进程池加速程序运行
1055 0

热门文章

最新文章

相关实验场景

更多
下一篇
无影云桌面