来自服务的Android更新活动UI

来自服务的Android更新活动UI,android,Android,我有一个服务,它一直在检查新任务。如果有新任务,我想刷新活动UI以显示该信息。 我确实找到了这个例子。这是一个好方法吗?还有其他例子吗 谢谢。我建议退房,这是一款专为Android定制的事件总线。您的Activity/UI可以从服务侦听总线上发布的事件,并将自身与后端断开连接。我将使用a来实现这一点,并通过在Activity中实现侦听器与之通信。因此,如果您的应用程序实现了myServiceListener,您可以在绑定后将其注册为服务中的侦听器,从绑定的服务中调用listener.onUpda

我有一个服务,它一直在检查新任务。如果有新任务,我想刷新活动UI以显示该信息。 我确实找到了这个例子。这是一个好方法吗?还有其他例子吗


谢谢。

我建议退房,这是一款专为Android定制的事件总线。您的Activity/UI可以从服务侦听总线上发布的事件,并将自身与后端断开连接。

我将使用a来实现这一点,并通过在Activity中实现侦听器与之通信。因此,如果您的应用程序实现了myServiceListener,您可以在绑定后将其注册为服务中的侦听器,从绑定的服务中调用listener.onUpdateUI并在其中更新您的UI

下面是我最初的答案-这种模式运作良好,但最近我开始使用不同的服务/活动沟通方法:

  • 使用可使活动获得直接 引用服务,从而允许直接调用它,而不是 而不是使用意图
  • 使用RxJava执行异步操作

  • 如果服务需要继续后台操作,即使没有 活动正在运行,同时从应用程序启动服务 类,使其在解除绑定时不会停止

与startService()/LocalBroadcast技术相比,我发现这种方法的优点是

  • 不需要数据对象来实现Parcelable——这对我来说特别重要,因为我现在正在Android和iOS之间共享代码(使用RoboVM)
  • RxJava提供了固定的(和跨平台的)调度,以及顺序异步操作的简单组合
  • 这应该比使用LocalBroadcast更有效,尽管使用RxJava的开销可能会超过这一点
一些示例代码。首先是服务:

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);
    }
} 
此活动的布局为:

<?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>
当您要通知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();
}

Clyde的解决方案是可行的,但它是一个广播,我敢肯定这比直接调用方法效率要低。我可能弄错了,但我认为广播更多的是用于应用程序间的通信

我假设您已经知道如何将服务与活动绑定。 我执行类似于以下代码的操作来处理此类问题:

class MyService extends Service {
    MyFragment mMyFragment = null;
    MyFragment mMyOtherFragment = null;

    private void networkLoop() {
        ...

        //received new data for list.
        if(myFragment != null)
            myFragment.updateList();
        }

        ...

        //received new data for textView
        if(myFragment !=null)
            myFragment.updateText();

        ...

        //received new data for textView
        if(myOtherFragment !=null)
            myOtherFragment.updateSomething();

        ...
    }
}


class MyFragment extends Fragment {

    public void onResume() {
        super.onResume()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyFragment=this;
    }

    public void onPause() {
        super.onPause()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyFragment=null;
    }

    public void updateList() {
        runOnUiThread(new Runnable() {
            public void run() {
                //Update the list.
            }
        });
    }

    public void updateText() {
       //as above
    }
}

class MyOtherFragment extends Fragment {
             public void onResume() {
        super.onResume()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyOtherFragment=this;
    }

    public void onPause() {
        super.onPause()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyOtherFragment=null;
    }

