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;
  }
}
复制代码
相关文章
相关标签/搜索