写点什么

2020-Android- 面试重难点(万字篇),字节

用户头像
Android架构
关注
发布于: 1 小时前

屏幕旋转或 Activity 在后台被系统杀掉等情况会导致 Activity 的重新创建,之前运行的 AsyncTask 会持有一个之前 Activity 的引用,这个引用已经无效,这时调用 onPostExecute()再去更新界面将不再生效。


4、并行还是串行


在 Android1.6 之前的版本,AsyncTask 是串行的,在 1.6 至 2.3 的版本,改成了并行的。在 2.3 之后的版本又做了 修改,可以支持并行和串行,当想要串行执行时,直接执行 execute()方法,如果需要执行 executeOnExecutor(Executor)。

3.Android 机制

(1)Linux Sandbox 沙箱机制:Android 将数据分为 system 和 data 两个区。其中 system 是只读的,dada 用来存放应用自己的数据,这保证了系统数据不会被随意改写。


应用之间的数据相互独立,每个应用都会有一个 user id 和 group id,只有相同的 user id 并且来自同一个作者,才能访问它们的数据。作者通过对 apk 签名来标识自己,签名和 uid 构成了双重的保证。


(2)用户权限机制:文件权限,UID,GID


(3)用户权限机制:android permission 机制限制应用访问特定的资源,例如照相机、网络、外部存储等 api


如何让两个 app 运行在同一个进程里?


  1. 两个 app 要用相同的 private key 来签名

  2. 两个 app 的 Manifest 文件中要添加一样的属性 android:sharedUserId(设置成相同的 UID)

4.Binder 机制

跨进程间通信(IPC):四大组件之间通过 Intent 互相跳转,Android 实现 IPC 的方式是 binder 机制。


[android 中的跨进程通信的实现(一)——远程调用过程和 aidl]


[Android 中的 Binder 机制的简要理解]


[Android 中的 Binder 机制的简要理解二]


In the Android platform, the binder is used for nearly everything that happens accross in the core plateform.


最底层的是 Android 的 ashmen(Anonymous shared memoryy)机制,它负责辅助实现内存的分配,以及跨进程间通信所需要的内存共享。AIDL(Android Interface Definition Language)对 BInder 的使用进行了封装,可以让开发者方便的进行方法的远程调用,后面会详细介绍。Intent 是最高一层的抽象,方便开发者进行常用的跨进程调用。


从英文字面上意思看,Binder 具有粘结剂的意思,那么它把什么东西粘结在一起呢?在 Android 系统的 Binder 机制中,由一系统组件组成,分别是 Client、Server、Service Manager 和 Binder 驱动程序,其中 Client、Server 和 Service Manager 运行在用户区间,Binder 驱动程序运行内核空间。Binder 就是一种把这四个组件粘合在一起的粘结剂了,其中,核心组件便是 Binder 驱动了,ServiceManager 提供了辅助管理的功能,Client 和 Server 正是在 Binder 驱动和 ServiceManager 提供的基础设施上,进行 Client-Server 之间的通信。



  1. Client、Server 和 ServiceManager 实现在用户空间中,Binder 驱动程序实现在内核空间中

  2. Binder 驱动程序和 ServiceManager 在 Android 平台已经实现,开发者只需要在用户空间实现自己的 Client 和 Server

  3. Binder 驱动程序提供设备文件/dev/binder 与用户空间交互,Client、Server 和 ServiceManager 通过 open 和 ioctl 文件操作函数与 Binder 驱动程序进行通信

  4. Client 和 Server 之间的进程间通信通过 Binder 驱动程序间接实现

  5. Service Manager 是一个守护进程,用来管理 Server,并向 Client 提供查询 Server 接口的能力


服务器端:一个 Binder 服务器就是一个 Binder 类的对象。当创建一个 Binder 对象后,内部就会开启一个线程,这个线程用语接收 binder 驱动发送的信息,收到消息后,会执行相关的服务代码。


Binder 驱动:当服务端成功创建一个 Binder 对象后,Binder 驱动也会相应的创建一个 mRemote 对象,该对象的类型也是也是 Binder 类。客户就可以借助这个 mRemote 对象来访问远程服务。


客户端:客户想要访问 Binder 的远程服务,就必须获取远程服务的 Binder 对象在 binder 驱动层对应的 mRemote 引用。当获取到 mRemote 对象的引用后,就可以调用相应 Binder 对象的服务了。


