You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OkHttp客户端入队请求时ProgressBar不显示问题

ProgressBar 延迟显示问题:请求入队前无法立即显示

我在实现点击按钮发起REST请求的功能时遇到了一个问题:明明在调用REST请求前就设置了ProgressBar可见、禁用按钮并锁定窗口触摸,但ProgressBar却要等到请求入队完成后才会显示。如果注释掉RESTClient.pushToTalk()这行代码,ProgressBar就能正常立即显示。

我的代码如下:

点击按钮的处理逻辑:

final Button btnInvia = (Button) findViewById(R.id.btnInvia); btnInvia.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { btnInvia.setEnabled(false); mProgressBar.setVisibility(View.VISIBLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); Evento evento = new Evento(PushToTalkActivity.this, Constants.TIPO_EVENTO_ID_PUSH_TO_TALK); evento.setFile(BinaryHelper.binaryToString(mFileName)); RESTClient.pushToTalk(PushToTalkActivity.this, evento, new Callback<BaseResponse>() { @Override public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) { if (response.code() == 200) { Toast.makeText(PushToTalkActivity.this, getResources().getString(R.string.inizio_lavoro_inviato), Toast.LENGTH_LONG).show(); TransitionBuilder.with(PushToTalkActivity.this).clearStack().show(MainActivity.class); } } @Override public void onFailure(Call<BaseResponse> call, Throwable t) { mProgressBar.setVisibility(View.GONE); btnInvia.setEnabled(true); } }); } });

布局XML(部分内容):

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" ... >

问题原因分析

这个问题的核心是主线程被阻塞了:Android的UI更新操作(比如设置ProgressBar可见)是在主线程执行的,而RESTClient.pushToTalk()的入队操作,或者前面的BinaryHelper.binaryToString(mFileName)可能涉及耗时的同步操作(比如文件读取、对象序列化、请求参数准备),这些操作会占用主线程,导致UI线程来不及处理重绘请求,直到耗时操作完成后,才会轮到UI更新生效。

解决方案:将耗时操作移到子线程执行

我们需要把所有可能耗时的操作(包括请求对象准备、REST请求入队)放到子线程中,让主线程能及时完成UI更新。这里用AsyncTask来实现(也可以用Thread+Handler或者其他异步框架):

修改后的代码:

final Button btnInvia = (Button) findViewById(R.id.btnInvia); 
btnInvia.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View arg0) { 
        // 先在主线程完成所有UI更新
        btnInvia.setEnabled(false); 
        mProgressBar.setVisibility(View.VISIBLE); 
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 
                            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 

        // 把耗时操作放到子线程
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                // 这里处理请求对象准备和REST请求入队
                Evento evento = new Evento(PushToTalkActivity.this, Constants.TIPO_EVENTO_ID_PUSH_TO_TALK); 
                evento.setFile(BinaryHelper.binaryToString(mFileName)); 
                RESTClient.pushToTalk(PushToTalkActivity.this, evento, new Callback<BaseResponse>() { 
                    @Override 
                    public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) { 
                        // 回到主线程处理响应和UI恢复
                        runOnUiThread(() -> {
                            mProgressBar.setVisibility(View.GONE); 
                            btnInvia.setEnabled(true); 
                            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
                            
                            if (response.code() == 200) { 
                                Toast.makeText(PushToTalkActivity.this, 
                                            getResources().getString(R.string.inizio_lavoro_inviato), 
                                            Toast.LENGTH_LONG).show(); 
                                TransitionBuilder.with(PushToTalkActivity.this).clearStack().show(MainActivity.class); 
                            }
                        });
                    } 

                    @Override 
                    public void onFailure(Call<BaseResponse> call, Throwable t) { 
                        // 回到主线程处理失败和UI恢复
                        runOnUiThread(() -> {
                            mProgressBar.setVisibility(View.GONE); 
                            btnInvia.setEnabled(true); 
                            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
                        });
                    } 
                });
                return null;
            }
        }.execute();
    } 
});

关键说明

  1. 先更新UI再执行耗时操作:确保主线程先处理完UI更新的消息,再把耗时任务交给子线程,这样ProgressBar能立刻显示。
  2. 耗时操作全部移到子线程:包括BinaryHelper.binaryToString(mFileName)(如果这一步是读取大文件的话,肯定耗时)和REST请求入队,避免主线程被阻塞。
  3. 回调中切回主线程:不管请求成功还是失败,所有UI恢复操作都要在主线程执行,这里用runOnUiThread()来确保这一点(如果你的RESTClient回调本身就在主线程,这一步可以简化,但加上更保险)。

内容的提问来源于stack exchange,提问作者Giacomo M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:59:36