Xem bên dưới để biết câu trả lời ban đầu của tôi - mẫu đó đã hoạt động tốt, nhưng gần đây tôi đã bắt đầu sử dụng một cách tiếp cận khác để giao tiếp Dịch vụ / Hoạt động:
- Sử dụng một dịch vụ ràng buộc cho phép Hoạt động tham chiếu trực tiếp đến Dịch vụ, do đó cho phép các cuộc gọi trực tiếp trên đó, thay vì sử dụng Ý định.
Sử dụng RxJava để thực thi các hoạt động không đồng bộ.
Nếu Dịch vụ cần tiếp tục các hoạt động nền ngay cả khi không có Hoạt động nào đang chạy, hãy khởi động dịch vụ từ lớp Ứng dụng để nó không bị dừng khi không được liên kết.
Những lợi thế tôi đã tìm thấy trong cách tiếp cận này so với kỹ thuật startService () / LocalBroadcast là
- Không cần đối tượng dữ liệu triển khai Parcelable - điều này đặc biệt quan trọng đối với tôi vì tôi hiện đang chia sẻ mã giữa Android và iOS (sử dụng RoboVM)
- RxJava cung cấp tính năng lập lịch đóng hộp (và đa nền tảng) và dễ dàng cấu thành các hoạt động không đồng bộ tuần tự.
- Điều này sẽ hiệu quả hơn so với sử dụng LocalBroadcast, mặc dù chi phí sử dụng RxJava có thể lớn hơn điều đó.
Một số mã ví dụ. Đầu tiên là dịch vụ:
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);
}
}
Và một Hoạt động liên kết với dịch vụ và nhận cập nhật độ cao áp suất:
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);
}
}
Bố cục cho Hoạt động này là:
<?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>
Nếu dịch vụ cần chạy ở chế độ nền mà không có Hoạt động bị ràng buộc, nó có thể được khởi động từ lớp Ứng dụng cũng như trong quá trình OnCreate()sử dụng Context#startService().
Câu trả lời gốc của tôi (từ năm 2013):
Trong dịch vụ của bạn: (sử dụng COPA làm dịch vụ trong ví dụ bên dưới).
Sử dụng LocalBroadCastManager. Trong onCreate của dịch vụ của bạn, hãy thiết lập chương trình phát sóng:
broadcaster = LocalBroadcastManager.getInstance(this);
Khi bạn muốn thông báo cho UI về điều gì đó:
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);
}
Trong Hoạt động của bạn:
Tạo trình nghe trên 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.
}
};
}
và đăng ký nó trong 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();
}