在这里,我们可以看到,客户端是通过 Binder 驱动来调用服务端的相关服务。首先,在服务端创建一个 Binder 对象,然后相应的在 Binder 驱动中创建一个 Binder 对象,接着客户端通过获取 Binder 对象的引用来调用服务端的服务。在 Binder 机制中正是借着 Binder 驱动将不同进程间的组件 bind(粘连)在一起,实现通信。


mmap 将一个文件或者其他对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap 执行相反的操作,删除特定地址区域的对象映射。


当使用 mmap 映射文件到进程后,就可以直接操作这段虚拟内存进行文件的读写等操作,不必再调用 read,write 等系统调用。但需注意,直接对该段内存写时不会写入超过当前文件大小的内容。


采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保存共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。


aidl 主要就是帮助我们完成了包装数据和解包的过程,并调用了 transact 过程,而用来传递的数据包我们就称为 parcel


AIDL: xxx.aidl->xxx.java,注册 service


  1. 用 aidl 定义需要被调用方法接口

  2. 实现这些方法

  3. 调用这些方法

5.NDK

Dalvik 虚拟机在调用一个成员函数的时候,如果发现该成员函数是一个 JNI 方法,那么就会直接跳到它的地址去执行。也就是说,JNI 方法是直接在本地操作系统执行的,而不是 Dalvik 虚拟机解释器执行。由此也可以看出,JNI 方法是 Android 应用程序与本地操作系统直接进行通信的一个手段。


JNI 原理:


[Dalvik 虚拟机 JNI 方法的注册过程分析]


例子:当 libnanosleep.so 文件被加载的时候,函数 JNI_OnLoad 就会被调用。在函数 JNI_OnLoad 中,参数 vm 描述的是当前线程中的 Dalvik 虚拟机,通过调用它的成员函数 GetEnv 就可以获得一个 JNIEnv 对象。有了这个 JNIEnv 对象之后,我们就可以调用另外一个函数 jniRegisterNativeMethods 来向当前进程的 Dalvik 虚拟机注册一个 JNI 方法。

6.Android 系统启动过程,App 启动过程

App 启动过程:


[Activity 启动过程详解]


从桌面点击到 activity 启动的过程


1、Launcher 线程捕获 onclick 的点击事件,调用 Launcher.startActivitySafely,进一步调用 Launcher.startActivity,最后调用父类 Activity 的 startActivity。


2、Activity 和 ActivityManagerService 交互,引入 Instrumentation,将启动请求交给 Instrumentation,调用 Instrumentation.execStartActivity。


3、调用 ActivityManagerService 的 startActivity 方法,这里做了进程切换(具体过程请查看源码)。


4、开启 Activity,调用 onCreate 方法

7.Activity,Fragment,Service 生命周期

常见的例子:程序正运行着来电话了,这个程序咋办呢?中止了呗,如果中止的时候新出的一个 Activity 是全屏的 onPause->onStop,恢复的时候 onStart->onResume,如果打断这个应用程序的是一个 Theme 为 Translucent 或者 Dialog 的 Activity 那么只是 onPause,恢复的时候 onResume。


  • onPause:恢复的时候 onResume

  • onCreate:在这里创建界面,做一些数据的初始化工作

  • onStart:到这一步变成用户可见不可交互的

  • onPause:到这一步是可见但不可交互的,系统会停止动画等消耗 CPU 的事情,应该在这里保存你的一些 数据,因为这个时候你的程序的优先级降低,有可能被 系统回收。在这里保存的数据,应该在 onResume 里读出来。注意:这个方法里做的事情时间要短,因为下一个 Activity 不会等到这个方法完成才启动。

  • onStop:变得不可见,被下一个 Activity 覆盖了(onPause 和 onStop 的区别是否可见)

  • onDestroy:这是 Activity 被干掉前最后一个被调用方法了,可能是外面类调用 finish 方法或者是系统为了节省空间将它暂时性的干掉,可以用 isFinishing()来判断它,如果你有一个 ProgressDialog 在线程中转动,请在 onDestroy 里把它 cancel 掉,不然等线程结束的时候,调用 Dialog 的 cancel 会抛出异常的。


onPause,onstop,onDestroy,三种状态下,Activity 都有可能被系统干掉。


