문제

나는 첫 번째 Android 애플리케이션을 작성하고 서비스와 활동 사이의 의사 소통을 중심으로 노력하고 있습니다. 백그라운드에서 실행되고 GPS 및 시간 기반 로깅을 수행하는 서비스가 있습니다. 서비스를 시작하고 중지하는 데 사용될 활동이 있습니다.

먼저, 활동이 시작될 때 서비스가 실행되는지 알아낼 수 있어야합니다. 여기에 다른 질문이 있으므로 알아낼 수 있다고 생각합니다 (그러나 자유롭게 조언을 제공하십시오).

내 실제 문제 : 활동이 실행 중이고 서비스가 시작되면 서비스가 활동에 메시지를 보낼 수있는 방법이 필요합니다. 이 시점에서 간단한 문자열과 정수 - 대부분 상태 메시지. 메시지는 정기적으로 발생하지 않으므로 다른 방법이 있다면 서비스를 폴링하는 것이 좋은 방법이라고 생각하지 않습니다. 사용자가 활동을 시작했을 때만이 커뮤니케이션을 원합니다. 서비스에서 활동을 시작하고 싶지 않습니다. 다시 말해, 활동을 시작하고 서비스가 실행중인 경우 흥미로운 일이 발생하면 활동 UI에 일부 상태 메시지가 표시됩니다. 활동을 시작하지 않으면 이러한 메시지를 보지 못할 것입니다 (그다지 흥미롭지 않음).

서비스가 실행 중인지 판단 할 수 있어야하는 것 같습니다. 그렇다면 리스너로 활동을 추가하십시오. 그런 다음 활동이 일시 중지되거나 중지 될 때 리스너로 활동을 제거하십시오. 실제로 가능합니까? 내가 알아낼 수있는 유일한 방법은 활동이 구현 가능하고 AIDL 파일을 구축하여 서비스의 원격 인터페이스를 통해 전달할 수 있도록하는 것입니다. 그래도 과도한 것처럼 보이며 활동이 WriteToparcel () / readfromparcel ()을 어떻게 구현 해야하는지 전혀 모릅니다.

더 쉽고 더 나은 방법이 있습니까? 도움을 주셔서 감사합니다.

편집하다:

나중에 이것에 관심이있는 사람은 샘플 디렉토리에서 AIDL을 통해이를 처리하기위한 Google의 샘플 코드가 있습니다 : /apis/app/remoteservice.java

도움이 되었습니까?

해결책

서비스와 의사 소통하는 세 가지 명백한 방법이 있습니다.

  1. 의도 사용
  2. AIDL 사용
  3. 서비스 객체 자체 사용 (싱글 톤)

귀하의 경우, 나는 옵션 3과 함께 갈 것입니다.

void onCreate(Intent i) {
  sInstance = this;
}

정적 기능을 만드십시오 MyService getInstance(), 정적을 반환합니다 sInstance.

그런 다음 Activity.onCreate() 서비스를 시작하고 서비스가 실제로 시작될 때까지 비동기 적으로 기다립니다 (서비스에 대한 의도를 보내서 앱에 서비스를 알릴 수 있습니다.) 그리고 인스턴스를 얻습니다. 인스턴스가 있으면 서비스 리스너 개체를 서비스에 등록하면 설정되어 있습니다. 참고 : 활동 내부에서보기를 편집 할 때는 UI 스레드에서 뷰를 수정해야 할 때 서비스가 자체 스레드를 실행하므로 호출해야합니다. Activity.runOnUiThread().

마지막으로해야 할 일은 청취자 객체에 대한 참조를 제거하는 것입니다. Activity.onPause(), 그렇지 않으면 활동 컨텍스트의 인스턴스는 좋지 않습니다.

참고 :이 방법은 응용 프로그램/활동/작업이 서비스에 액세스 할 수있는 유일한 프로세스 인 경우에만 유용합니다. 그렇지 않은 경우 옵션 1 또는 2를 사용해야합니다.

다른 팁

Asker는 아마도 이것을 지나간 이래로 오래되었지만 다른 사람이 이것을 검색 할 경우를 대비하여 ...

이것을 처리하는 또 다른 방법이 있습니다.

을 추가하다 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 데이터베이스 테이블을 업데이트 한 다음 내 활동 내에서 변경 사항을 찾기 위해 돌아가는 대신 어쨌든 변경 사항이 지속되기를 원하기 때문에 DB를 통해 데이터를 전달하는 것이 합리적입니다.

사용 LocalBroadcastManager 앱 내부의 로컬 서비스에서 전송 된 방송을 듣기 위해 수신자를 등록하려면 참조가 다음과 같습니다.

http://developer.android.com/reference/android/support/v4/content/localbroadcastmanager.html

오토 이벤트 버스 도서관에 대해 아무도 언급하지 않았다는 사실에 놀랐습니다.

http://square.github.io/otto/

나는 이것을 내 Android 앱에서 사용하고 있으며 원활하게 작동합니다.

메신저를 사용하는 것은 서비스와 활동간에 의사 소통하는 또 다른 간단한 방법입니다.

활동에서 해당 메신저가있는 핸들러를 만듭니다. 서비스의 메시지를 처리합니다.

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());

메신저를 메시지에 첨부하여 서비스에 전달할 수 있습니다.

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의 작동 방식에 대한 전체 예제를 참조하십시오.

