Android性能UI卡頓

UI卡頓原理

Android當中保持60幀以上算是流暢:60fps ——>16ms/幀(數字量化)java

準則:儘可能保證每次在16ms內處理完全部的cpu與Gpu計算、繪製、渲染等操做,不然會形成丟幀卡頓等問題android

緣由:在主線程中執行耗時工做,把事件分發給合適的view或者widget的git

  1. 在子線程中處理
handler
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
複製代碼
  1. 佈局Layout過於複雜,沒法在16ms內完成渲染github

  2. View的過分繪製安全

  3. View頻繁的觸發measure、layoutbash

  4. 內存頻繁的觸發GC過多(STW,建立太多的臨時變量)實現的核心原理網絡

    ​ ###Blockcanary工具 在主線程ActivityThread中的 dispatchMessge(msg)上下方打印時間,計算閥值,超過了就打印併發

  5. postMessage(Handler)app

  6. Queue.next()獲取咱們的消息less

  7. 是否超過咱們的閥值 (Dump all Allocation)

DisplayActivity在 release 版本不會顯示

核心邏輯在BlockCanaryInternals:

LooperMonitor:判斷是否卡頓 isBlock

private boolean isBlock(long endTime) {
        return endTime - mStartTimestamp > mBlockThresholdMillis;
    }

    private void notifyBlockEvent(final long endTime) {
        final long startTime = mStartTimestamp;
        final long startThreadTime = mStartThreadTimestamp;
        final long endThreadTime = SystemClock.currentThreadTimeMillis();
        HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() {
            @Override
            public void run() {
                mBlockListener.onBlockEvent(startTime, endTime, startThreadTime, endThreadTime);
            }
        });
    }

    private void startDump() {
        if (null != BlockCanaryInternals.getInstance().stackSampler) {
            BlockCanaryInternals.getInstance().stackSampler.start();
        }

        if (null != BlockCanaryInternals.getInstance().cpuSampler) {
            BlockCanaryInternals.getInstance().cpuSampler.start();
        }
    }
複製代碼

stackSampler:

public ArrayList<String> getThreadStackEntries(long startTime, long endTime) {
        ArrayList<String> result = new ArrayList<>();
        synchronized (sStackMap) {
            for (Long entryTime : sStackMap.keySet()) {
                if (startTime < entryTime && entryTime < endTime) {
                    result.add(BlockInfo.TIME_FORMATTER.format(entryTime)
                            + BlockInfo.SEPARATOR
                            + BlockInfo.SEPARATOR
                            + sStackMap.get(entryTime));
                }
            }
        }
        return result;
    }

    @Override
    protected void doSample() {
        StringBuilder stringBuilder = new StringBuilder();

        for (StackTraceElement stackTraceElement : mCurrentThread.getStackTrace()) {
            stringBuilder
                    .append(stackTraceElement.toString())
                    .append(BlockInfo.SEPARATOR);
        }

        synchronized (sStackMap) {
            if (sStackMap.size() == mMaxEntryCount && mMaxEntryCount > 0) {
                sStackMap.remove(sStackMap.keySet().iterator().next());
            }
            sStackMap.put(System.currentTimeMillis(), stringBuilder.toString());
        }
    }
複製代碼

CpuSampler:

@Override
    protected void doSample() {
        BufferedReader cpuReader = null;
        BufferedReader pidReader = null;

        try {
            cpuReader = new BufferedReader(new InputStreamReader(
                    new FileInputStream("/proc/stat")), BUFFER_SIZE);
            String cpuRate = cpuReader.readLine();
            if (cpuRate == null) {
                cpuRate = "";
            }

            if (mPid == 0) {
                mPid = android.os.Process.myPid();
            }
            pidReader = new BufferedReader(new InputStreamReader(
                    new FileInputStream("/proc/" + mPid + "/stat")), BUFFER_SIZE);
            String pidCpuRate = pidReader.readLine();
            if (pidCpuRate == null) {
                pidCpuRate = "";
            }

            parse(cpuRate, pidCpuRate);
        } catch (Throwable throwable) {
            Log.e(TAG, "doSample: ", throwable);
        } finally {
            try {
                if (cpuReader != null) {
                    cpuReader.close();
                }
                if (pidReader != null) {
                    pidReader.close();
                }
            } catch (IOException exception) {
                Log.e(TAG, "doSample: ", exception);
            }
        }
    }