启动另一个 Activity 然后 finish,先调用旧 Activity 的 onPause 方法,然后调用新的 Activity 和 onCreate->onStart->onResume 方法,然后调用旧 Activity 的 onStop->onDestroy 方法。


如果没有调用 finish 那么 onDestroy 方法不会被调用,而且在 onStop 之前还会调用 onSavedInstanceState 方法


onRestart 方法执行完了之后还会调用 onStart 方法


fragment:[SupportFragmentManager,childFragment]


service:


[Android Service 的生命周期]


[android-Service 和 Thread 的区别]


Service 和 Intent Service:没啥区别,只是 IntentService 在 onCreate 方法中开启新的 HandlerThread 去执行。


Service 运行的进程和线程:当它运行的时候如果是 LocalService,那么对应的 Service 是运行在主进程的 main 线程上的。如 onCreate,onStart 这些函数都是在系统调用的时候在主进程的 main 线程上运行的。如果是 RemoteSevice,那么对应的 Service 则是运行在独立的 main 线程上。


  1. 服务不是单一的进程,服务没有自己的进程,应用程序可以不同,服务运行在相同的进程中

  2. 服务不是线程,可以在线程中工作

  3. 在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务

  4. 同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程

  5. 如果任务占用 CPU 时间多,资源大的情况下,要使用线程


Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 就会一直执行。


8.View 绘画机制

View 的绘制主要涉及三个方法:onMeasure()、onLayout()、onDraw()


  1. onMeasure 主要用于计算 view 的大小,onLayout 主要用于确定 view 在 ContentView 中的位置,onDraw 主要是绘制 View。

  2. 在执行 onMeasure()、onLayout()方法时都会通过相应的标志位或者对应的坐标点来判断是否需要执行对应的函数,如我们经常调用的 invalidate 方法就只会执行 onDraw 方法,因为此时的视图大小和位置均未发生变化,除非调用 requestLayout 方法完整强制进行 view 的绘制,从而执行上面三个方法。


进度条组件:


[ProgressView][AnnotationView]

9.事件传递机制

[android 事件处理机制总结,ScrollView ViewPager ListView GridView 嵌套小结]


当手指触摸到屏幕时,系统就会调用相应 View 的 onTouchEvent,并传入一系列的 action。


dispatchTouchEvent 的执行顺序为:


  1. 首先触发 ACTIVITY 的 dispatchTouchEvent,然后触发 ACTIVITY 的 onUserInteraction

  2. 然后触发 LAYOUT 的 dispatchTouchEvent,然后触发 LAYOUT 的 onInterceptTouchEvent


这就解释了重写 ViewGroup 时必须调用 super.dispatchTouchEvent();


(1)dispatchTouchEvent:


此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用 super.dispatchTouchEvent。这样就会继续调用 onInterceptTouchEvent,再由 onInterceptTouchEvent 决定事件流向。


(2)onInterceptTouchEvent:


若返回值为 true 事件会传递到自己的 onTouchEvent();若返回值为 false 传递到下一个 View 的 dispatchTouchEvent();


(3)onTouchEvent():


若返回值为 true,事件由自己消耗,后续动作让其处理;若返回值为 false,自己不消耗事件了,向上返回让其他的父 View 的 onTouchEvent 接受处理


三大方法关系的伪代码:如果当前 View 拦截事件,就交给自己的 onTouchEvent 去处理,否则就丢给子 View 继续走相同的流程。


public boolean dispatchTouchEvent(MotionEvent ev){boolean consume = false;if(onInterceptTouchEvent(ev)){consume = onTouchEvent(ev);}else{consume = child.dispatchTouchEvent(ev);}return consume;}


onTouchEvent 的传递:


