Android UI控件延迟显示 View.postDelayed() 方法使用及思考

问题描述

        在项目中遇到了一个代码时序引起UI控件显示异常的bug,代码如下:

@SuppressLint("NewApi")
public class TestFragment extends DialogFragment {
    private Button mButton;
    //默认初始值为true
    private boolean isSucess = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //NetworkUtil.requestNet是开启了一个异步线程做请求任务
        //执行网络请求,根据返回成功与否(true or false) 来设定 button上的文字
        //如下为 伪代码
        isSucess = NetworkUtil.requestNet();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View viewLayout = inflater.inflate(R.layout.fragment_test, container);
        mButton = viewLayout.findViewById(R.id.button2);
        return viewLayout;
    }

    @Override
    public void onResume() {
        super.onResume();
        //存在bug的代码
       /* if (isSucess) {
            mButton.setText("返回结果成功");
        } else {
            mButton.setText("返回结果失败");
        }*/

        //可以用 View.postDelayed(Runnable action, long delayMillis)方法来解决此问题
        mButton.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (isSucess) {
                    mButton.setText("返回结果成功");
                } else {
                    mButton.setText("返回结果失败");
                }
            }
        }, 1000); //这里延时时间根据网络环境的好坏设置
    }
}

原因分析:

        一般写代码的逻辑思维,是利用Fragment的生命周期时序方法,在onCreate方法中调用网络请求,然后在onResume方法中设置文字更新UI

        这里没有考虑到:请求网络的任务在异步线程中做的,什么时候返回结果是未知的,但是Fragment的各生命周期的方法肯定是顺序执行的

        在onResume方法中isSucess作为判断显示文字的条件,它的状态值是不准确的,所以存在显示不准确的bug。

     


解决方案:

        【第一种方案】 在onResume方法中,利用 View.postDelayed(Runnable action, long

delayMillis) 方法做Button上文字的延迟显示,在保证请求网络任务返回正确的结果后,再更新UI.

        【第二种方案】 当然第一种方案也存在缺陷,就是在弱网的环境下,可能2秒都无法返回正确的请求结果,这时就需要用在网络请求的回调接口中去更新UI, 代码如下:

//网络请求伪代码片段 
public class NetworkUtils {

    //回调接口
    public interface HttpCallbackListener {

        void onFinish(boolean status);

    }

    public static void getStatus(final HttpCallbackListener listener) {

        //网络中的请求任务,当正常返回结果

        if (null != listener) {
            listener.onFinish(true);
        }
 
        //网络请求任务,当出现异常时

        if (null != listener) {
            listener.onFinish(false);
        }
    }
}


//DialogFragment中的伪代码片段

 @Override
 public void onResume() {
    NetworkUtils.getStatus(new HttpCallbackListener() {

         @Override
         public void onFinish(boolean status)

            if (status == true) 
                mButton.setText("返回结果成功");

            } else {
                mButton.setText("返回结果失败");
            }

    } );

}


        


延伸思考:

        在Android某个组件中,做延迟处理一般有如下几种方法:

        1. 使用线程休眠延时: Thread.sleep(long time) 方法

        2. Timer定时器:  Timer.schedule(TimerTask task, long delay)方法

        3. 最常用的Handler: Handler.postDelayed(Runnable r, long delayMillis)方法

        4. AlarmManager :  这个一般用于精准定时/延时的任务 如: setExact( )等方法

        之前我是想用Handler去实现的,后来仔细查了下API, 对于某个UI控件延时显示有直接的方法

View.postDelayed可以实现,当后来去查看了下源码,发现最终还是调用的主线程的

Handler.postDelayed()方法实现的。

/**
     * <p>Causes the Runnable to be added to the message queue, to be run
     * after the specified amount of time elapses.
     * The runnable will be run on the user interface thread.</p>
     *
     * @param action The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *        will be executed.
     *
     * @return true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed --
     *         if the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *
     * @see #post
     * @see #removeCallbacks
     */
    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().postDelayed(action, delayMillis);
        return true;
    }

具体源码追踪和分析,可以参考大神的这篇博客文章:

View的postDelayed方法深度思考

·

        

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值