如何处理Java/Android中的网络连接缓慢问题。

5
我有一个使用MySQL数据库的应用程序,其中许多调用都在AsyncTask内部完成。以下是一个示例。
我的主要问题是这样的:有时候,主机(Godaddy)会决定暂停连接,我的progressDialog会一直加载,直到强制关闭并导致应用程序崩溃。特别是如果用户试图打断它(大多数我设置为不可取消)。
除了下面所示的try/catch之外,是否有更好的处理方法?但我不确定如何利用它优化。
class Task extends AsyncTask<String, String, Void> {
        private ProgressDialog progressDialog = new ProgressDialog(
                MasterCat.this);
        InputStream is = null;
        String result = "";

        protected void onPreExecute() {
            progressDialog.setMessage("Loading...");
            progressDialog.show();
            progressDialog.setCancelable(false);
        }

        @Override
        protected Void doInBackground(String... params) {
            String url_select = "http://www.---.com/---/master.php";

            HttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url_select);
            ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();

            try {
                httpPost.setEntity(new UrlEncodedFormEntity(param));
                HttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();

                // read content
                is = httpEntity.getContent();

            } catch (Exception e) {

                Log.e("log_tag", "Error in http connection " + e.toString());
            }
            try {
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(is));
                StringBuilder sb = new StringBuilder();
                String line = "";
                while ((line = br.readLine()) != null) {
                    sb.append(line + "\n");
                }
                is.close();
                result = sb.toString();

            } catch (Exception e) {
                // TODO: handle exception
                Log.e("log_tag", "Error converting result " + e.toString());
            }

            return null;

        }

        protected void onPostExecute(Void v) {

            String cat;
            try {
                jArray = new JSONArray(result);
                JSONObject json_data = null;
                for (int i = 0; i < jArray.length(); i++) {
                    json_data = jArray.getJSONObject(i);
                    cat = json_data.getString("category");

                    cats.add(cat);

                }
            } catch (JSONException e1) {
                Toast.makeText(getBaseContext(), "No Categories Found",
                        Toast.LENGTH_LONG).show();
            } catch (ParseException e1) {
                e1.printStackTrace();
            }

            ListView listView = getListView();
            listView.setTextFilterEnabled(true);

            listView.setOnItemClickListener(new OnItemClickListener() {

                public void onItemClick(AdapterView<?> arg0, View arg1,
                        int arg2, long id) {

                    Intent i = new Intent(getApplicationContext(), Items.class);
                    i.putExtra("category", cats.get(arg2));
                    startActivity(i);

                }
            });

            progressDialog.dismiss();

            MasterCatAdapter adapter = new MasterCatAdapter(MasterCat.this,
                    cats);
            setListAdapter(adapter);

        }
    }

编辑:我现在假设崩溃是由于连接不良引起的;但是我将尝试在可以重新创建它时获取alogcat。

编辑2:这里是LogCat:

08-13 14:57:00.580: E/WindowManager(2262): Activity com.---.---.MyFragmentActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@42b02cd0 that was originally added here
08-13 14:57:00.580: E/WindowManager(2262): android.view.WindowLeaked: Activity com.---.---.MyFragmentActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@42b02cd0 that was originally added here
08-13 14:57:00.580: E/WindowManager(2262):  at android.view.ViewRootImpl.<init>(ViewRootImpl.java:374)
08-13 14:57:00.580: E/WindowManager(2262):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:292)
08-13 14:57:00.580: E/WindowManager(2262):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
08-13 14:57:00.580: E/WindowManager(2262):  at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
08-13 14:57:00.580: E/WindowManager(2262):  at android.view.Window$LocalWindowManager.addView(Window.java:547)
08-13 14:57:00.580: E/WindowManager(2262):  at android.app.Dialog.show(Dialog.java:277)
08-13 14:57:00.580: E/WindowManager(2262):  at com.---.---.MyFragmentActivity$RateFragment$RatingTask.onPreExecute(MyFragmentActivity.java:374)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.AsyncTask.execute(AsyncTask.java:534)
08-13 14:57:00.580: E/WindowManager(2262):  at com.---.---.MyFragmentActivity$RateFragment$insertTask.onPostExecute(MyFragmentActivity.java:520)
08-13 14:57:00.580: E/WindowManager(2262):  at com.---.---.MyFragmentActivity$RateFragment$insertTask.onPostExecute(MyFragmentActivity.java:1)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.AsyncTask.finish(AsyncTask.java:631)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.AsyncTask.access$600(AsyncTask.java:177)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.Handler.dispatchMessage(Handler.java:99)
08-13 14:57:00.580: E/WindowManager(2262):  at android.os.Looper.loop(Looper.java:137)
08-13 14:57:00.580: E/WindowManager(2262):  at android.app.ActivityThread.main(ActivityThread.java:4745)
08-13 14:57:00.580: E/WindowManager(2262):  at java.lang.reflect.Method.invokeNative(Native Method)
08-13 14:57:00.580: E/WindowManager(2262):  at java.lang.reflect.Method.invoke(Method.java:511)
08-13 14:57:00.580: E/WindowManager(2262):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
08-13 14:57:00.580: E/WindowManager(2262):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
08-13 14:57:00.580: E/WindowManager(2262):  at dalvik.system.NativeStart.main(Native Method)
08-13 14:57:00.588: D/AndroidRuntime(2262): Shutting down VM
08-13 14:57:00.588: W/dalvikvm(2262): threadid=1: thread exiting with uncaught exception (group=0x4200b300)
08-13 14:57:00.596: E/AndroidRuntime(2262): FATAL EXCEPTION: main
08-13 14:57:00.596: E/AndroidRuntime(2262): java.lang.NullPointerException
08-13 14:57:00.596: E/AndroidRuntime(2262):     at com.---.---.MyFragmentActivity$RateFragment$RatingTask.onPostExecute(MyFragmentActivity.java:461)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at com.---.---.MyFragmentActivity$RateFragment$RatingTask.onPostExecute(MyFragmentActivity.java:1)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.os.AsyncTask.finish(AsyncTask.java:631)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.os.AsyncTask.access$600(AsyncTask.java:177)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.os.Looper.loop(Looper.java:137)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at android.app.ActivityThread.main(ActivityThread.java:4745)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at java.lang.reflect.Method.invokeNative(Native Method)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at java.lang.reflect.Method.invoke(Method.java:511)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
08-13 14:57:00.596: E/AndroidRuntime(2262):     at dalvik.system.NativeStart.main(Native Method)

