В этом уроке:

- обмен данными в биндинге

 

Подключаться к сервису мы теперь умеем. В этом уроке попробуем обменяться с ним данными.

В сервисе метод onBind возвращает объект, наследующий интерфейс IBinder. Проще всего использовать для этого объект Binder и расширить его необходимыми нам методами. Т.е. создаем свой класс MyBinder с предком Binder и рисуем в нем свои методы.

При биндинге, в методе onServiceConnected мы получаем объект Binder. Мы можем привести его к типу MyBinder (из сервиса) и вызывать его методы, а реализация будет срабатывать в сервисе, где мы описывали этот класс.

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

Нарисуем пример. У нас будет сервис, который с определенным интервалом будет что-то выводить в лог. Мы к нему подключимся, и будем повышать или понижать интервал и получать новое значение интервала обратно.

 

Создадим проект:

Project name: P0981_ServiceBindingLocal
Build Target: Android 2.3.3
Application name: ServiceBindingLocal
Package name: ru.startandroid.develop.p0981servicebindinglocal
Create Activity: MainActivity

 

Добавим в strings.xml строки:

<string name="start">Start</string>
<string name="up">Up</string>
<string name="down">Down</string>

 

Экран main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:orientation="vertical">
	<TextView
		android:id="@+id/tvInterval"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="">
	</TextView>
	<Button
		android:id="@+id/btnStart"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClickStart"
		android:text="@string/start">
	</Button>
	<Button
		android:id="@+id/btnUp"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClickUp"
		android:text="@string/up">
	</Button>
	<Button
		android:id="@+id/btnDown"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClickDown"
		android:text="@string/down">
	</Button>
</LinearLayout>

Кнопки для запуска сервиса, повышения интервала и понижения интервала.

 

Создаем сервис MyService.java:

package ru.startandroid.develop.p0981servicebindinglocal;

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

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {

  final String LOG_TAG = "myLogs";

  MyBinder binder = new MyBinder();

  Timer timer;
  TimerTask tTask;
  long interval = 1000;

  public void onCreate() {
    super.onCreate();
    Log.d(LOG_TAG, "MyService onCreate");
    timer = new Timer();
    schedule();
  }

  void schedule() {
    if (tTask != null) tTask.cancel();
    if (interval > 0) {
      tTask = new TimerTask() {
        public void run() {
          Log.d(LOG_TAG, "run");
        }
      };
      timer.schedule(tTask, 1000, interval);
    }
  }

  long upInterval(long gap) {
    interval = interval + gap;
    schedule();
    return interval;
  }

  long downInterval(long gap) {
    interval = interval - gap;
    if (interval < 0) interval = 0;
    schedule();
    return interval;
  }

  public IBinder onBind(Intent arg0) {
    Log.d(LOG_TAG, "MyService onBind");
    return binder;
  }

  class MyBinder extends Binder {
    MyService getService() {
      return MyService.this;
    }
  }
}

Здесь мы используем таймер – Timer. Он позволяет повторять какое-либо действие через заданный промежуток времени. Кратко распишу принцип действия. TimerTask – это задача, которую Timer будет периодически выполнять. В методе run – кодим действия этой задачи.  И далее для объекта Timer вызываем метод schedule, в который передаем задачу TimerTask, время через которое начнется выполнение, и период повтора. Чтобы отменить выполнение задачи, необходимо вызвать метод cancel для TimerTask. Отмененную задачу нельзя больше запланировать, и если снова надо ее включить – необходимо создать новый экземпляр TimerTask и скормить его таймеру.

Итак, в методе onCreate мы создаем таймер и выполняем метод schedule, в котором стартует задача.

Метод schedule проверяет, что задача уже создана и отменяет ее. Далее планирует новую, с отложенным на 1000 мс запуском и периодом = interval. Т.е. можно сказать, что этот метод перезапускает задачу с использованием текущего интервала повтора (interval), а если задача еще не создана, то создает ее. Сама задача просто выводит в лог текст run. Если interval = 0, то ничего не делаем.

