1.反正屏切换时Activity的生命周期
- 在没有配置activity的configChanges特点时 Activity的生命周期:onPause->onStop->onCreate->onStart->onResume
- 配置了activity的configChanges onConfigurationChanged
2..怎么设置activity成窗口样式
android:theme=“@android:style/Theme.Dialog”
3..Activity的发动办法
- standard 不论有没有已存在的实例,都生成新实例
- singleTop 假如发现有对应的Activity实例坐落栈顶,则重复使用,不然创立实例
- singleTask a)栈内复用,复用时具有clearTop机制 b)single taskAffinity in task
- singleInstance a)启用一个新的栈结构,将Activity放置于栈结构中,并保证不会有其它Activity实例进入 b)方便多个应用共享大局唯一的实例
4.Service的生命周期
经过startService调用 onCreate->onStartCommand->onDestroy 经过bindService调用 onCreate->onBind->onUnbind->onDestroy
Service Binder
>>//示例
a).public class LocalService extends Service{
private static LBinder binder=new LBinder(); //必须声明为static
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
@Override
public IBinder onBind(Intent intent)
{
return binder;
}
static class LBinder extends Binder{
public int getCount(){
return count;
}
}
}
5..IntentService
//敞开线程处理耗时操作
>>onCreate //创立单独的HandlerThread处理所有的intent恳求
:1.HandlertThread thread = new HandlerThread("")
thread.start()
mServiceLooper =thread.getLooper()
mServiceHandler =new ServiceHandler(mServiceLooper)
onSTartCommand.
:1.public int onStartCommand(Intent intent,int startId){
onStart(intent,startId)
return mRedelivery?START_REDELIVER_INTENT:START_NOT_STICKY;//在于service挂掉了intent是否会回传
}
:2.public void onStart(Intent intent,int startId)
{
Message msg=mServiceHandler.obtainMessage()
msg.obj=intent
msg.arg1=startId
mServiceHandler.sendMessage(msg)
}
ServiceHandler.
//onHandleIntent,为自定义的类所实现的耗时操作
:1.private final class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){super(looper);}
public void handleMessage(Message msg)
{
onHandleIntent((Intent)msg.obj)
stopSelf(msg.arg1)//当id为最终一个startService的startId时,进行封闭
}
}
总结
a).经过HandlerThread创立线程,并调用getLooper获取looper变量,以新建Handler
b).IntentService经过onStartCommand接纳新的intent,并放在message.obj发送过handler
c).handler调用实现的onHandleIntent(intent)
d).Message msg=mServiceHandler.obtainMessage() //obtainMessage和Looper调用msg.relycleUnchecked是以刺进链表头部和撤除的形式分配、释放Message资源
6.Android有哪几种布局
- LinearyLayout 按照水平或笔直的顺序排列子元素,android:layout_weight收效
- FrameLayout 后来的子元素掩盖在之前子元素的上层,以左上角为基准点
- AbsoluteLayout android:layout_x和android:layout_y会收效
- RelativeLayout android:layout_below和android:layout_above
TableLayout //表格布局由TableRow构成,layout_column指定开始列,layout_span拟定规模 a).大局特点 android:shrinkColumns=“0,1” //可缩短的列 android:stretchColumns=“0,1” //可拉伸的列 b).单元格特点 android:layout_span=“2” //该TableRow中的子View所横跨的列 android:layout_column=“1” //拟定子view在哪列开始排列
7.HashMap、HashTable的区别 //从线程安全性、速度
>>线程方面
:1.HashTable是线程安全的
>>HashTable的key、value不允许设置null,而hashmap能够
>>是否可在遍历时进行操作
:1.HashMap:fail-fast
a).既在遍历HashMap时,假如经过实例目标进行修正则报ConcurrentModificationException,
expectedModCount==modCount
>>解决抵触
:1.HashMap的装载因子是3/4,链接法解决抵触,当链表过长时转化为红黑树
>>实现
:1.HashMap 经过数组+链表的结构
>transient Node<K,V>[] table;
>static class Node<K,V> implements Map.Entry<K,V>{
final int hash;
final K key;
final V value;
Node<K,V> next;
public final int hashCode(){
return Objects.hashCode(key)^Objects.hashCode(v)
}
}
:2.hashCode 经过Objects.hashCode(key)^Objects.hashCode(value)计算
:3.扩容 哈希桶的容量形若100...0,扩容容量为2倍,阀值也为2倍,需要rehash
>final Node<K,V>[] resize(){
//oldTab 为当时表的哈希桶
Node<K,V>[] oldTab = table;
int oldCap=oldTab.length
//当时的阈值
int oldThr = threshold;
if (oldCap > 0) {
if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
{
newThr=oldThr<<1;
}
}
//更新阈值
threshold = newThr
table= newTab
if(oldTab!=null){
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if((e=oldTab[j])!=null){
oldTab[j] = null//将原哈希桶置空以便GC
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e; //直接将这个元素放置在新的哈希桶里
else
{
//因为扩容是容量翻倍,所以原链表上的每个节点,现在可能存放在原来的下标,即low位, 或者扩容后的下标,
//即high位。 high位= low位+原哈希桶容量
//低位链表的头结点、尾节点
Node<K,V> loHead = null, loTail = null;
//高位链表的头节点、尾节点
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;//暂时节点 存放e的下一个节点
do {
next = e.next;
//这儿又是一个使用位运算 替代常规运算的高效点: 使用哈希值 与 旧的容量 能够得到哈希值去模后,
//是大于等于oldCap还是小于oldCap,等于0代表小于oldCap,应该存放在低位,不然存放在高位
if ((e.hash & oldCap) == 0) {
//给头尾节点指针赋值
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}//高位也是相同的逻辑
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}//循环直到链表结束
} while ((e = next) != null)
//将低位链表存放在原index处,
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
//将高位链表存放在新index处
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
:4.刺进节点
>public V put(K key,V value){return putVal(hash(key),key,value)}
>final V putVal(int hash,K key,V value,...)
{
Node<K,V>[] tab=table
int n=tab.length
Node<K,V> p;
//假如当时哈希表是空的,代表是初始化
if ((tab = table) == null || (n = tab.length) == 0)
//那么直接去扩容哈希表,而且将扩容后的哈希桶长度赋值给n
n = (tab = resize()).length;
//假如当时index的节点是空的,表示没有发生哈希碰撞。 直接构建一个新节点Node,挂载在index处即可。
//这儿再啰嗦一下,index 是使用 哈希值 & 哈希桶的长度-1,替代模运算
if(p=tab[I=?(n-1)&hash)==null)
tab[I]=newNode(hash,key,value,null)
else//发生了抵触
{
Node<K,V> e;K k;
if(p.hash==hash && ((k=p.key)==key || key.equals(k))//键值相同,寻觅成功
e=p;
else if(p instance TreeNode) //红黑树
e=TreeNode<K,V)p.putTreeVal(this, tab,hash,key,value)
else{
//遍历链表
for(int binCount=0;;++binCount)
{
if((e=p.next)==null) //遍历到尾部,追加新节点到尾部
{
p.next=newNode(hash, key, value) //生成结点并刺进
if(binCount>=TREEIFY_THRESHOLD-1) //将链表转化为树结构
treeifyBin(tab,hash)
break
}
//假如找到了要掩盖的节点
if(e.hash==hash&&((k=e.key)==key)
break
p=e //迭代
}
}
if(e!=null)
e.value=value //设置新值
}
}
:5.删去结点
>final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
// p 是待删去节点的前置节点
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v
//假如链表头的便是需要删去的节点
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;//将待删去节点引用赋给node
else if ((e = p.next) != null) {//不然循环遍历 找到待删去节点,赋值给node
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
//假如有待删去节点node, 且 matchValue为false,或者值也相等
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)//假如node == p,说明是链表头是待删去节点
tab[index] = node.next;
else//不然待删去节点在表中间
p.next = node.next;
++modCount;//修正modCount
--size;//修正size
return node;
}
}
}
:6.查找
>final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//查找进程和删去根本差不多, 找到回来节点,不然回来null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
:7.iterator遍历
> final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
//fail-fast战略
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
//顺次取链表下一个节点,
if ((next = (current = e).next) == null && (t = table) != null) {
//假如当时链表节点遍历完了,则取哈希桶下一个不为null的链表头
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
40.LinkedHashMap
>>成员
:1.LinkedHashMapEntry //增加了before和after域,成为双向链表
static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
LinkedHashMapEntry<K,V> before, after;//双向链表
LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
:2.head tail
>transient LinkedHashMap.Entry<K,V> head //双向链表的头结点
>transient LinkedHashMap.Entry<K,V> tail //双向链表的尾节点
:3.accessOrder //默以为false,是按照刺进结点的顺序,若为true,则可构造LruCache
>>重写newNode,在HashMap调用putVal()办法被调用
>//在构建新节点时,构建的是`LinkedHashMap.Entry` 不再是`Node`.
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p); //!!!
return p;
}
//将新增的节点,连接在链表的尾部
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
//集合之前是空的
if (last == null)
head = p;
else {//将新节点连接在链表的尾部
p.before = last;
last.after = p;
}
}
>>删去结点
>//在删去节点e时,同步将e从双向链表上删去
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
//待删去节点 p 的前置后置节点都置空
p.before = p.after = null;
//假如前置节点是null,则现在的头结点应该是后置节点a
if (b == null)
head = a;
else//不然将前置节点b的后置节点指向a
b.after = a;
//同理假如后置节点时null ,则尾节点应是b
if (a == null)
tail = b;
else//不然更新后置节点a的前置节点为b
a.before = b;
}
>>get //获取结点
>public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);//因为访问了该结点,所以动态调整其位置
return e.value;
}
>若accessOrder==true //按访问顺序
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;//原尾节点
//假如accessOrder 是true ,且原尾节点不等于e
if (accessOrder && (last = tail) != e) {
//节点e强转成双向链表节点p
LinkedHashMapEntry <K,V> p =
(LinkedHashMapEntry <K,V>)e, b = p.before, a = p.after;
//p现在是尾节点, 后置节点一定是null
p.after = null;
//假如p的前置节点是null,则p以前是头结点,所以更新现在的头结点是p的后置节点a
if (b == null)
head = a;
else//不然更新p的前直接点b的后置节点为 a
b.after = a;
//假如p的后置节点不是null,则更新后置节点a的前置节点为b
if (a != null)
a.before = b;
else//假如原本p的后置节点是null,则p便是尾节点。 此刻 更新last的引用为 p的前置节点b
last = b;
if (last == null) //原本尾节点是null 则,链表中就一个节点
head = p;
else {//不然 更新 当时节点p的前置节点为 原尾节点last, last的后置节点是p
p.before = last;
last.after = p;
}
//尾节点的引用赋值成p
tail = p;
//修正modCount。
++modCount;
}
}
8.怎么在ListView间增加分割线
//引荐用divider设置drawable的分割线
>>.设置大局特点
a).android:divider="#FFF" //设置为null可无视距离
b).android:dividerHeight="1px"
c).默许的list view不支持设置footerDevider,采取在每个item布局中增加
> <View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#999"
/>
今日分享到此结束,对你有协助的话,点个赞再走呗,每日一个面试小技巧
重视公众号:Android老皮
解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发体系学习指南(附项目实战)
2.Android Framework学习指南,助力成为体系级开发高手
3.2023最新Android中高级面试题汇总+解析,离别零offer
4.企业级Android音视频开发学习道路+项目实战(附源码)
5.Android Jetpack从入门到通晓,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提高架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360全方面性能调优
10.Android零基础入门到通晓,高手进阶之路