2010-11-29 3 views
547

Я не мог найти примеров того, как отправлять сообщения между активностью и службой, и я потратил слишком много часов на то, чтобы понять это. Вот пример проекта для других ссылок.Пример: связь между активностью и службой с использованием сообщений

В этом примере вы можете начать или остановить службу напрямую, а отдельно связывать/отвязать услугу. Когда служба работает, она увеличивает число на 10   Гц. Если действие связано с Service, оно отображает текущее значение. Данные передаются как Integer и как String, поэтому вы можете увидеть, как это сделать двумя разными способами. Также есть кнопки в активности для отправки сообщений службе (изменение значения приращения).

Скриншот:

Screenshot of Android service messaging example

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
     package="com.exampleservice" 
     android:versionCode="1" 
     android:versionName="1.0"> 
    <application android:icon="@drawable/icon" android:label="@string/app_name"> 
     <activity android:name=".MainActivity" 
        android:label="@string/app_name"> 
      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 
       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 
    <service android:name=".MyService"></service> 
    </application> 
    <uses-sdk android:minSdkVersion="8" /> 
</manifest> 

Рез \ значения \ strings.xml:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="app_name">ExampleService</string> 
    <string name="service_started">Example Service started</string> 
    <string name="service_label">Example Service Label</string> 
</resources> 

Рез \ расположение \ main.xml:

<RelativeLayout 
    android:id="@+id/RelativeLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" > 

    <Button 
     android:id="@+id/btnStart" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Start Service" > 
    </Button> 

    <Button 
     android:id="@+id/btnStop" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentRight="true" 
     android:text="Stop Service" > 
    </Button> 
</RelativeLayout> 

<RelativeLayout 
    android:id="@+id/RelativeLayout02" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" > 

    <Button 
     android:id="@+id/btnBind" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Bind to Service" > 
    </Button> 

    <Button 
     android:id="@+id/btnUnbind" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentRight="true" 
     android:text="Unbind from Service" > 
    </Button> 
</RelativeLayout> 

<TextView 
    android:id="@+id/textStatus" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Status Goes Here" 
    android:textSize="24sp" /> 

<TextView 
    android:id="@+id/textIntValue" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Integer Value Goes Here" 
    android:textSize="24sp" /> 

<TextView 
    android:id="@+id/textStrValue" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="String Value Goes Here" 
    android:textSize="24sp" /> 

<RelativeLayout 
    android:id="@+id/RelativeLayout03" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" > 

    <Button 
     android:id="@+id/btnUpby1" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Increment by 1" > 
    </Button> 

    <Button 
     android:id="@+id/btnUpby10" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentRight="true" 
     android:text="Increment by 10" > 
    </Button> 
</RelativeLayout> 

ЦСИ \ com.exampleservice \ MainActivity.java:

package com.exampleservice; 

import android.app.Activity; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.ServiceConnection; 
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 android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.TextView; 

public class MainActivity extends Activity { 
    Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10; 
    TextView textStatus, textIntValue, textStrValue; 
    Messenger mService = null; 
    boolean mIsBound; 
    final Messenger mMessenger = new Messenger(new IncomingHandler()); 

    class IncomingHandler extends Handler { 
     @Override 
     public void handleMessage(Message msg) { 
      switch (msg.what) { 
      case MyService.MSG_SET_INT_VALUE: 
       textIntValue.setText("Int Message: " + msg.arg1); 
       break; 
      case MyService.MSG_SET_STRING_VALUE: 
       String str1 = msg.getData().getString("str1"); 
       textStrValue.setText("Str Message: " + str1); 
       break; 
      default: 
       super.handleMessage(msg); 
      } 
     } 
    } 
    private ServiceConnection mConnection = new ServiceConnection() { 
     public void onServiceConnected(ComponentName className, IBinder service) { 
      mService = new Messenger(service); 
      textStatus.setText("Attached."); 
      try { 
       Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT); 
       msg.replyTo = mMessenger; 
       mService.send(msg); 
      } 
      catch (RemoteException e) { 
       // In this case the service has crashed before we could even do anything with it 
      } 
     } 

