2021届阅文Android方向笔试卷

150 阅读8分钟

onSaveInstanceState方法会在什么时候被执行?

  1. 当用户按下HOME键时,系统不知道你按下HOME键后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据
  2. 长按HOME键,选择其他运行程序时
  3. 按下电源按键(关闭屏幕显示)时
  4. 从Activity A中启动一个新的activity时
  5. 屏幕方向切换时,例如从竖屏切换到横屏时

简述View Touch事件传递机制?

dispatchTouchEvent:进行事件的分发,返回值是boolean类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响

onInterceptTouchEvent:对事件进行拦截,该方法只在ViewGroup中有,View(不包括ViewGroup)中是没有的,一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View,且只调用一次,所以后面的事件都会交给ViewGroup处理

onTouchEvent:进行事件处理

为什么在子线程中执行 new Handler()会抛出异常?

当创建Handler时会获取当前线程的Looper,若Looper为null则抛出异常

invalidate()和postInvalidate()的区别?

invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。

res目录和assert目录的区别?

res/rew中的文件会被映射到R.java文件中,访问时可以直接使用资源id,不可以有目录结构 assets文件夹下的文件不会被映射到R.java中,访问时需要AssetManager类,可以创建子文件夹

onTouch()、onTouchEvent()和onClick()关系?

优先度onTouch()>onTouchEvent()>onClick()。因此onTouchListener的onTouch()方法会先触发;如果onTouch()返回false才会接着触发onTouchEvent(),同样的,内置诸如onClick()事件的实现等等都基于onTouchEvent();如果onTouch()返回true,这些事件将不会被触发。

android中如何处理耗时操作, 有哪几种方法?为什么子线程不能更新UI?

Android处理耗时的操作基本思路为将耗时的操作放到非UI线程执行。常用的是AsyncTask,Handler和Thread,Loaders.  主要问题是java的线程安全,如果不在主线程更新ui,多个子线程同时给TextView设值,TextView的显示就会出现问题,不知道最终显示哪一个线程的值

软引用和弱引用的区别?

软引用所指向的对象要进行回收,需要满足两个条件:

  • 没有任何强引用 指向 软引用指向的对象(内存中的Person对象)
  • JVM需要内存时,即在抛出OOM之前即SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。

弱引用所指向的对象要进行回收,只需要满足条件:

  • 没有任何强引用 指向 弱引用指向的对象(内存中的Person对象)即WeakReference不改变原有的强引用对象的垃圾回收机制。一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。

SharedPreference的apply和commit的区别?

  1. apply没有返回值,commit会返回一个Boolean值,表明是否修改成功

  2. apply是将修改的数据提交到了内存,而后异步真正提交到硬件磁盘;而commit是同步提交到硬件磁盘,因此在多个并发的提交commit的时候,它们会等待正在处理的commit保存到磁盘后再操作,从而降低了效率;而apply只是原子的提交到内容,后面有调用apply的函数,将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率

  3. apply方法不会提示任何失败的提示,由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要commit的。

ScrollView下嵌套一个ListView通常会出现什么问题?如何解决?

当ScrollView嵌套ListView时,ListView的高度设置为wrap_content时会产生,一般情况下ListView只显示的第一个Item。

当ListView自身接收到的滑动事件时,使ScrollView取消拦截。ListView区域内的滑动事件由自己处理,ListView区域外滑动事件由外层ScrollView处理。可以系统自带的API来实现:requestDisallowInterceptTouchEvent这一方法。

启动一个SingleTop模式的Activity,然后再次启动一次它,它的生命周期如何变化呢?

onCreate -> onStart -> onResume -> onPause -> onNewIntent -> onResume。

FragmentPagerAdapter和FragmentStatePagerAdapter区别?

在于fragment 存储、恢复、销毁 的方式不同,当Viewpager中fragment数量多的时候用FragmentStatePagerAdapter,反之则用FragmentPagerAdapter。

如何开启一个新的进程?Application在多进程下会多次调用onCreate() 吗?

当采用多进程的时候,比如下面的Service 配置:

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="false"
    android:process=":remote" />

android:process 属性中 :的作用就是把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称。

这样配置会调用 onCreate() 两次。

什么是OOM?检测OOM的机制是什么?如何避免?

内存泄漏。

WeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收, 手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。

如何避免内存泄露:

  1. 使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
  2. 对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner
  3. 对于资源对象 使用finally 强制关闭
  4. 内存压力过大就要统一的管理内存

