|
编写Android程序,离不开和Http打交道。android 的单线程UI模型,使得处理Http这样,耗时操作变得麻烦。传统的作法有Thread + Handler和AsyncTask 而这两种方式都是需要自己写很多重复的代码如创建HttpClient etc.不符合DRY(Don't repeat yourself),使Activity中需要作的逻辑处理非常多,代码变得臃肿, 导出,可复用性差,后期维护性差 。Activity的生命周期是极其不稳定的。无法控制,无法预判。试想下面的一段场景,用户正在向服务器发送一条信息,由于网速慢,或者网络 出现阻塞,发送到接收持续了几十秒,在这期间来了个电话或者用户决定切换出去换一首歌,这时候线程完成的提交工作并从服务器获得了数据再更新UI的时候,原来的 Activity已经不存在了。更有可能的是系统发现资源不够用了,决定直接把我们的进程杀掉了。
如果你也和我一样遇到了这些问题,这篇文章就是为你写的。
从Hello World开始
看段小程序,程序本身很简单,Activity中有三个控件上边是个EditText,中间是个TextView,下边是个Button,用户在EditText中输入一段网址,点击按钮,把 网页中HTML内容显示到TextView当中。 package com.chon.demo.httpoperation;
import java.io.IOException;
import com.chon.httpoperation.GetOperation;
import com.chon.httpoperation.HandledResult;
import com.chon.httpoperation.OperationListener;
//省略了一些引入
public class GetDemoActivity extends Activity {
private EditText urlEdit;
private TextView htmlText;
private Button submitButton;
OperationListener listener = new OperationListener(){
@Override
public void onError(long arg0, Bundle arg1, Exception e) {
htmlText.setText("E:" + e);
}
@Override
public void onError(long arg0, Bundle arg1, IOException e) {
htmlText.setText("IOE:" + e);
}
@Override
public void onNotOkay(long arg0, Bundle arg1, int code, String content) {
htmlText.setText("code:" + code + "content:" + content);
}
@Override
public void onResult(long arg0, Bundle arg1, HandledResult result) {
htmlText.setText(result.extras.getString("html"));
}
};
private MyApplication mApplication;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mApplication = (MyApplication)this.getApplication();
initUI();
}
private void initUI(){
this.setContentView(R.layout.get_demo);
urlEdit = (EditText)findViewById(R.id.urlEdit);
htmlText = (TextView)findViewById(R.id.htmlText);
htmlText.setMovementMethod(ScrollingMovementMethod.getInstance());
submitButton = (Button)findViewById(R.id.submitButton);
submitButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
GetOperation getOperation = new GetOperation(10, urlEdit.getText().toString(),
DummyHtmlHandler.class, listener);
mApplication.request(getOperation);
}
});
}
}
复制代码处理Http请求可以如此简单和清晰。我们来看一下上面的代码都作了些什么 submitButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
GetOperation getOperation = new GetOperation(10, urlEdit.getText().toString(),
DummyHtmlHandler.class, listener);
mApplication.request(getOperation);
}
});
复制代码当按钮被按下时,创建了一个GepOperation对象,在构造函数中传入了一定的参数,第一个是一次请求的id,因为本例中只有一次请求,所以这个参数可以 忽略,第二个是请求的url,第三个是一个类,表示如果处理服务器端返回的内容(如xml解析规则等,因本例不作任何处理,这个处理类也非常简单,一会儿 再说),第四个是一个OperationListener 的对象,一个回调方法,当HttpGet请求处理完毕后会把结果回调给这个对象。因为是从UI线程回调,我们可以直接 对UI进行操作。
下面我来解释下这期间到底发生了什么,GetOperation封装了对HttpGet的操作,当调用mApplication.request(getOperation)的时候,mApplication负责把这个请求 对象转发到后台的Service当中,并在一个线程池中处理运行,当服务器响应后,后台的Service会把服务器返回的数据交给DummyHtmlHanlder这个类处理,并把处理 结果从UI线程回调listener的onResult()返回给当前的Activity。 再看下这个极其简单的DummyHtmlHandler: package com.chon.demo.httpoperation;
import java.io.InputStream;
import android.os.Bundle;
import com.chon.httpoperation.Handleable;
import com.chon.httpoperation.HandledResult;
public class DummyHtmlHandler implements Handleable {
@Override
public int getContentType() {
return Handleable.TYPE_STRING;
}
@Override
public HandledResult handle(String content, Bundle bundle) {
Bundle extras = new Bundle();
extras.putString("html", content);
return new HandledResult(extras, null, null);
}
@Override
public HandledResult handle(InputStream arg0, Bundle arg1) {
return null;
}
}
复制代码希望处理服务器响应数据的类需要实现Handleable这个接口,接口定义了三个方法public int getContentType()是用来表示,自己希望得到什么形式的数据 有TYPE_STRING 和 TYPE_STREAM ,Service会根据这个值来回调下面的两个handle中的一种(也就是说如果getContentType返回的是TYPE_STRING, public HandledResult handle(String content, Bundle bundle),如果是TYPE_STREAMpublic HandledResult handle(InputStream arg0, Bundle arg1) 并将服务器返回的数据传入该方法中。处理后的数据通过一个叫HandledResult的类的对你返回给Activity,其实是个很简单的类,里边有三 个成员变量 一个Bundle,一个ArrayList和一个Object。起到信息传输的作用。好了,最后list.onResult(long arg0, Bundle arg1, HandledResult result)被回调,我们从 HandledResult对象中取出我们处理好的数据,更新UI就可以了。 |
|