Android 实现多进程通讯(如何实现多进程开发,Binder、AIDL)

news2024/11/14 13:43:05

目录


1)为什么App需要多进程

2)什么是多进程开发?

3)如何实现多进程开发?

4)跨进程间通讯(案例)

5)多进程需要注意什么问题?

6)多进程的底层原理是什么?【待写】



一、为什么App需要多进程?


某些应用场景下,单个进程可能无法满足需求,通过将任务分配到不同的进程中并行处理,可以提高系统整体的性能和响应速度。

在Android中,虚拟机分配给各个进程的运行内存是有限制值的,多进程app可以在系统中申请多份内存

通过创建独立的后台进程,可以确保应用在主进程被杀死或处于台时仍然能够执行一些必要的任务,如推送、消息接收等。

很多app都已经开始使用多进程,比如:推送,保活,插件化,内存不够用,webview,闹钟,电话等等。

在这里插入图片描述

二、什么是多进程开发?


多进程是指一个应用程序可以同时运行在多个独立的进程中。每个进程都有自己独立的虚拟机实例和资源管理器,并且它们之间相互隔离。一个应用可以有多个进程,就有多个dalivk虚拟机,对应多个内存空间。

默认情况下,Android应用程序在同一个进程中运行,即单进程模式。这意味着应用程序的所有组件(Activity、Service、BroadcastReceiver等)都在同一个进程中执行。但是,通过配置AndroidManifest.xml文件中的android:process属性,开发者可以为特定的组件或整个应用程序指定不同的进程名称,从而实现多进程开发。

三、如何实现多进程开发以及通讯?


默认情况下,启动一个APP,仅仅启动了一个进程,该进程名为包名,那如何定义多进程呢? Android 提供了一种方式,就是在 AndroidManifest 文件中可以通过 “android:process” 来指定进程:

  1. 不指定 process: 默认的进程,进程名为包名
  2. 指定 process,但以":"开头: 该进程为当前APP的私有进程,不允许其他APP访问
  3. 指定 process,但以小写字母开头的字符串: 该进程为全局进程 ,其他应用可设置相同的shareUID来共享该进程

场景

当应用的一部分需要高安全性或高稳定性时,比如支付模块或敏感数据处理模块。这里我将给出一个简化的案例:一个包含普通功能和支付功能的应用,我们将支付功能放在单独的进程中以提高安全性。

步骤一:定义支付进程的Service

<service  
    android:name=".PaymentService"  
    android:process=":payment"  
    android:exported="false" />

步骤二:创建PaymentService

public class PaymentService extends Service {  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // 接收来自其他组件的数据  
        String orderId = intent.getStringExtra("orderId");  
  
        // 模拟支付逻辑  
        boolean isSuccess = performPayment(orderId);  
  
        // 通知结果(实际开发中可能需要通过广播、AIDL等方式)  
        // 这里为了简化,我们假设有某种方式通知UI层  
  
        return START_NOT_STICKY;  
    }  
  
    private boolean performPayment(String orderId) {  
        // 这里实现具体的支付逻辑  
        // 例如调用第三方支付SDK等  
        return true; // 假设支付成功  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        // 如果需要,可以返回IBinder实现IPC  
        return null;  
    }  
}

步骤三:从主进程启动PaymentService

Intent intent = new Intent(this, PaymentService.class);  
intent.putExtra("orderId", "123456789");  
startService(intent);

如果支付服务需要返回结果给主进程,或者主进程需要向支付服务发送指令,下面我们需要学习一下跨进程通信(IPC)

四、跨进程间通讯


Android中支持的多进程通信方式主要有以下几种,它们之间各有优缺点,可根据使用场景选择选择:
1)BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。
2)Binder:是Android系统内部使用的一种高效IPC机制,基于C/S架构,通过内存映射实现高效的进程间通信。高效性,数据传输速度快。支持复杂的数据类型传递
3)ContentProvider:是Android系统中一种轻量级的跨进程通信方式,主要用于在不同应用程序之间共享数据。
4)AIDL:是Android系统中用于定义跨进程通信接口的一种语言,它允许定义可在不同进程间共享的服务接口。

