一線大廠大型APP性能優化系列-更優雅的延遲方案(四)

1.前言

通過前幾章的學習,大家已經掌握了在APP啓動時,如何對一些第三方初始化的內容 使用啓動器進行異步、同步及 使用有向無環圖的拓撲排序處理繼承關係等處理。這一章我們繼續來探討下在空閒期需要處理的Task。

還記得這張圖嗎?Application裏面的各種第三方的初始化的分類。

我們通過第三章的學習 一線大廠大型APP性能優化系列-自定義啓動器(三) 已經處理前4個,今天我們學習最後一個ilde task(延遲加載,空閒期處理方案)。


2.聊一聊假的延遲方案

(還是想吐槽,簡歷上都寫着會APP的性能優化,一問,什麼sendMessageDelayed,什麼IldeHandler的定義使用背的都很熟練,再一問項目中怎麼用的,基本都啞火了,就1個誠實的,直接回答,就那麼用啊。。。emmmm。。。。所以不建議你們刷面經,至少高級崗位不建議,會露餡的)

在我們日常處理一些耗時任務的時候,有很多的方案,比如

1.可以通過Handler().sendMessageDelayed() 達到延遲加載。

原理:將消息加入隊列中,然後MessageQueue會根據延時的時間進行隊列的排序,時間最短的在前,如果沒有要執行的,就進行阻塞,阻塞的時間爲最先要執行的任務的等待時間,如果不再添加新任務,則等時間到了會自動執行,如果添加了新任務,則重新排序,然後喚醒當前線程,將排序後,最先要執行的等待時間進行阻塞或者直接執行。

缺點:但是項目中是不建議這樣用的,因爲會強佔CPU,性能會進行耗損,比如一個頁面的一些第三方服務進行初始化操作,雖然說是可以延遲一段時間再去初始化,但是如果該頁面一直在執行,比如有個定時器或者輪詢請求接口等,那麼到了時間,依然是要強佔CPU來執行我們的第三方服務的初始化操作。所以不能直接這麼用

不知道會不會有槓精,“我們平時也是這麼用的呀,也沒問題呀”。但是你要記住,我聊的是大型項目,比如中石油終端,一個APP中,不光要作爲主設備接收其他設備傳遞的數據,保持的長鏈接,還通過自定義的一些協議,比如FTFS協議,與硬件進行連接,如加油機,前庭控制器,液位儀等,你直接來個延遲初始化,一開始沒什麼,等延遲時間到了,如果人員也在操作,油機也在實時上報數據,直接卡死你。

2.IldeHandler

這個的確能解決我們之前尷尬的問題,它的主張是在CPU空閒時再進行操作,不搶佔CPU

同學們,面經是不是就只寫到這呀,那你們考慮過,如果請求過多尼,如果併發尼?如果空閒執行中執行的任務還必須有先後執行的順序尼。比如A頁面,B頁面都把自己耗時的方法加入到了空閒執行隊列裏面,但是要想執行B頁面耗時方法,必須得先執行頁面A中的方法,你該怎麼做?

廢話不多說,直接上代碼,順便附一張之前戰鬥過的地方,項目雖好,但是工作室太“簡陋”,做完幾個版本就溜了。。


3.聊一聊IdleHandler的優化及封裝

不知道task是啥的,就去看第三章內容。

/**
 * @author: lybj
 * @date: 2020/5/26
 * @Description: 空閒隊列
 */
public class IldeTaskDispatcher {

    private Queue<Task> mIldeQueue = new LinkedList<>();

    private MessageQueue.IdleHandler messageQueue = new MessageQueue.IdleHandler(){

        @Override
        public boolean queueIdle() {

            if(mIldeQueue.size() > 0){

                // 如果CPU空閒了,
                Task IldeTask = mIldeQueue.poll();
                new DispatchRunnable(IldeTask).run();
            }
            // 如果返回false,則移除該 IldeHandler
            return !mIldeQueue.isEmpty();
        }
    };

    public IldeTaskDispatcher addTask(Task task){

        mIldeQueue.add(task);
        return this;
    }
    
    
    /**
     * 執行空閒方法,因爲用了DispatchRunnable,所以會優先處理需要依賴的task,再處理本次需要處理的task,順序執行
     * */
    public void start(){
        Looper.myQueue().addIdleHandler(idleHandler);
    }
}

調用的話也很簡單

  new IldeTaskDispatcher()
                .addTask(new InitBaiduMapTask())
                .addTask(new InitBuglyTask())
                .start();

4.其他代碼

不明白的,去看上一章的講解,這章本來就是在上一章內容上增加的拓展

DispatchRunnable

public class DispatchRunnable implements Runnable {
    private Task mTask;
    private TaskDispatcher mTaskDispatcher;

