Android IPC机制(三)进程间通信方式

在Android中有以下几种进程间通信方式:

目录

1.Bundle

2.文件共享

3.Messenger

 4.ContentProvider

5.AIDL


1.Bundle

  Bundle是Android中用于存储一组键值对的类,它实现了Parcelable接口。这使得Bundle能够在不同的进程之间传递数据。当我们通过Intent启动其他应用的组件(如ActivityServiceBroadcastReceiver)时,可以将数据存储在Bundle中,并将其附加到Intent中,从而实现跨进程通信。

下面是一个简单的示例,展示如何在MainActivity中使用Bundle将数据传递到SecondActivity

在MainActivity中:

Intent intent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("test", "这是Bundle跨进程通信");
intent.putExtra("test", bundle);
startActivity(intent);

在上述代码中,我们创建了一个Intent对象,用于启动SecondActivity。然后,我们创建了一个Bundle对象,并使用putString方法将一条字符串信息存储在其中。接着,我们将这个Bundle附加到Intent中,并调用startActivity方法启动目标活动。

在SecondActivity中接收数据:

SecondActivity中,我们可以通过以下代码来接收并使用传递过来的Bundle数据:

Bundle result = getIntent().getBundleExtra("test");
Log.e("data", "onCreate:" + result.getString("test"));

在这里,我们使用getIntent().getBundleExtra("test")方法获取传递过来的Bundle,然后通过getString方法提取出我们之前存储的字符串。最后,我们使用Log.e打印出接收到的数据。

运行结果:

当我们运行这个示例时,SecondActivity将会在日志中输出以下内容:

2.文件共享

       两个进程通过读/写同一个文件来交互数据,从而实现通信。但是Android上面没有对文件的并发读/写做限制,甚至两个进程同时对一个文件进行写操作都是运行的,因此有可能发送异常,所以,文件共享发送适合在对数据同步要求不高的进程间通信,并且要妥善处理并发的读/写问题。

      文件共享的实现,在我们的示例中,我们在MainActivity中序列化一个 User对象到应用的内部存储文件中。然后在SecondActivity 中去反序列化,我们期望在 SecondActivity中能够正确地恢复 User 对象的值。关键代码如下:

在MainActivity中序列化到文件:

 private void persistToFile(){try{User user=new User(1,"hello word",false);File file=new File(getExternalCacheDir(),"share_data.txt");if(!file.exists()){file.createNewFile();}FileOutputStream fos = new FileOutputStream(file);ObjectOutputStream objectOutputStream=new ObjectOutputStream(fos);objectOutputStream.writeObject(user);Log.e("xxx","persist user:"+user);fos.close();objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}

在 SecondActivity中从文件恢复:

 private void recoverFromFile(){try {User user = null;File file = new File(getExternalCacheDir(), "share_data.txt");if (!file.exists()) {file.createNewFile(); }ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));user = (User) objectInputStream.readObject();Log.e("xxx", "recover user:" + user);objectInputStream.close();} catch (Exception e) {e.printStackTrace();}}

下面看一下log,很显然,在SecondActivity 中成功地从文件从恢复了之前存储的 User对象的内容,这里之所以说内容,是因为反序列化得到的对象只是在内容上和序列化之前的对象是一样的,但它们本质上还是两个对象。

SharedPreferences的局限性:

我们知道SharedPerference是存储xml文件里面,从本质上说也是文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPerference文件的缓存,因此多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问时,SharedPerference有很大概率会丢失数据,因此不建议进程间的通信使用SharedPerference。

3.Messenger

        Messenger 可以翻译为信使,通过它可以在不同进程中传递 Message 对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。

为什么说Messenger的底层是基于AIDL实现?

我们大致看一下Messenger 这个类的构造方法就明白了。下面是Messenger 的两个构造方法,从构造方法的实现上我们可以明显看出AIDL的痕迹,不管是IMessenger还是 Stub.asInterface,这种使用方法都表明它的底层是 AIDL。

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

