programing

Android Service가 Activity와 통신하도록 하는 방법

powerit 2023. 10. 20. 14:53
반응형

Android Service가 Activity와 통신하도록 하는 방법

저는 첫 번째 안드로이드 애플리케이션을 작성하고 서비스와 활동 간의 커뮤니케이션을 이해하려고 노력하고 있습니다.백그라운드에서 실행되고 GPS 및 시간 기반 로깅을 수행하는 서비스가 있습니다.서비스를 시작하고 중지하는 데 사용할 활동을 준비합니다.

따라서 먼저 활동을 시작할 때 서비스가 실행되고 있는지 파악할 수 있어야 합니다.여기에는 이에 대한 다른 질문들이 몇 가지 있으므로, 저는 그것을 알아낼 수 있다고 생각합니다. (하지만 자유롭게 조언을 해주세요.)

제 진짜 문제는 활동이 실행되고 서비스가 시작되면 서비스가 활동에 메시지를 보낼 수 있는 방법이 필요하다는 것입니다.이 시점의 단순 문자열 및 정수 - 상태 메시지가 대부분입니다.메시지는 정기적으로 발생하지 않을 것이기 때문에 다른 방법이 있다면 서비스를 투표하는 것은 좋지 않다고 생각합니다.사용자가 작업을 시작한 경우에만 이 통신을 원합니다. 서비스에서 작업을 시작하고 싶지 않습니다.즉, 활동을 시작하고 서비스가 실행 중인 경우 흥미로운 일이 발생할 때 활동 UI에 일부 상태 메시지가 표시됩니다.활동을 시작하지 않으면 이러한 메시지가 표시되지 않습니다.

서비스가 실행 중인지 확인하고, 실행 중인 경우 활동을 리스너로 추가할 수 있어야 할 것 같습니다.그런 다음 활동이 일시 중지되거나 중지될 때 수신기로서 활동을 제거합니다.그게 실제로 가능한가요?제가 할 수 있는 유일한 방법은 활동이 파셀러블을 구현하도록 하고 AIDL 파일을 만들어서 서비스의 원격 인터페이스를 통해 전달하도록 하는 것입니다.하지만 너무 많이 쓴 것 같은데 활동에서 writeToParcel() / readFromParcel()을 어떻게 구현해야 할지 모르겠습니다.

더 쉬운 방법이나 더 좋은 방법이 있습니까?도와주셔서 감사합니다.

편집:

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

질문자가 이걸 지나간 지 오래됐을 겁니다 하지만 다른 사람이 이걸 찾을 경우를 대비해서...

이 문제를 해결할 수 있는 다른 방법이 있습니다. 가장 간단한 방법이라고 생각합니다.

를 합니다.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를 통해 데이터를 전달하는 것이 타당합니다.

서비스와 소통하는 데에는 다음과 같은 세 가지 분명한 방법이 방법은 다음과 같습니다.

  1. 인텐트 사용
  2. AIDL 사용하기
  3. 서비스 개체 자체 사용(싱글톤으로)

당신의 경우, 저는 옵션 3으로 하겠습니다.서비스 자체를 정적으로 참조하여 Create()에 입력합니다.

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

정적 함수 만들기MyService getInstance(), 정적인 상태를 반환합니다.sInstance.

그다음에Activity.onCreate()서비스를 시작하고, 비동기적으로 서비스가 실제로 시작될 때까지 기다립니다(활동에 대한 의도를 전송하여 서비스가 준비되었음을 앱에 알리도록 할 수 있습니다). 그리고 인스턴스를 가져옵니다.인스턴스가 있으면 서비스 수신기 개체를 서비스에 등록하면 설정됩니다.참고: 활동 내부의 보기를 편집할 때 UI 스레드에서 수정해야 합니다. 서비스는 아마도 자체 스레드를 실행하므로 호출해야 합니다.Activity.runOnUiThread().

마지막으로 해야 할 일은 리스너 개체에 대한 참조를 제거하는 것입니다.Activity.onPause(), 그렇지 않으면 활동 컨텍스트의 인스턴스가 새어나갑니다. 좋지 않습니다.

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

사용하다LocalBroadcastManager앱 내의 로컬 서비스에서 전송된 방송을 듣기 위해 수신기를 등록하는 방법은 다음과 같습니다.

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

사용할 수도 있습니다.LiveData그것은 아주 잘 작동합니다.EventBus.

