我认为这是一个很常见的问题,但我仍然没有找到一个满意的答案,所以我要问问自己。
这是一段代码:
// this is insine OnClickView
TextView status = (TextView) findViewById(R.id.status);
status.setText("Trying to connect to the server...");
try {
// this opens a socket and send a login request to the server.
int result = CommunicationManager.login(String email, String password);
switch (result) {
case CommunicationManager.SUCCESS:
// login ok, go on with next screen
break;
case CommunicationManager.WRONG_EMAIL:
status.setTextColor(Color.RED);
status.setText("Wrong Email!");
break;
case CommunicationManager.WRONG_PASSWORD:
status.setTextColor(Color.RED);
status.setText("Wrong Password!");
break;
}
} catch (CommunicationException e) {
status.setTextColor(Color.RED);
status.setText("Unable to estabilish a connection!");
} catch (ProtocolException e) {
status.setTextColor(Color.RED);
status.setText("Protocol error!");
}
这就是我想要实现的目标:
- 用户点击发送按钮
- status textview显示"正在尝试连接到服务器…"
- UI"等待"通信结束
- 状态文本视图相应地显示结果
但是,当用户单击"发送"按钮时,UI会冻结(奇怪的是,在状态文本出现之前),直到通信完成(我试图连接到未知主机)。一个快速的解决方案是设置套接字超时,但我不喜欢这种解决方案:UI仍然冻结,应该设置哪个超时?
我的第一个想法显然是线程,但正如您所看到的,我需要返回一个值,这在线程环境中没有多大意义,因为线程是独立异步运行的。
因此,我需要的是UI等待服务执行,但不会冻结。顺便说一句,在我看来,等待返回值意味着UI必须等待任务结束,我只是不让它冻结。
我遇到了AsyncTask,但我看到了两个主要的缺点:
- 在我看来,它与UI的结合过于紧密
- 如果我想用Integer、String和Boolean参数执行服务,该怎么办?我应该扩展
AsyncTask<Object, Void, Void>
吗
两者都导致了不可延展性。
我能做些什么来实现我的目标?请注意,对该服务的另一个请求将是对尚未准备好的东西的请求,所以我应该每隔几次(比方说十分钟)自动重复一次请求。因此,我可能需要一些可以与TimerTask
一起使用的东西,但每次执行该服务时,我仍然需要向UI返回一个值(这样我就可以更新状态文本并让用户知道发生了什么)。
这是处理外部通信(即HTTP调用)时的典型用例。
最好的方法是使用AsyncTask。为您的AsyncTask问题提供答案。
在我看来,它与UI的结合过于紧密;
良好的代码设计将在这里发挥作用。您可以编写自己的回调机制来消除紧密耦合。示例如下。
为WS调用所需的请求和响应创建您的版本。它可以是非常简单的基元类型,也可以是复杂的类型参数。
class Result{
//Define more para.
}
class Request{
//Deinf more para.
}
写在回调接口下面。
public interface MyCallBack {
public void onComplete(Result result);}
创建AsyncTask并在构造函数中获取上面的Interface对象,同一对象可以返回Result对象。
class LongRunningTask extends AsyncTask<Request, Integer, Long>{
private MyCallBack callback;
public LongRunningTask(MyCallBack callback) {
super();
this.callback = callback;
}
@Override
protected Long doInBackground(Request... params) {
// Perform your back ground task.
return null;
}
@Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
callback.onComplete(new Result()); //Here result is dummy but in real it should be contructred from doInBackground() method
}
}
现在是实现接口和调用asynctask的最后一个重要部分。我正在尝试重新使用您的代码以获得更好的清晰度。
public class MainActivity extends Activity implements MyCallBack{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView status = (TextView) findViewById(R.id.status);
status.setText("Trying to connect to the server...");
}
private void onClick(){
//Similer to CommunicationManager.login(String email, String password); in your code.
LongRunningTask longRunningTask = new LongRunningTask(this);
longRunningTask.execute(new Request());
}
@Override
public void onComplete(Result result) {
try {
int result = result.getStatus
switch (result) {
case CommunicationManager.SUCCESS:
// login ok, go on with next screen
break;
case CommunicationManager.WRONG_EMAIL:
status.setTextColor(Color.RED);
status.setText("Wrong Email!");
break;
case CommunicationManager.WRONG_PASSWORD:
status.setTextColor(Color.RED);
status.setText("Wrong Password!");
break;
}
} catch (CommunicationException e) {
status.setTextColor(Color.RED);
status.setText("Unable to estabilish a connection!");
} catch (ProtocolException e) {
status.setTextColor(Color.RED);
status.setText("Protocol error!");
}
}
如果我想用Integer、String和Boolean参数执行服务,该怎么办?我应该扩展AsyncTask吗?
第一个参数是任何用户定义的段落。如果您需要传递多个参数,则将它们放入实体形式(即-Class)中。此外,您可以在AsyncTask的构造函数中传递初始配置参数,即-Communication URL。
希望它能有所帮助。
使用多线程,在不同的线程中进行所有通信
使用工作线程或AsyncTask执行长时间运行的操作。
此外,在安卓蜂窝中,如果您在UI线程上执行网络操作,系统会抛出异常。