it-swarm.asia

كيفية جعل خدمة Android تتواصل مع النشاط

أقوم بكتابة أول تطبيق يعمل بنظام Android وأحاول أن أقوم بالتواصل بين الخدمات والأنشطة. لدي خدمة سيتم تشغيلها في الخلفية وتسجيل بعض الوقت في نظام تحديد المواقع والوقت. سيكون لدي نشاط سيتم استخدامه لبدء الخدمة وإيقافها.

أولاً ، يجب أن أكون قادرًا على معرفة ما إذا كانت الخدمة تعمل عند بدء النشاط. هناك بعض الأسئلة الأخرى حول هذا الموضوع ، لذلك أعتقد أنه يمكنني معرفة ذلك (لكن لا تتردد في تقديم المشورة).

مشكلتي الحقيقية: إذا كان النشاط قيد التشغيل وبدأ تشغيل الخدمة ، فأنا بحاجة إلى وسيلة لإرسال الخدمة إلى الرسائل. سلاسل بسيطة وأعداد صحيحة في هذه المرحلة - رسائل الحالة في الغالب. لن تحدث الرسائل بانتظام ، لذلك لا أعتقد أن الاقتراع بالخدمة طريقة جيدة للذهاب إذا كانت هناك طريقة أخرى. أريد هذا الاتصال فقط عندما يبدأ المستخدم النشاط - لا أريد بدء النشاط من الخدمة. بمعنى آخر ، إذا قمت ببدء تشغيل "النشاط" وكانت الخدمة قيد التشغيل ، فسترى بعض رسائل الحالة في "واجهة مستخدم النشاط" عندما يحدث شيء مثير للاهتمام. إذا لم تبدأ النشاط ، فلن ترى هذه الرسائل (ليست مثيرة للاهتمام).

يبدو أنني يجب أن أكون قادرًا على تحديد ما إذا كانت الخدمة قيد التشغيل ، وإذا كان الأمر كذلك ، أضف النشاط كمستمع. ثم قم بإزالة النشاط كمستمع عندما يتوقف النشاط مؤقتًا أو يتوقف. هل هذا ممكن فعلا؟ الطريقة الوحيدة التي يمكنني معرفة القيام بها هي أن يكون النشاط يطبق Parcelable ويقوم بإنشاء ملف AIDL حتى أتمكن من تمريره عبر واجهة الخدمة عن بُعد. يبدو ذلك كأنه مبالغة ، وليس لدي أي فكرة عن كيفية تطبيق النشاط على writeToParcel ()/readFromParcel ().

هل هناك طريقة أسهل أم أفضل؟ شكرا على اي مساعدة.

تصحيح:

لأي شخص مهتم بهذا لاحقًا ، يوجد نموذج من Google للتعامل معه عبر AIDL في دليل العينات: /apis/app/RemoteService.Java

232
Scott Saunders

هناك ثلاث طرق واضحة للتواصل مع الخدمات:

  1. باستخدام النوايا
  2. باستخدام AIDL
  3. استخدام كائن الخدمة نفسه (مثل المفرد)

في حالتك ، سأذهب مع الخيار 3. قم بالإشارة الثابتة إلى الخدمة الذاتية وملءها في onCreate ():

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

قم بإجراء دالة ثابتة MyService getInstance() ، والتي تقوم بإرجاع sInstance.

ثم في Activity.onCreate()- تبدأ الخدمة ، انتظر بشكل غير متزامن حتى تبدأ الخدمة فعليًا (يمكن أن تخطر خدمتك التطبيق الخاص بك أنه جاهز عن طريق إرسال نية إلى النشاط.) والحصول على مثيله. عندما يكون لديك المثيل ، قم بتسجيل كائن مستمع الخدمة لك في الخدمة وتم تعيينك. ملاحظة: عند تحرير طرق العرض داخل النشاط ، يجب عليك تعديلها في مؤشر ترابط واجهة المستخدم ، فمن المحتمل أن تقوم الخدمة بتشغيل مؤشر ترابط خاص بها ، لذلك تحتاج إلى الاتصال بـ Activity.runOnUiThread().

آخر شيء عليك القيام به هو إزالة المرجع الخاص بك كائن المستمع في Activity.onPause() ، وإلا فسوف يتسرب مثيل سياق النشاط الخاص بك ، وليس جيدًا.

ملاحظة: هذه الطريقة مفيدة فقط عندما يكون التطبيق/النشاط/المهمة الخاصة بك هي العملية الوحيدة التي ستصل إلى خدمتك. إذا لم يكن الأمر كذلك ، فعليك استخدام الخيار 1. أو 2.

83
MrSnowflake

ربما يكون السائل قد تخطى هذا الأمر منذ فترة طويلة ، ولكن في حالة قيام شخص آخر بالبحث عن هذا ...

هناك طريقة أخرى للتعامل مع ذلك ، والتي أعتقد أنها قد تكون أبسط.

أضف 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 بدلاً من تحديث جداول قاعدة البيانات ثم العودة للعثور على التغييرات في نشاطي ، لكن بما أنني أريد أن تستمر التغييرات على أي حال ، فمن المنطقي تمرير البيانات عبر قاعدة البيانات.

244
MaximumGoat

استخدم LocalBroadcastManager لتسجيل جهاز استقبال للاستماع إلى البث المرسل من الخدمة المحلية داخل تطبيقك ، والمرجع هنا:

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

37
Jack Gao

