用Jersey为Android客户端开发Restful Web Service
欢迎前往个人博客阅读
平时在做Android客户端的时候经常要与服务器之间通信,客户端通过服务端提供的接口获取数据,然后再展示在客户端的界面上,作为Android开发者,我们平时更多的是关注客户端的开发,而对服务端开发的关注相对较少,但是如果我们要自己一个人设计并开发一套完整的系统,我们就必须同时具备客户端和服务端的开发知识,而且我认为同时掌握客户端和服务端的开发技术还是很有用处的,不仅有助于提升我们的架构知识和能力,而且还……你懂得!身边一些做WEB开发的朋友很多都说为客户端开发接口和单纯地做WEB项目并没有很大的区别,不管后台语言用的是JAVA,PHP,还是C#等,用这些语言为客户端开发接口的时候有很多相似之处,比如说服务端请求路径的分发以及请求处理方式等。这几天出于好奇,想为自己的客户端开发Web Service接口,所以就在网上搜集了一些资料,发现有很多WEB框架可以很轻松地为移动端开发服务器接口,比如说Spring MVC,CXF,AXIS,RESTEasy等等,但是我最终选用了Jersey来完成服务端的开发。
首先,我们来大致说一下Jersey是什么。按照Jersey官网(https://jersey.java.net/)的介绍,Jersey是在Java开发中,为了简化RESTful Web Service以及客户端开发而设计的一套开源的,标准的,很优秀的,使用起来非常方便的JAX-RS API(即Java API for RESTful Web Services),Jersey支持JAX-RS API,并且是JAX-RS (JSR 311 & JSR 339) 的参考实现,值得注意的是,Jersey不仅仅是JAX-RS的参考实现,同时,它在JAX-RS工具包的基础上做了大量的扩展,增加了许多额外的特性和工具,并且都提供了丰富的说明文档以便开发者进一步对Jessey进行扩展以满足自己的实际开发需求。
为了方便大家更好地理解JAX-RS和RESTful,在这里我对这两个名词稍加说明。
JAX-RS按照百度百科上面的说明,即Java API for RESTful Web Services,是一个Java 编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建Web服务。JAX-RS使用了Java SE5引入的Java标注来简化Web服务的客户端和服务端的开发和部署。JAX-RS提供了一些标注将一个资源类,一个POJO Java类,封装为Web资源。JAX-RS包含了一系列的标注,包括:
1 package com.jerseyserver.service; 2 3 4 import java.util.ArrayList; 5 import java.util.HashMap; 6 7 import javax.ws.rs.FormParam; 8 import javax.ws.rs.GET; 9 import javax.ws.rs.POST; 10 import javax.ws.rs.Path; 11 import javax.ws.rs.Produces; 12 import javax.ws.rs.core.GenericEntity; 13 import javax.ws.rs.core.MediaType; 14 import javax.ws.rs.core.Response; 15 16 import com.fasterxml.jackson.core.JsonProcessingException; 17 import com.fasterxml.jackson.databind.ObjectMapper; 18 import com.jerseyserver.entity.ResponseDTO; 19 import com.jerseyserver.entity.User; 20 import com.jerseyserver.util.TextUtil; 21 22 23 @Path("/restService") 24 public class RestService { 25 @POST 26 @Path("/getUserText") 27 @Produces(MediaType.TEXT_PLAIN) 28 /** 29 * 测试返回text文本媒体类型数据的方式 30 * @return "Hello,World!" 31 */ 32 public String getUserText() { 33 return "Hello,World!"; 34 } 35 36 @GET 37 @Path("/getUserXml") 38 @Produces(MediaType.APPLICATION_XML) 39 /** 40 * 测试返回xml媒体类型数据的方式 41 * @return User object 42 */ 43 public User getUserXml() { 44 User user = new User(); 45 user.setName("snail"); 46 user.setAge("22"); 47 user.setSex("male"); 48 return user; 49 } 50 51 @GET 52 @Path("/getUserJson") 53 @Produces(MediaType.APPLICATION_JSON) 54 /** 55 * 测试返回json媒体类型数据的方式 56 * @return User object 57 */ 58 public User getUserJson() { 59 User user = new User(); 60 user.setName("snail"); 61 user.setAge("22"); 62 user.setSex("male"); 63 return user; 64 } 65 66 @POST 67 @Path("/getUserInfo") 68 @Produces(MediaType.APPLICATION_JSON) 69 /** 70 * 测试带请求参数时返回json媒体类型数据的方式 71 * @param username 72 * @return 73 */ 74 public User getUserInfo(@FormParam("username") String username) { 75 if (username == null || "".equals(username)) { 76 return null; 77 } 78 return getUserByName(username); 79 } 80 81 /** 82 * 通过用户名获取用户 83 * @param username 84 * @return User object 85 */ 86 private User getUserByName(String username) { 87 HashMap<String, User> map = initAllUsers(); 88 return map.get(username); 89 } 90 91 /** 92 * 获取所有用户的map 93 * @return 所有用户的map 94 */ 95 private HashMap<String, User> initAllUsers() { 96 HashMap<String, User> map = new HashMap<>(); 97 98 User user1 = new User(); 99 user1.setName("Jack"); 100 user1.setPasswd("Jack"); 101 user1.setAge(18 + ""); 102 user1.setSex("男"); 103 map.put(user1.getName(), user1); 104 105 User user2 = new User(); 106 user2.setName("Alice"); 107 user2.setPasswd("Alice"); 108 user2.setAge(18 + ""); 109 user2.setSex("女"); 110 map.put(user2.getName(), user2); 111 112 User user3 = new User(); 113 user3.setName("Allen"); 114 user3.setPasswd("Allen"); 115 user3.setAge(20 + ""); 116 user3.setSex("女"); 117 map.put(user3.getName(), user3); 118 119 return map; 120 } 121 122 @POST 123 @Path("/login") 124 @Produces(MediaType.APPLICATION_JSON) 125 /** 126 * 用户登录 127 * @param username 用户名 128 * @param password 密码 129 * @return Response object 130 */ 131 public Response login(@FormParam("username") String username, @FormParam("password") String password) { 132 if (TextUtil.isEmpty(username) || TextUtil.isEmpty(password)) { 133 return null; 134 } 135 User user = checkUser(username, password); 136 if (user == null) { 137 return null; 138 } 139 140 ObjectMapper mapper = new ObjectMapper(); 141 142 GenericEntity<String> payloadEntity; 143 try { 144 payloadEntity = new GenericEntity<String>( 145 mapper.writeValueAsString(new ResponseDTO(200, "ok", user))) { 146 }; 147 return Response.ok(payloadEntity).build(); 148 } catch (JsonProcessingException e) { 149 e.printStackTrace(); 150 return Response 151 .ok(" {\"status\": 404,\n\"message\": \"error\",\n\"response\": \"\"}") 152 .build(); 153 } 154 } 155 156 /** 157 * 验证用户是否存在 158 * @param username 159 * @param password 160 * @return User object 161 */ 162 private User checkUser(String username, String password) { 163 HashMap<String, User> map = initAllUsers(); 164 User user = map.get(username); 165 if (user != null) { 166 String passwd = user.getPasswd(); 167 if (password.equals(passwd)) { 168 return user; 169 } 170 } 171 return null; 172 } 173 174 @POST 175 @Path("/getAllUsers") 176 @Produces(MediaType.APPLICATION_JSON) 177 /** 178 * 获取所有用户的集合 179 * @return Response object 180 */ 181 public Response getAllUsers() { 182 ArrayList<User> list = new ArrayList<User>(); 183 User user1 = new User(); 184 user1.setName("Jack"); 185 user1.setPasswd("Jack"); 186 user1.setAge(18 + ""); 187 user1.setSex("男"); 188 list.add(user1); 189 190 User user2 = new User(); 191 user2.setName("Alice"); 192 user2.setPasswd("Alice"); 193 user2.setAge(18 + ""); 194 user2.setSex("女"); 195 list.add(user2); 196 197 User user3 = new User(); 198 user3.setName("Allen"); 199 user3.setPasswd("Allen"); 200 user3.setAge(20 + ""); 201 user3.setSex("女"); 202 list.add(user3); 203 204 ObjectMapper mapper = new ObjectMapper(); 205 206 GenericEntity<String> payloadEntity; 207 try { 208 payloadEntity = new GenericEntity<String>( 209 mapper.writeValueAsString(new ResponseDTO(200, "ok", list))) { 210 }; 211 return Response.ok(payloadEntity).build(); 212 } catch (JsonProcessingException e) { 213 e.printStackTrace(); 214 return Response 215 .ok(" {\"status\": 404,\n\"message\": \"error\",\n\"response\": \"\"}") 216 .build(); 217 } 218 } 219 }
我们可以看到,类和接口方法均有注解,这些注解是必须写上去的,因为这些注解指定了一些很关键的信息,客户端请求的时候必须依赖这些注解才能最终找到请求的接口,然后通过接口获取数据并返回。RestService.java上面的注解@Path("/restService")指定了Web Service接口的转发路径,每一个接口方法上面有三个注解,其中,@POST和@GET等表示网络请求方式,请求方式除了@POST和@GET之外,常见的还有put、delete等;@Path("/getUserText")、@Path("/getUserXml")、@Path("/getUserJson")表示的是请求的接口的路径,与类的@Path注解和项目目录一起拼接组成完整的请求路径,@Path注解后面的名字可以按照命名规范自己取;@Produces指定了请求的数据格式,一般为JSON和XML,当然还有TEXT文档等类型。这些注解都指定好之后,那么客户端就可以通过完整的请求路径来访问服务器了,完整的路径格式为:http://[IP地址]:[端口号]/[服务端工程名]/[路径映射]/[接口类路径]/[接口方法路径],例如,客户端要访问我们项目中的login接口,那么客户端请求的完整路径为:http://192.168.254.26:8080/JerseyServer/rest/restService/login,192.168.254.26是我电脑的局域网IP地址,8080为访问apache的端口号,rest是在web.xml中配置的路径映射名。web.xml的配置内容如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> 3 <web-app id="WebApp_ID"> 4 <!-- 同项目名 --> 5 <display-name>JerseyServer</display-name> 6 <servlet> 7 <!-- 为servlet取名 --> 8 <servlet-name>mobile</servlet-name> 9 <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> 10 <init-param> 11 <param-name>javax.ws.rs.Application</param-name> 12 <!-- 配置自己的资源加载类,用于加载资源 --> 13 <param-value>com.jerseyserver.RestApplication</param-value> 14 </init-param> 15 <load-on-startup>1</load-on-startup> 16 </servlet> 17 <servlet-mapping> 18 <!-- 保持和servlet名一致 --> 19 <servlet-name>mobile</servlet-name> 20 <!-- 分发根路径,用来做路径映射--> 21 <url-pattern>/rest/*</url-pattern> 22 </servlet-mapping> 23 <welcome-file-list> 24 <welcome-file>index.html</welcome-file> 25 <welcome-file>index.htm</welcome-file> 26 <welcome-file>index.jsp</welcome-file> 27 <welcome-file>default.html</welcome-file> 28 <welcome-file>default.htm</welcome-file> 29 <welcome-file>default.jsp</welcome-file> 30 </welcome-file-list> 31 </web-app>
web.xml中需要注意的项已经标注在了注释里面,其中有一个资源加载类RestApplication.java用于加载各种资源和所需工具,代码如下:
1 package com.jerseyserver; 2 3 import org.glassfish.jersey.filter.LoggingFilter; 4 import org.glassfish.jersey.server.ResourceConfig; 5 6 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; 7 8 public class RestApplication extends ResourceConfig { 9 public RestApplication() { 10 // 服务类所在的包路径 11 packages("com.jerseyserver.service"); 12 // 注册JSON转换器 13 register(JacksonJsonProvider.class); 14 // 打印访问日志,便于跟踪调试,正式发布可清除 15 register(LoggingFilter.class); 16 } 17 }
这里,主要指定了服务类的包路径,并用Jackson作为Json的解析转换工具,用glassfish的LoggingFilter打印日志。
此外,项目中的User实体类代码如下:
1 package com.jerseyserver.entity; 2 3 import javax.xml.bind.annotation.XmlRootElement; 4 5 @XmlRootElement 6 public class User { 7 private String name; 8 private String passwd; 9 private String age; 10 private String sex; 11 public String getName() { 12 return name; 13 } 14 public void setName(String name) { 15 this.name = name; 16 } 17 public String getPasswd() { 18 return passwd; 19 } 20 public void setPasswd(String passwd) { 21 this.passwd = passwd; 22 } 23 public String getAge() { 24 return age; 25 } 26 public void setAge(String age) { 27 this.age = age; 28 } 29 public String getSex() { 30 return sex; 31 } 32 public void setSex(String sex) { 33 this.sex = sex; 34 } 35 }
注意,需要用@XmlRootElement进行注释才能正常传递实体数据。
ResponseDTO.java用于包装返回给客户端的JSON数据,包括返回状态码,返回状态值以及实体数据,代码如下:
1 package com.jerseyserver.entity; 2 3 public class ResponseDTO { 4 private int status; 5 private String message; 6 private Object response; 7 8 public ResponseDTO(int status, String message, Object response) { 9 super(); 10 this.status = status; 11 this.message = message; 12 this.response = response; 13 } 14 15 public int getStatus() { 16 return status; 17 } 18 19 public void setStatus(int status) { 20 this.status = status; 21 } 22 23 public String getMessage() { 24 return message; 25 } 26 27 public void setMessage(String message) { 28 this.message = message; 29 } 30 31 public Object getResponse() { 32 return response; 33 } 34 35 public void setResponse(Object response) { 36 this.response = response; 37 } 38 }
好了,到此服务端就搞定了,接下来让我们简单看看android端的实现。客户端的项目结构如下:
1 package com.jy.jerseyclient.service; 2 3 import android.text.TextUtils; 4 import android.util.Log; 5 6 import com.jy.jerseyclient.entity.Response; 7 import com.jy.jerseyclient.utils.JsonUtil; 8 9 import java.io.ByteArrayOutputStream; 10 import java.io.InputStream; 11 import java.io.OutputStream; 12 import java.net.HttpURLConnection; 13 import java.net.URL; 14 import java.net.URLEncoder; 15 import java.util.HashMap; 16 import java.util.Map; 17 18 /** 19 * Created by xy on 2016/4/10. 20 */ 21 public class WebService { 22 private final static String TAG = "WebService"; 23 /**服务器接口根路径*/ 24 private static final String WEB_ROOT = "http://192.168.254.26:8080/JerseyServer/rest/restService/"; 25 /**登录*/ 26 private static final String LOGIN = "login"; 27 /**获取所有用户信息*/ 28 private static final String GET_ALL_USERS = "getAllUsers"; 29 30 /** 31 * 登录 32 * @param username 用户名 33 * @param password 用户密码 34 * @return 包含用户信息的Response对象 35 */ 36 public static Response login(String username, String password) { 37 String path = WEB_ROOT + LOGIN; 38 Map<String, String> map = new HashMap<>(); 39 map.put("username", username); 40 map.put("password", password); 41 InputStream is = connection(path, map); 42 if (is != null) { 43 String content = getStringFromIS(is); 44 if (content != null) { 45 return parseResponse(content); 46 } else { 47 Log.e(TAG, "contentS == null"); 48 } 49 } else { 50 Log.e(TAG, "is == null"); 51 } 52 return null; 53 } 54 55 /** 56 * 获取所有用户信息 57 * @return 包含所有用户信息的Response对象 58 */ 59 public static Response getAllUsers() { 60 String path = WEB_ROOT + GET_ALL_USERS; 61 InputStream is = connection(path, null); 62 if (is != null) { 63 String content = getStringFromIS(is); 64 if (content != null) { 65 return parseResponse(content); 66 } else { 67 Log.e(TAG, "contentS == null"); 68 } 69 } else { 70 Log.e(TAG, "is == null"); 71 } 72 return null; 73 } 74 75 /** 76 * 解析服务器返回的JSON数据 77 * @param content JSON数据 78 * @return Response对象 79 */ 80 private static Response parseResponse(String content) { 81 Log.e(TAG, "state======" + content); 82 if (TextUtils.isEmpty(content)) { 83 return null; 84 } 85 return JsonUtil.getEntity(content, Response.class); 86 } 87 88 /** 89 * 得到服务器返回的输入流数据 90 * @param path 请求路径 91 * @param map 包含密文的map集合 92 * @return 服务器返回的数据 93 */ 94 private static InputStream connection(String path, Map<String, String> map) { 95 try { 96 String pathUrl = path; 97 URL url = new URL(pathUrl); 98 HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); 99 StringBuffer sb = new StringBuffer(); 100 if (map != null) { 101 if (!map.isEmpty()) { 102 for (Map.Entry<String, String> entry : map.entrySet()) { 103 sb.append(entry.getKey()).append('=').append(URLEncoder.encode(entry.getValue(), "UTF-8")).append('&'); 104 } 105 sb.deleteCharAt(sb.length() - 1); 106 } 107 } 108 byte[] entityData = sb.toString().getBytes(); 109 httpConn.setDoOutput(true); 110 httpConn.setDoInput(true); 111 httpConn.setUseCaches(false); 112 httpConn.setRequestMethod("POST"); 113 //设置请求服务器连接的超时时间 114 httpConn.setConnectTimeout(5 * 1000); 115 //设置服务器返回数据的超时时间 116 //httpConn.setReadTimeout(30 * 1000); 117 httpConn.setRequestProperty("Content-length", "" + entityData.length); 118 httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 119 OutputStream outStream = httpConn.getOutputStream(); 120 outStream.write(entityData); 121 outStream.flush(); 122 outStream.close(); 123 int responseCode = httpConn.getResponseCode(); 124 if (HttpURLConnection.HTTP_OK == responseCode) { 125 InputStream is = httpConn.getInputStream(); 126 return is; 127 } 128 } catch (Exception ex) { 129 ex.printStackTrace(); 130 return null; 131 } 132 return null; 133 } 134 135 /** 136 * 将服务器返回的输入流转换为字符串 137 * @param is 服务器返回的输入流 138 * @return 输入流转换之后的字符串 139 */ 140 public static String getStringFromIS(InputStream is) { 141 byte[] buffer = new byte[1024]; 142 ByteArrayOutputStream os = new ByteArrayOutputStream(); 143 try { 144 int len = -1; 145 while ((len = is.read(buffer)) != -1) { 146 os.write(buffer, 0, len); 147 } 148 os.close(); 149 is.close(); 150 } catch (Exception e) { 151 e.printStackTrace(); 152 } 153 String reString = new String(os.toByteArray()); 154 155 Log.e(TAG, "geStringFromIS reString======" + reString); 156 157 return reString; 158 } 159 }
其中,使用HttpURLConnection向服务器发送请求数据,用GSON来解析JSON数据,在解析JSON的时候,为了解析的方便,我对GSON进行了进一步封装,以便在接口中对所有的实体和JSON进行统一的转换,其实现代码如下:
1 package com.jy.jerseyclient.utils; 2 3 import android.util.Log; 4 5 import com.google.gson.Gson; 6 import com.google.gson.JsonArray; 7 import com.google.gson.JsonElement; 8 import com.google.gson.JsonParser; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 13 /** 14 * Created by xy on 2015/12/29. 15 */ 16 public class JsonUtil { 17 private final static String TAG = "JsonUtil"; 18 /** 19 * 将对象转换为json字符串 20 * @param obj 对象,可以是实体对象,List对象,Map对象等 21 * @return json字符串 22 */ 23 public static String toJson(Object obj) { 24 if (obj == null) { 25 throw new IllegalArgumentException("illegal argument"); 26 } 27 28 return new Gson().toJson(obj); 29 } 30 31 /** 32 * 将json字符串转换为实体对象 33 * @param jsonString json字符串 34 * @param cls 实体类 35 * @param <T> 泛型参数 36 * @return 实体对象 37 */ 38 public static <T> T getEntity(String jsonString, final Class<T> cls) { 39 T t; 40 try { 41 Gson gson = new Gson(); 42 t = gson.fromJson(jsonString, cls); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 Log.e(TAG, jsonString + " 无法转换为 " + cls.getSimpleName() + " 对象"); 46 return null; 47 } 48 49 return t; 50 } 51 52 /** 53 * 将json字符串转换为List对象 54 * @param jsonString json字符串 55 * @param cls 实体类 56 * @param <T> 泛型参数 57 * @return 实体List对象 58 */ 59 public static <T> List<T> getEntityList(String jsonString, final Class<T> cls) { 60 List<T> list = new ArrayList<T>(); 61 62 JsonArray array = new JsonParser().parse(jsonString).getAsJsonArray(); 63 64 for (final JsonElement elem : array) { 65 list.add(new Gson().fromJson(elem, cls)); 66 } 67 68 return list; 69 } 70 }
同服务端一样,客户端也用一个Response类封装实体对象的信息。
Response.java的代码如下:
1 package com.jy.jerseyclient.entity; 2 3 import java.io.Serializable; 4 5 /** 6 * Description: 7 * Author: xy 8 * Date: 2016/4/12 14:49 9 */ 10 public class Response implements Serializable { 11 private static final long serialVersionUID = 8980216391057926016L; 12 public int status; 13 public String message; 14 public Object response; 15 16 public int getStatus() { 17 return status; 18 } 19 20 public void setStatus(int status) { 21 this.status = status; 22 } 23 24 public String getMessage() { 25 return message; 26 } 27 28 public void setMessage(String message) { 29 this.message = message; 30 } 31 32 public Object getResponse() { 33 return response; 34 } 35 36 public void setResponse(Object response) { 37 this.response = response; 38 } 39 }
User.java的代码如下:
1 package com.jy.jerseyclient.entity; 2 3 import java.io.Serializable; 4 5 /** 6 * Created by 123 on 2016/4/10. 7 */ 8 public class User implements Serializable { 9 private static final long serialVersionUID = -1044671445753823751L; 10 private String name; 11 private String passwd; 12 private String sex; 13 private String age; 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public String getPasswd() { 24 return passwd; 25 } 26 27 public void setPasswd(String passwd) { 28 this.passwd = passwd; 29 } 30 31 public String getSex() { 32 return sex; 33 } 34 35 public void setSex(String sex) { 36 this.sex = sex; 37 } 38 39 public String getAge() { 40 return age; 41 } 42 43 public void setAge(String age) { 44 this.age = age; 45 } 46 47 @Override 48 public String toString() { 49 return "User{" + 50 "name='" + name + '\'' + 51 ", passwd='" + passwd + '\'' + 52 ", sex='" + sex + '\'' + 53 ", age='" + age + '\'' + 54 '}'; 55 } 56 }
NetworkUtil.java用于网络状况的判断,实现代码如下:
1 package com.jy.jerseyclient.utils; 2 3 import android.content.Context; 4 import android.net.ConnectivityManager; 5 6 /** 7 * 网络相关工具类 8 * @author xy 9 * 10 */ 11 public class NetworkUtil { 12 /** 13 * 判断是否有可用网络 14 * @param context 15 * @return 16 */ 17 public static boolean isNetWorkOpened(Context context) { 18 ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 19 if (connManager.getActiveNetworkInfo() != null) { 20 return connManager.getActiveNetworkInfo().isAvailable(); 21 } 22 return false; 23 } 24 }
好了,剩下的就是登陆界面和主界面了,Activity的布局和逻辑都非常简单,我直接把代码贴上。
LoginActivty:
1 package com.jy.jerseyclient; 2 3 import android.content.Intent; 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.support.v7.app.AppCompatActivity; 7 import android.text.TextUtils; 8 import android.util.Log; 9 import android.view.View; 10 import android.widget.Button; 11 import android.widget.EditText; 12 import android.widget.Toast; 13 14 import com.jy.jerseyclient.entity.Response; 15 import com.jy.jerseyclient.entity.User; 16 import com.jy.jerseyclient.service.WebService; 17 import com.jy.jerseyclient.utils.JsonUtil; 18 import com.jy.jerseyclient.utils.NetworkUtil; 19 20 public class LoginActivity extends AppCompatActivity { 21 private final static String TAG = "LoginActivity"; 22 private EditText edt_username; 23 private EditText edt_password; 24 private Button btn_login; 25 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_login); 30 31 initView(); 32 initEvent(); 33 } 34 35 private void initView() { 36 edt_username = (EditText) findViewById(R.id.edt_username); 37 edt_password = (EditText) findViewById(R.id.edt_password); 38 btn_login = (Button) findViewById(R.id.btn_login); 39 } 40 41 private void initEvent() { 42 btn_login.setOnClickListener(new View.OnClickListener() { 43 @Override 44 public void onClick(View v) { 45 final String username = edt_username.getText().toString(); 46 if (TextUtils.isEmpty(username)) { 47 Toast.makeText(LoginActivity.this, 48 "user name must not be empty", Toast.LENGTH_SHORT).show(); 49 return; 50 } 51 final String password = edt_password.getText().toString(); 52 if (TextUtils.isEmpty(password)) { 53 Toast.makeText(LoginActivity.this, 54 "password must not be empty", Toast.LENGTH_SHORT).show(); 55 return; 56 } 57 if (NetworkUtil.isNetWorkOpened(LoginActivity.this)) { 58 new AsyncTask<String, Integer, Response>() { 59 @Override 60 protected Response doInBackground(String... params) { 61 Response response = WebService.login(username, password); 62 return response; 63 } 64 65 @Override 66 protected void onPostExecute(Response response) { 67 if (response == null) { 68 Toast.makeText(LoginActivity.this, "login failed,response is null", 69 Toast.LENGTH_SHORT).show(); 70 } else if (200 == response.getStatus()) { 71 Log.e(TAG, "user======" + response.toString()); 72 Object obj = response.getResponse(); 73 if (obj == null) { 74 Toast.makeText(LoginActivity.this, 75 "login failed,the response field is null", 76 Toast.LENGTH_SHORT).show(); 77 } else { 78 User user = JsonUtil.getEntity(obj.toString(), User.class); 79 if (user == null) { 80 Toast.makeText(LoginActivity.this, "login failed,illegal json", 81 Toast.LENGTH_SHORT).show(); 82 } else { 83 Toast.makeText(LoginActivity.this, "login succeed", 84 Toast.LENGTH_SHORT).show(); 85 Intent intent = new Intent(LoginActivity.this, MainActivity.class); 86 intent.putExtra("user", user); 87 startActivity(intent); 88 finish(); 89 } 90 } 91 } else { 92 Toast.makeText(LoginActivity.this, 93 "login failed," + response.getMessage(), 94 Toast.LENGTH_SHORT).show(); 95 } 96 super.onPostExecute(response); 97 } 98 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 99 } else { 100 Toast.makeText(LoginActivity.this, 101 "network is not available", Toast.LENGTH_SHORT).show(); 102 } 103 } 104 }); 105 } 106 }
LoginActivty的布局文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity" 8 android:orientation="vertical" 9 android:background="#FFFFFF" 10 android:gravity="center" > 11 12 <RelativeLayout 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" > 15 16 <TextView 17 android:id="@+id/tv_username" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:text="用户名" 21 android:textSize="18sp" 22 android:layout_alignParentLeft="true" /> 23 24 <EditText 25 android:id="@+id/edt_username" 26 android:layout_width="match_parent" 27 android:layout_height="wrap_content" 28 android:layout_alignParentRight="true" 29 android:layout_toRightOf="@id/tv_username" 30 android:textSize="18sp" /> 31 32 </RelativeLayout> 33 34 <RelativeLayout 35 android:layout_width="match_parent" 36 android:layout_height="wrap_content" 37 android:layout_marginTop="20dp" > 38 39 <TextView 40 android:id="@+id/tv_password" 41 android:layout_width="wrap_content" 42 android:layout_height="wrap_content" 43 android:text="密 码" 44 android:textSize="18sp" 45 android:layout_alignParentLeft="true" /> 46 47 <EditText 48 android:id="@+id/edt_password" 49 android:layout_width="match_parent" 50 android:layout_height="wrap_content" 51 android:layout_alignParentRight="true" 52 android:layout_toRightOf="@id/tv_password" 53 android:inputType="textPassword" 54 android:textSize="18sp" /> 55 </RelativeLayout> 56 57 <Button 58 android:id="@+id/btn_login" 59 android:layout_width="match_parent" 60 android:layout_height="wrap_content" 61 android:text="登录" 62 android:textSize="18sp" 63 android:gravity="center" 64 android:layout_marginTop="50dp" /> 65 </LinearLayout>
MainActivity:
1 package com.jy.jerseyclient; 2 3 import android.content.Intent; 4 import android.os.AsyncTask; 5 import android.os.Bundle; 6 import android.support.v7.app.AppCompatActivity; 7 import android.util.Log; 8 import android.view.View; 9 import android.widget.Button; 10 import android.widget.TextView; 11 import android.widget.Toast; 12 13 import com.jy.jerseyclient.entity.Response; 14 import com.jy.jerseyclient.entity.User; 15 import com.jy.jerseyclient.service.WebService; 16 import com.jy.jerseyclient.utils.JsonUtil; 17 import com.jy.jerseyclient.utils.NetworkUtil; 18 19 import java.util.ArrayList; 20 21 public class MainActivity extends AppCompatActivity { 22 private final static String TAG = "MainActivity"; 23 private TextView tv_welcome; 24 private Button btn_show_all_users; 25 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_main); 30 31 initView(); 32 initEvent(); 33 initData(); 34 } 35 36 private void initView() { 37 tv_welcome = (TextView) findViewById(R.id.tv_welcome); 38 btn_show_all_users = (Button) findViewById(R.id.btn_show_all_users); 39 } 40 41 private void initEvent() { 42 btn_show_all_users.setOnClickListener(new View.OnClickListener() { 43 @Override 44 public void onClick(View v) { 45 if (NetworkUtil.isNetWorkOpened(MainActivity.this)) { 46 new AsyncTask<String, Integer, Response>() { 47 @Override 48 protected Response doInBackground(String... params) { 49 Response response = WebService.getAllUsers(); 50 return response; 51 } 52 53 @Override 54 protected void onPostExecute(Response response) { 55 if (response == null) { 56 Toast.makeText(MainActivity.this, 57 "request failed,response is null", 58 Toast.LENGTH_SHORT).show(); 59 } else if (200 == response.getStatus()) { 60 Log.e(TAG, "user======" + response.toString()); 61 Object obj = response.getResponse(); 62 if (obj == null) { 63 Toast.makeText(MainActivity.this, 64 "request failed,the response field is null", 65 Toast.LENGTH_SHORT).show(); 66 } else { 67 ArrayList<User> users = (ArrayList<User>) 68 JsonUtil.getEntityList(obj.toString(), User.class); 69 if (users == null) { 70 Toast.makeText(MainActivity.this, 71 "request failed,illegal json", 72 Toast.LENGTH_SHORT).show(); 73 } else { 74 StringBuilder allUserInfo = new StringBuilder(); 75 for (User u : users) { 76 allUserInfo.append(u.getName() + ":" + u.getSex() + "," 77 + u.getAge() + "\n"); 78 } 79 tv_welcome.setText(allUserInfo); 80 } 81 } 82 } else { 83 Toast.makeText(MainActivity.this, 84 "request failed," + response.getMessage(), 85 Toast.LENGTH_SHORT).show(); 86 } 87 super.onPostExecute(response); 88 } 89 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 90 } else { 91 Toast.makeText(MainActivity.this, 92 "network is not available", Toast.LENGTH_SHORT).show(); 93 } 94 } 95 }); 96 } 97 98 private void initData() { 99 Intent intent = getIntent(); 100 if (intent != null) { 101 User user = (User) intent.getExtras().getSerializable("user"); 102 if (user != null) { 103 tv_welcome.setText("Welcome," + user.getName()); 104 } 105 } 106 } 107 }
MainActivty的布局文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:fitsSystemWindows="true" 8 tools:context=".MainActivity"> 9 10 <TextView 11 android:id="@+id/tv_welcome" 12 android:layout_width="match_parent" 13 android:layout_height="wrap_content" 14 android:text="欢迎您" 15 android:textSize="18sp" /> 16 17 <Button 18 android:id="@+id/btn_show_all_users" 19 android:layout_width="match_parent" 20 android:layout_height="wrap_content" 21 android:text="显示所有用户" 22 android:textSize="18sp" 23 android:layout_below="@id/tv_welcome" 24 android:layout_marginTop="20dp" /> 25 26 </RelativeLayout>
最后,别忘了配置清单文件,如下所示:
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.jy.jerseyclient" > 4 5 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 6 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> 7 <uses-permission android:name="android.permission.INTERNET" /> 8 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 9 10 <application 11 android:allowBackup="true" 12 android:icon="@mipmap/ic_launcher" 13 android:label="@string/app_name" 14 android:supportsRtl="true" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name=".LoginActivity" 18 android:label="@string/app_name" 19 android:theme="@style/AppTheme.NoActionBar" > 20 <intent-filter> 21 <action android:name="android.intent.action.MAIN" /> 22 23 <category android:name="android.intent.category.LAUNCHER" /> 24 </intent-filter> 25 </activity> 26 27 <activity android:name=".MainActivity" /> 28 </application> 29 30 </manifest>
好了,到此客户端和服务端均已大功告成,整个项目其实就是用最简单的模型来说明怎样利用Jersey这个框架来为我们的移动端搭建后台接口,其中,难免有诸多纰漏之处,请大家多多批评指正,谢谢!
最后,附上项目源码的下载链接:
客户端:①http://download.csdn.net/detail/owoxiangxin12/9492575 ②https://github.com/monkey1992/JerseyClient
服务端:①http://download.csdn.net/detail/owoxiangxin12/9492571 ②https://github.com/monkey1992/JerseyServer