Binder功能简介

用途:Binder是一种进程间通信的机制。

特点:虚拟物理设备驱动,像人血管一样,穿梭在各个组件和进程之间,多进程通信,采用C/S架构。

参与角色:

Client进程:使用服务的进程需等待Server注册后,再向ServiceManager获取服务。

Server进程:提供服务的进程。需先向ServiceManager进行注册。

ServiceManager进程:管理注册过的服务。

Binder驱动:负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

优势

安全优势:传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信的IP地址是客户端手动填入,很容易进行伪造。Binder机制从协议本身就支持对通信双方做身份校验,为每个APP分配UID,进程的UID是鉴别进程身份的重要标志,从而大大提高了安全性。

性能优势:

共享内存:数据拷贝0次,但复杂且容易产生安全问题

Binder:数据拷贝1次,兼顾简单已用和安全问题

传统Socket/管道/消息队列:数据拷贝2次。

代码例子并升级代码1

步骤:

​ 服务端:1、继承Service 2、编写AndroidManifest.xml

​ 客户端:1、编写Service的连接,并获取远程Ibinder 2、实现绑定Service代码 3、实现获取信息功能的代码

UseBinderService

MainActivity.java

package a.b.c.usebinderserver;

import android.content.Intent;
import android.os.Bundle;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}

MyService.java

package a.b.c.usebinderserver;

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

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class MyService extends Service
{

public static final int REQUEST_CODE = 1000;
private final Binder mBinder = new Binder()
{
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException
{
if(code == REQUEST_CODE)
{
String studentName = data.readString();
int age = getStudentAge(studentName);
if(reply !=null)
{
reply.writeInt(age);
}
return true;
}
return super.onTransact(code, data, reply, flags);
}
public int getStudentAge(String name)
{
Student student = new Student();
return student.getAge(name);
}

};

@Nullable
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
}

Student.java

package a.b.c.usebinderserver;

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

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class MyService extends Service
{

public static final int REQUEST_CODE = 1000;
private final Binder mBinder = new Binder()
{
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException
{
if(code == REQUEST_CODE)
{
String studentName = data.readString();
int age = getStudentAge(studentName);
if(reply !=null)
{
reply.writeInt(age);
}
return true;
}
return super.onTransact(code, data, reply, flags);
}
public int getStudentAge(String name)
{
Student student = new Student();
return student.getAge(name);
}

};

@Nullable
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UseBinderServer"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyService"
android:exported="true"
android:enabled="true"
android:process=":server">
<intent-filter>
<action android:name="android.intent.action.server.student"/>
</intent-filter>
</service>
</application>

</manifest>

UseBinderClient

MainActivity.java

package a.b.c.usebinderclient;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity
{
TextView tv;
Button btn_bindService,btn_searchAge;
IBinder binder;
public static final int REQUEST_CODE = 1000;

private final ServiceConnection serviceConnection = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
binder = iBinder;

}

@Override
public void onServiceDisconnected(ComponentName componentName)
{
binder = null;
}
};
private void bindRemoteService()
{
String action = "android.intent.action.server.student";
Intent intent = new Intent(action);
intent.setPackage("a.b.c.usebinderserver");//这里认真点,不要加package
boolean bt = this.bindService(intent, serviceConnection,BIND_AUTO_CREATE);
Log.i("ttttt-client","bindRemoteService result:" + bt);
}

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
tv = findViewById(R.id.tv_view);
btn_bindService = findViewById(R.id.btn_bindService);
btn_searchAge = findViewById(R.id.btn_searchAge);
btn_bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view)
{
bindRemoteService();
}
});
btn_searchAge.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view)
{
Log.i("tttt-client","开始查询");
getRemoteAge("zhangsan");
}
});
}
private void getRemoteAge(String name)
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeString(name);
try
{
if(binder == null)
{
return ;

}
binder.transact(REQUEST_CODE,data,reply,0);
int age = reply.readInt();
tv.setText("查询年龄结果:"+age);
}catch (Exception ee)
{
ee.printStackTrace();
tv.setText(ee.toString());
}

}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<package android:name="a.b.c.usebinderserver" />
</queries>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UseBinderClient"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

优化1:

​ 服务端:1、继承Service——优化创建Binder类实现传输和数据提供方法 2、编写AndroidManifest.xml

​ 客户端:1、编写Service的连接,并获取远程Ibinder——编写代理类管理Binder 2、实现绑定Service代码 3、实现获取信息功能代码——优化编写接口

UseBinderService

