• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

面试八连问,你能接下几招

武飞扬头像
派大星不吃蟹
帮助1

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版

内容如下

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgiicjj
系列文章
更多 icon
同类精品
更多 icon
继续加载