这里我们主要介绍Binder。

4.1 Binder


优点

解决安全问题,内存不够的问题。
1)比如加载图片的时候,出现崩溃,导致安全问题出现。如果用子线程进行加载,那么即使崩溃了也不影响主线程。在比如微信小程序就是另外进程,不能影响微信。
2)为什么手机运行内存8G,16G,加载一张大图就会导致内存不够用呢?难道他有几G那么大?并不是,因为每个app运行,根据手机的不同,进程可以分到几十兆,或者几百兆的内存空间(如下图)。所以多个进程,那么内存就多了。
在这里插入图片描述

内存划分

进程之间的内存是相互隔离的。
在这里插入图片描述
那么如何才能实现进程之间相互通讯?
Android 为什么要增加Binder?

在这里插入图片描述
传统的方式,需要进行两次拷贝。
在这里插入图片描述Binder拷贝:
在这里插入图片描述
内存映射到底是啥?虚拟地址映射到物理地址,而MMAP,就是将用户空间映射到内存空间。不需要拷贝了。通过快捷方式进行举例。

4.2 AIDL


是什么?简化调用Binder的流程。因为Binder的规则还是比较复杂的,而AIDL可以直接生成这套规则。这就是他的作用。

接下来我们就使用AIDL实现进程间通讯。比如说,两个进程,A进程通过发送一个字符串,B进程收到后进行对应的逻辑处理。

(1)打开aidl,不打开,生成不了aidl文件

先在build.gradle文件里面添加一个
android {
	...    
    buildFeatures.aidl = true
    ...
}

(2)步骤 1: 定义AIDL接口

创建一个AIDL文件夹
在这里插入图片描述
创建AIDL文件
在这里插入图片描述

// IMessageService.aidl
package com.example.myapplicationa;

// Declare any non-default types here with import statements

interface IMessageService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     String sendMessage(String message);
}

(3)步骤 2: 实现AIDL接口

在这里插入图片描述

package com.example.myapplicationa

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import java.util.Locale

class MessageService : Service() {
    private val mBinder: IMessageService.Stub = object : IMessageService.Stub() {

        override fun sendMessage(message: String): String {
            // 这里可以添加一些处理逻辑,比如转换大小写等
            Log.d("AIDL A", "收到 sendMessage: ")
            return "Processed: " + message.uppercase(Locale.getDefault())
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return mBinder
    }
}

在AndroidManifest.xml中声明这个Service,并指定它运行在多进程中:

  <service
            android:name=".MessageService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.myapplicationa.IMessageService" />
            </intent-filter>
        </service>

(4)步骤 3: 在App B中绑定Service

将IMessageService.aidl文件复制到App B的src/main/java/com/example/myapplicationa目录下。
在这里插入图片描述
在App B中创建一个ServiceConnection来绑定到App A的Service:

package com.example.myapplicationb

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.util.Log
import androidx.activity.ComponentActivity
import com.example.myapplicationa.IMessageService


class MainActivity : ComponentActivity() {
    private var mMessageService: IMessageService? = null

    private var mIsBound = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

    private val mConnection: ServiceConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            mMessageService = IMessageService.Stub.asInterface(service)
            try {
                val response = mMessageService!!.sendMessage("Hello from App B")

                // 处理响应
                Log.d("AIDL B", "Received response: $response")
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
            mIsBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mMessageService = null
            mIsBound = false
        }
    }

    override fun onStart() {
        super.onStart()

        val intent = Intent()
        intent.setComponent(ComponentName("com.example.myapplicationa", "com.example.myapplicationa.MessageService"))
        bindService(intent, mConnection, BIND_AUTO_CREATE)
    }

    override fun onStop() {
        super.onStop()
        if (mIsBound) {
            unbindService(mConnection)
            mIsBound = false
        }
    }
}

运行程序,如果收到这个log,就说明成功了。

在这里插入图片描述

好了,以上就是AIDL的简单使用。

五、多进程需要注意什么问题?


