精华内容
下载资源
问答
  • Activity (借助Intent调用其他APP的Activity实现跨进程通信)Android四大组件之一被调用方代码段Intent i = getIntent();if(null != i){System.out.println("被调用的Activity获取数据:"+i.getIntExtra("id", 0));}...

    Activity (借助Intent调用其他APP的Activity实现跨进程通信)

    Android四大组件之一

    被调用方代码段

    Intent i = getIntent();

    if(null != i){

    System.out.println("被调用的Activity中获取数据:"+i.getIntExtra("id", 0));

    }

    清单文件中的Activity中添加如下属性(如果有intentfilter应该默认就是true)

    android:exported="true"

    - 调用方的代码段

    // 参数1:包名;参数2:包名+类名

    ComponentName componentName= new ComponentName("cn.cc.testandroid", "cn.cc.testandroid.TesttwoActivity");

    Intent intent = new Intent();

    intent.putExtra("id", 9999);

    intent.setComponent(componentName);

    startActivity(intent);

    广播接收者(BroadcastReceiver)

    Android四大组件之一,有广播接收者,与之对应要有发送者。

    广播:存在1个数据的发送方,和若干个接收方。(理解为:某个电台发送广播,在此接收频率上的收音机都可以接得收到)。

    在Android系统中,把符合“广播”的特性的数据传递方式称之为“广播”,“广播”是一种在Android系统中全设备通信的机制。

    广播分类:普通广播(无序广播)、有序广播、粘滞普通(无序)广播、粘滞有序广播。

    广播发送:在Activity或Service中调用sendBroadcast(Intent)方法即可发普通广播,或调用sendOrderedBroadcast(Intent, String)发送有序广播。

    广播接收者注册方式:静态注册(清单文件)、动态注册(registerReceiver,请在合适时机调用unregisterReceiver取消注册)。

    接收者:

    // 新建类继承BroadcastReceiver ,重写onReceive

    public class CustomReceiver extends BroadcastReceiver {

    @Override

    public void onReceive(Context context, Intent intent) {

    Bundle bundle = intent.getExtras();

    Log.i("CustomReceiver", "onReceive:"+bundle.getInt("id"));

    }

    }

    清单文件注册:

    android:name="cn.cc.testandroid.CustomReceiver">

    发送者:

    Intent intent = new Intent();

    intent.setAction("cctv");

    Bundle bundle = new Bundle();

    bundle.putInt("id", 8888);

    intent.putExtras(bundle);

    sendBroadcast(intent);

    内容提供者(ContentProvider)

    Android四大组件之一,有提供者,与之对应也要有调用者。

    ContentProvider是一种设备内部共享数据的机制,APP可以通过ContentProvider将自身应用的数据对外提供共享,使得其它应用可以对这些数据实现增删改查的访问功能。

    Android系统使用了许多ContentProvider将系统中的数据对外提供共享,任何APP都可以通过访问这些ContentProvider对相关的数据进行操作。

    内容提供者:

    public class CustomProvider extends ContentProvider {// 新建类继承ContentProvider并实现需实现的方法

    private static UriMatcher MATCHER;// UriMatcher对象

    private static final int MATCH_ROOT = 1;

    static {

    MATCHER = new UriMatcher(UriMatcher.NO_MATCH);

    // 添加匹配规则 content://www.cc.cn

    MATCHER.addURI("www.cc.cn", null, MATCH_ROOT);

    }

    public Cursor query(Uri uri, String[] projection, String selection,

    String[] selectionArgs, String sortOrder) {

    Log.i("CustomProvider", "CustomProvider->query()...");

    return null;

    }//此处还有其他需要实现的方法,篇幅原因就不贴了,都未作处理。默认就好,只是简单实现query方法调用。

    }

    清单文件中注册:

    android:name="cn.cc.testandroid.CustomProvider"

    android:authorities="www.cc.cn"

    android:exported="true" >

    内容调用者:

    ContentResolver cr = getContentResolver();

    Uri uri = Uri.parse("content://www.cc.cn");

    cr.query(uri, null, null, null, null);// 此处返回游标...

    AIDL(Android Interface definition language,Android接口定义语言)

    与之前三个相比,AIDL可能稍显复杂。

    Service,Android四大组件之一,借助AIDL也可以实现跨进程访问服务

    下面实现跨进程访问Service,两个项目:TestAIDLServer是服务端的项目,TestAndroid2是客户端的项目,服务端安装后,客户端可以访问服务端的Service。客户端展示图如下:

    af1e81439c62

    Paste_Image.png

    点击远程访问按钮后:

    af1e81439c62

    Paste_Image.png

    AIDL实现步骤(实体类为 Goods:货物):

    1) 服务端新建包aidl,并在包内自定义实体类Goods.java,让其实现Parcelable接口,实现Parcelable的详细步骤...

    af1e81439c62

    Paste_Image.png

    ** 2)服务端相同包下**,创建Goods.aidl文件添加如下代码

    parcelable Goods;// 如果忘记添加,IAidl.aidl中的import语句可能会报错

    3)服务端相同包下,新建IAidl.aidl文件,添加如下代码

    package cn.cc.testaidlserver;// 跟Goods实体类一样

    import cn.cc.testaidlserver.Goods;// 手动导入实体类包

    interface IServer{

    double getPrice();

    int getCount();

    String getName();

    }

    至此如果没有错误则Eclipse会在项目的gen目录下自动生成对应的包及其Iserver.java文件

    ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/3266538-60b6a47a68d1b369.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    4) 服务端另外的包下,新建CustomService继承自Service

    af1e81439c62

    Paste_Image.png

    CustomService.java代码:

    
     

    public class CustomService extends Service {

    @Override

    public IBinder onBind(Intent intent) {

    return new CustomServiceImpl();

    }

    class CustomServiceImpl extends IServer.Stub{// 实现类

    @Override

    public double getPrice() throws RemoteException {

    return 999;

    }

    @Override

    public int getCount() throws RemoteException {

    return 5;

    }

    @Override

    public String getName() throws RemoteException {

    return "地下城堡2-教团长剑x2";

    }

    }

    }

    清单文件中注册Service

    5)将服务端的整个aidl包拷贝到客户端中 gen目录下也会自动生成IServier.java文件

    af1e81439c62

    Paste_Image.png

    客户端剩余的代码比较简单了直接贴

    布局文件:

    
     

    android:layout_width="match_parent"

    android:id="@+id/tv_message"

    android:background="#dddddd"

    android:layout_height="100dp"

    />

    android:id="@+id/btn_show"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text="访问远程Service获取商品名称"

    />

    android:id="@+id/btn_clear"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text="Clear"

    />

    MainActivity.java代码:

    
     

    public class MainActivity extends Activity implements OnClickListener {

    private IServer server;// 访问的接口

    private ServiceConnection conn;

    // 控件

    private TextView tvMessage;

    private Button btnShow;

    private Button btnClear;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    // 初始化控件

    tvMessage = (TextView) findViewById(R.id.tv_message);

    btnShow = (Button) findViewById(R.id.btn_show);

    btnClear = (Button) findViewById(R.id.btn_clear);

    // 设置监听

    btnShow.setOnClickListener(this);

    btnClear.setOnClickListener(this);

    // ServiceConnection初始化

    conn = new ServiceConnection() {

    @Override

    public void onServiceDisconnected(ComponentName name) {

    Log.i("==================", "onServiceDisconnected");

    }

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

    Log.i("==================", "onServiceConnected");

    // 获取远程service中onBind方法的return的对象

    server = IServer.Stub.asInterface(service);

    }

    };

    // 绑定远程Service

    Intent intent = new Intent("cn.cc.test.server.CustomService");

    bindService(intent, conn, Context.BIND_AUTO_CREATE);

    }

    @Override

    public void onClick(View v) {

    switch (v.getId()) {

    case R.id.btn_show:// 获取远程Service信息

    try {

    if(null != server){

    String strRet = server.getName();// 调用远程Service的方法,此处简单调用一个。

    tvMessage.setText(strRet);

    }else{

    Toast.makeText(MainActivity.this, "访问远程Service失败", Toast.LENGTH_SHORT).show();

    }

    } catch (RemoteException e) {

    e.printStackTrace();

    }

    break;

    case R.id.btn_clear:// 清除

    tvMessage.setText("");

    break;

    }

    }

    }

    至此,按照步骤,中间没出差错的话已经实现了跨进程访问Service的方法了。

    注意事项:

    1)步骤1中最好是新建一个包,往客户端拷贝时直接拷贝整个包,这样不容易出错。

    2)服务端跟客户端的包名要相同

    3)CustomService类中,的onBind方法return的是IServer.Stub抽象类的实现类对象。

    4)MainActivity.java类中的bindService会消耗一定的时间。

    以上为学习中加深记忆、克服手懒、方便以后翻阅所用,若碰巧对其他童鞋有所帮助,不甚荣幸。

    展开全文
  • android源码的驱动目录下,一般会有...1.在linux,不同进程间拥有自己独立的内存空间,32位操作系统好像一个进程能用的内存大小是4G吧。而且一般不同进程间不能够互相使用各自内存的数据。当然不同进程间共...

    在android源码的驱动目录下,一般会有共享内存的相关实现源码,目录是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是讲解android共享内存的功能实现原理,而是讲怎么运用它。

    1.

    在linux中,不同进程间拥有自己独立的内存空间,32位操作系统中好像一个进程能用的内存大小是4G吧。而且一般不同进程间不能够互相使用各自内存的数据。

    当然不同进程间共享数据方法很多,比如之前说的进程间通信binder,socket等等,不过android出了一个共享内存的概念,为的是不同进程间能够共同操作同一块内存数据,比如进程1001往一块共享内存addr里面写数据“hello world”,进程1009往这块共享内存addr读取出“hello world”,也能够往这块共享内存addr写数据“hello china”。这就是共同享用了一块内存的基本概念了(说白了就是同耕一块田)。讲的够仔细了吧,如果不清楚评论区见。

    注意:好像binder传输的数据实现也是类似于共享内存,读者可以自行去了解。

    2.

    先说一下等会写程序的思路:

    首先想想代码编译出两个可执行文件后如何操作,打开两个终端,都进入设备adb shell,第一个终端执行进程a,第二个终端执行进程b。在进程a输入一串数据后,在进程b中可以读出这段数据(也能够改写这段数据,读者可以自行添加这部分功能)。

    然后再想想实现的方式,

    进程a:1. 创建共享内存,设置共享内存大小,这时会得到一个fd。2. 获取共享内存地址。3. 先读取地址数据,然后往地址写入数据。4. 把fd通过binder发送给需要使用的进程。

    进程b:1. 通过binder读取到fd。2. 用fd获取共享内存地址。3. 读取共享内存数据,然后往地址写入数据。

    注意:linux一切皆文件,所以文件描述符fd很重要。

    3.

    3.1

    捋清思路后,就可以开始写代码了(android.mk的编写可以参考之前的文章),进程a,命名为mysharememory_a代码如下:

    #include #include#include#include#include#include#include#include#include#include#include#include#include

    #define DEVASHMEM "/dev/ashmem"

    #define SHNAME "hellomemory"

    #define MAXBUFSIZE 1024

    #define TRANSFDCODE 1000

    #define WRITEDATACODE 1001

    using namespaceandroid;int main(int argc, char *argv[])

    {

    int fd =open(DEVASHMEM, O_RDWR);if(fd < 0)

    {return -1;

    }int ret =ioctl(fd, ASHMEM_SET_NAME, SHNAME);if(ret < 0){

    close(fd);return -1;

    }char *get_sh_addr_write =NULL;

    ret=ioctl(fd, ASHMEM_SET_SIZE, MAXBUFSIZE);if(ret < 0){

    close(fd);return -1;

    }

    get_sh_addr_write= (char*)mmap(NULL, MAXBUFSIZE , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if(NULL ==get_sh_addr_write)

    {return -1;

    }

    sp sm =defaultServiceManager();

    sp binder = sm->checkService(String16("mybindertag"));

    Parcel data, reply;

    data.writeDupFileDescriptor(fd);

    binder->transact(TRANSFDCODE, data, &reply);char input_data[MAXBUFSIZE] = {0};while(1)

    {

    printf("read share memory buf is %s\n", get_sh_addr_write);

    printf("please input data to buf :");

    scanf("%s", input_data);

    getchar();

    strcpy(get_sh_addr_write,input_data);

    binder->transact(WRITEDATACODE, data, &reply);

    }returnret;

    }

    3.2

    mysharememory_b代码如下:

    #include #include#include#include#include#include#include#include#include#include#include#include#include

    #define DEVASHMEM "/dev/ashmem"

    #define SHNAME "hellomemory"

    #define MAXBUFSIZE 1024

    #define TRANSFDCODE 1000

    #define WRITEDATACODE 1001

    using namespaceandroid;int g_sh_fd = 0;class MyBinderService : publicBBinder{

    status_t onTransact(uint32_t code,const Parcel& data, Parcel*reply, uint32_t flags)

    {intret;char *get_sh_addr_read =NULL;intget_sh_size;

    printf("songsong!! **** onTransact ***** code = %d \n",code);switch(code)

    {caseTRANSFDCODE:

    g_sh_fd=data.readFileDescriptor();

    break;caseWRITEDATACODE:

    get_sh_size=ioctl(g_sh_fd, ASHMEM_GET_SIZE,NULL);if(get_sh_size > 0)

    {

    get_sh_addr_read= (char*)mmap(NULL, get_sh_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_sh_fd, 0);

    }else{

    printf("mmap failed %d\n", get_sh_size);return -1;

    }

    printf("what is in the share memory: %s\n", get_sh_addr_read);break;default:break;

    }returnNO_ERROR;

    }

    };int main(int argc, char *argv[])

    {

    defaultServiceManager()->addService(String16("mybindertag"), newMyBinderService());

    spproc(ProcessState::self());

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();return 0;

    }

    3.3

    回收关闭部分代码可选择添加在mysharememory_b中,如下:

    intret;

    ret= munmap((void*)get_sh_addr_read, get_sh_size);if(ret == -1)

    {return -1;

    }

    ret=close(g_sh_fd);if(ret == -1)

    {return -1;

    }

    3.4

    演示截图:

    6101f7dc40c51e045efcc8c415a09e21.png

    4. 为了骗取评论,我不再解释代码,心累。不过您可以把代码直接拷贝去编译执行,再通过调试去理解代码的精髓,也是没问题的。

    原文出处:https://www.cnblogs.com/songsongman/p/11249974.html

    展开全文
  • 进程间通信基本原理 进程间通信的原理 Binder 的作用 Binder 的使用场景 Binder 是什么? 什么时候需要用到进程间通信? 为什么要多进程? 进程间通信为什么要用到Binder机制? 手机正在运行的进程 内存划分 ...

    Android多进程

    哪个大型项目不是多进程的?进程间最基本的通信原理你了解多少?

    手机正在运行的进程:
    在这里插入图片描述

    进程间通信基本原理

    • 进程间通信的原理
    • Binder 的作用
    • Binder 的使用场景

    Binder 是什么?

    Binder简介:
    Binder是Android系统新增的一种高效的进程间通信机制。四大组件都在使用Binder机制进行跨进程通信,Binder基于OpenBinder项目实现,java层的Binder通信用到的相关java类型都是对应native层C++类型的一个封装。

    什么时候需要用到进程间通信?常见使用多进程的场景?

    1.Android中的Binder无处不在,四大组件间的通信用的都是Binder。
    2.调用系统服务,比如获取输入法服务、闹钟服务、摄像头、电话等系统服务都要用到进程间通信Binder。
    3.吃大内存的模块,比如:地图模块、大图浏览、webview等,Android对内存的限制是针对于进程的,即每个进程的内存空间是有大小限制的。

    为什么要多进程?使用多进程的原因?

    1.突破虚拟机分配进程的运行内存限制。
    Android虚拟机分配给每个进程的内存是有限的(如16M,32M,64M,96M等等),可以在新的进程中去执行,避免主进程OOM
    2.提高app稳定性,单个进程崩溃不影响整个app
    如微信的小程序进程崩溃后,微信依然可以正常运行。
    3.对内存更可控,通过主动释放进程,减小系统压力,提高系统流畅性
    在接收到系统发出的trimMemory(int level)里主动释放重要级低的进程。
    4.Android的LMK机制
    当系统内存不足时会杀死低优先级的进程,这样不会杀死主进程,主功能不受影响。

    内存划分

    进程A为什么不能直接与进程B通信?涉及到操作系统的内存划分。
    内存空间分为用户空间和内核空间,用户空间与用户空间之间是隔离的,不能随意访问。
    用户空间要访问其他用户空间的数据可以通过内核空间来进行。
    在这里插入图片描述
    对于32位系统,一般是内核空间是1GB,用户空间是3GB

    看个例子: 包裹发送
    在这里插入图片描述
    城市A的包裹要发送到城市B,需要通过快递小哥经过公路来完成,这里的公路就可以认为是内核空间。快递小哥就可以认为是进程间的通信机制Binder。
    即:
    公路 - 内核空间
    快递 - 进程间的通信机制

    进程间通信为什么要新增Binder机制?

    参考下面的 Android新增Binder机制的原因

    Android新增Binder机制的原因

    • 性能:Binder更高效
    • 特点:Binder只需要一次数据拷贝
    • 安全性:Binder更安全

    Linux已经有通信机制,比如共享内存,Socket,消息队列,管道,信号量等等,为什么还要增加Binder 机制呢?

    Binder与传统 IPC 对比

    这里仅做Binder和共享内存、Socket的对比分析,对于消息队列、管道等可以类似分析。
    在这里插入图片描述

    传统的Socket的IPC传输数据

    传统 IPC 传输数据,比如Socket,需要两次数据拷贝,性能低。
    Socket(也是C/S架构)作为一种通用的通信方式,传输效率是非常低的,而且Socket一般用于跨网络的进程间通信方式,即使是用于本地的进程间通信时,一般也是用于低速的进程间通信的情况,而Binder的进程间通信是高速的。

    进程A访问进程B无非是访问它的数据或者调用它的方法。
    在这里插入图片描述
    数据从用户空间到内核空间,再从内核空间到用户空间,经过两次系统调用:copy_from_user()和copy_to_user()。

    和送快递非常相似,即把快递打包成包裹,然后给快递小哥(数据经历一次拷贝),然后快递小哥通过公路到达另一个城市,把包裹送给目标客户(数据经历一次拷贝),总共是两次数据拷贝。

    Binder传输数据

    在这里插入图片描述
    整体看起来大致流程也是和上述的传统IPC传输数据一样的,数据也是从用户空间到内核空间,再从内核空间到用户空间,那为什么只需要一次数据拷贝呢?因为服务端进程的用户空间通过系统调用mmap机制和内核空间进行了映射,当客户端进程把数据拷贝到内核空间的那块内存后,服务端进程就可以读取到数据。

    相当于你送快递的目标客户就是快递小哥或者是快递小哥认识的朋友,你把快递给快递小哥(一次数据拷贝),快递小哥直接把包裹给他朋友,无需经过公路跑去另外一个城市。

    传统的共享内存的传输数据

    共享内存为什么无需拷贝?
    因为共享内存是,客户端进程的用户空间也和服务端进程一样,通过系统调用mmap机制和内核空间进行了映射,实现了客户端进程的用户空间、服务端进程的用户空间、内核空间这三者都映射到了同一块内存空间,服务端进程可以直接访问到客户端进程的数据,因此无需拷贝。

    但是这种方式也造成了共享内存控制复杂,易用性差的特点,因为通过mmap把客户端进程的用户空间、服务端进程的用户空间、内核空间这三者都映射到了同一块内存空间,会造成读写数据同步的问题,可能存在客户端进程修改了这块内存空间的数据,结果服务端进程去读取的时候读取到的是之前旧的数据的情况,这就需要用到数据同步机制,而数据同步机制处理不好就非常容易导致数据不同步,产生死锁等问题。

    Binder为什么不设计成像共享内存那样无需数据拷贝

    Binder为什么选择设计成拷贝一次,为什么不像共享内存那样用mmap机制把客户端进程的用户空间、服务端进程的用户空间、内核空间这三者都映射到同一块内存,从而无需拷贝呢?
    上述共享内存的传输数据已经讲述了共享内存的控制复杂,易用性差的特点以及原因,这就是Binder宁愿牺牲一些性能也要拷贝一次的原因。

    当然,Binder为什么不选择像传统IPC传输数据那样拷贝两次?那当然是为了性能考虑,数据拷贝一次性能高。

    从安全性考虑

    1.Binder是使用Android系统为每个app分配的pid进行通信的,安全性高;而共享内存和Socket是依赖上层协议的,安全性低。比如,客户端进程通过Socket与服务端进程通信,客户端进程的pid是自己根据通信协议写在数据包上的,即pid是自己写的,而不是系统分配给你的,服务端进程无法根据pid判断安全性,这样就无法控制安全性。
    2.Binder同时支持实名和匿名,安全性高; 而共享内存和Socket的访问接入点时开放的,不安全。即谁都可以访问接入点,是不安全的。

    Binder的实名和匿名:
    ServiceManager与实名Binder:SMgr的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
    匿名Binder:并不是所有Binder都需要注册给SMgr广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向SMgr注册名字,所以是个匿名Binder。

    总结

    Binder通信机制的优点:

    1. 采用C/S的通信模式。而在linux通信机制中,目前只有socket支持C/S的通信模式,但是Socket是低速的且安全性低,Binder比Socket速度快且安全性高。
    2. 传输性能更好。Binder只需一次数据拷贝。对比于Linux的通信机制,socket:是一个通用接口,导致其传输效率低,开销大;管道和消息队列:因为采用存储转发方式,所以至少需要拷贝2次数据,效率低;共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂(比如跨进程通信时,需获取对方进程的pid,得多种机制协同操作)。
    3. 安全性高
      (1). Binder是使用Android系统为每个app分配的pid进行通信的,安全性高;而共享内存和Socket是依赖上层协议的,安全性低。
      (2). Binder同时支持实名和匿名,安全性高;而共享内存和Socket的访问接入点时开放的,不安全。
      (3). Linux的IPC机制在本身的实现中,并没有安全措施,得依赖上层协议来进行安全控制。而Binder机制的UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高;并且Binder可以建立私有通道,这是Linux的通信机制无法实现的(Linux访问的接入点是开放的)。
    4. 调用方便。客户端像调用本地服务一样调用服务端的服务。对用户来说,通过Binder屏蔽了client的调用server的隔阂,client端函数的名字、参数和返回值和server的方法一模一样,对用户来说犹如就在本地(也可以做得不一样),这样的体验或许其他ipc方式也可以实现,但Binder最初就是这么设计的。

    源码分析

    • 如何获得另一个进程的对象(另一个进程的句柄之类的东西)
    • 如何进行通信的

    以一个具体例子进行分析。

    AIDL简介

    • 客户端如何获取到AIDL的遥控器的?
    • 通过这个遥控器是如何调到服务端的呢?
    • 服务端又是如何做处理的?

    AIDL就是基于Binder实现的,只是通过自动生成相关代码帮助我们简化了Binder机制的实现,否则,如果完全通过我们手写代码,要写很多Binder通信相关的代码,复杂且易出错,AIDL简化了这一过程。

    1.Android Studio中,AIDL文件创建好后必须手动编译下(点击 Build -> Make Projet)才会在build目录下生成对应的代码;Eclipse会自动生成,不需要手动编译。

    2.Android SDK里面的构建工具(Android\Sdk\build-tools\30.0.1\aidl.exe)会根据AIDL文件生成对应的代码。

    3.aidl目录下的包名必须和java目录下的包名一样,否则会报错:

    IMyAidl should be declared in a file called com\example\binder_client\IMyAidl.aidl
    

    4.客户端的aidl文件必须和服务端的aidl文件一模一样,即把服务端的aidl文件拷贝一份到客户端中即可。

    AIDL中的定向 tag

    AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

    AIDL的简单使用

    一个具体例子看下AIDL如何使用。
    定义aidl文件IMyAidl.aidl,里面声明了接口方法:
    在这里插入图片描述
    这个文件以及内容在服务端和客户端中是一模一样的:
    在这里插入图片描述
    经过编译后(Build -> Make Project),会在build目录下生成对应的IMyAidl.java文件,内容如下:

    //IMyAidl.java
    
    /*
     * This file is auto-generated.  DO NOT MODIFY.
     */
    package com.binder.service;
    //Person即使在同一个包下,也要声明,只要是非默认的类型,都要声明。
    
    public interface IMyAidl extends android.os.IInterface {
        /**
         * Default implementation for IMyAidl.
         */
        public static class Default implements com.binder.service.IMyAidl {
            @Override
            public void addPerson(com.binder.service.Person person) throws android.os.RemoteException {
            }
    
            @Override
            public java.util.List<com.binder.service.Person> getPersonList() throws android.os.RemoteException {
                return null;
            }
    
            @Override
            public android.os.IBinder asBinder() {
                return null;
            }
        }
    
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.binder.service.IMyAidl {
            private static final java.lang.String DESCRIPTOR = "com.binder.service.IMyAidl";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.binder.service.IMyAidl interface,
             * generating a proxy if needed.
             */
            public static com.binder.service.IMyAidl asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.binder.service.IMyAidl))) {
                    return ((com.binder.service.IMyAidl) iin);
                }
                return new com.binder.service.IMyAidl.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_addPerson: {
                        data.enforceInterface(descriptor);
                        com.binder.service.Person _arg0;
                        if ((0 != data.readInt())) {
                            _arg0 = com.binder.service.Person.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        this.addPerson(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getPersonList: {
                        data.enforceInterface(descriptor);
                        java.util.List<com.binder.service.Person> _result = this.getPersonList();
                        reply.writeNoException();
                        reply.writeTypedList(_result);
                        return true;
                    }
                    default: {
                        return super.onTransact(code, data, reply, flags);
                    }
                }
            }
    
            private static class Proxy implements com.binder.service.IMyAidl {
                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 addPerson(com.binder.service.Person person) throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        if ((person != null)) {
                            _data.writeInt(1);
                            person.writeToParcel(_data, 0);
                        } else {
                            _data.writeInt(0);
                        }
                        boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                        if (!_status && getDefaultImpl() != null) {
                            getDefaultImpl().addPerson(person);
                            return;
                        }
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
    
                @Override
                public java.util.List<com.binder.service.Person> getPersonList() throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    java.util.List<com.binder.service.Person> _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        boolean _status = mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
                        if (!_status && getDefaultImpl() != null) {
                            return getDefaultImpl().getPersonList();
                        }
                        _reply.readException();
                        _result = _reply.createTypedArrayList(com.binder.service.Person.CREATOR);
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
    
                public static com.binder.service.IMyAidl sDefaultImpl;
            }
    
            static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    
            public static boolean setDefaultImpl(com.binder.service.IMyAidl impl) {
                // Only one user of this interface can use this function
                // at a time. This is a heuristic to detect if two different
                // users in the same process use this function.
                if (Stub.Proxy.sDefaultImpl != null) {
                    throw new IllegalStateException("setDefaultImpl() called twice");
                }
                if (impl != null) {
                    Stub.Proxy.sDefaultImpl = impl;
                    return true;
                }
                return false;
            }
    
            public static com.binder.service.IMyAidl getDefaultImpl() {
                return Stub.Proxy.sDefaultImpl;
            }
        }
    
        public void addPerson(com.binder.service.Person person) throws android.os.RemoteException;
    
        public java.util.List<com.binder.service.Person> getPersonList() throws android.os.RemoteException;
    }
    
    

    生成的IMyAidl.java文件内容的整体结构:
    在这里插入图片描述
    主要就三个内容:AIDL接口定义的addPerson方法、getPersonList方法、以及一个Stub类(Stub类里面有一个Proxy类)。

    IMyAidl.java实现了IInterface,aidl接口都要实现IInterface:

    /**
     * Base class for Binder interfaces.  When defining a new interface,
     * you must derive it from IInterface.
     */
    public interface IInterface
    {
        /**
         * Retrieve the Binder object associated with this interface.
         * You must use this instead of a plain cast, so that proxy objects
         * can return the correct result.
         */
        public IBinder asBinder();
    }
    
    

    客户端调用服务端方法的时序图:
    在这里插入图片描述

    客户端的使用很简单,首先获取aidl对象,然后通过aidl对象调用定义的接口方法。
    1.首先获取aidl对象

    //获取aidl对象的方法
    iMyAidl = IMyAidl.Stub.asInterface(service);
    

    这句代码具体是在ServiceConnection#onServiceConnected()方法中:

        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected(): success, name=" + name);
                iMyAidl = IMyAidl.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "onServiceDisconnected(): name=" + name);
                iMyAidl = null;
            }
        };
    

    IMyAidl.Stub.asInterface()方法返回的其实是IMyAidl.Stub.Proxy对象(客户端与服务端不在同一个进程的情况):

            /**
             * Cast an IBinder object into an com.binder.service.IMyAidl interface,
             * generating a proxy if needed.
             */
            public static com.binder.service.IMyAidl asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.binder.service.IMyAidl))) {
                    return ((com.binder.service.IMyAidl) iin);
                }
                return new com.binder.service.IMyAidl.Stub.Proxy(obj);
            }
    

    2.然后通过aidl对象调用定义的接口方法

      iMyAidl.addPerson(new Person("流川枫", 3));
    

    Stub.asInterface()方法分析

    //IMyAidl.java
    
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.binder.service.IMyAidl {
        private static final java.lang.String DESCRIPTOR = "com.binder.service.IMyAidl";
    
        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        /**
         * Cast an IBinder object into an com.binder.service.IMyAidl interface,
         * generating a proxy if needed.
         */
        public static com.binder.service.IMyAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.binder.service.IMyAidl))) {
                return ((com.binder.service.IMyAidl) iin);
            }
            return new com.binder.service.IMyAidl.Stub.Proxy(obj);
        }
    
    	...
    
    }
    

    看下queryLocalInterface()方法

    //Binder.java
    
        /**
         * Use information supplied to attachInterface() to return the
         * associated IInterface if it matches the requested
         * descriptor.
         */
        public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
            if (mDescriptor != null && mDescriptor.equals(descriptor)) {
                return mOwner;
            }
            return null;
        }
    
    

    该方法主要是判断Binder的mDescriptor属性是否和传递来的参数descriptor相等,那么这个mDescriptor是在哪里初始化呢,是在Stub的构造方法里进行初始化的:

    //IMyAidl.java
    
     /**
      * Local-side IPC implementation stub class.
      */
     public static abstract class Stub extends android.os.Binder implements com.binder.service.IMyAidl {
         private static final java.lang.String DESCRIPTOR = "com.binder.service.IMyAidl";
    
         /**
          * Construct the stub at attach it to the interface.
          */
         public Stub() {
             this.attachInterface(this, DESCRIPTOR);
         }
         
         ...
    }
    
    //Binder.java
    
        /**
         * Convenience method for associating a specific interface with the Binder.
         * After calling, queryLocalInterface() will be implemented for you
         * to return the given owner IInterface when the corresponding
         * descriptor is requested.
         */
        public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
            mOwner = owner;
            mDescriptor = descriptor;
        }
    

    DESCRIPTOR 是一个字符串常量,就是定义的AIDL接口IMyAidl的全类名。

    是不是很疑惑啊,构造的时候传递的值是DESCRIPTOR ,queryLocalInterface(DESCRIPTOR)传递的值也是DESCRIPTOR,那肯定相等啊!其实不一定,因为这个Stub不一定是在本地进程创建的,也就是在调用IMyAidl.Stub.asInterface(service)时本地进程很可能并没有Stub对象,而且android.os.IBinder obj的obj对象也不一定是Stub对象,有可能只是实现了IBinder接口的对象而已,因此 mDescriptor 的值很可能是null。(熟悉Binder机制的同学肯定知道Stub对象是在服务端创建的,而服务端和客户端不一定运行在同一个进程,很可能是运行在另外的一个服务端进程,Stub对象通过Binder机制传递到客户端时是转为一个BinderProxy对象)。

    其实调式下就可看看obj.queryLocalInterface(DESCRIPTOR)返回的结果是什么(调式时客户端和服务端是在不同进程):
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    当客户端和服务端不在同一进程时, public void onServiceConnected(ComponentName name, IBinder service) 返回的service对象是一个BinderProxy对象,BinderProxy的queryLocalInterface()返回是null,因此asInterface()方法最终会执行return new com.binder.service.IMyAidl.Stub.Proxy(obj);这句代码,返回一个Stub.Proxy对象。

    Stub对象是在服务端创建的:

        private IBinder iBinder = new IMyAidl.Stub() {
            @Override
            public void addPerson(Person person) throws RemoteException {
                persons.add(person);
            }
    
            @Override
            public List<Person> getPersonList() throws RemoteException {
                return persons;
            }
        };
    

    客户端接收:

        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected(), name=" + name + " ,service=" + service);
                //onServiceConnected(), name=ComponentInfo{com.binder.service/com.binder.service.MyService} ,service=android.os.BinderProxy@305651b
    
                iMyAidl = IMyAidl.Stub.asInterface(service);
                
                Log.i(TAG, "iMyAidl=" + iMyAidl);
                //iMyAidl=com.binder.service.IMyAidl$Stub$Proxy@e6c5045
            }
    		...
    	}
    

    因此,如果服务端和客户端是在同一个进程,客户端onServiceConnected()回调方法接收到的对象service就是Stub对象,那么android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);的obj.queryLocalInterface的结果就是Stub对象。可以进行测试:

        private ServiceConnection mlocalserviceconnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected(), name=" + name + " ,service=" + service);
                //onServiceConnected(), name=ComponentInfo{com.binder.client/com.binder.service.MyLocalService} ,service=com.binder.service.MyLocalService$1@e6c5045
                //MyLocalService$1@e6c5045其实就是MyLocalService中创建的Stub对象(准确说是Stub的匿名子类对象)
    
                iMyAidl = IMyAidl.Stub.asInterface(service);
    
    			Log.i(TAG, "iMyAidl=" + iMyAidl);
    			//iMyAidl=com.binder.service.MyLocalService$1@e6c5045
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "onServiceDisconnected(), name=" + name);
                iMyAidl = null;
            }
        };
    
        /**
         * 测试如果Service和客户端在同一个进程的情况
         * MyLocalService与客户端进程在同一个进程,这种情况下,onServiceConnected()回调方法的service参数就是MyLocalService中创建的Stub对象
         */
        private void bindLocalService() {
            Log.i(TAG, "bindLocalService()");
            Intent intent = new Intent();
            //注意pkg参数是取applicationId的值,不是取AndroidManifest.xml中package的值(applicationId的值可能与package的值不一样)
            intent.setComponent(new ComponentName("com.binder.client", "com.binder.service.MyLocalService"));
            bindService(intent, mlocalserviceconnection, Context.BIND_AUTO_CREATE);
        }
    

    可以看到onServiceConnected()回调方法的service参数:service=com.binder.service.MyLocalService$1@e6c5045MyLocalService$1@e6c5045其实就是MyLocalService中创建的Stub对象(准确说是Stub的匿名子类对象)。

    这个时候调用iMyAidl.addPerson()方法就是直接调用,与调用本地方法一模一样,不需要经过binder驱动,因为根本没有经过binder驱动,可以看下调用栈:

    2020-12-26 18:13:52.770 8988-8988/com.binder.client I/MyLocalService: addPerson()
        java.lang.Exception
            at com.binder.service.MyLocalService$1.addPerson(MyLocalService.java:33)
            at com.binder.client.MainActivity$3.onClick(MainActivity.java:80)
            at android.view.View.performClick(View.java:7448)
            at android.view.View.performClickInternal(View.java:7425)
            at android.view.View.access$3600(View.java:810)
            at android.view.View$PerformClick.run(View.java:28305)
            at android.os.Handler.handleCallback(Handler.java:938)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:223)
            at android.app.ActivityThread.main(ActivityThread.java:7656)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
    2020-12-26 18:13:52.770 8988-8988/com.binder.client E/MainActivity: [Person{name='流川枫', grade=3}]
    

    以上分别对客户端和服务端在同一个进程、客户端和服务端不在同一个进程的情况进行了分析。

    Binder的客户端/服务端架构模式(C/S架构)

    Binder将通信的双方分为client和server,即C/S架构。

    跨进程通信时,两端进程均使用IBinder接口的实例进行通信(BinderProxy和Binder),客户端进程通过Proxy的mRemote对象(mRemote是BinderProxy对象,BinderProxy实现了IBinder)的transact方法将数据经过Binder驱动发送给服务端。
    transact方法定义在IBinder中:

    //IBinder.java
    
        /**
         * Perform a generic operation with the object.
         * 
         * @param code The action to perform.  This should
         * be a number between {@link #FIRST_CALL_TRANSACTION} and
         * {@link #LAST_CALL_TRANSACTION}.
         * @param data Marshalled data to send to the target.  Must not be null.
         * If you are not sending any data, you must create an empty Parcel
         * that is given here.
         * @param reply Marshalled data to be received from the target.  May be
         * null if you are not interested in the return value.
         * @param flags Additional operation flags.  Either 0 for a normal
         * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
         *
         * @return Returns the result from {@link Binder#onTransact}.  A successful call
         * generally returns true; false generally means the transaction code was not
         * understood.
         */
        public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
            throws RemoteException;
    

    客户端进程通过上述的mRemote.transact方法将数据发出后,会通过客户端进程的native层 -> Binder驱动 -> 服务端的native层 -> 调用服务端的Stub对象(Stub继承了Binder)的onTransact()方法 -> 调用Stub实现了的aidl接口中定义的接口方法。
    onTransact()方法定义在Binder中:

    //Binder.java
    
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
    	
    	...
    
    }
    

    BinderProxy与Binder

    Binder 通信机制提供了 Binder 和 BinderProxy 作为 IBinder 接口的实现类:

    //android/os/Binder.java
    
    public class Binder implements IBinder 
    
    //android/os/BinderProxy.java
    
    public final class BinderProxy implements IBinder
    
    • transact方法定义在IBinder中
    • onTransact()方法定义在Binder中

    当跨进程通信时,Binder对象是在服务端进程的,BinderProxy对象是在客户端进程的,BinderProxy意味着是服务端进程的Binder对象在客户端进程的一个代理对象。
    每一个Binder对象都有一个BinderProxy对象与之对应。
    客户端进程通过BinderProxy对象的transact方法发出数据,服务端进程的Binder对象的onTransact方法接收数据。

    Binder中的transact方法是什么时候调用的?

    Binder对象是在服务端进程的,BinderProxy对象是在客户端进程的。
    Binder和BinderProxy都实现了IBinder的transact方法,但Binder中的transact方法与BinderProxy中的transact方法是不一样的,光看代码,Binder中的transact方法代码要少的很多,我们知道当跨进程通信时,客户端进程是通过BinderProxy对象的transact方法发出数据,服务端进程的Binder对象的onTransact方法接收数据,那么Binder的transact方法是什么时候调用的?
    Binder机制支持进程间的递归调用。例如两个进程A和进程B,客户端进程A执行transact方法调用服务端进程B的Binder的onTransact方法,而进程B在其Binder#onTransact方法中又调用transact方法向进程A发起调用,那么进程A在等待它发出的调用返回结果的同时,还会用Binder#onTransact方法响应进程B的transact调用。

    客户端(Proxy):

    Proxy(是BinderProxy的代理):

    private static class Proxy implements com.binder.service.IMyAidl {
    	...
    }
    
    • Proxy是一个实体类,不是抽象类(Stub是一个抽象类)。
    • Proxy实现了我们定义的AIDL接口,因此Proxy实现了AIDL接口中定义的方法。
    • Proxy里面维护了一个BinderProxy对象,Proxy是BinderProxy的代理。

    Proxy实现的AIDL接口中的方法:addPerson()、getPersonList()

     private static class Proxy implements com.binder.service.IMyAidl {
     			private android.os.IBinder mRemote;//BinderProxy对象
     			
     			...
                
                @Override
                public void addPerson(com.binder.service.Person person) throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        if ((person != null)) {
                            _data.writeInt(1);
                            person.writeToParcel(_data, 0);
                        } else {
                            _data.writeInt(0);
                        }
                        boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                        if (!_status && getDefaultImpl() != null) {
                            getDefaultImpl().addPerson(person);
                            return;
                        }
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
                
                @Override
                public java.util.List<com.binder.service.Person> getPersonList() throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    java.util.List<com.binder.service.Person> _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        boolean _status = mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
                        if (!_status && getDefaultImpl() != null) {
                            return getDefaultImpl().getPersonList();
                        }
                        _reply.readException();
                        _result = _reply.createTypedArrayList(com.binder.service.Person.CREATOR);
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
                
                ...
    
    }
    
    

    其中关键的代码:

    //_data  发送到服务端的数据 
    //_reply 服务端返回的数据
    boolean _status = mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
    

    code参数:传的是Stub.TRANSACTION_addPerson,代表方法的序号(序号是从1开始的)。
    _data: 发送到服务端的数据
    _reply: 服务端返回的数据
    flags参数:0表示阻塞调用,1表示非阻塞调用。这里mRemote.transact方法的flags参数设置的是0,即发起远程过程调用(RPC)请求,同时客户端对应的线程会挂起,等待服务端返回数据后才会继续执行。

    flags 参数
    flags – Additional operation flags. Either 0 for a normal RPC, or FLAG_ONEWAY for a one-way RPC.

    默认情况下flags = 0,跨进程的操作是同步的,即transact方法会阻塞当前调用线程,直到远程服务端返回;当flags = FLAG_ONEWAY时,表示对transact方法调用是单向调用,不会阻塞当前调用线程,而是立即返回。

    那么Android framework中哪些调用是阻塞调用,哪些调用是立即返回的呢?
    1.APP调用sysytem_server的方法基本是阻塞调用,比如app调用AMS的方法基本上是阻塞调用,ActivityManagerProxy中的方法mRemote.transact()使用的是的flag为0。
    2.sysytem_server调用APP的方法基本是非阻塞调用,比如AMS调用app的方法基本上是非阻塞调用,ApplicationThreadProxy中的方法mRemote.transact()使用的是flag为IBinder.FLAG_ONEWAY。
    总体而言,系统进程sysytem_server是很忙的,不能阻塞等待应用,一般是应用等待系统进程。

    总结:
    1、参数的传递过程,首先客户端数据转换成序列化数据,然后传递给remote对象,remote.transact()方法执行后的结果_reply是序列化数据,后续会转换成方法的返回数据;binder进程里面会将序列化数据转换成类进行处理,处理的结果也会转换成序列化数据,跨进程通讯只能传递序列化数据。
    2.mRemote.transact方法的flags参数是0,线程会阻塞,直到服务端返回数据后才继续执行。
    3.服务端和客户端在同一个进程时,onServiceConnected()回调方法返回的是Stub对象;服务端和客户端不在同一个进程时,onServiceConnected()回调方法返回的是BinderProxy对象。

    服务端(Stub):

    Stub(是一个Binder对象):

    public static abstract class Stub extends android.os.Binder implements com.binder.service.IMyAidl {
    	...
    }
    
    • Stub是一个抽象类
    • Stub继承了android.os.Binder,因此Stub就是一个Binder对象,实现了在服务端的代码
    • Stub实现了我们定义的AIDL接口,因此Stub要实现AIDL接口中定义的方法,因为Stub是抽象类,本身并没有实现AIDL接口中定义的方法,因此服务端代码在创建Stub对象返回给客户端时要实现AIDL接口中定义的方法。

    Stub称为存根类,或者桩。Stub 跟 Proxy 是一对,俗称“代理-桩”,一般在代理设计模式或者RMI远程方法调用会用到。
    Proxy 相当于是拿在手里的遥控器,而 Stub 相当于长在电视机里的遥控接收器,它们有着一一对应的接口方法,但操作的方向刚好相反。
    Proxy 的接口供客户端程序调用,然后它内部会把信息包装好,以某种方式传递给 Stub,而后者通过对应的接口作用于服务端系统,从而完成了“远程调用”。

        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.binder.service.IMyAidl {
            private static final java.lang.String DESCRIPTOR = "com.binder.service.IMyAidl";
    	
    		...
    	
            @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;
                //根据客户端传递的code值判断调用哪个方法
                switch (code) {
                    case INTERFACE_TRANSACTION: {
                        reply.writeString(descriptor);
                        return true;
                    }
                    case TRANSACTION_addPerson: {//根据code值判断调用addPerson方法
                    	//进行descriptor的校验
                        data.enforceInterface(descriptor);
                        //binder传输的是序列化数据,需要进行数据反序列化
                        com.binder.service.Person _arg0;
                        if ((0 != data.readInt())) {
                            _arg0 = com.binder.service.Person.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        //调用定义的aidl接口方法
                        this.addPerson(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getPersonList: {//根据code值判断调用getPersonList方法
                        data.enforceInterface(descriptor);
                        java.util.List<com.binder.service.Person> _result = this.getPersonList();
                        reply.writeNoException();
                        reply.writeTypedList(_result);
                        return true;
                    }
                    default: {
                        return super.onTransact(code, data, reply, flags);
                    }
                }
            }
            
    		...
    
    }
    
    

    关键的是onTransact()方法,方法里会调用this.addPerson(_arg0);方法。

    看下服务端的调用栈:

    2020-12-26 17:42:00.335 6652-6672/com.binder.service I/MyService: addPerson()
        java.lang.Exception
            at com.binder.service.MyService$1.addPerson(MyService.java:30)
            at com.binder.service.IMyAidl$Stub.onTransact(IMyAidl.java:72)
            at android.os.Binder.execTransactInternal(Binder.java:1154)
            at android.os.Binder.execTransact(Binder.java:1123)
    
    

    即数据经过Binder驱动后会到达服务端进程的Binder的native层,然后最终会调用服务端创建的Stub对象的onTransact方法,onTransact方法中会调用Stub对象的实现的aidl接口中的方法。

    bindService()流程分析:

    只有先经过了bindService()流程,才有前面分析的客户端调用服务端方法的过程,那么bindService()流程是怎么样的?下面来分析。

    参看:bindService()流程源码分析

    读相关源码时一遍不理解很正常,读2遍,读3遍,多读几遍多思考,第一遍时理解整体大概流程就可以了,后面再理解细节源码。

    AMS的bindService方法与我们自定义的aidl接口的方法的比较

    在这里插入图片描述
    将ActivityManagerService与IMyAidl做个对比,ActivityManagerService就类似我们在MyService中创建的Stub的子类对象:

        private IBinder iBinder = new IMyAidl.Stub() {
            @Override
            public void addPerson(Person person) throws RemoteException {
                Log.i(TAG, "addPerson()", new Exception());
                persons.add(person);
            }
    
            @Override
            public List<Person> getPersonList() throws RemoteException {
                return persons;
            }
        };
    

    只不过我们创建的是Stub的匿名子类对象,而ActivityManagerService是继承ActivityManagerNative(ActivityManagerNative就类似IMyAidl.Stub类)。
    即new IMyAidl.Stub()创建的对象是继承IMyAidl.Stub,ActivityManagerService对象是继承ActivityManagerNative。

    ActivityManagerService中的bindService方法(定义在IActivityManager中)就类似IMyAidl中的addPerson方法。

    ActivityManagerService是一个Binder对象,那他是在哪个系统服务中返回的?
    SystemServer进程启动了一系列的系统服务,ActivityManagerService是其中一个重要的服务。

    整体原理图

    不同进程中的对象是不能直接调用方法的,只有当对象在同一个进程中才可以。

    图中A进程的Test对象可以直接调用B进程的Test对象的方法吗?是不能的,只有通过Binder驱动进行跨进程通信才可以实现。这道理看起来很简单,但是理解它非常重要,只有理解了这一点,那么就知道上述例子中,如果启动的Service(即例子中的MyLocalService)是与客户端进程在同一个进程,那么iMyAidl = IMyAidl.Stub.asInterface(service);得到的iMyAidl对象其实就是MyLocalService中创建的Stub对象;如果启动的Service(即例子中的MyService)与客户端进程不在同一个进程,那么iMyAidl = IMyAidl.Stub.asInterface(service);得到的iMyAidl对象其实是Stub.Proxy对象,Stub.Proxy对象里面维护了一个BinderProxy对象,BinderProxy意味着是远端服务端进程的Binder对象在客户端进程的一个代理对象。
    在这里插入图片描述

    总结

    Binder通信机制在系统组件间以及很多大型项目中都经常使用,比如:

    • 插件化:支付宝纳入海量应用
    • 大型登入架构的实现

    理解了Binder通信机制的原理后再看相关源码时才能容易读懂。

    Binder深入

    关于Binder驱动

    Binder在Android里被设计成了一个驱动,安装在/dev/binder,这也是Android和linux的重要区别之一,Binder是Android特有的IPC机制,linux中并没有。

    参考:
    https://developer.android.google.cn/guide/components/aidl.html?hl=zh-cn

    整体流程:
    Android Binder(也许是最容易理解的)
    关于Binder机制的简单认识
    简单理解 Binder 调用流程
    Android中的Binder机制二(匿名Binder)

    Binder中的transact方法是什么时候调用的?

    AIDL:
    Android:学习AIDL,这一篇文章就够了

    Binder的发展历史:
    为什么 Android 要采用 Binder 作为 IPC 机制?
    【Android系统】binder 到底是什么?openbinder 又是什么?它是什么机制?

    关于Binder驱动
    Android Binder实现浅析-Binder驱动

    源码:
    https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/app/ActivityManagerNative.java

    展开全文
  •  switch (match){ case PEOPLE: //需要做的事情 case PEOPLE_ID: //需要做的事情 } 而ContentUris类用在调用这个provider的进程中使用,用于拼装Uri: 1)为路径加上ID: withAppendedId(uri, id) 2)从路径获取ID...

    在讲ContentProvider之前,我们先要了解Uri,URI即为通用资源标识符(Uniform Resource

    Identifier),由4部分组成:

    a4c26d1e5885305701be709a3d33442f.png

    将其分为A,B,C,D 4个部分:

    A:标准前缀,用来说明一个Content

    Provider控制这些数据,无法改变的,而我们自定义的provider都是以content://开头;

    B:URI的标识,它定义了是哪个Content

    Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在AndroidManifest中的

    元素的 authorities属性中说明:

    C:路径,Content

    Provider使用这些路径来确定当前需要生什么类型的数据,URI中可能不包括路径,也可能包括多个;

    D:如果URI中包含,表示需要获取的记录的ID;如果没有ID,就表示返回全部;

    例如:Uri CONTENT_URI = Uri.parse("content://" +

    AUTHORITY + "/employee");该Uri中没有D部分,如果用该Uri就放回全部。

    而对于Uri的操作需要用到两个类,分别是UriMatcher类和ContentUris类。其中UriMatcher类在提供provider的进程中使用,用于匹配Uri,得到Uri中包含的信息,例如:

    注册需要匹配的uri

    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

    matcher.addURI("com.yfz.Lesson", "people", PEOPLE);

    matcher.addURI("com.yfz.Lesson", "person/#", PEOPLE_ID);

    匹配uri int match =

    matcher.match(uri); switch (match){ case PEOPLE:

    //需要做的事情

    case PEOPLE_ID:

    //需要做的事情 }

    而ContentUris类用在调用这个provider的进程中使用,用于拼装Uri:

    1)为路径加上ID: withAppendedId(uri, id)

    2)从路径中获取ID: parseId(uri)

    当然,还得用到SQLite的知识,这里就不多做介绍了。我们在创建一个provider的时候,会用到ContentProvider这个类,该类是存储和获取数据提供统一的接口。我们创建一个类继承contentprovider并实现:

    query:查询

    insert:插入

    update:更新

    delete:删除

    getType:得到数据类型

    onCreate:创建数据时调用的回调函数

    等方法,该类的功能在于:1.组织应用程序的数据;2.向其他应用程序提供数据;对于该类,我们需要在

    AndroidManifest.XML中使用元素明确定义。具体代码可以如下写:

    package com.zjwujlei.myprovider;

    import java.util.ArrayList;

    import java.util.HashMap;

    import android.content.ContentProvider;

    import android.content.ContentProviderOperation;

    import android.content.ContentProviderResult;

    import android.content.ContentUris;

    import android.content.ContentValues;

    import android.content.OperationApplicationException;

    import android.content.UriMatcher;

    import android.database.Cursor;

    import android.database.sqlite.SQLiteDatabase;

    import android.database.sqlite.SQLiteQueryBuilder;

    import android.net.Uri;

    import android.text.TextUtils;

    import com.zjwujlei.myprovider.Employees.Employee;

    public class EmployeeProvider extends ContentProvider{

    private static final UriMatcher

    sUriMatcher;

    private DBHelper dbHelper;

    private static final int EMPLOYEE = 1;

    private static final int EMPLOYEE_ID = 2;

    private static HashMap empProjectionMap;

    static{

    sUriMatcher = new

    UriMatcher(UriMatcher.NO_MATCH);

    sUriMatcher.addURI(Employees.AUTHORITY,

    "employee", EMPLOYEE);

    sUriMatcher.addURI(Employees.AUTHORITY,

    "employee/#", EMPLOYEE_ID);

    empProjectionMap = new

    HashMap();

    empProjectionMap.put(Employee._ID,

    Employee._ID);

    empProjectionMap.put(Employee.NAME,

    Employee.NAME);

    empProjectionMap.put(Employee.GENDER,

    Employee.GENDER);

    empProjectionMap.put(Employee.AGE,

    Employee.AGE);

    }

    @Override

    public int delete(Uri uri, String selection,

    String[] selectionArgs) {

    // TODO Auto-generated method

    stub

    SQLiteDatabase db =

    dbHelper.getWritableDatabase();

    int count;

    switch (sUriMatcher.match(uri))

    {

    case EMPLOYEE:

    count =

    db.delete(DBHelper.EMPLOYEES_TABLE_NAME, selection,

    selectionArgs);

    break;

    case EMPLOYEE_ID:

    String noteId

    = uri.getPathSegments().get(1);

    count =

    db.delete(DBHelper.EMPLOYEES_TABLE_NAME, Employee._ID + "=" +

    noteId

    +

    (!TextUtils.isEmpty(selection)?"AND ("+selection +')':""),

    selectionArgs);

    break;

    default:

    throw new

    IllegalArgumentException("错误的Uri" + uri);

    }

    getContext().getContentResolver().notifyChange(uri,

    null);

    return count;

    }

    @Override

    public String getType(Uri uri) {

    // TODO Auto-generated method

    stub

    return null;

    }

    @Override

    public Uri insert(Uri uri, ContentValues values)

    {

    // TODO Auto-generated method

    stub

    System.out.println("进入Employee的insert方法");

    SQLiteDatabase db =

    dbHelper.getWritableDatabase();

    long rawId =

    db.insert(DBHelper.EMPLOYEES_TABLE_NAME, Employee.NAME,

    values);

    if(rawId>0){

    Uri empUri =

    ContentUris.withAppendedId(Employee.CONTENT_URI, rawId);

    getContext().getContentResolver().notifyChange(empUri,

    null);

    System.out.println("rawId"+rawId);

    System.out.println("完成Employee的insert方法");

    return

    empUri;

    }

    return null;

    }

    @Override

    public boolean onCreate() {

    // TODO Auto-generated method

    stub

    dbHelper = new

    DBHelper(getContext());

    return true;

    }

    @Override

    public Cursor query(Uri uri, String[] projection,

    String selection,

    String[]

    selectionArgs, String sortOrder) {

    // TODO Auto-generated method

    stub

    SQLiteQueryBuilder qb = new

    SQLiteQueryBuilder();

    switch (sUriMatcher.match(uri))

    {

    case EMPLOYEE:

    qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);

    qb.setProjectionMap(empProjectionMap);

    break;

    case EMPLOYEE_ID:

    qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);

    qb.setProjectionMap(empProjectionMap);

    qb.appendWhere(Employee._ID

    + "=" + uri.getPathSegments().get(1));

    break;

    default:

    throw new

    IllegalArgumentException("错误的Uri" + uri);

    }

    String orderBy;

    if

    (TextUtils.isEmpty(sortOrder)) {

    orderBy =

    Employee.DEFAULT_SORT_ORDER;

    }else{

    orderBy =

    sortOrder;

    }

    SQLiteDatabase db =

    dbHelper.getReadableDatabase();

    Cursor cursor = qb.query(db,

    projection, selection, selectionArgs, null, null, orderBy);

    cursor.setNotificationUri(getContext().getContentResolver(),

    uri);

    return cursor;

    }

    @Override

    public int update(Uri uri, ContentValues values,

    String selection,

    String[]

    selectionArgs) {

    // TODO Auto-generated method

    stub

    SQLiteDatabase db =

    dbHelper.getWritableDatabase();

    int count;

    switch (sUriMatcher.match(uri))

    {

    case EMPLOYEE:

    count =

    db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, selection,

    selectionArgs);

    break;

    case EMPLOYEE_ID:

    String noteId

    = uri.getPathSegments().get(1);

    count =

    db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, Employee._ID + "="

    + uri.getPathSegments().get(1)

    +

    (!TextUtils.isEmpty(selection)?" AND (" + selection + ')':""),

    selectionArgs);

    // System.out.println(DBHelper.EMPLOYEES_TABLE_NAME,

    values, Employee._ID + "=" + uri.getPathSegments().get(1)+

    (TextUtils.isEmpty(selection)?" AND (" + selection + ')':""),

    selectionArgs);

    break;

    default:

    throw new

    IllegalArgumentException("错误的Uri" + uri);

    }

    getContext().getContentResolver().notifyChange(uri,

    null);

    return count;

    }

    }

    当然,这里用到了Sqlite的知识,即DBHleper这个类,在该类里做的就是创建数据库的操作,具体代码如下:

    package com.zjwujlei.myprovider;

    import android.content.Context;

    import android.database.sqlite.SQLiteDatabase;

    import android.database.sqlite.SQLiteOpenHelper;

    import com.zjwujlei.myprovider.Employees.Employee;

    public class DBHelper extends SQLiteOpenHelper{

    public static final String DATABASE_NAME =

    "Employees.db";

    public static final int DATABASE_VERSION =

    1;

    public static final String EMPLOYEES_TABLE_NAME =

    "employee";

    public DBHelper(Context context) {

    super(context, DATABASE_NAME,

    null, DATABASE_VERSION);

    // TODO Auto-generated

    constructor stub

    }

    @Override

    public void onCreate(SQLiteDatabase db) {

    // TODO Auto-generated method

    stub

    String sql = "CREATE TABLE " +

    EMPLOYEES_TABLE_NAME +" ("+ Employee._ID+" INTEGER PRIMARY

    KEY,"+

    Employee.NAME + " TEXT," +

    Employee.GENDER + " TEXT," + Employee.AGE + " INTERGER" +

    ")";

    db.execSQL(sql);

    }

    @Override

    public void onUpgrade(SQLiteDatabase db, int

    oldVersion, int newVersion) {

    // TODO Auto-generated method

    stub

    String sql = "DROP TABLE IF

    EXISTS "+EMPLOYEES_TABLE_NAME;

    db.execSQL(sql);

    onCreate(db);

    }

    }

    还有就是一个封装类Employee,里面封装了一些静态常量:

    package com.zjwujlei.myprovider;

    import android.net.Uri;

    import android.provider.BaseColumns;

    public class Employees {

    public static final String AUTHORITY =

    "com.zjwujlei.myprovider.employees";

    private Employees(){};

    public static final class Employee implements

    BaseColumns{

    private Employee(){}

    public static final Uri

    CONTENT_URI = Uri.parse("content://" + AUTHORITY

    + "/employee");

    public static final String

    CONTENT_TYPE = "vnd.android.cursor.dir/vnd.amaker.employees";

    public static final String

    CONTENT_ITEM_TYPE =

    "vnd.android.cursor.item/vnd.amaker.employees";

    public static final String

    DEFAULT_SORT_ORDER = "name DESC";

    public static final String NAME

    = "name";

    public static final String

    GENDER = "gender";

    public static final String AGE

    = "age";

    }

    }

    然后对于provider的创建基本是这样了,然后我们在另外一个进程中访问该provider,就需要用到一个类ContentResolver,用于获取ContentProvider提供的数据并修改/添加/删除更新数据等。我们用getContentResolver()方法得到ContentResolver的对象,该对象有update(),query()等方法,而该些方法实际上调用的就是我们在ContentProvider中定义的方法,其具体代码可以这样写:

    package com.zjwujlei.myprovider;

    import android.app.Activity;

    import android.content.ContentUris;

    import android.content.ContentValues;

    import android.database.Cursor;

    import android.net.Uri;

    import android.os.Bundle;

    import com.zjwujlei.myprovider.Employees.Employee;

    public class ProviderActivity extends Activity{

    @Override

    protected void onCreate(Bundle

    savedInstanceState) {

    // TODO Auto-generated method

    stub

    super.onCreate(savedInstanceState);

    insert();

    query();

    update();

    query();

    insert();

    query();

    del();

    query();

    }

    public void del(){

    Uri uri =

    ContentUris.withAppendedId(Employee.CONTENT_URI, 1);

    getContentResolver().delete(uri,

    null, null);

    }

    public void update(){

    Uri uri =

    ContentUris.withAppendedId(Employee.CONTENT_URI, 1);

    ContentValues values = new

    ContentValues();

    values.put(Employee.NAME,

    "hz.guo");

    values.put(Employee.AGE,

    31);

    values.put(Employee.GENDER,

    "male");

    getContentResolver().update(uri,

    values, null, null);

    }

    public void query(){

    Uri uri =

    Employee.CONTENT_URI;

    String[] PROJECTION = new

    String[]{

    Employee._ID,

    Employee.NAME,

    Employee.GENDER,

    Employee.AGE

    };

    Cursor cursor =

    getContentResolver().query(uri, PROJECTION, null, null,

    null);

    System.out.println("cursor.moveToFirst()"+cursor.moveToFirst());

    if(cursor.moveToFirst()){

    System.out.println("cursor.getCount()"+cursor.getCount());

    for(int i =

    0;i

    cursor.moveToPosition(i);

    int

    id = cursor.getInt(0);

    String

    name = cursor.getString(1);

    int

    age = cursor.getInt(3);

    System.out.println("id"+id+"name"+name+"age"+age);

    }

    }

    }

    public void insert(){

    System.out.println("进入providerActivity的insert方法");

    Uri uri =

    Employee.CONTENT_URI;

    ContentValues values = new

    ContentValues();

    values.put(Employee.AGE,

    30);

    values.put(Employee.GENDER,

    "male");

    values.put(Employee.NAME,

    "amaker");

    getContentResolver().insert(uri,

    values);

    System.out.println("完成providerActivity的insert方法");

    }

    }

    这些就是对于contentProvider的创建以及使用的大致方法。当然,我们还可以对我们的ContentProvider添加事物的功能,这里就不多做介绍了。

    展开全文
  • Android进程间通信总结

    2021-09-06 21:26:37
    安卓主要采用 Binder 进行进程间通信,当然也支持其他 IPC 方式,如:管道,Socket,文件共享,信号量等 Binder简介 1.为什么使用Binder? 性能方面: ​ 在移动设备上(性能受限制的设备,比如要省电),广泛地...
  • 有以下四个ThreadMode:PostThread:事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程。对应的函数名是onEvent。MainThread: 事件的处理会在UI线....
  • 在一个应用启动多个进程 例如:将自己封装的CommonWebViewActivity在一个单独的进程中启动 使用android:process <activity android:name=".activity.CommonWebViewActivity" android:process=":webview" ...
  • Binder是Android进程间通信机制,就像共享内存,共享文件等一样,是一套IPC的机制;Android的Binder机制是在OpenBinder开源项目实现的;Binder机制是一个C/S的通信架构相比于其他IPC的优势?更好的传输性能SOCKET:...
  • Android 进程间通信之binder - 实战

    千次阅读 2021-02-21 10:04:03
    我们从binder由来开始说起,说说Android在binder的规范写法和非规范的写法;应文章标题,通过实战代码讲述binder传输内容的组织形式。分别用java和c++两种语言实现binder通信;分析本地binder和远程binder;实现一个...
  • Android进程间通信机制

    2021-05-10 02:24:26
    Android所拥有的IPCAndroid系统有大量IPC(进程间通信)的场景,比如我们想要创建一个新的进程,需要通过Socket这种IPC方式去让Zygote Fork新进程;如果我们要杀掉一个进程,需要通过信号这种IPC方式去将SIGNAL_KILL...
  • 首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待...
  • Messenger,即进程间通信的信使.它是基于Message的进程间通信,我们可以像在线程间利用Handler.send(Message)一样. Messenger是一种轻量级的IPC方案,它的底层实现其实就是AIDL.跨进程通信使用Messenger时,Messenger会...
  • 每一个应用程序都是由一些Activity和Service组成的,这些Activity和Service有可能运行在同一个进程中,也有可能运行在不同的进程中android的IPC机制也就是Binder机制,Android系统是基于Linux内核的,而Linux内...
  • 一.IPC简介1.IPC:是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。2.线程:是CPU调度的最小单元,同时线程是一种有限的系统资源3.进程:是指一个执行...
  • Android进程间和线程间通信方式

    千次阅读 2021-01-17 13:22:41
    线程自己基本上不拥有系统资源,只拥有一些在运行必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。区别:(1)、一个程序至少有一个进程,一个进...
  • Android进程间通信方式

    2021-05-15 13:12:51
    Activity,Service,Receiver 都支持在 Intent 传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。 Bundle bundle = new Bundle(); bundle.putString("test", "来自A"); ...
  • 好文推荐:Android 操作系统架构开篇 - Gityuan博客 | 袁辉辉的技术博客Android ...专业术语称为IPC(Inter-Process Communication,进程间通信),常见Linux的IPC机制有:Pipe管道,Message Queue消息队列,Shared ...
  • 本文Android代码来源androidxref.com的Android11版本。从binder环境初始化开始,讲述这几个在binder需要掌握的数字。首先我们思考这么几个问题;...6,ams启动进程,跟zygote通信方式为什么选用socket?
  • Android——进程间通信方式

    千次阅读 2021-06-01 16:05:16
    Activity,Service,Receiver 都支持在 Intent 传递 Bundle 数据,而 Bundle实现了 Parcelable 接口,可以在不同的进程间进行传输。 在一个进程启动了另一个进程的 Activity,Service 和 Receiver ,可以在...
  • AIDL是Android Interface definition language(Android接口定义语言)的缩写,由于不同进程间不能共享内存,为了解决进程间通信的问题,可以通过AIDL接口语言来实现进程间的通信。让你可以在自己的APP里绑定一个...
  • AIDL(Android Interface Definition Language)是Android中为实现IPC(Inter Process Communication,进程间通信)的一种方式,本质是通过Binder实现,Binder是Android中最具特色的、广泛应用的进程间通信框架,...
  • 本篇文章为Android进程间通信 深入浅出AIDL系列的第二篇,建议配合AIDL第一篇食用 3. AIDL原理 3.1 AIDL是怎么工作的? 我们编写了aidl文件之后,啥也没干就自动拥有了跨进程通信的能力.这一切得归功于Android Studio...
  • 所以两个进程如果需要相互访问就涉及到一个跨进程通信的概念即IPC(Inter-process communication,进程间通讯)。本质上是借助于不同进程的内核空间都是共享的原理,两个不同的进程都去访问内核空间,从而达到“间接”...
  • Android中,每一个进程都有自己的Dalvik VM实例,拥有一个独立的内存空间,进程与进程之间是不共享内存的,每个进程都在自己的内存空间内进程数据存储,有时候我们需要两个进程之间进行数据交互,这就产生了进程间...
  • 由于android系统应用程序之间不能共享内存。因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些。系统提供了4种跨进程通讯的方式。对应于应用程序四大组件:ActivityContent ProviderBroadcastService...
  • 那么,还有其他的方式实现进程间的通信吗,当然有啦,之前讲过,进程间的通信方式有:Messenger,Bundle,文件共享,Content Provider……,我们这一节讲一下文件共享二、文件共享共享文件也是一种不错的进程间通信....
  • Android中进程间通信

    2021-10-28 09:28:45
    IPC(Inter-Process Communication)为进程间通信或跨进程通信,是指两个进程进行进程间通信的过程。 在Android中,为每一个应用程序都分配了一个独立的虚拟机,不同虚拟机在内存分配上都有不同的地址空间,互相访问...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,445
精华内容 18,978
关键字:

android中进程间通信