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를 통해 데이터를 전달하는 것이 타당합니다.
서비스와 소통하는 데에는 다음과 같은 세 가지 분명한 방법이 방법은 다음과 같습니다.
- 인텐트 사용
- AIDL 사용하기
- 서비스 개체 자체 사용(싱글톤으로)
당신의 경우, 저는 옵션 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의 작동 방식에 대해서는 전체 예시를 참조합니다.
오토 이벤트 버스 도서관에 대해 아무도 언급을 안해서 놀랍습니다.
저는 안드로이드 앱에서 이것을 사용해왔는데 그것은 원활하게 작동합니다.
다른 의견에서 언급되지 않은 다른 방법은 bindService()를 사용하여 활동에서 서비스에 바인딩하고 서비스 연결 콜백에서 서비스의 인스턴스를 가져오는 것입니다.여기 http://developer.android.com/guide/components/bound-services.html 에 설명되어 있습니다.
바인딩은 의사소통의 또 다른 방법입니다.
콜백 만들기
public interface MyCallBack{
public void getResult(String result);
}
활동측:
활동에서 인터페이스 구현
메소드에 대한 구현을 제공합니다.
서비스에 활동 바인딩
서비스가 활동과 바인딩 및 바인딩 해제되면 콜백을 등록하고 등록 취소합니다.
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 ); } }
서비스 측:
콜백 초기화
필요할 때마다 콜백 메소드 호출
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´s
Xabber에서 시작된 것은 어떤 유형으로 보고할 것입니다.Listener
그들은 그렇다.그리고 언제.Service
에 보고하기 시작합니다.Application
수업 시작과 동시에이제 메시지를 보내려고 합니다.Activity
당신이 해야 할 일은 당신의Activity
가 되다listener
필요한 유형이 무엇입니까?eOnStart()
OnPause()
등록/등록 취소. TheService
물을 수 있습니다Application
그것만을 위한 수업listener
대화가 필요하고, 대화가 있으면 활동을 받을 준비가 됩니다.
다음 단계를 거칩니다.Application
이것보다 더 많은 전리품이 있다는 것을 보게 될 겁니다
Madhur가 말한 것처럼, 당신은 소통을 위해 버스를 이용할 수 있습니다.
버스를 이용하는 경우 다음과 같은 옵션이 있습니다.
오토 이벤트 버스 라이브러리(RxJava에서 삭제됨)
그린 로봇의 이벤트 버스
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
'programing' 카테고리의 다른 글
mysql의 다른 활동을 기반으로 행 가져오기 (0) | 2023.10.20 |
---|---|
스트리밍 버퍼를 utf8-string으로 변환 (0) | 2023.10.20 |
Woocommerce에서 선택한 "일" 사용자 정의 필드를 기반으로 카트 항목 가격 계산 (0) | 2023.10.20 |
잘린 LISTAGG 문자열 (0) | 2023.10.20 |
libxml2 라이브러리에서 함수 xmlCheckVersion을 찾을 수 없습니다.pip을 통해 lxml을 설치할 때 libxml2가 설치되어 있습니까? (0) | 2023.10.20 |