我正在编写我的第一个Android应用程序,并试图了解服务和活动之间的沟通。我有一个将在后台运行的服务,并执行一些基于gps和时间的日志记录。我将有一个用于启动和停止服务的活动。
首先,我需要能够确定在Activity启动时服务是否正在运行。这里还有其他一些问题,所以我想我可以解决这个问题(但随意提供建议)。
我的真正问题:如果Activity正在运行且服务已启动,我需要一种方法让服务向Activity发送消息。此时简单的字符串和整数 - 主要是状态消息。消息不会定期发生,因此如果有其他方法,我认为轮询服务不是一个好方法。我只想在用户启动Activity时进行此通信 - 我不想从服务启动Activity。换句话说,如果您启动Activity并且服务正在运行,那么当有趣的事情发生时,您将在Activity UI中看到一些状态消息。如果你没有启动活动,你将看不到这些消息(它们并不那么有趣)。
似乎我应该能够确定服务是否正在运行,如果是,则将Activity添加为侦听器。然后在“活动”暂停或停止时将“活动”作为侦听器删除。这有可能吗?我能想到的唯一方法是让Activity实现Parcelable并构建一个AIDL文件,这样我就可以通过Service的远程接口传递它。这看起来有点矫枉过正,我不知道Activity应该如何实现writeToParcel()/ readFromParcel()。
有更简单或更好的方法吗?谢谢你的帮助。
编辑:
对于后来对此感兴趣的任何人,都有来自Google的示例代码,用于通过示例目录中的AIDL处理此问题:/apis/app/RemoteService.java
答案 0 :(得分:244)
提问者可能已经过了很长时间,但是如果其他人搜索了这个......
还有另一种处理方法,我认为这可能是最简单的方法。
为您的活动添加BroadcastReceiver
。注册它以在onResume
中接收一些自定义意图,并在onPause
中取消注册。然后,当您想要发送状态更新或有什么内容时,请从您的服务中发出该意图。
如果其他应用听了你的Intent
(任何人都可以做恶意吗?),请确保你不会不高兴。但除此之外,你应该没事。
请求代码示例:
在我的服务中,我有这个:
// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(RefreshTask.REFRESH_DATA_INTENT
只是一个常量字符串。)
在我的听力活动中,我定义了我的BroadcastReceiver
:
private class DataUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
// Do stuff - maybe update my view based on the changed DB contents
}
}
}
我宣布我的接收器位于班级的顶端:
private DataUpdateReceiver dataUpdateReceiver;
我重写onResume
以添加此内容:
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
我重写onPause
以添加:
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
现在我的活动正在倾听我的服务,说“嘿,自己动手更新”。我可以在Intent
中传递数据而不是更新数据库表,然后返回查找我的活动中的更改,但由于我希望更改仍然存在,因此通过数据库传递数据是有意义的。
答案 1 :(得分:83)
与服务沟通有三种明显的方式:
在你的情况下,我会选择选项3.对自己的服务进行静态引用并在onCreate()中填充它:
void onCreate(Intent i) {
sInstance = this;
}
创建一个静态函数MyService getInstance()
,它返回静态sInstance
。
然后在Activity.onCreate()
中启动服务,异步等待服务实际启动(您可以让服务通过向活动发送意图来通知您的应用程序已准备就绪。)并获取其实例。拥有实例时,将服务侦听器对象注册到您的服务并进行设置。注意:在Activity中编辑Views时你应该在UI线程中修改它们,服务可能会运行自己的Thread,所以你需要调用Activity.runOnUiThread()
。
您需要做的最后一件事是删除Activity.onPause()
中对您的侦听器对象的引用,否则您的活动上下文的实例将泄漏,而不是好。
注意:此方法仅在您的应用程序/活动/任务是访问您的服务的唯一过程时才有用。如果不是这种情况,则必须使用选项1.或2.
答案 2 :(得分:37)
使用LocalBroadcastManager
注册接收方以收听应用内部本地服务发送的广播,请参阅此处:
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
答案 3 :(得分:18)
使用Messenger是另一种在服务和活动之间进行通信的简单方法。
在“活动”中,使用相应的Messenger创建一个处理程序。这将处理来自您服务的消息。
class ResponseHandler extends Handler {
@Override public void handleMessage(Message message) {
Toast.makeText(this, "message from service",
Toast.LENGTH_SHORT).show();
}
}
Messenger messenger = new Messenger(new ResponseHandler());
可以将Messenger附加到消息传递给服务:
Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
myService.send(message);
catch (RemoteException e) {
e.printStackTrace();
}
可以在API演示中找到完整的示例:MessengerService and MessengerServiceActivity。有关MyService的工作原理,请参阅完整示例。
答案 4 :(得分:18)
答案 5 :(得分:8)
其他注释中未提及的另一种方法是使用bindService()从活动绑定到服务,并在ServiceConnection回调中获取服务的实例。如此处所述http://developer.android.com/guide/components/bound-services.html
答案 6 :(得分:3)
您也可以使用LiveData
的{{1}}。
EventBus
然后从您的class MyService : LifecycleService() {
companion object {
var BUS = MutableLiveData<Object>()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val testItem : Object
// expose your data
if (BUS.hasActiveObservers()) {
BUS.postValue(testItem)
}
return START_NOT_STICKY
}
}
中添加一个观察者。
Activity
您可以从此blog.
中了解更多信息答案 7 :(得分:2)
另一种方法是通过活动和服务本身使用具有假模型类的观察者,实现MVC模式变体。我不知道这是否是实现这一目标的最佳方式,但这对我有用。如果你需要一些例子请求它,我会发布一些东西。
答案 8 :(得分:2)
使用代码示例跟进@MrSnowflake答案。
This is the XABBER now open source Application
class。 Application
类正在集中和协调Listeners
和ManagerInterfaces等。各种经理都是动态加载的。在Xabber中开始的Activity´s
将报告它们是什么类型的Listener
。当Service
启动时,它会在Application
类中开始报告。现在要向Activity
发送消息,您只需要使Activity
成为所需类型的listener
即可。在OnStart()
OnPause()
注册/ unreg中。 Service
可以向Application
课程询问它需要发言的listener
,如果它在那里,那么活动就可以接收了。
通过Application
课程,你会发现还有更多的战利品。
答案 9 :(得分:0)
如Madhur所述,您可以使用公交车进行通信。
如果使用总线,您有一些选择:
Otto事件总线库(不赞成使用RxJava)
绿色机器人的EventBus
http://greenrobot.org/eventbus/
NYBus(RxBus,使用RxJava实现。与EventBus非常相似)
答案 10 :(得分:0)
除了 LocalBroadcastManager,Event Bus和Messenger 已在此问题中回答,我们可以使用待定意图与服务进行通信。
正如我在博文中提到的here
服务和活动之间的通信可以使用 PendingIntent.For我们可以使用 createPendingResult ()。createPendingResult()创建一个新的 PendingIntent对象,您可以将其交给服务使用和发送 结果数据返回到onActivityResult内的活动(int,int, Intent)callback.Since PendingIntent是Parcelable,并且可以 因此,加入一个额外的Intent,你的活动可以通过这个 PendingIntent to service。服务反过来可以调用send() PendingIntent上的方法通过通知活动 onActivityResult事件。
活动
public class PendingIntentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PendingIntent pendingResult = createPendingResult( 100, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), PendingIntentService.class); intent.putExtra("pendingIntent", pendingResult); startService(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100 && resultCode==200) { Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show(); } super.onActivityResult(requestCode, resultCode, data); } }
服务
public class PendingIntentService extends Service { private static final String[] items= { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; private PendingIntent data; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getParcelableExtra("pendingIntent"); new LoadWordsThread().start(); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); } class LoadWordsThread extends Thread { @Override public void run() { for (String item : items) { if (!isInterrupted()) { Intent result = new Intent(); result.putExtra("name", item); try { data.send(PendingIntentService.this,200,result); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } SystemClock.sleep(400); } } } } }
答案 11 :(得分:0)
我的方法:
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 java.util.ArrayList;
import java.util.List;
public class MessageManager {
public interface IOnHandleMessage{
// Messages
int MSG_HANDSHAKE = 0x1;
void onHandleMessage(Message msg);
}
private static final String LOGCAT = MessageManager.class.getSimpleName();
private Messenger mMsgSender;
private Messenger mMsgReceiver;
private List<Message> mMessages;
public MessageManager(IOnHandleMessage callback, IBinder target){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
mMsgSender = new Messenger(target);
mMessages = new ArrayList<>();
}
public MessageManager(IOnHandleMessage callback){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
mMsgSender = null;
mMessages = new ArrayList<>();
}
/* START Getter & Setter Methods */
public Messenger getMsgSender() {
return mMsgSender;
}
public void setMsgSender(Messenger sender) {
this.mMsgSender = sender;
}
public Messenger getMsgReceiver() {
return mMsgReceiver;
}
public void setMsgReceiver(Messenger receiver) {
this.mMsgReceiver = receiver;
}
public List<Message> getLastMessages() {
return mMessages;
}
public void addMessage(Message message) {
this.mMessages.add(message);
}
/* END Getter & Setter Methods */
/* START Public Methods */
public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
if(mMsgSender != null && mMsgReceiver != null) {
try {
Message msg = Message.obtain(null, what, arg1, arg2);
msg.replyTo = mMsgReceiver;
if(msgData != null){
msg.setData(msgData);
}
mMsgSender.send(msg);
} catch (RemoteException rE) {
onException(rE);
}
}
}
public void sendHandshake(){
if(mMsgSender != null && mMsgReceiver != null){
sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
}
}
/* END Public Methods */
/* START Private Methods */
private void onException(Exception e){
Log.e(LOGCAT, e.getMessage());
e.printStackTrace();
}
/* END Private Methods */
/** START Private Classes **/
private class MessageHandler extends Handler {
// Types
final static int TYPE_SERVICE = 0x1;
final static int TYPE_ACTIVITY = 0x2;
private IOnHandleMessage mCallback;
private int mType;
public MessageHandler(IOnHandleMessage callback, int type){
mCallback = callback;
mType = type;
}
@Override
public void handleMessage(Message msg){
addMessage(msg);
switch(msg.what){
case IOnHandleMessage.MSG_HANDSHAKE:
switch(mType){
case TYPE_SERVICE:
setMsgSender(msg.replyTo);
sendHandshake();
break;
case TYPE_ACTIVITY:
Log.v(LOGCAT, "HERE");
break;
}
break;
default:
if(mCallback != null){
mCallback.onHandleMessage(msg);
}
break;
}
}
}
/** END Private Classes **/
}
public class activity extends AppCompatActivity
implements ServiceConnection,
MessageManager.IOnHandleMessage {
[....]
private MessageManager mMessenger;
private void initMyMessenger(IBinder iBinder){
mMessenger = new MessageManager(this, iBinder);
mMessenger.sendHandshake();
}
private void bindToService(){
Intent intent = new Intent(this, TagScanService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
/* START THE SERVICE IF NEEDED */
}
private void unbindToService(){
/* UNBIND when you want (onDestroy, after operation...)
if(mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
/* START Override MessageManager.IOnHandleMessage Methods */
@Override
public void onHandleMessage(Message msg) {
switch(msg.what){
case Constants.MSG_SYNC_PROGRESS:
Bundle data = msg.getData();
String text = data.getString(Constants.KEY_MSG_TEXT);
setMessageProgress(text);
break;
case Constants.MSG_START_SYNC:
onStartSync();
break;
case Constants.MSG_END_SYNC:
onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
mBound = false;
break;
}
}
/* END Override MessageManager.IOnHandleMessage Methods */
/** START Override ServiceConnection Methods **/
private class BLEScanServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
initMyMessenger(iBinder);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mMessenger = null;
mBound = false;
}
}
/** END Override ServiceConnection Methods **/
public class Blablabla extends Service
implements MessageManager.IOnHandleMessage {
[...]
private MessageManager mMessenger;
@Nullable
@Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
initMessageManager();
return mMessenger.getMsgReceiver().getBinder();
}
private void initMessageManager(){
mMessenger = new MessageManager(this);
}
/* START Override IOnHandleMessage Methods */
@Override
public void onHandleMessage(Message msg) {
/* Do what you want when u get a message looking the "what" attribute */
}
/* END Override IOnHandleMessage Methods */
mMessenger.sendMessage(what, arg1, arg2, dataBundle);
在活动上启动或绑定服务。 服务“ OnBind”方法将Binder返回到他的MessageManager,在“活动”中通过“服务连接”接口方法实现“ OnServiceConnected”,您将获得该IBinder并使用它来初始化MessageManager。 活动初始化其MessageManager之后,MessageHandler将向该服务发送消息并进行握手,以便它可以设置其“ MessageHandler”发送者(在MessageManager中为“私有Messenger mMsgSender;”)。这样做的服务知道谁送他的消息。
您还可以在MessageManager中使用Messenger“发件人”的列表/队列来实现此目的,以便可以将多个消息发送到不同的活动/服务,也可以在MessageManager中使用Messenger“收件人”的列表/队列来实现,可以从不同的活动接收多个消息/服务。
在“给messageManager”实例你有接收的所有消息的列表。
正如你可以看到使用这种“给messageManager”实例“活动的信使”和“服务信使”之间的连接被自动的,它是通过“OnServiceConnected”方法和完成通过使用
希望这是对你有帮助:)非常感谢你! 再见:d
答案 12 :(得分:0)
创建回调
public interface MyCallBack{
public void getResult(String result);
}
活动方面:
在与服务绑定和解除绑定时注册和注销回调 活动。
public class YourActivity extends AppCompatActivity implements MyCallBack{
private Intent myIntent;
private GPSService gpsService;
private boolean bound = false;
@Override
public void getResult(String result){
// show in textView textView.setText(result);
}
@Override
protected void onStart()
{
super.onStart();
bindService();
}
@Override
protected void onStop() {
super.onStop();
unbindService();
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
gpsService= binder.getService();
bound = true;
gpsService.registerCallBack(YourActivity.this); // register
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
private void bindService() {
bindService(notifyMeIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void unbindService(){
if (bound) {
gpsService.registerCallBack(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
// Call this method somewhere to start Your GPSService
private void startService(){
myIntent = new Intent(this, GPSService.class);
startService(myIntent );
}
}
服务端:
在需要时调用回调方法
public class GPSService extends Service{
private MyCallBack myCallback;
private IBinder serviceBinder = new GPSBinder();
public void registerCallBack(MyCallBack myCallback){
this.myCallback= myCallback;
}
public class GPSBinder extends Binder{
public GPSService getService(){
return GPSService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent){
return serviceBinder;
}
}