1. 程式人生 > >PullToRefreshScrollView踩坑經歷以及原始碼分析

PullToRefreshScrollView踩坑經歷以及原始碼分析

         目前下拉重新整理已經滿大街都是,在自己的應用如果不使用這個模式的話,出門都不好意思和人家打招呼吐舌頭,該文章結合自己使用PullToRefreshScrollView過程中所遇到的問題,簡單探討下針對於github 上的這個開源專案的使用心得,具體的使用方法不做探究。

         首先描述下自己使用PullToRefreshScrollView所遇到的BUG現象吧:在ScrollView下拉重新整理時去服務端獲取資料並進行展示,獲取資料的過程採用執行緒池管理執行緒(具體優點兒不做解釋),進行到該步發現一個奇妙的問題,發現一下拉重新整理APP就閃退了,檢視Log錯誤顯示ThreadPoolExecutor佇列滿了RejectedExecutionException,懷疑我的ThreadPoolExecutor容量不夠大,雖擴大至20,再次測試依然閃退,並沒有什麼亂用,仍然是佇列已滿錯誤。之後層層原始碼追看,發現了端倪,onPullDownToRefresh該方法一直不斷的在呼叫,所以導致ThreadPoolExecutor裡面的任務一直變多,導致容量急劇增加,進而閃退。把自己的解決過程記錄下來獻給遇到同樣問題的童鞋。

          →代表呼叫的意思

         當用戶下拉重新整理並鬆開手指時會呼叫PullToRefreshBase.java的onTouchEvent的case MotionEvent.ACTION_UP→PullToRefreshBase.java的final void setState(State state, final boolean... params)其PullToRefreshBase.java的中state=MANUAL_REFRESHING,params=[true]→PullToRefreshBase.java的protected void onRefreshing(final boolean doScroll) 其中doScroll=true→PullToRefreshBase.java的protected final void smoothScrollTo(int scrollValue, OnSmoothScrollFinishedListener listener)→PullToRefreshBase.java的private final void smoothScrollTo(int newScrollValue, long duration, long delayMillis,OnSmoothScrollFinishedListener listener)→PullToRefreshBase.java的public SmoothScrollRunnable(int fromY, int toY, long duration, OnSmoothScrollFinishedListener listener)例項化一個執行緒→PullToRefreshBase.java的post(mCurrentSmoothScrollRunnable);執行SmoothScrollRunnable執行緒從SmoothScrollRunnable內部原始碼

    final class SmoothScrollRunnable implements Runnable
    {
        private final Interpolator mInterpolator;
        
        private final int mScrollToY;
        
        private final int mScrollFromY;
        
        private final long mDuration;
        
        private OnSmoothScrollFinishedListener mListener;
        
        private boolean mContinueRunning = true;
        
        private long mStartTime = -1;
        
        private int mCurrentY = -1;
        
        public SmoothScrollRunnable(int fromY, int toY, long duration, OnSmoothScrollFinishedListener listener)
        {
            Log.i("TTT", " SmoothScrollRunnable 例項化");
            mScrollFromY = fromY;
            mScrollToY = toY;
            mInterpolator = mScrollAnimationInterpolator;
            mDuration = duration;
            mListener = listener;
        }
        
        @Override
        public void run()
        {
            
            /**
             * Only set mStartTime if this is the first time we're starting,
             * else actually calculate the Y delta
             */
            if (mStartTime == -1)
            {
                mStartTime = System.currentTimeMillis();
                Log.i("TTT", " mStartTime=" + mStartTime);
            }
            else
            {
                
                /**
                 * We do do all calculations in long to reduce software float
                 * calculations. We use 1000 as it gives us good accuracy and
                 * small rounding errors
                 */
                long normalizedTime = (1000 * (System.currentTimeMillis() - mStartTime)) / mDuration;
                normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0);
                
                final int deltaY =
                    Math.round((mScrollFromY - mScrollToY) * mInterpolator.getInterpolation(normalizedTime / 1000f));
                mCurrentY = mScrollFromY - deltaY;
                setHeaderScroll(mCurrentY);
                Log.i("TTT",
                    " normalizedTime=" + normalizedTime + ", mContinueRunning=" + mContinueRunning + ",mScrollToY="
                        + mScrollToY + ",mCurrentY=" + mCurrentY);
            }
            
            // If we're not at the target Y, keep going...
            if (mContinueRunning && mScrollToY != mCurrentY)
            {
                ViewCompat.postOnAnimation(PullToRefreshBase.this, this);
            }
            else
            {
                if (null != mListener)
                {
                    Log.i("TTT", "mListener.onSmoothScrollFinished(),isSmoothScrollFinished=" + isSmoothScrollFinished);
                    mListener.onSmoothScrollFinished();
                }
            }
        }
        
        public void stop()
        {
            Log.i("TTT", " stop()");
            mContinueRunning = false;
            removeCallbacks(this);
        }
    }
