1. ホーム
  2. android

[解決済み] サービスからAndroidのアクティビティUIを更新する

2022-09-15 08:13:30

質問

常に新しいタスクがあるかどうかをチェックするサービスがあります。新しいタスクがある場合、その情報を表示するためにアクティビティUIを更新したいと思います。 私は見つけた https://github.com/commonsguy/cw-andtutorials/tree/master/18-LocalService/ この例を見つけました。これは良いアプローチですか?他の例は?

ありがとうございます。

どのように解決するのですか?

私の元の答えは以下をご覧ください。そのパターンはうまくいっていましたが、最近、私はService/Activityのコミュニケーションに別のアプローチを使い始めました。

  • サービス/アクティビティ間の通信に バウンドサービス を使うことで、アクティビティが直接 アクティビティがサービスに対する直接の参照を取得できるようにします。 インテントを使用するのではなく、直接呼び出すことができます。
  • RxJavaを使用して非同期処理を実行します。

  • アクティビティが実行されていないときでも、サービスがバックグラウンドのオペレーションを継続する必要がある場合。 アクティビティが実行されていないときでもバックグラウンドのオペレーションを継続する必要がある場合、アプリケーションクラスからサービスを開始します。 クラスからサービスを開始します。

startService()/LocalBroadcastの手法と比較して、このアプローチで私が見つけた利点は以下の通りです。

  • Parcelable を実装するためのデータ オブジェクトが不要 - これは、現在 Android と iOS の間でコードを共有している私にとっては特に重要です (RoboVM を使用)。
  • RxJava は定型的な(そしてクロスプラットフォームな)スケジューリングと、シーケンシャルな非同期操作の容易な合成を提供します。
  • これは、RxJava を使用するオーバーヘッドがそれを上回るかもしれませんが、LocalBroadcast を使用するよりも効率的であるべきです。

いくつかのサンプルコードです。まずサービスです。

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);
    }
} 

そして、サービスにバインドして気圧高度更新を受信するActivity。

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);
    }
}

このActivityのレイアウトは

<?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>

もしサービスが Activity に束縛されることなくバックグラウンドで実行される必要がある場合、Application クラスから同様に OnCreate() を使って Context#startService() .


私のオリジナル回答(2013年から)。

あなたのサービスでは。 (下記の例ではCOPAをサービスにしています)。

LocalBroadCastManagerを使用します。サービスのonCreateで、ブロードキャスターを設定します。

broadcaster = LocalBroadcastManager.getInstance(this);

UIに何かを通知したいとき。

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);
}

あなたのアクティビティで

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.
        }
    };
}

で、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();
}