Android和java知識點總結

2020-10-19 16:00:24

Android和java知識點總結(均為網路轉載,方便複習和學習)

1 java基礎部分

1.1 JVM部分

1.2 JVM記憶體區域(執行時資料區域):

Java 虛擬機器器在執行 Java 程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域都各有各自的用途,以及建立和銷燬的時間,有的區域隨著虛擬機器器程序的啟動而存在,有些區域則依賴使用者執行緒的啟動和結束而建立和銷燬。
在這裡插入圖片描述

1.3 JVM記憶體區域(虛擬機器器棧):

見文章:https://www.jianshu.com/p/ecfcc9fb1de7

1.4 JVM記憶體區域(程式計數器):

見文章:https://www.jianshu.com/p/77c882f47b29

1.5 JVM記憶體區域(本地方法棧):

見文章:https://www.jianshu.com/p/8a775d747c47

1.6 JVM記憶體區域(Java 堆):

見文章:https://www.jianshu.com/p/702eddcac053

1.7 JVM記憶體區域(方法區):

見文章:https://www.jianshu.com/p/59f98076b382

1.8 GC演演算法

見文章:https://blog.csdn.net/yanghw117/article/details/80903927

1.9 java類載入機制

見文章:https://www.cnblogs.com/ityouknow/p/5603287.html

1.10 反射

見文章:https://blog.csdn.net/sinat_38259539/article/details/71799078

1.11 註解

見文章:https://www.cnblogs.com/yangming1996/p/9295168.html

1.12 泛型

使用泛型的意義在於
1,適用於多種資料型別執行相同的程式碼(程式碼複用)
2, 泛型中的型別在使用時指定,不需要強制型別轉換(型別安全,編譯器會檢查型別)
見文章:https://www.jianshu.com/p/986f732ed2f1

1.13 列舉

見文章:https://blog.csdn.net/qq_34988624/article/details/86592229

1.12 Java物件導向的三種特性

(轉自:https://www.jianshu.com/p/3a01dee87561
(1)首先,要引申出類:類是物件導向中的一個很重要的概念,因為類是很多個具有相同屬性和行為特徵的物件所抽象出來的,物件是類的一個範例。
(2)物件導向共有三個特徵:封裝,繼承,多型。
封裝: 把一些屬性和方法封裝起來,形成一個類;可以隱藏實現細節。提高了程式碼的複用性。也提高了安全性。

封裝的意義在於保護或者防止程式碼(資料)被我們無意中破壞。
封裝的作用:
1、提高了程式碼的複用性。
2、隱藏了實現細節,還要對外提供可以存取的方式。便於呼叫者的使用。這是核心之一,也可以理解為就是封裝的概念。
3、提高了安全性。

繼承: 是指在一個現有類的基礎上去構建一個新的類,構建出來的新類被稱作子類,現有類被稱作父類別,子類能呼叫父類別的非private修飾的成員,同時還可以自己新增一些新的成員,擴充父類別,甚至重寫父類別已有的方法,使其表現符合子類的特徵。

繼承的好處:
1.提高了程式碼的可維護性
2.提高了程式碼的複用性
3.讓類與類之間產生了繼承關係

繼承的弊端:
1.類與類之間的耦合度過高

繼承特點:
1.java中類只能夠單繼承,不能多繼承,可以多層繼承

注:封裝和繼承其實有一個共同的目的就是程式碼重用。

多型: 多型是同一個行為具有多個不同表現形式的能力。多型性是物件多種表現形式的體現
Java作為物件導向的語言,同樣可以描述一個事物的多種形態。

方法的過載和重寫都體現了多型性。
類中多個方法的過載叫多型,父子類中方法的覆蓋也叫多型。
多型有方法的多型和物件的多型(一個物件多種形態)。

1.13 Java物件導向面試題總結

見文章:https://blog.csdn.net/qq_31622345/article/details/78686559

1.14 多執行緒

執行緒池:https://www.jianshu.com/p/0f7f3e353dcf
面試題見文章:https://www.jianshu.com/p/00fb4d37d9cf

1.15 多執行緒三大特性

原子性:…
可見性:…
有序性:…
見文章:https://www.cnblogs.com/javatalk/p/9917348.html

1.16 HashMap

見文章:hashmap面試題原理、hashmapj基本範例 :

import java.util.HashMap;
/**
 * Created by landyChris on 2017/10/29.
 */
public class FHashMap<K,V> implements Map<K,V> {

    public static void main(String[] args) {
//        FHashMap FHashMap =new FHashMap();
//        FHashMap.put("速度和咯技術的好的",99999);
//        FHashMap.put("速度和咯技術的好的",3333);
//        FHashMap.put("快速度和咯技術的好的",555555);
//        FHashMap.put("加快速度和咯技術的好",99444999);
//        System.out.println(FHashMap.get("和咯技術的好的").toString()+";size="+ FHashMap.size);

        HashMap hashMap=new HashMap();
        hashMap.put("1","2222");
        hashMap.put("1","2222w");
        hashMap.put("1","2222d");
        hashMap.put("1","2222d2");
        hashMap.put("2","2222d2");
        System.out.println(hashMap.size());
    }
    //預設容量
    private static int defaultLength = 16;
    //預設載入因子
    private static double defualtLoaderFactor = 0.75;

    private Entry<K,V> table[] = null;
    private int size = 0;

    public FHashMap() {
        this(defaultLength,defualtLoaderFactor);
    }

    public FHashMap(int length, double loaderFactor) {
        defaultLength = length;
        defualtLoaderFactor = loaderFactor;
        table = new Entry[defaultLength];
    }

    private int hash(K k) {
        int m = defaultLength;
        int i = k.hashCode() % m;
        System.out.println("i:"+i);
        return i > 0 ? i : -i;
    }

    @Override
    public V put(K key, V value) {
        int index = hash(key);

        Entry<K,V> entry = table[index];
        if(entry == null) {
            table[index] = new Entry(key,value,null);
            size ++;
        }else {
            table[index] = new Entry(key,value,entry);
            System.out.println("oldVlaue=" + table[index].next.getValue()+"newValue="+table[index].getValue());
        }
        return table[index].getValue();
    }

    @Override
    public V get(K key) {
        int index = hash(key);
        if(table[index] == null) {
            return  null;
        }
        return find(table[index],key);
    }

    private V find(Entry<K, V> entry, K key) {

        if(key == entry.getKey()||key.equals(entry.getKey())) {
            if(entry.next != null) {
                System.out.println("oldValue1=" + entry.next.getValue());
            }
            return entry.getValue();
        }else {
            //不相等的時候,就直接遞回去取下一個值
            if(entry.next != null) {
                System.out.println("oldValue2=" + entry.next.getValue());
                return find(entry.next,key);
            }
        }
        return null;
    }

    @Override
    public int size() {
        return size;
    }

    class Entry<K,V> implements Map.Entry<K,V> {
        K k;
        V v;

        Entry<K,V> next;

        public Entry(K k,V v,Entry<K,V> next) {
            this.k = k;
            this.v = v;
            this.next = next;
        }

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }
    }

}