首先將HeaderView採用postOnAnimation慢慢的隱藏掉,直到HeaderView隱藏掉之後mScrollToY == mCurrentY然後呼叫mListener.onSmoothScrollFinished(),該方法最終呼叫onPullDownToRefresh方法,如果onPullDownToRefresh裡面開啟一個執行緒去服務端獲取資料,因為獲取資料和SmoothScrollRunnable不在一個執行緒中,那麼此時SmoothScrollRunnable獲得時間片它的run方法會不斷的執行,因此mListener.onSmoothScrollFinished()會不斷的執行,所以會導致不停地向ThreadPoolExecutor提交任務,所以ThreadPoolExecutor容量會瞬間變滿導致異常。因此考慮在SmoothScrollRunnable當中增加一個標誌位,標誌我當次已經在初始化中,具體程式碼如下,紅色是新增的邏輯。
final class SmoothScrollRunnable implements Runnable
    {
        private final Interpolator mInterpolator;
        
        private final int mScrollToY;
        
        private final int mScrollFromY;
        
        private final long mDuration;
        
        private OnSmoothScrollFinishedListener mListener;
        
        private boolean mContinueRunning = true;
        
        private long mStartTime = -1;
        
        private int mCurrentY = -1;
        
        <span style="color:#FF0000;">private boolean isSmoothScrollFinished = false;</span>
        
        public SmoothScrollRunnable(int fromY, int toY, long duration, OnSmoothScrollFinishedListener listener)
        {
            Log.i("TTT", " SmoothScrollRunnable 例項化");
            mScrollFromY = fromY;
            mScrollToY = toY;
            mInterpolator = mScrollAnimationInterpolator;
            mDuration = duration;
            mListener = listener;
        }
        
        @Override
        public void run()
        {
            
            /**
             * Only set mStartTime if this is the first time we're starting,
             * else actually calculate the Y delta
             */
            if (mStartTime == -1)
            {
                mStartTime = System.currentTimeMillis();
                Log.i("TTT", " mStartTime=" + mStartTime);
            }
            else
            {
                
                /**
                 * We do do all calculations in long to reduce software float
                 * calculations. We use 1000 as it gives us good accuracy and
                 * small rounding errors
                 */
                long normalizedTime = (1000 * (System.currentTimeMillis() - mStartTime)) / mDuration;
                normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0);
                
                final int deltaY =
                    Math.round((mScrollFromY - mScrollToY) * mInterpolator.getInterpolation(normalizedTime / 1000f));
                mCurrentY = mScrollFromY - deltaY;
                setHeaderScroll(mCurrentY);
                Log.i("TTT",
                    " normalizedTime=" + normalizedTime + ", mContinueRunning=" + mContinueRunning + ",mScrollToY="
                        + mScrollToY + ",mCurrentY=" + mCurrentY);
            }
            
            // If we're not at the target Y, keep going...
            if (mContinueRunning && mScrollToY != mCurrentY)
            {
                ViewCompat.postOnAnimation(PullToRefreshBase.this, this);
            }
            else
            {
                if (null != mListener)
                {
                    Log.i("TTT", "mListener.onSmoothScrollFinished(),isSmoothScrollFinished=" + isSmoothScrollFinished);
                    <span style="color:#FF0000;">if (!isSmoothScrollFinished)</span>
                    <span style="color:#FF0000;">{</span>
                        mListener.onSmoothScrollFinished();
                    <span style="color:#FF0000;">}</span>
                    <span style="color:#FF0000;">isSmoothScrollFinished = true;</span>
                }
            }
        }
        
        public void stop()
        {
            Log.i("TTT", " stop()");
            mContinueRunning = false;
            <span style="color:#FF0000;">isSmoothScrollFinished = true;</span>
            removeCallbacks(this);
        }
    }
如有不正還望留言改正吐舌頭