     public void onServiceDisconnected(ComponentName className) { 
      // This is called when the connection with the service has been unexpectedly disconnected - process crashed. 
      mService = null; 
      textStatus.setText("Disconnected."); 
     } 
    }; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     btnStart = (Button)findViewById(R.id.btnStart); 
     btnStop = (Button)findViewById(R.id.btnStop); 
     btnBind = (Button)findViewById(R.id.btnBind); 
     btnUnbind = (Button)findViewById(R.id.btnUnbind); 
     textStatus = (TextView)findViewById(R.id.textStatus); 
     textIntValue = (TextView)findViewById(R.id.textIntValue); 
     textStrValue = (TextView)findViewById(R.id.textStrValue); 
     btnUpby1 = (Button)findViewById(R.id.btnUpby1); 
     btnUpby10 = (Button)findViewById(R.id.btnUpby10); 

     btnStart.setOnClickListener(btnStartListener); 
     btnStop.setOnClickListener(btnStopListener); 
     btnBind.setOnClickListener(btnBindListener); 
     btnUnbind.setOnClickListener(btnUnbindListener); 
     btnUpby1.setOnClickListener(btnUpby1Listener); 
     btnUpby10.setOnClickListener(btnUpby10Listener); 

     restoreMe(savedInstanceState); 

     CheckIfServiceIsRunning(); 
    } 

    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     outState.putString("textStatus", textStatus.getText().toString()); 
     outState.putString("textIntValue", textIntValue.getText().toString()); 
     outState.putString("textStrValue", textStrValue.getText().toString()); 
    } 
    private void restoreMe(Bundle state) { 
     if (state!=null) { 
      textStatus.setText(state.getString("textStatus")); 
      textIntValue.setText(state.getString("textIntValue")); 
      textStrValue.setText(state.getString("textStrValue")); 
     } 
    } 
    private void CheckIfServiceIsRunning() { 
     //If the service is running when the activity starts, we want to automatically bind to it. 
     if (MyService.isRunning()) { 
      doBindService(); 
     } 
    } 

    private OnClickListener btnStartListener = new OnClickListener() { 
     public void onClick(View v){ 
      startService(new Intent(MainActivity.this, MyService.class)); 
     } 
    }; 
    private OnClickListener btnStopListener = new OnClickListener() { 
     public void onClick(View v){ 
      doUnbindService(); 
      stopService(new Intent(MainActivity.this, MyService.class)); 
     } 
    }; 
    private OnClickListener btnBindListener = new OnClickListener() { 
     public void onClick(View v){ 
      doBindService(); 
     } 
    }; 
    private OnClickListener btnUnbindListener = new OnClickListener() { 
     public void onClick(View v){ 
      doUnbindService(); 
     } 
    }; 
    private OnClickListener btnUpby1Listener = new OnClickListener() { 
     public void onClick(View v){ 
      sendMessageToService(1); 
     } 
    }; 
    private OnClickListener btnUpby10Listener = new OnClickListener() { 
     public void onClick(View v){ 
      sendMessageToService(10); 
     } 
    }; 
    private void sendMessageToService(int intvaluetosend) { 
     if (mIsBound) { 
      if (mService != null) { 
       try { 
        Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0); 
        msg.replyTo = mMessenger; 
        mService.send(msg); 
       } 
       catch (RemoteException e) { 
       } 
      } 
     } 
    } 


    void doBindService() { 
     bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE); 
     mIsBound = true; 
     textStatus.setText("Binding."); 
    } 
    void doUnbindService() { 
     if (mIsBound) { 
      // If we have received the service, and hence registered with it, then now is the time to unregister. 
      if (mService != null) { 
       try { 
        Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT); 
        msg.replyTo = mMessenger; 
        mService.send(msg); 
       } 
       catch (RemoteException e) { 
        // There is nothing special we need to do if the service has crashed. 
       } 
      } 
      // Detach our existing connection. 
      unbindService(mConnection); 
      mIsBound = false; 
      textStatus.setText("Unbinding."); 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     try { 
      doUnbindService(); 
     } 
     catch (Throwable t) { 
      Log.e("MainActivity", "Failed to unbind from the service", t); 
     } 
    } 
} 

SRC \ com.examplese rvice \ MyService.java:

package com.exampleservice; 

import java.util.ArrayList; 
import java.util.Timer; 
import java.util.TimerTask; 

import android.app.Notification; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.app.Service; 
import android.content.Intent; 
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; 

public class MyService extends Service { 
    private NotificationManager nm; 
    private Timer timer = new Timer(); 
    private int counter = 0, incrementby = 1; 
    private static boolean isRunning = false; 

    ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients. 
    int mValue = 0; // Holds last value set by a client. 
    static final int MSG_REGISTER_CLIENT = 1; 
    static final int MSG_UNREGISTER_CLIENT = 2; 
    static final int MSG_SET_INT_VALUE = 3; 
    static final int MSG_SET_STRING_VALUE = 4; 
    final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler. 


