在 Android 上调用返回 JSON 响应的 HTTP Web API 调用的最有效方法是啥?

Posted

技术标签:

【中文标题】在 Android 上调用返回 JSON 响应的 HTTP Web API 调用的最有效方法是啥?【英文标题】:What is the most efficient way on Android to call HTTP Web API calls that return a JSON response?在 Android 上调用返回 JSON 响应的 HTTP Web API 调用的最有效方法是什么? 【发布时间】:2013-10-03 17:21:47 【问题描述】:

我是完美主义者,我已经使用 Google Places API 进行了 Web API 调用(仅作为示例),但我觉得它有时很慢,或者我可能做得不对。有些博客说我应该使用 androidHttpClient,但我不是,应该吗?

我正在使用返回 json 的 Web API 调用,我不在 UI 线程上运行它们,因此使用 AsyncTask(AsyncTask 是在后台线程上运行的最有效方式还是应该使用其他方法?)

请查看我的代码并告诉我如何才能更有效

public static class NearbySearchRequest extends AsyncTask<String, Void, JSONObject>

    Exception mException = null;

    @Override
    protected void onPreExecute()
    
        super.onPreExecute();
        this.mException = null;
    

    @Override
    protected JSONObject doInBackground(String... params)
    
        StringBuilder urlString = new StringBuilder();
        urlString.append("https://maps.googleapis.com/maps/api/place/nearbysearch/json?");
        urlString.append("key=").append(Constants.GOOGLE_SIMPLE_API_KEY);
        urlString.append("&location=").append(params[0]);
        urlString.append("&sensor=").append("true");
        urlString.append("&language=").append("en-GB");
        urlString.append("&name=").append(params[1]);
        urlString.append("&rankby=").append("distance");

        LogHelper.Log(urlString.toString());

        HttpURLConnection urlConnection = null;
        URL url = null;
        JSONObject object = null;

        try
        
            url = new URL(urlString.toString());
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            urlConnection.connect();
            InputStream inStream = null;
            inStream = urlConnection.getInputStream();
            BufferedReader bReader = new BufferedReader(new InputStreamReader(inStream));
            String temp, response = "";
            while ((temp = bReader.readLine()) != null)
                response += temp;
            bReader.close();
            inStream.close();
            urlConnection.disconnect();
            object = (JSONObject) new JSONTokener(response).nextValue();
        
        catch (Exception e)
        
            this.mException = e;
        

        return (object);
    

    @Override
    protected void onPostExecute(JSONObject result)
    
        super.onPostExecute(result);

        if (this.mException != null)
            ErrorHelper.report(this.mException, "Error # NearbySearchRequest");
    

【问题讨论】:

根据Android developer,您正在使用的 HttpURLConnection 似乎是 Gingerbread 及更高版本的最佳选择。 【参考方案1】:

您使用的 Http 引擎似乎是最佳选择。实际上任何其他第 3 方引擎要么基于 Apache,要么基于 HttpUrlConnection。我更喜欢使用Spring for Android,因为该 API 提供了对 Http Engine 的抽象,并且您实际上不需要关心基于 API 级别使用什么 API。或者你可以使用Volley - 一个非常时尚的库。

我会触摸你的一些代码:

    如果读取流时出现异常怎么办?然后流保持打开状态,连接也保持打开状态。所以我建议有一个 finally 块,无论您是否遇到异常,流和连接都会关闭:

    HttpURLConnection urlConnection = null;
    URL url = null;
    JSONObject object = null;
    InputStream inStream = null;
    try 
        url = new URL(urlString.toString());
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestMethod("GET");
        urlConnection.setDoOutput(true);
        urlConnection.setDoInput(true);
        urlConnection.connect();
        inStream = urlConnection.getInputStream();
        BufferedReader bReader = new BufferedReader(new InputStreamReader(inStream));
        String temp, response = "";
        while ((temp = bReader.readLine()) != null) 
            response += temp;
        
        object = (JSONObject) new JSONTokener(response).nextValue();
     catch (Exception e) 
        this.mException = e;
     finally 
        if (inStream != null) 
            try 
                // this will close the bReader as well
                inStream.close();
             catch (IOException ignored) 
            
        
        if (urlConnection != null) 
            urlConnection.disconnect();
        
    
    

    JSON 解析:您使用的是 Android 标准解析 JSON 的方式,但这不是最快和最容易使用的。 GSON 和 Jackson 更好用。 To make a comparison 对于 JSON 解析器,我会选择 Jackson。这是another SO topic 的比较结果。

    不要像这样连接字符串,因为每次连接字符串都会创建另一个字符串。请改用StringBuilder

    异常处理(无论如何,这在所有编程论坛中都是一个长期争论的话题)。首先,您必须记录它(使用Log 类而不是System.out.printXXX)。然后您需要通知用户:您可以发送消息,或者显示标签或通知。该决定取决于用户案例以及您拨打的电话的相关程度。

