Android使用自己封装的Http和Thread、Handler实现异步任务

来源:互联网 发布:英国男装品牌 知乎 编辑:程序博客网 时间:2024/05/21 21:33

目录结构如下:

这里写图片描述

Http协议的封装:

使用http协议有request和response这两个主要的域,下边是Http协议封装的结构图
这里写图片描述

(1)HttpRequestInter.java:作为request域对象,应该可以获得客户端请求的地址和httpRequest对象,这样的话才可以获得客户端请求的参数等信息;另外public HttpResponseInter request() throws Exception; 使用这个方法,是当执行完request请求之后,返回一个response对象(这里用接口表示)

/** * 请求的接口 * @author xuliugen */public interface HttpRequestInter {    //获得httpRequest    public HttpUriRequest getHttpRequest();    //获得http请求的url地址    public String getRequestURL();    //请求服务端:要返回一个response对象    public HttpResponseInter request() throws Exception;}

(2)HttpResponseInter.java作为和(1)中相对应的response对象,应该具有的方法:获取返回的状态码、获取返回的流、获取返回返回的string数据等,下边的方法就是获取相应的数据

/** * 响应的接口 * @author xuliugen */public interface HttpResponseInter {    //返回状态码    public int statusCode();    // 向客户端返回流数    public InputStream getResponseStream() throws IllegalStateException,IOException;    //向客户端返回字节数组    public byte[] getResponseStreamAsByte() throws IOException;    //向客户端返回JSON数据    public String getResponseStreamAsString() throws ParseException, IOException;}

(3)这是HttpRequestImpl.java接口的实现类,我们可以看到我们不但实现了HttpRequestInter接口还实现了ResponseHandler 第二个就是用于当执行完request请求之后需要返回的数据,存放在一个response的Handler中。

public class HttpRequestImpl implements HttpRequestInter,ResponseHandler<HttpResponseInter> {    protected HttpUriRequest httpUriRequest;// 用于获取request的url地址    private AbstractHttpClient abstractHttpClient; // client对象    // 构造犯法    public HttpRequestImpl(AbstractHttpClient httpClient) {        this.abstractHttpClient = httpClient;    }    // get方法    public HttpUriRequest getHttpRequest() {        return httpUriRequest;    }    //获得request的url    public String getRequestURL() {        return httpUriRequest.getURI().toString();    }    //执行request请求,并返回�?个response对象接口    public HttpResponseInter request() throws Exception {        return abstractHttpClient.execute(httpUriRequest, this);//传入的ResponseHandler对象    }    /**     * 继承ResponseHandler接口要实现的方法     * 执行完毕之后对response对象的处理接口     */    public HttpResponseInter handleResponse(HttpResponse response)throws ClientProtocolException, IOException {        //返回实现HttpResponseInter的类:返回给一个response接口        HttpResponseInter httpResponseInter = new HttpResponseImpl(response); //返回的时候需要response        return httpResponseInter;    }}

(4)然后下边就是接口的实现类:HttpResponseImpl.java 可以在构造方法中看到一个HttpResponse response对象,这就是在执行完request之后的handler返回的response对象。

/** * 接口的实现类 * @author xuliugen */public class HttpResponseImpl implements HttpResponseInter {    private HttpResponse response; // HttpResponse对象    private HttpEntity entity; // HttpEntity试题对象    public HttpResponseImpl(HttpResponse response) throws IOException {        this.response = response;        HttpEntity tempEntity = response.getEntity();// 获得服务器端返回的entity        if (null != tempEntity) {            entity = new BufferedHttpEntity(tempEntity);        }    }    // 返回response对象的状态码    public int statusCode() {        return response.getStatusLine().getStatusCode();    }    // 获得结果的stream    public InputStream getResponseStream() throws IllegalStateException,            IOException {        InputStream inputStream = entity.getContent();        return inputStream;    }    // 获得的结果转化为string    public String getResponseStreamAsString() throws ParseException,            IOException {        return EntityUtils.toString(entity);    }    // 获得的结果转化为字符数组    public byte[] getResponseStreamAsByte() throws IOException {        return EntityUtils.toByteArray(entity);    }}

(5)ExecuteHttpPost.java这个类继承了HttpRequestImpl.java在里边主要写了两个构造方法,构造方法就是实际的进行post请求的方法,和参数的设置:

/** * 这里才是真正执行post请求的地�? *  * 继承HttpRequestImpl 实现客户端向服务器端的请�? *  * @author xuliugen *  */public class ExecuteHttpPost extends HttpRequestImpl {    public ExecuteHttpPost(AbstractHttpClient httpClient, String url) {        this(httpClient, url, null);    }    public ExecuteHttpPost(AbstractHttpClient httpClient, String url,HttpEntity entity) {        super(httpClient);//父类中的httpClient        this.httpUriRequest = new org.apache.http.client.methods.HttpPost(url);// 初始化httpUriRequest        if (null != entity) {// 设置参数            ((HttpEntityEnclosingRequestBase) httpUriRequest).setEntity(entity);        }    }}

(6)另外一个重要的类就是客户端的实现了:BaseHttpClient.java在这里边我们设置了一系列的方法,用于实现不同客户端的请求方法,以及如何将客户端请求的参数转化为post请求的参数类型、将返回的数据转化为相应的格式,方法的层叠调用,希望大家静下心慢慢看。

/** * HttpClient客户端的顶层类 */public class BaseHttpClient {    private AbstractHttpClient httpClient;    public static final int DEFAULT_RETIES_COUNT = 5;    protected int retriesCount = DEFAULT_RETIES_COUNT;    // 设置最大连接数    public final static int MAX_TOTAL_CONNECTIONS = 100;    // 设置获取连接的最大等待时间    public final static int WAIT_TIMEOUT = 30000;    // 设置每个路由最大连接数    public final static int MAX_ROUTE_CONNECTIONS = 100;    // 设置连接超时时间    public final static int CONNECT_TIMEOUT = 10000;    // 设置读取超时时间    public final static int READ_TIMEOUT = 10000;    /**     * 构造方法,调用初始化方法     */    public BaseHttpClient() {        initHttpClient();    }    /**     * 初始化客户端参数     */    private void initHttpClient() {        //http的参数        HttpParams httpParams = new BasicHttpParams();        //设置最大连接数        ConnManagerParams.setMaxTotalConnections(httpParams,MAX_TOTAL_CONNECTIONS);        //设置获取连接的最大等待时间        ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);        //设置每个路由最大连接数        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);        // 设置连接超时时间        HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);        // 设置读取超时时间        HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);        HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);        SchemeRegistry schemeRegistry = new SchemeRegistry();        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));//设置端口80        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));//设置端口443        //就是管理SchemeRegistry的        ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);        httpClient = new DefaultHttpClient(clientConnectionManager, httpParams);        //创建http重新连接的handler        httpClient.setHttpRequestRetryHandler(new BaseHttpRequestRetryHandler(retriesCount));    }    /**     * 将参数转化为 List<BasicNameValuePair> 的集合     */    private List<BasicNameValuePair> parseParams(HashMap<String, Object> params) {        if (params == null || 0 == params.size()){            return null;        }        List<BasicNameValuePair> paramsList = new ArrayList<BasicNameValuePair>(params.size());        for (Entry<String, Object> entry : params.entrySet()) {            paramsList.add(new BasicNameValuePair(entry.getKey(), entry.getValue() + ""));        }        return paramsList;    }    /**     * 向服务器端请求:当请求只有url 没有参数的时候     */    public String post(String url) throws Exception {        return post(url, null); //调用有参数的时候执行的post并将参数设置为null    }    /**     * post请求之后返回T类型的结果     */    public <T> T post(String url, HashMap<String, Object> params, Class<T> clz) throws Exception {        String json = post(url, params);        return JSONUtil.fromJson(json, clz); //转化为具体的类型返回    }    /**     * 当请求有参数的时候,其他函数间接调用该方法     */    public String post(String url, HashMap<String, Object> params) throws Exception {        //将传入的参数转化为参数实体:将params转化为enrity的对象:表单entity        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));        return request(url, entity).getResponseStreamAsString();    }    /**     * 将post执行的结果直接返回     */    public Result postAsResult(String url, HashMap<String, Object> params)throws Exception {        return post(url, params, Result.class);    }    /**     * 将post执行的结果一Stream的形式返回     */    public InputStream postAsStream(String url, HashMap<String, Object> params) throws Exception {        //将传入的参数转化为参数实体        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parseParams(params));        return request(url, entity).getResponseStream();    }    public HttpResponseInter request(String url, HttpEntity entity) throws Exception {        HttpRequestImpl httpRequestImpl = new ExecuteHttpPost(httpClient, url, entity);        return httpRequestImpl.request();    }}

(7)最后一个就是我们在httpClient中使用的一个BaseHttpRequestRetryHandler.java用于实现网络重复请求的次数

/** * http重新尝试连接:主要用于完成尝试重新连接 * @author xuliugen */public class BaseHttpRequestRetryHandler implements HttpRequestRetryHandler {    private int max_retry_count;// 最大尝试连接的次数    public BaseHttpRequestRetryHandler(int maxretryCount) {        this.max_retry_count = maxretryCount;    }    private static HashSet<Class<? extends IOException>> exceptionWhiteList = new HashSet<Class<? extends IOException>>();    private static HashSet<Class<? extends IOException>> exceptionBlackList = new HashSet<Class<? extends IOException>>();    static {        exceptionWhiteList.add(NoHttpResponseException.class);        exceptionWhiteList.add(UnknownHostException.class);        exceptionWhiteList.add(SocketException.class);        exceptionBlackList.add(SSLException.class);        exceptionBlackList.add(InterruptedIOException.class);        exceptionBlackList.add(SocketTimeoutException.class);    }    public boolean retryRequest(IOException exception, int executionCount,HttpContext context) {        if (executionCount > max_retry_count){            return false;        }        if (exceptionBlackList.contains(exception.getClass())){            return false;        }        if (exceptionWhiteList.contains(exception.getClass())){            return true;        }        HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);        boolean idempotent = (request instanceof HttpEntityEnclosingRequest);        if (!idempotent) {            // 濡傛灉璇锋眰琚涓烘槸骞傜瓑鐨勶紝閭d箞灏遍噸璇�            return true;        }        Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT);        boolean sent = (b != null && b.booleanValue());        if (!sent) {            return true;        }        return false;    }}

Service和AsyncTask的结合使用

大致流程如下:
这里写图片描述
(1)我们将任务统一的交给Service进行处理,这样的话我们就需要一个Task实体

public class Task {    private int taskId;// 任务ID    private Map<String, Object> taskParams;// 参数    public static final int USER_LOGIN = 1; //自定义的一个任务ID    //构造方法和get、set方法省略}

(2)下边就是统一管理Task的Service,在Service中我们不仅需要统一的管理Task即是异步任务,我们还需要负责管理更新界面的操作,因为更新界面的操作不能再住UI中进行,所以我们需要统一的管理activity,在Service中,我们执行异步任务的操作使用过Thread和Handler实现的。

public class MainService extends Service implements Runnable {    // 任务队列:用于存放任务的队列    private static Queue<Task> tasks = new LinkedList<Task>();    // 将需要更新的UI添加到集合中    private static ArrayList<Activity> appActivities = new ArrayList<Activity>();    private boolean isRun;// 是否运行线程    Handler handler = new Handler() {        public void handleMessage(android.os.Message msg) {            switch (msg.what) {            case Task.USER_LOGIN: {// 用户登录 :更新UI                //根据name找到activity:因为MAinActivity实现了MainActivityInter接口,所以可以强转为MainActivityInter类型                MainActivityInter activity = (MainActivityInter) getActivityByName("MainActivity");                 activity.refresh(msg.obj.toString());                break;            }            default:                break;            }        };    };    /**     * 添加任务到任务队列中     */    public static void newTask(Task t) {        tasks.add(t);    }    @Override    public void onCreate() {        isRun = true;        Thread thread = new Thread(this);        thread.start();        super.onCreate();    }    /**     * 让服务一直遍历执行     */    public void run() {        while (isRun) { // 去监听任务            Task task = null;            if (!tasks.isEmpty()) { // 判断队列中是否有值                task = tasks.poll();// 执行完任务后把改任务从任务队列中移除                if (null != task) {                    doTask(task); // TO DO :执行任务                }            }            try {                Thread.sleep(1000);            } catch (Exception e) {            }        }    }    // 处理任务    private void doTask(Task task) {        Message msg = handler.obtainMessage();        msg.what = task.getTaskId();        switch (task.getTaskId()) {        case Task.USER_LOGIN: { // 用户登录            HashMap<String, Object> paramsHashMap =  (HashMap<String, Object>) task.getTaskParams();            //访问网络,进行判断用户是否存在            String url = "http://172.23.252.89:8080/igouServ/userlogin.action";            BaseHttpClient httpClient = new  BaseHttpClient();            try {                String result = httpClient.post(url, paramsHashMap);                msg.obj= result; //返回到handler进行处理            } catch (Exception e) {                e.printStackTrace();            }            break;        }        default:            break;        }        handler.sendMessage(msg);    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    /**     * 添加一个Activity对象到集合中     */    public static void addActivity(Activity activity) {        if (!appActivities.isEmpty()) {            for (Activity ac : appActivities) {                if (ac.getClass().getName().equals(ac.getClass().getName())) {                    appActivities.remove(ac);                    break;                }            }        }        appActivities.add(activity);    }    /**     * 根据Activity的Name获取Activity对象     */    private Activity getActivityByName(String name) {        if (!appActivities.isEmpty()) {            for (Activity activity : appActivities) {                if (null != activity) {                    if (activity.getClass().getName().indexOf(name) > 0) {                        return activity;                    }                }            }        }        return null;    }    /**     * 退出系统     */    public static void appExit(Context context) {        // Finish 所有的Activity        for (Activity activity : appActivities) {            if (!activity.isFinishing())                activity.finish();        }        // 结束 Service        Intent service = new Intent("com.xuliugen.frame.task.MainService");        context.stopService(service);    }}

(3)为了可以让Service统一的管理activity的话,我们可以书写一个Interface接口MainActivityInter.java有两个方法,其中一个就是为了刷新界面,以便于我们在service中进行界面的操作

public interface MainActivityInter {    /**     * 初始化操作     */    public void init();    /**     * 刷新UI     */    public void refresh(Object... params);}

测试步骤

(1)创建MainActivity.java 主要是为了模拟一次登录操作,在这里边我们需要开启服务,差UN该就爱弄一个任务,将任务加到Service管理的任务队列中去,然后其他的操作就交给MainService.java(Service)进行操作了。

public class MainActivity extends Activity implements MainActivityInter {    private Button btn_login;    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn_login = (Button) this.findViewById(R.id.btn_login);        textView=  (TextView) this.findViewById(R.id.textView1);        // 启动服务        Intent serviceIntent = new Intent(this, MainService.class);        startService(serviceIntent);        btn_login.setOnClickListener(new OnClickListener() {            public void onClick(View v) {                //构造参数传给Task进行处理                Map<String, Object> paramsHashMap = new HashMap<String, Object>(2);                paramsHashMap.put("userName", "xuliugen");                paramsHashMap.put("password", "123456");                Task task = new Task(Task.USER_LOGIN, paramsHashMap);                MainService.newTask(task);            }        });        // 将activity放入到activity队列集合中        MainService.addActivity(this);    }    /******************** 以下两个方法是MainActivityInter接口中的 ********************/    public void init() {    }    public void refresh(Object... params) {        //根据返回的参数进行更新UI         textView.setText(params[0].toString());    }}

这里写图片描述
这里写图片描述

项目下载地址:https://github.com/xuliugen/HttpAndAsyncTaskFrame

6 3
原创粉丝点击