    public void updateSomething() {//etc... }
}

我省略了线程安全性的部分,这是非常重要的。在检查、使用或更改服务上的片段引用时,请确保使用锁或类似的东西。

对我来说,最简单的解决方案是发送广播,在activity oncreate中,我注册并定义了如下广播(updateUIReciver定义为类实例):

通过该服务,您可以发送如下意图:

Intent local = new Intent();

local.setAction("com.hello.action");

this.sendBroadcast(local);
不要忘记在销毁时在“活动”中注销恢复:

unregisterReceiver(updateUIReciver);

我的解决方案可能不是最干净的,但应该不会有任何问题。 逻辑只是创建一个静态变量,将数据存储在
服务上,并在
活动上每秒更新视图

假设您的
服务上有一个
字符串
,您希望将其发送到
活动
上的
文本视图
。应该是这样的

您的服务:

public class TestService extends Service {
    public static String myString = "";
    // Do some stuff with myString
您的活动:

public class TestActivity extends Activity {
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tv = new TextView(this);
        setContentView(tv);
        update();
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    while (!isInterrupted()) {
                        Thread.sleep(1000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                update();
                            }
                        });
                    }
                } catch (InterruptedException ignored) {}
            }
        };
        t.start();
        startService(new Intent(this, TestService.class));
    }
    private void update() {
        // update your interface here
        tv.setText(TestService.myString);
    }
}

这看起来正是我要找的。让我试试看。谢谢。小心不要泄露你的活动。因为您的活动可能会在轮换时被销毁和重新创建。您好,请查看此链接。我已经为此共享了一个示例代码。将链接放在这里,假设某人将来可能会觉得有帮助+1这是一个可行的解决方案,但就我而言,我确实需要该服务保持运行,即使所有活动都与之解除绑定(例如,用户关闭应用程序,过早终止操作系统),我别无选择,只能使用广播接收器。请看我的回答。使用监听器轻松定义类之间通信的接口@user200658是的,onStart()和onStop()是活动生命周期的一部分-请参阅小注释-您缺少COPA_消息的定义。谢谢:)如果您询问COPA_结果,它只不过是用户生成的静态变量。“COPA”是他的服务名称,因此您可以完全用自己的名称替换它。在我的例子中,它是静态的最终公共字符串MP_Result=“com.widefide.musicplayer.MusicService.REQUEST_PROCESSED”;如果用户导航离开活动,然后在服务完成后返回活动,则UI不会更新。处理这个问题的最佳方法是什么?@savaging您需要向服务发送
onStart()
onResume()方法中适当的请求。通常,如果活动要求服务执行某些操作,但在收到结果之前退出,则可以合理地假设不再需要该结果。类似地,在启动活动时,它应该假设服务没有处理任何未完成的请求。是为应用程序内的通信而设计的,因此非常高效。当服务的客户端数量有限且服务不需要独立运行时,绑定服务方法是可以的。本地广播方法允许服务更有效地与其客户端分离,并使线程安全成为一个非问题。这是一个更好的解决方案,但如果在应用程序中使用LocalBroadcastManager,则最好使用LocalBroadcastManager,这将更有效。之后的下一步。sendBroadcast(local);在服务中?@angel没有下一步,在intent的附加功能中,只需添加您想要的ui更新,这就是它永远不会在服务中或任何其他应用程序中执行静态变量操作activity@MurtazaKhursheedHussain-你能详细说明一下吗?静态成员是
unregisterReceiver(updateUIReciver);
Callback from service to activity to update UI.
ResultReceiver receiver = new ResultReceiver(new Handler()) {
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        //process results or update UI
    }
}

Intent instructionServiceIntent = new Intent(context, InstructionService.class);
instructionServiceIntent.putExtra("receiver", receiver);
context.startService(instructionServiceIntent);
public class TestService extends Service {
    public static String myString = "";
    // Do some stuff with myString
public class TestActivity extends Activity {
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tv = new TextView(this);
        setContentView(tv);
        update();
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    while (!isInterrupted()) {
                        Thread.sleep(1000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                update();
                            }
                        });
                    }
                } catch (InterruptedException ignored) {}
            }
        };
        t.start();
        startService(new Intent(this, TestService.class));
    }
    private void update() {
        // update your interface here
        tv.setText(TestService.myString);
    }
}