MainActivity.java

package a.b.c.usebinderserver;

import android.content.Intent;
import android.os.Bundle;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}

MyService.java

package a.b.c.usebinderserver;

import android.os.Binder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class ServerBinder extends Binder implements IStudentInterface {
public static final int REQUEST_CODE = 1000;

@Override
public int getStudentAge(String name) {
Student student = new Student();
return student.getAge(name);
}

@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == REQUEST_CODE){
String studentName = data.readString();
Log.i("ttttttt-service", "recv :"+studentName);
int age = getStudentAge(studentName);
Log.i("ttttttt-service", "return :"+age);
if (reply != null)
reply.writeInt(age);
return true;
}
return super.onTransact(code, data, reply, flags);
}
}

Student.java

package a.b.c.usebinderserver;
import java.util.HashMap;
import java.util.Map;
public class Student
{
private Map<String,Integer> info = new HashMap<>();
public Student ()
{
info.put("zhangsan",23);
info.put("lisi",19);
info.put("wangwu",20);
}
public int getAge(String name)
{
return info.get(name);
}
public void setAge(String name,int age)
{
info.put(name,age);
}
}


ServerBinder.java

package a.b.c.usebinderserver;

import android.os.Binder;
import android.os.Parcel;
import android.os.RemoteException;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class ServerBinder extends Binder implements IStudentInterface
{
public static final int REQUEST_CODE = 1000;

@Override
public int getStudentAge(String name)
{
Student student = new Student();
return student.getAge(name);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException
{
if (code == REQUEST_CODE) {
String studentName = data.readString();
int age = getStudentAge(studentName);
if (reply != null) {
reply.writeInt(age);
}
return true;
}
return super.onTransact(code, data, reply, flags);
}

}

IStudentInterface.java

package a.b.c.usebinderserver;

public interface IStudentInterface
{
//查询年龄接口
int getStudentAge(String name);
}

UseBinderClient

MainActivity.java

package a.b.c.usebinderclient;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity
{
TextView tv;
Button btn_bindService,btn_searchAge;
IStudentInterface binder;


private final ServiceConnection serviceConnection = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
binder = BinderProxy.asInerface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName)
{
binder = null;
}
};
private void bindRemoteService()
{
String action = "android.intent.action.server.student";
Intent intent = new Intent(action);
intent.setPackage("a.b.c.usebinderserver");
boolean bt = this.bindService(intent, serviceConnection,BIND_AUTO_CREATE);
Log.i("ttttt-client","bindRemoteService result:" + bt);
}

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
tv = findViewById(R.id.tv_view);
btn_bindService = findViewById(R.id.btn_bindService);
btn_searchAge = findViewById(R.id.btn_searchAge);
btn_bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view)
{
bindRemoteService();
}
});
btn_searchAge.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view)
{
Log.i("tttt-client","开始查询");
binder.getStudentAge("zhangsan");
}
});
}

}

BinderProxy.java

package a.b.c.usebinderclient;

import android.os.IBinder;
import android.os.Parcel;

public class BinderProxy implements IStudentInterface
{
public static final int REQUEST_CODE = 1000;
private IBinder mRemoteBinder;
public BinderProxy(IBinder mRemoteBinder)
{
this.mRemoteBinder = mRemoteBinder;
}


@Override
public int getStudentAge(String name)
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeString(name);
int age = -1;
try
{
if(mRemoteBinder== null)
{
return age;
}
mRemoteBinder.transact(REQUEST_CODE,data,reply,0);
age = reply.readInt();
return age;

}catch (Exception ee)
{
ee.printStackTrace();
}
return age;
}

public static IStudentInterface asInerface(IBinder iBinder)
{
if(iBinder == null)
{
return null;
}
if(iBinder instanceof IStudentInterface)
{
return (IStudentInterface) iBinder;
}
else
{
return new BinderProxy(iBinder);
}
}
}

IStudetnInterface.java

package a.b.c.usebinderclient;

public interface IStudentInterface
{
//查询年龄接口
int getStudentAge(String name);
}

注意:Android11及以后的版本要在客户端加上服务端的包名

<queries>
<package android:name="a.b.c.usebinderserver"
</queries>

在AndroidManifest.xml文件中,<queries>元素是用于声明应用需要的查询信息,这主要用于提高应用的安全性和隐私性,特别是在Android 11(API 级别 30)及更高版本中。<queries>元素允许应用声明它可以查询哪些其他应用的数据或组件,这有助于系统更好地管理应用的权限和可见性。