当有多个层级的 View 时,在父层级允许的情况下,这个 action 会一直传递直到遇到最深层的 View。所以 touch 事件最先调用的是最底层 View 的 onTouchEvent,如果 View 的 onTouchEvent 接收到某个 touch action 并做了相应处理,最后有两种返回方式 return true 和 return false;return true 会告诉系统当前的 View 需要处理这次的 touch 事件,以后的系统发出的 ACTION_MOVE,ACTION_UP 还是需要继续监听并接收的,并且这次的 action 已经被处理掉了,父层的 View 是不可能触发 onTouchEvent 的了。所以每一个 action 最多只能有一个 onTouchEvent 接口返回 true。如果返回 false,便会通知系统,当前 View 不关心这一次的 touch 事件,此时这个 action 会传向父级,调用父级 View 的 onTouchEvent。但是这一次的 touch 事件之后发出任何 action,该 View 都不在接受,onTouchEvent 在这一次的 touch 事件中再也不会触发,也就是说一旦 View 返回 false,那么之后的 ACTION_MOVE,ACTION_UP 等 ACTION 就不会在传入这个 View,但是下一次 touch 事件的 action 还是会传进来的。


父层的 onInterceptTouchEvent


前面说了底层的 View 能够接收到这次的事件有一个前提条件:在父层允许的情况下。假设不改变父层级的 dispatch 方法,在系统调用底层 onTouchEvent 之前会调用父 View 的 onInterceptTouchEvent 方法判断,父层 View 是否要截获本次 touch 事件之后的 action。如果 onInterceptTouchEvent 返回了 true,那么本次 touch 事件之后的所有 action 都不会向深层的 View 传递,统统都会传给父层 View 的 onTouchEvent,就是说父层已经截获了这次 touch 事件,之后的 action 也不必询问 onInterceptTouchEvent,在这次的 touch 事件之后发出的 action 时 onInterceptTouchEvent 不会再被调用,直到下一次 touch 事件的来临。如果 onInterceptTouchEvent 返回 false,那么本次 action 将发送给更深层的 View,并且之后的每一次 action 都会询问父层的 onInterceptTouchEvent 需不需要截获本次 touch 事件。只有 ViewGroup 才有 onInterceptTouchEvent 方法,因为一个普通的 View 肯定是位于最深层的 View,只有 ViewGroup 才有 onInterceptTouchEvent 方法,因为一个普通的 View 肯定是位于最深层的 View,touch 能够传到这里已经是最后一站了,肯定会调用 View 的 onTouchEvent()。


底层 View 的 getParent().requestDisallowInterceptTouchEvent(true)


对于底层的 View 来说,有一种方法可以阻止父层的 View 获取 touch 事件,就是调用 getParent().requestDisallowInterceptTouchEvent(true)方法。一旦底层 View 收到 touch 的 action 后调用这个方法那么父层 View 就不会再调用 onInterceptTouchEvent 了,也无法截获以后的 action(如果父层 ViewGroup 和最底层 View 需要截获不同焦点,或不同手势的 touch,不能使用这个写死)。


曾经开发过程中遇到的两个示例:左边是处理 ViewPager 和 ListView 的冲突,纪录水平和垂直方向的偏移量,如果水平方向的偏移更多的话就让 ViewPager 处理 pager 滑动


右边处理的 ViewPager 和 ImageBanner 的滑动冲突,同样是纪录偏移量,如果发生在 ImageBanner 上的水平偏移量大于垂直偏移量的话就让 banner 滚动


想想为什么右边是重写 dispatchTouchEvent 方法而不是 onInterceptTouchEvent 方法?


FixedViewPager@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev){switch(ev.getAction() & MotionEvent.ACTION_MASK){case MotionEvent.ACTION_DOWN:mX = ev.getX();mY = ev.getY();break;case MotionEvent.ACTION_MOVE:float x = ev.getX();float y = ev.getY();float dX = x - mX;float dY = y - mY;float tmp = Math.abs(dX) / Math.abs(dY);mX = x;mY = y;if(tmp > 1){return true;}else{return super.omInterceptTouchEvent(ev);}}}


FixedImageLoadBanner@overridepublic boolean dispatchTouchEvent(MotionEvent ev){if(mX != 0 || mY != 0){float dY = ev.getRawY() - mY;float dX = ev.getRawX() - mX;if(Math.abs(dY) > Math.abs(dX)){requestDisallowInterceptTouchEvent(false);}else{requestDisallowInterceptTouchEvent(true);}}mX = ev.getRawX();mY = ev.getRawY();return super.dispatchTouchEvent(ev);}

10.ART 和 Dalvik 区别

art 上应用启动快,运行快,但是耗费更多存储空间,安装时间长,总的来说 ART 的功效就是”空间换时间”。


ART: Ahead of Time Dalvik: Just in Time


