Android UI Thread在简单的客户端/服务器架构中冻结等待套接字

tmh*_*tmh 0 sockets android client-server freeze

我认为这是一个很常见的问题,但我仍然找不到满意的答案,所以我会问自己.

这是一段代码:

// 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!");
}
Run Code Online (Sandbox Code Playgroud)

这就是我想要实现的目标:

  1. 用户点击发送按钮;
  2. status textview显示"试图连接到服务器......";
  3. UI"等待"通信结束;
  4. 状态textview相应地显示结果.

但是当用户单击"发送"按钮时,UI会冻结(奇怪地在状态文本出现之前),直到通信完成(我尝试连接到未知主机).快速修复是设置套接字超时,但我不喜欢这种解决方案:UI仍然冻结,应该设置哪个超时?

我的第一个想法显然是Thread,但正如你所看到的,我需要返回一个值,因为线程独立且异步地运行,所以在线程环境中没有多大意义.

所以我需要的是UI肯定等待服务执行但没有冻结.顺便说一句,在我看来,等待返回值意味着UI 必须等待任务结束,我不会让它冻结.

我遇到了AsyncTask,但我发现两个主要缺点:

  1. 在我看来,它与UI紧密耦合;
  2. 如果我想用Integer,String和Boolean参数执行服务怎么办?我应该延长AsyncTask<Object, Void, Void>吗?

两者都导致无法表达.

我能做些什么来实现我的目标?请注意,对该服务的另一个请求是对尚未准备好的东西的请求,所以我应该每隔几次自动重复请求(比方说十分钟).所以我可能需要一些我可以使用的东西TimerTask,但是每次执行该服务时我仍然需要向UI返回一个值(所以我可以更新状态文本并让用户知道发生了什么).

Raj*_*ant 6

这是通过外部通信(即HTTP调用)进行处理时的典型用例.

最好的方法是使用AsyncTask.为您提供有关AsyncTask问题的答案.

在我看来,它与UI紧密耦合;

这里好的代码设计将发挥作用.您可以编写自己的回调机制来摆脱紧耦合.示例可以在下面.

为WS调用所需的请求和响应创建您的版本.它可以是非常简单的原始类型或复杂类型参数.

class Result{
    //Define more para.
Run Code Online (Sandbox Code Playgroud)

}

class Request{
    //Deinf more para.
}
Run Code Online (Sandbox Code Playgroud)

写下面的回调接口.

public interface MyCallBack {
     public void onComplete(Result result);}
Run Code Online (Sandbox Code Playgroud)

创建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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在最后和重要的部分实现接口并调用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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我想用Integer,String和Boolean参数执行服务怎么办?我应该扩展AsyncTask吗?

第一个参数是任何用户定义的段.如果您需要传递多个参数,则将它们放入实体形式(即 - 类).此外,您可以在AsyncTask的构造函数中传递初始配置参数,即 - 通信URL.

希望它会有所帮助.