Android Bound Service
Oct. 17th, 2016 03:58 pmЯ сейчас занят пинанием хуев в рабочее время повышением квалификации и исследовательской работой.
Набрел на ряд решений которые мне кажутся очень удачными.
Вот например интересный вариант реализации boundService на RX для Андроид.
Решение из этого широко известного гайда обладает одним большим и рядом мелких недостатков. Большой в том, что в ходе работы активности мы ничего не можем сказать о подключении сервиса. И нам приходится использовать безумное количество хендлеров, либо ставить проверку перед каждым вызовом. Кроме того это решение весьма тяжеловесно и негибко.
Вот более интересный способ:
public abstract class BoundService extends Service { private IBinder binder = new ServiceBinder(this); @Override public IBinder onBind(Intent intent) { return binder; } }
public class ServiceBinder extends Binder { private BoundService service; public ServiceBinder(BoundService service) { this.service = service; } public BoundService getService() { return service; } }
public class ServiceConnector <T extends BoundService> { public abstract class LocalBinder extends Binder { public abstract Service getService(); } private BehaviorSubject<T> serviceSubject = BehaviorSubject.create(); private BoundServiceConnection boundServiceConnection = new BoundServiceConnection(); public ServiceConnector(Context context, Class<T> tClass) { Intent orderServiceIntent = new Intent(context, tClass); context.bindService(orderServiceIntent, boundServiceConnection, Context.BIND_AUTO_CREATE); serviceSubject.subscribe(s -> { }, t -> { }, () -> context.unbindService(boundServiceConnection)); } private class BoundServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder binder) { Service service = ((ServiceBinder) binder).getService(); serviceSubject.onNext((T) service); // May produce Class cast Exception } @Override public void onServiceDisconnected(ComponentName name) { serviceSubject.onCompleted(); } } public Observable<T> getService() { return serviceSubject.first(); } public BehaviorSubject<T> getBehavior() { return serviceSubject; } }
Чтобы все собралось добавляем в Градл скрипт лямбды и саму RX
apply plugin: 'me.tatarka.retrolambda' buildscript { dependencies { classpath 'me.tatarka:gradle-retrolambda:3.2.3' } } android { dependencies { compile 'io.reactivex:rxjava:1.1.6' compile group: 'io.reactivex', name: 'rxandroid', version: '1.2.1' } }
В первых 2 классах нет ничего необычного. Они цельнотянуты со стека. Третий класс реализует подключение к сервису на RX. Идея проста: Вместо лисенеров "onServiceConnected" мы используем бехевиор, который выполняет за нас всю нудную работу отслеживания состояний.
Как это использовать?
Создадим новый сервис:
public class SimpleService extends BoundService { public static class Connector extends ServiceConnector<SimpleService> { public Connector(Context context) { super(context, SimpleService.class); } } }
В активности подключаем его:
private Observable<SimpleService> simpleService; public void onCreate(Bundle savedInstanceState) { ... simpleService = new SimpleService.Connector(this).getService(); }
И все. Можно работать с сервисом:
simpleService.subscribe(service -> { Log.v(TAG, service.getPackageCodePath()); // например });
Все RX фишки здесь прекрасно работают. Например:
simpleService.map(SimpleService::getCloningDataType).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> Log.v(TAG,data));
Как видно это гораздо проще чем создавать ServiceConnection а потом отслеживать все его состояния.