UI卡顿原理
Android当中保持60帧以上算是流畅:60fps ——>16ms/帧(数字量化)
准则:尽量保证每次在16ms内处理完所有的cpu与Gpu计算、绘制、渲染等操作,否则会造成丢帧卡顿等问题
原因:在主线程中执行耗时工作,把事件分发给合适的view或者widget的
- 在子线程中处理
handler
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
复制代码
-
布局Layout过于复杂,无法在16ms内完成渲染
-
View的过度绘制
-
View频繁的触发measure、layout
-
内存频繁的触发GC过多(STW,创建太多的临时变量)实现的核心原理
###Blockcanary工具 在主线程ActivityThread中的 dispatchMessge(msg)上下方打印时间,计算阀值,超过了就打印
-
postMessage(Handler)
-
Queue.next()获取我们的消息
-
是否超过我们的阀值 (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 getThreadStackEntries(long startTime, long endTime) {
ArrayList 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的分类
-
watchDog-anr是如何监控anr的?
- Service Timeout(5秒)
- BroadcastQueue Timeout(10秒)
- inputDispatch Timeout (5秒)
-
主线程耗时操作 ()
-
主线程被锁住
-
cpu被其它的进程占用
如何解决
- 主线程读取数据
- sharepreference的 commit(), 子线程中 apply()替换
- Broadcast的reciever不能进行耗时操作, IntentService中操作
- 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可以运行。
存在的问题:
- 多个耗时任务开启多个线程,开销是非常大的
- 如果在线程中执行循环任务,只能通过一个Flag开控制它的停止
- 没有线程切换的接口
- 如果从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()中访问相同的资源,线程池会造成线程的并发访问造成线程安全问题,所以设计成串行的,就不会有线程安全问题。
将任务从主线程抛到工作线程
- Thread/Runnable
- HandlerThread
- 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.
*
* @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
-
intentService是Service类的子类
-
单独开启一个线程来处理所有的Intent请求所对应的任务
-
当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); } 复制代码
多进程的好处
- 解决OOM问题(Android会限制单一进程的内存大小)
- 合理的利用内存
- 单一进程奔溃不会影响整体应用
- 项目解耦、模块化开发
问题
- Application会多次创建的问题(根据进程名进行不同的初始化,不要做过多的静态对象初始化)
- 文件读写潜在的问题(Java中文件锁基于Java 虚拟机、进程存在的,特别Sharepreference)
- 静态变量和单例模式完全失效。(多进程中不要过多的用静态变量,失效了)
- 线程同步都会失效,这些都是在虚拟机、进程的基础上。
synchronized和volidate
- 阻塞线程与否
- 使用范围
- 原子性(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;
}
}
复制代码