أنا مندهش لأن لا أحد قد أشار إلى مكتبة حافلة الحدث أوتو

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

لقد تم استخدام هذا في تطبيقات Android الخاصة بي ويعمل بسلاسة.

18
Madhur Ahuja

يعد استخدام 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 و MessengerServiceActivity . ارجع إلى المثال الكامل لمعرفة كيفية عمل خدمة MyService.

18
Kjetil

الطريقة الأخرى غير المذكورة في التعليقات الأخرى هي ربط الخدمة من النشاط باستخدام bindService () والحصول على مثيل للخدمة في رد اتصال ServiceConnection. كما هو موضح هنا http://developer.Android.com/guide/components/bound-services.html

8
miguel

هناك طريقة أخرى يمكن أن تستخدم المراقبين مع فئة نموذج وهمية من خلال النشاط والخدمة نفسها ، وتنفيذ تنوع نمط MVC. لا أعرف ما إذا كانت هذه هي أفضل طريقة لتحقيق ذلك ، لكنها الطريقة التي عملت من أجلي. إذا كنت بحاجة إلى بعض الأمثلة ، اسأل عن ذلك وسأقوم بنشر شيء ما.

2
ReDirEct__

يمكنك أيضًا استخدام 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
    }
})

يمكنك قراءة المزيد من هذا بلوق.

2
zeenosaur

لمتابعةMrSnowflake الإجابة مع مثال التعليمات البرمجية. هذا هو XABBER مفتوح المصدر الآن فئة Application . فئة Application تتمركز وتنسيق Listeners و ManagerInterfaces والمزيد. يتم تحميل المديرين من جميع الأنواع بشكل حيوي. Activity´s بدأت في Xabber سوف تقدم تقريرا عن نوع Listener. وعندما يبدأ Service برفع تقرير إلى فئة Application كما بدأ. الآن لإرسال رسالة إلى Activity كل ما عليك القيام به هو جعل Activity الخاص بك يصبح listener من أي نوع تحتاجه. في OnStart()OnPause() تسجيل/إلغاء التسجيل. يمكن لـ Service أن يطلب من فئة Application ذلك listener الذي يحتاج إلى التحدث إليه وإذا كان هناك ، يكون النشاط جاهزًا للاستلام.

بالانتقال إلى فئة Application ، سترى أن هناك نهبًا كبيرًا يحدث بعد ذلك.

2
Erik

الربط هو وسيلة أخرى للتواصل

إنشاء رد اتصال

public interface MyCallBack{

   public void getResult(String result);

}

جانب النشاط:

  1. تطبيق الواجهة في النشاط
  2. توفير التنفيذ للطريقة
  3. ربط النشاط بالخدمة
  4. قم بالتسجيل وإلغاء تسجيل رد الاتصال عندما تصبح الخدمة ملزمة وغير مرتبطة بالنشاط.

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

جانب الخدمة:

  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;
        }
     }
    
0
Rohit Singh

كما ذكر مادهور ، يمكنك استخدام حافلة للتواصل.

في حالة استخدام حافلة لديك بعض الخيارات:

مكتبة حافلة حدث Otto (تم إهمالها لصالح RxJava)

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

GreenBot's EventBus

http://greenrobot.org/eventbus/

NYBus (RxBus ، تم تنفيذه باستخدام RxJava. يشبه إلى حد كبير EventBus)

https://github.com/MindorksOpenSource/NYBus

0
Mahpooya

بجانب LocalBroadcastManager و Event Bus و Messenger تمت الإجابة عليه بالفعل في هذا السؤال ، يمكننا استخدام في انتظار النية للاتصال من الخدمة.

كما ذكر هنا في بلدي بلوق وظيفة

يمكن إجراء الاتصال بين الخدمة والنشاط باستخدام PendingIntent.For التي يمكننا استخدامها createPendingResult (). createPendingResult () ينشئ كائن PendingIntent جديدًا يمكنك إرساله إلى الخدمة لاستخدامه وإرسال بيانات النتائج إلى حسابك نشاط داخل onActivityResult (int، int، Intent) call.S.Send من PendingIntent لا يتجزأ ، وبالتالي يمكن وضعها في نية إضافية ، يمكن للنشاط الخاص بك تمرير هذا 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);

                }
            }
        }
    }
}
0
Android Developer

طريقتي:

فئة لإدارة إرسال واستقبال الرسائل من/إلى الخدمة/النشاط:

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" ("Messenger mMsgSender الخاص ؛" في MessageManager). القيام بذلك تعرف على من يرسل رسالته.

يمكنك أيضًا تنفيذ ذلك باستخدام "مرسل" قائمة/قائمة انتظار لبرنامج Messenger في MessageManager حتى تتمكن من إرسال رسائل متعددة إلى أنشطة/خدمات مختلفة أو يمكنك استخدام قائمة/قائمة انتظار برنامج "Messenger" في MessageManager حتى تتمكن من تلقي رسائل متعددة رسالة من مختلف الأنشطة/الخدمات.

في مثيل "MessageManager" لديك قائمة بجميع الرسائل المستلمة.

كما ترى ، فإن الاتصال بين "Activity's Messenger" و "Service Messenger" باستخدام مثيل "MessageManager" هو تلقائي ، ويتم ذلك من خلال طريقة "OnServiceConnected" ومن خلال استخدام "Handshake".

آمل أن يكون هذا مفيدا لك :) شكرا جزيلا لك! وداعا: د

0
Z3R0