Android OkHttp(一)初识,这篇文章最后提供了一个封装Okhttp请求的类,今天就来看看在项目中具体的使用情况。
一、简单接口请求。
接口请求,需要有一个服务端,这里就使用之前用SpringMVC做的一个接口服务,接口有关的详细开发步骤,请参考这篇文章,SpringMVC 开发接口。
1.启动接口服务后,运行后的效果截图如下,
可以看到 ,接口返回的是json格式的数据,json数据定义了返回状态、返回码以及返回数据等,有关接口定义,可以看这篇文章,java web开发(二) 接口开发。
2.客户端程序开发。
客户端使用Android小程序去调用接口。这个更加详细的描述,请参考, java web开发(三) 接口使用。记得加入网络访问请求权限
下面主要展示具体的调用,
public class MainActivity extends AppCompatActivity {
private Button btn;
private TextView tv;
private ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
tv = (TextView) findViewById(R.id.tv);
iv = (ImageView) findViewById(R.id.iv);
Log.e("Thread.currentThread()--->", Thread.currentThread().getId() + "");//打印当前线程的id
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getAllStudent();
}
});
}
}
MainActivity界面有Button和TextView,点击Button去调用接口,TextView则用来显示接口数据。下面是具体调用方法,
/**
* 获取接口数据`
*/
private void getAllStudent() {
//接口返回的json
TypeToken> typeToken = new TypeToken>() {
};
OkHttpUtils.getInstance().getAsyn(Constant.GET_ALL_STUDENT, null, typeToken, new BaseResponseCallback>() {
@Override
public void onCompleted(final Throwable e, ListResponse result) {
if (e != null) {
tv.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
});
} else {
Log.e("Thread.currentThread()--->", Thread.currentThread().getId() + "");//打印当前线程的id
//获取接口返回的列表数据
List list = result.getItems();
final StringBuffer sb = new StringBuffer();
for (Students students : list) {
sb.append("姓名:" + students.getName() + ", 年龄" + students.getAge() + ", 电话" + students.getMobile()).append("\n");
}
//更新UI,在子线程中,不能直接更新UI
tv.post(new Runnable() {
@Override
public void run() {
tv.setText(sb.toString());
}
});
}
}
});
}
在回调方法中要加入判断请求是否成功,最后记得显示接口数据时,不能直接在回调方法中更新UI,否则会报异常,如下图所示,
可以看到打印了两个线程的id是不一样的,一个是主线程,一个是子线程。程序运行后的效果截图,
PS: 我们还可以在点击Button时,显示一个进度条(模态对话框),当请求结束后,关闭该进度条。这样在请求时,有一个比较好的用户体验,明确告诉用户,程序现在在做什么!
更新UI,有多种方式,还可以使用handler发消息到主线程中,让主线程更新UI!
二、文件下载。
1.服务端。
下载一张图片。该图片位于Tomact服务器路径底下。
启动在Tomact服务器,然后在浏览器中输入图片地址,截图如下所示,
2.客户端程序开发。
MainActivity界面有Button和ImageView,点击Button去调用接口,ImageView则用来显示下载的图片。
首先记得加入读写sd卡权限,
下面看具体调用,
/**
* 下载图片
*/
private void downLoadPic() {
OkHttpUtils.getInstance().downloadFileAsyn(Constant.PIC_URL, null, FileUtils.createFile(MainActivity.this, "pic", "1.png"), new BaseResponseCallback() {
@Override
public void onCompleted(final Throwable e, final Object result) {
if (e != null) {
tv.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
});
} else {
if (result != null) {
iv.post(new Runnable() {
@Override
public void run() {
String path = (String) result;
Log.e("path--->", path);
Bitmap bitmap = BitmapFactory.decodeFile(path);
iv.setImageBitmap(bitmap);
}
});
}
}
}
});
}
FileUtils.createFile(MainActivity.this,"pic","1.png") //是创建一个文件
点击Button去调用下载图片,下载完成后在ImageView中加载图片,点击运行后的效果如下,
打印出下载文件的路径,我们在手机上去该目录底下看看文件是否存在,
在目录下,已经存在下载的文件了,下面显示手机运行后的效果截图,
PS: 图片下载需要和图片缓存结合使用!
三、文件上传。
在调用接口上传文件前,先做了一个JSP网页上传一个文件。
1. 使用form 上传一个文件。
(1). 首先需要创建一个上传的JSP网页,具体代码
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
Insert title here
该JSP网页功能很简单,选择文件,然后点击提交。
(3).创建一个上传接口,具体代码如下,
public interface UpLoadFileService {
public boolean uploadFile(String destinationDir, MultipartFile file);
}
该接口只有一个上传方法,下面在看该接口的实现类,
public class UploadFileServiceImpl implements UpLoadFileService {
public boolean upload(String destinationDir, MultipartFile file) {
return uploadFile(destinationDir, file);
}
/**
* 保存文件
*
* @param stream
* @param path
* @param filename
* @throws IOException
*/
private void SaveFileFromInputStream(InputStream stream, String path, String filename) throws IOException {
FileOutputStream outputStream = new FileOutputStream(path + "/" + filename);
int byteCount = 0;
byte[] bytes = new byte[1024];
while ((byteCount = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, byteCount);
}
outputStream.close();
stream.close();
}
@Override
public boolean uploadFile(String destinationDir, MultipartFile file) {
StringBuffer sb = new StringBuffer();
sb.append("文件长度: " + file.getSize());
sb.append("文件类型: " + file.getContentType());
sb.append("文件名称: " + file.getName());
sb.append("文件原名: " + file.getOriginalFilename());
System.out.println(sb.toString());
try {
SaveFileFromInputStream(file.getInputStream(), destinationDir, file.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}
该实现类,将文件流写入到具体目录中,和我们第二部的下载文件很类似。
@Controller
public class UpLoadFileServlet {
@RequestMapping(value = "/upLoadFile.dbo", method = RequestMethod.POST)
public ModelAndView getAllStudent(HttpServletRequest request, HttpServletResponse response,
ModelMap modelMap) {
// 转型为MultipartHttpRequest:
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 获得文件:
MultipartFile file = multipartRequest.getFile("file");
String realPath = "D:/java_web/upload";//文件存放路径,需要首先创建
boolean result = false;
result = UploadFileBusiness.upload(realPath, file);
String resultJsp = "";
SingleObject singleObject = new SingleObject();
if (result) {
resultJsp = "uploadFileSuccess";
singleObject.setCode(StatusCode.CODE_SUCCESS);
singleObject.setMsg("上传成功");
} else {
resultJsp = "uploadFileFail";
singleObject.setCode(StatusCode.CODE_ERROR);
singleObject.setMsg("上传失败");
}
singleObject.setObject("");
modelMap.addAttribute("result", singleObject);
return new ModelAndView(resultJsp, modelMap);
}
}
public class UploadFileBusiness {
/**
* 上传文件
* @param destinationDir
* @param file
* @return
*/
public static boolean upload(String destinationDir, MultipartFile file) {
return new UploadFileServiceImpl().upload(destinationDir, file);
}
}
(5).最后记得要在springmvc.xml中配置过滤器,
...
error_fileupload
...
因为需要上传文件,所以还要导入几个jar包才可以,commons-fileupload-*.jar和commons-io-*.jar等包。
此致,上传功能已经完成了!下面展示一张,项目截图,
最后运行,效果截图如下,选择文件,点击提交,点击提交后,如果上传成功,会跳转至上传成功(uploadFileSuccess.jsp),并且在管理端打印了该文件的信息,
我们去上传文件的保存目录(D:/java_web/upload)看看是否有上传文件,
点击提交后,如果上传失败,会跳转至上传失败(uploadFileFail.jsp),在控制台会输出上传失败原因,便于大家分析出错原因。
2.客户端调用接口上传文件。
在Android设备上,选择一张图片,上传至后台。
(1). 选择图片,
/**
* 选择图片
*/
private void selectPic() {
Intent intent = new Intent();
if (Build.VERSION.SDK_INT < 19) {//因为Android SDK在4.4版本后图片action变化了 所以在这里先判断一下
intent.setAction(Intent.ACTION_GET_CONTENT);
} else {
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
}
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, PICTURE);
}
回调处理,
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null) {
return;
}
Uri uri = data.getData();
switch (requestCode) {
case PICTURE:
String imagePath = FileUtils.getUriPath(this, uri); //(因为4.4以后图片uri发生了变化)通过文件工具类 对uri进行解析得到图片路径
if (!TextUtils.isEmpty(imagePath)) {
uploadFile(imagePath);
}
break;
default:
break;
}
}
上传文件,
/**
* 文件上传
*
* @param filePath
*/
private void uploadFile(String filePath) {
File file = new File(filePath);
TypeToken> typeToken = new TypeToken>() {
};
OkHttpUtils.getInstance().postAsyn(Constant.UPLOAD_PIC_URL, null, file,"file",typeToken, new BaseResponseCallback>() {
@Override
public void onCompleted(final Throwable e, EntityResponse result) {
if (e != null) {
tv.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
});
} else {
//获取接口返回的列表数据
Image image = result.getObject();
final StringBuffer sb = new StringBuffer();
sb.append("文件名称:" + image.getName() + ", 下载地址:" + image.getUrl() );
//更新UI,在子线程中,不能直接更新UI
tv.post(new Runnable() {
@Override
public void run() {
tv.setText(sb.toString());
}
});
}
}
});
}
客户端运行截图,首先选择图片,
选择完成后,上传成功返回json数据截图,
上传成功截图,
再看看服务端截图,控制台输入日志截图,
然后打开上传文件目录,
上传文件也OK了!
四、总结
本文主要是基于封装Okhttp请求的类调用后台服务接口,实现不同的功能!经测试,该类基本满足功能!本文对上篇文章封装的Okhttp请求类有所优化,更加详细的代码,请看本篇的例子工程源代码!本文需要有点Okhttp和SpringMVC方面的知识!
PS: Demo下载链接(包含服务端和客户端程序)。
下篇文章将结合OKHttp的源码,详细解析OKHttp的整个流程,详情请看, Android OkHttp(三)源码解析。