1.事件分发机制:
参考文章:
当一个点击操作发生时,事件最先传递给当前的Activity,由Activity的dispatchTouchEvent来进行事件的分发,具体工作是由Activity内部的Window(PhoneWindow)来完成的。Window会将事件传递给DecorView,Decor View一般就是当前界面的底层容器(即setContentView所设置的View的父容器),通过Activity.getWindow.getDecorView()可以获得:
Activity.dispatchTouchEvent(MotionEvent event) -> PhoneWindow.superDispatchTouchEvent(MotionEvent event) -> DecorView.superDispatchTouchEvent(MotionEvent event) -> FrameLayout.dispatchTouchEvent(MotionEvent event) -> ViewGroup.dispatchTouchEvent(MotionEvent event) ->
再逐级分发到各个ViewGroup/View当中去
一段伪代码表示事件分发,拦截,消费三者的关系 :
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false;//事件是否被消费 if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件 consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法 }else{ consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法 } return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法 }
-------------------------
比如点击了一个butto按钮,他的事件分发机制是这样的:
事件由activity开始下发,调用activity的dispatchTouchEvent,然后执行PhoneWindow的superDispatchTouchEvent,再执行DecorView的superDispatchTouchEvent,再执行ViewGroup(就是我们setContentView布局)的dispatchTouchEvent方法,事件继续分发到View(即button)上,执行button的dispatchTouchEvent方法,在这个方法中:
如果设置了OnTouchListener并且onTouch方法返回了true,那么onTouchEvent不会被调用。
当没有设置OnTouchListener或者设置了OnTouchListener但是onTouch方法返回false则会调用View自己的onTouchEvent方法。
继续执行onTouchEvent方法,在该方法的action_up的时候会调用performClick方法,里面会执行onclick方法。
如果是一个viewgroup的view被点击,那么从viewgroup的dispatchTouchEvent事件开始,先判断是否要进行拦截
(子View可以通过requestDisallowInterceptTouchEvent方法干预父View的事件分发过程(ACTION_DOWN事件除外),而这就是我们处理滑动冲突常用的关键方法),调用onInterceptTouchEvent方法判断是否需要拦截,如果返回true,事件不会继续下发;如果不需要拦截,继续下发到子view中,调用子view的dispatchTouchEvent,如何判断是哪个子view触发事件:
子View:1.可见2.没有播放动画3.点击事件坐标落在子View内部
最终调用子View的dispatchTouchEvent方法处理事件
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.fragment是什么,以及生命周期,各个生命周期是干什么的
fragment翻译过来是碎片,是activity的一部分。拥有自己的生命周期和UI,fragment作为UI界面的载体,使用上十分灵活,比activity更加节省内存,可以说是一个轻量级的界面载体。一个actiity中可以有多个fragment,可以添加,隐藏,移除,替换fragment。
如果一个界面分左右两栏,左边显示文章列表,右边显示文章内容,可以使用一个activity+两个fragment来显示,一个fragment显示列表,另一个显示具体内容。
onattacth():当fragment附加到activity上的时候回调
onCreate() :创建fragment的时候回调
onCreateView():创建和返回fragment所对应的View的时候回调
onActivityCreated():当fragment所在的Activity启动的时候调用
onstart():启动fragment的时候回调
onResume():回复fragment的时候回调
onPause():暂停fragment的时候回调
onStop(): 停止fragment的时候回调
onDestoryView():和onCreateView相对应,销毁fragment所包含的view时候回调
onDestory():销毁fragment的时候回调
onDetach():与attach相对应,fragment从activity删除时会回调该方法,并且该方法只会调用一次
-------------------------------
Fragment的基本使用
1.把fragment当做控件使用
声明一个MyFragment继承自ifragment,oncreateView中加载好布局,再在activity的布局中直接引入
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.xn.myproject.MainActivity"> <fragment android:id="@+id/fragmenta"
android:name="com.xn.myproject.fragment.MyFragment" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
2.FragmentManager动态加载fragment
MyFragment myFragment = new MyFragment (); getFragmentManager().beginTransaction() .replace(R.id.main_container, myFragment ).commit(); getFragmentManager().beginTransaction().show(myFragment );
FragmentTransaction 对象有add,replace,hide,show,remove等操作
----------------------------
Fragment的典型应用场景
ViewPager+Fragment结构
相信绝大多数的人都用ViewPager+Fragment的形式实现过界面,同时目前市面上主流的APP也都是采用这种结构来进行UI架构的
FragmentPagerAdapter:对于不再需要的fragment,仅仅只会调用到onDestroyView方法,也就是仅仅销毁视图而并没有完全销毁Fragment。
FragmentStatePagerAdapter:会销毁不再需要的fragment,一直调用到onDetach方法失去与Activity的绑定。销毁时,会调用onSaveInstanceState(Bundle outState)方法通过bundle将信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,我们可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。
----------------------------
3.java object类重写哪些方法
equals
判断两个对象是否相等,如果是基本类型,用==;如果是string,integer,data类型的,因为重写了equals,所以,equals是用来比较两个值是否相等,必须重写hashcode,如果两个值相等,那么他们返回的哈希值也应该相等;反之,equals比较的不只是数值相同,还比较他们的地址是否相同。
hashCode
是一个本地方法,返回对象的地址
clones
创建并返回此对象的副本,浅拷贝(地址值不一样,成员变量一样)
finalize
当垃圾回收器确定不存在该对象的引用时,由垃圾回收器调用此方法。
wait
表示持有对象锁的线程a,准备释放对象锁权限,释放cpu资源并进入等待
notify
表示持有对象锁的线程a,准备释放对象锁权限,通知jvm唤醒某个竞争该对象锁的线程x,线程a的synchronized 代码作用域结束后,线程x持有对象锁,其他线程继续等待,直到notify,ntifyall被调用
notifyall
表示持有对象锁的线程a,准备释放对象锁权限,通知jvm唤醒所有竞争该对象锁的线程,线程a的synchronized 代码作用域结束后,jvm通过算法,将对象锁权限指派给某个线程,其他的线程不再等待。
------------------------举个比喻,好多人去药店取药
线程:N个买家
对象锁:取药窗口 只有一个
买家睡觉(wait),工作人员叫醒某个买家(notify),叫醒全部买家(notifyall)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4.java中的引用
四种:
强软弱虚
强引用: 生活必需品 new的对象 垃圾回收器绝不会回收
软引用: 可有可无的生活用品 如果内存不足 会回收
弱引用: 可有可无的生活用品 比软引用个的对象生命周期更短 不管当前内存空间足够与否,都
会的回收它
虚引用: 它就和没有任何引用一样,在任何时候都可能被垃圾回收
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5.java中的hashmap
hashmap底层是数组,数组中的每一项又是一个链表,所以hashmap是数组+链表的结合体
hashmap的存储和读取操作
Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,
1.存储:
根据对key进行hash算法(hashcode()),得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,后加入的放在链头,先加入的放在链尾(类似于栈的后进先出);如果该位置没有元素,那么直接将元素放在该数组的位置上。
public V put(K key, V value) {
// HashMap允许存放null键和null值。
// 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
if (key == null)
return putForNullKey(value);
// 根据key的keyCode重新计算hash值。
int hash = hash(key.hashCode());
// 搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length);
// 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
// 如果发现已有该键值,则存储新的值,并返回原始值
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 如果i索引处的Entry为null,表明此处还没有Entry。
modCount++;
// 将key、value添加到i索引处。
addEntry(hash, key, value, i);
return null;
}
根据上面 put 方法的源代码可以看出,当程序试图将一个key-value对放入HashMap中时,程序首先根据该 key的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry的 value,但key不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。
2.读取:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
在hashmap中get元素时,首先计算key的hashcode,找到数组中对应位置的元素,然后通过key的equals方法找到对应元素的链表中找到需要的元素。
总结:HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个
Entry 对象。HashMap 底层采用一个
Entry[]数组来保存所有的
key-value 键值对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
6.线程同步,实现原理
参考文章:
关键:
synchronized
Synchronized是
Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
Synchronized的作用主要有三个:
(1)确保线程互斥的访问同步代码(
原子性:读操作的时候不能写操作)
(2)保证共享变量的修改能够及时可见(
可见性:修改了变量的值,其他线程操作对修改后的值可见)
(3)有效解决重排序问题(
有序性:比如创建一个对象,分配空间,指向引用,初始化对象这三步的顺序)
语法:
1.修饰普通方法
2.修饰静态方法(对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法))
3.修饰代码块
原理:
同步代码块:Monitor
方法同步块:
ACC_SYNCHRONIZED 标志设置成1
volatile 特殊域变量实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量