既然Messenger是基于AIDL实现的,那么它与AIDL有什么区别呢?

  • Messenger是通过Bundle传递数据,多以只能传递基本数据类型,而AIDL可以传递实体类,其实Bundler也可以传递序列化之后的实体类。
  • Messenger适合传递数据少,数据量少的情况,AIDL适合数据多,数据量大的情况。
  • Messenger返回值是异步的,AIDL是同步的。

下面是一个简单的示例,展示如何在Android应用中使用Messenger进行进程间通信。

客户端代码(MainActivity)

public class MainActivity extends AppCompatActivity {private Handler mHandler=new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if(msg.what==11) {Bundle data = msg.getData();String test = data.getString("test");Log.d("xxx", "handleMessage:" + test);}}};private final  Messenger mMessager=new Messenger(mHandler); // 用于与服务通信的Messenger@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent=new Intent(this,MessengerService.class);// 创建服务的IntentbindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);// 绑定服务}private final ServiceConnection mServiceConnection=new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Messenger messenger=new Messenger(service);// 获取服务的MessengerMessage message= Message.obtain();// 创建消息message.what=1;Bundle bundle=new Bundle();bundle.putString("test","Messenger传递数据");message.setData(bundle);message.replyTo=mMessager;// 设置回复的Messengertry {messenger.send(message);// 发送消息到服务} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onDestroy() {super.onDestroy();if(mServiceConnection!=null){unbindService(mServiceConnection);// 解绑服务}}
}

 服务端代码(MessengerService)

public class MessengerService extends Service {private Handler mHandler=new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if(msg.what==1) {Bundle data = msg.getData();String test = data.getString("test");Log.d("xxx", "handleMessage:" + test);Messenger replyTo=msg.replyTo;Message message=new Message();message.what=11;Bundle bundle=new Bundle();bundle.putString("test","这是服务器回传数据到客户端");message.setData(bundle);message.replyTo=mMessenger;try {replyTo.send(message);} catch (RemoteException e) {e.printStackTrace();}}}};private Messenger mMessenger=new Messenger(mHandler);@Nullable@Overridepublic IBinder onBind(Intent intent) {return mMessenger.getBinder();}
}

AndroidManifest.xml配置

在AndroidManifest.xml中,需要声明服务并指定其运行在单独的进程中:

<service android:name=".MessengerService" android:process=":test"/>

运行结果: 服务端收到了客户端发送的信息,而客户端也收到了服务端的回复。

 4.ContentProvider

ContentProvider是 Android 中提供的专门用于不同应用间进行数据共享的方式,虽然ContentProvider的底层实现是 Binder,但是它的使用过程要比 AIDL简单许多,这是因为系统已经为我们做了封装,使得我们无须关注底层细节即可轻松实现IPC。

实现一个 ContentProvider 涉及几个步骤,包括创建类、定义数据模型、实现必要的方法以及在 AndroidManifest.xml 中注册。以下是一个简单的示例,展示如何实现一个 ContentProvider

1. 创建数据模型

首先,我们需要定义一个数据模型。假设我们要管理一个简单的联系人信息,包括姓名和电话号码。

public class Contact {public static final String TABLE_NAME = "contacts";public static final String COLUMN_ID = "_id";public static final String COLUMN_NAME = "name";public static final String COLUMN_PHONE = "phone";
}

2. 创建 ContentProvider 类

