我目前正在开发一款使用蓝牙,GPS并将数据上传到远程服务器的应用程序。我有一个简单的按钮,它启动一系列事件和线程,以便让一切工作在一起。
我现在在屏幕上添加TextView
组件,向用户显示正在发生的更详细的过程。 GPS正在运行吗?我的蓝牙设备是否已连接?等等。这个过程可能需要10秒钟,这就是为什么我要添加更多关于后台发生的事情的信息。
但是,当我单击我的按钮时,只会显示最后一个更改。我想TextView
组件是在Onclick
之后呈现的?
一个例子:
@Override
public void onClick(View view) {
textView.setText(R.string.launching_text);
// start a thread
textView.setText(R.string.start_new_thread);
// start another thread
textView.setText(R.string.almost_there);
// start last thread
textView.setText(R.string.done);
}
想象一下这个过程大约需要10秒钟。它看起来像应用程序“冻结”,但直到OnClick
完成后才能看到更改。
如何在OnClick
活动期间实时显示我的信息?是否有更好的做法?是否有可能采取某种方式异步推动TextView
变化?
我建议你先检查一下this Android Performance Patterns video,看看你可以使用的一些选项。我还建议不要在生命周期环境中执行多线程(例如Activities
,Fragments
),因为这只是在寻找麻烦。
在你的onClick
例子中,R.string.done
可以(并且很可能会)在第一个线程完成其工作之前显示。我假设那不是你想要的。
我不知道你正在处理的问题,你正在使用的工具或你正在关注的架构,所以这里有一个稍微通用的方法来使它工作。你的Thread
实现中的每个onClick
都具有各种状态。您可以使用简单的抽象在代码中表示:
class Holder {
@StringRes int status;
Runnable runnable;
Holder(@StringRes int status, @NonNull Runnable runnable) {
this.status = status;
this.runnable = runnable;
}
}
注意使用Runnable
而不是Thread
。
你也在按顺序执行事情。您可以使用简单的List
或Queue
在代码中表示这一点,从而提供流畅,富有表现力的API,例如:
class StatusRunnableBuilder {
private final WeakReference<TextView> viewRef;
private final Queue<Holder> queue;
@StringRes private int finalStatus;
StatusRunnableBuilder(@NonNull TextView view) {
viewRef = new WeakReference<>(view);
queue = new ArrayDeque<>();
}
StatusRunnableBuilder addStep(@StringRes int status,
@NonNull Runnable runnable) {
queue.add(new Holder(status, runnable));
return this;
}
StatusRunnableBuilder withFinalStatus(@StringRes int status) {
finalStatus = status;
return this;
}
Runnable build() {
return new Runnable() {
@Override
public void run() {
for (Holder item: queue) {
updateStatus(item.status);
item.runnable.run();
}
if (finalStatus != 0) {
updateStatus(finalStatus);
}
}
};
}
private void updateStatus(@StringRes final int status) {
final TextView view = viewRef.get();
if (view != null) {
view.post(new Runnable() {
@Override
public void run() {
// As this has been posted to a queue,
// it could have been processed with some delay,
// so there is no guarantee the view is still present.
// Let's check again.
final TextView v = viewRef.get();
if (v != null) {
v.setText(status);
}
}
});
}
}
}
然后你的onClick
变成了:
@Override
public void onClick(View v) {
final Runnable runnable = new StatusRunnableBuilder(view)
.addStep(R.string.launching_text, launchingRunnable)
.addStep(R.string.almost_done, almostDoneRunnable)
.withFinalStatus(R.string.finally_done)
.build();
service.execute(runnable);
}
其中service
是一个ExecutorService,它允许你在任何生命周期事件上创建/关闭,例如:
@Override
protected void onStart() {
super.onStart();
service = Executors.newSingleThreadExecutor();
}
@Override
protected void onStop() {
super.onStop();
service.shutdownNow();
}
你可以使用Runnable
和Handler
。 Handler
将在一定时间间隔后向Runnable
发布更新。
对于蓝牙连接,您也可以使用广播接收器。
您可以尝试使用AsyncTask。它非常简单,它可以处理所有后台线程以及线程间通信。有几个注意事项,为避免内存泄漏,您可以使用EventBus或类似的机制。这是我发现非常有用的文章:
http://simonvt.net/2014/04/17/asynctask-is-bad-and-you-should-feel-bad/