Unten finden Sie meine ursprüngliche Antwort - dieses Muster hat gut funktioniert, aber seit kurzem verwende ich einen anderen Ansatz für die Service- / Aktivitätskommunikation:
- Verwenden Sie einen gebundenen Dienst der es der Aktivität ermöglicht, einen direkten Verweis auf den Dienst abzurufen, sodass direkte Anrufe auf ihn möglich sind, anstatt Absichten zu verwenden.
Verwenden Sie RxJava, um asynchrone Operationen auszuführen.
Wenn der Dienst Hintergrundvorgänge fortsetzen muss, auch wenn keine Aktivität ausgeführt wird, starten Sie den Dienst auch über die Anwendungsklasse, damit er nicht gestoppt wird, wenn er nicht gebunden ist.
Die Vorteile, die ich bei diesem Ansatz gegenüber der startService () / LocalBroadcast-Technik gefunden habe, sind:
- Für die Implementierung von Parcelable sind keine Datenobjekte erforderlich. Dies ist für mich besonders wichtig, da ich jetzt Code zwischen Android und iOS (mithilfe von RoboVM) freigebe.
- RxJava bietet eine vordefinierte (und plattformübergreifende) Planung sowie eine einfache Zusammenstellung von sequentiellen asynchronen Vorgängen.
- Dies sollte effizienter sein als die Verwendung eines LocalBroadcasts, obwohl der Aufwand für die Verwendung von RxJava diesen überwiegen kann.
Ein Beispielcode. Zuerst der Service:
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);
}
}
Und eine Aktivität, die an den Dienst gebunden ist und Aktualisierungen der Druckhöhe erhält:
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);
}
}
Das Layout für diese Aktivität lautet:
<?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>
Wenn der Dienst im Hintergrund ohne gebundene Aktivität ausgeführt werden muss, kann er auch über die Anwendungsklasse gestartet OnCreate()werden Context#startService().
Meine ursprüngliche Antwort (von 2013):
In Ihrem Dienst: (Verwenden von COPA als Dienst im folgenden Beispiel).
Verwenden Sie einen LocalBroadCastManager. Richten Sie in onCreate Ihres Dienstes den Sender ein:
broadcaster = LocalBroadcastManager.getInstance(this);
Wenn Sie die Benutzeroberfläche über etwas informieren möchten:
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);
}
In Ihrer Aktivität:
Erstellen Sie einen Listener auf 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.
}
};
}
und registrieren Sie es in 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();
}