複製代碼

ANR形成緣由

  • ANR: Application Not responding
  • Activity Manager和 WindowManager(系統服務監控)
  • ANR的分類
  1. watchDog-anr是如何監控anr的?

    1. Service Timeout(5秒)
    2. BroadcastQueue Timeout(10秒)
    3. inputDispatch Timeout (5秒)
  2. 主線程耗時操做 ()

  3. 主線程被鎖住

  4. cpu被其它的進程佔用

如何解決

  1. 主線程讀取數據
  2. sharepreference的 commit(), 子線程中 apply()替換
  3. Broadcast的reciever不能進行耗時操做, IntentService中操做
  4. Activity的生命週期中不該該有太耗時的操做

WatchDog監控

github.com/SalomonBrys…

建立一個監控線程

private final Handler _uiHandler = new Handler(Looper.getMainLooper());
複製代碼

改線程不斷往UI線程post一個任務

private final Runnable _ticker = new Runnable() {
        @Override public void run() {
            _tick = (_tick + 1) % Integer.MAX_VALUE;
        }
 };


@Override
public void run() {
  setName("|ANR-WatchDog|");

  int lastTick;
  int lastIgnored = -1;
  while (!isInterrupted()) {
    lastTick = _tick;
    _uiHandler.post(_ticker);
    try {
      //睡眠固定時間
      Thread.sleep(_timeoutInterval);
    }
    catch (InterruptedException e) {
      _interruptionListener.onInterrupted(e);
      return ;
    }

    // If the main thread has not handled _ticker, it is blocked. ANR.
    if (_tick == lastTick) {
      if (!_ignoreDebugger && Debug.isDebuggerConnected()) {
        if (_tick != lastIgnored)
          Log.w("ANRWatchdog", "An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");
        lastIgnored = _tick;
        continue ;
      }

      ANRError error;
      if (_namePrefix != null)
        error = ANRError.New(_namePrefix, _logThreadsWithoutStackTrace);
      else
        error = ANRError.NewMainOnly();
      _anrListener.onAppNotResponding(error);
      return;
    }
  }
}
複製代碼

new Thread

  • 問題:Thread 的 start() 啓動線程, 處於就緒狀態,並無運行,告訴CPU能夠運行。

存在的問題:

  1. 多個耗時任務開啓多個線程,開銷是很是大的
  2. 若是在線程中執行循環任務,只能經過一個Flag開控制它的中止
  3. 沒有線程切換的接口
  4. 若是從UI線程啓動,則該線程優先級默認爲Default

Process.setThreadPriority(Process.THREAD_PRIO_RITY_BACKGROUD), 須要把線程優先級下降

線程間通訊

將子線程中的消息拋到主線程中去:
Handler handler = new Handler(){
    void handlerMessage(){
		do UI things...
	}
}

New Thread(){
  void run(){
     handler. sendMessage();
  }
}.start();
  
handler.post(runnable);

Activity.runOnUiThread(Runnable)
複製代碼
AsynTask

AsynTask的線程優先級是background不會阻塞UI。

AsyncTask 3.0以後改爲順序執行,當一個進程中有多個AsynTask同時並行執行,他們會公用線程池,主要緣由在doInBackground()中訪問相同的資源,線程池會形成線程的併發訪問形成線程安全問題,因此設計成串行的,就不會有線程安全問題。

將任務從主線程拋到工做線程

  1. Thread/Runnable
  2. HandlerThread
  3. InterService
thread/runnable

Runnable做爲匿名內部類的話會持有外部類的引用,容易內存泄漏,不建議採起這種方式