编辑:这是在不同活动中的任务,但在LogCat中被引用:

class RatingTask extends AsyncTask<String, String, Void> {

            private ProgressDialog progressDialog = new ProgressDialog(
                    getActivity());

            InputStream is = null;
            String result = "";

            protected void onPreExecute() {
                progressDialog.setMessage("Loading...");
                progressDialog.show();
                progressDialog.setOnCancelListener(new OnCancelListener() {

                    public void onCancel(DialogInterface dialog) {
                        RatingTask.this.cancel(true);
                    }
                });
            }

            @Override
            protected Void doInBackground(String... params) {
                String url_select = "http://www.---.com/---/get_ratings.php";

                ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
                param.add(new BasicNameValuePair("item", Item));
                param.add(new BasicNameValuePair("category", Category));

                HttpClient httpClient = new DefaultHttpClient();
                HttpPost httpPost = new HttpPost(url_select);

                try {
                    httpPost.setEntity(new UrlEncodedFormEntity(param));
                    HttpResponse httpResponse = httpClient.execute(httpPost);
                    HttpEntity httpEntity = httpResponse.getEntity();

                    // read content
                    is = httpEntity.getContent();

                } catch (Exception e) {

                    Log.e("log_tag", "Error in http connection " + e.toString());
                }
                try {
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(is));
                    StringBuilder sb = new StringBuilder();
                    String line = "";
                    while ((line = br.readLine()) != null) {
                        sb.append(line + "\n");
                    }
                    is.close();
                    result = sb.toString();

                } catch (Exception e) {
                    Log.e("log_tag", "Error converting result " + e.toString());
                }

                return null;

            }

            protected void onPostExecute(Void v) {

                String starTotal = null, starAvg = null;
                try {
                    JSONArray jArray = new JSONArray(result);
                    JSONObject json_data = null;
                    for (int i = 0; i < jArray.length(); i++) {
                        json_data = jArray.getJSONObject(i);

                        starTotal = json_data.getString("TotalRating");
                        starAvg = json_data.getString("AverageRating");

                    }
                } catch (JSONException e1) {
                    Log.e("log_tag",
                            "Error in http connection " + e1.toString());
                    Toast.makeText(getActivity(), "JSONexception",
                            Toast.LENGTH_LONG).show();
                } catch (ParseException e1) {
                    e1.printStackTrace();
                }

                int total = 0;

                if (starTotal != null) {
                    total = Integer.parseInt(starTotal);
                } else {
                    starTotal = "0";
                }

                if (total > 0) {
                    total = Integer.parseInt(starTotal);
                } else {
                    total = 0;
                }

                StarTotal = (TextView) getActivity().findViewById(
                        R.id.tvStarTotal);
                StarTotal.setText("(" + String.valueOf(total) + (")"));

                float avg = 0.f;
                try {
                    avg = Float.parseFloat(starAvg);
                } catch (NumberFormatException e) {
                    avg = 0;
                }

                DecimalFormat myFormat = new DecimalFormat("0.00");
                StarNumbers = (TextView) getActivity().findViewById(
                        R.id.tvStarNumber);
                StarNumbers.setText(myFormat.format(avg));

                ratingsBarTwo.setRating(Float.valueOf(avg));
                progressDialog.dismiss();
            }
        }

