سؤال

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

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

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

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

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

تحرير:

لمن هو مهتم في هذا في وقت لاحق ، هناك نموذج التعليمات البرمجية من جوجل من أجل التعامل مع هذا عبر AIDL في العينات الدليل:/apis/app/RemoteService.java

هل كانت مفيدة؟

المحلول

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

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

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

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

جعل دالة ثابتة MyService getInstance(), التي ترجع ثابت sInstance.

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

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

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

نصائح أخرى

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

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

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

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

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

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

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

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

باستخدام 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());

الرسول يمكن أن تنتقل إلى الخدمة عن طريق ربط إلى الرسالة:

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() على سبيل المثال من الخدمة في ServiceConnection رد.كما هو موضح هنا http://developer.android.com/guide/components/bound-services.html

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

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

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

متابعة @MrSnowflake الإجابة مع التعليمات البرمجية المثال.هذا هو XABBER الآن مفتوحة المصدر Application الدرجة.على Application الطبقة المركزية وتنسيق Listeners و ManagerInterfaces وأكثر من ذلك.المديرين من كل نوع يتم تحميلها بشكل حيوي. Activity´s بدأت في Xabber سوف التقرير في ما هو نوع من Listener هم.و عندما Service نبدأ هذا التقرير في Application الفئة كما بدأ.الآن لإرسال رسالة إلى Activity كل ما عليك القيام به هو جعل الخاص بك Activity تصبح 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" المرسل ("خاص رسول mMsgSender;" في MessageManager ).تقوم هذه الخدمة تعرف أن الذين يرسلون له الرسائل.

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

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

كما يمكنك أن ترى العلاقة بين "النشاط رسول" و "خدمة ماسنجر" باستخدام هذا "MessageManager" المثال التلقائي ، ويتم ذلك من خلال "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/

الأخضر الروبوت EventBus

http://greenrobot.org/eventbus/

NYBus (RxBus ، نفذت باستخدام RxJava.مشابهة جدا EventBus)

https://github.com/MindorksOpenSource/NYBus

إلى جانب LocalBroadcastManager , حافلة الحدث رسول أجبت عن هذا السؤال يمكن أن نستخدم في انتظار القصد للتواصل من الخدمة.

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

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

                }
            }
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top