Github地址:新聞?lì)怉pp (MVP + RxJava + Retrofit+Dagger+ARouter)
繪制原理
- CPU負(fù)責(zé)計(jì)算顯示的內(nèi)容
- GPU負(fù)責(zé)柵格化(UI元素繪制到屏幕上)
- 16ms發(fā)出VSync信號(hào)觸發(fā)UI渲染
- 大多數(shù)Android設(shè)備屏幕刷新頻率:60Hz
優(yōu)化工具
Systrance
- 關(guān)注Framas
- 正常:綠色圓點(diǎn)辛燥,丟幀:黃色或者紅色
- Alerts欄
Layout Inspector 查看視圖層次結(jié)構(gòu)
image.png
我的頁(yè)面結(jié)果
image.png
Choreographer
獲取fps,線上使用,具備實(shí)時(shí)性
- API16之后
- Choreographer.getInstance().postFrameCallback
- 代碼
private int mFrameCount = 0;
private static final long MONITOR_INTERVAL = 160L; //單次計(jì)算FPS使用160毫秒
private static final long MONITOR_INTERVAL_NANOS = MONITOR_INTERVAL * 1000L * 1000L;
private static final long MAX_INTERVAL = 1000L; //設(shè)置計(jì)算fps的單位時(shí)間間隔1000ms,即fps/s;
private long mStartFrameTime=0;
private void getFPS() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return;
}
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (mStartFrameTime == 0) {
mStartFrameTime = frameTimeNanos;
}
long interval = frameTimeNanos - mStartFrameTime;
if (interval > MONITOR_INTERVAL_NANOS) {
double fps = (((double) (mFrameCount * 1000L * 1000L)) / interval) * MAX_INTERVAL;
LogUtils.e(fps);
mFrameCount = 0;
mStartFrameTime = 0;
} else {
++mFrameCount;
}
Choreographer.getInstance().postFrameCallback(this);
}
});
}
獲取布局耗時(shí)
- 背景:獲取每個(gè)界面加載耗時(shí)
- 實(shí)現(xiàn):覆寫方法该酗,手動(dòng)埋點(diǎn)
- AOP實(shí)現(xiàn),關(guān)于AOP的使用大家可以看我之前啟動(dòng)優(yōu)化這篇文章http://www.reibang.com/p/6d5cddd56d94
@Around("execution (* android.app.Activity.setContentView**(..))")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.e("SectionAspect", signature.getName() + " cost Time:" + (System.currentTimeMillis() - time));
}
獲取某個(gè)布局每個(gè)控件耗時(shí)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
long time = System.currentTimeMillis();
View view = getDelegate().createView(parent, name, context, attrs);
LogUtils.e(name + " cost " + (System.currentTimeMillis() - time));
return view;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
});
super.onCreate(savedInstanceState);
}
異步Inflate
- 背景介紹
布局文件讀取慢,創(chuàng)建View慢(反射:比new慢3倍) - AsyncLayoutInflater實(shí)踐,只是緩解并不能根本去解決
WorkThread加載布局->回掉主線程 ->節(jié)約主線程時(shí)間
添加依賴
implementation 'com.android.support:asynclayoutinflater:28.0.0-alpha1'
代碼
new AsyncLayoutInflater(this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
setContentView(view);
//初始化一些參數(shù)比如findViewById等
}
});
- 缺陷:不能設(shè)置layoutInflater.Factory,view中不能有依賴主線程的操作
布局加載優(yōu)化
java代碼寫布局
本質(zhì)上解決了問(wèn)題,但是不便于開發(fā),可維護(hù)性差X2C框架
保留XML優(yōu)點(diǎn)杖挣,解決其性能問(wèn)題開發(fā)人員寫xml,加載Java代碼
原理:APT編譯期間翻譯XML為Java代碼
缺點(diǎn):部分屬性Java不支持,失去了系統(tǒng)的兼容,不能用于線上
依賴
annotationProcessor 'com.zhangyue.we:x2c-apt:1.1.2'
implementation 'com.zhangyue.we:x2c-lib:1.0.6'
代碼
@Xml(layouts = "activity_main")
public class MainActivity extends BaseActivity
原本的setContentView-> X2C.setContentView(MainActivity.this,R.layout.activity_main);
視圖繪制優(yōu)化
- 減少View樹層級(jí)
- 布局寬而淺,避免窄而深
布局的選擇
- ConstraintLayout
實(shí)現(xiàn)幾乎完全扁平化布局
構(gòu)建復(fù)雜布局性能更高
具有RelativeLayout和LinearLayout特性 - FrameLayout能實(shí)現(xiàn)的優(yōu)先使用FrameLayout
- 優(yōu)先選擇RelativeLayout
- 當(dāng)在不嵌套的情況下刚陡,RelativeLayout和LinearLayout同時(shí)能滿足需求時(shí),優(yōu)先選擇LinearLayout株汉。因?yàn)镽elativeLayout功能復(fù)雜且會(huì)出現(xiàn)重復(fù)繪制
- 不嵌套使用RelativeLayout
- 不在嵌套linearLayout中使用weight
-
使用include
目的是提高代碼的復(fù)用性筐乳,減少代碼,將布局中公共部分抽取供其他layout使用 -
使用merge
解決布局層次級(jí)的優(yōu)化乔妈,減少布局嵌套的層次蝙云,提高布局加載的效率 -
使用viewStub
ViewStub只有加載該布局的時(shí)候才占用資源,INVISIBLE狀態(tài)是不會(huì)繪制出來(lái)的路召。如:加載網(wǎng)絡(luò)錯(cuò)誤的時(shí)候顯示的布 - onDraw中避免:創(chuàng)建大對(duì)象勃刨,耗時(shí)操作
- TextView優(yōu)化
過(guò)度繪制
- 一個(gè)像素最好只被繪制一次
- 調(diào)試GPU過(guò)度繪制
- 藍(lán)色可以接受
避免過(guò)度繪制的方法
- 去掉多余背景色波材,減少?gòu)?fù)雜shape使用
- 避免層級(jí)疊加
- 自定義view使用clipRect 屏蔽被覆蓋View繪制
打開自己的GPU過(guò)度繪制
image.png
我的app優(yōu)化后的效果
image.png
藍(lán)色1x過(guò)度繪制
綠色2x過(guò)度繪制
淡紅色3x過(guò)度繪制
紅色超過(guò)4x過(guò)度繪制