Android OkHttp(一)初识,这篇文章最后提供了一个封装Okhttp请求的类,今天就来看看在项目中具体的使用情况。
一、简单接口请求。
接口请求,需要有一个服务端,这里就使用之前用SpringMVC做的一个接口服务,接口有关的详细开发步骤,请参考这篇文章,SpringMVC 开发接口。
1.启动接口服务后,运行后的效果截图如下,
Image may be NSFW.
Clik here to view.
可以看到 ,接口返回的是json格式的数据,json数据定义了返回状态、返回码以及返回数据等,有关接口定义,可以看这篇文章,java web开发(二) 接口开发。
2.客户端程序开发。
客户端使用Android小程序去调用接口。这个更加详细的描述,请参考, java web开发(三) 接口使用。记得加入网络访问请求权限
<uses-permission android:name="android.permission.INTERNET"/>
下面主要展示具体的调用,
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<ListResponse<Students>> typeToken = new TypeToken<ListResponse<Students>>() { }; OkHttpUtils.getInstance().getAsyn(Constant.GET_ALL_STUDENT, null, typeToken, new BaseResponseCallback<ListResponse<Students>>() { @Override public void onCompleted(final Throwable e, ListResponse<Students> 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<Students> 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,否则会报异常,如下图所示,
Image may be NSFW.
Clik here to view.
再看下面这种截图,
Image may be NSFW.
Clik here to view.
可以看到打印了两个线程的id是不一样的,一个是主线程,一个是子线程。程序运行后的效果截图,
Image may be NSFW.
Clik here to view.
PS: 我们还可以在点击Button时,显示一个进度条(模态对话框),当请求结束后,关闭该进度条。这样在请求时,有一个比较好的用户体验,明确告诉用户,程序现在在做什么!Image may be NSFW.
Clik here to view.
更新UI,有多种方式,还可以使用handler发消息到主线程中,让主线程更新UI!
二、文件下载。
1.服务端。
下载一张图片。该图片位于Tomact服务器路径底下。
Image may be NSFW.
Clik here to view.
启动在Tomact服务器,然后在浏览器中输入图片地址,截图如下所示,
Image may be NSFW.
Clik here to view.
2.客户端程序开发。
MainActivity界面有Button和ImageView,点击Button去调用接口,ImageView则用来显示下载的图片。
首先记得加入读写sd卡权限,
<!-- 在SD卡中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 向SD卡写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_SETTINGS"/>下面看具体调用,
/** * 下载图片 */ 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中加载图片,点击运行后的效果如下,
Image may be NSFW.
Clik here to view.
打印出下载文件的路径,我们在手机上去该目录底下看看文件是否存在,
Image may be NSFW.
Clik here to view.
在目录下,已经存在下载的文件了,下面显示手机运行后的效果截图,
Image may be NSFW.
Clik here to view.
三、文件上传。
在调用接口上传文件前,先做了一个JSP网页上传一个文件。
1. 使用form 上传一个文件。
(1). 首先需要创建一个上传的JSP网页,具体代码
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> <form action="./upLoadFile.dbo" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="file"/> <input type="submit" value="提交"/> </form> </body> </html>该JSP网页功能很简单,选择文件,然后点击提交。
(2).创建两个JSP网页,一个是成功,另外一个是失败,用于上传后跳转,内容自己可以定制,我这边只显示文字。具体代码就不列举了!具体代码详解Demo工程。
(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; } }该实现类,将文件流写入到具体目录中,和我们第二部的下载文件很类似。
(4).接着定义个Action,提供外部调用接口,
@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); } }
上传文件调用了UploadFileBusiness.upload()方法,
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中配置过滤器,
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!--配置自动解析的包 --> <context:component-scan base-package="cn.springmvc"></context:component-scan> <!--配置视图解析器:如何把handler 方法返回值解析为实际的物理视图 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"/> <!-- 指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 --> <property name="maxUploadSize" value="200000"/> </bean> ... <!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException --> <!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,而且此时还没有进入到Controller方法中 --> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到/WEB-INF/jsp/error_fileupload.jsp页面 --> <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error_fileupload</prop> </props> </property> </bean> ... </beans>
因为需要上传文件,所以还要导入几个jar包才可以,commons-fileupload-*.jar和commons-io-*.jar等包。
此致,上传功能已经完成了!下面展示一张,项目截图,
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
点击提交后,如果上传成功,会跳转至上传成功(uploadFileSuccess.jsp),并且在管理端打印了该文件的信息,
Image may be NSFW.
Clik here to view.
我们去上传文件的保存目录(D:/java_web/upload)看看是否有上传文件,
Image may be NSFW.
Clik here to view.
点击提交后,如果上传失败,会跳转至上传失败(uploadFileFail.jsp),在控制台会输出上传失败原因,便于大家分析出错原因。
Image may be NSFW.
Clik here to view.
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<EntityResponse<Image>> typeToken = new TypeToken<EntityResponse<Image>>() { }; OkHttpUtils.getInstance().postAsyn(Constant.UPLOAD_PIC_URL, null, file,"file",typeToken, new BaseResponseCallback<EntityResponse<Image>>() { @Override public void onCompleted(final Throwable e, EntityResponse<Image> 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()); } }); } } }); }客户端运行截图,首先选择图片,
Image may be NSFW.
Clik here to view.
选择完成后,上传成功返回json数据截图,
Image may be NSFW.
Clik here to view.
上传成功截图,
Image may be NSFW.
Clik here to view.
再看看服务端截图,控制台输入日志截图,
Image may be NSFW.
Clik here to view.
然后打开上传文件目录,
Image may be NSFW.
Clik here to view.
上传文件也OK了!Image may be NSFW.
Clik here to view.
四、总结
本文主要是基于封装Okhttp请求的类调用后台服务接口,实现不同的功能!经测试,该类基本满足功能!本文对上篇文章封装的Okhttp请求类有所优化,更加详细的代码,请看本篇的例子工程源代码!本文需要有点Okhttp和SpringMVC方面的知识!Image may be NSFW.
Clik here to view.