什么是 Dalvik:Dalvik 是 Google 公司自己设计用于 Android 平台的 Java 虚拟机。Dalvik 虚拟机是 Google 等厂商合作开发的 Android 移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即 Dalvik Executable)格式的 Java 应用程序的运行,.dex 格式是专为 Dalvik 应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik 应用作为独立的 Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。


什么是 ART:Android 操作系统已经成熟,Google 的 Android 团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的 Dalvik 运行时。Google 开发者已经花了两年时间开发更快执行效率更高更省电的替代 ART 运行时。ART 代表 Android Runtime,其处理应用程序执行的方式完全不同于 Dalvik,Dalvik 是依靠一个 Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART 则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫 Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。


ART 优点:


  1. 系统性能的显著提升

  2. 应用启动更快、运行更快、体验更流畅、触感反馈更及时。

  3. 更长的电池续航能力

  4. 支持更低的硬件


ART 缺点:


  1. 更大的存储空间占用,可能会增加 10%-20%

  2. 更长的应用安装时间


Scroller 执行流程里面的三个核心方法


  1. mScroller.startScroll()

  2. mScroller.computeScrollOffset()

  3. view.computeScroll()


1、在 mScroller.startScroll()中为滑动做了一些初始化准备,比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等。


2、mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。因为在 mScroller.startScroll()中设置了动画时间,那么在 computeScrollOffset()方法中依据已经消逝的时间就很容易得到当前时刻应该所处的位置并将其保存在变量 mCurrX 和 mCurrY 中。除此之外该方法还可判断动画是否已经结束。


12.Activity Manager Service, ActivityThread

13.Android 几种进程

  1. 前台进程: 即与用户正在交互的 Activity 或者 Activity 用到的 Service 等,如果系统内存不足时前台进程是最后被杀死的

  2. 可见线程:可以是处于暂停状态(onPause)的 Activity 或者绑定在其上的 Service,即被用户可见,但由于失去了焦点而不能与用户交互

  3. 服务进程:其中运行着使用 startService 方法启动的 Service,虽然不被用户可见,但是却是用户关系的,例如用户正在非音乐界面听的音乐或者正在非下载页面自己下载的文件等,当系统要用空间运行前两者进程时才会被终止

  4. 后台进程:其中运行着执行 onStop 方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的 QQ,这样的进程系统一旦没有内存就首先被杀死。

  5. 空进程:不包含任何应用程序的程序组件的进程,这样的进程系统是一般不会让他存在的。


如何避免后台进程被杀死?


  1. 调用 startForegound,让你的 Service 所在的进程成为前台进程

  2. Service 的 onStartCommand 返回 START_STICKY 或 START_REDELIVER_INTENT

  3. Service 的 onDestroy 里面重新启动自己

14.Activity 启动模式

standard:Activity 的默认加载方式,该方法会通过跳转到一个新的 Activity,同时将该实例压入到栈中(不管该 Activity 是否已经存在在 Task 栈中,都是采用 new 操作,生命周期从 onCreate()开始)。例如:栈中顺序是 A B C D,此时 D 通过 Intent 跳转到 A,那么栈中结构就变成 A B C D A,点击返回按钮的显示顺序是 D C B A,依次摧毁。


singleTop:singleTop 模式下,当前 Activity D 位于栈顶的时候,如果通过 Intent 跳转到它本身的 Activity(D),那么不会重新创建一个新的 D 实例(走 onNewIntent()),所以栈中的结构依次为 A B C D,如果跳转到 B,那么由于 B 不处于栈顶,所以会新建一个 B 实例并压入到栈中,结构就变成了 A B C D B。应用实例:三条推送,点进去都是一个 Activity,这肯定用 singletop


singleTask:singleTask 模式下,Task 栈中只能有一个对应的 Activity 实例。例如:Task 栈 1 中结构为:A B C D。此时 D 通过 Intent 跳转到 B(走 onNewIntent()),则栈的结构变成了:A,B。其中的 C 和 D 被栈弹出销毁了,也就是说位于 B 之上的实例都被销毁了。通常应用于首页,首


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


页肯定在栈底部,也只能在栈底部。


