Reason: Broadcast of Intent { act=android.intent.action.TIME_TICK
ActivityManager:
ANR in com.***.***
PID: 16227
Reason: Broadcast of Intent { act=android.intent.action.TIME_TICK flg=0x50000014 (has extras) }
有那么一段时间我被这个ANR折磨到每天吃不下饭睡不着觉日渐憔悴,Read the fucking source code,每天看android源码各种跟踪调试编译烧写之后,终于找出了问题所在,应用层APP的UI线程刷新太频繁!
不要使用Html.fromHtml()
Textview里的文字设置多种颜色时不要偷懒,分两个Textview来写,使用Html.fromHtml加载时会耗时500+毫秒,用多了引起卡顿。
Handler传参尽量要使用基础类型
msg.obj传递java类对象在1秒400次频率下会导致数据丢失卡顿,并且会莫名其妙出现数据错乱。
UI线程一定要只做刷新UI动作
UI线程最好只做界面相关的动作,不要为了偷懒少些几行代码就把整个数据抛给UI线程,并发大的时候会丢失数据。
数据的各种格式和逻辑要在子线程中判断,把不需要刷新UI的数据拦截下来并抛掉,总之就是不要频繁刷新UI。
EventBus不适合高并发数据的处理,老老实实写接口用回调吧
在实现一些设计模式上EventBus更有用,但在高并发数据下即使加了eventBusIndex处理速度还是慢,并且会越来越慢。
定时任务不要用timer,使用Handler的sendEmptyMessageDelayed()或sendMessageDelayed()
timer耗费的资源多,并且一旦异常之后就会把整个任务终结掉出现各种难以预料的问题。
jni回调java函数时,一定要去冗余数据
jni层把有用数据过滤精简之后再回调java函数,1000毫秒回调400多次根本刷新不过来。
JNI DETECTED ERROR IN APPLICATION: JNI SetByteArrayRegion called with pending exception 'android.view.ViewRootImpl$CalledFromWrongThreadException' thrown in unknown throw location
刚开始遇到这个错误我以为jni层SetByteArrayRegion这个地方byte数组copy出错,单独调试jni的库也一点问题没有。最后感觉这个地方又有view又有Thread,那么会不会是在jni的子线程里调用了UI线程导致,十几个从jni回调上来的地方一个一个地方排查,果然是在callback线程直接更新UI所致!一个地方遗漏掉了,同事以为我改了,我以为他改了,结果谁都没改,因为板子没接收音机的模块,CAN总线没有发收音机的数据上来,这个模块一直没调试过,直到被测试发现之后叼了我们一顿。信息同步很重要啊!
Skipped *** frames! The application may be doing too much work on its main thread.
场景是不停的用handler定时发delay message,然后分发给UI来跑界面,模拟真实使用场景,跑了一夜,第二天又卡又慢,这个问题不用到处搜索了,写的清清楚楚明明白白
The application may be doing too much work on its main thread
只能看自己的代码调试看看耗时操作,逐个场景分析。我这里是因为真实数据是在子线程分发出去,然后个UI线程获取数据之后再用Handler来更新UI,但是因为模拟分发数据,为了图方便用了handler的sendMessageDelayed函数,所以相当于主线程分发数据之后,UI线程又开一个Handler又在主线程中刷新数据,造成不必要开销。解决方法就是,加一个开关,如果是模拟数据,直接更新UI不再发handler更新UI。
android.view.InflateException: Binary XML file line # *: Error inflating class
自定义view库在另外一个功能,通过compile project的方式引入,然后在项目工程的layout文件里直接按照包名类名来引入,总是报这个错误,包名类名路径都正确,很郁闷,一般也不会怀疑一个经过各种验证的自己写好的封装view库,但是原因就在这里。
@RequiresApi(api = Build.VERSION_CODES.N)
public MyView(Context context, AttributeSet attrs, int defStyleAttr)
为了调用新接口构造函数加了的API版本,而运行的机器低于这个版本,所以在xml里配置这个view的时候就会报错,这个地方调用低版本的API,去掉版本限制就OK了,RequiresApi要慎用。
自定义View抗锯齿、画大于180度的扇形问题
画仪表盘的时候,刚开始是用切割画布的方式实现clipPath,但是锯齿太明显了,后来用图层的方式实现,效果非常平滑完美,主要用到PorterDuff.Mode.DST_OUT,但是要先设置setLayerType为LAYER_TYPE_SOFTWARE或者LAYER_TYPE_HARDWARE
大于180度的扇形时,由于path的addArc函数只能添加一个弧形,如果想要一个扇形还要切割一个三角形,和这个弧形拼凑起来,如果大于180度的时候,下面会多出一条线,这时候要把大于180度的面积,分割成两个扇形,分别添加到path中,多说无益,上代码,示例代码是从150度开始绘制.
补充:后面我再看这段代码发现还有优化的空间
对于Dalvik虚拟机来说,要尽量避免频繁生成临时变量特别是onDraw等函数,也要避免产生很多长生命周期的对象,但是出于懒的关系,下面这段代码并没有修改
private void drawProgressIndicator(Canvas canvas, int percent) {
if (percent == 0) {
return;
}
canvas.save();
canvas.translate(0f, 0f);
canvas.drawBitmap(bitmapIndicatorShadow, 0, 0, dPaint);
float startAngle = START_ARC + percent;
dPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
float sX, sY;
float eX, eY;
sX = (float) (CENTER + (2 + CENTER) * Math.cos((startAngle) * Math.PI / 180));
sY = (float) (CENTER + (2 + CENTER) * Math.sin((startAngle) * Math.PI / 180));
eX = (float) (CENTER + (2 + CENTER) * Math.cos((endAngle) * Math.PI / 180));
eY = (float) (CENTER + (2 + CENTER) * Math.sin((endAngle) * Math.PI / 180));
RectF rectF = new RectF(0, 0, mWidth, mWidth);
Path path = new Path();
path.reset();
path.moveTo(CENTER, CENTER);
path.lineTo(sX, sY);
if (startAngle <= 210) {
float cX = (float) (CENTER + (2 + CENTER) * Math.cos((270) * Math.PI / 180));
float cY = (float) (CENTER + (2 + CENTER) * Math.sin((270) * Math.PI / 180));
path.lineTo(cX, cY);
path.lineTo(eX, eY);
path.close();
path.addArc(rectF, startAngle, 270 - startAngle);
path.addArc(rectF, 270, endAngle - 270);
} else {
path.lineTo(eX, eY);
path.close();
path.addArc(rectF, startAngle, endAngle - startAngle);
}
canvas.drawPath(path, dPaint);
dPaint.setXfermode(null);
canvas.restore();
}
优化内存
1、所有的HashMap尽量改ArrayMap
2、所有的enum尽量改成static变量
3、所有xml布局层级嵌套超过3层精简一下
4、所有的for或while循环里,尽量避免new对象除非逻辑如此
5、所有for或while避免使用Iterator或者for(Object obj:list)形式便利,直接用最原始语句遍历
6、onDraw()里不要new对象
7、能用jpg的图就不要用png,jpg的图质量最低压缩比例最高,网上搜索一下jpg和png压缩工具,压缩图片大小
8、所有fragment,特别是主界面里的,改成用时加载不用时销毁,不再一直加载,容易被回收
9、编译时剪裁系统应用主要在./build/target/product下,利用find语句查找并注释删减应用,在注释时,# Launcher2 \
要把连接符\
直接删除掉,否则注释语句相当于/** Launcher2 \ 后面的一长串代码 **/
find . -type f -name '*' | xargs grep 'Launcher2'
find . -name '*' | xargs grep dalvik.vm.heapsize
10、内存相关
adb remount
adb shell
mount -o remount rw /system
dumpsys meminfo
adb shell dumpsys meminfo com.***.***
adb shell dmesg
adb shell cat /proc/kmsg
adb shell procrank
//查看应用启动时间
adb shell am start -W com.***.***/com.***.***.MainActivity
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
修改framework层应用内存阈值大小在./frameworks/native/build
下,比如android:largeHeap="true"时,调整内存到512m
PRODUCT_PROPERTY_OVERRIDES += \
dalvik.vm.heapstartsize=8m \
dalvik.vm.heapgrowthlimit=64m \
dalvik.vm.heapsize=512m \
dalvik.vm.heaptargetutilization=0.75 \
dalvik.vm.heapminfree=512k \
dalvik.vm.heapmaxfree=8m
11、固定应用方向,使android:screenOrientation="reverseLandscape"
无效,固定在某个方向,修改frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java
源文件
@Override
public int rotationForOrientationLw(int orientation, int lastRotation) {
if (false) {
Slog.v(TAG, "rotationForOrientationLw(orient="
+ orientation + ", last=" + lastRotation
+ "); user=" + mUserRotation + " "
+ ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
? "USER_ROTATION_LOCKED" : "")
);
}
if (mForceDefaultOrientation) {
return Surface.ROTATION_0;
}
修改为固定旋转180度
@Override
public int rotationForOrientationLw(int orientation, int lastRotation) {
if (false) {
Slog.v(TAG, "rotationForOrientationLw(orient="
+ orientation + ", last=" + lastRotation
+ "); user=" + mUserRotation + " "
+ ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
? "USER_ROTATION_LOCKED" : "")
);
}
if (mForceDefaultOrientation || true) {
return Surface.ROTATION_180;
}
12、删减系统服务
在frameworks/base/services/java/com/android/server/SystemServer.java中删减服务, 这里也是启动SystemUI的地方
网友评论