class MyService : LifecycleService() {
    companion object {
        val BUS = MutableLiveData<Any>()
    }

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

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

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 = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
    myService.send(message);
catch (RemoteException e) {
    e.printStackTrace();
}

완전한 예는 API 데모에서 찾을 수 있습니다.Messenger Service 및 Messenger Service Activity.MyService의 작동 방식에 대해서는 전체 예시를 참조합니다.

오토 이벤트 버스 도서관에 대해 아무도 언급을 안해서 놀랍습니다.

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

저는 안드로이드 앱에서 이것을 사용해왔는데 그것은 원활하게 작동합니다.

다른 의견에서 언급되지 않은 다른 방법은 bindService()를 사용하여 활동에서 서비스에 바인딩하고 서비스 연결 콜백에서 서비스의 인스턴스를 가져오는 것입니다.여기 http://developer.android.com/guide/components/bound-services.html 에 설명되어 있습니다.

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

콜백 만들기

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

또 다른 방법은 활동과 서비스 자체를 통해 가짜 모델 클래스를 가진 관찰자를 사용하여 MVC 패턴 변형을 구현하는 것입니다.이게 최선의 방법인지는 모르겠지만, 제게 효과가 있었던 방법입니다.예시가 필요하시면 요청하시면 글을 올리겠습니다.

이 질문에서 이미 답변한 로컬 브로드캐스트 매니저, 이벤트 버스 및 메신저 외에도 서비스에서 Pending Intent를 사용하여 커뮤니케이션할 수 있습니다.

여기 제 블로그 포스트에 언급된 것처럼.

서비스와 활동 간의 통신은 보류 중을 사용하여 수행할 수 있습니다.의도.경우 createPendingResult()를 사용할 수 있습니다.createPendingResult()는 새 Pending을 만듭니다.ActivityResult(int, int, int) 콜백에서 서비스에 사용하고 결과 데이터를 활동 내부로 다시 보낼 수 있는 Intent 개체입니다.Since a Pending인텐트는 소포 가능하므로 인텐트를 추가로 넣을 수 있습니다. 활동이 보류 중인 상태를 할 수 있습니다.서비스에 대한 의도.서비스는 보류 중인 상태에서 send() 메서드를 호출할 수 있습니다.ActivityResult를 통해 이벤트를 알리려고 합니다.

활동

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

                }
            }
        }
    }
}

내 메소드:

서비스/활동에서 메시지를 주고 받는 것을 관리하는 클래스:

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로 반환하고, "Service Connection" 인터페이스 메서드 구현 "OnServiceConnected"를 통해 활동에서 이 IBinder를 얻고 이를 사용하여 MessageManager를 시작합니다.활동이 MessageManager를 시작한 후, MessageHandler는 서비스에 "MessageHandler" 송신자(MessageManager의 "개인 메신저 mMsgSender;")를 설정할 수 있도록 서비스에 메시지 핸들러 송신 및 악수를 합니다.이렇게 함으로써 서비스는 자신의 메시지를 누가 보내는지 알 수 있습니다.

또한 Message Manager의 Messenger "발송인" 목록/큐를 사용하여 여러 개의 메시지를 다른 활동/서비스로 보낼 수도 있고, Message Manager의 Messenger "수신인" 목록/큐를 사용하여 다른 활동/서비스에서 여러 개의 메시지를 받을 수도 있습니다.

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

이 "MessageManager" 인스턴스를 사용하는 "Activity's Messenger"와 "Service Messenger"의 연결이 자동인 것을 알 수 있듯이, "OnServiceConnected" 방식과 "Handshake"를 사용하여 수행됩니다.

이것이 당신에게 도움이 되길 바랍니다 :) 정말 감사합니다!안녕 :D

@MrSnowflake에 대한 후속 조치는 코드 예제와 함께 대답합니다.XABBER 오픈소스 클래스 입니다.Application수업은 집중과 조정입니다.Listeners매니저와인터페이스 등.모든 종류의 관리자는 동적으로 로드됩니다.Activity´sXabber에서 시작된 것은 어떤 유형으로 보고할 것입니다.Listener그들은 그렇다.그리고 언제.Service에 보고하기 시작합니다.Application수업 시작과 동시에이제 메시지를 보내려고 합니다.Activity당신이 해야 할 일은 당신의Activity가 되다listener필요한 유형이 무엇입니까?eOnStart() OnPause()등록/등록 취소. TheService물을 수 있습니다Application그것만을 위한 수업listener대화가 필요하고, 대화가 있으면 활동을 받을 준비가 됩니다.

다음 단계를 거칩니다.Application이것보다 더 많은 전리품이 있다는 것을 보게 될 겁니다

Madhur가 말한 것처럼, 당신은 소통을 위해 버스를 이용할 수 있습니다.

버스를 이용하는 경우 다음과 같은 옵션이 있습니다.

오토 이벤트 버스 라이브러리(RxJava에서 삭제됨)

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

그린 로봇의 이벤트 버스

http://greenrobot.org/eventbus/

NYBus(RxBus, RxJava를 사용하여 구현됨)이벤트 버스(EventBus)

https://github.com/MindorksOpenSource/NYBus

언급URL : https://stackoverflow.com/questions/2463175/how-to-have-android-service-communicate-with-activity

반응형