结尾
最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
高级UI,自定义View
UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。
不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
**2. 谈一下JVM内存区域划分?哪部分是线程公有的,哪部分是私有的?**JVM 的内存区域可以分为两类:线程私有和区域和线程共有的区域。 线程私有的区域:程序计数器、JVM 虚拟机栈、本地方法栈;线程共有的区域:堆、方法区、运行时常量池。
- 程序计数器,也有称作PC寄存器。每个线程都有一个私有的程序计数器,任何时间一个线程都只会有一个方法正在执行,也就是所谓的当前方法。程序计数器存放的就是这个当前方法的JVM指令地址。当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。
- JVM虚拟机栈。创建线程的时候会创建线程内的虚拟机栈,栈中存放着一个个的栈帧,对应着一个个方法的调用。JVM 虚拟机栈有两种操作,分别是压栈和出站。栈帧中存放着局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。
- 本地方法栈。本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。
- 堆。堆是内存管理的核心区域,用来存放对象实例。几乎所有创建的对象实例都会直接分配到堆上。所以堆也是垃圾回收的主要区域,垃圾收集器会对堆有着更细的划分,最常见的就是把堆划分为新生代和老年代。java堆允许处于不连续的物理内存空间中,只要逻辑连续即可。堆中如果没有空间完成实例分配无法扩展时将会抛出OutOfMemoryError异常。
- 方法区。方法区与堆一样所有线程所共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
其实除了程序计数器,其他的部分都会发生 OOM。
- 堆。 通常发生的 OOM 都会发生在堆中,最常见的可能导致 OOM 的原因就是内存泄漏。
- JVM虚拟机栈和本地方法栈。 当我们写一个递归方法,这个递归方法没有循环终止条件,最终会导致 StackOverflow 的错误。当然,如果栈空间扩展失败,也是会发生 OOM 的。
- 方法区。方法区现在基本上不太会发生 OOM,但在早期内存中加载的类信息过多的情况下也是会发生 OOM 的。
3. final关键字的用法?
final 可以修饰类、变量和方法。修饰类代表这个类不可被继承。修饰变量代表此变量不可被改变。修饰方法表示此方法不可被重写 (override)。
4. 死锁是怎么导致的?
如何定位死锁某个任务在等待另一个任务,而后者又等待别的任务,这样一直下去,直到这个链条上的任务又在等待第一个任务释放锁。这得到了一个任务之间互相等待的连续循环,没有哪个线程能继续。这被称之为死锁。当以下四个条件同时满足时,就会产生死锁:
(1) 互斥条件。任务所使用的资源中至少有一个是不能共享的。
(2) 任务必须持有一个资源,同时等待获取另一个被别的任务占有的资源。
(3) 资源不能被强占。
(4) 必须有循环等待。一个任务正在等待另一个任务所持有的资源,后者又在等待别的任务所持有的资源,这样一直下去,直到有一个任务在等待第一个任务所持有的资源,使得大家都被锁住。
要解决死锁问题,必须打破上面四个条件的其中之一。在程序中,最容易打破的往往是第四个条件。
5. 数据库如何进行升级?SQLite增删改查的基础sql语句?
/**
- Create a helper object to create, open, and/or manage a database.
- This method always returns very quickly. The database is not actually
- created or opened until one of {@link #getWritableDatabase} or
- {@link #getReadableDatabase} is called.
- @param context to use to open or create the database
- @param name of the database file, or null for an in-memory database
- @param factory to use for creating cursor objects, or null for the default
- @param version number of the database (starting at 1); if the database is older,
-
{@link #onUpgrade} will be used to upgrade the database; if the database is
-
newer, {@link #onDowngrade} will be used to downgrade the database
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
private SQLiteDatabase getDatabaseLocked(boolean writable) {
…
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
在 SQLiteOpenHelper 的构造函数中,包含了一个 version 的参数。这个参数即是数据库的版本。 所以,我们可以通过修改 version 来实现数据库的升级。 当version 大于原数据库版本时,onUpgrade()会被触发,可以在该方法中编写数据库升级逻辑。具体的数据库升级逻辑示例可参考这里。
常用的SQL增删改查:
- 增:INSERT INTO table_name (列1, 列2,…) VALUES (值1, 值2,….)
- 删: DELETE FROM 表名称 WHERE 列名称 = 值
- 改:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
- 查:SELECT 列名称(通配是*符号) FROM 表名称
ps:操作数据表是:ALTER TABLE。该语句用于在已有的表中添加、修改或删除列。ALTER TABLE table_name ADD column_name datatypeALTER TABLE table_name DROP COLUMN column_name
Android
1. Broadcast的分类?有序,无序?粘性,非粘性?本地广播?
- 广播可以分为有序广播、无序广播、本地广播、粘性广播。其中无序广播通过sendBroadcast(intent)发送,有序广播通过sendOrderedBroadcast(intent)发送。
- 有序广播。(1) 有序广播可以用priority来调整优先级 取值范围-1000~+1000,默认为0,数值越大优先级越高,优先级越高越优先获得广播响应。(2) abortBroadcast()可来终止该广播的传播,对更低优先级的屏蔽,注意只对有序广播生效。(3) 有序广播在传播数据中会发生比如setResultData(),getResultData(),在传播过程中,可以从新设置数据
- 关于本地广播,可以查看这篇文章。总的来说,本地广播是通过LocalBroadcastManager内置的Handler来实现的,只是利用了IntentFilter的match功能,至于BroadcastReceiver 换成其他接口也无所谓,顺便利用了现成的类和概念而已。在register()的时候保存BroadcastReceiver以及对应的IntentFilter,在sendBroadcast()的时候找到和Intent对应的BroadcastReceiver,然后通过Handler发送消息,触发executePendingBroadcasts()函数,再在后者中调用对应BroadcastReceiver的onReceive()方法。
- 粘性消息:粘性消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)
2. Android中的事件传递机制?
当我们的手指触碰到屏幕,事件是按照Activity->ViewGroup->View这样的流程到达最终响应触摸事件的View的。而在事件分发过程中,涉及到三个最重要的方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。我们的手指触摸到屏幕的时候,会触发一个Action_Down类型的事件,当前页面的Activity会首先做出相应,也就是说会走到Activity的dispatchTouchEvent()方法内。在这个方法内部有下面两个逻辑:
- 调用getWindow.superDispatchTouchEvent()。
- 如果上一步返回true,则直接返回true;否则return自己的onTouchEvent()。显然,当getWindow.superDispatchTouchEvent()返回true,表示当前事件已经被消费掉,无需调用onTouchEvent;否则代表事件并没有被处理,因此需要调用Activity的onTouchEvent进行处理。我们都知道,getWindow()返回的是PhoneWindow,因此这句代码本质上调用了PhoneWindow中的superDispatchTouchEvent()。而后者实际上调用了mDecor.superDispatchTouchEvent(event)。这个mDecor也就是DecorView,它是FrameLayout的一个子类。在DecorView中的
- superDispatchTouchEvent(event)中调用的是super.dispatchTouchEvent()。因此,本质上调用的是ViewGroup的dispatchTouchEvent()。到这里,事件已经从Activity传递到ViewGroup了。接下来我们分析ViewGroup。在ViewGroup的dispatchTouchEvent()中逻辑大致如下:
- 通过onInterceptTouchEvent()判断当前ViewGroup是否拦截,默认的ViewGroup都是不拦截的;
- 如果拦截,则return自己的onTouchEvent();
- 如果不拦截,则根据child.dispatchTouchEvent()的返回值判断。如果返回true,则return true;否则return自身的onTouchEvent(),在这里实现了未处理事件的向上传递。
通常情况下,ViewGroup 的 onInterceptTouchEvent() 都返回 false,表示不拦截。这里需要注意的是事件序列,比如Down事件、Move事件…Up事件,从 Down到 Up 是一个完整的事件序列,对应着手指从按下到抬起这一系列事件,如果ViewGroup 拦截了 Down 事件,那么后续事件都会交给这个 ViewGroup 的onTouchEvent。如果 ViewGroup 拦截的不是 Down 事件,那么会给之前处理这个Down 事件的 View发送一个Action_Cancel 类型的事件,通知子View这个后续的事件序列已经被 ViewGroup 接管了,子 View 恢复之前的状态即可。
这里举一个常见的例子:
最后
简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。
选定你想去的几家公司后,先去一些小的公司练练,学习下面试技巧,总结下,也算是熟悉下面试氛围,平时和同事或者产品PK时可以讲得头头是道,思路清晰至极,到了现场真的不一样,怎么描述你所做的一切,这绝对是个学术性问题!
面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。
金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!