这些是我在你的代码中看到的主题。

编辑我意识到我没有回答这个问题:is AsyncTask the most efficient way to run on background thread or should I use something else?

我给出的简短答案是:如果您应该执行一个短期的请求,那么AsyncTask 是完美的。但是,如果您需要获取一些数据并显示它 - 但您不想担心如果屏幕旋转等是否再次下载,我强烈建议使用AsyncTaskLoaderLoaders一般.

如果您需要下载一些大数据,那么您可以使用IntentService,或者对于重量级操作,使用DownloadManager

享受编码!

【讨论】:

非常感谢您的精彩回答!现在,我保存异常的原因是我可以在 onPostExecute 函数中处理它,因为我的错误处理涉及 Toasting e.getMessage(),而我不能在 doInBackground 中敬酒。创建包装对象和保存响应代码不会有同样的问题吗?还请详细介绍赛车异常点 我现在意识到异常保存在AsyncTask 对象中,很抱歉,我将其删除 我将编辑字符串连接,因为最后一句话毁了它:) @gunar,Volley 对于并行短请求很有用,尤其是那些用数据填充视图的请求。它不应用于大数据传输。【参考方案2】:

--------为您的项目创建一个服务处理程序类--------

public class ServiceHandler 

static String response = null;
public final static int GET = 1;
public final static int POST = 2;

public ServiceHandler() 


/*
 * Making service call
 * @url - url to make request
 * @method - http request method
 * */
public String makeServiceCall(String url, int method) 
    return this.makeServiceCall(url, method, null);


/*
 * Making service call
 * @url - url to make request
 * @method - http request method
 * @params - http request params
 * */
public String makeServiceCall(String url, int method,
        List<NameValuePair> params) 
    try 
        // http client
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpEntity httpEntity = null;
        HttpResponse httpResponse = null;

        // Checking http request method type
        if (method == POST) 
            Log.e("in POST","in POST");
            HttpPost httpPost = new HttpPost(url);
            // adding post params
            if (params != null) 
                Log.e("in POST params","in POST params");
                httpPost.setEntity(new UrlEncodedFormEntity(params));
            
            Log.e("url in post service",url);
            httpResponse = httpClient.execute(httpPost);

         else if (method == GET) 
            // appending params to url
            Log.e("in GET","in GET");
            if (params != null) 
                Log.e("in GET params","in GET params");
                String paramString = URLEncodedUtils
                        .format(params, "utf-8");
                url += "?" + paramString;
            
            Log.e("url in get service",url);
            HttpGet httpGet = new HttpGet(url);

            httpResponse = httpClient.execute(httpGet);

        
        httpEntity = httpResponse.getEntity();
        response = EntityUtils.toString(httpEntity);

     catch (UnsupportedEncodingException e) 
        e.printStackTrace();
     catch (ClientProtocolException e) 
        e.printStackTrace();
     catch (IOException e) 
        e.printStackTrace();
    

    return response;


public String makeServiceCallIMAGE(String url, int method,
        List<NameValuePair> params) 
    try 
        // http client
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpEntity httpEntity = null;
        HttpResponse httpResponse = null;

        // Checking http request method type
        if (method == POST) 
            HttpPost httpPost = new HttpPost(url);
            // adding post params
            if (params != null) 
                httpPost.setEntity(new UrlEncodedFormEntity(params));

            

            httpResponse = httpClient.execute(httpPost);

         else if (method == GET) 
            // appending params to url
            if (params != null) 
                String paramString = URLEncodedUtils
                        .format(params, "utf-8");
                url += "?" + paramString;
            
            HttpGet httpGet = new HttpGet(url);

            httpResponse = httpClient.execute(httpGet);

        
        httpEntity = httpResponse.getEntity();
        response = EntityUtils.toString(httpEntity);

     catch (UnsupportedEncodingException e) 
        e.printStackTrace();
     catch (ClientProtocolException e) 
        e.printStackTrace();
     catch (IOException e) 
        e.printStackTrace();
    
    return response;