    @Override 
    public IBinder onBind(Intent intent) { 
     return mMessenger.getBinder(); 
    } 
    class IncomingHandler extends Handler { // Handler of incoming messages from clients. 
     @Override 
     public void handleMessage(Message msg) { 
      switch (msg.what) { 
      case MSG_REGISTER_CLIENT: 
       mClients.add(msg.replyTo); 
       break; 
      case MSG_UNREGISTER_CLIENT: 
       mClients.remove(msg.replyTo); 
       break; 
      case MSG_SET_INT_VALUE: 
       incrementby = msg.arg1; 
       break; 
      default: 
       super.handleMessage(msg); 
      } 
     } 
    } 
    private void sendMessageToUI(int intvaluetosend) { 
     for (int i=mClients.size()-1; i>=0; i--) { 
      try { 
       // Send data as an Integer 
       mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0)); 

       //Send data as a String 
       Bundle b = new Bundle(); 
       b.putString("str1", "ab" + intvaluetosend + "cd"); 
       Message msg = Message.obtain(null, MSG_SET_STRING_VALUE); 
       msg.setData(b); 
       mClients.get(i).send(msg); 

      } 
      catch (RemoteException e) { 
       // The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop. 
       mClients.remove(i); 
      } 
     } 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     Log.i("MyService", "Service Started."); 
     showNotification(); 
     timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L); 
     isRunning = true; 
    } 
    private void showNotification() { 
     nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); 
     // In this sample, we'll use the same text for the ticker and the expanded notification 
     CharSequence text = getText(R.string.service_started); 
     // Set the icon, scrolling text and timestamp 
     Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis()); 
     // The PendingIntent to launch our activity if the user selects this notification 
     PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0); 
     // Set the info for the views that show in the notification panel. 
     notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent); 
     // Send the notification. 
     // We use a layout id because it is a unique number. We use it later to cancel. 
     nm.notify(R.string.service_started, notification); 
    } 
    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Log.i("MyService", "Received start id " + startId + ": " + intent); 
     return START_STICKY; // run until explicitly stopped. 
    } 

    public static boolean isRunning() 
    { 
     return isRunning; 
    } 


    private void onTimerTick() { 
     Log.i("TimerTick", "Timer doing work." + counter); 
     try { 
      counter += incrementby; 
      sendMessageToUI(counter); 

     } 
     catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks. 
      Log.e("TimerTick", "Timer Tick Failed.", t); 
     } 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     if (timer != null) {timer.cancel();} 
     counter=0; 
     nm.cancel(R.string.service_started); // Cancel the persistent notification. 
     Log.i("MyService", "Service Stopped."); 
     isRunning = false; 
    } 
} 
+51

Отличный пример! Другая приятная особенность: если вы поместите атрибут `android: process =: myservicename` в тег` service` вашей службы в вашем файле manifest.xml, например: ``, тогда он будет запускать вашу службу как другой процесс - таким образом, в другом потоке. Это означает, что любой тяжелый расчет, выполненный/длительный запрос службы, не повредит ваш поток пользовательского интерфейса. – sydd 2011-12-02 21:01:39

+0

Вы можете разместить ссылку, откуда у вас есть этот пример кода ..? – 2012-04-10 10:45:04

+26

Я знаю, что вы приложили все усилия, чтобы сделать это, но было бы разумнее поставить его на github или аналогичный сайт для обмена исходными кодами и опубликовать ссылку здесь. Люди легче поднять и запустить этот путь. – 2012-05-08 15:24:30

ответ

43

Посмотрите на LocalService example.

Ваш Service возвращает экземпляр себя потребителям, которые звонили onBind. Затем вы можете напрямую взаимодействовать с сервисом, например. регистрируя свой собственный интерфейс слушателя с сервисом, чтобы вы могли получать обратные вызовы.

14

Примечание: вам не нужно проверять, работает ли ваша служба, CheckIfServiceIsRunning(), потому что bindService() запустит его, если он не запущен.

Кроме того: если вы поворачиваете телефон, вы не хотите его снова bindService(), потому что снова будет вызван onCreate(). Обязательно определите , чтобы предотвратить это.

8

Все в порядке. Хороший пример activity/service связь с использованием Messenger.

Один комментарий: метод MyService.isRunning() не требуется .. bindService() можно сделать любое количество раз. в этом нет вреда.

Если MyService работает в другом процессе, статическая функция MyService.isRunning() всегда будет возвращать значение false. Поэтому нет необходимости в этой функции.

0

Кажется мне, вы могли бы вы сохранили некоторую память, объявляя вашу деятельность с «реализует Handler.Callback»

7
Message msg = Message.obtain(null, 2, 0, 0); 
        Bundle bundle = new Bundle(); 
        bundle.putString("url", url); 
        bundle.putString("names", names); 
        bundle.putString("captions",captions); 
        msg.setData(bundle); 