请写出广播的两种注册形式。他们区别在哪?

第一种:使用代码进行订阅
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");  
IncomingSMSReceiver receiver = new IncomingSMSReceiver();  
registerReceiver(receiver, filter);  

第二种:在AndroidManifest.xml文件中的节点里进行订阅:

<receiver android:name=".IncomingSMSReceiver">  
    <intent-filter>  
        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
    </intent-filter>  
</receiver>  

在AndroidManifest中进行注册后,不管改应用程序是否处于活动状态,都会进行监听。

在代码中进行注册后,当应用程序关闭后,就不再进行监听。

如何设计开发一个图片轮播组件?简述要点或写代码。

① 整个组件采用ViewPager

② 适配器继承自PagerAdapter

③ 重写getCount(),isViewFromObject(View arg0, Object arg1),destroyItem(ViewGroup container, int position, Object object),instantiateItem(ViewGroup container, int position)四个方法。

④ getCount代表返回的条目,要实现无限轮播,这里就要给出一个很大的值,我们可以采Integer.MAX_VALUE。其他的和普通ViewPager开发一样,在isViewFromObject返回arg0 == arg1,在

destroyItem中摧毁滑出的View,container.removeView((View) object),在instantiateItem中添加对应的item,记得添加item,container.addView(child)。里面的postion都要做取余处理,避免数组越界。

⑤ 在一开始展示的时候,把item定位到较中间的位置,这里我采用vp.setCurrentItem(10000 * ids.length)。当然这样只是一个滑不到边界的轮播,并不是真正的首尾相连的轮播。

⑥ 最后,可以使用一个handler实现自动轮播,重写onTouchEvent来对自动轮播控制。还可以将这些全部封装起来,当一个自定义view使用。

Kotlin中协程和线程的区别?

线程和协程的目的本质上存在差异:

  • 线程的目的是提高CPU资源使用率, 使多个任务得以并行的运行, 所以线程是为了服务于机器的.
  • 协程的目的是为了让多个任务之间更好的协作, 主要体现在代码逻辑上, 所以协程是为了服务于人的, 写代码的人. (也有可能结果会能提升资源的利用率, 但并不是原始目的)

在调度上, 协程跟线程也不同:

  • 线程的调度是系统完成的, 一般是抢占式的, 根据优先级来分配, 是空分复用.
  • 协程的调度是开发者根据程序逻辑指定好的, 在不同的时期把资源合理的分配给不同的任务, 是时分复用的.

作用上的不同:

  • 协程确保了代码逻辑是顺序的, 不管同步操作要是异步操作, 前一个完成, 后一个才会开始.
  • 线程可以被调度到CPU上执行, 这样代码才能真正运行起来.

Activity A 跳转Activity B,Activity B再按back键回退,两个过程各自的生命周期?

  • ActivityA跳转ActivityB的过程中,各自生命周期的执行顺序。例如:A.onCreate A.onStart A.onPause A.onStop B.onCreate B.onStart B.onPause B.onStop B.onDestroy?

    ActivityA和ActivityB生命周期执行顺序如下: A.onPause -> B.onCreate -> B.onStart-> B.onResume-> A.onStop

  • ActivityB 按back键呢回退
    按下back键后: B.onPause->A.onRestart->A.onStart->A.onResume->B.onStop->B.onDestory

聊聊RecyclerView的缓存机制?

RecyclerView是四级缓存。

一级缓存    mAttachedScrap和mChangedScrap    这是优先级最高的缓存,RecyclerView在获取ViewHolder时,优先会到这两个缓存来找。其中mAttachedScrap存储的是当前还在屏幕中的ViewHolder,mChangedScrap存储的是数据被更新的ViewHolder,比如说调用了Adapter的notifyItemChanged方法。可能有人对这两个缓存还是有点疑惑,不要急,待会会详细的解释。

二级缓存    mCachedViews    默认大小为2,通常用来存储预取的ViewHolder,同时在回收ViewHolder时,也会可能存储一部分的ViewHolder,这部分的ViewHolder通常来说,意义跟一级缓存差不多。

三级缓存    ViewCacheExtension    自定义缓存,通常用不到,在本文中先忽略

四级缓存    RecyclerViewPool    根据ViewType来缓存ViewHolder,每个ViewType的数组大小为5,可以动态的改变。