2

我正在开发一个 android 应用程序,并且在注册用户时遇到了一些麻烦。我想将一个 JSON 对象发布到我的服务器并收到一个返回。我可以使用正确的信息成功地创建一个 JSON 对象,但是当我发布它时,我得到一个 NetworkOnMainThreadException 或者我的 HttpClient 类在它应该返回一个 JSONObject 时返回 null,我非常有信心我的 Web 服务器可以正常工作。我知道您无法在主线程上连接到网络,并创建了一个使用 AsnycTask 的 HttpClient 类(尽管可能不正确)。我已经为此工作了很长一段时间,并希望得到任何正确方向的指导。

//Main activity
@Override
public void onClick(View arg0) {
    if(!(isEmpty(name) || isEmpty(username) || isEmpty(password) || isEmpty(email))) {
        user = new JSONObject();
        try {
            user.put("username", username.getText().toString());
            user.put("name", name.getText().toString());
            user.put("email", email.getText().toString());
            user.put("password", password.getText().toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }

        jRegister = new JSONObject();

        try {
            jRegister.put("apiToken", Utilities.apiToken);
            jRegister.put("user", user);

            Log.i("MainActivity", jRegister.toString(2));

        } catch (JSONException e) {
            e.printStackTrace();
        }

       //
        HttpClient client = new HttpClient(url, jRegister);
        result = client.getJSONFromUrl();


        try {
            if(result  != null)
                tv.setText(result.toString(2));
            else
                 tv.setText("null");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }else {
        tv.setText("");
    }
}

HttpClient 类

public class HttpClient extends AsyncTask<Void, Void, JSONObject>{
    private final String TAG = "HttpClient";
    private String URL;
    private JSONObject jsonObjSend;
    private JSONObject result = null;

    public HttpClient(String URL, JSONObject jsonObjSend) {
        this.URL = URL;
        this.jsonObjSend = jsonObjSend;
    }

    public JSONObject getJSONFromUrl() {
        this.execute();
        return result;
    }

    @Override
    protected JSONObject doInBackground(Void... params) {

            try {
                    DefaultHttpClient httpclient = new DefaultHttpClient();
                    HttpPost httpPostRequest = new HttpPost(URL);

                    StringEntity se;
                    se = new StringEntity(jsonObjSend.toString());

                    // Set HTTP parameters
                    httpPostRequest.setEntity(se);
                    httpPostRequest.setHeader("Accept", "application/json");
                    httpPostRequest.setHeader("Content-type", "application/json");

                    long t = System.currentTimeMillis();
                    HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
                    Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                            // Read the content stream
                            InputStream instream = entity.getContent();

                            // convert content stream to a String
                            String resultString= convertStreamToString(instream);
                            instream.close();
                            resultString = resultString.substring(1,resultString.length()-1); // remove wrapping "[" and "]"

                            JSONObject jsonObjRecv = new JSONObject(resultString);

                            // Raw DEBUG output of our received JSON object:
                            Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");

                            return jsonObjRecv;
                    } 

            }
            catch (Exception e) {
                    e.printStackTrace();
            }
            return null;
    }

    protected void onPostExecute(JSONObject jObject) {
        result = jObject;
    }
    private static String convertStreamToString(InputStream is) {

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();

            String line = null;
            try {
                    while ((line = reader.readLine()) != null) {
                            sb.append(line + "\n");
                    }
            } catch (IOException e) {
                    e.printStackTrace();
            } finally {
                    try {
                            is.close();
                    } catch (IOException e) {
                            e.printStackTrace();
                    }
            }
            return sb.toString();
    }

}

4

2 回答 2

1

我知道您无法在主线程上连接到网络,并创建了一个使用 AsnycTask 的 HttpClient 类(尽管可能不正确)。

你是对的,你没有以正确的方式实现它。

在您的onClick事件中(仍在主线程上)您执行了导致错误的网络活动:

HttpClient client = new HttpClient(url, jRegister);
result = client.getJSONFromUrl();

相反,您应该在 AsnycTask 内运行网络操作

public class GetJsonTask extends AsyncTask<Void, Void, JSONObject >{
    private String URL;
    private JSONObject jsonObjSend;

    public GetJsonTask(String URL, JSONObject jsonObjSend) {
        this.URL = URL;
        this.jsonObjSend = jsonObjSend;
    }

    @Override
    protected JSONObject doInBackground(Void... params) {
    JSONObject jsonObjRecv;
           try {
                    DefaultHttpClient httpclient = new DefaultHttpClient();
                    HttpPost httpPostRequest = new HttpPost(URL);

                    StringEntity se;
                    se = new StringEntity(jsonObjSend.toString());

                    // Set HTTP parameters
                    httpPostRequest.setEntity(se);
                    httpPostRequest.setHeader("Accept", "application/json");
                    httpPostRequest.setHeader("Content-type", "application/json");

                    long t = System.currentTimeMillis();
                    HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
                    Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                            // Read the content stream
                            InputStream instream = entity.getContent();

                            // convert content stream to a String
                            String resultString= convertStreamToString(instream);
                            instream.close();
                            resultString = resultString.substring(1,resultString.length()-1); // remove wrapping "[" and "]"

                            jsonObjRecv = new JSONObject(resultString);

                            // Raw DEBUG output of our received JSON object:
                            Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");


                    } 

            }
            catch (Exception e) {
                    e.printStackTrace();
            }
            return jsonObjRecv;         
    }

    protected void onPostExecute(JSONObject result) {
        try {
            if(result  != null)
                tv.setText(result.toString(2));
            else
                 tv.setText("null");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }else {
        tv.setText("");
    }
    }

}

然后你在 onclik 方法中调用你的异步,如下所示:

public void onClick(View arg0) {
//.......
GetJsonTask client = new GetJsonTask(url, jRegister);
client.execute();
}
于 2013-11-05T16:12:07.150 回答
0

您的代码中的一个问题是您的期望AsyncTask并不完全正确。特别是这个功能:

public JSONObject getJSONFromUrl() {
    this.execute();
    return result;
}

AsyncTaskdoInBackground()在单独的线程中运行函数中的代码。这意味着一旦你调用execute(),你就有两条并行的执行线。您最终会得到所谓的Race Condition。当你到达这return result条线时,可能会发生几件事:

  • doInBackground()尚未运行,因此result仍具有默认值。在这种情况下null
  • doInBackground()可以在代码中间。在您的特定情况下,因为它不会修改result,所以这不会对您产生太大影响。但是当返回发生时,它可能在任何一行(如果操作不是原子的,有时也可能在一行中间)。
  • doInBackground()本可以完成,但由于onPostExecute()在 UI 线程上运行,它必须等到您的onClick处理程序完成。到onPostExecute()有机会运行时,onClick已经尝试使用返回tv的任何内容进行更新getJSONFromUrl,很可能是null.

设置任务的方法AsyncTask是给它完成工作所需的信息,用execute启动它,由于你不知道需要多长时间才能完成,让它处理任务的完成步骤.

这意味着在调用之后,execute您不必等待更新视图的结果(就像您的情况一样),而是依靠AsyncTask'sonPostExecute或相关方法来接管后续步骤。

对于您的情况,这意味着您onPostExecute应该看起来像:

protected void onPostExecute(JSONObject result) {
    try {
        if(result  != null)
            tv.setText(result.toString(2));
        else
            tv.setText("null");
    } catch (JSONException e) {
        e.printStackTrace();
    }
}
于 2013-11-05T22:37:07.683 回答