1、静态成员和单例模式完全失效 。Android 为每一个进程分配一个独立的虚拟机,不同虚拟机在内存分配上有不同地址空间,这就导致多进程下访问同一个类的对象会产生多分副本。所以在一个进程中修改某个值,只会在当前进程有效,对其他进程不会造成任何影响。

2、线程同步机制完全失效。因为多进程的内存地址空间不同,锁的不是同一个对象,所以不管是锁对象还是锁全局对象都无法保证线程同步。

3、SharedPreferences 的可靠性下降。因为SharedPreferences 底层通过读写XML实现,并发读写显然是不安全的操作,甚至会出现数据错乱。

4、Application 会多次创建。

5.1 Application 会多次创建 的解决方法


在Application的onCreate中获取进程Id来判断不同进程,然后做不同的事情。

public class MyApplication extends BaseApplication {

    public static MyApplication instances;

    @Override
    public void onCreate() {
        super.onCreate();
        instances = this;

        if (isAppMainProcess()) {
        		.....
        }
    .....
    }
}

 public static boolean isAppMainProcess() {
        try {
            int pid = android.os.Process.myPid();
            String process = getAppNameByPID(instances, pid);
            if (TextUtils.isEmpty(process)) {
                return true;
            } else if ("com.xxx.xxx".equalsIgnoreCase(process)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return true;
        }
    }

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

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

相关文章

【Python机器学习】树回归——使用Python的tkinter库创建GUI

机器学习给我们提供了一些强大的工具&#xff0c;能从未知数据中抽取出有用的信息。因此&#xff0c;能否这些信息以易于人们理解的方式呈现十分重要。如果人们可以直接与算法和数据交互&#xff0c;将可以比较轻松的进行解释。其中一个能够同时支持数据呈现和用户交互的方式就…

手机IP地址:是根据网络还是设备决定的?

在日益数字化的今天&#xff0c;手机已经成为我们日常生活中不可或缺的一部分。它不仅是我们沟通的桥梁&#xff0c;更是我们获取信息、享受娱乐和完成工作的得力助手。然而&#xff0c;在使用手机上网的过程中&#xff0c;你是否曾经好奇过手机的IP地址是如何被分配的&#xf…

Java中class文件结构分析二

第17个常量池:01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01&#xff1a;tag位表示的是utf8类型的字面量常量 00 15 二个字节表示的是字面量常量的长度为21 接下来21个字节: 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56…

经典大语言模型解读(1):BERT——基于双向Transformer的预训练语言模型

论文&#xff1a;BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 前言 BERT&#xff08;Bidirectional Encoder Representation from Transformer&#xff09;是Google于2019年提出的预训练语言模型。与寻常的Transformer架构不同&#…

eval和长度限制

目录 源码 解决方案 方法一 方法二 方法三 源码 <?php $param $_REQUEST[param]; if(strlen($param)<17 && stripos($param,eval) false && stripos($param,assert) false) {eval($param); } ?> 限制条件&#xff1a; 传入的参数长度不能…

Go语言+Vue3开发前后端后台管理系统实战 用户管理的前端界面和表结构分析

首页&#xff1a; 用户管理界面&#xff1a; 到这一步以后来看一下后端代码的表结构是如何设计的&#xff1a; 后端代码中&#xff0c;使用的操作MySQL的技术是gorm&#xff1a; gorm.io/gorm v1.25.5其中&#xff0c;用户表的定义位置如下&#xff1a; 此时的完整代码如…

C++虚函数习题

#include <iostream>using namespace std;class Animal { public:Animal() {}virtual void perform()0; };class Lion:public Animal { public:Lion() {}void perform(){cout << "狮子会吃小朋友&#xff01;&#xff01;&#xff01;快跑&#xff01;&#x…

设计模式(1)创建型模式和结构型模式

1、目标 本文的主要目标是学习创建型模式和结构型模式&#xff0c;并分别代码实现每种设计模式 2、创建型模式 2.1 单例模式&#xff08;singleton&#xff09; 单例模式是创建一个对象保证只有这个类的唯一实例&#xff0c;单例模式分为饿汉式和懒汉式&#xff0c;饿汉式是…

IP问题总结

IP基础知识 IP 在 TCP/IP 参考模型中处于第三层&#xff0c;也就是⽹络层。 ⽹络层的主要作⽤是&#xff1a;实现主机与主机之间的通信&#xff0c;也叫点对点&#xff08;end to end&#xff09;通信。 1.⽹络层与数据链路层有什么关系呢&#xff1f; 其实很容易区分&#…

eNSP 华为浮动路由

R1&#xff1a; <Huawei>system-view [Huawei]sysname R1 [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 172.16.1.1 24 [R1-GigabitEthernet0/0/0]int g0/0/1 [R1-GigabitEthernet0/0/1]ip add 10.10.1.1 24 [R1-GigabitEthernet0/0/1]quit [R1]vlan 10 //e口是…

使用docker搭建aria2-pro+ariang并在alist中配置

一、安装aria2-pro 1.创建映射目录 # 配置目录 mkdir -p /usr/local/docker/aria2/config # 下载目录 mkdir -p /share_root/download-aria22.创建容器 docker run -d \--name aria2-pro \--restart unless-stopped \--log-opt max-size1m \--network host \-e PUID$UID \-e …

【秋招笔试】8.12-4399秋招(第一套)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…

全网最详细HAProxy入门小知识

目录 一. 负载均衡 负载均衡的意义&#xff1a; 负载均衡的类型&#xff1a; 二. HAProxy 简介 HAProxy 的特点&#xff1a; 社区版和企业版&#xff1a; 三. HAProxy 的安装和服务信息 1、实验环境 1&#xff09;安装并配置 Nginx 2&#xff09;在客户端测试 2、安装…

【项目实战】C++视频共享点播系统

目录 一、项目介绍 1.1 对视频共享点播系统的认识 1.2服务端程序负责功能 1.3 服务端功能模块划分 1.4 项目界面演示 1.5预备知识 二.环境搭建 2.1 安装 Jsoncpp 库 2.1.1 使用jsoncpp 2.2 引入httplib库 2.2.1 安装Git&#xff08;如果你的系统尚未安装Git&#xf…

基于Hadoop的共享单车分布式存储与计算

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍研究背景研究目的和意义国内外研究现状总体研究思路数据可视化每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 共享单车的普及带…

Elasticsearch:使用 ES|QL 进行地理空间搜索

作者&#xff1a;来自 Elastic Craig Taverner 多年来&#xff0c;Elasticsearch 一直具有强大的地理空间搜索和分析功能&#xff0c;但其 API 与典型的 GIS 用户习惯的 API 截然不同。在过去的一年中&#xff0c;我们添加了 ES|QL 查询语言&#xff0c;这是一种管道查询语言&a…

React原理之React整体渲染流程

前置知识&#xff1a;深度优先搜索(DFS)、Fiber 节点 在上一篇 React原理篇之 React 整体架构解读中&#xff0c;提到了 Fiber 架构中的几个核心概念&#xff1a; Scheduler&#xff08;调度器&#xff09;&#xff1a;根据任务的优先级安排任务执行顺序。Reconciler&#xff…

CUDA-MODE 第一课课后实战(下)

我的课程笔记&#xff0c;欢迎关注&#xff1a;https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode CUDA-MODE 第一课课后实战&#xff08;下&#xff09; Nsight Compute Profile结果分析 继续对Nsight Compute的Profile结果进行分析&#xff0…

PyQT 串口改动每次点开时更新串口信息

class MainWindow(QWidget, Ui_Form):def __init__(self):super().__init__(parentNone)self.setupUi(self)self.comboBox.installEventFilter(self) # 加载事件过滤器self.comboBox.addItems(get_ports())def eventFilter(self, obj, event): # 定义事件过滤器if isinstance(o…

前端容器化部署:解决重启容器时的静态资源丢失问题

文章目录 什么是前端容器化&#xff1f;重启容器时静态资源丢失的问题解决静态资源丢失的方案1. 使用持久化卷创建和挂载卷使用Docker Compose定义卷 2. 使用对象存储将静态资源上传到对象存储 3. 使用构建时持久化使用CI/CD管道 4. 使用动态加载和缓存使用浏览器缓存使用服务端…