Как установить связь с Android-службой
Я пишу свое первое приложение для Android и пытаюсь найти связь между службами и действиями. У меня есть Служба, которая будет работать в фоновом режиме и делать некоторые gps и протоколы, основанные на времени. У меня будет Activity, который будет использоваться для запуска и остановки Сервиса.
Итак, во-первых, мне нужно выяснить, работает ли Служба при запуске Activity. Здесь есть некоторые другие вопросы, поэтому я думаю, что могу понять это (но не стесняйтесь предлагать советы).
Моя реальная проблема: если действие запущено и служба запущена, мне нужно, чтобы служба отправила сообщения в Activity. Простые строки и целые числа в данный момент - сообщения о статусе в основном. Сообщения не будут происходить регулярно, поэтому я не думаю, что опрос службы - это хороший способ пойти, если есть другой способ. Мне нужна эта связь только в том случае, если действие было запущено пользователем - я не хочу запускать Activity из службы. Другими словами, если вы запустите Activity и службу, вы увидите некоторые сообщения о статусе в пользовательском интерфейсе активности, когда произойдет что-то интересное. Если вы не запустите Activity, вы не увидите эти сообщения (они не так интересны).
Похоже, я должен быть в состоянии определить, работает ли Служба, и если да, добавьте Activity в качестве слушателя. Затем удалите Activity как прослушиватель, когда действие приостанавливается или останавливается. Это действительно возможно? Единственный способ, с помощью которого я могу это сделать, - это реализовать Activity Parcelable и создать файл AIDL, чтобы я мог передать его через удаленный интерфейс службы. Тем не менее, это похоже на избыток, и я не знаю, как должно быть реализовано действие writeToParcel()/readFromParcel().
Есть ли более простой или лучший способ? Спасибо за любую помощь.
EDIT:
Для тех, кто интересуется этим позже, есть образец кода из Google для обработки этого через AIDL в каталоге samples:/apis/app/RemoteService.java
Ответы
Ответ 1
Существует три очевидных способа общения с службами:
- Использование намерений
- Использование AIDL
- Использование самого объекта службы (как singleton)
В вашем случае, я бы пошел с опцией 3. Сделать статическую ссылку на службу самостоятельно и заполнить ее в onCreate():
void onCreate(Intent i) {
sInstance = this;
}
Сделайте статическую функцию MyService getInstance()
, которая возвращает статический sInstance
.
Затем в Activity.onCreate()
вы запустите службу, асинхронно дождитесь, пока служба фактически запустится (вы можете сообщить своему сервису о готовности своего приложения, отправив намерение на это мероприятие.) и получить его экземпляр. Когда у вас есть экземпляр, зарегистрируйте свой объект прослушивателя службы в своем сервисе, и вы настроены. ПРИМЕЧАНИЕ. При редактировании представлений внутри Activity вы должны изменить их в потоке пользовательского интерфейса, служба, вероятно, будет запускать собственный поток, поэтому вам нужно вызвать Activity.runOnUiThread()
.
Последнее, что вам нужно сделать, это удалить ссылку на ваш объект-слушатель в Activity.onPause()
, иначе экземпляр вашего контекста активности будет протекать, а не хороший.
ПРИМЕЧАНИЕ. Этот метод полезен только тогда, когда ваше приложение/действие/задача является единственным процессом, который будет обращаться к вашему сервису. Если это не так, вам нужно использовать опцию 1. или 2.
Ответ 2
Аскер, вероятно, давно прошел мимо этого, но в случае, если кто-то еще ищет это...
Есть еще один способ справиться с этим, который, я думаю, может быть самым простым.
Добавьте BroadcastReceiver
к вашей деятельности. Зарегистрируйте его, чтобы получить пользовательское намерение в onResume
и 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
вместо обновления таблиц базы данных и затем возвращаться, чтобы найти изменения в моей деятельности, но, поскольку я хочу, чтобы изменения сохранялись в любом случае, имеет смысл передавать данные через DB.
Ответ 3
Используйте LocalBroadcastManager
чтобы зарегистрировать получателя для прослушивания трансляции, отправленной с локального сервиса внутри вашего приложения, ссылка здесь:
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Ответ 4
Использование Messenger - еще один простой способ связи между Сервисом и Activity.
В Управлении создайте обработчик с соответствующим 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 и MessengerServiceActivity. См. Полный пример того, как работает MyService.
Ответ 5
Я удивлен, что никто не дал ссылку на библиотеку Bus событий Отто
http://square.github.io/otto/
Я использую это в своих приложениях для Android, и он работает без проблем.
Ответ 6
Другим способом, который не упоминается в других комментариях, является привязка к службе из операции с помощью bindService() и получение экземпляра службы в обратном вызове ServiceConnection. Как описано здесь http://developer.android.com/guide/components/bound-services.html
Ответ 7
Вы также можете использовать LiveData
который работает как 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
.
MyService.BUS.observe(this, Observer {
it?.let {
// Do what you need to do here
}
})
Вы можете прочитать больше из этого блога.
Ответ 8
Другим способом может быть использование наблюдателей с поддельным классом модели через активность и саму службу, реализующую вариацию шаблона MVC. Я не знаю, лучший ли это для этого, но это способ, который сработал у меня. Если вам нужен пример, попросите его, и я отправлю что-нибудь.
Ответ 9
Чтобы ответить на вопрос @MrSnowflake, приведите пример кода.
Теперь это класс XABBER с открытым исходным кодом Application
. Класс Application
централизует и координирует Listeners
и ManagerInterfaces и многое другое. Менеджеры всех видов загружаются динамически. Activity´s
, запущенный в Xabber, сообщит, к какому типу Listener
они относятся. И когда Service
запускается, он сообщает классу Application
как начатый. Теперь, чтобы отправить сообщение Activity
, все, что вам нужно сделать, - это сделать Activity
того, кто вам нужен. В OnStart()
OnPause()
зарегистрироваться/отменить. Service
может запросить у класса Application
только то, с чем ему нужно поговорить listener
, и если он там, то действие готово к приему.
Пройдя класс Application
, вы увидите, что здесь происходит гораздо больше.
Ответ 10
Мой метод:
Класс для управления отправкой и получением сообщения от/к службе/деятельности:
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 */
Отправить сообщение от Activity/Service:
mMessenger.sendMessage(what, arg1, arg2, dataBundle);
Как это работает:
на активности вы запускаете или привязываете сервис. Методы "OnBind" службы возвращают Binder его MessageManager. В Activity через реализацию методов интерфейса "Service Connection" "OnServiceConnected" вы получаете этот IBinder и инициализируете его с помощью MessageManager. После того, как Activity инициирует свой MessageManager, MessageHandler отправляет и Handshake в службу, чтобы он мог установить своего отправителя "MessageHandler" ("private Messenger mMsgSender;" в MessageManager). При этом сервис узнает, кто отправляет его сообщения.
Вы также можете реализовать это с помощью "отправителя" Списка/Очереди Messenger в MessageManager, чтобы вы могли отправлять несколько сообщений в различные Операции/Службы, или вы можете использовать "получатель" Списка/Очереди Messenger в MessageManager, чтобы вы могли получить несколько сообщение из разных видов деятельности/услуг.
В экземпляре "MessageManager" у вас есть список всех полученных сообщений.
Как вы можете видеть, соединение между "Activity Messenger" и "Service Messenger" с использованием этого экземпляра "MessageManager" является автоматическим, оно осуществляется с помощью метода "OnServiceConnected" и с помощью "Рукопожатия".
Надеюсь, что это полезно для вас :) Большое спасибо! Пока: D
Ответ 11
Связывание - это еще один способ общения
Создать обратный звонок
public interface MyCallBack{
public void getResult(String result);
}
Activity side:
- Реализуйте интерфейс в Деятельности
- Предоставить реализацию для метода
- Привязать активность к сервису
Зарегистрируйтесь и отмените обратный вызов, когда Служба будет связана и не связана с
Активность.
public class YourActivity extends AppCompatActivity implements MyCallBack{
private Intent notifyMeIntent;
private GPSService gpsService;
private boolean bound = false;
@Override
public void onCreate(Bundle sis){
// activity code ...
startGPSService();
}
@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 startGPSService(){
notifyMeIntent = new Intent(this, GPSService.class);
startService(myIntent );
}
}
Service Side:
- Инициализировать обратный вызов
При необходимости вызывайте метод обратного вызова
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;
}
}
Ответ 12
Как упомянул Мадхур, вы можете использовать автобус для общения.
В случае использования шины у вас есть несколько вариантов:
Библиотека шины событий Otto (устарела в пользу RxJava)
http://square.github.io/otto/
Зеленые роботы EventBus
http://greenrobot.org/eventbus/
NYBus (RxBus, реализованный с использованием RxJava. Очень похож на EventBus)
https://github.com/MindorksOpenSource/NYBus
Ответ 13
Помимо LocalBroadcastManager, Event Bus и Messenger уже ответили на этот вопрос, мы можем использовать Pending Intent для связи с сервисом.
Как уже упоминалось здесь, в моем блоге
Связь между службой и Activity может быть осуществлена с помощью PendingIntent. Для этого мы можем использовать createPendingResult().createPendingResult() создает новый объект PendingIntent, который вы можете передать службе и использовать для отправки данных результатов обратно в вашу деятельность внутри onActivityResult (int, int, Intent) callback. Поскольку PendingIntent имеет значение Parcelable, и поэтому его можно поместить в дополнительный Intent, ваша деятельность может передать этот PendingIntent службе. Служба, в свою очередь, может вызвать метод 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);
}
}
}
}
}