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中的多线程:线程使用、线程安全、线程通信