阿里面试主要问的是一些原理性的东西,比如HashMap实现原理,线程间通讯,线程间数据共享,android对java在哪些方法上 有优化,android的异步任务是怎么实现的等等,接下来我们一一讲解。


1,HashMap实现原理,HashMap是不是有序的,如果不是,那么有没有实现Map的子类?

首先说下java语音的,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,所以HaspMap底层实现是以数组的方式实现的,如果想深入了解里面的实现,请访问下面的链接javascript:void(0)。

HaspMap不是有序的,那么有没有Map的实现类是有序的呢?有,比如我们常用的TreeMap和LinkHashMap,s首先说下TreeMap吧,TreeMap<String, String> 需要重写里面一个方法compare(String o1, String o2),这里返回的是一个经过比较了大小的有序数组;LinkHashMap看名字意思就是有一个链表的,肯定是有序的,实际上

LinkedHashMap内部维持了一个双向链表,可以保持顺序。有兴趣的可以自己写一个小Demo.


2,在Android子线程中初始化handler后,为什么该子线程也能更新UI

答:首先明确在子线程是没办法更新UI线程的视图的。


3.在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可(内存限制为 2G的意思就是,可以使用2G的空间来运行程序,而不考虑这台机器上的其他软件的占用内存)。

关于中位数:数据排序后,位置在最中间的数值。即将数据分成两部分,一部分大于该数值,一部分小于该数值。中位数的位置:当样本数为奇数时,中位数=(N+1)/2 ; 当样本数为偶数时,中位数为N/2与1+N/2的均值(那么10G个数的中位数,就第5G大的数与第5G+1大的数的均值了)。

解法:

(1)根据整数二进制数高12位取值, 对10G个文件进行分割, 分割成2的12次方(4096)个文件, 每个文件大约有2.5M个整数。

(2)因为4096个文件按整数高12位分割的, 所以文件间是有序的, 例如高12位为0000 0000 0000的文件里的数字是所有数字里最小的, 高12位为 0000 0000 0001的文件中的数字是所有数字中相对次小的。

(3)以此按从小到大的顺序对文件中数字的个数进行统计, 分别记为x1, x2, ..., xk..., 直到某个文件i使x1+x2+...+xi和大于5G结束, 中位数就在这个文件中。然后对这个文件进行处理。

(4)第i个文件中, 大约有2.5M个数字, 取值有1M个,遍历文件对1M个数字出现的次数进行统计。

(5)对1M个取值, 按照从小到大进行加和, 第一个使总和到达5G的数字就是中位数。


4,java的多线程直接怎么通讯的?

java的线程通讯无外乎用到了Thead.wait()和notify()等,那么无聊的人肯定会问你了,这wait(),sleep(),等常用方法的区别了,其实也就是线程的阻塞,就绪,运行等,有兴趣的可以好好去查查相关的文章。


5,接下来是算法题,有一个一位数组,数组的长度很大,然后里面有唯一的两个数相等的,问你用最快的算法实现去找到这两个数及所在数组的位置。


6,说说Thead的sleep和wait的区别

sleep(100L):占用CPU,线程休眠100毫秒
wait(100L):不占用CPU,线程等待100毫秒


7,缓存策略,LruCache和DiskLruCache原理。

这个比较坑,一时半会也说不清楚,对于LruCache的原理我们可以去看系统给我们的提供的类LruCache<K, V>,

LruCache使用一个LinkedHashMap简单的实现内存的缓存,没有软引用,都是强引用。如果添加的数据大于设置的最大值,就删除最先缓存的数据来调整内存。他的主要原理在trimToSize方法中。

maxSize是通过构造方法初始化的值,他表示这个缓存能缓存的最大值是多少。

size在添加和移除缓存都被更新值,他通过safeSizeOf这个方法更新值。safeSizeOf默认返回1,但一般我们会根据maxSize重写这个方法,比如认为maxSize代表是KB的话,那么就以KB为单位返回该项所占的内存大小。

然后你只要按着这个思路说,就好了

DiskLruCache意思是磁盘缓存,就是将网络的数据缓存到磁盘,这个用的比较多的就是我们的第三方缓存策略库了,

其实DiskLruCache并没有限制数据的缓存位置,可以自由地进行设定,但是通常情况下多数应用程序都会将缓存的位置选择为 /sdcard/Android/data/<application package>/cache 这个路径。选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。


8,Android Handler leak 分析及解决办法

在Android中,Handler类应该是静态的,否则,可能发生泄漏。在应用程序线程的MessageQueue中排队的Message对象 还保留他们的目标Handler。所以,如果我们如果在一个类的内部使用Handler,应将它设置为static

