Android 进程间通信Service 两种实现方式 AIDL 、Messenger

2024-08-31 06:08

本文主要是介绍Android 进程间通信Service 两种实现方式 AIDL 、Messenger,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这篇我们讲下如何使用service实现进程间通信 。通常讲到这里大家都会想到aidl,其实不然,aidl只是其中的一种方式,并且使用起来,我个人感觉太不灵活了,相对Messenger更加灵活。

整个过程我们还是借助于 Activity 跨进程通信  中使用的项目

1.AIDL 跨进程通讯 

   aidl 是个简写,全拼android interface difine language(android自定义接口语言)

  服务端:

    1.1先从新建开始,选中main 右键选择aidl file 新建,会在main 下生成aidl文件夹,我的新建默认名字叫

IMyAidlInterface,也就是IMyAidlInterface.aidl文件。这个就是我们要定义的接口类,默认会有
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);接口,也就是默认支持六种基本数据类型

先写两个基本数据类型,再自定义一个自定义接口

package com.example.intentmode;
import com.example.intentmode.Person;// Declare any non-default types here with import statementsinterface IRemoteService {void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);int getPid();String getName(int id);Person getPerson(int id);
}

先别着急,

1.2 Person getPerson(int id);是会报错的。现在定义Person类,在和IRemoteService类平级目录下新建Person类,并且必须实现Parcelable接口,为啥Serializable 接口不行呢,在android 中都使用Parcelable接口因为Serializable的序列化效率相对较低。

Person 如下:

public class Person implements Parcelable {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(this.name);dest.writeInt(this.age);}public Person() {}protected Person(Parcel in) {this.name = in.readString();this.age = in.readInt();}public static final Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel source) {return new Person(source);}@Overridepublic Person[] newArray(int size) {return new Person[size];}};
}

1.3 新建Person.aidl类 :

// Person.aidl
package com.example.intentmode;// Declare any non-default types here with import statementsparcelable Person;

1.4 新建服务类 ServiceAIDL ,继承自Service

 

public class ServiceAIDL extends Service {private String [] names={"吕布","关羽","赵子龙","张飞"};@Nullable@Overridepublic IBinder onBind(Intent intent) {return mBinder;}private final IMyAidlInterface.Stub mBinder=new IMyAidlInterface.Stub() {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {System.out.println("Thread: " + Thread.currentThread().getName());System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);}@Overridepublic int getPid() throws RemoteException {System.out.println("Thread: " + Thread.currentThread().getName());System.out.println("RemoteService getPid ");return android.os.Process.myPid();}@Overridepublic String getName(int id) throws RemoteException {return names[id];}@Overridepublic Person getPerson(int id) throws RemoteException {Person person=new Person();try {person.setAge(20);person.setName(names[id]);} catch (Exception e) {e.printStackTrace();}return person;}};
}

 

在这里我们定义了接口的返回数据,用于客户端调用时返回想要的数据,其实叫他服务端另一端客户端是有问题的,他们是可以相互调用的,我们暂且这么叫吧。这里有的朋友可能会报attempting to use incompatible return type ,这是因为使用自定义类型要在接口类中导入包名,

package com.example.csdnactivity;// Declare any non-default types here with import statements
//这个要手动导入下
import com.example.csdnactivity.Person;
interface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);int getPid();String getName(int id);Person getPerson(int id);
}

1.5 注册service 并且设置标签 action

  <service android:name=".ServiceAIDL"><intent-filter><action android:name="com.example.intentmode"/></intent-filter></service>

需要注意的是,aidl写完后需要重新构建下,会在build --> generated--> source --> aidl --> debug--> 接口类

如果没有,有可能是代码有错误了。

服务端结束,下面看客户端

客户端:

1.1 将服务端的aidl文件夹粘贴到客户端的main文件下。

