总结
我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题
等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~
Android 基础知识点
Java 基础知识点
Android 源码相关分析
常见的一些原理性问题
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
对于好定位,当然是可以通过日志信息点击,定位到具体行,所以今天demo代码的效果是这样的:
当然了,你可以根据自己喜好,去添加各种信息,以及装饰。
那么,现在最大的一个问题就是
- 我怎么输出具体的日志调用行呢?
这个秘密就在:
Thread.currentThread().getStackTrace();
我们可以通过当前的线程,拿到当前调用的栈帧集合(称呼不一定准备)。
- 这个栈帧集合是什么玩意呢?
你可以理解为当我们调用方法的时候,每进入一个方法,会将该方法的相关信息(例如:类名,方法名,方法调用行数等)存储下来,压入到一个栈中,当方法返回的时候再将其出栈。
下面看个具体的例子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
a();
}
void a() {
b();
}
void b() {
StringBuffer err = new StringBuffer();
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
for (int i = 0; i < stack.length; i++) {
err.append("\tat ");
err.append(stack[i].toString());
err.append(“\n”);
}
Log.e(“TAG”, err.toString());
}
我在onCreate中,调用了a方法,然后a中调用的b方法。在b方法中打印出当前线程中的栈帧集合信息。
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:579)
at com.zxy.recovery.test.MainActivity.b(MainActivity.java:26)
at com.zxy.recovery.test.MainActivity.a(MainActivity.java:21)
at com.zxy.recovery.test.MainActivity.onCreate(MainActivity.java:17)
at android.app.Activity.performCreate(Activity.java:5231)
…
可以看到我们整个方法的调用过程,底部的最先开始调用,顺序为onCreate->a->b->Thread.getStackTrace->VMStack.getThreadStackTrace.
最后两个是因为我们的stacks是在VMStack.getThreadStackTrace方法中获取,然后返回的,所以包含了这两个的内部调用信息。
这里我们直接调用的StackTraceElement的toString方法,它内部有:
-
getClassName
-
getMethodName
-
getFileName
-
getLineNumber
看名字就知道什么意思了,我们可以根据这些信息拼接要打印的信息。
所以,不管怎么说,我们现在已经确定了,可以通过该种方式得到我们的调用某个方法的行数,而且是支持点击跳转到指定位置的。
到这里相当于,方案的可行性就通过了,剩下就是码代码了。
三、实现
先写个大致的代码:
public class L{
private static boolean sDebug = true;
private static String sTag = “zhy”;
public static void init(boolean debug, String tag){
L.sDebug = debug;
L.sTag = tag;
}
public static void e(String msg, Object… params){
e(null, msg, params);
}
public static void e(String tag, String msg, Object[] params){
if (!sDebug) return;
tag = getFinalTag(tag);
//TODO 通过stackElement打印具体log执行的行数
Log.e(tag, content);
}
private static String getFinalTag(String tag){
if (!TextUtils.isEmpty(tag)){
return tag;
}
return sTag;
}
}
因为我平时基本上只用Log.e,所以我就不对其他方法进行处理了,你可以根据你的喜好来决定。
ok,那么现在只有一个地方没有处理,就是打印log执行的类以及代码行。
我在onCreate的17行调用了:
L.e(“Hello World”);
然后在e()方法中,打印了所有的栈帧信息:
E/zhy: at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:579)
at com.zxy.recovery.test.L.e(L.java:32)
at com.zxy.recovery.test.L.e(L.java:25)
at com.zxy.recovery.test.MainActivity.onCreate(MainActivity.java:19)
at android.app.Activity.performCreate(Activity.java:5231)
//…
E/zhy: Hello World
我们要输出的就是上述的MainActivity.onCreate(MainActivity.java:19)
- 那么我们如何定位呢?
观察上面的信息,因为我们的入口是L类的方法,所以,我们直接遍历,L类相关的下一个非L类的栈帧信息就是具体调用的方法。
于是我们这么写:
private StackTraceElement getTargetStackTraceElement() {
// find the target invoked method
StackTraceElement targetStackTrace = null;
boolean shouldTrace = false;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
boolean isLogMethod = stackTraceElement.getClassName().equals(L.class.getName());
if (shouldTrace && !isLogMethod) {
targetStackTrace = stackTraceElement;
break;
}
shouldTrace = isLogMethod;
}
return targetStackTrace;
}
拿到确定的方法调用相关的栈帧之后,就是输出啦~~
添加到e()方法中:
public static void e(String tag, String msg, Object… params) {
if (!sDebug) return;
String finalTag = getFinalTag(tag);
StackTraceElement targetStackTraceElement = getTargetStackTraceElement();
Log.e(finalTag, “(” + targetStackTraceElement.getFileName() + “:”
- targetStackTraceElement.getLineNumber() + “)”);
Log.e(finalTag, String.format(msg, params));
}
现在再看下输出结果:
现在就可以迅速的定位到日志输出行,再也不要全局搜索去查找了~
到这里,对于我个人的需求已经满足了,如果你有特殊需要,比如也想像logger那样搞个框,那就自己绘制吧,也可以参考它的源码。
对了,还有json,有时候希望可以看json字符串更加的直观,像looger那样:
你可以参考它的做法,其实就是将json字符串,通过JsonArray和JsonObject进行了一个类似format这样的操作。
private static String getPrettyJson(String jsonStr) {
try {
jsonStr = jsonStr.trim();
if (jsonStr.startsWith(“{”)) {
JSONObject jsonObject = new JSONObject(jsonStr);
return jsonObject.toString(JSON_INDENT);
}
if (jsonStr.startsWith(“[”)) {
文末
当你打算跳槽的时候,应该把“跳槽成功后,我能学到什么东西?对我的未来发展有什么好处”放在第一位。这些东西才是真正引导你的关键。在跳槽之前尽量“物尽其用”,把手头上的工作做好,最好是完成了某个项目或是得到提升之后再走。跳槽不是目的,而是为了达到最终职业目标的手段
最后祝大家工作升职加薪,面试拿到心仪Offer
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
好处”放在第一位。这些东西才是真正引导你的关键。在跳槽之前尽量“物尽其用”,把手头上的工作做好,最好是完成了某个项目或是得到提升之后再走。跳槽不是目的,而是为了达到最终职业目标的手段**
最后祝大家工作升职加薪,面试拿到心仪Offer
[外链图片转存中…(img-x3L70sR5-1715408388209)]
[外链图片转存中…(img-W8LxoLpA-1715408388209)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!