补充:关于PHP服务端可能出现的问题:
如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!!
在android应用中访问的IP都是10.0.2.2,如果在apache虚拟主机配置文件中配置了多个虚拟主机,那么将默认解析为对第一个虚拟主机的请求,所以,在调试android应用时,应该将对应的服务端所配置的那个虚拟主机放在配置文件中的第一个虚拟主机的位置。否则就会出现请求的文件不存在等的错误。
服务端返回JSON数据及在android应用中解析JSON数据:
1.新建一个json.php文件,返回json格式的数据:
<?php if(isset($_REQUEST["username"])&& isset($_REQUEST["password"])){ $username = $_REQUEST["username"]; $password = $_REQUEST["password"]; if($username == "zhangsan" && $password == "123"){ $arr = array( "errorCode"=>200, "errorMsg"=>"login success" ); echo json_encode($arr); }else{ $arr = array( "errorCode"=>404, "errorMsg"=>"login failure" ); echo json_encode($arr); } }else{ $arr = array( "errorCode"=>500, "errorMsg"=>"illeagle params" ); echo json_encode($arr); } ?>
返回的信息格式如下:
{"errorCode":500,"errorMsg":"illeagle params"}
一个大括号内的表示的是一个json对象,内部存储的是键值对数据,键与值用冒号分隔,多个键值对之间用逗号分隔。
形式如同下面格式的是json array对象:
[{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"}]
中括号里面存放的是用逗号分隔的一个个json对象。
2.Android中要解析json格式数据需要用到的类有JSONObject和JSONArray:
JSONObject直接使用传入字符串形式的参数的构造方法创建JSONObject实例,然后调用相应的get方法,即可获取json形式数据中的每个键对应的值。
当返回的数据是用中括号括着的多个json形式字符串是,就要用到JSONArray了,同样可以使用字符串参数构造出JSONArray实例,然后可以使用下标的形式获取到其中的每一个JSONObject对象,然后使用JSONObject的方法分别取解析即可。
简单使用示例:
在MainActivity中添加上修改Handler的消息处理方法,当前通过get或者post取得的是json格式的数据,所以添加上对json数据的解析:
private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { if(msg.what == OK){ String str = msg.obj.toString(); try { JSONObject obj = new JSONObject(str); Log.i(TAG,obj.getString("errorCode")); Log.i(TAG,obj.getString("errorMsg")); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } };
运行结果:
有时候,服务端返回的可能是一些实体类信息,若用户登录成功,返回存放用户个人信息用以缓存在本地,这时,可能希望直接能将返回的JSON格式的数据解析为实体类对象,以简化操作。使用JSONObject类当然也能实现,只要获取到各个字段的值,然后通过它们构造实体类对象即可。但是,还有更简单的方式,那就是使用GSON。
3.使用GSON解析json数据:
要在自己项目中使用GSON,有两种方式:
方式1:从网上下载gson的jar包,放到项目的libs目录中,然后添加到项目的build path中,就可以直接使用GSON了
方式2:下载GSON的源码,可以到github上下载,然后把源码整个复制到src目录下,之后也可以直接使用GSON了。
下面是GSON的简单使用示例,更多的使用可以参考GSON的帮助文档:在下载源码的包中有docs目录,里面就是GSON使用的帮助文档,可以在需要时参考。
新建一个实体类,存放返回的errorCode和errorMsg:
package cn.csc.start.bean; public class ResponseInfo { private int errorCode; private String errorMsg; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public ResponseInfo(int errorCode, String errorMsg) { super(); this.errorCode = errorCode; this.errorMsg = errorMsg; } public ResponseInfo() { super(); } }
修改Handler中消息处理中解析json数据的代码,这次改用GSON方式解析:
Gson gson = new Gson(); ResponseInfo info = gson.fromJson(str, ResponseInfo.class); Toast.makeText(MainActivity.this, info.getErrorCode()+info.getErrorMsg(), Toast.LENGTH_LONG).show();
注意到,使用GSON的方式解析到实体类对象非常简单:
没有特别的需求时,使用默认构造创建GSON实例即可,需要特殊的配置时,可以使用GsonBuilder来创建GSON实例,具体参考说明文档
调用fromJson()传入要解析的数据,及解析后的对象类型,即可完成从json格式字符串到实体类对象的解析。
若是json array形式的字符串,解析则稍微麻烦一点:
如:想要解析[{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"}]为List<ResponseInfo>
但是不能List<ResponseInfo>.class,这时GSON中提供了获取泛型的类型信息的方法:
使用TypeToken:
TypeToken< List<ResponseInfo>> list = new TypeToken< List<ResponseInfo>>() {};
然后调用list.getType()注意返回的是Type类型的对象,而Gson的fromJson()方法刚好有第二个参数为Type类型的重载形式。
所以,对[{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"},{"errorCode":200,"errorMsg":"login success"}]解析的具体代码如下:
Gson gson = new Gson(); TypeToken<List<ResponseInfo>> list = new TypeToken<List<ResponseInfo>>(){}; List<ResponseInfo> info_list = gson.fromJson(str, list.getType());
以上,就是两种对json形式数据的解析方式。
与服务器交互的方式除了内置的HttpURLConnection和HttpClient之外,还可以选择一些优秀的开源项目,来简化与服务端交互的工作,如使用AsyncHttpClient:
AsyncHttpClient可以到github上获取:
在github中搜说async-http即可找到该项目,然后clone或者下载zip包就可以获得该项目了。
在自己的项目中使用async-http的方式如同GSON,也可以直接使用源码,或者引用jar包,根据个人喜好即可。
简单演示下async-http发送get和post请求的示例:
在MainActivity中添加两个方法:
使用AsyncHttpClient发送get请求:
private void async_get_test(){ String username = et_username.getText().toString(); String password = et_password.getText().toString(); AsyncHttpClient client = new AsyncHttpClient(); client.get("http://10.0.2.2/index.php?username="+username+"&password="+password, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub Message msg = new Message(); msg.what = OK; msg.obj = new String(responseBody); handler.sendMessage(msg); } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { // TODO Auto-generated method stub Message msg = new Message(); msg.what = OK; msg.obj = new String(responseBody); handler.sendMessage(msg); } }); }
使用AsyncHttpClient发送post请求:
private void async_post_test(){ String username = et_username.getText().toString(); String password = et_password.getText().toString(); AsyncHttpClient client = new AsyncHttpClient(); RequestParams params = new RequestParams(); params.add("username", username); params.add("password", password); client.post("http://10.0.2.2/index.php", params , new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { // TODO Auto-generated method stub Message msg = new Message(); msg.what = OK; msg.obj = new String(responseBody); handler.sendMessage(msg); } @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { // TODO Auto-generated method stub Message msg = new Message(); msg.what = OK; msg.obj = new String(responseBody); handler.sendMessage(msg); } }); }
可以看到使用AsyncHttpClient的方式非常简单:
get方式:
创建AsyncHttpClient对象,然后调用get方法即可。
唯一稍微复杂的就是get方法的第二个参数:AsyncHttpResponseHandler,需要创建一个匿名内部类对象,然后重写AsyncHttpResponseHandler的两个方法,分别是网络请求成功的回调方法及网络请求失败的回调方法。
post方式:
就比get方式多了一个传递参数而已:
RequestParams params = new RequestParams();
params.add("username", username);
params.add("password", password);
使用RequestParams对象存放要提交的参数,然后将其传递给post()方法即可。