SpringMVC Android 发送JSON数据

最近在倒腾自己的毕业设计,需要通过Android手机采集sensors 的数据,然后通过post方式向服务器发送json格式的数据,以json数组的方式发送。服务器端采用SpringMVC+hibernate的方式搭建。服务器端接收客户端传来的json数据,通过service保存到数据库,然后返回json格式的消息。客户端接收返回消息,显示上传成功或失败。

看似很easy的事情,其实好多坑,坑了我好久。下面一一详细说明。

1、 首先是服务器端的配置,搭建Spring+hibernate的具体配置方法就不细说了,可以参考我上篇文章。首先看下Controller怎么写:

@Controller
@RequestMapping("/upload")
public class UploadController {
    @Autowired
    private SensordataService sensordataService;

    @RequestMapping(value = "/data",method = RequestMethod.POST)
    @ResponseBody
    public JsonResponse uploadData(@RequestBody List<Sensordata> list){
        try {
            sensordataService.saveAll(list);
            System.out.println("save to database");
            return new JsonResponse(WebStatus.UPLOAD_SUCCESSFULL,"");
        }catch (Exception e) {
            return new JsonResponse(WebStatus.UPLOAD_FAILED,e.getMessage());
        }
    }
}

首先@RequestBody的作用是让传进来的jsonArray格式的Sensordata数组自动封装成List<Sensordata>形式。非常方便。如果传来的是单个的Sensordata对象,则可以写成

@Request Sensordata data.  

其次@ResponseBody 将会根据传进来的数据格式,自动把返回的对象封装成相应的格式,比如我这里传入的时json格式,则@ResponseBody则自动把返回的JsonResponse对象封装成json格式

当然,这些需要提前,首先是设置POST的内容为json格式,这个后面细说。其次是需要在Spring-MVC.xml的配置文件中添加下面一行

<mvc:annotation-driven />

最后增加依赖:

 <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.8.3</version>
        </dependency>

这样,服务器端基本完毕,可以单独写个类测试下,是否可以接收Json格式的数据,这里建议不要在android下写,因为android下也有坑。出错了你就不知道到底是哪里错了。

2. 服务器端涉及的类

第一个是POJO类,对应数据库的sensordata表。用于保存androd传来的sensor数据。

package org.bnu.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

/**
 * Created by xie on 14-11-6.
 */
@Entity
@Table(name = "sensordata")
public class Sensordata {
    @Id
    @GeneratedValue
    @Column(name="id")
    @Getter@Setter
    private Integer id;
    @Column(name="accX")
    @Getter@Setter
    private Double accX;
    @Column(name = "accY")
    @Getter@Setter
    private Double accY;
    @Column(name = "accZ")
    @Getter@Setter
    private Double accZ;
    @Column(name = "gyroX")
    @Getter@Setter
    private Double gyroX;
    @Column(name = "gyroY")
    @Getter@Setter
    private Double gyroY;
    @Column(name = "gyroZ")
    @Getter@Setter
    private Double gyroZ;
    @Column(name = "magnetX")
    @Getter@Setter
    private Double magnetX;
    @Column(name = "magnetY")
    @Getter@Setter
    private Double magnetY;
    @Column(name = "magnetZ")
    @Getter@Setter
    private Double magnetZ;
    @Column(name = "orientX")
    @Getter@Setter
    private Double orientX;
    @Column(name = "orientY")
    @Getter@Setter
    private Double orientY;
    @Column(name = "orientZ")
    @Getter@Setter
    private Double orientZ;
    @Column(name = "timestamp")
    @Getter@Setter
    private String timestamp;
}

第一个类是返回的对象,用于返回消息。

package org.bnu.util;

import lombok.Getter;
import lombok.Setter;

/**
 * Created by xie on 14-11-8.
 */
public class JsonResponse {
    @Getter@Setter
    private String status;
    @Getter@Setter
    private String errorMessage;

    public JsonResponse(String status,String errorMessage){
        this.status=status;
        this.errorMessage=errorMessage;
    }
}

3. Android客户端

首先第一点需要申明的是,Android从3.0开始就不允许在主线程,即UI线程里直接访问web。

为什么?访问Web往往需要较长时间,在UI线程访问Web则会导致UI线程阻塞,等待网络返回。结果就是你的界面卡住,不能做其他交互。