接下来,我们创建一个继承自 ContentProvider 的类,并实现必要的方法。

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;public class ContactProvider extends ContentProvider {private static final String AUTHORITY = "com.example.app.provider";private static final String BASE_PATH = "contacts";public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);private static final int CONTACTS = 1;private static final int CONTACT_ID = 2;private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);static {uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTACTS);uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTACT_ID);}private SQLiteDatabase database;@Overridepublic boolean onCreate() {// 初始化数据库DatabaseHelper dbHelper = new DatabaseHelper(getContext());database = dbHelper.getWritableDatabase();return database != null;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {Cursor cursor;switch (uriMatcher.match(uri)) {case CONTACTS:cursor = database.query(Contact.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);break;case CONTACT_ID:selection = Contact.COLUMN_ID + " = ?";selectionArgs = new String[]{uri.getLastPathSegment()};cursor = database.query(Contact.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);break;default:throw new IllegalArgumentException("Unknown URI: " + uri);}cursor.setNotificationUri(getContext().getContentResolver(), uri);return cursor;}@Overridepublic Uri insert(Uri uri, ContentValues values) {long id = database.insert(Contact.TABLE_NAME, null, values);getContext().getContentResolver().notifyChange(uri, null);return Uri.parse(BASE_PATH + "/" + id);}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {int rowsUpdated;switch (uriMatcher.match(uri)) {case CONTACTS:rowsUpdated = database.update(Contact.TABLE_NAME, values, selection, selectionArgs);break;case CONTACT_ID:selection = Contact.COLUMN_ID + " = ?";selectionArgs = new String[]{uri.getLastPathSegment()};rowsUpdated = database.update(Contact.TABLE_NAME, values, selection, selectionArgs);break;default:throw new IllegalArgumentException("Unknown URI: " + uri);}if (rowsUpdated != 0) {getContext().getContentResolver().notifyChange(uri, null);}return rowsUpdated;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {int rowsDeleted;switch (uriMatcher.match(uri)) {case CONTACTS:rowsDeleted = database.delete(Contact.TABLE_NAME, selection, selectionArgs);break;case CONTACT_ID:selection = Contact.COLUMN_ID + " = ?";selectionArgs = new String[]{uri.getLastPathSegment()};rowsDeleted = database.delete(Contact.TABLE_NAME, selection, selectionArgs);break;default:throw new IllegalArgumentException("Unknown URI: " + uri);}if (rowsDeleted != 0) {getContext().getContentResolver().notifyChange(uri, null);}return rowsDeleted;}@Overridepublic String getType(Uri uri) {switch (uriMatcher.match(uri)) {case CONTACTS:return "vnd.android.cursor.dir/" + AUTHORITY + "." + BASE_PATH;case CONTACT_ID:return "vnd.android.cursor.item/" + AUTHORITY + "." + BASE_PATH;default:throw new IllegalArgumentException("Unknown URI: " + uri);}}private static class DatabaseHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME = "contacts.db";private static final int DATABASE_VERSION = 1;public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {String createTable = "CREATE TABLE " + Contact.TABLE_NAME + " (" +Contact.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +Contact.COLUMN_NAME + " TEXT, " +Contact.COLUMN_PHONE + " TEXT" + ");";db.execSQL(createTable);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS " + Contact.TABLE_NAME);onCreate(db);}}
}

3. 在 AndroidManifest.xml 中注册 ContentProvider

在 AndroidManifest.xml 文件中注册 ContentProvider,并设置权限(如果需要)。

<providerandroid:name=".ContactProvider"android:authorities="com.example.app.provider"android:exported="true" />

4. 使用 ContentProvider

其他应用程序可以通过 ContentResolver 来访问 ContentProvider 提供的数据。例如,查询联系人数据:

