我有一个 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/