3

我有一个Activity A(不是主 Activity),它启动一个Service S,它在后台做一些事情,同时,应该对 A 的 UI 进行一些更改。

假设S 从 0 计数到 100并且A 应该在 Real-Time 中显示这个计数。由于 S 的实际工作相当复杂且占用 CPU 资源,因此我不想为其使用AsyncTask(事实上,“AsyncTasks 应该理想地用于短操作(最多几秒钟。)[...]” ) 但只是在一个新线程中启动了一个常规服务(一个IntentService也可以)。

这是我的活动 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(...)中的活动A。最后,如果我再次单击“startButton”,则计数不会从开头 (0) 开始,而是从 B 到达的位置开始(我通过在通知栏中显示它来仔细检查它)。

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

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

4

1 回答 1

3

当您单击后退按钮时,您将Activity被销毁。当你重新开始时,Activity你会得到一个新的Activity. 旋转设备时也会发生这种情况。这是Android 生命周期事件

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

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

更新
稍加阅读,这是 Android 官方文档和完美的开始,因为它可以满足您的要求。正如您在示例中看到onStart()的 Activity 与服务建立连接并在onStop()连接被删除。在 on 之后建立连接是没有意义的onStop()。就像你要求的那样。我会采用这种设置,不要让Service连续发送数据,因为这会消耗资源并且Activity并不总是在监听,因为它会在后台停止。
这是一个绑定到 LocalService 并在单击按钮时调用 getRandomNumber() 的活动:

于 2013-09-11T16:52:42.973 回答