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"); 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>
元素允许应用声明它可以查询哪些其他应用的数据或组件,这有助于系统更好地管理应用的权限和可见性。