为了避免泄漏外部类,声明一个Handler子类为静态内部类(注:这样就避免了Handler对象对外部类实例的自动引用),其内部持有一个对外部类对象的WeakReference。

那么什么情况下容易造成Handler leak 呢?(1) 排队中的Message对象对Handler的持有导致泄漏;(2)Handler对象对外部类(如Activity或Service)实例的强引用持 有。

​static​​​ ​​class​​​ ​​MyHandler ​​​​extends​​​ ​​Handler {​

​private​​​ ​​WeakReference<MyActivity> mOuter;​

​public​​​ ​​MyHandler(MyActivity activity) {​

​mOuter = ​​​​new​​​ ​​WeakReference<MyActivity>(activity);​

​}​

​}​



​9.说说你在常用开发中,用到设计模式。​

大家可以点击链接:​​23种设计模式​​


10,hashmap的实现原理。

HashMap里面实现一个静态内部类Entry,里面有三个重要的属性 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

有需要的可以点击链接:​​hashmap源码分析​​


11,HashMap会在什么什么情况下出现性能问题?


12,HashTable和CocurrentHashMap的区别?

hashtable是做了同步的,hashmap未考虑同步。所以hashmap在单线程情况下效率较高。hashtable在的多线程情况下,同步操作能保证程序执行的正确性。

CocurrentHashMap是hashtable和hashmap的综合实现,ConcurrentHashMap锁的方式是稍微细粒度的。具体请看: ​​cocurrentHashMap​​


13.Volatile和Sychronized的区别


14,知道JVM的分区么?


15.Android中的线程池使用过哪些,介绍一下?回答用过AsyncTask,追问AsyncTask的内部机制是什么?


16 能描述一下Android的启动过程么?


17,请分下下Android的View事件分发机制?

在阐述view的事件分发机制的时候一定要区分说明view和viewgroup的分发的异同点

阿里面试_缓存

18,安卓view绘制机制和加载过程,请详细说下整个流程

  • 1.ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw()。
  • 2.performMeasure()会调用最外层的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild()这之中会用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起获取本View的MeasureSpec,然后调用子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默认返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。
  • 3.performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame()-->子View.layout()。
  • 4.performDraw()会调用最外层ViewGroup的draw():其中会先后调用background.draw()(绘制背景)、onDraw()(绘制自己)、dispatchDraw()(绘制子View)、onDrawScrollBars()(绘制装饰)。
  • 5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(对应精确值和match_parent)、AT_MOST(对应warp_content))和30位SpecSize组成一个int,DecorView的MeasureSpec由窗口大小和其LayoutParams决定,其他View由父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。
  • 6.三种方式获取measure()后的宽高:
  • 1.Activity#onWindowFocusChange()中调用获取
  • 2.view.post(Runnable)将获取的代码投递到消息队列的尾部。
  • 3.ViewTreeObservable.

19,请分析下activity的启动过程?

1.Activity中最终到startActivityForResult()(mMainThread.getApplicationThread()传入了一个ApplicationThread检查APT)
->Instrumentation#execStartActivity()和checkStartActivityResult()(这是在启动了Activity之后判断Activity是否启动成功,例如没有在AM中注册那么就会报错)
->ActivityManagerNative.getDefault().startActivity()(类似AIDL,实现了IAM,实际是由远端的AMS实现startActivity())
->ActivityStackSupervisor#startActivityMayWait()
->ActivityStack#resumeTopActivityInnerLocked
->ActivityStackSupervisor#realStartActivityLocked()(在这里调用APT的scheduleLaunchActivity,也是AIDL,不过是在远端调起了本进程Application线程)
->ApplicationThread#scheduleLaunchActivity()(这是本进程的一个线程,用于作为Service端来接受AMS client端的调起)
->ActivityThread#handleLaunchActivity()(接收内部类H的消息,ApplicationThread线程发送LAUNCH_ACTIVITY消息给H)
->最终在ActivityThread#performLaunchActivity()中实现Activity的启动完成了以下几件事:2.从传入的ActivityClientRecord中获取待启动的Activity的组件信息3.创建类加载器,使用Instrumentation#newActivity()加载Activity对象4.调用LoadedApk.makeApplication方法尝试创建Application,由于单例所以不会重复创建。5.创建Context的实现类ContextImpl对象,并通过Activity#attach()完成数据初始化和Context建立联系,因为Activity是Context的桥接类,
最后就是创建和关联window,让Window接收的事件传给Activity,在Window的创建过程中会调用ViewRootImpl的performTraversals()初始化View。6.Instrumentation#callActivityOnCreate()->Activity#performCreate()->Activity#onCreate().onCreate()中会通过Activity#setContentView()调用PhoneWindow的setContentView()
更新界面。