Android Activity 和 Service 通信 : how to keep the UI up-to-date

标签 android android-activity android-service android-ui android-handler

我有一个 Activity A(不是主 Activity)启动一个 Service S 在后台做一些事情,与此同时,应该做一些改变到 A 的 UI。

假设S 从 0 到 100 计数A 应该实时显示此计数。由于 S 的实际工作相当复杂且耗费 CPU,因此我不想使用 AsyncTask为此(事实上“理想情况下,AsyncTasks 应该用于短操作(最多几秒钟。)[...]”)但只是一个常规的 Service在一个新线程中开始(IntentService 也可以)。

这是我的 Activity A:

public class A extends Activity {
    private static final String TAG = "Activity";
    private TextView countTextView;    // TextView that shows the number
    Button startButton;                // Button to start the count
    BResultReceiver resultReceiver;


    /**
     * Receive the result from the Service B.
     */
    class BResultReceiver extends ResultReceiver {
        public BResultReceiver(Handler handler) {
            super(handler);
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            switch ( resultCode )  {
                case B.RESULT_CODE_COUNT:
                        String curCount = resultData.getString(B.RESULT_KEY_COUNT);
                        Log.d(TAG, "ResultReceived: " + curCount + "\n");
                        runOnUiThread( new UpdateUI(curCount) );  // NOT WORKING AFTER onResume()!!!
                   break;
            }
        }
    }


    /**
     * Runnable class to update the UI.
     */
    class UpdateUI implements Runnable {
        String updateString;

        public UpdateUI(String updateString) {
            this.updateString = updateString;
        }

        public void run() {
            countTextView.setText(updateString);
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.counter);

        countTextView = (TextView) findViewById(R.id.countTextView);
        startButton = (Button) findViewById(R.id.startButton);

        resultReceiver = new BResultReceiver(null);
    }


    public void startCounting(View view) {
        startButton.setEnabled(false);

        //Start the B Service:
        Intent intent = new Intent(this, B.class);
        intent.putExtra("receiver", resultReceiver);
        startService(intent);
    }
}

这是我的服务B:

public class B extends Service {
    private static final String TAG = "Service";
    private Looper serviceLooper;
    private ServiceHandler serviceHandler;
    private ResultReceiver resultReceiver;
    private Integer count;

    static final int RESULT_CODE_COUNT = 100;
    static final String RESULT_KEY_COUNT = "Count";


    /**
     * Handler that receives messages from the thread.
     */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            while ( count < 100 ) {
                count++;
                //Sleep...
                sendMessageToActivity(RESULT_CODE_COUNT, RESULT_KEY_COUNT, count.toString());
            }

            //Stop the service (using the startId to avoid stopping the service in the middle of handling another job...):
            stopSelf(msg.arg1);
        }
    }


    @Override
    public void onCreate() {
        //Start up the thread running the service:
        HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        this.count = 0;

        //Get the HandlerThread's Looper and use it for our Handler
        serviceLooper = thread.getLooper();
        serviceHandler = new ServiceHandler(serviceLooper);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.resultReceiver = intent.getParcelableExtra("receiver");

        //For each start request, send a message to start a job and deliver the start ID so we know which request we're stopping when we finish the job:
        Message msg = serviceHandler.obtainMessage();
        msg.arg1 = startId;
        serviceHandler.sendMessage(msg);

        //If we get killed, after returning from here, restart:
        return START_REDELIVER_INTENT;
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    /**
     * Send a message from to the activity.
     */
    protected void sendMessageToActivity(Integer code, String name, String text) {
        Bundle bundle = new Bundle();
        bundle.putString(name, text);

        //Send the message:
        resultReceiver.send(code, bundle);
    }
}

一切正常,但如果我点击后退按钮(或主页按钮),然后我重新打开 Activity A,然后是 A 的 UI不再更新(它仅显示 A 的初始配置 - 即“startButton”仍然可点击并且未显示计数 - 似乎 runOnUiThread(...) 不是工作了)。但是,服务 B 仍在后台运行,我可以看到正确的计数已传递给 Log.d(...) 中的 Activity A。最后,如果我再次点击“startButton”,计数不会从头开始 (0),而是从 B 到达的地方开始(我已经通过在通知栏中显示它来仔细检查)。

我该如何解决这个问题?我希望,当我重新打开 Activity A 时,它会自动继续接收和更新来自 Service B 的数据。或者,换句话说,Service 使 Activity A 的 UI 保持最新。

请给我一些提示、链接或一段代码。谢谢!

最佳答案

当您点击后退按钮时,您的 Activity 将被销毁。当您再次启动 Activity 时,您将获得一个新的 Activity。当您旋转设备时也会发生这种情况。这是 Android lifecycle event

Activity 不适合仅用于显示内容/控制内容的繁重业务逻辑。 您要做的是创建一个简单的 MVC,Model View Controller . View (Activity) 应该只用于显示结果和控制事件流。

Service 可以保存 count 的数组,当您的 Activity 启动时,它会 onBind()您正在运行的服务(或者如果未运行,将启动 Service 因为您绑定(bind)到它)让 Activity(View) 获取结果数组并显示它。这个简单的设置不包括 (M)Model 业务逻辑。

更新
接下来阅读一下这是 Android 官方文档和完美的开始,因为它可以满足您的要求。正如您在 onStart() 示例中看到的,Activity 与服务建立连接,而在 onStop() 中,连接被删除。在 onStop() 之后建立连接是没有意义的。就像你要求的那样。我会采用此设置,不要让 Service 持续发送数据,因为这会耗尽资源,而且 Activity 并不总是在监听,因为它会在后台停止。< br/> Here's an activity that binds to LocalService and calls getRandomNumber() when a button is clicked:

关于Android Activity 和 Service 通信 : how to keep the UI up-to-date,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18746074/

相关文章:

android - 在bluedroid Android HAL下是否仍然可以获得配对或连接的Android设备的RSSI?

android - 检查设备上是否存在通知 LED 以及可用的颜色

android - 如何移动 editText 光标?

java - 开发app每次都强制关闭

java - CordovaActivity 上的 onBackpressed 未调用

java - 使用 EditText 进行计算

android - Paho MQTT Android 服务唤醒 Activity

android - 在 onLoadFinished 方法中启动 Activity

android - Android 服务内的计时器

android - 在绑定(bind)之前启动服务