singleInstance:singleInstance 模式下,会将打开的 Activity 压入一个新的任务栈中。例如:Task 栈 1 中结构为:A B C,C 通过 Intent 跳转到了 D(D 的模式为 singleInstance),那么则会新建一个 Task,栈 1 中结构依旧为 A B C,栈 2 中结构为 D。此时屏幕显示 D,之后 D 通过 Intent 跳转到 D,栈 2 不会压入新的 D,所以两个栈中的情况没发生改变。如果 D 跳转到了 C,那么就会根据 C 对应的 launchMode 在栈 1 中进行对应的操作,C 如果为 standard,那么 D 跳转到 C,栈 1 的结构为 A B C C ,此时点击返回按钮,还是在 C,栈 1 的结构变为 A B C,而不会回到 D。


launchMode 为 singleTask 的时候,通过 Intent 启动到一个 Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的 onCreate 方法,而不是调用 onNewIntent 方法。


onSavedInstanceState 的调用遵循一个重要原则,即当系统”未经你许可”时销毁了你的 Activity,则 onSavedInstanceState 会被系统调用,这时系统的责任,因为它必须要提供一个机会让你保存你的数据,至于 onRestoreInstanceState 方法,需要注意的是,onSavedInstanceState 方法和 onRestoreInstanceState 方法”不一定”是成对调用的。


onRestoreInstanceState 被调用的前提是,Activity A 确实被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示 Activity A 的时候,用户按下 HOME 键回到主界面,然后用户紧接着又返回到 Activity A,这种情况下 Activity A 一般不会因为内存的原因被销毁,故 Activity 的 onRestoreInstanceState 方法不会被执行。


另外,onRestoreInstanceStated 的 bundle 参数也会传递到 onCreate 方法中,你也可以选择在 onCreate 方法中做数据还原。


onSavedInstanceState(Bundle bundle)通常和 onRestoreInstanceState(Bundle bundle)不会成对出现,onRestoreInstanceState 这玩意不太好触发,给大家提个好办法,横竖屏切换的时候 100%会触发。然后保存在 onRestoreInstanceState bundle 里面的数据,就是 onCreate 的那个参数 bundle 啦,要怎么恢复就看开发者了。

15.TMImageView 图片库的设计

Feature 机制

16.ListView 优化

  1. 首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView 都新建。ListView 的核心原理就是重用 View。ListView 有一个回收器,Item 滑出界面的时候 view 就会回收这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。

  2. 利用好 ViewType,例如你的 ListView 中有几个类型的 Item,需要给每个类型创建不同的 View,这样有利于 ListView 的回收,当然类型不能太多

  3. 尽量让 ItemView 的 Layout 层次结构简单,这时所有 Layout 都必须遵守的

  4. 善用自定义 View,自定义 View 可以有效的减小 Layout 的层级,而且对绘制过程可以很好的控制

  5. 尽量保证 Adapter 的 hasStableIds()返回 true,这样在 notifyDataSetChanged()的时候,如果 id 不变,listView 将不会重新绘制这个 View,达到优化的目的。

  6. 每个 item 不能太高,特别是不要超出屏幕的高度,可以参考 Facebook 的优化方法,把特别复杂的 Item 分解为若干个小的 Item.

  7. 为了保证 ListView 滑动的流畅性,getView()中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载。

  8. 使用 RecyclerView 代替。ListView 每次更新数据都要 notifyDataSetChanged(),有些太暴力了。RecyclerView 在性能和可定制性上都有很大的改善,推荐使用。

  9. 有时候,需要从根本上考虑,是否真的要使用 listView 来实现你的需求,或者是否有其他选择?

17.webView

如何使用 webview 在 js 中调用 java 方法?


webView.addJavaScriptInterface(new Object(){xxx}, "xxx");


答案:可以使用 WebView 控件执行 JavaScript 脚本,并且可以在 JavaScript 中执行 Java 代码。要想让 WebView 控件执行 JavaScript,需要调用 WebSettings.setJavaScriptEnabled 方法,代码如下:


WebView webView = (WebView)findViewById(R.id.webview)WebSettings webSettings = webView.getSettings()//设置 WebView 支持 JavaScriptwebSettings.setJavaScriptEnabled(true)webView.setWebChromeClient(new WebChromeClient())


JavaScript 调用 Java 方法需要使用 WebView.addJavascriptInterface 方法设置 JavaScript 调用的 Java 方法,代码如下:


webView.addJavascriptInterface(new Object(){

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
2020-Android-面试重难点(万字篇),字节