Android 大疆面经
一面
- 自我介绍
- 问项目聊了10分钟
- View的绘制流程
- MVC,MVP,MVVM的区别
- view和viewmodel的通信,除了databing还有其他的方式
- 面向对象和面向过程的区别
- 工厂模式和策略模式,哪些框架使用了策略模式
- 广播的本地广播和全局广播区别
- 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄露的问题。
- 其他的程序无法将广播发送到我们的程序内部,因此不需要担心会有安全漏洞的隐患。
- 发送本地广播比起发送系统全局广播将会更加高效。
- 事件分发机制
- 滑动冲突拦截
- 外部拦截法
- 直接在父容器中拦截吧 的滑动事件,让其不能进入子元素中
- 就是在我们接受
<font style="color:#F5222D;">ACTION_MOVE</font>
事件的时候直接通过<font style="color:#F5222D;"> onInterceptTouchEvent</font>
方法返回<font style="color:#FA541C;">ture</font>
直接拦截掉这个事件
- 伪代码:
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev?.run {
if (action == MotionEvent.ACTION_MOVE && 父容器需要点击事件){
return true
}
}
return super.onInterceptTouchEvent(ev)
4. 不能拦截 `<font style="color:#F5222D;">ACTION_DOWN</font>` 和 `<font style="color:#F5222D;">ACTION_UP</font>`,如果拦截了 `<font style="color:#F5222D;">ACTION_DOWN</font>` 事件,那后续的 `<font style="color:#F5222D;">ACTION_MOVE</font>`<font style="color:#F5222D;">、</font>`<font style="color:#F5222D;">ACTION_UP </font>`等其它事件均不会在调用 `<font style="color:#F5222D;">onInterceptTouchEvent()</font>` 方法,会直接交给当前容器处理。而如果我们拦截掉 `<font style="color:#F5222D;">ACTION_UP</font> `的话,肯定会导致子元素的点击事件无法被处理,一个点击事件从 `<font style="color:#F5222D;">ACTION_DOWN</font>` 开始,从 `<font style="color:#F5222D;">ACTION_UP</font>` 结束,二者缺一不可。
2. **内部拦截法**
1. 这个很明显就是在子view里面做事件判断和拦截,这里我们直接重写子元素的`<font style="color:#F5222D;">dispatchTouchEvent()</font>`方法,在这个方法里面判断是不是需要父View去拦截。
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
ev?.run {
when(action){
MotionEvent.ACTION_DOWN -> parent.requestDisallowInterceptTouchEvent(true)
MotionEvent.ACTION_MOVE ->{
if(满足需要让外部容器拦截事件){
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
}
return super.dispatchTouchEvent(ev)
}
2. <font style="color:rgb(51, 51, 51);">我们给父容器的 </font>`<font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">requestDisallowInterceptTouchEvent()</font><font style="color:rgb(51, 51, 51);"> </font>`<font style="color:rgb(51, 51, 51);">传递的参数代表是否不允许其拦截事件,当参数为 </font>`<font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">true</font><font style="color:rgb(51, 51, 51);"> </font>`<font style="color:rgb(51, 51, 51);">的时候代表不允许拦截,为 </font>`<font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">false</font><font style="color:rgb(51, 51, 51);"> </font>`<font style="color:rgb(51, 51, 51);">的时候代表拦截。</font>
3. <font style="color:rgb(51, 51, 51);">我们得在父view里面重写他的</font>`<font style="color:#F5222D;">onInterceptTouchEvent</font>`<font style="color:#F5222D;"> </font>,不允许他拦截`<font style="color:#F5222D;">ACTION_DOWN</font>`<font style="color:#F5222D;"> ,</font>否认任何事件都无法再传递到子元素中
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
ev?.run {
if (action == MotionEvent.ACTION_DOWN){
return false
}
}
return super.onInterceptTouchEvent(ev)
}
- Android动画有咩有用过,属性动画
- Android进程通信
- **使用Bundle **,由于bundle实现了Parcelable接口,可以在不同进程间传输。
- **使用文件共享,**两个进程通过读一个文件来交换数据,Android是基于Linux,可以并发读写,并发读的话可能导致读出的数据不是最新的,并发写的话会把问题写错。文件共享适用于对数据同步要求不高的进程通信。多进程下不建议使用sharedpreferences,会有很大概率丢失数据,sharedpreferences是在一个个进程会有自己的缓存。
- **使用Messenger,是一种轻量级的IPC方案,底层是AIDL **,服务端需要创建一个Service,创建一个handler,并通过他来创建一个Messenger对象,然后在Service的OnBind的方法返回,客户端需要绑定Service,绑定成功后使用服务端返回的IBinder创建一个Messenger,这样客户端就可以和服务端发送消息了,消息类型是Messenger,如果希望服务端也能发送消息给客户端,在客户端创建一个handler用来处理服务端发送的消息,使用这个handler创建一个Messenger,将Messenger通过replyTo返回给服务器,服务器拿到客户端的Messenger就可以进行通信了。
- **使用AIDL,**服务端创建一个Service用来监听客户端的请求连接,创建一个AIDL文件,将暴露给客户端的接口在这个文件中声明,最后在Service中,声明实现AIDL的接口,客户端绑定服务端的Service,将服务端成功返回的Binder对象转为AIDL接口所属于的类型,接着可以调用AIDL的方法
- **ContentProvider,**可以理解为受约束的AIDL,主要提供数据源的CRUD
- **Socket,**服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了
- Broadcast
<font style="color:#F5222D;">sendBroadcast()</font>
方法里面<font style="color:#F5222D;">Intent</font>
可以携带<font style="color:#F5222D;">Bundle</font>
数据进行跨进程通信
- Android线程通信
- Handler
- runOnUIThread
- AsyncTask
- View.post
- ThreadLocal
- Java进程通信
- **管道/匿名管道(Pipes) **:用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。
- **有名管道(Names Pipes) **: 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
- 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
- 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。
- 信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
- 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
- **套接字(Sockets) **: 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
- 推荐阅读:Java进程间通信学习** **进程间通信IPC (InterProcess Communication)
- Java线程通信
- 线程通信:要想实现两个线程之间的协同,如线程的先后顺序,获取某个线程的执行结果
- 锁与同步:这种方式就是定义一些锁,使用syschronized关键字对象或代码块lock加锁,来达成线程通信
- 等待和通知机制:
- Object.wait和Object.notify,Object.notifyAll wait()方法和notify()方法必须写在synchronized代码块里面
- join()方法让当前线程陷入“等待”状态,等join的这个线程执行完成后,再继续执行当前线程。
- sleep方法,让当前线程休眠不释放锁
- CountdownLatch 主要是用来实现线程 D 在A、B、C都同步执行完毕后执行,它的基本用法是:创建一个计数器,并设置一个初始值, CountdownLatch countDownLatch = new CountDownLatch(3);调用countDownLatch.await()进入等待状态,直到计数值变为0;在其他线程调用countDownLatch.countDown(),该方法会将计数值减一;当计数器的值变为 0 时,countDownLatch.await()等待线程中的方法会继续执行下面的代码。CountDownLatch适用于一个线程需要等待多个线程的情况。
- CyclicBarrier,CyclicBarrier 的作用就是等待多个线程同时执行。其基本用法为:首先创建一个公共对象CyclicBarrier,并设置同时等待的线程数,CyclicBarrier cyclicBarrier = new CyclicBarrier(3);这些线程同时开始准备,准备好后,需要等待别人准备好,所以调用cyclicBarrier.await()方法等待别人;当指定的需要同时等待的线程都调用了该cyclicBarrier.await()方法时,意味着这些线程准备好了,那么这些线程就会开始同时继续执行。
- 线程执行完返回结果:FutureTask、Callable
- 推荐阅读:Java 中如何实现线程间通信 java中的多线程:线程使用、线程安全、线程通信