转载请标明出处: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)——网络模块优化