Github地址:新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)
列表页卡顿优化
常规方案
- convertView复用, 使用ViewHolder
- 耗时任务异步处理
布局相关
- 减少布局层级,避免过度绘制
- 异步inflate或者X2C
图片相关
- 避免过大尺寸:GC频繁,内存抖动
- 滑动时取消加载
线程相关
- 使用线程池收敛线程,j降低线程优先级
- 避免UI线程时间片被抢占
TextView优化
- 原因:面对复杂文本性能不佳
- 原理:
Boringlayout单行,StaticLayout多行
DynamicLayout可编辑 - 展示类StaticLayout即可,性能优于DynamicLayout
- 异步创建StaticLayout
- 代码(项目中不可直接使用)
@Deprecated
public class CustomTextView extends View {
private String text="测试StaticLayout";
private TextPaint mTextPaint;
private StaticLayout mStaticLayout;
public CustomTextView(Context context) {
this(context,null);
}
public CustomTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initLableView();
}
private void initLableView() {
mTextPaint=new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16*getResources().getDisplayMetrics().densityDpi);
mTextPaint.setColor(Color.BLACK);
int width= (int) mTextPaint.measureText(text);
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
mStaticLayout = new StaticLayout(text, mTextPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
postInvalidate();
}
});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mStaticLayout != null){
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mStaticLayout.draw(canvas);
canvas.restore();
}
}
}
- 推荐库:TextLayoutBuilder
Github:https://github.com/facebook/TextLayoutBuilder
其它
- sysTrace跟踪
- 注意字符串拼接,最好是用StringBuilder
存储优化
常规方案
- 确保IO操作发生在非主线程
- Hook或者是AOP辅助
SharePreference相关
- 加载慢:初始化加载整个文件
- 全量写入:单次改动都会导致整体写入
- 卡顿:补偿策略导致
- 不支持跨进程通信
SharePreference替代者MMKV
- mmap和文件锁保证数据完整
- 增量写入,使用prorocol Buffer
- 支持从SharePreference迁移
- Github:https://github.com/Tencent/MMKV
- 依赖
implementation 'com.tencent:mmkv:1.0.19'
- 代码
MMKV.initialize(this);
MMKV.defaultMMKV().encode("times",100);
MMKV.defaultMMKV().decodeInt("times");
日志存储优化
-
大量服务需要日志库支持
-
对于性能的要求:不影响性能,日志不丢失,安全
-
常规实现
没产生一个日志,写一遍到磁盘中:不丢失,性能损耗
开辟一个内存buffer,先存buffer,再存文件:丢日志 -
常用数据的缓存,避免多次读取
-
合理选择缓冲区Buffer大小, 4-8kb
WebView异常监控
推荐库:https://github.com/Tencent/VasSonic
- 监控屏幕是否白屏,白屏则WebView有问题
- 确认是白屏,所有像素一致认为白屏
- 代码:webview白屏检测
public class BlankDetect {
/**
* 判断Bitmap是否都是一个颜色
*/
public static boolean isBlank(View view) {
Bitmap bitmap = getBitmapFromView(view);
if (bitmap == null) {
return true;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (width > 0 && height > 0) {
int originPix = bitmap.getPixel(0, 0);
int[] target = new int[width];
Arrays.fill(target, originPix);
int[] source = new int[width];
boolean isWhiteScreen = true;
for (int col = 0; col < height; col++) {
bitmap.getPixels(source, 0, width, 0, col, width, 1);
if (!Arrays.equals(target, source)) {
isWhiteScreen = false;
break;
}
}
return isWhiteScreen;
}
return false;
}
/**
* 从View获取转换到的Bitmap
*/
private static Bitmap getBitmapFromView(View view){
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
if (Build.VERSION.SDK_INT >= 11) {
view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY));
view.layout((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getMeasuredWidth(), (int) view.getY() + view.getMeasuredHeight());
} else {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
view.draw(canvas);
return bitmap;
}
}
网友评论