1.2 绑定客户端service

        
//服务端设置的actionintent.setAction("com.example.intentmode");//服务端的包名intent.setPackage("com.example.csdnactivity");bindService(intent, conn, Context.BIND_AUTO_CREATE);
package com.example.csdnactivity1;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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;import com.example.csdnactivity.IMyAidlInterface;
import com.example.csdnactivity.Person;/*** Created by LCT* Time:2018/12/24 13:49.* Annotation:*/
public class AIDLActivity extends Activity implements View.OnClickListener {private static final String TAG = "AIDLActivity";/*** 获取远aidl程数据*/private Button mGetData;IMyAidlInterface iMyAidlInterface;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.aidl_activity);initView();bindService();}private void bindService() {Intent intent = new Intent();//服务端设置的actionintent.setAction("com.example.intentmode");//服务端的包名intent.setPackage("com.example.csdnactivity");bindService(intent, conn, Context.BIND_AUTO_CREATE);}private ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {/*** IBinder 转为IRemoteService接口*/iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}};private void initView() {mGetData = (Button) findViewById(R.id.get_Data);mGetData.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {default:break;case R.id.get_Data:/*** 通过iMyAidlInterface 获取数据*/try {if (iMyAidlInterface != null) {String name = iMyAidlInterface.getName(0);iMyAidlInterface.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");Toast.makeText(this, name, Toast.LENGTH_SHORT).show();Log.d(TAG, "onClick: name:"+ name);Person person = iMyAidlInterface.getPerson(0);String str=person.getName() + "_" +person.getAge();Log.d(TAG, "onClick: person:"+ str);}} catch (RemoteException e) {e.printStackTrace();}break;}}
}

客户端代码较少,现在可以运行服务端,如果你使用的是studio 使用了默人新建的build.gradle 文件,那么现在项目可能运行不起来,会提示你:错误:找不到符号符号:类 Person 我用的3.2的编辑器,即使这样还是不太智能需要手动写点东西

在build.gradle  添加如下代码

 sourceSets {main {manifest.srcFile 'src/main/AndroidManifest.xml'java.srcDirs = ['src/main/java', 'src/main/aidl']resources.srcDirs = ['src/main/java', 'src/main/aidl']aidl.srcDirs = ['src/main/aidl']res.srcDirs = ['src/main/res']assets.srcDirs = ['src/main/assets']}}

这样问题就解决了。

现在可以运行服务端,然后启动客户端,获取数据了。

1.Messenger跨进程通讯

  原理与实现

      这个相对aidl相对比较简单,并不是代码实现少,而是比较好理解,首先在服务端会有一个Messenger 它绑定了一个Hander ,客户端同样有一个Messenger 同样绑定了一个Hander ,然后客户端绑定服务通过service onBind返回服务端的Messenger对象,然后将客户端的Messenger    Message.replyTo=cMessenger到服务端的messenger中,然后发送消息,服务端在Hander收到消息,得到客户端的Messenger ,这样就各自持有对方messenger对象,互相发送消息了。接着我们看看具体实现。

服务端

1.1 新建Messenger对象

  Messenger有两个构造函数分别是

public Messenger(Handler target) {mTarget = target.getIMessenger();}
public Messenger(IBinder target) {mTarget = IMessenger.Stub.asInterface(target);}

handler用于绑定Messenger,接收消息。

IBinder用于将服务端的IBinder构造为Messenger对象

 @Overridepublic void onCreate() {super.onCreate();sMessenger=new Messenger(handler);}

1.2获取Messenger 的IBinder通过onBind返回

 public IBinder onBind(Intent intent) {return sMessenger.getBinder();}

1.3Handler 接收数据处理,将收到的客户端Messenger保存,并处理相关逻辑

 Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg!=null) {Log.d(TAG, "handleMessage:  收到客户端发来的消息");/*** 保存客户端Messenger,用于向客户端发送消息*/if (msg.replyTo!=null) {cMessenger=msg.replyTo;}Bundle bundle=msg.getData();String ms;if (bundle !=null) {ms=bundle.getString("serviceData");}else {Log.d(TAG, "handleMessage: null bundle");return;}if (ms==null) {Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();return;}/*** 如果收到消息1 就往客户端发送一条消息*/if (ms.equals("1")) {Message message=Message.obtain();Bundle bundle1=new Bundle();bundle1.putString("clientData","2");message.setData(bundle1);try {cMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}else {Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();}}}};

服务端的完整代码

public class ServiceMessenger extends Service {private static final String TAG = "ServiceMessenger";/*** 服务端Messenger*/Messenger sMessenger;/*** 客户端 Messenger*/Messenger cMessenger=null;@Overridepublic void onCreate() {super.onCreate();sMessenger=new Messenger(handler);}@Nullable@Overridepublic IBinder onBind(Intent intent) {return sMessenger.getBinder();}Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg!=null) {Log.d(TAG, "handleMessage:  收到客户端发来的消息");/*** 保存客户端Messenger,用于向客户端发送消息*/if (msg.replyTo!=null) {cMessenger=msg.replyTo;}Bundle bundle=msg.getData();String ms;if (bundle !=null) {ms=bundle.getString("serviceData");}else {Log.d(TAG, "handleMessage: null bundle");return;}if (ms==null) {Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();return;}/*** 如果收到消息1 就往客户端发送一条消息*/if (ms.equals("1")) {Message message=Message.obtain();Bundle bundle1=new Bundle();bundle1.putString("clientData","2");message.setData(bundle1);try {cMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}else {Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();}}}};
}

