Android Activity与Service通信

本文介绍了Android中Activity和Service之间通信的四种方法:Binder类、内部类广播、Messenger和AIDL。通过这些方法,Activity能直接调用Service中的方法,Service也能向Activity发送消息。对于跨应用和多线程通信,AIDL提供了更强大的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Android中,Activity和Service分别管理着应用的前台和后台,他们都是相当重要的四大组件之一,那么Activity和Service之间是如何通信的呢?以下总结了四种可行的方法:

方法一:Binder类

该方法要求我们构造一个Binder类来封装Service实例,并在Service类中重写onBind方法返回该binder对象:
package com.example.test;

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

public class MyService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		return new MyBinder();
	}

	class MyBinder extends Binder {
		/**
		 * 获取service实例
		 * @return 
		 */
		public MyService getService() {
			return MyService.this;
		}
	}
	
}
在Activity中我们则需要使用bindService方法绑定Service,并传入一个ServiceConnection参数,通过其onServiceConnected方法来获取binder实例,进而获取service实例:
package com.example.test;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;


public class MyActivity extends Activity{

	// 服务实例
	private MyService myService = null;
	private ServiceConnection connection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// 获取服务实例
			myService = ((MyService.MyBinder)service).getService();
		}
	};
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, connection, BIND_AUTO_CREATE);
    }

}

方法二:通过内部类广播

我们可以分别在Service和Activity中实现两个广播内部类,当两个组件需要互相通信的时候只要发送适当的广播即可:
package com.example.test;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class MyService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	
	class MyServiceBroadcastReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			// do something	
		}
	}
	
}
package com.example.test;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;


public class MyActivity extends Activity{
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
    
    class MyActivityBroadcastReceiver extends BroadcastReceiver {
    	@Override
    	public void onReceive(Context context, Intent intent) {
    		// do something
    	}
    }

}

上面两种方法适用于同一个进程中的Activity和Service组件,那么对于不在同一个进程中的Activity和Service我们该如何通信呢?

方法三:Messenger

Messenger类的官方注释如下:
/**
 * Reference to a Handler, which others can use to send messages to it.
 * This allows for the implementation of message-based communication across
 * processes, by creating a Messenger pointing to a Handler in one process,
 * and handing that Messenger to another process.
 */