1.17 Arraylist

ArrayList小結
ArrayList是List介面的一個可變大小的陣列的實現
ArrayList的內部是使用一個Object物件陣列來儲存元素的
初始化ArrayList的時候,可以指定初始化容量的大小,如果不指定,就會使用預設大小,為10
當新增一個新元素的時候,首先會檢查容量是否足夠新增這個元素,如果夠就直接新增,如果不夠就進行擴容,擴容為原陣列容量的1.5倍
當在index處放置一個元素的時候,會將陣列index處右邊的元素全部右移
當在index處刪除一個元素的時候,會將陣列index處右邊的元素全部左移

見文章:https://www.cnblogs.com/lierabbit/p/8383683.html
手寫arraylist:

import java.util.Arrays;

public class ExtArrayList<E> {
    
    //ArrayList底層採用陣列存放
    private Object[] elementData;
    //預設陣列容量
    private static final int DEFAULT_CAPACITY = 10;
    //記錄實際ArrayList大小
    private int size;
    
    
    //預設初始化容量為10
    public ExtArrayList() {
        this(DEFAULT_CAPACITY);
    }
    
    //ArryList指定陣列初始的容量
    public ExtArrayList(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("初始化容量不能小於0");
        }
        elementData = new Object[initialCapacity];
    }
    
    public void add(E e) {
        //1.判斷實際存放的資料容量是否大於elementData
        ensureCapacityInternal(size + 1);
        //2.使用下標進行賦值
        elementData[size++] = e; 
    }
    
    public void add(int index, E e) {
        rangeCheck(index);
        ensureCapacityInternal(size + 1);
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = e;
        size ++; 
    }
    
    // int minCapacity 當前 size + 1
    private void ensureCapacityInternal(int minCapacity) {
        if (size == elementData.length) {
//          //新陣列容量大小
//          int newCapacity = 2 * size;
//          Object[] newObjects = new Object[newCapacity];
            //新舊陣列複製
//          for (int i = 0; i < elementData.length; i++) {
//              newObjects[i] = elementData[i];
//          }
//          elementData = newObjects;
            //新舊陣列複製
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            //最小擴容容量;防止初始容量大小為1
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
    
    //使用下標獲取陣列元素
    public Object get(int index) {
        return elementData[index];
    }
    
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
    
    public Object remove(int index) {
        rangeCheck(index);
        //1.使用下標查詢該值是否存在
        Object object = get(index);
        //計算刪除元素後面的長度
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            //2.刪除原理分析,將刪除元素後面的所有元素往前移動一位
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        //將最後一個元素變為空
        elementData[--size] = null;
        return object;
    }
    
    // 刪除相同元素 刪除第一個
    public boolean remove(E e) {
        for (int i = 0; i < elementData.length; i++) {
            Object value = elementData[i];
            if (value.equals(e)) {
                remove(i);
                return true;
            }
        }
        return false;
    }
    
    //判斷下標是否越界
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException("越界啦!");
    }
    
    public int getSize() {
        return size;
    }

}

1.18 List、Map、Set的理解(LinkedList和ArrayList、Vector和ArrayList、HashMap和HashTable和HashSet區別與使用)

轉自:https://www.jianshu.com/p/64734a935588
List特點:元素有放入順序,元素可重複
Map特點:元素按鍵值對儲存,無放入順序
Set特點:元素無放入順序,元素不可重複(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的)
List介面有三個實現類:LinkedList,ArrayList,Vector
LinkedList:底層基於連結串列實現,連結串列記憶體是散亂的,每一個元素儲存本身記憶體地址的同時還儲存下一個元素的地址。連結串列增刪快,查詢慢
ArrayList和Vector的區別:ArrayList是非執行緒安全的,效率高;Vector是基於執行緒安全的,效率低
Set介面有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet
SortedSet介面有一個實現類:TreeSet(底層由平衡二元樹實現)
Query介面有一個實現類:LinkList
Map介面有三個實現類:HashMap,HashTable,LinkeHashMap
HashMap非執行緒安全,高效,支援null;HashTable執行緒安全,低效,不支援null
SortedMap有一個實現類:TreeMap
其實最主要的是,list是用來處理序列的,而set是用來處理集的。Map是知道的,儲存的是鍵值對
set 一般無序不重複.map kv 結構 list 有序