1.4.注册service 并设置action,action用来客户端绑定服务时使用

 <service android:name=".ServiceMessenger"><intent-filter><action android:name="com.android.serviceMessenger.action" /></intent-filter></service>

服务端结束。

 

客户端

1.1 创建客户端Messenger ,绑定handler,用于接收消息

cMessenger = new Messenger(handler);

1.2 绑定服务并监听绑定状态,获取服务端的Messenger对象。

 private void bindMessengerService() {Intent intent = new Intent();//服务端设置的actionintent.setAction("com.android.serviceMessenger.action");//服务端的包名intent.setPackage("com.example.csdnactivity");bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);}

获取服务端Messenger,并将客户端的Messenger发送到服务端

 private ServiceConnection connMessenger = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {/*** 得到服务端的messenger*/sMessenger = new Messenger(service);if (sMessenger == null) {Log.d(TAG, "onServiceConnected:  messenger is null");return;}/*** 发送客户端的messenger到服务端*/Message message = Message.obtain();message.replyTo = cMessenger;try {sMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};

 

1.3 发送消息到服务端

 String str = mSendDataToServiceEdit.getText().toString();if (str != null) {Message message = Message.obtain();Bundle bundle = new Bundle();bundle.putString("serviceData",str);message.setData(bundle);try {sMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}

完整代码类

 

public class MessengerActivity extends Activity implements View.OnClickListener {private static final String TAG = "MessengerActivity";/*** 输入发送的信息 通过Messenger 发送*/private EditText mSendDataToService;/*** SENd*/private Button mSendData;/*** 服务端 Messenger*/Messenger sMessenger;/*** 客户端 Messenger*/Messenger cMessenger;/*** messenger*/private Button mMessengerButton;/*** 输入发送的信息 通过Messenger 发送*/private EditText mSendDataToServiceEdit;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.messenger_activity);initView();initMessenger();}private void initMessenger() {cMessenger = new Messenger(handler);bindMessengerService();}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Bundle bundle = msg.getData();if (bundle != null) {String ms = bundle.getString("clientData");if (ms.equals("2")) {Toast.makeText(MessengerActivity.this, "收到服务端发来的数据了" + ms + "", Toast.LENGTH_LONG).show();}}};};private void bindMessengerService() {Intent intent = new Intent();//服务端设置的actionintent.setAction("com.android.serviceMessenger.action");//服务端的包名intent.setPackage("com.example.csdnactivity");bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);}private ServiceConnection connMessenger = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {/*** 得到服务端的messenger*/sMessenger = new Messenger(service);if (sMessenger == null) {Log.d(TAG, "onServiceConnected:  messenger is null");return;}/*** 发送客户端的messenger到服务端*/Message message = Message.obtain();message.replyTo = cMessenger;try {sMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};private void initView() {mSendDataToServiceEdit = (EditText) findViewById(R.id.send_data_to_service_edit);mSendDataToServiceEdit.setOnClickListener(this);mSendData = (Button) findViewById(R.id.send_data);mSendData.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {default:break;case R.id.send_data:String str = mSendDataToServiceEdit.getText().toString();if (str != null) {Message message = Message.obtain();Bundle bundle = new Bundle();bundle.putString("serviceData",str);message.setData(bundle);try {sMessenger.send(message);} catch (RemoteException e) {e.printStackTrace();}}break;}}
}

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><EditTextandroid:id="@+id/send_data_to_service_edit"android:layout_width="match_parent"android:layout_height="50dp"android:layout_toLeftOf="@+id/send_data"android:hint="输入发送的信息 通过Messenger 发送"android:textSize="14sp" /><Buttonandroid:id="@+id/send_data"android:layout_width="100dp"android:layout_height="50dp"android:layout_alignParentRight="true"android:text="SENd" /></RelativeLayout></LinearLayout>

 

客户端完毕。这里注意一点,在调试时记得将两端都启动起来,客户端可以向服务端通过输入框发送消息,服务端收到后会toast出来,如果发送的数据是1,那么服务端会给客户端发送一条消息内容为 2,客户端会toast出来,整个service 跨进程通信就这么多了。

 

 

service 跨进程通信源码

 

 

 

这篇关于Android 进程间通信Service 两种实现方式 AIDL 、Messenger的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123106

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M