Android OkHttp(一)初识,这篇文章最后提供了一个封装Okhttp请求的类,今天就来看看在项目中具体的使用情况。
一、简单接口请求。
接口请求,需要有一个服务端,这里就使用之前用SpringMVC做的一个接口服务,接口有关的详细开发步骤,请参考这篇文章,SpringMVC 开发接口。
1.启动接口服务后,运行后的效果截图如下,
可以看到 ,接口返回的是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,否则会报异常,如下图所示,
再看下面这种截图,
可以看到打印了两个线程的id是不一样的,一个是主线程,一个是子线程。程序运行后的效果截图,
PS: 我们还可以在点击Button时,显示一个进度条(模态对话框),当请求结束后,关闭该进度条。这样在请求时,有一个比较好的用户体验,明确告诉用户,程序现在在做什么!
更新UI,有多种方式,还可以使用handler发消息到主线程中,让主线程更新UI!
二、文件下载。
1.服务端。
下载一张图片。该图片位于Tomact服务器路径底下。
启动在Tomact服务器,然后在浏览器中输入图片地址,截图如下所示,
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中加载图片,点击运行后的效果如下,
打印出下载文件的路径,我们在手机上去该目录底下看看文件是否存在,
在目录下,已经存在下载的文件了,下面显示手机运行后的效果截图,
三、文件上传。
在调用接口上传文件前,先做了一个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等包。
此致,上传功能已经完成了!下面展示一张,项目截图,
点击提交后,如果上传成功,会跳转至上传成功(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<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()); } }); } } }); }客户端运行截图,首先选择图片,
选择完成后,上传成功返回json数据截图,
上传成功截图,
再看看服务端截图,控制台输入日志截图,
然后打开上传文件目录,
上传文件也OK了!
四、总结
本文主要是基于封装Okhttp请求的类调用后台服务接口,实现不同的功能!经测试,该类基本满足功能!本文对上篇文章封装的Okhttp请求类有所优化,更加详细的代码,请看本篇的例子工程源代码!本文需要有点Okhttp和SpringMVC方面的知识!