HandlerThread

它集成了Thread

它有本身的內部Looper對象,經過Looper.loop()進行looper循環

HandlerThread的looper對象傳遞給Handler對象,而後在handleMessge()方法中執行異步任務

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /** * This method returns the Looper associated with this thread. If this thread not been started * @return The looper. */
  //這個是在UI線程中調用,須要解決同步問題,由於looper對象在 run方法裏運行。
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /** * @return a shared {@link Handler} associated with this thread * @hide */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    /** * Quits the handler thread's looper. * <p> * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * @see #quitSafely */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
          	//清空全部消息
            looper.quit();
            return true;
        }
        return false;
    }

    /** * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
          //清除全部的延遲消息
            looper.quitSafely();
            return true;
        }
        return false;
    }
    /** * Returns the identifier of this thread. See Process.myTid(). */
    public int getThreadId() {
        return mTid;
    }
}

複製代碼

HandlerThread適合單線程或者異步隊列,I/O流讀取文件,進行異步轉化比較合適,只有一個線程。

網絡的數據須要併發處理,不太適合。

IntentService

  1. intentService是Service類的子類

  2. 單獨開啓一個線程來處理全部的Intent請求所對應的任務

  3. 當IntentService處理完任務以後,會本身在合適的時候結束

    public abstract class IntentService extends Service {
        private volatile Looper mServiceLooper;
        private volatile ServiceHandler mServiceHandler;
        private String mName;
        private boolean mRedelivery;
    
        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                onHandleIntent((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
        public IntentService(String name) {
            super();
            mName = name;
        }
        
        public void setIntentRedelivery(boolean enabled) {
            mRedelivery = enabled;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    
        @Override
        public void onStart(@Nullable Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    
        @Override
        public void onDestroy() {
            mServiceLooper.quit();
        }
    
        /** * Unless you provide binding for your service, you don't need to implement this * method, because the default implementation returns null. * @see android.app.Service#onBind */
        @Override
        @Nullable
        public IBinder onBind(Intent intent) {
            return null;
        }
      
        @WorkerThread
        protected abstract void onHandleIntent(@Nullable Intent intent);
    }
    
    複製代碼

多進程的好處

  1. 解決OOM問題(Android會限制單一進程的內存大小)
  2. 合理的利用內存
  3. 單一進程奔潰不會影響總體應用
  4. 項目解耦、模塊化開發

問題

  1. Application會屢次建立的問題(根據進程名進行不一樣的初始化,不要作過多的靜態對象初始化)
  2. 文件讀寫潛在的問題(Java中文件鎖基於Java 虛擬機、進程存在的,特別Sharepreference)
  3. 靜態變量和單例模式徹底失效。(多進程中不要過多的用靜態變量,失效了)
  4. 線程同步都會失效,這些都是在虛擬機、進程的基礎上。

synchronized和volidate

  1. 阻塞線程與否
  2. 使用範圍
  3. 原子性(synchronized保證原子性)
volidate與單例
//餓漢
public class Singleton{
  private static Singleton intance = new Singleton();
  private Singleton(){}
  
  public static Singleton getInstance(){
    return instance;
  }
}

//懶漢
public class SingletonLazy{
  private static SingletonLazy intance = null;
  private Singleton(){}
  
  public static SingletonLazy getInstance(){
    if(null == instance){
      instance = new SingletonLazy();
    }
    return instance;
  }
  //性能損耗較大
  public static synchronized SingletonLazy getInstance1(){
    if(null == instance){
      instance = new SingletonLazy();
    }
    return instance;
  }
}

//雙重校驗鎖
public class SingletonDouble{
  private static volatile SingletonDouble intance = null;
  private SingletonDouble(){}
  
  public static SingletonDouble getInstance(){
    if(null == instance){
      synchronized(SingletonDouble.this){
        if(null == instance){
          instance = new SingletonDouble();
        }
      }
    }
    return instance;
  }
}
複製代碼
相關文章
相關標籤/搜索