为了防止这类情况,android从系统上禁止在UI线程访问web。说明了这个大坑后,先看下AndroidManifest.xml的配置,增加网络访问权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sensorcollect"
    android:versionCode="1"
    android:versionName="1.0" >
	<!-- 取得网络访问权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 取得访问网络状态权限 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
    <!-- 取得访问wifi状态权限 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 取得改变wifi状态权限 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> 
    
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.sensorcollect.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

然后,我们在UI线程外,访问web,把数据上传到服务器,这里是用AsynTask.

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bt2.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
                if(isConnected(getApplicationContext())) {
                    new DataUploadTask(getApplicationContext()).execute();
                }else{
                    Toast.makeText(getApplicationContext(),"web is not connected",Toast.LENGTH_LONG).show();
                }
			}
		});
	}

用button控件触发AsynTask, AsynTask写法如下:

	private class DataUploadTask extends AsyncTask<Void,Void,String>{
        private Context context;

        public DataUploadTask(Context context){
            this.context=context;
        }

        @Override
        protected String doInBackground(Void... params) {
            WebService webService=new WebService(context);
            boolean res=webService.upload();
            if(res){
                return WebStatus.UPLOAD_SUCCESSFULL;
            }else{
                return WebStatus.UPLOAD_FAILED;
            }
        }

        @Override
        protected void onPostExecute(String s) {
            if(s.equals(WebStatus.UPLOAD_SUCCESSFULL)){
                Toast.makeText(context,"data upload successfully",Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(context,"data upload failed",Toast.LENGTH_LONG).show();
            }
        }
    }

在doInBackground中,调用webService 的upload方法,实现数据的上传。根据返回值,在PostExcute显示数据上传成功或失败

下面看webService如何实现,主要看如果用post的方式发送json数据。

package com.example.sensorcollect;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;

import android.content.Context;
import org.json.JSONException;
import org.json.JSONObject;

public class WebService {
	private static DbManager dbManager;
	private static Context context;
    private static final String TAG="sensorCollect";
	public WebService(Context context){
		dbManager=new DbManager(context);
		this.context=context;
	}
	public boolean upload(){
		String json_str=dbManager.queryAllInJson();
		StringBuffer sb=new StringBuffer();
		try {
            <span style="white-space:pre">		</span>String url="http://172.16.144.48:8080/dataserver/upload/data";
			HttpClient client=new DefaultHttpClient();
			HttpPost request=new HttpPost(url);
            <span style="white-space:pre">		</span>System.out.println(json_str);
            <span style="white-space:pre">		</span>StringEntity stringEntity=new StringEntity(json_str);
            <span style="white-space:pre">		</span>stringEntity.setContentType("application/json");
            <span style="white-space:pre">		</span>request.setEntity(stringEntity);

			HttpResponse response=client.execute(request);
			HttpEntity entity=response.getEntity();
            <span style="white-space:pre">		</span>StringBuilder builder=new StringBuilder();
			if(entity!=null){
				BufferedReader reader=new BufferedReader(new InputStreamReader(entity.getContent()));
				String line="";
				while((line=reader.readLine())!=null){
                    <span style="white-space:pre">			</span>builder.append(line+"\n");
				}
				reader.close();
			}
			JSONObject object=new JSONObject(builder.toString());
			String status=object.getString("status");
            <span style="white-space:pre">		</span>if(status.equals(WebStatus.UPLOAD_SUCCESSFULL)) {
                <span style="white-space:pre">		</span>return true;
           <span style="white-space:pre">		</span>}else if(status.equals(WebStatus.UPLOAD_FAILED)){
              <span style="white-space:pre">			</span>Log.i(TAG,object.getString("errorMessage"));
            <span style="white-space:pre">		</span>    <span style="white-space:pre">	</span>return false;
           <span style="white-space:pre">		</span>}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSONException e) {
            e.printStackTrace();
        }
        return false;
	}

}

首先把待传数据转成json数组的格式。然后StringEntity把json串塞进去,并把Content-type设为application/json格式。这步非常重要,这样服务器端就知道传入的是json格式的数据,spring就可以自动将数据从json格式的转成List<Sensordata>的形式。


4. 小结。JSON格式的数据处理可以用alibaba提供的fastjson,听说性能很高,主要是用起来非常方便,只需要引入依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.41</version>
        </dependency>

List<T> 转JSON数组

List<SensorBean> datalist=queryAll();
String json=JSON.toJSONString(datalist);

JSON数组转List<T>

List<Sensordata> list=JSON.parseArray(json_str,Sensordata);



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值