    public DispatchRunnable(Task task) {
        this.mTask = task;
    }
    public DispatchRunnable(Task task,TaskDispatcher dispatcher) {
        this.mTask = task;
        this.mTaskDispatcher = dispatcher;
    }

    @Override
    public void run() {

        Process.setThreadPriority(mTask.priority());

        long startTime = System.currentTimeMillis();

        mTask.setWaiting(true);
        mTask.waitToSatisfy();

        long waitTime = System.currentTimeMillis() - startTime;
        startTime = System.currentTimeMillis();

        // 執行Task
        mTask.setRunning(true);
        mTask.run();

        // 執行Task的尾部任務
        Runnable tailRunnable = mTask.getTailRunnable();
        if (tailRunnable != null) {
            tailRunnable.run();
        }

        if (!mTask.needCall() || !mTask.runOnMainThread()) {
            printTaskLog(startTime, waitTime);

            TaskStat.markTaskDone();
            mTask.setFinished(true);
            if(mTaskDispatcher != null){
                mTaskDispatcher.satisfyChildren(mTask);
                
                // --> 8
                mTaskDispatcher.markTaskDone(mTask);
            }
        }
        TraceCompat.endSection();
    }
}

task

public abstract class Task implements ITask {

    private volatile boolean mIsWaiting; // 是否正在等待
    private volatile boolean mIsRunning; // 是否正在執行
    private volatile boolean mIsFinished; // Task是否執行完成
    private volatile boolean mIsSend; // Task是否已經被分發

    // 當前Task依賴的Task數量(需要等待被依賴的Task執行完畢才能執行自己),默認沒有依賴
    private CountDownLatch mDepends = new CountDownLatch(dependsOn() == null ? 0 : dependsOn().size());

    /**
     * 依賴的Task執行完一個
     */
    public void satisfy() {
        mDepends.countDown();
    }
    
     /**
     * 當前Task等待,讓依賴的Task先執行
     */
    public void waitToSatisfy() {
        try {
            mDepends.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 異步線程執行的Task是否需要在被調用await的時候等待,默認不需要
     *
     * @return
     */
    @Override
    public boolean needWait() {
        return false;
    }

    /**
     * 當前Task依賴的Task集合(需要等待被依賴的Task執行完畢才能執行自己),默認沒有依賴
     *
     * @return
     */
    @Override
    public List<Class<? extends Task>> dependsOn() {
        return null;
    }
}

自定義的task

public class InitJPushTask extends Task {

    @Override
    public boolean needWait() {
        return true;
    }

    @Override
    public List<Class<? extends Task>> dependsOn() {
        
        // 先執行GetDeviceIdTask,再執行自己
        List<Class<? extends Task>> tasks = new ArrayList<>();
        tasks.add(GetDeviceIdTask.class);
        return tasks;
    }

    @Override
    public void run() {
        // 模擬InitJPush初始化
        try {
            Thread.sleep(1500);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

5.End

已經寫了4篇了,感謝大家一直以來的支持,還差2篇,優化啓動這個章節就算完事了,接下來就是一些瘦身,容災,卡頓,網絡等優化方面的內容了。距離全部結束還差15篇文章。我會繼續更新完畢的。

另外需要重點說明的是,這個系列的內容,不光是爲了教學,均是可以在大型項目中直接使用的,穩定性已經驗證過了,不用擔心出現問題,另外大家真的是一直在學嗎?特意沒上傳代碼,竟然沒有一個向我要的,只能說大家還是很厲害的。

Android的形勢越來越嚴峻了,大家爲了提升自己均開始了多面的發展,比如kotlin,比如flutter的學習,但是作者其實並不太看好它們,因爲總感覺只有一些小型的項目或者是練習項目,更或者是外包項目,纔會考慮它們,作者做java大概有7年了,各種底層源碼,代碼設計,大型的項目經驗均具備,也分析了很多很多開源框架的設計方案,但是對於Java,我還是不能說自己就懂了。

在日常開發中,一些模塊的開發,也是先畫各種草圖,設計圖,生怕哪裏現在寫死了,以後拓展會出問題,再會動手去寫,比如說flutter,在使用的時候真的具備解決所有問題的能力嗎?真的具備哪怕是迭代了幾十個版本的項目也能依然維護?具體的源碼是否已經研究過?是否可用滿足日常所需?總不能都寫了1年了,突然一個需求,告訴產品做不了,不然要重構吧!

所以對於一個架構崗的程序員來說,學習kotlin也好,flutter也好,應該是瞭解其語法,功能的設計,更好的用到自己的項目中來,學習要深入,而不僅僅是懂語法。

程序員真正要學會的是一些語言的設計思想,而並不是語言的本身,任何語言,設計的初衷永遠都是共同的,語言總有更替,而我們要學其設計的核心及技巧,這些東西,任何語言都通用,永遠要有一個信念 “任語言千千萬,我永遠是最強”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章