2
首先,考虑httpclient上的http超时并捕获这些异常。为了处理进度对话框,我宁愿在任务之前显示它,并在获取超时异常后使用Handler进行处理。这是一个很好的示例:https://dev59.com/Am855IYBdhLWcg3wGQRM - Sergey Benner
那我在哪里会使用AsyncTask呢?它们不是与onCreate中正在发生的任何事情并行(异步)运行的吗?(我在onCreate中调用任务。) - TheLettuceMaster
请您发布logcat的异常,并检查我上面写的链接。 - Sergey Benner
我刚刚添加了一个LogCat。看起来发生的事情比连接问题要多得多,但可能是我的用户界面出了问题。 - TheLettuceMaster
1
从一开始就存在连接不良的问题。连接不良会导致后续没有有效数据和空变量。在使用数据之前,请检查其是否为空,并通过在一定时间内取消http请求来处理连接不良(无连接)的情况。 - Wroclai
显示剩余3条评论
3个回答

5

首先检查是否有可用的连接,如果没有则通知用户。

特别是当用户试图中断它时(尽管我将大多数设置为不可取消)。

个人认为应该重新考虑这个决定。我不喜欢不可中断的进程。我的建议是,继续从@CommonsWare在评论这里中提出的建议开始。简而言之,有一个布尔变量来检查数据是否无效或者您自己的检查来查看数据是否为空。如果是,则不执行任何基于此数据的命令,您就不会遇到与此相关的强制关闭问题。

除了上面所述的内容,还有更好的处理方法吗?

除了上述建议外,我建议向您的http客户端添加一些HTTP参数。例如:

final int CONN_WAIT_TIME = 3000;
final int CONN_DATA_WAIT_TIME = 2000;

HttpParams httpParams = new BasicHttpParams();      
HttpConnectionParams.setConnectionTimeout(httpParams, CONN_WAIT_TIME);
HttpConnectionParams.setSoTimeout(httpParams, CONN_DATA_WAIT_TIME);

DefaultHttpClient postClient = new DefaultHttpClient(httpParams);

// Go on...

如果您的 HTTP 客户端超过了在其各自字段中设置的时间,它将简单地给您一个 ConnectTimeoutException。现在您已经了解足够的知识来考虑数据是否在 onPostExecute() 中有效,以及是否应该继续使用它。


如果我添加这两个参数,如果它们过期或超时会发生什么?基本上——什么也不会发生?换句话说:没有强制关闭,只是一个空列表? - TheLettuceMaster
它会发送一个异常,然后你可以分析出问题所在并通知用户。 - Wroclai
我把这个标记为正确,因为它是最完整的答案。所以在连接不佳的最好情况下,用户可能会得到很多空列表(如果数据为空,则会弹出“超时!”之类的消息)。我想这比崩溃要好。我只需要找到更好的共享托管可能性。 - TheLettuceMaster

2
您的异常是因为您仍然有一个对话框显示,而您已经进入了不同的活动。在您说出之前,请先使用以下代码关闭对话框:
Intent i = new Intent(getApplicationContext(), Items.class);
i.putExtra("category", cats.get(arg2));
startActivity(i);

请调用此函数:

progressDialog.dismiss()

而且您的活动将不再泄漏。


那不是问题所在。这个答案中的第一个代码片段位于setOnClickListener()函数内部,除非用户点击视图,否则它不会被调用。错误与此代码片段无关。 - Wroclai
好的,上面的代码实际上并不是被LogCat引用的活动的一部分。所以我不会去看它,很抱歉没有解释清楚。也许你可以告诉我什么原因会导致活动泄漏,我会检查一下? - TheLettuceMaster

1
  1. 你的doInBackground返回了NULL。 return null; 通常应该 return result;

  2. 你的onPostExecute有一个NuLL :))作为结果。 在这里使用onPostExecute(String result)并按照以下方式处理。 再次检查所发布的示例。

  3. 你必须处理这些null值(and 可能在onPostExecute()中解除此progressdialog)

8-13 14:57:00.596: E/AndroidRuntime(2262):     at com.---.--  -.MyFragmentActivity$RateFragment$RatingTask.onPostExecute(MyFragmentActivity.java:461)
   08-13 14:57:00.596: E/AndroidRuntime(2262):     at com.---.---.MyFragmentActivity$RateFragment$RatingTask.onPostExecute(MyFragmentActivity.java:1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接