HashSet,儲存object的集合,既然是集合,就不允許有重複元素。判斷兩個元素是否相同,是由hashCode與equals方法共同完成的。

1.19 為什麼重寫了equals()也要重寫hashCode()

見文章:連結

1.20 Java字串池(String Pool)深度解析

見文章:https://www.cnblogs.com/cold-windy/p/11514977.html

1.21 計算機網路知識

見文章:https://blog.csdn.net/vip97yigang/article/details/78306837
TCP/IP協定:https://blog.csdn.net/wuzhiwei549/article/details/105965493?utm_medium=distribute.pc_feed.none-task-blog-alirecmd-1.nonecase&depth_1-utm_source=distribute.pc_feed.none-task-blog-alirecmd-1.nonecase&request_id=

2 安卓部分

2.1 安卓系統架構

見文章:https://www.jianshu.com/p/cf260703f8ff

2.2 安卓效能優化

見文章:https://blog.csdn.net/github_37130188/article/details/89788571

2.3 APP啟動優化

1、冷啟動
App沒有啟動過或App程序被killed, 系統中不存在該App程序。需要建立App程序, 載入相關資源, 啟動Main Thread, 初始化首屏Activity等。在這個過程中, 螢幕會顯示一個空白的視窗(顏色基於主題), 直至首屏Activity完全啟動。
2、熱啟動
熱啟動意味著你的App程序只是處於後臺, 系統只是將其從後臺帶到前臺, 展示給使用者。在這個過程中, 螢幕會顯示一個空白的視窗(顏色基於主題), 直至activity渲染完畢。
3、溫啟動
介於冷啟動和熱啟動之間, 一般來說在以下兩種情況下發生:
使用者back退出了App, 然後又啟動. App程序可能還在執行, 但是activity需要重建
使用者退出App後, 系統可能由於記憶體原因將App殺死, 程序和activity都需要重新啟動
啟動優化主要是針對冷啟動. 熱啟動和溫啟動都相對較快。

冷啟動優化

2.4 安卓介面繪製原理

在這裡插入圖片描述
文章:https://blog.csdn.net/dream_caoyun/article/details/83412984?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

視訊:https://www.bilibili.com/video/BV1f7411p72Q?from=search&seid=8899746014938436225

2.5 安卓事件機制

在這裡插入圖片描述
見文章:https://segmentfault.com/a/1190000012227736

2.6 安卓app啟動流程

在這裡插入圖片描述
見文章:https://www.cnblogs.com/net19880504/p/10931222.html

2.7 recycleview原理

見文章:https://blog.csdn.net/qq_33275597/article/details/93849695
手寫實現簡單的recycleview:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn1920

multiType分析

見文章:https://blog.csdn.net/hyb1234hi/article/details/78049460

2.8 設計模式

設計模式的七大原則:https://www.cnblogs.com/neverth/p/11760931.html
設計模式23種模式程式碼實現:https://quanke.gitbooks.io/design-pattern-java/content/

2.9 handler機制

在這裡插入圖片描述
見文章:https://www.jianshu.com/p/592fb6bb69fa
處理訊息的執行緒,其實是傳送handler所持有的Looper所在的執行緒。
訊息是如何插入到MessageQueue中:
我們之前說,所有handler.post和handler.sendMessage都會呼叫到Handler的sendMessageDelayed方法,方法如下:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

這裡邏輯就很簡單了,直接呼叫了sendMessageAtTime方法,第一個引數為Message,第二個引數為SystemClock.uptimeMillis() + delayMillis,其中delayMillis為延時的時間,單位為毫秒,SystemClock.uptimeMillis() 為開機到現在的時間(不包括休眠時間),單位為毫秒。第二個引數主要是決定該Message在MessageQueue的順序,比如現在開機時間為100s,傳送一個延時20s的訊息,則兩者之和為120s; 過了5秒,又發了一個延時5s的訊息,則兩者只喝為105+5 = 110s。

