Android OkHttp(二)实战

     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中加载图片,点击运行后的效果如下,

打印出下载文件的路径,我们在手机上去该目录底下看看文件是否存在,


在目录下,已经存在下载的文件了,下面显示手机运行后的效果截图,

PS: 图片下载需要和图片缓存结合使用!

三、文件上传。

      在调用接口上传文件前,先做了一个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方面的知识!微笑

PS: Demo下载链接(包含服务端和客户端程序)

      下篇文章将结合OKHttp的源码,详细解析OKHttp的整个流程,详情请看, Android OkHttp(三)源码解析


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值