进程间通信(IPC)方式
Bundle
Android中三大组件Activity、Service、Receiver都支持在Intent中传递Bundle数据,而Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间进行传输。当在一个进程中启动另外一个进程的Activity、Service、Receiver时,我们就可以在Bundle中附加我们所需要传输给远程进程的信息并通过intent发送出去,传输的数据必须能够被序列化,且只能单方向的简单数据传输
private void startWithIntent(){
Intent intent = new Intent();
//制定要打开的程序的包名(必须写全包名,不然会报错)和地址(activity名)
intent.setComponent(new ComponentName("PackageName",
"PackageName.intentIpcActivity"));
//通过budle传递数据,可以携带序列化数据
Bundle bundle = new Bundle();
bundle.putInt("intextra", 0);
bundle.putString("stringextra", "测试数据");
intent.putExtras(bundle);
try{
startActivity(intent);
}catch(Exception e){
ToastUtils.showMessage("没有找到对应文件");
}
}
文件共享
两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件FILE,B进程可以通过读取这个文件来获取这个数据。通过这种方式,除了可以交换简单的文本信息外,我们还可以序列化一个对象到文件系统中,另一个进程可以通过反序列化恢复这个对象
//比如A在进程中创建一个线程进行写数据
new Thread(new Runnable(){
@Override
public void run(){
User user = new User(1, "user", false);
File cachedFile = new File(CACHE_FILE_PATH);
ObjectOutputStream objectOutputStream = null;
try{
objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
}catch(IOException e){
e.printStackTrace();
}finally{
objectOutputStream.close();
}
}
}).start();
//在B进程创建一个线程进行读取数据
new Thread(new Runnable(){
@Override
public void run(){
User user = null;
File cachedFile = new File(CACHE_FILE_PATH);
if (cachedFile.exists()){
ObjectInputStream objectInputStream = null;
try{
objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile));
user = objectInputStream.readObject(user);
} catch(IOException e){
e.printStackTrace();
}finally{
objectInputStream.close();
}
}
try{
objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
}catch (IOException e){
e,printStackTrace();
}finally{
objectOutputStream.close();
}
}
Messenger
Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,可以在不同进程中传递Messenger对象,在Messenger中放入我们需要传递的数据。它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。实现一个Messenger有如下几步,分为服务端和客户端:
-
服务端进程:在A进程创建一个Service来处理其他进程的连接请求,同时创建一个Handler并通过他来创建一个Messenger对象,然后在Service的onBind中返回这个Messneger对象底层的Binder即可
public class MessengerService extends Service { private static final String TAG = MessengerService.class.getSimpleName(); private class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case Constants.MSG_FROM_CLIENT: Log.d(TAG, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]"); Toast.makeText(MessengerService.this, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show(); Messenger client = msg.replyTo; Message replyMsg = Message.obtain(null, Constants.MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString(Constants.MSG_KEY, "我已经收到你的消息,稍后回复你!"); replyMsg.setData(bundle); try { client.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private Messenger mMessenger = new Messenger(new MessengerHandler()); @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } }
-
客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,消息类型是Message。如果需要服务端响应,则需要创建一个Handler并通过它来创建一个Messenger(和服务端一样),并通过Message的replyTo参数传递给服务端。服务端通过Message的replyTo参数就可以回应客户端了
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private Messenger mGetReplyMessenger = new Messenger(new MessageHandler()); private Messenger mService; //为了收到Service的回复,客户端需要创建一个接收消息的Messenger和Handler private class MessageHandler extends Handler { @Override public void handleMessage(Message msg) { //消息处理 switch (msg.what) { case Constants.MSG_FROM_SERVICE: Log.d(TAG, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]"); Toast.makeText(MainActivity.this, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void bindService(View v) { Intent mIntent = new Intent(this, MessengerService.class); bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } public void sendMessage(View v) { Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT); Bundle data = new Bundle(); data.putString(Constants.MSG_KEY, "Hello! This is client."); msg.setData(data); msg.replyTo = mGetReplyMessenger; try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onDestroy() { unbindService(mServiceConnection); super.onDestroy(); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //根据得到的IBinder对象创建Messenger mService = new Messenger(service); //通过得到的mService 可以进行通信 Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT); Bundle data = new Bundle(); data.putString(Constants.MSG_KEY, "Hello! This is client."); msg.setData(data); msg.replyTo = mGetReplyMessenger; try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; }
-
流程图
-
总结:Messenger内部消息处理使用Handler实现的,所以他是以串行的方式处理客户端发送过来的消息的,如果有大量的消息发送给服务端,服务端只能一个一个处理,如果并发量大的话用Messenger就不合适了,而且Messenger的主要作用是为了传递消息的,很多时候我们需要跨进程调用服务端的方法,这种需求Messenger就无法做到了
客户端和服务端是通过拿到对方的 Messenger 来发送 Message 的。只不过客户端通过 bindService onServiceConnected 而服务端通过 message.replyTo 来获得对方的 Messenger 。Messenger 中有一个 Hanlder 以串行的方式处理队列中的消息。不存在并发执行,因此我们不用考虑线程同步的问题
AIDL
AIDL (Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDL是IPC的一个轻量级实现,用了对于Java开发者来说很熟悉的语法。Android也提供了一个工具,可以自动创建Stub(类构架,类骨架)。当我们需要在应用间通信时,我们需要按以下几步走:定义一个AIDL接口--->为远程服务(Service)实现对应Stub--->将服务“暴露”给客户程序使用
-
特点:只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。而Messager是单线程处理
-
AIDL文件支持的数据类型
- 基本数据类型
- String 和 CharSequence
- ArrayList,里面的元素必须能够被AIDL支持
- HashMap,里面的元素必须能够被AIDL支持
- Parcelable,实现Parcelable接口的对象。如果 AIDL 文件中用到了自定义的 Parcelable 对象,必须新建一个和它同名的 AIDL 文件。
- AIDL。AIDL接口本身也可以在AIDL文件中使用
-
简单使用
-
服务端:服务端首先要创建一个Service用来监听客户端的请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可
-
创建AIDL接口文件:AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。重要的是必须导入所有非内置类型,哪怕是这些类型是在与接口相同的包中。
package com.example.android; //即使在同一包下也必须导包 import com.example.android.Person; interface IRemoteService { int getPid(); //in表示从客户端传输到服务端 void addPerson(in Person person); List<Person> getPersonList(); void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
package com.example.android; parcelable Person;
-
向客户端暴露接口
public class DDService extends Service { @Override public void onCreate() { super.onCreate(); System.out.println("DDService onCreate........" + "Thread: " + Thread.currentThread().getName()); } @Override public IBinder onBind(Intent arg0) { System.out.println("DDService onBind"); return mBinder; } private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ System.out.println("Thread: " + Thread.currentThread().getName()); System.out.println("DDService getPid "); return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { System.out.println("Thread: " + Thread.currentThread().getName()); System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString); } }; }
-
-
客户端:客户端所做的事情就要简单很多了,首先需要绑定服务端Service,绑定成功后将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了
-
客户端调用远程服务的方法,被调用的方法运行在服务端的 Binder 线程池中,同时客户端的线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,导致 ANR 。客户端的 onServiceConnected 和 onServiceDisconnected 方法都在 UI 线程中
public class MainActivity extends Activity { private IRemoteService remoteService; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { remoteService = IRemoteService.Stub.asInterface(service); try { int pid = remoteService.getPid(); int currentPid = Process.myPid(); System.out.println("currentPID: " + currentPid +" remotePID: " + pid); remoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "我们的爱,我明白"); } catch (RemoteException e) { e.printStackTrace(); } System.out.println("bind success! " + remoteService.toString()); } }; /** * 监听按钮点击 * @param view */ public void buttonClick(View view) { System.out.println("begin bindService"); Intent intent = new Intent("duanqing.test.aidl"); bindService(intent, conn, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } }
-
-
ContentProvider
ContentProvider(内容提供者)是Android中的四大组件之一,为了在应用程序之间进行数据交换,Android提供了ContentProvider,ContentProvider是不同应用之间进行数据交换的API,一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过接口来操作接口内的数据,包括增、删、改、查等。ContentProvider分为系统的和自定义的,系统的例如联系人,图片等数据
- 定义自己的ContentProvider类,该类继承ContentProvider基类
- 在AndroidManifest.xml中注册这个ContentProvider,类似于Activity注册,注册时要给ContentProvider绑定一个域名
- 注册好ContentProvider后,其他应用就可以访问ContentProvider暴露出来的数据了
- ContentProvider只是暴露出来可供其他应用操作的数据,其他应用则需要通过ContentReslover来操作ContentProvider所暴露出来的数据。Context提供了getContentResolver()方法来获取ContentProvider对象,获取之后皆可以对暴露出来的数据进行增、删、改、查操作
Socaket
- Socket用于描述IP地址和端口,是一个通信链的句柄,用来实现不同虚拟机或物理机之间的通信。应用程序通过Socket向网络发出请求或应答请求。网络中两个进程通过一个双向的通信连接实现数据的交换,建立网络通信连接至少需要一对Socket,连接的一端称为一个Socket。
- 具有唯一标识的网络进程可利用 Socket 进行通信,而 Socket 是在应用层和传输层之间的一个抽象层,Socket把TCP/IP层复杂的操作抽象为简单的接口供应用层调用,以实现进程在网络中的通信。TCP/IP协议存在于操作系统中,网络服务是通过操作系统提供的,因此在操作系统中增加支持TCP/IP的系统调用Socket。
- Socket 作为应用层与 TCP/IP 协议簇通信的中间软件抽象层,是一组接口。在设计模式中 Socket 其实就是一个门面模式,它将复杂的 TCP/IP 协议簇隐藏在 Socket 接口后面。对用户而言一组简单地接口就是全部,让 Socket 去组织数据已符合指定的协议。
线程间通信方式
Handler机制
private MyHandler mHandler = new MyHandler(this);
public void byHandler(View v) {
new Thread() {
@Override
public void run() {
super.run();
Message msg = Message.obtain();
msg.obj = "通过Handle机制修改";
mHandler.sendMessage(msg);
}
}.run();
}
private static class MyAsyncTask extends AsyncTask {
private WeakReference<Context> reference;
public MyAsyncTask (Context context) {
reference = new WeakReference<>(context);
}
@Override
protected Object doInBackground(Object[] objects) {
return objects[0].toString();
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
MainActivity activity = (MainActivity) reference.get();
if (activity != null) {
activity.tv.setText(o.toString());
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//移除队列中所有的Runable和消息
//这里也可以使用mHandler.removeMessage和mHandler.removeCallBacks来移除指定的Message和Runable
mHandler.removeCallbacksAndMessages(null);
}
runOnUiThread
public void byRunOnUIThread(View v) {
new Thread() {
@Override
public void run() {
super.run();
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("通过runOnUiThread方法");
}
});
}
}.run();
}
View.post(Runnable r)
public void byViewPost(View v) {
new Thread() {
@Override
public void run() {
super.run();
tv.post(new Runnable() {
@Override
public void run() {
tv.setText("通过View.post方法");
}
});
}
}.run();
}
AsyncTask
private MyAsyncTask mAsyncTask = new MyAsyncTask(this);
public void byAsyncTask(View v) {
mAsyncTask.execute("通过AsyncTask方法");
}
private static class MyAsyncTask extends AsyncTask {
private WeakReference<Context> reference;
public MyAsyncTask (Context context) {
reference = new WeakReference<>(context);
}
@Override
protected Object doInBackground(Object[] objects) {
return objects[0].toString();
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
MainActivity activity = (MainActivity) reference.get();
if (activity != null) {
activity.tv.setText(o.toString());
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mAsyncTask.cancel(true);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