題目:
Android中,有哪些是基於Handler來實現通訊的?
答:App的執行、更新UI、AsyncTask、Glide、RxJava等
處理Handler訊息,是在哪個執行緒?一定是建立Handler的執行緒麼?
答:建立Handler所使用的Looper所在的執行緒
訊息是如何插入到MessageQueue中的?
答: 是根據when在MessageQueue中升序排序的,when=開機到現在的毫秒數+延時毫秒數
當MessageQueue沒有訊息時,它的next方法是阻塞的,會導致App ANR麼?
答:不會導致App的ANR,是Linux的pipe機制保證的,阻塞時,執行緒掛起;需要時,喚醒執行緒,應用卡死與Looper沒有關係,應用在沒有訊息需要處理的時候,它是在睡眠,釋放執行緒;卡死是ANR,而Looper是睡眠。
見文章:https://blog.csdn.net/qingtiantianqing/article/details/99557277
子執行緒中可以使用Toast麼?
答:可以使用,但是Toast的顯示是基於Handler實現的,所以需要先建立Looper,然後呼叫Looper.loop。
Looper.loop()是死迴圈,可以停止麼?
答:可以停止,Looper提供了quit和quitSafely方法
Handler記憶體洩露怎麼解決?
答: 靜態內部類+弱參照 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的訊息
handler.postDelayed函數延時執行計時是否準確?
答:當上一個訊息存在耗時任務的時候,會佔用延時任務執行的時機,此時是不準確的。那麼如何準確執行延時任務呢,可以開啟一個HandlerThread為一個專門的唯一的延時訊息服務。
解析:根據原始碼說原因
由於Loop.loop裡面訊息是序列取出並行給handler.dispatchMessage的,而postDelayed函數呼叫的時候執行時間就已經算好了,這裡假設呼叫postDelayed的當前時間SystemClock.uptimeMillis為1s,那麼算上delayMillis就是1s+1s=2s,也就是系統開機後第2s,它應該在系統時間的第2s就應該被執行了,但因為訊息執行是序列執行的,上一個runnable 裡面呼叫了Thread.sleep(3000),也就是說上一個任務執行需要3s,系統時間一下就到了第3S,那麼輪到處理第二個延時runnable的時候,MessageQueue類的next方法再執行到`
138行if(now < msg.when)的時候,now是系統開機後第3秒,而此時msg.whe 是開機後第2秒,那麼就會走到下面的else分支,就立刻return了該msg,然後由handler.dispatchMessage處理,執行到該runnable的run方法,此時Log列印的時間就是3150毫秒了.

2.10 binder機制

見文章:https://blog.csdn.net/augfun/article/details/82343249

2.11 Java 運運算元

見文章:https://www.runoob.com/java/java-operators.html

2.12 進位制轉換

見文章:https://jingyan.baidu.com/article/495ba84109665338b30ede98.html

2.13 java基本型別的範圍

基本型別:byte 二進位制位數:8
包裝類:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127

基本型別:short 二進位制位數:16
包裝類:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767

基本型別:int 二進位制位數:32
包裝類:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647

基本型別:long 二進位制位數:64
包裝類:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807

基本型別:float 二進位制位數:32
包裝類:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38

基本型別:double 二進位制位數:64
包裝類:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308

基本型別:char 二進位制位數:16
包裝類:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

2.14 Arouter 原理

視訊:https://www.bilibili.com/video/BV1wz4y197XD?from=search&seid=1188900803975595839

問題:
元件化開發有什麼優勢?
在這裡插入圖片描述
元件化路由是什麼,有什麼用,說說實現方式?
使用**註解處理器**掃描所有模組的activity並生成對應的程式碼,通過單例執行生成類的方法,把所有的activity新增到單例的路由表中。
自己寫的元件化

2.15 熱修復和外掛化原理

見文章:https://www.jianshu.com/p/704cac3eb13d

2.16 橫豎屏切換時 Activity 的生命週期

此時的生命週期跟清單檔案裡的設定有關係。
1.不設定 Activity 的 android:configChanges 時,切屏會重新呼叫各個生 命週期預設首先銷燬當前 activity,然後重新載入。
2.設定 Activity
android:configChanges=「orientation|keyboardHidden|screenSize」 時,切
屏不會重新呼叫各個生命週期,只會執行 onConfigurationChanged 方法。

2.17 如 何 退 出 Activity ? 如 何 安 全 退 出 已 調 用 多 個 Activity 的 Application?

1、通常情況使用者退出一個 Activity 只需按返回鍵,我們寫程式碼想退出 activity 直接呼叫 finish()方法就行。
2、記錄開啟的 Activity:
每開啟一個 Activity,就記錄下來。在需要退出時,關閉每一個 Activity 即可。
3、傳送特定廣播:
在需要結束應用時,傳送一個特定的廣播,每個 Activity 收到廣播後,關閉 即可。
//給某個 activity 註冊接受接受廣播的意圖
registerReceiver(receiver, filter)
// 如 果 過 接 受 到 的 是 關 閉 activity 的 廣 播 activity finish()掉
4、遞迴退出
就 調 用 finish() 方 法 把 當 前 的
在開啟新的 Activity 時使用 startActivityForResult,然後自己加標誌,在 onActivityResult 中處理,遞迴關閉。
5、其實 也可以通過 intent 的 flag 來實現
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 啟用一個新的 activity。
此時如果該任務棧中已經有該 Activity,那麼系統會把這個 Activity 上面的所有 Activity 幹掉。其實相當於給 Activity 設定的啟動模式為 SingleTop。

2.18 請描述一下 Service 的生命週期

Service 有繫結模式和非繫結模式,以及這兩種模式的混合使用方式。不同 的使用方法生命週期方法也不同。
" 繫結模式: 當第一次呼叫 startService 的時候執行的方法依次為
onCreate()、onStartCommand(),當 Service 關閉的時候呼叫 onDestory 方 法。
繫結模式:第一次 bindService()的時候,執行的方法為 onCreate()、 onBind()解除繫結的時候會執行 onUnbind()、onDestory()。
上面的兩種生命週期是在相對單純的模式下的情形。我們在開發的過程中還
必須注意 Service 範例只會有一個,也就是說如果當前要啟動的 Service 已經存
在了那麼就不會再次建立該 Service 當然也不會呼叫 onCreate()方法。
一個 Service 可以被多個客戶進行繫結,只有所有的繫結物件都執行了
onBind()方法後該 Service 才會銷燬,不過如果有一個客戶執行了 onStart() 方法,那麼這個時候如果所有的 bind 客戶都執行了 unBind()該 Service 也不會 銷燬。
在這裡插入圖片描述

2.19 什麼是IntentService?有何優點?

一、IntentService 簡介
IntentService 是 Service 的子類,比普通的 Service 增加了額外的功能。 先看 Service 本身存在兩個問題:
Service 不會專門啟動一條單獨的程序,Service 與它所在應用位於同一個進 程中;
Service 也不是專門一條新執行緒,因此不應該在 Service 中直接處理耗時的 任務;
二、IntentService 特徵
會建立獨立的 worker 執行緒來處理所有的 Intent 請求;
會建立獨立的 worker 執行緒來處理 onHandleIntent() 方法實現的程式碼,無需 處理多執行緒問題;
所有請求處理完成後,IntentService 會自動停止,無需呼叫 stopSelf()方法 停止 Service;
為 Service 的 onBind()提供預設實現,返回 null;
為 Service 的 onStartCommand 提供預設實現,將請求 Intent 新增到佇列 中;

2.20 請介紹下 ContentProvider 是如何實現資料共用的

在 Android 中如果想將自己應用的資料(一般多為資料庫中的資料)提供 給第三發應用,那麼我們只能通過 ContentProvider 來實現了。
ContentProvider 是應用程式之間共用資料的介面。使用的時候首先自定義 一個類繼承 ContentProvider,然後覆寫 query 、 insert、 update、 delete 等
方法。因為其是四大元件之一因此必須在 AndroidManifest 檔案中進行註冊。
把自己的資料通過 uri 的形式共用出去,android 系 統 下 不 同 程 序 數 據 默 認 是 不 能 共 享 訪 問 , 需 要 去 實 現 一 個 類 去 繼 承 ContentProvider
public class PersonContentProvider extends ContentProvider{
public boolean onCreate(){
}
query(Uri, String[], String, String[], String)
insert(Uri, ContentValues)
update(Uri, ContentValues, String, String[])
delete(Uri, String, String[])
}
第三方可以通過 ContentResolver 來存取該 Provider。

2.21 為什麼要用 ContentProvider?它和 sql 的實現上有什麼差 別?

ContentProvider 遮蔽了資料儲存的細節 ,內部實現對使用者完全 透明,使用者只需要關心運算元據的 uri 就可以了,ContentProvider 可以實現不同 app之間共用。Sql 也有增刪改查的方法, 但是 sql 只能查詢本應用下的資料庫。而 ContentProvider 還可以去增刪改查本地檔案. xml 檔案的讀取等。

2.22 說說 ContentProvider、ContentResolver、ContentObserver 之間的關係

a. ContentProvider 內容提供者,用於對外提供資料
b. ContentResolver.notifyChange(uri)發出訊息
c. ContentResolver 內容解析者,用於獲取內容提供者提供的資料
d. ContentObserver 內容監聽器,可以監聽資料的改變狀態
e. ContentResolver.registerContentObserver()監聽訊息。

2.23 使用 contentProvider 獲取本地所有的音訊檔

Android 中,系統為多媒體型別的檔案(比如圖片、音訊、視訊等)建立了
資料庫(sqlite 資料庫),將檔案的名稱、大小等資訊儲存起來,從而完成多媒體 資料的維護工作;所以我們需要實現建立一個實體類。
可以根據 ContentResover 獲取到一個 Cursor,然後根據這個遊標,遍歷所
有的歌曲的資訊,設定給實 體 類 ,得到你想要的音訊檔。因為是從本地讀取數 據,所以需要新增許可權

<uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"/> 

2.24 ListView 如何實現分頁載入

①設 置 ListView 的 滾 動 監 聽 器 : setOnScrollListener(newOnScrollListener{….})在監聽器中有兩個方法: 捲動狀態發生變化的方法(onScrollStateChanged)和 listView 被捲動時呼叫的方法(onScroll)
② 在捲動狀態發生改變的方法中,有三種狀態:
手指按下移動的狀態:SCROLL_STATE_TOUCH_SCROLL: // 觸控滑動 慣性捲動(滑翔(flgin)狀態):SCROLL_STATE_FLING: // 滑翔
靜止狀態: SCROLL_STATE_IDLE: // 靜止
對不同的狀態進行處理:
分批載入資料,只關心靜止狀態:關心最後一個可見的條目,如果最後一個 可見條目就是資料介面卡( 集 合 )裡的最後一個,此時可載入更多的資料。在每次載入的時候,計算出捲動的數量,當捲動的數量大於等於總數量的時候,可以提 示使用者無更多資料了。

2.25 如何在ScrollView 中如何嵌入 ListView

通常情況下我們不會在 ScrollView 中巢狀 ListView,但是如果面試官非 讓我巢狀的話也是可以的。
在 ScrollView 新增一個 ListView 會導致 listview 控制元件顯示不全,通常只會顯示一條,這是因為兩個控制元件的捲動事件衝突導致。所以需要通過 listview 中的item 數量去計算 listview 的顯示高度,從而使其完整展示,現階段最好的處理的方式是: 自定義 ListView,過載 onMeasure()方法,設定 全部顯示。

2.26 Serializable 和 Parcelable 的 區別

在使用記憶體的時候, Parcelable 類比 Serializable 效能高, 所以推薦使用 Parcelable 類。
1.Serializable 在序列化的時候會產生大量的臨時變數,從而引起頻繁的 GC。
2.Parcelable 不能使用在要將資料儲存在磁碟上的情況。儘管 Serializable 效率低點,但在這種情況下,還是建議你用 Serializable 。
實現:
1.Serializable 的實現,只需要繼承 Serializable 即可。這只是給物件打
了一個標記,系統會自動將其序列化。
2.Parcelabel 的實現,需要在類中新增一個靜態成員變數 CREATOR,這 個變數需要繼承 Parcelable.Creator 介面。

2.27 如何避免 OOM 異常

OOM 記憶體溢位,想要避免 OOM 異常首先我們要知道什麼情況下會導致 OOM 異常。
(1)圖片過大導致OOM
Android 中 用 bitmap 時 很 容 易 內 存 溢 出 , 比 如 報 如 下 錯 誤 : Java.lang.OutOfMemoryError : bitmap size exceeds VM budget。解決方法:方法 1: 等比例縮小圖片,方法 2:對圖片採用軟參照,及時地進行 recyle()操作,方法 3:使用載入圖片框架處理圖片,如專業處理載入圖片的 ImageLoader 圖 片載入框架。
(2)介面切換導致OOM
一般情況下,開發中都會禁止橫屏的。因為如果是來回切換話,activity 的 生命週期會重新銷燬然後建立。
有時候我們會發現這樣的問題,橫豎屏切換 N 次後 OOM 了。
這種問題沒有固定的解決方法,但是我們可以從以下幾個方面下手分析。
1、看看頁面佈局當中有沒有大的圖片,比如背景圖之類的。
去除 xml 中相關設定,改在程式中設定背景圖(放在 onCreate()方法中):
在 Activity destory 時注意,drawable.setCallback(null); 防止 Activity 得 不到及時的釋放。
2、跟上面方法相似,直接把 xml 組態檔載入成 view 再 放 到 一 個 容 器 裡, 然 後 直 接 調 用 this.setContentView(View view);方法,避免 xml 的重複載入。
3、 在頁面切換時儘可能少地重複使用一些程式碼
比如:重複呼叫資料庫,反覆使用某些物件等等…
(3)查詢資料庫沒有關閉遊標
程式中經常會進行查詢資料庫的操作,但是經常會有使用完畢 Cursor 後 沒 有關閉的情況。如果我們的查詢結果集比較小,對記憶體的消耗不容易被發現,只有在常時間大量操作的情況下才會出現記憶體問題,這樣就會給以後的測試和問題排 查帶來困難和風險。
(4)構造Adapter 時,沒有使用快取的 convertView
在使用 ListView 的時候通常會使用 Adapter,那麼我們應該儘可能的使用 ConvertView 。
為什麼要使用convertView?
當 convertView 為空時,用 setTag()方法為 每個 View 繫結一個存放控制元件的ViewHolder 物件。當convertView 不為空,重複利用已經建立的 view 的時候,使用 getTag()方法獲取繫結的 ViewHolder 物件,這樣就避免了 findViewById 對控制元件的層層查詢,而是快速定位到控制元件。
(5)Bitmap 物件不再使用時呼叫recycle()釋放記憶體
有時我們會手工的操作 Bitmap 物件,如果一個 Bitmap 物件比較佔記憶體,當它不再被使用的時候,可以呼叫 Bitmap.recycle()方 法回收此 物件的畫素 所佔 用的記憶體,但這不是必須的,視情況而定。
(6)其他
Android 應用程式中最典型的需要注意釋放資源的情況是在Activity 的生命週期中,在 onPause()、onStop() 、 onDestroy()方法 中需 要適當的釋 放資 源的情況。使用廣播沒有登出也會產生 OOM。

2.28 ANR 是什麼?怎樣避免和解決 ANR

在 Android 上,如果你的應用程式有一段時間響應不夠靈敏,系統會向用 戶顯示一個對話方塊,這個對話方塊稱作應用程式無響應(ANR:Application NotResponding ) 對話方塊。原因是主執行緒在接受到其他訊息的時候沒有時間去響應,它的時間都在處理那一個耗時的操作,造成點選事件沒有辦法響應,點選事件沒有辦法響應就容易出現ANR。
ANR 一 般 有 三 種 類 型 :
1:KeyDispatchTimeout(5 seconds) --主要型別按鍵或觸控事件在特定時間內無響應
2:BroadcastTimeout(10 seconds)BroadcastReceiver 在特定時間內無法處理完成
3:ServiceTimeout(20 seconds) --小概率型別Service 在特定的時間內無法處理完成
超時的原因一般有兩種:
(1)當前的事件沒有機會得到處理(UI 執行緒正在處理前一個事件沒有及時完成或 者 looper 被某種原因阻塞住)
(2)當前的事件正在處理,但沒有及時完成
UI 執行緒儘量只做跟 UI 相關的工作,耗時的工作(資料庫操作,I/O,連線網路或者其他可能阻礙 UI 執行緒的操作)放入單獨的執行緒處理,儘量用 Handler 來處 理 UI thread 和 thread 之間的互動。
查詢 ANR 的方式:

  1. 匯出/data/data/anr/traces.txt,找出函數和 呼叫過程, 分析程式碼
  2. 通過效能 LOG 人肉查詢

2.29 Android 執行緒間通訊有哪幾種方式

 共用記憶體(變數);
 檔案,資料庫;
 Handler;
 Java 裡的 wait(),notify(),notifyAll()

2.30 android 應用對記憶體是如何限制的?我們應該如何合 理使用內 存?

Android 應用的開發語言為 Java,每個應用最大可使用的堆記憶體受到 Android 系統的限制
•Android 每一個應用的堆記憶體大小有限
•通常的情況為 16M-48M
•通過 ActivityManager 的 getMemoryClass()來查詢可用堆記憶體限制
•3.0(HoneyComb)以上的版本可以通過 largeHeap=「true」來申請更多 的堆記憶體
•NexueHeap 512
•如果試圖申請的記憶體大於當前餘下的堆記憶體就會引發 OutOfMemoryError()
•應用程式由於各方面的限制,需要注意減少記憶體佔用,避免出現記憶體漏失。

2.31 如何合理使用記憶體?

1、注意資源回收,像資料庫,輸入輸出流,定位元運算這樣的物件,要在使 用完及時關閉流。
2、少 使用 靜態 變數 ,因為 系統 將靜 態變 量的優 先級 設定 的很 高, 會最後 回
收。所以可能因為靜態變數導致該回收的沒有回收。而回收了不該回收的內 存。
3、注意大圖片的縮放,如果載入的圖片很大,要先經過自己程式的處理,
降低解析度等。最好設定多種解析度格式的圖片,以減少記憶體消耗。
4、動態註冊監聽,把一些只有顯示的時候才使用到的監聽放程序式內部, 而不是放在 manifesat 中去。
5、減少使用動畫,或者適當減少動畫的幀數。
6 、 注意 自己 的 程 序 邏輯, 在該 關閉 自己 程式的 控制元件 的時 候, 主動 關閉, 不 要交給系統去決定。(這個要自己把握好,也不是說都自己搞定,只有那些 自己確定需要關閉的物件,自己將其關閉。)

2.32 簡述android 應用程式結構是哪些?

src 目錄是原始碼目錄,所有允許使用者修改的 java 檔案和使用者自己新增的 java 檔案都儲存在這個目錄中
gen 目錄是 1.5 版本新增的目錄,用來儲存 ADT 自動生成的 java 檔案,例 如 R.java 或 AIDL 檔案
注 意 :R.java 檔案(非常重要)
a) R.java 檔案是 ADT 自動生成的檔案,包含對 drawable、layout 和 values目錄內的資源的參照指標,Android 程式能夠直接通過 R 類參照目錄中的資源
b) R.java 檔案不能手工修改,如果向資源目錄中增加或刪除了資原始檔,則需 要在工程名稱上右擊,選擇 Refresh 來更新 R.java 檔案中的程式碼
c) R 類包含的幾個內部類,分別與資源型別相對應,資源 ID 便儲存在這些內部類中,例如子類 drawable 表示影象資源,內部的靜態變數 icon 表示資源 名稱,其資源 ID 為 0x7f020000 。一般情況下,資源名稱與資原始檔名相同android.jar 檔案是 Android 程式所能參照的函數庫檔案,Android 通過平 臺所支援 API 都包含在這個檔案中assets 目錄用來存放原始格式的檔案,例如音訊檔、視訊檔等二進位制格式檔案。此目錄中的資源不能被 R.java 檔案索引。,所以只能以資截流的形式 讀取。一般情況下為空layout 目錄用來存放我們為每個介面寫的佈局檔案Strings.xml 檔案是程式中的一些字串的參照
AndroidManifest.xml 是 XML 格式的 Android 程式宣告檔案,包含了Android 系統執行 Android 程式前所必須掌握的重要資訊,這些資訊包含應用程式名稱、圖示、包名稱、模組組成、授權和 SDK 最低版本等,而且每個 Android 程式必須在根目錄下包含一個 AndroidManifest.xml 檔案
注:AndroidMainfest.xml 檔案:

  1. AndroidManifest.xml 檔案的根元素是 manifest,包含了
    xmlns:android、package、android:versionCode 和 android:versionName 共 4 個屬性
  2. xmlns:android 定義了 Android 的名稱空間,值為 http://schemas.android.com/apk/res/android
  3. package 定義了應用程式的包名稱
  4. android:versionCode 定義了應用程式的版本號,是一個整數值,數值
    越大說明版本越新,但僅在程式內部使用,並不提供給應用程式的使用者
  5. android:versionName 定義了應用程式的版本名稱,是一個字串,僅 限於為使用者提供一個版本標識
  6. manifest 元素僅能包含一個 application 元素,application 元素中能夠
    宣告 Android 程式中最重要的四個組成部分,包括 Activity、Service、
    BroadcastReceiver 和 ContentProvider,所定義的屬性將影響所有組成部分
  7. android:icon 定義了 Android 應用程式的圖示,其中@drawable/icon 是 一種資源 參照方式 ,表示資源類 型是影象 ,資源名稱為 icon ,對應 的資 源文 件為 res/drawable 目錄下的 icon.png
  8. android:label 則定義了 Android 應用程式的標籤名稱
    default.properties 檔案記錄 Android 工程的相關設定,該檔案不能手動修 改,需右鍵單擊工程名稱,選擇「Properties」進行修改

2.33 多執行緒間通訊和多程序之間通訊有什麼不同,分別怎麼實現?

一、程序間的通訊方式

)管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只
能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。
) 有名管道 (namedpipe) : 有名管道也是半雙工的通訊方式,但是它允許 無親緣關係程序間的通訊。
) 信 號 量 ( semophore ) : 號誌是一個計數器,可以用來控制多個程序對 共用資源的存取。它常作為一種鎖機制,防止某程序正在存取共用資源時,其
他程序也存取該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的 同步手段。
) 消 息 隊 列 ( messagequeue ) : 訊息佇列是由訊息的連結串列,存放在核心中
並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無 格式位元組流以及緩衝區大小受限等缺點。
) 訊號 (sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程序某個 事件已經發生。
)共 享 內 存 ( shared memory ) :共用記憶體就是對映一段能被其他程序所存取 的記憶體,這段共用記憶體由一個程序建立,但多個程序都可以存取。共用記憶體是最
快 的 IPC 方 式, 它 是 針 對其他 程序 間 通訊 方 式 執行 效 率低 而 專門 設 計 的。它
往往與其他通訊機制,如訊號兩,配合使用,來實現程序間的同步和通訊。
)通訊端(socket ) : 套解口也是一種程序間通訊機制,與其他通訊機制不同 的是,它可用於不同及其間的程序通訊。

二、執行緒間的通訊方式
)鎖機制:包括互斥鎖、條件變數、讀寫鎖
*互斥鎖提供了以排他方式防止資料結構被並行修改的方法。

*讀寫鎖允許多個執行緒同時讀共用資料,而對寫操作是互斥的。

*條件變數可以以原子的方式阻塞程序,直到某個特定條件為真為止。對條件的測試是在互斥鎖的保護下進行的。條件變數始終與互斥鎖一起使用。 # 號誌機制(Semaphore):包括無名執行緒號誌和命名執行緒號誌
訊號機制(Signal):類似程序間的訊號處理
執行緒間的通訊目的主要是用於執行緒同步,所以執行緒沒有像程序通訊中的用於 資料交換的通訊機制。

2.34 什麼是 AIDL 以及如何使用

① aidl 是 Android interface definition Language 的英文縮寫, 意思Android 介面定義語言。
②使用 aidl 可以幫助我們釋出以及呼叫遠端服務,實現跨程序通訊。 ③將服務的 aidl 放到對應的 src 目錄,工程的 gen 目錄會生成相應的介面類
我們通過 bindService(Intent,ServiceConnect,int)方法系結遠端服務, 在 bindService 中有一個 ServiceConnec 介面, 我們需要覆寫該類的
onServiceConnected(ComponentName,IBinder) 方法,這個方 法的第二個參
數 IBinder 物件其實就是已經在 aidl 中定義的介面,因此我們可以將 IBinder 物件強制轉換為 aidl 中的介面類。
我們通過 IBinder 獲取到的物件(也就是 aidl 檔案生成的介面)其實是系統 產生的代理物件,該代理物件既可以跟我們的程序通訊,又可以跟遠端程序通訊, 作 為一箇中間的角色實現了程序間通訊。

2.35 安卓螢幕適配方案

見文章:https://www.cnblogs.com/whycxb/p/9755012.html
適配框架:https://github.com/JessYanCoding/AndroidAutoSize/blob/master/README-zh.md

2.36 Git

見文章:https://gitee.com/all-about-git

2.37 Linux常用命令

見文章:https://blog.csdn.net/qq_24946689/article/details/105365912

2.38 Glide原理解析

見文章:https://www.jianshu.com/p/bce65d04d831
手寫高並行圖片載入框架:https://blog.csdn.net/sw5131899/article/details/66970195

2.39 webview記憶體洩露分析

見文章:https://www.jianshu.com/p/3e8f7dbb0dc7

2.40 BigDecimal一定不會失去精度嗎?

BigDecimal一定不會失去精度嗎?

2.4.1 service生命週期

在這裡插入圖片描述

服務生命週期從建立到銷燬可以遵循兩條不同的路徑:

啟動服務
該服務在其他元件呼叫 startService() 時建立,然後無限期執行,必須通過呼叫 stopSelf() 來自行停止執行或通過其他元件呼叫 stopService() 來停止服務。服務停止後,系統會將其銷燬

繫結服務
服務在另一個元件(使用者端)呼叫 bindService() 時建立。然後,使用者端通過 IBinder 介面與服務進行通訊。使用者端可以通過呼叫 unbindService() 關閉連線。多個使用者端可以繫結到相同服務,而且當所有繫結全部取消後,系統即會銷燬該服務(服務不必自行停止執行)

這兩條路徑並非完全獨立。也就是說,可以繫結到已經使用 startService() 啟動的服務。例如,可以通過使用 Intent(標識要播放的音樂)呼叫 startService() 來啟動後臺音樂服務。隨後,可能在使用者需要稍加控制播放器或獲取有關當前播放歌曲的資訊時,Activity 可以通過呼叫 bindService() 繫結到服務。在這種情況下,除非所有使用者端均取消繫結,否則 stopService() 或 stopSelf() 不會實際停止服務

需要注意的點:
1.通過startService啟動的Service,只有呼叫了stopService(外部元件呼叫)或stopSelf(Service內部自己呼叫),才會停止。
2.通過startService啟動的Service,在Service執行中無法與Service進行互動,即外部元件只能控制其開關,無法進行互動。
3.通過startService啟動的Service,與外部元件之間沒有關係,外部元件的生死跟它沒有聯絡。
3.通過startService啟動的Service,啟動之後重複啟動的話不會觸發onCreate方法,但是會重複觸發onStartCommand,其實貌似也算是資料交流了吧,不過是單向的。
4.bindService啟動的Service表示將一個Service繫結到一個元件上,其生命週期與該元件的生命週期繫結在一起,比如綁到一個Activity,Activity在Destroy後Service跟著就Destroy了。
4.注意是不能繫結廣播的,因為廣播發完了其生命就到頭了,常用的是綁Activity,還可以是Service。
5.bindService啟動的Service在使用完之後可以解除繫結,當一個Service上的所有繫結的元件都解綁之後,它就會被銷燬。
6.可以同時使用兩種啟動方式,此時的生命週期就變的有些複雜了,兩種關聯到一起,總結來說的話,先startService與先bindService兩種方式達到的效果是一樣的,即此時unbindService的話,Service並不會結束,而是要等到stopService才會結束(onDestroy);若是此時stopService,也不會結束,而是要等到unbindService時才會結束(由於已經呼叫過stopService,此時會直接onDestroy)。
7.onRebind呼叫時機:
當舊client與service之間的關聯在onUnbind中都結束之後,新client繫結時,
必須是onUnbind返回true,且服務在解綁之後沒有銷燬

2.4.2 啟動模式

在這裡插入圖片描述