Cursor cursor = getContentResolver().query(ContactProvider.CONTENT_URI, null, null, null, null);
if (cursor != null) {while (cursor.moveToNext()) {String name = cursor.getString(cursor.getColumnIndex(Contact.COLUMN_NAME));String phone = cursor.getString(cursor.getColumnIndex(Contact.COLUMN_PHONE));// 处理数据}cursor.close();
}

5.AIDL

     在Android平台中,每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用进程的服务,而且经常需要在不同的进程间传递数据对象。但是一个进程不能直接访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的进行进程边界。而AIDL就用于生成可以在Android设备上两个进程之间进行进程间通信的代码。

在AIDL文件中,并不是所有的数据类型都是可以使用的,那么到底AIDL文件支持哪些数据类型呢?如下所示。

  • 基本数据类型(int、long、char、boolean、double等);
  • String 和 CharSequence;
  • List:只支持ArrayList,里面每个元素都必须能够被 AIDL支持;
  • Map:只支持 HashMap,里面的每个元素都必须被 AIDL支持,包括key和 value;
  • Parcelable:所有实现了Parcelable接口的对象:
  • AIDL:所有的 AIDL接口本身也可以在 AIDL 文件中使用。

参数定向TAG:

AIDL除了基本数据类型,其他类型的参数都需要一个定向tag来指出数据的流向,不管是in,out,还是inout,基于参数的定向tag默认是并且只能是in。

  • in:表示数据只能由客户端流向服务端
  • out:表示数据只能由服务端流向客户端
  • inout:表示数据可在服务端与客户端之间双向流通

以下是AIDL的基本实现步骤和示例:

1. 创建AIDL文件

首先,在你的Android项目中创建一个AIDL文件。通常,这个文件会放在 src/main/aidl 目录下。

例如,创建一个名为 MyAidl.aidl 的文件,内容如下:

// MyAidl.aidl
package com.ex.binder1;interface MyAidl {void add();
}

2. 编写服务端实现

在服务端,你需要实现这个接口。创建一个服务类,继承 Service,并实现AIDL接口。


public class AidlService extends Service {private final Binder mbinder=new MyAidl.Stub(){@Overridepublic void add() throws RemoteException {Log.e("xxx","服务端add被调用");}};@Nullable@Overridepublic IBinder onBind(Intent intent) {return mbinder;}
}

3. 在AndroidManifest.xml中注册服务

在 AndroidManifest.xml 中注册你的服务:

  <serviceandroid:name=".aidl.AidlService"android:process=":aidltest" />

4. 客户端调用

在客户端,你需要绑定到服务并调用AIDL接口。以下是一个简单的示例:

public class AidlActivity extends AppCompatActivity {private ServiceConnection mServiceConnection=new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {MyAidl myAidl= MyAidl.Stub.asInterface(service);try {myAidl.add();} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_aidl);Intent intent=new Intent(this,AidlService.class);bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);}
}

运行结果:


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/59228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu系统安装升级jdk到17

百度安全验证 https://blog.csdn.net/qq_44866828/article/details/130557027 然后修改一下配置路径 试下java --version命令

GEE数据集:全球天然林和人工林数据集提供了一张高分辨率(30 米)地图,用于区分截至 2021 年全球的天然林和人工林

目录 简介 全球天然林和人工林 数据生成和分类 代码 引用 License 网址推荐 知识星球 机器学习 GEE数据集&#xff1a;全球天然林和人工林数据集提供了一张高分辨率&#xff08;30 米&#xff09;地图&#xff0c;用于区分截至 2021 年全球的天然林和人工林 简介 全球…

20241031 Apache2修改日志里面的时间格式

问题背景,Apache2里面日志输出,关于时间这一块,看着难受,所以有了如下需求,修改日志里面的时间格式 案例日志 127.0.0.1 - - [31/Oct/2024:19:20:34 0800] "GET /index.php/vod/search/actor/XimenadelSolar.html HTTP/1.1" 200 4233 "-" "Mozilla/5…

基于SpringBoot司机信用评价的货运管理系统【附源码】

基于SpringBoot司机信用评价的货运管理系统 效果如下&#xff1a; 系统主页面 系统注册页面 司机注册页面 管理员主页面 订单评价页面 货物信息页面 个人信息页面 研究背景 随着我国物流行业的迅猛发展&#xff0c;货运管理系统的效率与安全性日益受到重视。在货运过程中&am…

11.4OpenCV_图像预处理习题02

1.身份证号码识别&#xff08;结果&#xff1a;身份证号识别结果为&#xff1a;911124198108030024&#xff09; import cv2 import numpy as np import paddlehub as hubdef get_text():img cv2.imread("images1/images/shenfen03.jpg")# 灰度化gray_img cv2.cvt…

推荐:自然语言处理方向的一些创新点

以下是自然语言处理研究方向的一些创新点&#xff1a; 一、预训练模型的改进与优化 模型架构创新 融合多模态信息&#xff1a; 传统的自然语言处理模型主要处理文本信息。创新点在于将图像、音频等多模态信息融合到预训练模型中。例如&#xff0c;对于描述一幅画的文本&#x…

<项目代码>YOLOv8 煤矸石识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

netty之实现一个redis的客户端

写在前面 本文看下如何使用redis来实现一个类似于redis官方提供的redis-cli.exe的客户端工具。 1&#xff1a;用到的模块 主要需要用到netty针对redis的编解码模块&#xff0c;可以解析redis的协议&#xff0c;从而可以实现和redis交互的功能。 2&#xff1a;正文 首先来…