Метод upInterval получает на вход значение, увеличивает interval на это значение и перезапускает задачу. Соответственно задача после этого будет повторяться реже.

Метод downInterval получает на вход значение, уменьшает interval на это значение (но так, чтоб не меньше 0) и перезапускает задачу. Соответственно задача после этого будет повторяться чаще.

onBind возвращает binder. Это объект класса MyBinder.

MyBinder расширяет стандартный Binder, мы добавляем в него один метод getService. Этот метод возвращает наш сервис MyService.

Т.е. в подключаемом Activity, в методе onServiceConnected мы получим объект, который идет на выход метода onBind. Далее преобразуем его к типу MyBinder, вызовем getService и вуаля - у нас в Activity будет ссылка на объект-сервис MyService.

 

Кодим MainActivity.java:

package ru.startandroid.develop.p0981servicebindinglocal;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";

  boolean bound = false;
  ServiceConnection sConn;
  Intent intent;
  MyService myService;
  TextView tvInterval;
  long interval;
  

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tvInterval = (TextView) findViewById(R.id.tvInterval);
    intent = new Intent(this, MyService.class);
    sConn = new ServiceConnection() {

      public void onServiceConnected(ComponentName name, IBinder binder) {
        Log.d(LOG_TAG, "MainActivity onServiceConnected");
        myService = ((MyService.MyBinder) binder).getService(); 
        bound = true;
      }

      public void onServiceDisconnected(ComponentName name) {
        Log.d(LOG_TAG, "MainActivity onServiceDisconnected");
        bound = false;
      }
    }; 
  }
  
  @Override
  protected void onStart() {
    super.onStart();
    bindService(intent, sConn, 0);
  }
  
  @Override
  protected void onStop() {
    super.onStop();
    if (!bound) return;
    unbindService(sConn);
    bound = false;
  }

  public void onClickStart(View v) {
    startService(intent);
  }
  
  public void onClickUp(View v) {
    if (!bound) return;
    interval = myService.upInterval(500);
    tvInterval.setText("interval = " + interval);
  }
  
  public void onClickDown(View v) {
    if (!bound) return;
    interval = myService.downInterval(500);
    tvInterval.setText("interval = " + interval);
  }
}

В onCreate создаем Intent для доступа к сервису и ServiceConnection. В методе onServiceConnected мы берем binder, преобразуем его к MyService.MyBinder, вызываем метод getService и получаем наш сервис MyService. Теперь мы можем выполнять методы сервиса. Нас интересуют методы увеличения и понижения интервала.

В методах onStart и onStop мы соответственно подключаемся к сервису и отключаемся.

В onClickStart запускаем сервис.

В onClickUp и onClickDown проверяем, что соединение с сервисом есть, и вызываем соответствующие методы сервиса для увеличения или понижения интервала. В качестве дельты изменения интервала передаем 500. В ответ эти методы передают нам новое значение интервала, и мы выводим его в TextView.

 

Все сохраним и запускаем.

Жмем Start – запускаем сервис. В логах:

MyService onCreate
MyService onBind
MainActivity onServiceConnected

Создается сервис, и мы к нему подключаемся. Биндинг был ранее вызван в onStart и, когда сервис стартанул, мы автоматически подключились.  В прошлом уроке мы разбирали такую ситуацию, когда биндинг идет к незапущенному пока сервису.


run
run
run

run

Далее с интервалом в одну секунду в лог падает текст run.

Попробуем увеличить интервал. Жмем Up.

На экране появилось текущее значение интервала. И по времени записей в логах видим, что именно с этим интервалом срабатывает задача (вывод текста run в лог) .

Далее можно нажимать Up и Down и наблюдать, как меняется интервал выполнения задачи. Таким образом, мы из Activity подключились к сервису и управляем его работой.

 

В начале урока я отметил, что все это будет работать, если приложение и сервис в одном процессе. Если же они в разных процессах, то используется немного более сложный вариант для обмена данными. Но это гораздо менее используемый случай, поэтому сейчас я про него не рассказываю. Но попозже об этом обязательно будет статья.

На следующем уроке:

- шлем уведомление из сервиса


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal