最近打算换工作了,看了一下面试题 发现我原来知识是如此的匮乏 吓得我赶紧去写个Demo冷静下,异步和网络请求在Android开发中是最基本的东西,在Android6.0中抛弃了Httpclient 我们只能使用Httpurlconnection了
在上一篇博客中我们看了AsyncTask 我们知道 它是采用handler+线程池的方式来实现的异步请求,自己再写一个http网络请求工具类就行了
开搞:
先看http请求
import android.util.Log;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* Http请求的工具类
*
* @author zhy
*
*/
public class HttpUtils{
private static final int TIMEOUT_IN_MILLIONS = 5000;
public interface CallBack{
void onRequestComplete(byte[] arr);
}
/**
* 异步的Get请求
*
* @param urlStr
* @param callBack
*/
public static void doGetAsyn(final String urlStr, final CallBack callBack){
new Thread(){
public void run(){
try{
byte []arr = doGet(urlStr);
if (callBack != null){
callBack.onRequestComplete(arr);
}
} catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
/**
* 异步的Post请求
* @param urlStr
* @param params
* @param callBack
* @throws Exception
*/
public static void doPostAsyn(final String urlStr, final Map<String, String> params,
final CallBack callBack) throws Exception{
new Thread(){
public void run(){
try{
byte []arr = doPost(urlStr, params);
if (callBack != null){
callBack.onRequestComplete(arr);
}
} catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
/**
* Get请求,获得返回数据
*
* @param urlStr
* @return
* @throws Exception
*/
public static byte[] doGet(String urlStr){
URL url = null;
HttpURLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
int len =0;
byte[] buf = new byte[1024];
try{
url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);//连接超时 单位毫秒
conn.setReadTimeout(2000);//读取超时 单位毫秒
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");//维持长链接
conn.setRequestProperty("Charset", "UTF-8");
if (conn.getResponseCode() == 200){
is = conn.getInputStream();
baos = new ByteArrayOutputStream();
while ((len = is.read(buf)) != -1){
baos.write(buf, 0, len);
}
baos.flush();
return baos.toByteArray();
} else{
Log.i("iiiiiiiii", "请求失败" + conn.getResponseCode());
}
} catch (Exception e){
e.printStackTrace();
}finally{
try{
if (is != null)
is.close();
if (baos != null)
baos.close();
} catch (IOException e){
e.printStackTrace();
}
conn.disconnect();
}
return null ;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url
* 发送请求的 URL
* @param params
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。遍历map 然后拼接
* @return 所代表远程资源的响应结果
*/
public static byte[] doPost(String url, Map<String, String> params){
// StringBuilder是用来组拼请求参数
StringBuilder sb = new StringBuilder();
if (params != null && params.size() != 0) {
for (Map.Entry<String, String> entry : params.entrySet()) {
try {
sb.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
sb.append("&");
}
sb.deleteCharAt(sb.length() - 1);
}
// entity为请求体部分内容
// 如果有中文则以UTF-8编码为username=%E4%B8%AD%E5%9B%BD&password=123
byte[] entity = sb.toString().getBytes();
PrintWriter out = null;
InputStream in = null;
//本地的输出流
ByteArrayOutputStream os;
HttpURLConnection conn=null;
//每次读的字节数
byte[] data =new byte[1024];
//每次读到的字节数,一般是1024,如果到了最后一行就会少于1024,到了末尾就是 -1
int len=0;
try{
URL realUrl = new URL(url);
// 打开和URL之间的连接
conn = (HttpURLConnection) realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestMethod("POST");
//设置连接主机的超时时间(单位:毫秒)
conn.setConnectTimeout(5000);
// 从主机读取数据的超时时间(单位:毫秒)
conn.setReadTimeout(5000);
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("charset", "utf-8");
conn.setUseCaches(false);
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
//此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,
//所以在开发中不调用上述的connect()也可以)。添加参数
OutputStream ops = conn.getOutputStream();
// 以POST方式发送请求体
ops.write(entity);
ops.close();
if (conn.getResponseCode() == 200){
in = conn.getInputStream();
os=new ByteArrayOutputStream();
while ((len=in.read(data))!=-1){
os.write(data,0,len);
}
return os.toByteArray();
//return new String(os.toByteArray());//此处可以返回字节 字符串 流三种形式
}else{
Log.i("iiiiiiiii", "请求失败" + conn.getResponseCode());
}
} catch (Exception e){
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally{
try{
if (out != null){
out.close();
}
if (in != null){
in.close();
}
} catch (IOException ex){
ex.printStackTrace();
}
conn.disconnect();
}
return null;
}
/**
* 向指定 URL 发送POST方法的请求
* 请求体为json
* @param path
* 发送请求的 URL
* @param obj
* @return 所代表远程资源的响应结果
*/
public static String doJson(String path,String obj) throws IOException, JSONException {
//创建连接
URL url = new URL(path);
HttpURLConnection connection ;
StringBuffer sbuffer=null;
try {
//添加 请求内容
connection= (HttpURLConnection) url.openConnection();
//设置http连接属性
connection.setDoOutput(true);// http正文内,因此需要设为true, 默认情况下是false;
connection.setDoInput(true);// 设置是否从httpUrlConnection读入,默认情况下是true;
connection.setRequestMethod("PUT"); // 可以根据需要 提交 GET、POST、DELETE、PUT等http提供的功能
connection.setUseCaches(false);//设置缓存,注意设置请求方法为post不能用缓存
// connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Host", "*******"); //设置请 求的服务器网址,域名,例如***.**.***.***
connection.setRequestProperty("Content-Type", " application/json");//设定 请求格式 json,也可以设定xml格式的
connection.setRequestProperty("Accept-Charset", "utf-8"); //设置编码语言
connection.setRequestProperty("X-Auth-Token", "token"); //设置请求的token
connection.setRequestProperty("Connection", "keep-alive"); //设置连接的状态
connection.setRequestProperty("Transfer-Encoding", "chunked");//设置传输编码
connection.setRequestProperty("Content-Length", obj.toString().getBytes().length + ""); //设置文件请求的长度
connection.setReadTimeout(10000);//设置读取超时时间
connection.setConnectTimeout(10000);//设置连接超时时间
connection.connect();
OutputStream out = connection.getOutputStream();//向对象输出流写出数据,这些数据将存到内存缓冲区中
out.write(obj.toString().getBytes()); //out.write(new String("测试数据").getBytes()); //刷新对象输出流,将任何字节都写入潜在的流中
out.flush();
// 关闭流对象,此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区中
out.close();
//读取响应
if (connection.getResponseCode()==200){
// 从服务器获得一个输入流
InputStreamReader inputStream =new InputStreamReader(connection.getInputStream());//调用HttpURLConnection连接对象的getInputStream()函数, 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。
BufferedReader reader = new BufferedReader(inputStream);
String lines;
sbuffer= new StringBuffer("");
while ((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(), "utf-8");
sbuffer.append(lines);
}
reader.close();
return sbuffer.toString();
}else{
Log.i("iiiiiiiii", "请求失败" + connection.getResponseCode());
}
//断开连接
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
再写两个自定义的AsyncTask 借助接口回调 一个普通的异步任务 一个图片下载的异步任务
import android.os.AsyncTask;
import com.longlian.asynctask.util.HttpUtils;
import java.util.HashMap;
import java.util.Map;
public class MyAsyncTask extends AsyncTask<String, Void, String> {
// 获取到JSON后的接口回调的实现类对象
private JsonCallback callback;
Map<String, String>map = new HashMap<String, String>();
public MyAsyncTask(JsonCallback callback,Map<String, String> map2) {
super();
this.callback = callback;
this.map = map2;
}
@Override
protected String doInBackground(String... params) {
byte [] array = HttpUtils.doPost(params[0], map);
return new String(array,0,array.length);
}
@Override
protected void onPostExecute(String result) {
// 调用回调接口中的方法
callback.onListObtained(result);
super.onPostExecute(result);
}
/**
* 获取到JSON后的回调接口
* @author Administrator
*/
public interface JsonCallback {
public void onListObtained(String result);
}
}
//图片下载
import android.graphics.Bitmap;
import android.os.AsyncTask;
import com.longlian.asynctask.util.BitmapUtils;
import com.longlian.asynctask.util.HttpUtils;
public class ImageTask extends AsyncTask<String, Void, Bitmap> {
//图片下载回调接口
private ImageCallback callback;
public ImageTask(ImageCallback imageCallback) {
this.callback = imageCallback;
}
String url = null;
@Override
protected Bitmap doInBackground(String... params) {
url = params[0];
try {
byte[] imageData = HttpUtils.doGet(params[0]);
Bitmap bitmap = BitmapUtils.doParse(imageData);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap result) {
// 调用回调接口中的方法
callback.onImageDownloaded(url, result);
super.onPostExecute(result);
}
/**
* 获取到JSON后的回调接口
* @author Administrator
*/
public interface ImageCallback {
//传这个url是为了防止图片错位,给图片贴标签
public void onImageDownloaded(String url, Bitmap bmp);
}
}
再贴一个二次采样工具类
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
/**
*
* @类名称: BitmapUtils
* @类描述: 处理图片的工具类,用于图片的二次采样
* @创建时间:2015年7月27日 下午1:09:13
* @备注:
* @version V1.0
*/
public class BitmapUtils {
private static Config mDecodeConfig;
private static int mMaxWidth;
private static int mMaxHeight;
/**
* Creates a new image request, decoding to a maximum specified width and
* height. If both width and height are zero, the image will be decoded to
* its natural size. If one of the two is nonzero, that dimension will be
* clamped and the other one will be set to preserve the image's aspect
* ratio. If both width and height are nonzero, the image will be decoded to
* be fit in the rectangle of dimensions width x height while keeping its
* aspect ratio.
*
* Listener to receive the decoded bitmap
* @param maxWidth
* Maximum width to decode this bitmap to, or zero for none
* @param maxHeight
* Maximum height to decode this bitmap to, or zero for none
* @param decodeConfig
* Format to decode the bitmap to
* Error listener, or null to ignore errors
*/
public void ImageRequest(int maxWidth, int maxHeight, Config decodeConfig) {
mDecodeConfig = decodeConfig;
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
}
/**
* 获取图片尺寸 Scales one side of a rectangle to fit aspect ratio.
*
* @param maxPrimary
* Maximum size of the primary dimension (i.e. width for max
* width), or zero to maintain aspect ratio with secondary
* dimension
* @param maxSecondary
* Maximum size of the secondary dimension, or zero to maintain
* aspect ratio with primary dimension
* @param actualPrimary
* Actual size of the primary dimension
* @param actualSecondary
* Actual size of the secondary dimension
*/
private static int getResizedDimension(int maxPrimary, int maxSecondary,
int actualPrimary, int actualSecondary) {
// If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) {
return actualPrimary;
}
// If primary is unspecified, scale primary to match secondary's scaling
// ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
return (int) (actualPrimary * ratio);
}
if (maxSecondary == 0) {
return maxPrimary;
}
double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
if (resized * ratio > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
/**
* 对图片进行二次采样. The real guts of parseNetworkResponse. Broken out for
* readability.
*/
public static Bitmap doParse(byte[] data) {
// byte[] data = data;
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
Bitmap bitmap = null;
if (mMaxWidth == 0 && mMaxHeight == 0) {
decodeOptions.inPreferredConfig = mDecodeConfig;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,
decodeOptions);
} else {
// If we have to resize this image, first get the natural bounds.
decodeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
int actualWidth = decodeOptions.outWidth;
int actualHeight = decodeOptions.outHeight;
// Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth);
// Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false;
// (ficus): Do we need this or is it okay since API 8 doesn't
// support it?
// decodeOptions.inPreferQualityOverSpeed =
// PREFER_QUALITY_OVER_SPEED;
decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
actualHeight, desiredWidth, desiredHeight);
Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0,
data.length, decodeOptions);
// If necessary, scale down to the maximal acceptable size.
if (tempBitmap != null
&& (tempBitmap.getWidth() > desiredWidth || tempBitmap
.getHeight() > desiredHeight)) {
bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth,
desiredHeight, true);
tempBitmap.recycle();
} else {
bitmap = tempBitmap;
}
}
return bitmap;
}
/**
* 获取最佳图片压缩率. Returns the largest power-of-two divisor for use in
* downscaling a bitmap that will not result in the scaling past the desired
* dimensions.
*
* @param actualWidth
* Actual width of the bitmap
* @param actualHeight
* Actual height of the bitmap
* @param desiredWidth
* Desired width of the bitmap
* @param desiredHeight
* Desired height of the bitmap
*/
static int findBestSampleSize(int actualWidth, int actualHeight,
int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
while ((n * 2) <= ratio) {
n *= 2;
}
return (int) n;
}
}