防重方案-订单防重方案笔记

订单防重设计 订单重复提交概念解决方案前端防重机制后端防重机制利用Token机制基于数据库的唯一索引 Token机制方案介绍 其他 订单重复提交概念 重复提交指&#xff0c;连点按钮进行重复提交操作&#xff0c;不包括刷新后的重新下单&#xff0c;重新下单已非同一订单的概念。…

Vision - 开源视觉分割算法框架 Grounded SAM2 配置与推理 教程 (1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/143388189 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Ground…

【C++刷题】力扣-#697-数组的度

题目描述 给定一个非空且只包含非负数的整数数组 nums&#xff0c;数组的 度 的定义是指数组里任一元素出现频数的最大值。 你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组&#xff0c;返回其长度。 示例 示例 1 输入&#xff1a;nums [1,2,2,3,1] 输出…

LocalDate 类常用方法详解(日期时间类)

LocalDate 类常用方法详解 LocalDate 是 Java 8 引入的日期时间API中的一个类&#xff0c;用于表示不含时间和时区的日期&#xff08;年、月、日&#xff09;。以下是一些常用的 LocalDate 方法&#xff1a; 创建 LocalDate 实例 now()&#xff1a;获取当前日期 LocalDate t…

一些常用的react hooks以及各自的作用

一些常用的react hooks以及各自的作用 一、React Hooks是什么二、一些常用的Hooks以及各自的作用1、useState2、useEffect3、useContext4、useMemo5、useCallback6、useReducer7、useRef 一、React Hooks是什么 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情…

不用买PSP,画质甚至更好,这款免费神器让你玩遍经典游戏

作为掌机游戏爱好者的福音&#xff0c;PPSSPP模拟器为玩家带来了前所未有的PSP游戏体验&#xff0c;彻底改变了掌机游戏的体验方式。这款精湛的软件不仅完美复刻了PSP主机的游戏体验&#xff0c;更通过先进的模拟技术&#xff0c;将经典游戏提升到了全新的高度。对于那些珍藏PS…

lua学习笔记---面向对象

在 Lua 中&#xff0c;封装主要通过元表&#xff08;metatable&#xff09;来实现。元表可以定义 __index、__newindex、__call 等元方法来控制对表的访问和赋值行为。 __index 元方法&#xff1a;当尝试访问一个不存在的键时&#xff0c;Lua 会查找元表的 __index 字段。如果 …

第15课 算法(下)

掌握冒泡排序、选择排序、插入排序、顺序查找、对分查找的的基本原理&#xff0c;并能使用这些算法编写简单的Python程序。 一、冒泡排序 1、冒泡排序的概念 冒泡排序是最简单的排序算法&#xff0c;是在一列数据中把较大&#xff08;或较小&#xff09;的数据逐次向右推移的…

golang通用后台管理系统03(登录校验,并生成token)

代码 package serviceimport ("fmt"//"fmt""gin/common""gin/config"sysEntity "gin/system/entity"sysUtil "gin/system/util""github.com/gin-gonic/gin""log" )func Login(c *gin.Contex…

Java环境下配置环境(jar包)并连接mysql数据库

目录 jar包下载 配置 简单连接数据库 一、注册驱动&#xff08;jdk6以后会自动注册&#xff09; 二、连接对应的数据库 以前学习数据库就只是操作数据库&#xff0c;根本不知道该怎么和软件交互&#xff0c;将存储的数据读到软件中去&#xff0c;最近学习了Java连接数据库…

快速遍历包含合并单元格的Word表格

Word中的合并表格如下&#xff0c;现在需要根据子类&#xff08;例如&#xff1a;果汁&#xff09;查找对应的品类&#xff0c;如果这是Excel表格&#xff0c;那么即使包含合并单元格&#xff0c;也很容易处理&#xff0c;但是使用Word VBA进行查找&#xff0c;就需要一些技巧。…

「C/C++」C/C++标准库 之 #include<ctime> 时间日期库

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…