从注释我们可以得知,这是一个基于Message的,使用Handler来处理消息的,进程间组件通信的工具类。
Messenger的使用方法如下:
Service类:
package com.example.test;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class MyRemoteService extends Service {

	private final String TAG = "MyService"; 
	
	@Override
	public IBinder onBind(Intent intent) {
		// 返回Messenger的Binder对象
		return messenger.getBinder();
	}

	@Override
	public void onCreate() {
		super.onCreate();
		Log.v(TAG, "onCreate");
	}
	
	// 传入一个Handler参数用来处理消息,Handler默认构造方法绑定主线程
	private Messenger messenger = new Messenger(new Handler() {
		public void handleMessage(android.os.Message msg) {
			// 收到消息打印Log
			Log.v(TAG, "Get Message from remote activity : " + ((Bundle)msg.obj).getString("String"));
			// 回复服务端消息
			Message msgReply = Message.obtain();
<span style="white-space:pre">			</span>// 使用Bundle类包装信息
			Bundle bundle = new Bundle();
			bundle.putString("String", "From Remote Service");
			msgReply.obj = bundle;
			try {
				msg.replyTo.send(msgReply);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		};
	});

}
在Service类中我们构造了一个Messenger对象,并在onBind方法里调用了Messenger的getBinder方法,返回了一个IBinder对象。我们在构造Messenger对象时传入了一个Handler参数,该Handler用于处理从服务端接收到的Message。此处我们把接收到的消息通过Log打印出来,并获取Message中的replyTo属性发送一条消息作为回复。
<service android:name="com.example.test.MyRemoteService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.test.messenger"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
以上是Service注册表的信息,记得设置好Action和exported属性以便外部应用访问,此处把enabled属性设置为true允许其让系统启动。
接下来我们来看看另一个应用中Activity的代码:
package com.example.test2;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;


public class MyActivity extends Activity {

	private final String TAG = "MyActivity"; 
	
	// 用于发送消息
	private Messenger messengerSender = null;
	// 用于接收消息
	private Messenger messengerReceiver = new Messenger(new Handler() {
		public void handleMessage(android.os.Message msg) {
			// 收到消息打印Log
			Log.v(TAG, "Get Message from remote Service : " + ((Bundle)msg.obj).getString("String"));
		};
	});
	private ServiceConnection connection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {	
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// 获取来自Service的Binder对象,构造MessengerSender用于发送消息
			messengerSender = new Messenger(service);
			// 发送消息
			Message message = new Message();
			// 设置消息返回的Messenger
			message.replyTo = messengerReceiver;
			// 设置消息内容
			Bundle bundle = new Bundle();
			bundle.putString("String", "From Activity");
			message.obj = bundle;
			try {
				messengerSender.send(message);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	};
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 绑定远程Service
        Intent intent = new Intent();
        intent.setAction("com.example.test.messenger");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

}

在Activity中我们构造了两个Messenger,一个传入Handler参数构造用于处理接收到的Message,一个传入在ServiceConnection中获取的IBinder对象用于发送Message。在Activity中我们通过指定Action,使用bindService方法完成Activity与Service的绑定。此处我们在与Service建立连接后发送了一条消息给Service,并把Service返回的消息给打印出来。
Service LogcatActivity Logcat
从Logcat信息中我们可以看出Activity和Service通信成功了,并且是在两个应用,各自的进程当中(PID不同)。
值的注意的是,通信过程中Message的obj对象必须是实现了Parcelable接口的,否则系统会报RuntimeException:Can't marshal non-Parcelable objects across processes.一般而言我们只需要使用Bundle类包装要传输的数据即可。

方法四:AIDL

AIDL:Android Interface Definition Language,即Android接口定义语言。要是使用Messenger还不能满足你的需求,我们就必须使出AIDL这项工具来实现进程间通信了。那么AIDL适用于什么场景呢?我们来看看官方的解释:
Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
上面最重要的就是第一句话:只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL。在其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。
那么接下来我们就来看看如何使用AIDL吧:
首先我们在src下面编写.aidl文件定义接口,其语法与java类似:
package com.example.aidl;

interface IRemoteService {
	String sendMessage(String s);
}
在定义了该aidl文件后,保存后Android编译器会在gen目录下自动生成对应的文件:
aidl
注意,在Activity和Service所在的应用中都必须有完全相同的aidl文件,包名称也必须相同,否则会报java.lang.SecurityException: Binder invocation to an incorrect interface的错误。
接下来我们就可以利用aidl定义的接口编写Service和Activity了:
package com.example.test;

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

import com.example.aidl.IRemoteService;

public class MyRemoteService extends Service {

	private final String TAG = "MyService"; 
	
	@Override
	public IBinder onBind(Intent intent) {
		// 返回IRemoteService.Stub对象
		return binder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		Log.v(TAG, "onCreate");
	}
	
	private final IRemoteService.Stub binder = new IRemoteService.Stub() {
		
		@Override
		public String sendMessage(String s) throws RemoteException {
			// 通过Log打印传入的字符串
			Log.v(TAG, "String from Activity : "+s);
			return "Service Message!";
		}
	};

}
在Service中我们实现了IRemoteService内部的Stub类,并把它当做返回值在onBind方法返回给建立连接的组件。
Service的xml代码与Messenger相比没有什么改动,故不再贴出来。
接下来是Activity的代码:
package com.example.test2;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.example.aidl.IRemoteService;

public class MyActivity extends Activity {

	private final String TAG = "MyActivity";
	private IRemoteService mRemoteService;

	private ServiceConnection connection = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mRemoteService = IRemoteService.Stub.asInterface(service);
			try {
				Log.v(TAG, mRemoteService.sendMessage("Activity Message!"));
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
<span style="white-space:pre">		Log.v(TAG, "onCreate");</span>

		// 绑定远程Service
		Intent intent = new Intent();
		intent.setAction("com.example.test.aidl");
		bindService(intent, connection, Context.BIND_AUTO_CREATE);
	}

}
我们利用Stub的asInterface方法构造了用于通信的IRemoteService接口,通过接口定义的sendMessage方法向另一个进程中的Service发送了一条消息,并把Service返回的信息打印了出来。
通过Logcat我们可以发现,在两个进程中的Activity和Service通信成功了(PID不同):
service
activity
值的注意的是Activity是在主线程中与Service通信,而Service却并没有在主线程通信,而是新开启了一个线程和Activity通信(TID不同)。这也印证了官方对于AIDL工具的定位,当我们需要在不同进程中实现组件通信,并且需要处理多线程问题时,才使用AIDL,不然我们还是用回简单方便的Messenger吧。

总结:

  1. Activity调用bindService方法,通过IBinder对象得到Service的实例,这样Activity可以直接调用到Service中的方法,如果需要Service主动通知Activity,我们还可以为其添加回调方法。
  2. Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。
  3. 当Activity和Service分别处在不同的应用之中时,我们可以通过使用Messenger类来完成两组件之间的基于Message机制的交流通信,使用Handler来处理发送接收的消息。
  4. 当不仅Activity和Service所处应用不同,而且我们还需要处理多线程通信问题时,我们就需要使用AIDL来进行通信了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值