다른 의견에 언급되지 않은 다른 방법은 Bindservice ()를 사용하여 활동의 서비스에 바인딩하고 ServiceConcection 콜백에서 서비스 인스턴스를 얻는 것입니다. 여기에 설명 된대로 http://developer.android.com/guide/components/bound-services.html

당신은 또한 사용할 수도 있습니다 LiveData 그것은 an처럼 작동합니다 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
    }
})

이것에서 더 많은 것을 읽을 수 있습니다 블로그.

또 다른 방법은 활동과 서비스 자체를 통해 가짜 모델 클래스와 함께 관찰자를 사용하여 MVC 패턴 변동을 구현하는 것입니다. 나는 이것이 이것을 달성하는 가장 좋은 방법인지는 모르겠지만, 그것은 나를 위해 일한 방식입니다. 예제가 필요한 경우 요청하면 무언가를 게시하겠습니다.

코드 예제로 @MRSNOWFLAKE 답변에 대한 후속 조치.이것은 Xabber 이제 오픈 소스입니다 Application 수업. 그만큼 Application 클래스는 중앙 집중화 및 조정입니다 Listeners 그리고 ManagerInterfaces 등. 모든 종류의 관리자는 동적으로로드됩니다. Activity´s Xabber에서 시작하여 어떤 유형의 Listener 그들은. 그리고 언제 a Service 보고서를 시작하십시오 Application 시작대로 수업. 이제 메시지를 보내려고합니다 Activity 당신이해야 할 일은 당신을 만드는 것입니다 Activity a listener 어떤 유형이 필요한지. 에서 OnStart() OnPause() 등록/Unreg. 그만큼 Service 물어볼 수 있습니다 Application 바로 그 수업 listener 대화해야하며 거기에 있다면 활동이받을 준비가되었습니다.

통과합니다 Application 클래스 당신은 더 많은 전리품이 진행되고 있음을 알 수 있습니다.

내 방법 :

수업을 관리하는 클래스 보내기/서비스/활동에서 메시지를 받고 수신합니다.

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"메소드는 바인더를 그의 Messagemanager로 반환합니다. "서비스 연결"인터페이스 메소드 구현을 통한 활동 "OnserviceConnected" "귀하는이 iBinder를 가져 와서 Messagemanager를 사용하여이를 시작합니다. 활동이 그의 Messagemanager를 시작한 후 MessageHandler를 보내고 핸드 셰이크를 서비스로 보내서 "MessageHandler"발신자 (Messagemanager에서 "Private Messenger MMSGSender;")를 설정할 수 있습니다. 이렇게하면 서비스는 누가 메시지를 보내는 사람에게 알고 있습니다.

Messagemanager의 Messenger "Sender"의 목록/큐를 사용하여이를 구현할 수 있으므로 여러 메시지를 다른 활동/서비스로 보낼 수 있거나 Messagemanager의 Messenger "수신기"목록/큐를 사용할 수 있으므로 여러 수신을 수신 할 수 있습니다. 다른 활동/서비스의 메시지.

"Messagemanager"인스턴스에는 수신 된 모든 메시지 목록이 있습니다.

이 "Messagemanager"인스턴스를 사용하여 "Activity 's Messenger"와 "Service Messenger"간의 연결을 볼 수 있듯이 "onserviceConnected"방법과 "핸드 셰이크"를 사용하여 수행됩니다.

이것이 당신에게 도움이되기를 바랍니다 :) 대단히 감사합니다! 안녕 : d

바인딩은 의사 소통하는 또 다른 방법입니다

콜백을 만듭니다

public interface MyCallBack{

   public void getResult(String result);

}

활동 측면 :

  1. 활동에서 인터페이스를 구현하십시오
  2. 방법에 대한 구현을 제공하십시오
  3. 활동에 서비스를 바인딩하십시오
  4. 서비스가 활동에 묶여 있지 않으면 콜백을 등록하고 등록하지 않습니다.

    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 );
          }
    
     }
    

서비스 측면 :

  1. 콜백 초기화
  2. 필요할 때마다 콜백 메소드를 호출하십시오

     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;
        }
     }
    

Madhur가 언급했듯이 버스를 사용하여 커뮤니케이션 할 수 있습니다.

버스를 사용하는 경우 몇 가지 옵션이 있습니다.

오토 이벤트 버스 도서관 (Rxjava에 유리한 감가 상각)

http://square.github.io/otto/

그린 로봇의 이벤트 버스

http://greenrobot.org/eventbus/

NYBUS (RXBUS, rxjava를 사용하여 구현되었습니다. EventBus와 매우 유사합니다)

https://github.com/mindorksopensource/nybus

게다가 LocalBroadcastManager, 이벤트 버스 및 메신저 이 질문에서 이미 답변을 사용할 수 있습니다 보류 의도 서비스에서 의사 소통합니다.

말한 바와 같이 여기 내 블로그 게시물에서

서비스와 활동 사이의 커뮤니케이션 CreatePendingResult() .CreatePendingResult ()는 새로운 보급 인텐트 객체를 생성하여 사용하기 위해 서비스를 제공하고 결과 데이터를 onactivityResult (int, int, intent) 내에서 활동으로 돌려 보내기 위해 서비스를 다시 보낼 수 있습니다. 의도적으로 추가로, 귀하의 활동은이 보드 인디언트를 서비스에 전달할 수 있습니다. 서비스는 이벤트의 onactivitiveResult를 통해 활동에 알리기 위해 보급 인턴에서 Send () 메소드를 호출 할 수 있습니다.

활동

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);

                }
            }
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top