-------------AsyncTask For Login------

public class Login_Activity extends ActionBarActivity 

//Internet Service
NetworkConnection nw;
ProgressDialog prgDialog;
Boolean netConnection = false;
//

//Login API
String loginURL ="url";
//

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    nw = new NetworkConnection(getApplicationContext());
    prgDialog = new ProgressDialog(this);
    // Set Cancelable as False
    prgDialog.setCancelable(false);

    new LoginOperation().execute();


private class LoginOperation  extends AsyncTask<String, Void, Void> 

    String status, message;
    @Override
    protected void onPreExecute() 
        // Set Progress Dialog Text
        prgDialog.setMessage("Logging...");
        prgDialog.show();
    

    @Override
    protected Void doInBackground(String... urls) 

        if(nw.isConnectingToInternet() == true)
        
            try
            
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
                nameValuePairs.add(new BasicNameValuePair("method", "ClientesLogin"));
                nameValuePairs.add(new BasicNameValuePair("Email", str_Email));
                nameValuePairs.add(new BasicNameValuePair("Senha", str_Password));
                ServiceHandler sh  = new ServiceHandler();
                String response = sh.makeServiceCall(loginURL, ServiceHandler.GET,
                        nameValuePairs);

                Log.e("response", response);

                JSONObject js = new JSONObject(response);
                status = js.getString("status");
                Log.e("status",status);

                if(status.contains("Fail"))
                
                    message = js.getString("message");
                
                /*else
                
                    JSONObject jslogin=js.getJSONObject("user_list");
                    for (int i = 0; i < jslogin.length(); i++) 
                    
                */

            catch(Exception ex)

            
            netConnection = true;
        else
        
            netConnection = false;
        

        return null;
    

    @Override
    protected void onPostExecute(Void result) 
        prgDialog.dismiss();

        if(netConnection == false)
        
            Toast toast = Toast.makeText(getApplicationContext(),"Internet is not available. Please turn on and try again.", Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            toast.show();
        
        else
        
            if(status.contains("Success"))
            
                Toast toast = Toast.makeText(getApplicationContext(), "Login Successful", Toast.LENGTH_SHORT);
                toast.setGravity(Gravity.CENTER, 0, 0);
                toast.show();

                Intent i=new Intent(Login_Activity.this,home_page_activity.class);
                startActivity(i);
            
            else
                Toast toast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT);
                toast.setGravity(Gravity.CENTER, 0, 0);
                toast.show();
            
        
        super.onPostExecute(result);
    


----------------网络连接类---------

public class NetworkConnection 

Context context;

public NetworkConnection(Context context)
    this.context = context;


public boolean isConnectingToInternet()
    ConnectivityManager connectivity = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
      if (connectivity != null) 
      
          NetworkInfo[] info = connectivity.getAllNetworkInfo();
          if (info != null) 
              for (int i = 0; i < info.length; i++) 
                  if (info[i].getState() == NetworkInfo.State.CONNECTED)
                  
                      return true;
                  
      
      return false;



JSONArray main1 = js.getJSONArray("Test 1");

  for (int i = 0; i < main1.length(); i++) 

    JSONObject jsonObject = main1.getJSONObject(i);

【讨论】:

以上是关于在 Android 上调用返回 JSON 响应的 HTTP Web API 调用的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 可调用函数在 Android SDK 中不返回 JSON 响应

调用 WCF Web 服务时出现错误的 Json 响应

有没有一种简单的方法可以在 Android 上展平 json:api 响应?

我是不是需要在 json 响应中返回 ios 的图像尺寸?

如何在android的Asynctask中显示json响应[重复]

如何为未经授权的 AJAX 调用而不是登录页面返回 JSON 响应作为 AJAX 响应?