为了KPI,对APK进行极限优化!,android面试八股文

在不同版本上,该对象的数据类型都不一样。

4.0(华为畅玩4C,版本4.4.4)

5.0(华为P8 Lite,版本5.0.2)

6.0(三星GALAXY S7,版本6.0.1)

7.0+(三星GALAXY C7,版本7.0)

这里是第一个比较坑的地方,有的是int数组,有的是long数组,有的是Object数组的第一/最后一项,而且指令集位置有的在一起,有的是间隔的,确实比较坑,需要适配兼容。

8.0

8.0有一个问题,异常处理系统初始化时会执行如下逻辑:

// 代码版本:Android8.0,文件名称:RuntimeInit.java

protected static final void commonInit() {

if (DEBUG) Slog.d(TAG, “Entered RuntimeInit!”);

/*

* set handlers; these apply to all threads in the VM. Apps can replace

* the default handler, but not the pre handler.

*/

Thread.setUncaughtExceptionPreHandler(new LoggingHandler());

Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());

}

其中 Thread.setUncaughtExceptionPreHandler(new LoggingHandler()); 是该版本新增的,会在uncaughtException 之前调用,LoggingHandler 会导致Throwable#getInternalStackTrace被调用,该方法逻辑如下:

/**

* Returns an array of StackTraceElement. Each StackTraceElement

* represents a entry on the stack.

*/

private StackTraceElement[] getInternalStackTrace() {

if (stackTrace == EmptyArray.STACK_TRACE_ELEMENT) {

stackTrace = nativeGetStackTrace(stackState);

stackState = null; // Let go of intermediate representation.

return stackTrace;

} else if (stackTrace == null) {

return EmptyArray.STACK_TRACE_ELEMENT;

} else {

return stackTrace;

}

}

因此,8.0以上版本在Hook默认的 UncaughtExceptionHandler 时,stackState信息**已经丢失了!!**我的解决办法是 反射Hook掉Thread#uncaughtExceptionPreHandler 字段,使 LoggingHandler 被覆盖

但是在9.0会有以下错误:

Accessing hidden field Ljava/lang/Thread;->uncaughtExceptionPreHandler:Ljava/lang/Thread$UncaughtExceptionHandler; (dark greylist, reflection)

java.lang.NoSuchFieldException: No field uncaughtExceptionPreHandler in class Ljava/lang/Thread; (declaration of ‘java.lang.Thread’ appears in /system/framework/core-oj.jar)

at java.lang.Class.getDeclaredField(Native Method)

at top.vimerzhao.testremovelineinfo.ExceptionHookUtils.init(ExceptionHookUtils.java:18)

通过类似FreeReflection目前可以突破这个限制,因此Android 9+ 的机型依然可以使用这个方案。

##   深入

这里再详细介绍下底层获取行号的逻辑,首先Throwable会调用到一个native方法(这里的注释信息讲的很清楚,注意看):

//http://androidxref.com/4.4_r1/xref/dalvik/vm/native/dalvik_system_VMStack.cpp

/*

* public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements)

* Retrieve a partial stack trace of the specified thread and return

* the number of frames filled.  Returns 0 on failure.

*/

static void Dalvik_dalvik_system_VMStack_fillStackTraceElements(const u4* args,

JValue* pResult) {

Object* targetThreadObj = (Object*) args[0];

ArrayObject* steArray = (ArrayObject*) args[1];

size_t stackDepth;

int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);

if (traceBuf == NULL)

RETURN_PTR(NULL);

/*

* Set the raw buffer into an array of StackTraceElement.

*/

if (stackDepth > steArray->length) {

stackDepth = steArray->length;

}

dvmFillStackTraceElements(traceBuf, stackDepth, steArray);

free(traceBuf);

RETURN_INT(stackDepth);

}

该方法计算行信息的是dvmFillStackTraceElements:

// http://androidxref.com/4.4_r1/xref/dalvik/vm/Exception.cpp

/*

* Fills the StackTraceElement array elements from the raw integer

* data encoded by dvmFillInStackTrace().

* “intVals” points to the first {method,pc} pair.

*/

void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray) {

unsigned int i;

/* init this if we haven’t yet */

if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))

dvmInitClass(gDvm.classJavaLangStackTraceElement);

/*

* Allocate and initialize a StackTraceElement for each stack frame.

* We use the standard constructor to configure the object.

*/

for (i = 0; i < stackDepth; i++) {

Object* ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);

if (ste == NULL) {

return;

}

Method* meth = (Method*) *intVals++;

int pc = *intVals++;

int lineNumber;

if (pc == -1)      // broken top frame?

lineNumber = 0;

else

lineNumber = dvmLineNumFromPC(meth, pc);

/*

* Invoke:

*  public StackTraceElement(String declaringClass, String methodName,

*      String fileName, int lineNumber)

* (where lineNumber==-2 means “native”)

*/

JValue unused;

dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,

ste, &unused, className, methodName, fileName, lineNumber);

dvmSetObjectArrayElement(steArray, i, ste);

}

}

由此可知,默认行号可能是0,否则通过dvmLineNumFromPC获取具体信息:

//http://androidxref.com/4.4_r1/xref/dalvik/vm/interp/Stack.cpp

/*

* Determine the source file line number based on the program counter.

* “pc” is an offset, in 16-bit units, from the start of the method’s code.

* Returns -1 if no match was found (possibly because the source files were

* compiled without “-g”, so no line number information is present).

* Returns -2 for native methods (as expected in exception traces).

*/

int dvmLineNumFromPC(const Method* method, u4 relPc) {

const DexCode* pDexCode = dvmGetMethodCode(method);

if (pDexCode == NULL) {

if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method))

return -2;

return -1;      /* can happen for abstract method stub */

}

LineNumFromPcContext context;

memset(&context, 0, sizeof(context));

context.address = relPc;

// A method with no line number info should return -1

context.lineNum = -1;

dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode,

method->clazz->descriptor,

method->prototype.protoIdx,

method->accessFlags,

lineNumForPcCb, NULL, &context);

return context.lineNum;

}

由此可知,默认行号还可能是-2/-1,而dexDecodeDebugInfo里面就是具体的解析信息了,不做深入分析(太复杂了,给看懵逼了~)。

##   效果

以一台Android6.0的魅族为例,我的Demo部分日志如下:

01-14 10:17:42.525 845-868/? I/ExceptionHookUtils: succeed [28, 12, 12, 5, 6]

01-14 10:17:42.525 845-868/? I/ExceptionHookUtils: set top.vimerzhao.testremovelineinfo.a from -1 to 28

01-14 10:17:42.526 845-868/? I/ExceptionHookUtils: set top.vimerzhao.testremovelineinfo.a from -1 to 12

01-14 10:17:42.526 845-868/? I/ExceptionHookUtils: set top.vimerzhao.testremovelineinfo.a from -1 to 12

01-14 10:17:42.526 845-868/? I/ExceptionHookUtils: set top.vimerzhao.testremovelineinfo.MainActivity$a from -1 to 5

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-3iGOjnKN-1710932830661)]
[外链图片转存中…(img-yNp57gS3-1710932830662)]
[外链图片转存中…(img-9ADbyjEu-1710932830662)]
[外链图片转存中…(img-19GNdFBr-1710932830662)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-OPtMKsao-1710932830663)]

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值