北京营销型网站建设,河南手机网站建设多少钱,wordpress中的搜索,设计效果图怎么做前言
我们经常开发过程中经常会听到线程和进程#xff0c;在讲述Android进程多进程前我打算先简单梳理一下这俩者。
了解什么是进程与线程
进程#xff1a; 系统中正在运行的一个应用程序#xff0c;某个程序一旦运行就是一个进程#xff0c;是资源分配的最小单位#…前言
我们经常开发过程中经常会听到线程和进程在讲述Android进程多进程前我打算先简单梳理一下这俩者。
了解什么是进程与线程
进程 系统中正在运行的一个应用程序某个程序一旦运行就是一个进程是资源分配的最小单位线程 程序执行的最小单位包含在进程中一个进程可以包含多个线程。
Android 应用中的多进程
1.dalivk虚拟机
Android 系统的底层任务管理以及驱动都是基于 Linux 系统一个 Android 系统其实就是一个 Linux 系统通过 adb shell 进入连接的手机就可以看到 Linux 系统的文件系统
像在运行一个 Java 程序我们知道 Linux 系统会启动一个Java虚拟机来运行该 Java 程序而 Android 系统是一个特殊的 Linux 系统当启动一个 APP系统会为该 APP 分配一个 Linux 进程而该进程中就会有一个 dalivk 虚拟机又称为 DVM 实例来运行该 APP所以 dalivk 虚拟机就是用来运行 APP 程序
不同的APP运行在不同的进程中对应着不同的dalivk虚拟机就对应着不同的地址空间。反过来在一个应用内如果新开一个进程那么由于系统在新开进程的时候会分配独立的dalivk虚拟机那么对于该APP内的不同进程的实例是互相独立互不影响。
每个进程有独立的dalivk虚拟机对应着单独的内存空间一个应用可以有多个进程就有多个dalivk虚拟机对应多个内存空间一个进程可以被多个应用访问多个应用可以共享该进程
2.Linux 系统组成
在 Linux 系统中虚拟内存空间我理解的就是运行软件程序的空间是对物理空间的映射只有 4G最高的 1GB对应虚拟地址0xC0000000到0xFFFFFFFF被成为内核空间而较低的 3GB对应虚拟地址0x00000000到0xBFFFFFFF被成为用户空间。内核空间是Linux内核运行空间用户空间是应用程序的运行空间。如图所示
内核空间可以访问受保护的内存空间有访问底层硬件设备的所有权限用户空间上层应用程序和Native层的运行空间用户空间没法直接访内核空间需要系统调用才可以访问内核空间。
不同进程之间用户空间是不共享但是内核空间是可以共享。
3.Android定义多进程的三种方式
默认情况下启动一个APP仅仅启动了一个进程该进程名为包名那如何定义多进程呢 Android 提供了一种方式就是在 AndroidManifest 文件中可以通过 “android:process” 来指定进程
不指定 process 默认的进程进程名为包名指定 process但以:开头 该进程为当前APP的私有进程不允许其他APP访问指定process但以小写字母开头的字符串 该进程为全局进程 其他应用可设置相同的shareUID来共享该进程
4.为什么要引入多进程
为什么一个 Android 应用要引入多进程多进程有哪些应用场景呢
通常在下面两种情况下需要引入多进程
由于 Android 系统会限制每个应用的最大内存所以如果一个应用需要更多可用的内存时就需要引入多进程让某些模块运行在另外的进程中获取更多的内存由于不同的应用运行在不同的进程中但是如果两个不同的应用之间需要进行数据通信
5.跨进程通信
既然在 Android 中引入了多进程而对于进程的用户空间不共享那么多进程之间怎么通信呢
这种多进程通信又称为IPCInter Process Communication
对于IPC并不是Android系统特有的在Linux系统中就存在的跨进程通信在Linux系统中常见的IPC方式有
管道Pipe 在内存中创建一个共享文件利用共享文件传递信息。该共享文件并不是文件系统只存在于内存中只能在一个方向上流动信号Signal 异步通信。信号在用户空间和内核空间之间交互内核可利用信号来通知用户空间的进程发生哪些系统事件。不适用于信号交换适用于过程中断控制信号量Semaphore 控制多个进程对共享资源的访问。主要是进程间以及同一进程不同线程之间的同步手段消息队列 Message Queue 存放在内存中并由消息对了标识符标识允许一个或多个进程对它进行读写消息。信息会复制两次不适用于频繁或信息量大的通信共享内存Shared Memory 直接读写内核的一块内存空间。不需要进行数据拷贝套接字Socket 不同机器之间进程间通信。
除去 Socket其他的都是基于 Binder 机制实现的
6.多进程带来的问题
在进程结构中也提到了用户空间是不共享的并且每个进程都是对应单独的系统堆栈区、静态区等那么多进程也引入了一些问题
每个进程都是保持自己单独的静态成员变量和单例每个进程都是单独的进程锁SharedPreferences可靠性下降不支持并发写对于单个APP的多进程就会创建多个Application每个进程都会拥有己的Application对象。
好了到了这里我们对进程和线程应该有了基本认识同时也知道了常用的跨进程通信方式包括Android常用的几种IPC方式接下来我们就对Android常用的几种IPC方式做一个深入的了解。
认识IPC
进程间通信InterProcess Communication缩写IPC是指在不同进程之间传播或交换信息。进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元。
IPC不是Android中所独有的任何一个操作系统都需要有相应的IPC机制。在Android系统中一个进程会对应一个虚拟机实例不同的虚拟机在内存分配上有不同的地址空间所以只有在多进程的环境下才需要考虑使用IPC进行通讯。Android中的应用程序可以为一个进程也可以配置成多进程每个进程都在自己独立的空间中运行。
在Android中有两个典型的场景第一种是应用自身需要开启多个进程比如多模块应用由于系统设置了应用获取的最大内存限制为了获得更多的内存空间将不同的模块放在不同的线程中。另外一种情况就是获取其他应用里的数据典型的案例就是获取通讯录和短信。
Android中的多进程模式
Android默认进程是运行在默认名为包名的进程中除非特别指定所有的组件都运行在默认进程中。可以通过修改AndroidManifest文件在 application 标签下添加 android:process 属性可以修改Android默认的进程名字
applicationandroid:name.MainApplicationandroid:allowBackuptrueandroid:iconmipmap/ic_launcherandroid:labelstring/app_nameandroid:roundIconmipmap/ic_launcher_roundandroid:supportsRtltrueandroid:processcom.test.processandroid:themestyle/Theme.Sunflower/application在Android中只有给四大组件Activity Service Broadcast ContentProvider设置android:process属性这一种使用多进程的方法。
serviceandroid:name.service.TestServiceandroid:process:services /上面的代码为TestService指定了process属性为应用增加了一个新的进程。当TestService 启动时系统会为它创建单独的进程。使用
adb shell ps | grep [包名] 查看进程信息.
注意上面中的XML代码process 属性有两种设置方式一种是如上文中:开头后面接进程名一种则是完整的进程名例如android:process“com.packages.name.services”。前者会在进程名前加上包名“com.packages.name:services”且规定了进程为当前应用的私有进程其他应用的组件不可以和它使用统一进程。后者则你写了什么进程名就是什么且为全局进程其他应用也可以使用该进程。
虽然开启多进程的方法很简单但是这种看似简单的操作稍有不深就会带来巨大的问题 问题一多进程会造成Application的多次创建当一个组件需要运行在新的进程中时实际的创建过程就相当于又重新启动了一次应用应用启动必然会创建Application。而运行在不同进程中的组件不仅属于不同的虚拟机而且其Application也是不同的。 问题二多进程会导致静态成员和单例完全无效由于不同进程都被分配了独立且不同的虚拟机其在内存分配上有这不同的地址空间。这就会导致在着一个类的多个副本各自修改互不影响。 问题三多进程模式下线程的同步机制也会失效因为不同的进程其线程不所属同一内存那么无论是对象锁还是类锁锁本身都不是同一个了。 问题四多进程模式下SharedPreferences风险会增大SharedPreferences底层是通过文件读写实现的并发操作可能会造成问题。
总之在不同进程中的组件如果使用内存来通讯都会存在隐患或者直接失败。这就是多进程带来的最主要的影响。
Android中的跨进程通讯IPC
接下来讲解一下多进程间通讯的方式。
1:使用Bundle
在Android开发中我们通过Intent启动Activity、Service和Receiver都是可以通过Bundle传递参数的。它实现了Parcelable接口并以键值对的方式保存数据。可以将其视为一个容器其支持基本数据类型string、int、boolean、byte、float、long、double以及它们对应的数据。当需要传递对象或对象数组时被传递的对象必须实现Serialiable或Parcelable接口。
接下来我们通过一个Activity和一个Service来看一下如何利用Bundle实现跨进程通讯。
首先让二者运行在不同的进程中
activity android:name.process.TestActivity/
serviceandroid:name.process.MessengerServiceandroid:process:services /在配置文件指定Service的运行线程即可。
在通过 bindService(Intent, ServiceConnection,int)为TestActivity绑定服务时为intent添加bundle
val intent Intent(this, MessengerService::class.java)
val bundle Bundle()
bundle.putString(name, Butler)
bundle.putInt(age, 28)
intent.putExtra(message, bundle)
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)在service中的onBind方法接受数据
override fun onBind(intent: Intent?): IBinder? {intent?.getBundleExtra(message)?.apply {Log.e(Name is:, ${this.getString(name)})Log.e(age is:, ${this.getInt(age) })}return mMessenger.binder
}运行结果如下 使用方法很简单跟我们平时使用Bundle没有什么区别。
2:使用文件共享
通过共享文件不同的进程可以使用读写文件的方式交换数据。在Android中对文件的并发读写并没有什么特殊的要求。虽然这可能造成问题但是却依然可以帮我们在不同进程间传递数据。我们创建一个新的ActivtyFileActivity并指定其进程android:process“:file”
在TestActivity中写入文件并实现跳转
findViewByIdButton(R.id.file).setOnClickListener {val coroutineScope CoroutineScope(SupervisorJob() Dispatchers.IO)coroutineScope.launch {val message name:Butler,age:28try {val fileOutputStream openFileOutput(FileName, MODE_PRIVATE)fileOutputStream.write(message.toByteArray())fileOutputStream.close()} catch (e: Exception) {print(e.message)} finally {startActivity(Intent(thisTestActivity, FileActivity::class.java))}}
}在FileActivity读取文件内容
coroutineScope.launch {try {val fileInputStream openFileInput(FileName)var n 0val sBuffer StringBuffer()while (n ! -1) {n fileInputStream.read()val by n.toChar()sBuffer.append(by)}Log.e(message:,$sBuffer)} catch (e:Exception){print(e.message)} finally {}
}运行结果如下 注意⚠️不要使用SharedPreferences去做跨进程通讯原则上它不支持多进程。虽然它本质上也是一个文件但是由于它在应用运行时会再内存中加载缓存然而进程间是不能内存共享的每个进程操作的SharedPreferences都会是一个单独的实例这就会导致安全性问题这个问题只能通过多进程间其它的通信方式或者是在确保不会同时操作SharedPreferences数据的前提下使用SharedPreferences来解决。 3:使用ContentProvider
ContentProvider提供一种应用管理其自身和其他应用所存储数据的能力并提供与其他应用共享这些数据的方法。它会封装数据并提供用于定义数据安全性的机制。无论你是否需要和其他应用分享数据你都可以使用ContentProvider去访问这些数据虽然当无需和其他应用共享数据时没必要使用它。系统预置了许多ContentProvider比如通话记录通讯录信息等只需要通过ContentProvider就可以拿到这些信息。ContentProvider以一个或多个表的形式将数据呈现给外部应用这些表与关系型数据库中的表类似。行表示提供程序收集的某种类型数据的实例行中的每一列表示为一个实例所收集的单个数据。
通常有两个典型的使用场景一种是通过实现代码访问其他应用中的现有的ContentProvider用来获得数据另一种是创建新的ContentProvider与其他应用共享数据。
下面我们用一个最简单的例子演示一下使用它进行跨进程通讯。为了方便演示我们在同一个应用内进行
首先创建一个ContentProvider。代码很简单就是一个使用Room的数据持久化只做了insert和查询的逻辑处理。
class UserProvider : ContentProvider() {private lateinit var appDatabase: AppDatabaseprivate var userDao: UserDao? nulloverride fun onCreate(): Boolean {appDatabase Room.databaseBuilder(context!!, AppDatabase::class.java, database-provider).build()userDao appDatabase.userDao()return true}override fun query(uri: Uri,projection: Arrayout String?,selection: String?,selectionArgs: Arrayout String?,sortOrder: String?): Cursor? {return userDao?.getAll()}override fun getType(uri: Uri): String? {return }override fun insert(uri: Uri, values: ContentValues?): Uri? {val user User(System.currentTimeMillis().toInt(),values?.getAsString(name),values?.getAsInteger(age))Log.e(-------${user.firstName}, ------------${user.age})userDao?.insertAll(user)return uri}override fun delete(uri: Uri, selection: String?, selectionArgs: Arrayout String?): Int {//不展示return 0}override fun update(uri: Uri,values: ContentValues?,selection: String?,selectionArgs: Arrayout String?): Int {//不展示return 0}
}接下来在配置文件里配置它并指明其所在的进程process。
providerandroid:name.provider.UserProviderandroid:authoritiescom.hirzy.test.providerandroid:permissioncom.karl.PROVIDERandroid:process:provider /然后创建ActivityProviderActivity并让它运行在应用的默认进程里面
class ProviderActivity : AppCompatActivity() {RequiresApi(Build.VERSION_CODES.O)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_provider)val textName findViewByIdEditText(R.id.et_name)val textAge findViewByIdEditText(R.id.et_age)val textView findViewByIdTextView(R.id.tv)findViewByIdButton(R.id.btn_save).setOnClickListener(){if (textName.text ! null textAge.text ! null) {val uri Uri.parse(content://com.hirzy.test.provider)val contentValue ContentValues()contentValue.put(name, ${textName.text})contentValue.put(age, textAge.text.toString().toInt())contentResolver.insert(uri, contentValue)}}findViewByIdButton(R.id.btn_find).setOnClickListener(){textView.text val uri Uri.parse(content://com.hirzy.test.provider)val query contentResolver.query(uri, null, null, null)var text while (query?.moveToNext()!!){text 姓名${query.getString(1)} 年龄${query.getInt(2)}\n}textView.text text}}
}运行效果如下
可以看到处于两个不同进程中的Activity和Provider成功的实现了数据通讯。但是由于设计原因ContentProvider只支持增删改查更像是一个跨进程的数据库。
4:使用Messenger
Messenger是跨进程通信的类通过它可以再不同进程中传递Message。底层通过AIDL实现可以理解为是官方为了怕我们使用AIDL太过于繁琐而提供的一种简单的方案。
它是使用也很简单首先创建服务端ServiceMessengerService并设置它所在的进程
android:process:servicesclass MessengerService : Service() {private val mHandlerThread: HandlerThread HandlerThread(服务端)private lateinit var mMessenger: Messengerprivate lateinit var mHandler: Handleroverride fun onCreate() {super.onCreate()mHandlerThread.start()mHandler object : Handler(mHandlerThread.looper) {override fun handleMessage(msg: Message) {if (msg.what 0) {val obtain Message.obtain(msg)obtain.what 1obtain.arg2 2*obtain.arg1Thread.sleep(2000L)obtain.replyTo.send(obtain)return}super.handleMessage(msg)}}mMessenger Messenger(mHandler)}
}接受客户端传来的Int值延迟两秒后并返回它的倍数。
接下来在应用默认进程中实现一下客户端Activity (MessengerActivity)
class MessengerActivity : AppCompatActivity() {private lateinit var mServiceMessenger: Messengerprivate val mServiceConnection object : ServiceConnection {override fun onServiceConnected(name: ComponentName?, service: IBinder?) {mServiceMessenger Messenger(service)}override fun onServiceDisconnected(name: ComponentName?) {}}private val handler: Handler object : Handler(Looper.getMainLooper()) {override fun handleMessage(msg: Message) {if (msg.what 1) {Log.e(${currentTime()}--客户端收到的消息, ${msg.arg2})}super.handleMessage(msg)}}private val mClientMessenger: Messenger Messenger(handler)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val intent Intent(this, MessengerService::class.java)val bundle Bundle()bundle.putString(name, Butler)bundle.putInt(age, 28)intent.putExtra(message, bundle)bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)setContentView(R.layout.activity_main_messenger)findViewByIdButton(R.id.Messenger).setOnClickListener {val nextInt Random().nextInt(100)Log.e(${currentTime()}--客户端发送的消息, $nextInt)val message Message.obtain(handler, 0, nextInt, 0)message.replyTo mClientMessengermServiceMessenger.send(message)}findViewByIdButton(R.id.file).setOnClickListener {val coroutineScope CoroutineScope(SupervisorJob() Dispatchers.IO)coroutineScope.launch {val message name:Butler,age:28try {val fileOutputStream openFileOutput(FileName, MODE_PRIVATE)fileOutputStream.write(message.toByteArray())fileOutputStream.close()} catch (e: Exception) {print(e.message)} finally {startActivity(Intent(thisMainMessengerActivity, FileActivity::class.java))}}}}override fun onDestroy() {unbindService(mServiceConnection)super.onDestroy()}fun currentTime(): String {val formatter SimpleDateFormat(yyyy年MM月dd日 HH:mm:ss)return formatter.format(Date(System.currentTimeMillis()))}
}Activity启动时和MessengerService绑定点击按钮向不同进程的服务发送数字并接受新的返回值。
Messenger通过Handler将Message发送到另一个进程实现了进程间通信底层依然是使用了Binder机制其本质上也是基于AIDL实现的。
Messenger的AIDL文件IMessenger内容如下
/* //device/java/android/android/app/IActivityPendingResult.aidl
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the License);
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an AS IS BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/package android.os;import android.os.Message;/** hide */
oneway interface IMessenger {void send(in Message msg);
}不难发现它的作用就是跨进程发送Message。它的大致流程如下
但是Messenger也有明显的缺点首先服务端是以串行的方式在处理消息不太适合用来出来大量的并发请求。其次它的主要作用是传递消息不太适合用来跨进程调用方法。
5:使用AIDL
通过上面我们知道Messenger实现跨进程通讯其本质上也是基于AIDL实现的这一节我们使用AIDL自己实现一个Messenger用来演示AIDL的使用以及穿插者学习Messenger的工作原理为了简化代码。我们并不直接使用Message作为我们传递的数据而是使用一个Bean作为传递的数据。其核心代码如下
package com.hirzy.test.aidlimport android.os.Parcel
import android.os.Parcelableclass Bean: Parcelable {var name: String? nullvar age 0constructor(parcel: Parcel) {name parcel.readString()age parcel.readInt()}}注意⚠️在AIDL中并不是所有的数据类型都是可以使用它支持一下数据类型
基本数据类型ArrayList且里面的元素必须是被AIDL支持的数据类型HashMap且里面的元素必须是被AIDL支持的数据类型实现了Parcelable接口的对象AIDL接口本身
所以我们的Bean类要实现Parcelable接口。同理上节中Messenger中传递的Message也是实现了Parcelable接口源码如下
/** Copyright (C) 2006 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the License);* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an AS IS BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package android.os;import android.compat.annotation.UnsupportedAppUsage;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;import com.android.internal.annotations.VisibleForTesting;/**** Defines a message containing a description and arbitrary data object that can be* sent to a {link Handler}. This object contains two extra int fields and an* extra object field that allow you to not do allocations in many cases.** p classnoteWhile the constructor of Message is public, the best way to get* one of these is to call {link #obtain Message.obtain()} or one of the* {link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull* them from a pool of recycled objects./p*/
public final class Message implements Parcelable {/*** User-defined message code so that the recipient can identify* what this message is about. Each {link Handler} has its own name-space* for message codes, so you do not need to worry about yours conflicting* with other handlers.*/public int what;/*** arg1 and arg2 are lower-cost alternatives to using* {link #setData(Bundle) setData()} if you only need to store a* few integer values.*/public int arg1;/*** arg1 and arg2 are lower-cost alternatives to using* {link #setData(Bundle) setData()} if you only need to store a* few integer values.*/public int arg2;/*** An arbitrary object to send to the recipient. When using* {link Messenger} to send the message across processes this can only* be non-null if it contains a Parcelable of a framework class (not one* implemented by the application). For other data transfer use* {link #setData}.** pNote that Parcelable objects here are not supported prior to* the {link android.os.Build.VERSION_CODES#FROYO} release.*/public Object obj;/*** Optional Messenger where replies to this message can be sent. The* semantics of exactly how this is used are up to the sender and* receiver.*/public Messenger replyTo;/*** Indicates that the uid is not set;** hide Only for use within the system server.*/public static final int UID_NONE -1;/*** Optional field indicating the uid that sent the message. This is* only valid for messages posted by a {link Messenger}; otherwise,* it will be -1.*/public int sendingUid UID_NONE;/*** Optional field indicating the uid that caused this message to be enqueued.** hide Only for use within the system server.*/public int workSourceUid UID_NONE;/** If set message is in use.* This flag is set when the message is enqueued and remains set while it* is delivered and afterwards when it is recycled. The flag is only cleared* when a new message is created or obtained since that is the only time that* applications are allowed to modify the contents of the message.** It is an error to attempt to enqueue or recycle a message that is already in use.*//*package*/ static final int FLAG_IN_USE 1 0;/** If set message is asynchronous *//*package*/ static final int FLAG_ASYNCHRONOUS 1 1;/** Flags to clear in the copyFrom method *//*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM FLAG_IN_USE;UnsupportedAppUsage/*package*/ int flags;/*** The targeted delivery time of this message. The time-base is* {link SystemClock#uptimeMillis}.* hide Only for use within the tests.*/UnsupportedAppUsageVisibleForTesting(visibility VisibleForTesting.Visibility.PACKAGE)public long when;/*package*/ Bundle data;UnsupportedAppUsage/*package*/ Handler target;UnsupportedAppUsage/*package*/ Runnable callback;// sometimes we store linked lists of these thingsUnsupportedAppUsage/*package*/ Message next;/** hide */public static final Object sPoolSync new Object();private static Message sPool;private static int sPoolSize 0;private static final int MAX_POOL_SIZE 50;private static boolean gCheckRecycle true;/*** Return a new Message instance from the global pool. Allows us to* avoid allocating new objects in many cases.*/public static Message obtain() {synchronized (sPoolSync) {if (sPool ! null) {Message m sPool;sPool m.next;m.next null;m.flags 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}/*** Same as {link #obtain()}, but copies the values of an existing* message (including its target) into the new one.* param orig Original message to copy.* return A Message object from the global pool.*/public static Message obtain(Message orig) {Message m obtain();m.what orig.what;m.arg1 orig.arg1;m.arg2 orig.arg2;m.obj orig.obj;m.replyTo orig.replyTo;m.sendingUid orig.sendingUid;m.workSourceUid orig.workSourceUid;if (orig.data ! null) {m.data new Bundle(orig.data);}m.target orig.target;m.callback orig.callback;return m;}/*** Same as {link #obtain()}, but sets the value for the emtarget/em member on the Message returned.* param h Handler to assign to the returned Message objects emtarget/em member.* return A Message object from the global pool.*/public static Message obtain(Handler h) {Message m obtain();m.target h;return m;}/*** Same as {link #obtain(Handler)}, but assigns a callback Runnable on* the Message that is returned.* param h Handler to assign to the returned Message objects emtarget/em member.* param callback Runnable that will execute when the message is handled.* return A Message object from the global pool.*/public static Message obtain(Handler h, Runnable callback) {Message m obtain();m.target h;m.callback callback;return m;}/*** Same as {link #obtain()}, but sets the values for both emtarget/em and* emwhat/em members on the Message.* param h Value to assign to the emtarget/em member.* param what Value to assign to the emwhat/em member.* return A Message object from the global pool.*/public static Message obtain(Handler h, int what) {Message m obtain();m.target h;m.what what;return m;}/*** Same as {link #obtain()}, but sets the values of the emtarget/em, emwhat/em, and emobj/em* members.* param h The emtarget/em value to set.* param what The emwhat/em value to set.* param obj The emobject/em method to set.* return A Message object from the global pool.*/public static Message obtain(Handler h, int what, Object obj) {Message m obtain();m.target h;m.what what;m.obj obj;return m;}/*** Same as {link #obtain()}, but sets the values of the emtarget/em, emwhat/em,* emarg1/em, and emarg2/em members.** param h The emtarget/em value to set.* param what The emwhat/em value to set.* param arg1 The emarg1/em value to set.* param arg2 The emarg2/em value to set.* return A Message object from the global pool.*/public static Message obtain(Handler h, int what, int arg1, int arg2) {Message m obtain();m.target h;m.what what;m.arg1 arg1;m.arg2 arg2;return m;}/*** Same as {link #obtain()}, but sets the values of the emtarget/em, emwhat/em,* emarg1/em, emarg2/em, and emobj/em members.** param h The emtarget/em value to set.* param what The emwhat/em value to set.* param arg1 The emarg1/em value to set.* param arg2 The emarg2/em value to set.* param obj The emobj/em value to set.* return A Message object from the global pool.*/public static Message obtain(Handler h, int what,int arg1, int arg2, Object obj) {Message m obtain();m.target h;m.what what;m.arg1 arg1;m.arg2 arg2;m.obj obj;return m;}/** hide */public static void updateCheckRecycle(int targetSdkVersion) {if (targetSdkVersion Build.VERSION_CODES.LOLLIPOP) {gCheckRecycle false;}}/*** Return a Message instance to the global pool.* p* You MUST NOT touch the Message after calling this function because it has* effectively been freed. It is an error to recycle a message that is currently* enqueued or that is in the process of being delivered to a Handler.* /p*/public void recycle() {if (isInUse()) {if (gCheckRecycle) {throw new IllegalStateException(This message cannot be recycled because it is still in use.);}return;}recycleUnchecked();}/*** Recycles a Message that may be in-use.* Used internally by the MessageQueue and Looper when disposing of queued Messages.*/UnsupportedAppUsagevoid recycleUnchecked() {// Mark the message as in use while it remains in the recycled object pool.// Clear out all other details.flags FLAG_IN_USE;what 0;arg1 0;arg2 0;obj null;replyTo null;sendingUid UID_NONE;workSourceUid UID_NONE;when 0;target null;callback null;data null;synchronized (sPoolSync) {if (sPoolSize MAX_POOL_SIZE) {next sPool;sPool this;sPoolSize;}}}/*** Make this message like o. Performs a shallow copy of the data field.* Does not copy the linked list fields, nor the timestamp or* target/callback of the original message.*/public void copyFrom(Message o) {this.flags o.flags ~FLAGS_TO_CLEAR_ON_COPY_FROM;this.what o.what;this.arg1 o.arg1;this.arg2 o.arg2;this.obj o.obj;this.replyTo o.replyTo;this.sendingUid o.sendingUid;this.workSourceUid o.workSourceUid;if (o.data ! null) {this.data (Bundle) o.data.clone();} else {this.data null;}}/*** Return the targeted delivery time of this message, in milliseconds.*/public long getWhen() {return when;}public void setTarget(Handler target) {this.target target;}/*** Retrieve the {link android.os.Handler Handler} implementation that* will receive this message. The object must implement* {link android.os.Handler#handleMessage(android.os.Message)* Handler.handleMessage()}. Each Handler has its own name-space for* message codes, so you do not need to* worry about yours conflicting with other handlers.*/public Handler getTarget() {return target;}/*** Retrieve callback object that will execute when this message is handled.* This object must implement Runnable. This is called by* the emtarget/em {link Handler} that is receiving this Message to* dispatch it. If* not set, the message will be dispatched to the receiving Handlers* {link Handler#handleMessage(Message)}.*/public Runnable getCallback() {return callback;}/** hide */UnsupportedAppUsagepublic Message setCallback(Runnable r) {callback r;return this;}/*** Obtains a Bundle of arbitrary data associated with this* event, lazily creating it if necessary. Set this value by calling* {link #setData(Bundle)}. Note that when transferring data across* processes via {link Messenger}, you will need to set your ClassLoader* on the Bundle via {link Bundle#setClassLoader(ClassLoader)* Bundle.setClassLoader()} so that it can instantiate your objects when* you retrieve them.* see #peekData()* see #setData(Bundle)*/public Bundle getData() {if (data null) {data new Bundle();}return data;}/*** Like getData(), but does not lazily create the Bundle. A null* is returned if the Bundle does not already exist. See* {link #getData} for further information on this.* see #getData()* see #setData(Bundle)*/public Bundle peekData() {return data;}/*** Sets a Bundle of arbitrary data values. Use arg1 and arg2 members* as a lower cost way to send a few simple integer values, if you can.* see #getData()* see #peekData()*/public void setData(Bundle data) {this.data data;}/*** Chainable setter for {link #what}** hide*/public Message setWhat(int what) {this.what what;return this;}/*** Sends this Message to the Handler specified by {link #getTarget}.* Throws a null pointer exception if this field has not been set.*/public void sendToTarget() {target.sendMessage(this);}/*** Returns true if the message is asynchronous, meaning that it is not* subject to {link Looper} synchronization barriers.** return True if the message is asynchronous.** see #setAsynchronous(boolean)*/public boolean isAsynchronous() {return (flags FLAG_ASYNCHRONOUS) ! 0;}/*** Sets whether the message is asynchronous, meaning that it is not* subject to {link Looper} synchronization barriers.* p* Certain operations, such as view invalidation, may introduce synchronization* barriers into the {link Looper}s message queue to prevent subsequent messages* from being delivered until some condition is met. In the case of view invalidation,* messages which are posted after a call to {link android.view.View#invalidate}* are suspended by means of a synchronization barrier until the next frame is* ready to be drawn. The synchronization barrier ensures that the invalidation* request is completely handled before resuming.* /pp* Asynchronous messages are exempt from synchronization barriers. They typically* represent interrupts, input events, and other signals that must be handled independently* even while other work has been suspended.* /pp* Note that asynchronous messages may be delivered out of order with respect to* synchronous messages although they are always delivered in order among themselves.* If the relative order of these messages matters then they probably should not be* asynchronous in the first place. Use with caution.* /p** param async True if the message is asynchronous.** see #isAsynchronous()*/public void setAsynchronous(boolean async) {if (async) {flags | FLAG_ASYNCHRONOUS;} else {flags ~FLAG_ASYNCHRONOUS;}}/*package*/ boolean isInUse() {return ((flags FLAG_IN_USE) FLAG_IN_USE);}UnsupportedAppUsage/*package*/ void markInUse() {flags | FLAG_IN_USE;}/** Constructor (but the preferred way to get a Message is to call {link #obtain() Message.obtain()}).*/public Message() {}Overridepublic String toString() {return toString(SystemClock.uptimeMillis());}UnsupportedAppUsageString toString(long now) {StringBuilder b new StringBuilder();b.append({ when);TimeUtils.formatDuration(when - now, b);if (target ! null) {if (callback ! null) {b.append( callback);b.append(callback.getClass().getName());} else {b.append( what);b.append(what);}if (arg1 ! 0) {b.append( arg1);b.append(arg1);}if (arg2 ! 0) {b.append( arg2);b.append(arg2);}if (obj ! null) {b.append( obj);b.append(obj);}b.append( target);b.append(target.getClass().getName());} else {b.append( barrier);b.append(arg1);}b.append( });return b.toString();}void dumpDebug(ProtoOutputStream proto, long fieldId) {final long messageToken proto.start(fieldId);proto.write(MessageProto.WHEN, when);if (target ! null) {if (callback ! null) {proto.write(MessageProto.CALLBACK, callback.getClass().getName());} else {proto.write(MessageProto.WHAT, what);}if (arg1 ! 0) {proto.write(MessageProto.ARG1, arg1);}if (arg2 ! 0) {proto.write(MessageProto.ARG2, arg2);}if (obj ! null) {proto.write(MessageProto.OBJ, obj.toString());}proto.write(MessageProto.TARGET, target.getClass().getName());} else {proto.write(MessageProto.BARRIER, arg1);}proto.end(messageToken);}public static final android.annotation.NonNull Parcelable.CreatorMessage CREATOR new Parcelable.CreatorMessage() {public Message createFromParcel(Parcel source) {Message msg Message.obtain();msg.readFromParcel(source);return msg;}public Message[] newArray(int size) {return new Message[size];}};public int describeContents() {return 0;}public void writeToParcel(Parcel dest, int flags) {if (callback ! null) {throw new RuntimeException(Cant marshal callbacks across processes.);}dest.writeInt(what);dest.writeInt(arg1);dest.writeInt(arg2);if (obj ! null) {try {Parcelable p (Parcelable)obj;dest.writeInt(1);dest.writeParcelable(p, flags);} catch (ClassCastException e) {throw new RuntimeException(Cant marshal non-Parcelable objects across processes.);}} else {dest.writeInt(0);}dest.writeLong(when);dest.writeBundle(data);Messenger.writeMessengerOrNullToParcel(replyTo, dest);dest.writeInt(sendingUid);dest.writeInt(workSourceUid);}private void readFromParcel(Parcel source) {what source.readInt();arg1 source.readInt();arg2 source.readInt();if (source.readInt() ! 0) {obj source.readParcelable(getClass().getClassLoader(), java.lang.Object.class);}when source.readLong();data source.readBundle();replyTo Messenger.readMessengerOrNullFromParcel(source);sendingUid source.readInt();workSourceUid source.readInt();}
}接下创建AIDL文件BeanAidl.aidl
package com.hirezy.test.aidl;parcelable Bean;
interface BeanAidl {Bean send(in Bean bean);
}注意⚠️自定义的对象必须import进来。除此之外AIDL中除了基本数据类型其他类型的参数必须标上方向in、out或者inout
in: 对象中的变量可以从客户端传到服务端, 而服务端对该对象的修改不会影响到客户端.out: 对象中的变量无法从客户端传到服务端, 服务端收到的是一个内部变量全为初始值的变量, 但是服务端对该对象的的修改可以影响到客户端inout: 对象中的变量既可以传到服务端, 服务队对其的修改也可以影响到客户端
接下来创建服务端代码AIdlService
class AIdlService : Service() {override fun onBind(intent: Intent?): IBinder? {return iService.asBinder()}private val iService object : BeanAidl.Stub() {override fun send(bean: Bean?): Bean {Thread.sleep(5000L)return bean}}
}我们需要做的是创建一个BeanAidl.Stub自动生成的代码稍后讲解的实例并将其作为IBinder返回即可。而在BeanAidl.Stub的具体实现中我们实现了BeanAidl.aidl中的send方法。当客户端通过AIDL调用send方法时最终服务端的send方法会被调用。并返回一个bean对象。
接下来我们看一下IMessenger中对应的实现。首先aidl文件中定义的send方法在Handler有具体实现 private final class MessengerImpl extends IMessenger.Stub {public void send(Message msg) {msg.sendingUid Binder.getCallingUid();Handler.this.sendMessage(msg);}}可见它将客户端的消息使用Handler又发了一遍。这就是上文中我们需要创建Handler并使用handleMessage来处理客户端发送的消息。那么Handler哪里来的呢又是怎么和Messenger关联了呢我们接着看MessengerService.kt的代码
private val mHandlerThread: HandlerThread HandlerThread(服务端)
private lateinit var mMessenger: Messenger
private lateinit var mHandler: Handleroverride fun onCreate() {super.onCreate()mHandlerThread.start()mHandler object : Handler(mHandlerThread.looper) {override fun handleMessage(msg: Message) {if (msg.what 0) {val obtain Message.obtain(msg)obtain.what 1obtain.arg2 2*obtain.arg1Thread.sleep(2000L)obtain.replyTo.send(obtain)return}super.handleMessage(msg)}}mMessenger Messenger(mHandler)
}不难看到在创建Messenger实例时必须传入一个Handler对象。因为它只有一个构造方法
private final IMessenger mTarget;public Messenger(Handler target) {mTarget target.getIMessenger();
}而Handler的getIMessenger方法实现如下
UnsupportedAppUsage
final IMessenger getIMessenger() {synchronized (mQueue) {if (mMessenger ! null) {return mMessenger;}mMessenger new MessengerImpl();return mMessenger;}
}注意里面的MessengerImpl 正是上文中AIDL的send方法的实现处。就是那个Handler里的内部类。 而在services的onBinder方法中
public IBinder getBinder() {return mTarget.asBinder();
}可以明显看到调用了asBinder方法。 注意上文中我们的服务端代码会延时五秒返回给客户端信息。此时我们会发现应用的页面被阻塞了无法响应任何事件。而且有时还会触发ANR异常。这是因为客户端调用的方法是运行在服务端的Binder线程池中这时客户端线程会挂起此时如果服务端方法执行比较耗时会导致客户端线程的阻如果是UI线程的话就触发ANR异常。因此如果明确知道服务端要进行耗时操作一定要在子线程里去访问服务端的代码。然而服务端则不同它本身就是运行在Binder线程池中的所以无需开启新的子线程唯一需要注意的就是当多个客户端和服务端通信时需要保证线程安全。 AIDL同样支持接口回调的监听方式。首先创建一个新的AILDChangeListener
package com.hirezy.test.aidl;parcelable Bean;interface ChangeListener {void onChangeListener(in Bean bean);
}然后在BeanAidl里增加如下方法
void sendBean(in Bean bean);
void setListener(in ChangeListener listener);
void removeListener(in ChangeListener listener);修改AIdlService
private var listener: ChangeListener? null
private val iService object : BeanAidl.Stub() {override fun send(bean: Bean?): Bean {bean!!.age 2 * bean!!.ageThread.sleep(5000L)listener?.onChangeListener(bean)return bean}override fun sendBean(bean: Bean?) {listener?.onChangeListener(bean)}override fun setListener(on: ChangeListener?) {listener on}override fun removeListener(on: ChangeListener?) {listener null}}
客户端AidlActivity添加如下代码即可实现监听
private val listener object : ChangeListener.Stub() {override fun onChangeListener(bean: Bean) {textView.text bean.toString()Log.i(bean,bean info is:${bean.toString()})}
}AIDL的工作原理
Android 接口定义语言 (Android Interface Definition Language) 是一款可供用户用来抽象化IPC的工具具体通信还是得用Binder来进行。以在 .aidl 文件中指定的接口为例各种构建系统都会使用 aidl 二进制文件构造 C 或 Java 绑定以便跨进程使用该接口。
AIDL 可以在 Android 中的任何进程之间使用在平台组件之间使用或在应用之间使用均可。但是AIDL 绝不能用作应用的API。
AIDL 使用 Binder 内核驱动程序进行调用。当发出调用时系统会将方法标识符和所有对象打包到某个缓冲区中然后将其复制到远程进程该进程中有一个Binder线程正在等待读取数据。Binder 线程收到某个事务的数据后该线程会在本地进程中查找原生桩对象然后此类会解压缩数据并调用本地接口对象。此本地接口对象正是服务器进程所创建和注册的对象。当在同一进程和同一后端中进行调用时不存在代理对象因此直接调用即可无需执行任何打包或解压缩操作。
Binder就是一个很深的话题由于它本身很复杂。这里我们就不过多的探讨它的底层原理了。在Android开发中Binder主要用在Service中其中普通Service中的Binder不涉及进程间通信。这里主要讲跨进程的只是在AIDL和Messenger中Messenger的底层其实是AIDL所以用AIDL来探究一下Binder的工作机制。
我们新建一个最简单的AIDLTestAidl文件
package com.hirezy.aidl;interface TestAidl {void fun(int val);void func(int val);
}此时SDK会为我们自动生成AIDL所生成的Binder类它处于build/generated目录下
/** This file is auto-generated. DO NOT MODIFY.*/
package com.hirezy.aidl;
public interface TestAidl extends android.os.IInterface
{/** Default implementation for TestAidl. */public static class Default implements com.hirezy.aidl.TestAidl{Override public void fun(int val) throws android.os.RemoteException{}Override public void func(int val) throws android.os.RemoteException{}Overridepublic android.os.IBinder asBinder() {return null;}}/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.hirezy.aidl.TestAidl{private static final java.lang.String DESCRIPTOR com.hirezy.aidl.TestAidl;/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.hirezy.aidl.TestAidl interface,* generating a proxy if needed.*/public static com.hirezy.aidl.TestAidl asInterface(android.os.IBinder obj){if ((objnull)) {return null;}android.os.IInterface iin obj.queryLocalInterface(DESCRIPTOR);if (((iin!null)(iin instanceof com.hirezy.aidl.TestAidl))) {return ((com.hirezy.aidl.TestAidl)iin);}return new com.hirezy.aidl.TestAidl.Stub.Proxy(obj);}Override public android.os.IBinder asBinder(){return this;}Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{java.lang.String descriptor DESCRIPTOR;switch (code){case INTERFACE_TRANSACTION:{reply.writeString(descriptor);return true;}case TRANSACTION_fun:{data.enforceInterface(descriptor);int _arg0;_arg0 data.readInt();this.fun(_arg0);reply.writeNoException();return true;}case TRANSACTION_func:{data.enforceInterface(descriptor);int _arg0;_arg0 data.readInt();this.func(_arg0);reply.writeNoException();return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.hirezy.aidl.TestAidl{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote remote;}Override public android.os.IBinder asBinder(){return mRemote;}public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR;}Override public void fun(int val) throws android.os.RemoteException{android.os.Parcel _data android.os.Parcel.obtain();android.os.Parcel _reply android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(val);boolean _status mRemote.transact(Stub.TRANSACTION_fun, _data, _reply, 0);if (!_status getDefaultImpl() ! null) {getDefaultImpl().fun(val);return;}_reply.readException();}finally {_reply.recycle();_data.recycle();}}Override public void func(int val) throws android.os.RemoteException{android.os.Parcel _data android.os.Parcel.obtain();android.os.Parcel _reply android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(val);boolean _status mRemote.transact(Stub.TRANSACTION_func, _data, _reply, 0);if (!_status getDefaultImpl() ! null) {getDefaultImpl().func(val);return;}_reply.readException();}finally {_reply.recycle();_data.recycle();}}public static com.hirezy.aidl.TestAidl sDefaultImpl;}static final int TRANSACTION_fun (android.os.IBinder.FIRST_CALL_TRANSACTION 0);static final int TRANSACTION_func (android.os.IBinder.FIRST_CALL_TRANSACTION 1);public static boolean setDefaultImpl(com.hirezy.aidl.TestAidl impl) {if (Stub.Proxy.sDefaultImpl null impl ! null) {Stub.Proxy.sDefaultImpl impl;return true;}return false;}public static com.hirezy.aidl.TestAidl getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}public void fun(int val) throws android.os.RemoteException;public void func(int val) throws android.os.RemoteException;
}上面的代码是系统根据DemoAidl.aidl生成的。它是一个实现了IInterface这个接口的接口要想使用Binder机制实现跨进程通讯就必须实现该接口。这个类主要有结构如下
首先声明两个方法fun(int val)和func(int val)也就是我们在AIDL文件中定义的两个方法。同时声明了两个静态常量
static final int TRANSACTION_fun (android.os.IBinder.FIRST_CALL_TRANSACTION 0);
static final int TRANSACTION_func (android.os.IBinder.FIRST_CALL_TRANSACTION 1);标识这两个方法。目的是为了在onTransact中标识到底是哪个方法被调用。
接着定义了一个内部类Stub它继承了Binder并实现了DemoAidl这个接口很明显它就是Binder。同时这个内部类里还有一个代理类Proxy。当客户端和服务端在同一个进程内时方法调用不会走跨进程的Transact过程。当不在一个进程内时就会通过内部的代理类来完成。那么它是怎么判断呢
我们看一下其asInterface的代码
public static com.hirezy.test.aidl.BeanAidl asInterface(android.os.IBinder obj)
{if ((objnull)) {return null;}android.os.IInterface iin obj.queryLocalInterface(DESCRIPTOR);if (((iin!null)(iin instanceof com.karl.butler.aidl.BeanAidl))) {return ((com.hirezy.test.aidl.BeanAidl)iin);}return new com.hirezy.test.aidl.BeanAidl.Stub.Proxy(obj);
}可以看到它是通过queryLocalInterface首先判断本地是否有对应的Binder对象。也就是当前线程内是否有如果有则说明客户端和服务端位于同一进程那么此方法返回的就是服务端的Stub对象本身。否则就是在不同的进程内返回的是Stub.proxy对象。
接着看一下onTransact方法它是运行在服务端的Binder线程池中的当客户端发起跨进程请求时远程请求会通过系统底层封装后交由此方法来处理。
最后看一下Proxy中的fun和func方法方法运行在客户端当方法被调用时首先准备数据然后发起远程请求并挂起当前线程经过底层驱动调用度无端的onTransact方法会。返回结果后当前线程继续执行。
流程大致如下 其实AIDL可以理解为Binder规定的一种规范。是为了便于我们开发而用来自动生成代码的工具。我们完全可以自己来写代码而无需借助AIDL文件完成Binder。
Binder实现原理
Binder 是一种进程间通信机制基于开源的 OpenBinder 实现。
Binder是一种CS架构的进程间通讯机制机制一个虚拟的物理设备驱动在应用层是一个能发起通讯的Java类。
当应用中使用多进程通讯时就需要Binder。而由于虚拟机分配给各个进程的运行内存是有限制的同时LMK也会优先回收占用资源较多的进程。所以我们可以运用多进程
规避内存限制如大量占用内存的图库提高稳定性使用独立的进程保持长连接规避内存泄露独立的webView进程规避风险使用独立进程运行高风险的功能避免影响主业务
同时Android应用是有四大组件Activity、Service、Broadcast Receiver 和 Content Provide组成的。有时这些组件运行在同一进程有时运行在不同的进程。这些进程间的通信就依赖于 Binder IPC 机制。同时android中的AMS、PMS都是基于Binder机制来实现的。所以组件的启动流程、通信机制以及AIDL都离不开Binder。
先了解一下系统中的内存划分。操作系统会将内存划分为两块
用户空间用户程序代码运行的地方应用独享内核空间内核代码运行的空间所有进程共享
为了确保安全性它们是隔离开的。而且不同进程之间也是隔离开的内存是不共享的无法直接访问不同进程间的数据。
不同进程之间依赖内核空间作为桥梁进行通讯通过将A进程中的数据copy到内核空间copy from user再由内核空间copy到B进程中copy to user实现A进程和B进程之间的通讯。本质上是通过内核内存空间拷贝赋值来完成进程间的数据传输 Linux提供了管道Handler使用了管道机制epoll、信号量、 消息队列、共享内存Fresco和 Socket 等 IPC 机制。而Binder相比它们具有以下优势
Binder 只需要一次数据拷贝性能上仅次于共享内存而其他的则需拷贝两次。Binder 基于 C/S 架构内存共享机制需要自行管理和控制并发和同步等稳定性和便捷性不如Binder且所有进程都可以访问安全性低。Binder通过在内核中获取PID添加身份标识而非程序自己生成安全性更高。Binder是一对多的关系一个服务端可以有多个客户端与之通讯而管道只支持一对一的通讯方式。
在Linux中使用的虚拟内存寻址方式
用户空间的虚拟内存地址是映射到物理内存中的对虚拟内存的读写实际上是对物理内存的读写通过系统调用mmap()来实现内存映射。
Binder借助了内存映射的方法在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射就相当于直接拷贝到了接收方用户空间的数据缓存区从而减少了一次数据拷贝。 由于个人对Binder的了解也不多这里只对其基本概念做一个简单的描述。想更深入的学习Binder机制推荐去看Android Binder设计与实现 - 设计篇介绍的非常详细。
总结
我认识了进程和线程的区别
进程 系统中正在运行的一个应用程序某个程序一旦运行就是一个进程是资源分配的最小单位线程 程序执行的最小单位包含在进程中一个进程可以包含多个线程。
也知道了常用了Android IPC通信方式
Bundle 实现了Parcelable接口常用于Activity、Service、BroadcastReceive之间的通信文件共享 常用于无并发交换实时性不高的数据Messenger 低并发的一对多即时通信。串行的方式处理Client发来的消息只能传输数据不能方法调用RPCContentProvider 存储和获取数据不同程序之间共享数据。一对多的数据共享AIDLSocket 网络数据交换 注意⚠️除去 Socket其他的都是基于 Binder 机制实现的 常见多进程通信IPCInter Process Communication有如下几种
管道Pipe 在内存中创建一个共享文件利用共享文件传递信息。该共享文件并不是文件系统只存在于内存中只能在一个方向上流动信号Signal 异步通信。信号在用户空间和内核空间之间交互内核可利用信号来通知用户空间的进程发生哪些系统事件。不适用于信号交换适用于过程中断控制信号量Semaphore 控制多个进程对共享资源的访问。主要是进程间以及同一进程不同线程之间的同步手段消息队列 Message Queue 存放在内存中并由消息对了标识符标识允许一个或多个进程对它进行读写消息。信息会复制两次不适用于频繁或信息量大的通信共享内存Shared Memory 直接读写内核的一块内存空间。不需要进行数据拷贝套接字Socket 不同机器之间进程间通信。
Android引入多进程的目的
由于 Android 系统会限制每个应用的最大内存所以如果一个应用需要更多可用的内存时就需要引入多进程让某些模块运行在另外的进程中获取更多的内存由于不同的应用运行在不同的进程中但是如果两个不同的应用之间需要进行数据通信