Lihat di bawah untuk jawaban asli saya - pola itu telah bekerja dengan baik, tetapi baru-baru ini saya mulai menggunakan pendekatan yang berbeda untuk komunikasi Layanan / Aktivitas:
- Gunakan layanan terikat yang memungkinkan Aktivitas mendapatkan referensi langsung ke Layanan, sehingga memungkinkan panggilan langsung padanya, daripada menggunakan Intent.
Gunakan RxJava untuk menjalankan operasi asinkron.
Jika Layanan perlu melanjutkan operasi latar belakang meskipun tidak ada Aktivitas yang berjalan, mulai juga layanan dari kelas Aplikasi sehingga tidak dihentikan saat tidak terikat.
Keuntungan yang saya temukan dalam pendekatan ini dibandingkan dengan teknik startService () / LocalBroadcast adalah
- Tidak perlu objek data untuk mengimplementasikan Parcelable - ini sangat penting bagi saya karena saya sekarang berbagi kode antara Android dan iOS (menggunakan RoboVM)
- RxJava menyediakan penjadwalan terekam (dan lintas platform), dan komposisi mudah dari operasi asinkron berurutan.
- Ini seharusnya lebih efisien daripada menggunakan LocalBroadcast, meskipun biaya penggunaan RxJava mungkin lebih besar daripada itu.
Beberapa kode contoh. Pertama layanannya:
public class AndroidBmService extends Service implements BmService {
private static final int PRESSURE_RATE = 500000; // microseconds between pressure updates
private SensorManager sensorManager;
private SensorEventListener pressureListener;
private ObservableEmitter<Float> pressureObserver;
private Observable<Float> pressureObservable;
public class LocalBinder extends Binder {
public AndroidBmService getService() {
return AndroidBmService.this;
}
}
private IBinder binder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
logMsg("Service bound");
return binder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
if(pressureSensor != null)
sensorManager.registerListener(pressureListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if(pressureObserver != null) {
float lastPressure = event.values[0];
float lastPressureAltitude = (float)((1 - Math.pow(lastPressure / 1013.25, 0.190284)) * 145366.45);
pressureObserver.onNext(lastPressureAltitude);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}, pressureSensor, PRESSURE_RATE);
}
@Override
public Observable<Float> observePressure() {
if(pressureObservable == null) {
pressureObservable = Observable.create(emitter -> pressureObserver = emitter);
pressureObservable = pressureObservable.share();
}
return pressureObservable;
}
@Override
public void onDestroy() {
if(pressureListener != null)
sensorManager.unregisterListener(pressureListener);
}
}
Dan Aktivitas yang mengikat ke layanan dan menerima pembaruan ketinggian tekanan:
public class TestActivity extends AppCompatActivity {
private ContentTestBinding binding;
private ServiceConnection serviceConnection;
private AndroidBmService service;
private Disposable disposable;
@Override
protected void onDestroy() {
if(disposable != null)
disposable.dispose();
unbindService(serviceConnection);
super.onDestroy();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.content_test);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
logMsg("BlueMAX service bound");
service = ((AndroidBmService.LocalBinder)iBinder).getService();
disposable = service.observePressure()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(altitude ->
binding.altitude.setText(
String.format(Locale.US,
"Pressure Altitude %d feet",
altitude.intValue())));
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
logMsg("Service disconnected");
}
};
bindService(new Intent(
this, AndroidBmService.class),
serviceConnection, BIND_AUTO_CREATE);
}
}
Tata letak untuk Aktivitas ini adalah:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.controlj.mfgtest.TestActivity">
<TextView
tools:text="Pressure"
android:id="@+id/altitude"
android:gravity="center_horizontal"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
Jika layanan perlu dijalankan di latar belakang tanpa Aktivitas terikat, layanan dapat dimulai dari kelas Aplikasi juga dalam OnCreate()penggunaan Context#startService().
Jawaban Asli Saya (dari 2013):
Dalam layanan Anda: (menggunakan COPA sebagai layanan pada contoh di bawah).
Gunakan LocalBroadCastManager. Di layanan onCreate Anda, siapkan penyiar:
broadcaster = LocalBroadcastManager.getInstance(this);
Saat Anda ingin memberi tahu UI tentang sesuatu:
static final public String COPA_RESULT = "com.controlj.copame.backend.COPAService.REQUEST_PROCESSED";
static final public String COPA_MESSAGE = "com.controlj.copame.backend.COPAService.COPA_MSG";
public void sendResult(String message) {
Intent intent = new Intent(COPA_RESULT);
if(message != null)
intent.putExtra(COPA_MESSAGE, message);
broadcaster.sendBroadcast(intent);
}
Dalam Aktivitas Anda:
Buat pendengar di onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.copa);
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(COPAService.COPA_MESSAGE);
// do something here.
}
};
}
dan daftarkan di onStart:
@Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((receiver),
new IntentFilter(COPAService.COPA_RESULT)
);
}
@Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
super.onStop();
}