转载请标明出处:http://blog.csdn.net/android_ls/article/details/8732427
声明:仿人人项目,所用所有图片资源都来源于官方人人android客户端,编写本应用的目的在于学习交流,如涉及侵权请告知,我会及时换掉用到的相关图片。
上一篇聊到了通过WebView组件加载人人的访问授权页面,接下来就是当用户输入人人访问账号和密码,点击“登录”按钮,成功后人人的服务器端会返回AccessToken的值。初次使用,我们本地是没有存储session Key的,我们需要通过AccessToken去服务器端请求数据,获得session Key、sessionSecret和用户的userId。存储当前获得的sessionKey, sessionSecret以备后用。这里就需要用到网络这块的知识,这一篇就来说下仿人人的网络模块是如何架构的,先来看下网络处理模块的类图:
一、网络请求基类(AsyncBaseRequest),是一个实现Runnable接口的线程类。
初始化时的参数列表:
public AsyncBaseRequest(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) {}
参数列表解释:
第一个参数:网络请求URL;
第二个参数:网络请求的参数;
第三个参数:网络请求返回数据的解析器;
第四个参数:网络请求返回的结果回调接口;
网络请求具体实现抽象方法:
protected abstract InputStream getRequestResult() throws IOException;
线程核心run()方法里面的实现:
@Override public void run() { try { mInStream = getRequestResult(); if (mInStream != null) { String result = new String(readInputStream(mInStream)); Object obj = parseHandler.parse(result); requestCallback.onSuccess(flag, obj); } else { System.out.println("get InputStream By HttpURLConnection return result is NULL."); } } catch (IOException e) { requestCallback.onFail(flag, Constant.NETWORK_REQUEST_IOEXCEPTION_CODE); // IO异常标识 e.printStackTrace(); } }
二、通过HTTP协议发送GET请求的具体实现
package com.everyone.android.net; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.Map; import org.apache.http.protocol.HTTP; import com.everyone.android.callback.ParseCallback; import com.everyone.android.callback.ResultCallback; /** * 功能描述:通过HTTP协议发送GET请求 * @author android_ls * */ public class AsyncHttpGet extends AsyncBaseRequest { /** * */ private static final long serialVersionUID = 2L; public AsyncHttpGet(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) throws IOException { super(url, parameter, handler, requestCallback); } @Override protected InputStream getRequestResult() throws IOException { StringBuilder sb = new StringBuilder(requestUrl); sb.append('?'); for(Map.Entry<String, String> entry : parameter.entrySet()){ sb.append(entry.getKey()).append('=') .append(URLEncoder.encode(entry.getValue(), HTTP.UTF_8)).append('&'); } sb.deleteCharAt(sb.length()-1); URL url = new URL(sb.toString()); mHttpURLConn = (HttpURLConnection)url.openConnection(); mHttpURLConn.setConnectTimeout(connectTimeout); mHttpURLConn.setRequestMethod("GET"); if(mHttpURLConn.getResponseCode()== HttpURLConnection.HTTP_OK){ return mHttpURLConn.getInputStream(); } return null; } }
三、通过HTTP协议发送POST请求的具体实现
package com.everyone.android.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.Map; import org.apache.http.protocol.HTTP; import com.everyone.android.callback.ParseCallback; import com.everyone.android.callback.ResultCallback; /** * 功能描述:通过HTTP协议发送POST请求 * @author android_ls * */ public class AsyncHttpPost extends AsyncBaseRequest { /** * */ private static final long serialVersionUID = 3L; public AsyncHttpPost(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) { super(url, parameter, handler, requestCallback); } @Override protected InputStream getRequestResult() throws IOException { StringBuilder sb = new StringBuilder(); if(parameter!=null && !parameter.isEmpty()){ for(Map.Entry<String, String> entry : parameter.entrySet()){ sb.append(entry.getKey()).append('=') .append(URLEncoder.encode(entry.getValue(), HTTP.UTF_8)).append('&'); } sb.deleteCharAt(sb.length()-1); } // 得到实体的二进制数据 byte[] entitydata = sb.toString().getBytes(); URL url = new URL(requestUrl); mHttpURLConn = (HttpURLConnection)url.openConnection(); mHttpURLConn.setRequestMethod("POST"); mHttpURLConn.setConnectTimeout(5 * 1000); // 如果通过post提交数据,必须设置允许对外输出数据 mHttpURLConn.setDoOutput(true); mHttpURLConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); mHttpURLConn.setRequestProperty("Content-Length", String.valueOf(entitydata.length)); OutputStream outStream = mHttpURLConn.getOutputStream(); outStream.write(entitydata); outStream.flush(); outStream.close(); if(mHttpURLConn.getResponseCode()== HttpURLConnection.HTTP_OK){ return mHttpURLConn.getInputStream(); } return null; } }
四、线程池类
package com.everyone.android.net; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import android.util.Log; /** * 功能描述:网络请求线程池类 * @author android_ls */ public class DefaultThreadPool { /** * 用于保存等待执行的任务的阻塞队列。(有序的先进先出阻塞队列) */ private static ArrayBlockingQueue<Runnable> mBlockingQueue = new ArrayBlockingQueue<Runnable>(15, true); /** * 线程池 */ private static AbstractExecutorService mThreadPoolExecutor = new ThreadPoolExecutor(5, 7, 10, TimeUnit.SECONDS, mBlockingQueue, new ThreadPoolExecutor.DiscardOldestPolicy()); private static DefaultThreadPool instance = null; public static DefaultThreadPool getInstance() { if (instance == null) { instance = new DefaultThreadPool(); } return instance; } /** * 执行任务 * @param r */ public void execute(Runnable r) { mThreadPoolExecutor.execute(r); } /** * 关闭,并等待任务执行完成,不接受新任务 */ public static void shutdown() { if (mThreadPoolExecutor != null) { mThreadPoolExecutor.shutdown(); Log.i(DefaultThreadPool.class.getName(), "DefaultThreadPool shutdown"); } } /** * 关闭,立即关闭,并挂起所有正在执行的线程,不接受新任务 */ public static void shutdownRightnow() { if (mThreadPoolExecutor != null) { mThreadPoolExecutor.shutdownNow(); try { // 设置超时极短,强制关闭所有任务 mThreadPoolExecutor.awaitTermination(1, TimeUnit.MICROSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } Log.i(DefaultThreadPool.class.getName(), "DefaultThreadPool shutdownRightnow"); } } }
五、网络请求返回的数据解析接口
package com.everyone.android.callback; /** * 功能描述:网络请求返回的数据解析接口 * @author android_ls * */ public interface ParseCallback { /** * 对网络请求返回的数据进行解析 * @param str 要解析的字符串,JSON格式或XML * @return 解析的结果 */ public Object parse(String str); }
六、网络请求返回的结果回调接口
package com.everyone.android.callback; /** * 功能描述:网络请求返回的结果回调接口 * @author android_ls * */ public interface ResultCallback { public void onSuccess(Object obj); public void onFail(int errorCode); }
七、应用中界面(Activity)的基类
package com.everyone.android; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.Toast; import com.everyone.android.net.AsyncBaseRequest; import com.everyone.android.net.DefaultThreadPool; /** * 功能描述:应用中界面(Activity)的基类 * 对原有的Activity类进行扩展 * @author android_ls */ public abstract class AppBaseActivity extends Activity { /** * 当前activity所持有的所有网络请求 */ protected List<AsyncBaseRequest> mAsyncRequests; protected Handler mHandler; protected DefaultThreadPool mDefaultThreadPool; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); ((EveryoneApplication) this.getApplication()).addActivity(this); mHandler = new Handler(); mAsyncRequests = new ArrayList<AsyncBaseRequest>(); mDefaultThreadPool = DefaultThreadPool.getInstance(); // 初始化组件 setupView(); // 初始化数据 initializedData(); } /** * 布局文件ID * @return */ protected abstract int getLayoutId(); /** * 初始化组件 */ protected abstract void setupView(); /** * 初始化数据 */ protected abstract void initializedData(); @Override protected void onPause() { /** * 在activity销毁的时候同时设置停止请求,停止线程请求回调 */ cancelRequest(); super.onPause(); } private void cancelRequest() { if (mAsyncRequests != null && mAsyncRequests.size() > 0) { for (AsyncBaseRequest request : mAsyncRequests) { HttpURLConnection conn = request.getRequestConn(); if (conn != null) { try { conn.disconnect(); System.out.println("onDestroy disconnect URL: " + conn.getURL()); mAsyncRequests.remove(request); } catch (UnsupportedOperationException e) { //do nothing . } } } } } /** * 显示Toast形式的提示信息 * @param message */ protected void show(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } @Override protected void onDestroy() { /** * 在activity销毁的时候同时设置停止请求,停止线程请求回调 */ cancelRequest(); super.onDestroy(); } }
八、测试(继承自AppBaseActivity的子类),方法的调用
String url = "http://www.baidu.com/"; Map<String, String> parameter = new HashMap<String, String>(); parameter.put("name", "android_ls"); parameter.put("age", "23"); AsyncBaseRequest asyncBaseRequest = new AsyncHttpPost(url, parameter, new ParseCallback(){ @Override public Object parse(String str) { // TODO Auto-generated method stub return null; } }, new ResultCallback(){ @Override public void onSuccess(int flag, Object obj) { // TODO Auto-generated method stub } @Override public void onFail(int flag, int errorCode) { // TODO Auto-generated method stub } }); mDefaultThreadPool.execute(asyncBaseRequest); mAsyncRequests.add(asyncBaseRequest);
AsyncBaseRequest的完整代码
package com.everyone.android.net; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.HttpURLConnection; import java.util.Map; import com.everyone.android.callback.ParseCallback; import com.everyone.android.callback.ResultCallback; import com.everyone.android.utils.Constant; /** * 功能描述:通过HTTP协议发送网络请求线程类 * @author android_ls * */ public abstract class AsyncBaseRequest implements Runnable, Serializable { /** * */ private static final long serialVersionUID = 1L; /** * 网络连接超时,默认值为5秒 */ protected int connectTimeout = 5 * 1000; /** * 网络数据读取超时,默认值为5秒 */ protected int readTimeout = 5 * 1000; protected void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } protected void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } protected String requestUrl; protected Map<String, String> parameter; private ParseCallback parseHandler; private ResultCallback requestCallback; protected HttpURLConnection mHttpURLConn; protected InputStream mInStream; public AsyncBaseRequest(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) { this.parseHandler = handler; this.requestUrl = url; this.parameter = parameter; this.requestCallback = requestCallback; } /** * 发送网络请求 * * @return 网络请求返回的InputStream数据流 * @throws IOException */ protected abstract InputStream getRequestResult() throws IOException; @Override public void run() { try { mInStream = getRequestResult(); if (mInStream != null) { String result = new String(readInputStream(mInStream)); Object obj = parseHandler.parse(result); requestCallback.onSuccess(obj); } else { System.out.println("get InputStream By HttpURLConnection return result is NULL."); } } catch (IOException e) { requestCallback.onFail(Constant.NETWORK_REQUEST_IOEXCEPTION_CODE); // IO异常标识 e.printStackTrace(); } } /** * 从输入流读取数据 * * @param inStream * @return * @throws IOException * @throws Exception */ protected byte[] readInputStream(InputStream inStream) throws IOException { ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); return outSteam.toByteArray(); } public HttpURLConnection getRequestConn() { return mHttpURLConn; } }
好了就到这里吧,后面的待续。。。
吐槽下:在CSDN上写文章,写完后你若想删除掉之前写的一部分内容,就得一行一行删,不能选中某个区域,之后一次性删除,真郁闷。我记得之前不是这样的,现在为什么会这样呢?我刚才删除写错的文字和代码就用了我五分钟左右,真的想说粗话了,但想了下,算了。我是一个文明的人,呵呵。
这一篇网络模块处理有些地方有待优化,优化处理部分请阅读下篇Android仿人人客户端(v5.7.1)——网络模块优化