【问题标题】:ProgressDialog in Android AsyncTask does not display at the right timeAndroid AsyncTask 中的 ProgressDialog 未在正确的时间显示
【发布时间】:2013-06-03 20:44:17
【问题描述】:

所以我有这些长时间运行的服务器调用,这些调用基本上是在我的 Android 应用程序中进行的 OData。调用的消费者使用.execute().get() 等待来自网络线程的响应(我知道正确的方法是使整个事情异步和基于回调,但是如果没有这些数据,应用程序将无法以任何方式运行,并且完全重新设计它以这种方式工作似乎并没有带来任何好处)。

所以我在网上发现了许多 sn-ps,其中将ProgressDialogonPreExecute()onPostExecute() 结合使用,如下面的代码示例所示,可用于在AsyncTask 执行时显示进度对话框。我正在使用提供的示例,但发生的情况是调用开始,它等待网络事务,然后 very 快速闪烁并隐藏进度对话框。它可以在事务上等待整整几秒钟,我知道它在 doInBackground() 中等待,但对话框直到最后才会弹出,使其实际上毫无用处。

在下面的代码中,DoEvents() 位基本上只是一个很短的睡眠。我试过有和没有它,似乎没有区别,但似乎值得一试。

class GetFromServerTask extends AsyncTask<String, Void, String>
    {
        private Context context;
        private ProgressDialog dialog;

        public GetFromServerTask(Context ctx) {
            context = ctx;
            dialog = new ProgressDialog(ctx);
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            dialog.setMessage("Loading...");
            dialog.show();
            DoEvents();
        }

        @Override
        protected String doInBackground(String... parms) {
            if(InOfflineMode)
                return "notdeserializable";

            String url = parms[0]; 
            HttpURLConnection urlConnection = null;
            try {
                URL typedUrl = new URL(url);
                urlConnection = (HttpURLConnection) typedUrl.openConnection();

                //Add Authorization token
                if(InDebugMode) {
                    urlConnection.addRequestProperty("AuthToken", AuthToken);
                } else {
                    urlConnection.addRequestProperty("Authorization", "Bearer " + AuthToken);
                }
                urlConnection.addRequestProperty("Accept", "application/json");

                DoEvents();
                InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                byte[] contents = new byte[in.available()];

                int bytesRead = 0;
                String strContents = ""; 
                while((bytesRead = in.read(contents)) != -1){ 
                    strContents += new String(contents, 0, bytesRead);
                    DoEvents();
                }

                if(strContents.startsWith("<HTML>"))
                    return "Error: Received unexpected HTML when connecting to service.  Make sure you are not connected to a WIFI that requires authentication.";

                return strContents;
            } catch(UnknownHostException hex) {
                return "Error: Could not find server address.  Make sure you are connected to the internet.  If you just changed connections (ie: turning WIFI on or off) it make take a minute to refresh";
            }
            catch(Exception ex) {
                String msg = "Error: " + ex.getClass().getName() + ": " + ex.getMessage();
                Log.e("TE", msg);
                return msg;
            } finally {
                if(urlConnection != null)
                    urlConnection.disconnect();
            }
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            if(dialog != null && dialog.isShowing())
                dialog.dismiss();
            DoEvents();
        }
    }

我还尝试了 SO 上其他地方建议的略有不同的版本(如下所示),结果完全相同:

protected void onPreExecute() {
    dialog=ProgressDialog.show(context, "", "Loading...", true, false);
    super.onPreExecute();
}

我还尝试将ProgressDialog 全部从AsyncTask 中取出,并将其显示在任务“外部”,如下所示。在这种情况下,它甚至不会出现。

ProgressDialog dialog = ProgressDialog.show(ServerAccessLayer.m_context, "", "Loading...", true, false);
String retVal = new GetFromServerTask(ServerAccessLayer.m_context).execute(url).get();
dialog.dismiss();

return retVal; 

【问题讨论】:

  • 如果您遵循此处描述的修复方法会发生什么? (stackoverflow.com/questions/11943133/…)
  • @Ken Wolf:同样的行为。可以看到 UI 响应(即:加载从服务器提取的数据的微调器)进度对话框快速弹出然后消失之前发生。
  • 您通过将其置于任务之外进行的修复将不起作用,因为执行会立即返回。这意味着它将立即调用dismiss。为此,您将在执行之前调用 show 并在 onPostExecute 中调用dismiss。
  • 此外,休眠也无济于事,因为要显示对话框,您需要进入事件循环的绘图部分 - 在您完成 onPostExecute 或 onPreExecute 函数之前不会发生。跨度>

标签: android android-asynctask progressdialog


【解决方案1】:

好的,你的问题是你的.get().get 是一个阻塞调用。这意味着您不会返回到事件循环(Android 框架中调用onCreateonPause、事件处理程序、onPostExecuteonPreExecute 等的代码),直到它返回。如果您不返回事件循环,您将永远不会进入绘图代码,也不会显示进度对话框。如果要显示对话框,则需要重新架构应用程序以实际异步使用任务。旁注——如果你在你的 UI 线程上像这样调用.get(),你的整个应用程序将冻结并且看起来很糟糕。这就是为什么他们一开始就强迫人们不要在 UI 线程上进行网络 IO。

【讨论】:

  • 不幸的是,这似乎是我必须走的路。非常感谢 Alejandro,但由于我的架构,这个人是对的,我的应用程序依赖于数据,而且由于似乎没有办法在不占用 UI 线程的情况下可靠地阻止读取,这是我必须走的路。我从没想过我真的会怀念温索克,但我完全怀念。
【解决方案2】:

正如@Gabe-Sechan 所说,.get() 是您的 UI 冻结且 ProgressDialog 未按您希望显示的原因。另一方面,我不同意他的说法:

如果您想显示对话框,您需要重新架构您的应用程序以 实际使用异步任务。

如果您只是删除您的.get() 并让您的对话框显示在onPreExecute() 中并在onPostExecute(String result) 中关闭,您将得到您正在寻找的内容:在执行任务时显示一个 ProgressDialog。

更新:

我基于使用 Android 的 AsyncTask 类的get() 方法的意义/有用性开始了question

【讨论】:

  • 重新架构的原因在于,他的应用程序被编写为假定他始终拥有来自异步任务的数据。有了接听电话,他就有了。没有它,他不会——应用程序将在数据未准备好的情况下处于活动状态。根据他的代码的工作方式,这可能(或可能不需要)需要额外的更改。
  • 我的意思是,由于他想让用户等待 AsyncTask 完成,这就是他只需删除get() 并使用ProgressDialog 即可实现的目标,无需额外编码或重新架构。
  • 不是基于我读到的他的评论。现在他正在调用get,然后他有一堆使用结果的代码。如果他删除了 get 就行不通了,他必须修复它。
猜你喜欢
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 2014-10-11
  • 2012-08-13
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多