【问题标题】:Async task's messages exchange with Java Servlet异步任务的消息与 Java Servlet 交换
【发布时间】:2015-05-25 16:35:34
【问题描述】:

我有一个简单的应用程序通过异步任务与其 Servlet 后端进行通信。 我在理解消息是如何包装的以及如何操作这些消息的数据结构时遇到了一些麻烦。 我想要做的是接收多个对象或多个异构信息。 我的代码:

public class MyServlet extends HttpServlet {
    ArrayList<Tour> m_tours;

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    resp.setContentType("text/plain");
    resp.getWriter().println("Please use the form to POST to this url");

    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String order = req.getParameter("order");
    resp.setContentType("text/plain");
    if (order == null) {
        resp.getWriter().println("Please enter a name");
    }

      resp.getWriter().println("yay name received");
      ArrayList<Tour> m_tours = getTours(); //returns a populated ArrayList of custom Tour objects
      resp.getWriter().print(m_tours);
}
    private void getTours(){
        //some code here
    }
}`

还有我的异步任务类:

class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private Context context;
@Override
protected String doInBackground(Pair<Context, String>... params) {
    context = params[0].first;
    String order = params[0].second;

    String[] url = new String[3];
    url[0] = "http://192.168.169.85:8080/hello";
    url[1] = "http://10.0.2.2:8080/hello";
    url[2] = "http://192.168.1.102:8080/hello";
    HttpClient httpClient = new DefaultHttpClient(); 
    HttpPost httpPost = new HttpPost(url[2]);

    List<NameValuePair> nameValuePairs = new ArrayList<>(1);
    nameValuePairs.add(new BasicNameValuePair("order", order));
        try {
            // Add name data to request
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // Execute HTTP Post Request
            HttpResponse response = httpClient.execute(httpPost);

            HttpEntity entity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200) {
                return EntityUtils.toString(entity);
            }
                return "Error: " + response
                        .getStatusLine()
                        .getStatusCode() + " " + response
                        .getStatusLine().getReasonPhrase();
        } catch (ClientProtocolException e) {
            return e.getMessage();
        } catch (IOException e) {
            return e.getMessage();
        }
    }

@Override
protected void onPostExecute(String result) {
       String result1 = "Response: "+result;
           Toast.makeText(context, result1, Toast.LENGTH_LONG).show();
    }
}

响应消息以文本形式返回 ArrayList:

 Response: yay name received
 packagename@objectkey1
 packagename@objectkey2
 packagename@objectkey3
 ...
 packagename@objectkeyn

但是,相反,我想要将它按原样存储为 ArrayList。 如何配置我的异步任务以接收我的 m_tours ArrayList 并将其存储在某处以供进一步使用? 此外,如何将其配置为接收多个对象?

* 编辑 *

我已经按照@orip 的建议使用 Gson 进行了尝试,将异步任务设置如下:

@Override
protected String doInBackground(Pair<Context, String>... params) {
    context = params[0].first;
    String order = params[0].second;

    String[] url = new String[3];
    url[0] = "http://192.168.169.85:8080/hello";
    url[1] = "http://10.0.2.2:8080/hello";
    url[2] = "http://192.168.1.102:8080/hello";
    // HttpPost httpPost = new HttpPost("http://semiotic-art-88319.appspot.com/hello");
    HttpClient httpClient = new DefaultHttpClient(); //127.0.0.1 - 10.201.19.153
    HttpPost httpPost = new HttpPost(url[2]);

    List<NameValuePair> nameValuePairs = new ArrayList<>(1);
    nameValuePairs.add(new BasicNameValuePair("order", order));

    try {
        // Add name data to request
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        // Execute HTTP Post Request
        HttpResponse response = httpClient.execute(httpPost);
        if (response.getStatusLine().getStatusCode() == 200) {
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        }
        return "Error: " + response
                .getStatusLine()
                .getStatusCode() + " " + response
                .getStatusLine().getReasonPhrase();
    } catch (ClientProtocolException e) {
        return e.getMessage();
    } catch (IOException e) {
        return e.getMessage();
    }
}

@Override
protected void onPostExecute(String jsonResponse) {
    Gson gson = new Gson();
    tours = (gson.fromJson(jsonResponse, Tours.class));
    Toast.makeText(context, jsonResponse, Toast.LENGTH_LONG).show();
}

在服务器端:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String asyncMessage = req.getParameter("order");
    if(asyncMessage.equals("tours")){
        m_tours = getTours();  //ArrayList<Tour> m_tours;
        Tours tours = new Tours(m_tours);
        resp.setContentType("application/json");
        PrintWriter out = resp.getWriter();
        out.print(new Gson().toJson(tours));
        out.flush();

        resp.getWriter().print(m_tours);
    }

}

但我得到一个错误:

03-23 13:27:09.523  32387-32387/madapps.bicitourbo E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: madapps.bicitourbo, PID: 32387
com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 692 path $
        at com.google.gson.Gson.assertFullConsumption(Gson.java:786)
        at com.google.gson.Gson.fromJson(Gson.java:776)
        at com.google.gson.Gson.fromJson(Gson.java:724)
        at com.google.gson.Gson.fromJson(Gson.java:696)
        at madapps.bicitourbo.ServletPostAsyncTask.onPostExecute(ServletPostAsyncTask.java:92)
        at madapps.bicitourbo.ServletPostAsyncTask.onPostExecute(ServletPostAsyncTask.java:36)
        at android.os.AsyncTask.finish(AsyncTask.java:632)
        at android.os.AsyncTask.access$600(AsyncTask.java:177)
        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:149)
        at android.app.ActivityThread.main(ActivityThread.java:5257)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
        at dalvik.system.NativeStart.main(Native Method)
 Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 692 path $

出现这个错误就行了:

Tour tours = (gson.fromJson(jsonResponse, Tours.class));

我做错了什么?

* EDIT2 * 已解决:

错误:Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON 是因为我调用了两次resp.getWriter().print(),正如@orip 所建议的那样。谢谢!

【问题讨论】:

  • resp.getWriter().print(m_tours); 还在代码中吗?应该吗?
  • 哦,对,不,它不应该在那里。我删除了它,现在它可以工作了;)

标签: java android servlets android-asynctask


【解决方案1】:

将 servlet 的内容类型设置为 application/json 并返回 JSON 字符串(例如使用 Gson 或 Jackson 序列化结果。

在 Android 端,您可以使用 Android 的内置 JSON 类或(更好)使用您在 servlet 中使用的相同库来反序列化 JSON 字符串。

例如,如果Tour 类似于:

public class Tour {
  // some simple int/string/list fields
}

您可以构建一个响应类,例如:

public class Tours {
  private List<Tour> tours;
  // ...
}

然后在服务器端(见this question,我在这里使用Gson):

List<Tour> listOfTours = ...;
Tours tours = new Tours(listOfTours);
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.print((new Gson()).toJson(tours));
out.flush();

在客户端:

String jsonResponse = ...;
Tours tours = (new Gson()).fromJson(jsonResponse, Tours.class);

需要进行一些优化,但这可以帮助您入门。 此外,考虑使用OkHttp 代替HttpClient 进行HTTP 连接,您可能会得到更简单、更健壮的代码。

【讨论】:

  • 感谢您的宝贵建议!它工作顺利;但现在,在 Servlet 中,我在发送消息“响应:收到消息”后将内容类型设置为“应用程序/json”。因此,如何在 Async 任务收到的​​响应中区分 String 消息和 String 格式的序列化 json?因为我要把它们都放在一起。简而言之:如何将我的字符串与我的 json 分开?
  • 只返回 json。如果要添加字符串字段或成功状态,请将它们添加到响应类型(例如,在我的示例中添加到 Tours)
  • 好的,完成了,但我收到了这个错误com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true),正如我在上次编辑中发布的那样。我做错了什么?
  • 您需要在某处发布 json,以便我们查看问题所在(如果需要,可以手动清理以保护隐私)。
  • 假设您在doInBackground 中返回列表,您将在onPostExecute 中获得结果。然后,您可以使用它来保存在字段中、保存到磁盘、更新 UI 等。
猜你喜欢
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多