扒一扒,互联网大厂最爱问的 “Framework Binder” 面试题

互联网大厂对于 “Binder” 会问什么?会问的有多深?

我相信大家和我一样都很好奇。虽然“Binder”中的很多技术一部分只有互联网大厂中项目才能用的到,但是不甘于当咸鱼的我还是对BAT怀揣着向往,想在明年的春招中一举拿下 offer,所以年末的复习总结主要是以面试相关的技术知识点,和我有同样想法的朋友,可以参考我在Github花几个月整理 《Android面试核心知识点》。

面试参考

本篇,主要以我整理的面试题的方式来讲解 Binder机制,如觉写的可以,请留下你的点赞

  • 分析进程间通信方式
  • binder里面的proxy类采用了动态代理。核心方法:mRotate.transact(Stub.TRANSACTION_xxx,_data,_reply,flags);
  • Binder有什么优势?(字节跳动真题)
  • Binder是如何做到一次拷贝的?(腾讯真题)
  • MMAP的原理讲解;(腾讯真题)
  • 描述AIDL生成的Java类细节;(字节跳动真题)
  • 四大组件底层的通信机制;(字节跳动真题)
  • 为什么Intent不能传递大数据?(阿里真题)

进程间通信方式

进程间通信方式有蛮多种,比如通过共享内存、Socket、管道啊等等,但是我们作为安卓开发并不会采用这些(而且这块我也不懂,后续如果需要再研究呗),安卓中使用的是binder。至于区别,如下图。

用binder的优势:

  • 1.拷贝只需要一次,虽然有不需要拷贝的通信机制,但是就会涉及到信息资源时刻同步,这个是很耗性能的,而且控制极其复杂,易用性也差
  • 2.安全,因为为每个app分配UID(可以看作是应用的身份证)。

内存划分

想知道怎么传输数据的,需要先了解内存划分

Binder与传统IPC如何传输数据的呢?

binder与传统ipc(进程间通信机制)对比
传统ipc传输数据流程
一次拷贝:服务端一块空间(虚拟内存)跟内核空间(虚拟内存)通过MMP映射到同一块物理内存
无需拷贝:发送端、接收端、共享区域映射到同一块物理内存.(会涉及到各种信息资源同步,导致控制复杂、易用性差)

上层通过AIDL、底层通过Binder实现安卓端的进程通信,AIDL实现进程间通信流程

哈哈,这张图是从我自己画的,感觉诠释进程间通信很透彻了,上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等\color{#FF0000}{上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等}上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等 整条线路就完整了。

客户端如何拿到AIDL句柄,通过这个句柄如何调用服务端,服务端怎么处理

Demo实例

  • 1.工程结构

服务端


客户端

  • 2.代码

如何新建aidl文件我就不说了,这个是基础。
服务端

//1.ILiaoAidlInterface

package com.xm.aidl_server;

// Declare any non-default types here with import statements
import com.xm.aidl_server.Person;
interface ILiaoAidlInterface {

       List<Person> getPersonList();

       void addPerson(in Person person);
}

--------------------------------
//2.Person aidl文件

package com.xm.aidl_server;

parcelable Person;
-------------------


//3.Person对应的实体类
package com.xm.aidl_server;

import android.os.Parcel;
import android.os.Parcelable;

//1.Android开发:什么是Parcel?  http://blog.csdn.net/nkmnkm/archive/2011/05/28/6451699.aspx
//2.android开发之Parcelable使用详解  https://blog.csdn.net/u012702547/article/details/47151001/
public class Person implements Parcelable {
    private String name;
    private int grade;

    public Person(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    protected Person(Parcel in) {
        this.name = in.readString();
        this.grade = in.readInt();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(grade);
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

-------------------------


//4.服务端app对应的服务(Service)类
package com.xm.aidl_server;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.Nullable;

public class MyService extends Service {

    private List<Person> personList;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        personList = new ArrayList<>();
        return iBinder;
    }
    private IBinder iBinder = new ILiaoAidlInterface.Stub() {
        @Override
        public List<Person> getPersonList() throws RemoteException {
            return personList;
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            personList.add(person);
        }
    };
}


--------------------------------
//5.启动服务端app的Service

public class MainActivity extends AppCompatActivity {

    private Intent intent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intent = new Intent(this, MyService.class);
        startService(intent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(intent);
    }
}

上面就是服务端所有代码了,本来想截图放的 但是担心观看这篇博客的人能快速上手就都贴出来了。

客户端


这块代码是从服务端拷贝过来的,要想实现进程间通信,这块代码必须保持一致!!!

       Button btn = findViewById(R.id.btn);
        //1.第一步 绑定服务端service
        bindService();
        //第二步 设置按钮点击事件
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    iLiaoAidlInterface.addPerson(new Person("姓名",7));
                    List<Person> personList = iLiaoAidlInterface.getPersonList();
                    Log.i("MainActivity", "onClick: "+personList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        
        
 ---------------------------------------
 
 
 //2.bindService()方法
    private void bindService() {
        Intent intent = new Intent();
        //todo 为啥不能直接通过intent带参数 为啥非得这种方式  全包名、类名
        intent.setComponent(new ComponentName("com.xm.aidl_server","com.xm.aidl_server.MyService"));
        //todo 三个参数都得介绍
        //service 指的是服务端service
        //
        //参数三:flags: 为0,表示客户端可以发送到服务端,服务端也可以返回 为1,表示客户端发送到服务端,但是服务端不可以返回
       //_data:是客户端发送到服务端 _reply:是服务端可以发送到客户端
        bindService(intent,connection,0);
    }

----------------------------------------------

// 3.内部类connection 
 ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //todo
            iLiaoAidlInterface = ILiaoAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //todo
            iLiaoAidlInterface = null;
        }
    };

输出

//场景1:服务端Service如果没有开启 点击客户端按钮 日志如下

2020-12-26 23:42:32.821 10001-10001/com.xm.aidl_client W/System.err:     at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)
2020-12-26 23:42:54.203 10001-10001/com.xm.aidl_client W/System.err:     at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)
2020-12-26 23:43:08.919 10001-10001/com.xm.aidl_client W/System.err:     at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)

-----------------------------------------------------------
//场景2:服务端Service如果开启 点击客户端按钮 日志如下

2020-12-26 23:44:59.928 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}]
2020-12-26 23:45:01.761 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}, Person{name='姓名', grade=7}]
2020-12-26 23:45:04.672 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}, Person{name='姓名', grade=7}, Person{name='姓名', grade=7}]

A进程访问B进程时的几种状态

  • 1.进程B未启动
  • 2.进程B启动了,但是Service没有创建
  • 3.进程B启动了,Service创建了,但是Service没有绑定过,回调onBind()
  • 4.进程B启动了,Service创建了,但是Service绑定过,回调onRebind()

面试参考

猜你喜欢

转载自blog.csdn.net/qq_39477770/article/details/111866723