Таким образом, вы отправить его на службу. После этого принимаю.

18

Для отправки данных на услугу вы можете использовать:

Intent intent = new Intent(getApplicationContext(), YourService.class); 
intent.putExtra("SomeData","ItValue"); 
startService(intent); 

И после того, как на службе в onStartCommand() получить данные от намерения.

Для отправки данных или событие от службы в приложение (для одного или нескольких видов деятельности):

private void sendBroadcastMessage(String intentFilterName, int arg1, String extraKey) { 
    Intent intent = new Intent(intentFilterName); 
    if (arg1 != -1 && extraKey != null) { 
     intent.putExtra(extraKey, arg1); 
    } 
    sendBroadcast(intent); 
} 

Этот метод вызова от службы. Вы можете просто отправить данные для своей деятельности.

private void someTaskInYourService(){ 

    //For example you downloading from server 1000 files 
    for(int i = 0; i < 1000; i++) { 
     Thread.sleep(5000) // 5 seconds. Catch in try-catch block 
     sendBroadCastMessage(Events.UPDATE_DOWNLOADING_PROGRESSBAR, i,0,"up_download_progress"); 
    } 

Для получения события с данными, создать и зарегистрировать метод registerBroadcastReceivers() в вашей деятельности:

private void registerBroadcastReceivers(){ 
    broadcastReceiver = new BroadcastReceiver() { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      int arg1 = intent.getIntExtra("up_download_progress",0); 
      progressBar.setProgress(arg1); 
     } 
    }; 
    IntentFilter progressfilter = new IntentFilter(Events.UPDATE_DOWNLOADING_PROGRESS); 
    registerReceiver(broadcastReceiver,progressfilter); 

Для отправки больше данных, вы можете изменить метод sendBroadcastMessage();. Помните: вы должны регистрировать трансляции в onResume() & unregister в методах onStop()!

UPDATE

Пожалуйста, не используйте мой тип связи между деятельностью & обслуживания. Это неправильный путь. Для лучшего опыта, пожалуйста, используйте специальную LIBS, такие нам:

1) EventBus от greenrobot

2) Otto от площади Inc

P.S. Я только с помощью EventBus из greenrobot в моих проектах,

2

Это, как я implemeted деятельность-> Сервис Общение: на моей деятельности я имел

private static class MyResultReciever extends ResultReceiver { 
    /** 
    * Create a new ResultReceive to receive results. Your 
    * {@link #onReceiveResult} method will be called from the thread running 
    * <var>handler</var> if given, or from an arbitrary thread if null. 
    * 
    * @param handler 
    */ 
    public MyResultReciever(Handler handler) { 
     super(handler); 
    } 

    @Override 
    protected void onReceiveResult(int resultCode, Bundle resultData) { 
     if (resultCode == 100) { 
      //dostuff 
     } 
    } 

И тогда я использовал это, чтобы начать свою службу

protected void onCreate(Bundle savedInstanceState) { 
MyResultReciever resultReciever = new MyResultReciever(handler); 
     service = new Intent(this, MyService.class); 
     service.putExtra("receiver", resultReciever); 
     startService(service); 
} 

В моих службах я имел

public int onStartCommand(Intent intent, int flags, int startId) { 
    if (intent != null) 
     resultReceiver = intent.getParcelableExtra("receiver"); 
    return Service.START_STICKY; 
} 

Надеется, что это Помогает

0

Отличный учебник, фантастическая презентация. Аккуратно, просто, коротко и очень понятно. Хотя, notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent); способ не более.Как trante заявил here, хороший подход был бы:

private static final int NOTIFICATION_ID = 45349; 

private void showNotification() { 
    NotificationCompat.Builder builder = 
      new NotificationCompat.Builder(this) 
        .setSmallIcon(R.mipmap.ic_launcher) 
        .setContentTitle("My Notification Title") 
        .setContentText("Something interesting happened"); 

    Intent targetIntent = new Intent(this, MainActivity.class); 
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT); 
    builder.setContentIntent(contentIntent); 
    _nManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 
    _nManager.notify(NOTIFICATION_ID, builder.build()); 
} 

@Override 
public void onDestroy() { 
    super.onDestroy(); 
    if (_timer != null) {_timer.cancel();} 
    _counter=0; 
    _nManager.cancel(NOTIFICATION_ID); // Cancel the persistent notification. 
    Log.i("PlaybackService", "Service Stopped."); 
    _isRunning = false; 
} 

Проверены себя, все работает как шарм (деятельность и услуги могут отличаться от оригинала).

 Смежные вопросы

  • Нет связанных вопросов^_^