2018.03.12、Android知識點-Android篇

Traning發表於2018-03-15

1、Activity相關:

1、Activity的生命週期

image


2、Activity的啟動模式以及使用場景

啟動模式

  1. standard:預設的啟動模式,每次建立都會產生新的例項,誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中
  2. singleTop:棧頂複用模式,如果新的activity已經位於棧頂,那麼這個Activity不會被重寫建立,同時它的onNewIntent(Intent intent)方法會被呼叫,通過此方法的引數我們可以去除當前請求的資訊,該 Activity的例項不在該棧或者不在棧頂 時,其行為同standard啟動模式
  3. singleTask:棧內複用模式,如果棧中存在這個Activity的例項就會複用這個Activity,不管它是否位於棧頂,複用時,會將它上面的Activity全部出棧,並且會回撥該例項的onNewIntent方法。
  4. singleInstance:全域性唯一模式,具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全域性唯一性,即整個系統中就這麼一個例項,由於棧內複用的特性,後續的請求均不會建立新的Activity例項,除非這個特殊的任務棧被銷燬了,當複用該例項 會回撥onNewIntent方法

3、Activity的啟動過程(不要回答生命週期)

Activity 啟動流程圖

Activity的啟動過程 必須掌握的Activity啟動過程

4、在Activity中如何儲存/恢復狀態?

分別呼叫onSaveInstanceState(Bundle outState)和 
onRestoreInstanceState(Bundle outState)  2個方法儲存和恢復狀態。
複製程式碼

5、在Activity中如何儲存/恢復狀態?

  • 不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次;
  • 設定Activity的android:configChanges="orientation"時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次;
  • 設定Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新呼叫各個生命週期,只會執行onConfigurationChanged方法;

6、Activity上有Dialog的時候按Home鍵時的生命週期?

按Home鍵,再開啟activity.

2018.03.12、Android知識點-Android篇

7、Application 和 Activity 的 Context 物件的區別

  • 1、生命週期不同,Application的context隨應用存在而存在,activity的context隨該activity的存在而存在。
  • 2、適用範圍不同,Application的context 不能用在 show dialog()、start activity()、Layout inflation().ativity 可以使用。

2、Service相關

1. Service的startService(Intent)啟動方式

  • 使用這種start方式啟動的Service的生命週期如下: onCreate()--->onStartCommand()(onStart()方法已過時) ---> onDestory()
  • 如果服務已經開啟,不會重複的執行onCreate(), 而是會呼叫onStart()和onStartCommand()
  • 服務停止的時候呼叫 onDestory()。服務只會被停止一次。
  • 特點:一旦服務開啟跟呼叫者(開啟者)就沒有任何關係了。 開啟者退出了,開啟者掛了,服務還在後臺長期的執行。 開啟者不能呼叫服務裡面的方法。

2. 採用bind的方式開啟服務

  • 使用這種start方式啟動的Service的生命週期如下:onCreate() --->onBind()--->onunbind()--->onDestory()
  • bind的方式開啟服務,繫結服務,呼叫者掛了,服務也會跟著掛掉。 繫結者可以呼叫服務裡面的方法

3.service和activity怎麼進行資料互動?

一種是使用broadcast,另一種是使用bindService。

  • 1、通過廣播:在service中傳送廣播,在activity中建立一個BroadcastReceive()接收;
  • 2、bindService:activity通過Intent傳值給service,並建立ServiceConnection()物件,獲得BindService.MyBind。

4.怎麼保證service不被殺死?

  • 1.onStartCommand方法,返回START_STICKY,當service因記憶體不足被kill,當記憶體又有的時候,service又被重新建立。
  • 2.提升service優先順序,在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數字越小則優先順序越低
  • 3、提升service程式優先順序
  • 4、service +broadcast 方式,就是當service走ondestory的時候,傳送一個自定義的廣播,當收到廣播的時候,重新啟動service;
  • 5、兩個守護程式

3、Broadcast相關

1、Broadcast註冊方式與區別

  1. Manifest.xml中靜態註冊:
//new出上邊定義好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();

//例項化過濾器並設定要過濾的廣播  
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

//註冊廣播   
myContext.registerReceiver(smsBroadCastReceiver,intentFilter, 
             "android.permission.RECEIVE_SMS", null);

複製程式碼
  1. 程式碼中動態註冊:

直接在Manifest.xml檔案的節點中配置廣播接收者

<receiver android:name=".MyBroadCastReceiver">  
            <!-- android:priority屬性是設定此接收者的優先順序(從-1000到1000) -->
            <intent-filter android:priority="20">
            <actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>  
            </intent-filter>  
</receiver>

複製程式碼

2、兩種註冊廣播的不同

  • 第一種不是常駐型廣播,也就是說廣播跟隨程式的生命週期。
  • 第二種是常駐型,也就是說當應用程式關閉後,如果有資訊廣播來,程式也會被系統呼叫自動執行。

3、傳送廣播的兩種方式

  • 無序廣播:所有的接收者都會接收事件,不可以被攔截,不可以被修改。
  • 有序廣播:按照優先順序,一級一級的向下傳遞,接收者可以修改廣播資料,也可以終止廣播事件。

Android 兩種註冊、傳送廣播的區別


補1、ContentProvider相關:

ContentProvider的基本使用方法和作用。ContentValue的使用方法,他和HashMap的區別是什麼?

ContentProvider 是Android系統中提供的專門使用者不同應用間進行資料共享的元件,提供了一套標準的介面用來獲取以及運算元據,准許開發者把自己的應用資料根據需求開放給其他應用進行增刪改查,而無須擔心直接開放資料庫許可權而帶來的安全問題。

ContentValue: 儲存資料封裝的HashMap,提供 put、get等方法

1、說說ContentProvider、ContentResolver、ContentObserver 之間的關係?

  • 1.ContentProvider 內容提供者,用於對外提供資料
  • 2.ContentResolver 內容解析者,用於獲取內容提供者提供的資料
  • 3.ContentObserver 內容監聽器,可以監聽資料的改變狀態
  • 4.ContentResolver.notifyChange(uri)發出訊息
  • 5.ContentResolver.registerContentObserver()監聽訊息。

2、ContentProvider與資料庫(SQL)的區別?

  • 1、ContentProvider 遮蔽了資料儲存細節,可以在不同應用間進行資料操作。
  • 2、SQL也有增刪改查的方法,只能對本應用的資料進行操作。

Android ContentProvider基本用法

4、網路相關

1、HttpClient與HttpUrlConnection的區別 2

  1. 功能上:
  • Http Client:適用於web瀏覽器,擁有大量靈活的API,提供了很多工具,封裝了http的請求頭,引數,內容體,響應,還有一些高階功能,代理、COOKIE、鑑權、壓縮、連線池的處理正因此,在不破壞相容性的前提下,其龐大的API也使人難以改進。
  • HttpURLConnection: 對於大部分功能都進行了包裝,Http Client的高階功能程式碼會較複雜,

2.效能上:

  • HttpURLConnection直接支援GZIP壓縮,預設新增"Accept-Encoding: gzip"頭欄位到請求中,並處理相應的迴應,
  • 而Http Client雖然支援,但需要自己寫程式碼處理。

3.選用:

  • Volley裡用的哪種請求方式(2.3前HttpClient,2.3後HttpUrlConnection)

2、HTTPS和HTTP的區別 2

1、什麼是 HTTPS

HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,簡單講是HTTP的安全版。

即HTTP下加入SSL (Secure Socket Layer)層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。


2、HTTPS 和 HTTP 的區別
  • https 用的 443 埠, http 用的 80 埠
  • https協議需要到ca申請證照,一般免費證照很少,需要交費。
  • http是超文字傳輸協議,資訊是明文傳輸,https 則是具有安全性的ssl加密傳輸協議。
  • http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443。
  • http的連線很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。

Android使用OkHttp請求自簽名的https網站 Android Https相關完全解析 當OkHttp遇到Https HTTPS和HTTP的區別


5、View相關

1、view的Touch事件傳遞機制

1、和touch相關的三個方法
  1. public boolean dispatchTouchEvent(MotionEvent ev); //用來分派event
  2. public boolean onInterceptTouchEvent(MotionEvent ev); //用來攔截event
  3. public boolean onTouchEvent(MotionEvent ev); //用來處理event
方 法 解析
dispatchTouchEvent() 用來分派事件。其中呼叫了onInterceptTouchEvent()和onTouchEvent(),一般不重寫該方法,返回true則表示該事件被消費
onInterceptTouchEvent() 用來攔截事件。ViewGroup類中的原始碼實現就是{return false;}表示不攔截該事件,事件將向下傳遞(傳遞給其子View);若手動重寫該方法,使其返回true則表示攔截,事件將終止向下傳遞,事件由當前ViewGroup類來處理,就是呼叫該類的onTouchEvent()方法
onTouchEvent() 用來處理事件。返回true則表示該View能處理該事件,事件將終止向上傳遞(傳遞給其父View);返回false表示不能處理,則把事件傳遞給其父View的onTouchEvent()方法來處

Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()


6、動畫相關

1、Android中有幾種動畫?

Android3.0之前有2種,3.0後有3種。
複製程式碼
  • FrameAnimation(逐幀動畫):將多張圖片組合起來進行播放,類似於早期電影的工作原理,很多App的loading是採用這種方式。
  • TweenAnimation(補間動畫):是對某個View進行一系列的動畫的操作,包括淡入淡出(AlphaAnimation),縮放(ScaleAnimation),平移(TranslateAnimation),旋轉(RotateAnimation)四種模式。
  • PropertyAnimation(屬性動畫):屬性動畫不再僅僅是一種視覺效果了,而是一種不斷地對值進行操作的機制,並將值賦到指定物件的指定屬性上,可以是任意物件的任意屬性。

注1:AnimationSet 繼承自Animation,是上面四種的組合容器管理類,沒有自己特有的屬性,他的屬性繼承自Animation,所以特別注意,當我們對set標籤使用Animation的屬性時會對該標籤下的所有子控制元件都產生影響。

注2:補間動畫執行之後並未改變View的真實佈局屬性值。切記這一點,譬如我們在Activity中有一個Button在螢幕上方,我們設定了平移動畫移動到螢幕下方然後保持動畫最後執行狀態呆在螢幕下方,這時如果點選螢幕下方動畫執行之後的Button是沒有任何反應的,而點選原來螢幕上方沒有Button的地方卻響應的是點選Button的事件。


2、屬性動畫

  • ObjectAnimator:繼承自ValueAnimator,允許你指定要進行動畫的物件以及該物件的一個屬性。
  • 大多數的情況使用ObjectAnimator就足夠了,因為它使得目標物件動畫值的處理過程變得足夠簡單,不用像ValueAnimator那樣自己寫動畫更新的邏輯
  • ObjectAnimator的動畫原理是不停的呼叫setXXX方法更新屬性值,所有使用ObjectAnimator更新屬性時的前提是Object必須宣告有getXXX和setXXX方法
ObjectAnimator mObjectAnimator= ObjectAnimator.ofInt(view, "customerDefineAnyThingName", 0,  1).setDuration(2000);
mObjectAnimator.addUpdateListener(new AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                //int value = animation.getAnimatedValue();  可以獲取當前屬性值
                //view.postInvalidate();  可以主動重新整理
                //view.setXXX(value);
                //view.setXXX(value);
                //......可以批量修改屬性
            }
        });
複製程式碼
ObjectAnimator.ofFloat(view, "rotationY", 0.0f, 360.0f).setDuration(1000).start();
複製程式碼
  • PropertyValuesHolder:多屬性動畫同時工作管理類。有時候我們需要同時修改多個屬性,那就可以用到此類
PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);  
PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);  
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();
複製程式碼
  • ValueAnimator:屬性動畫中的時間驅動,管理著動畫時間的開始、結束屬性值,相應時間屬性值計算方法等。包含所有計算動畫值的核心函式以及每一個動畫時間節點上的資訊、一個動畫是否重複、是否監聽更新事件等,並且還可以設定自定義的計算型別。
ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight);  //定義動畫
animator.setTarget(view);   //設定作用目標
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation){
        float value = (float) animation.getAnimatedValue();
        view.setXXX(value);  //必須通過這裡設定屬性值才有效
        view.mXXX = value;  //不需要setXXX屬性方法
    }
});
複製程式碼

特別注意:ValueAnimator只是動畫計算管理驅動,設定了作用目標,但沒有設定屬性,需要通過updateListener裡設定屬性才會生效。

  • AnimationSet:動畫集合,提供把多個動畫組合成一個組合的機制,並可設定動畫的時序關係,如同時播放、順序播放或延遲播放。具體使用方法比較簡單,如下:

Android應用開發之所有動畫使用詳解

7、Android中跨程式通訊

android系統中應用程式之間不能共享記憶體。 因此,在不同應用程式之間互動資料(跨程式通訊)就稍微麻煩一些

  • 1、訪問其他應用程式的Activity
如呼叫系統通話應用
Intent callIntent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:12345678");
startActivity(callIntent);
複製程式碼
  • 2、Content Provider

Content Provider提供了一種在多個應用程式之間資料共享的方式(跨程式共享資料)。應用程式可以利用Content Provider完成下面的工作

  1. 查詢資料
  2. 修改資料
  3. 新增資料
  4. 刪除資料

雖然Content Provider也可以在同一個應用程式中被訪問,但這麼做並沒有什麼意義。Content Provider存在的目的向其他應用程式共享資料和允許其他應用程式對資料進行增、刪、改操作。 Android系統本身提供了很多Content Provider,例如,音訊、視訊、聯絡人資訊等等。我們可以通過這些Content Provider獲得相關資訊的列表。這些列表資料將以Cursor物件返回。因此,從Content Provider返回的資料是二維表的形式。

如訪問系統相簿
複製程式碼
  • 3、廣播(Broadcast)
廣播是一種被動跨程式通訊的方式。當某個程式向系統傳送廣播時,其他的應用程式只能被動地接收廣播資料。這就象電臺進行廣播一樣,聽眾只能被動地收聽,而不能主動與電臺進行溝通。例如獲取手機電量資訊
複製程式碼
  • 4、AIDL服務

2、AIDL理解

1、定義:Android系統中的程式之間不能共享記憶體,因此,需要提供一些機制在不同程式之間進行資料通訊。 為了使其他的應用程式也可以訪問本應用程式提供的服務,Android系統採用了遠端過程呼叫(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種介面定義語言(Interface Definition Language,IDL)來公開服務的介面。我們知道4個Android應用程式元件中的3個(Activity、BroadcastReceiver和ContentProvider)都可以進行跨程式訪問,另外一個Android應用程式元件Service同樣可以。因此,可以將這種可以跨程式訪問的服務稱為AIDL(Android Interface Definition Language)服務。


3、Binder理解

1、什麼是binder:

Binder是Android實現 跨程式通訊(IPC)的方式,是一種虛擬的物理裝置驅動,實現了IBindler 介面。

2、知識概念
  1. 一個程式空間分為 使用者空間 & 核心空間(Kernel),即把程式內 使用者 & 核心 隔離開來

區別:

  1. 程式間,使用者空間的資料不可共享,所以使用者空間 = 不可共享空間
  2. 程式間,核心空間的資料可共享,所以核心空間 = 可共享空間
  1. 程式隔離:保證 安全性 & 獨立性,一個程式 不能直接操作或者訪問另一個程式,即Android的程式是相互獨立、隔離的
  2. 跨程式通訊( IPC ):隔離後,由於某些需求,程式間 需要合作 / 互動
  3. 跨程式間通訊的原理
  • 先通過 程式間 的核心空間進行 資料互動
  • 再通過 程式內 的使用者空間 & 核心空間進行 資料互動,從而實現 程式間的使用者空間 的資料互動
    image
  • 而Binder,就是充當 連線 兩個程式(核心空間)的通道。
3、Binder 跨程式通訊機制 模型
  1. 模型原理:

image

4、優點

對比 Linux (Android基於Linux)上的其他程式通訊方式(管道、訊息佇列、共享記憶體、 訊號量、Socket),Binder 機制的優點有:

image

Binder學習指南

圖文詳解 Android Binder跨程式通訊的原理


8、Handler相關

1、handler 訊息傳遞分析

作用:Handle 進行訊息傳遞

  1. Handle傳送的msg通過enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)新增進(MessageQueue)訊息佇列中.

Android 訊息傳遞機制分析


2、Handle、Message、MessageQueue、Looper之間的關係

  • Handle:主要傳送 Message
  • Message:訊息
  • MessageQueue:訊息佇列.儲存由Looper傳送的訊息列表。
  • Looper:用於為執行緒執行訊息迴圈的類。Thread預設情況下沒有與之關聯,通過prepare()迴圈執行線上程中,通過loop(for(;;))來處理訊息。

9、熱修復相關

1、熱修復包含:

  1. 資源替換
  2. 類替換(四大元件、類)
  3. SO補丁

2、資源替換方法:

思路:Andorid APP預設的類載入是PathClassLoader,這個只能載入自己APK的dex檔案,所以我們需要使用DexClassLoader。我們用DexClassLoader載入外部的APK之後,通過反射獲取對應的資源。


3、類替換(四大元件、類):

  1. 通過PathClassLoader 來載入我們自身App的dex
  2. 通過DexClassLoader來載入我們的補丁dex檔案,這裡面就是沒有bug的dex
  3. 反射兩個classLoader的<DexPathList pathList;>
  4. 接著我們來繼續反射兩個classloader中的pathList(注意:是兩個!一個是我們自己應用的,另一個是我們補丁的,PathClassLoader和DexClassLoader都繼承BaseDexClassLoader),DexPathList裡面的<Element[] dexElements;>,沒錯還是拿到這個陣列的值
  5. 合併兩個反射到的Element 陣列!這裡是重中之重.我們需要把我們的補丁dex放在陣列的最前面!
  6. 將合併的新的陣列,通過Field重新設定到我們自身App的DexPathList->dexElements.沒錯!就是合併之後覆蓋有bug那個loader的Element 陣列!!
  7. 通過Android build-tools 中的dx命令打包一個沒有bug的dex

Android 熱修復(全網最簡單的熱修復講解)


4、SO補丁:


10、圖片載入快取相關

1、設計一套圖片非同步載入快取方案

快取層分為三層:記憶體層,磁碟層,網路層

  • 記憶體層:記憶體快取相對於磁碟快取而言,速度要來的快很多,但缺點容量較小且會被系統回收,這裡的實現我用到了LruCache。
  • 磁碟層:相比記憶體快取而言速度要來得慢很多,但容量很大,這裡的實現我用到了DiskLruCache類。
  • 網路層:網路訪問實現我用到了開源框架Volley

11、記憶體洩露及管理

1、記憶體洩漏:

  • 記憶體洩露:程式在向系統申請分配記憶體空間後(new),在使用完畢後未釋放。結果導致一直佔據該記憶體單元,我們和程式都無法再使用該記憶體單元,直到程式結束,這是記憶體洩露。
  • 記憶體溢位:程式向系統申請的記憶體空間超出了系統能給的。比如記憶體只能分配一個int型別,我卻要塞給他一個long型別,系統就出現oom。又比如一車最多能坐5個人,你卻非要塞下10個,車就擠爆了。
  • 大量的記憶體洩露會導致記憶體溢位(oom)

2、記憶體:

  • 棧(stack):是簡單的資料結構,但在計算機中使用廣泛。棧最顯著的特徵是:LIFO(Last In, First Out, 後進先出),棧中只存放基本型別和物件的引用(不是物件)。
  • 堆(heap):堆記憶體用於存放由new建立的物件和陣列。在堆中分配的記憶體,由java虛擬機器自動垃圾回收器來管理。JVM只有一個堆區(heap)被所有執行緒共享,堆中不存放基本型別和物件引用,只存放物件本身。
  • 方法區(method):又叫靜態區,跟堆一樣,被所有的執行緒共享。方法區包含所有的class和static變數。

3、記憶體優化

  1. 單例導致記憶體洩露
public class AppSettings {

    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
        
        //this.mContext = context.getApplicationContext();
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

複製程式碼

2、靜態變數導致記憶體洩露

public class MainActivity extends AppCompatActivity {

    private static Info sInfo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (sInfo != null) {
            sInfo = new Info(this);
        }
    }
}

class Info {
    public Info(Activity activity) {
    }
}

複製程式碼

Info作為Activity的靜態成員,並且持有Activity的引用,但是sInfo作為靜態變數,生命週期肯定比Activity長。所以當Activity退出後,sInfo仍然引用了Activity,Activity不能被回收,這就導致了記憶體洩露。

3、非靜態內部類導致記憶體洩露

非靜態內部類(包括匿名內部類)預設就會持有外部類的引用,當非靜態內部類物件的生命週期比外部類物件的生命週期長時,就會導致記憶體洩露。

public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
    }

    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                // 做相應邏輯
            }
        }
    };
}

複製程式碼

熟悉Handler訊息機制的都知道,mHandler會作為成員變數儲存在傳送的訊息msg中,即msg持有mHandler的引用,而mHandler是Activity的非靜態內部類例項,即mHandler持有Activity的引用,那麼我們就可以理解為msg間接持有Activity的引用。msg被髮送後先放到訊息佇列MessageQueue中,然後等待Looper的輪詢處理(MessageQueue和Looper都是與執行緒相關聯的,MessageQueue是Looper引用的成員變數,而Looper是儲存在ThreadLocal中的)。那麼當Activity退出後,msg可能仍然存在於訊息對列MessageQueue中未處理或者正在處理,那麼這樣就會導致Activity無法被回收,以致發生Activity的記憶體洩露。

通常在Android開發中如果要使用內部類,但又要規避記憶體洩露,一般都會採用靜態內部類+弱引用的方式。

public class MainActivity extends AppCompatActivity {

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(this);
        start();
    }

    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> activityWeakReference;

        public MyHandler(MainActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = activityWeakReference.get();
            if (activity != null) {
                if (msg.what == 1) {
                    // 做相應邏輯
                }
            }
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null);
}
複製程式碼

非靜態內部類造成記憶體洩露還有一種情況就是使用Thread或者AsyncTask

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模擬相應耗時邏輯
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


複製程式碼

這種方式新建的子執行緒Thread和AsyncTask都是匿名內部類物件,預設就隱式的持有外部Activity的引用,導致Activity記憶體洩露。

4、未取消註冊或回撥導致記憶體洩露

  • 在Activity中註冊廣播,如果在Activity銷燬後不取消註冊

5、Timer和TimerTask導致記憶體洩露

  • Timer和TimerTask在Android中通常會被用來做一些計時或迴圈任務

6、集合中的物件未清理造成記憶體洩露

如果一個物件放入到ArrayList、HashMap等集合中,這個集合就會持有該物件的引用。當我們不再需要這個物件時,也並沒有將它從集合中移除,這樣只要集合還在使用(而此物件已經無用了),這個物件就造成了記憶體洩露。並且如果集合被靜態引用的話,集合裡面那些沒有用的物件更會造成記憶體洩露了。所以在使用集合時要及時將不用的物件從集合remove,或者clear集合,以避免記憶體洩漏。

7、資源未關閉或釋放導致記憶體洩露

在使用IO、File流或者Sqlite、Cursor等資源時要及時關閉。這些資源在進行讀寫操作時通常都使用了緩衝,如果及時不關閉,這些緩衝物件就會一直被佔用而得不到釋放,以致發生記憶體洩露。因此我們在不需要使用它們的時候就及時關閉,以便緩衝能及時得到釋放,從而避免記憶體洩露。

8、屬性動畫造成記憶體洩露

動畫同樣是一個耗時任務,比如在Activity中啟動了屬性動畫(ObjectAnimator),但是在銷燬的時候,沒有呼叫cancle方法,雖然我們看不到動畫了,但是這個動畫依然會不斷地播放下去,動畫引用所在的控制元件,所在的控制元件引用Activity,這就造成Activity無法正常釋放。因此同樣要在Activity銷燬的時候cancel掉屬性動畫,避免發生記憶體洩漏。

@Override
protected void onDestroy() {
    super.onDestroy();
    mAnimator.cancel();
}
複製程式碼

9、WebView造成記憶體洩露

關於WebView的記憶體洩露,因為WebView在載入網頁後會長期佔用記憶體而不能被釋放,因此我們在Activity銷燬後要呼叫它的destory()方法來銷燬它以釋放記憶體。

@Override
protected void onDestroy() {
    super.onDestroy();
    // 先從父控制元件中移除WebView
    mWebViewContainer.removeView(mWebView);
    mWebView.stopLoading();
    mWebView.getSettings().setJavaScriptEnabled(false);
    mWebView.clearHistory();
    mWebView.removeAllViews();
    mWebView.destroy();
}

複製程式碼

10、總結

1).資源物件沒關閉造成的記憶體洩漏 2).構造Adapter時,沒有使用快取的convertView 3).Bitmap物件不在使用時呼叫recycle()釋放記憶體 4).試著使用關於application的context來替代和activity相關的context 5).註冊沒取消造成的記憶體洩漏 6).集合中物件沒清理造成的記憶體洩漏

查詢記憶體洩漏可以使用Android Stdio 自帶的Android Profiler工具,也可以使用Square產品的LeadCanary.

12、android 螢幕適配

  • 在 XML 佈局檔案中指定尺寸時使用 wrap_content、match_parent 或 dp 單位 。
  • 不要在應用程式碼中使用硬編碼的畫素值
  • 不要使用 AbsoluteLayout(已棄用)
  • 為不同螢幕密度提供替代點陣圖可繪製物件

image

支援多種螢幕

面向多種螢幕的設計

Android 螢幕適配:最全面的解決方案

13、HybridJAVA 與JS互動

  • Android 調 JS : 1、java
webView.loadUrl("javascript:javacalljs()");
複製程式碼

  2、Html

image

**注意:**考慮有返回值情況;Android在4.4之前並沒有提供直接呼叫js函式並獲取值的方法,所以在此之前,常用的思路是 java呼叫js方法,js方法執行完畢,再次呼叫java程式碼將值返回。

// 4.4之後 java程式碼時用evaluateJavascript方法呼叫
private void testEvaluateJavascript(WebView webView) {
  webView.evaluateJavascript("getGreetings()", new ValueCallback<String>() {

  @Override
  public void onReceiveValue(String value) {
      Log.i(LOGTAG, "onReceiveValue value=" + value);
  }});
}
複製程式碼
  • JS 調 Android : 1、java
webView.addJavascriptInterface(MainActivity.this,"android");
複製程式碼

  2、html

<body>
HTML 內容顯示 <br/>
<h1><div id="content">內容顯示</div></h1><br/>
<input type="button"  value="點選呼叫java程式碼" onclick="window.android.startFunction()" /><br/>
<input type="button"  value="點選呼叫java程式碼並傳遞引數" onclick="window.android.startFunction('http://blog.csdn.net/Leejizhou')"  />
</body>
複製程式碼

Android中Java和JavaScript互動

14、單例模式(手寫)

//懶漢式
public class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static synchronized Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }

        return singleton;
    }
}

複製程式碼
//餓漢式
public class Singleton {
    private static final Singleton singleton = new Singleton();

    private Singleton () {

    }

    public static Singleton getSingleton() {
        return singleton;
    }
}
複製程式碼
//double-lock
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {

    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized(Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}
複製程式碼

14、ANR相關

1、什麼是ANR

在Android上,如果你的應用程式有一段時間響應不夠靈敏,系統會向使用者顯示一個對話方塊,這個對話方塊稱作應用程式無響應(ANR:Application Not Responding)對話方塊。


2、ANR產生的原因

ANR產生的根本原因是APP阻塞了UI執行緒


3、ANR產生的原因

1:UI執行緒儘量只做跟UI相關的工作,但一些複雜的UI操作,還是需要一些技巧來處理,不如你讓一個Button去setText一個10M的文字,UI肯定崩掉了,不過對於此類問題,分段載入貌似是最好的方法了。 2:讓耗時的工作(比如資料庫操作,I/O,連線網路或者別的有可能阻礙UI執行緒的操作)把它放入單獨的執行緒處理。 3:儘量用Handler來處理UIthread和別的thread之間的互動。


ANR完全解析

15、SharedPreference相關

  • SharedPreference三種獲得方法和區別:
1、 Activity類中的getPreferences(int mode)檔案自動命名為當前活動的類名。
2、 Context類中getSharedPreferences("fileName", int mode) 此方法可以指定檔名,mode同上。
3、PreferenceManager類中的getDefaultSharedPreferences(Context context)它只接收一個context引數。
檔案用當前應用程式的包名和PreferenceActivity一起來命名。屬於整個應用程式
複製程式碼

SharedPreference獲得方法對比

  • commit和apply的區別:
    1. apply沒有返回值而commit返回boolean表明修改是否提交成功。
    2. apply是將修改資料原子提交到記憶體, 而後非同步真正提交到硬體磁碟, 而commit是同步的提交到硬體磁碟,因此,在多個併發的提交commit的時候,他們會等待正在處理的commit儲存到磁碟後在操作,從而降低了效率。而apply只是原子的提交到內容,後面有呼叫apply的函式的將會直接覆蓋前面的記憶體資料,這樣從一定程度上提高了很多效率。
    3. apply方法不會提示任何失敗的提示。 由於在一個程式中,sharedPreference是單例項,一般不會出現併發衝突,如果對提交的結果不關心的話,建議使用apply,當然需要確保提交成功且有後續操作的話,還是需要用commit的。

16、View,SurfaceView,GLSurfaceView的關係和區別:

  • View:顯示檢視,內建畫布,提供圖形繪製函式、觸屏事件、按鍵事件函式等;必須在UI主執行緒內更新畫面,速度較慢。
  • SurfaceView:基於view檢視進行擴充的檢視類,更適合2D遊戲的開發;是view的子類,類似使用雙緩機制,在新的執行緒中更新畫面所以重新整理介面速度比view快。
  • GLSurfaceView:基於SurfaceView檢視再次進行擴充的檢視類,專用於3D遊戲開發的檢視;是SurfaceView的子類,openGL專用。

17、其他

1、Android 中序列化有哪些方式?區別?

  • Serializable(Java自帶): Serializable是序列化的意思,表示將一個物件轉換成可儲存或可傳輸的狀態。序列化後的物件可以在網路上進行傳輸,也可以儲存到本地。
  • Parcelable(android 專用): 不過不同於將物件進行序列化,Parcelable方式的實現原理是將一個完整的物件進行分解, 而分解後的每一部分都是Intent所支援的資料型別
  • 區別:
    • Parcelable比Serializable效能高,所以應用內傳遞資料推薦使用Parcelable
    • Serializable程式碼量少,寫起來方便,缺點是使用了反射,序列化的過程較慢。這種機制會在序列化的時候建立許多的臨時物件,容易觸發垃圾回收。

2、glide 原始碼

3、Android中程式的級別,以及各自的區別。

  • 1、前臺程式

使用者當前正在做的事情需要這個程式。如果滿足下面的條件之一,一個程式就被認為是前臺程式: 1).這個程式擁有一個正在與使用者互動的Activity(這個Activity的onResume()方法被呼叫)。 2).這個程式擁有一個繫結到正在與使用者互動的activity上的Service。 3).這個程式擁有一個前臺執行的Service(service呼叫了方法startForeground()). 4).這個程式擁有一個正在執行其任何一個生命週期回撥方法(onCreate(),onStart(),或onDestroy())的Service。 5).這個程式擁有正在執行其onReceive()方法的BroadcastReceiver。 通常,在任何時間點,只有很少的前臺程式存在。它們只有在達到無法調合的矛盾時才會被殺--如記憶體太小而不能繼續執行時。通常,到了這時,裝置就達到了一個記憶體分頁排程狀態,所以需要殺一些前臺程式來保證使用者介面的反應.

  • 2、可見程式

一個程式不擁有執行於前臺的元件,但是依然能影響使用者所見。滿足下列條件時,程式即為可見: 這個程式擁有一個不在前臺但仍可見的Activity(它的onPause()方法被呼叫)。當一個前臺activity啟動一個對話方塊時,就出了這種情況。

  • 3、服務程式

一個可見程式被認為是極其重要的。並且,除非只有殺掉它才可以保證所有前臺程式的執行,否則是不能動它的。 這個程式擁有一個繫結到可見activity的Service。 一個程式不在上述兩種之內,但它執行著一個被startService()所啟動的service。 儘管一個服務程式不直接影響使用者所見,但是它們通常做一些使用者關心的事情(比如播放音樂或下載資料),所以系統不到前臺程式和可見程式活不下去時不會殺它。

  • 4、後臺程式

一個程式擁有一個當前不可見的activity(activity的onStop()方法被呼叫)。

  • 5、空程式

一個程式不擁有任何active元件。

4、執行緒池的相關知識。

Android中的執行緒池都是之間或間接通過配置ThreadPoolExecutor來實現不同特性的執行緒池.Android中最常見的四類具有不同特性的執行緒池分別為FixThreadPool、CachedThreadPool、SingleThreadPool、ScheduleThreadExecutor.

  • **FixThreadPool: **只有核心執行緒,並且數量固定的,也不會被回收,所有執行緒都活動時,因為佇列沒有限制大小,新任務會等待執行. 優點:更快的響應外界請求.

  • **SingleThreadPool:**只有一個核心執行緒,確保所有的任務都在同一執行緒中按順序完成.因此不需要處理執行緒同步的問題.

  • **CachedThreadPool:**只有非核心執行緒,最大執行緒數非常大,所有執行緒都活動時,會為新任務建立新執行緒,否則會利用空閒執行緒(60s空閒時間,過了就會被回收,所以執行緒池中有0個執行緒的可能)處理任務. 優點:任何任務都會被立即執行(任務佇列SynchronousQueue相當於一個空集合);比較適合執行大量的耗時較少的任務.

  • **ScheduleThreadExecutor:**核心執行緒數固定,非核心執行緒(閒著沒活幹會被立即回收)數沒有限制. 優點:執行定時任務以及有固定週期的重複任務

Android開發之執行緒池使用總結

5、怎樣計算一張圖片的大小,載入bitmap過程(怎樣保證不產生記憶體溢位),二級快取,LRUCache演算法。

1、計算一張圖片的大小

圖片佔用記憶體的計算公式:圖片高度 * 圖片寬度 * 一個畫素佔用的記憶體大小.所以,計算圖片佔用記憶體大小的時候,要考慮圖片所在的目錄跟裝置密度,這兩個因素其實影響的是圖片的高寬,android會對圖片進行拉昇跟壓縮。

2、載入bitmap過程(怎樣保證不產生記憶體溢位)

由於Android對圖片使用記憶體有限制,若是載入幾兆的大圖片便記憶體溢位。
Bitmap會將圖片的所有畫素(即長x寬)載入到記憶體中,如果圖片解析度過大,會直接導致記憶體OOM,只有在BitmapFactory載入圖片時使用BitmapFactory.Options對相關引數進行配置來減少載入的畫素。

3、BitmapFactory.Options相關引數詳解

(1).Options.inPreferredConfig值來降低記憶體消耗。
  比如:預設值ARGB_8888改為RGB_565,節約一半記憶體。
(2).設定Options.inSampleSize 縮放比例,對大圖片進行壓縮 。
(3).設定Options.inPurgeable和inInputShareable:讓系統能及時回 收記憶體。
  A:inPurgeable:設定為True時,表示系統記憶體不足時可以被回 收,設定為False時,表示不能被回收。
  B:inInputShareable:設定是否深拷貝,與inPurgeable結合使用,inPurgeable為false時,該引數無意義。

(4).使用decodeStream代替其他方法。
decodeResource,setImageResource,setImageBitmap等方法


複製程式碼

**LRUCache演算法:**內部存在一個LinkedHashMap和maxSize,把最近使用的物件用強引用儲存在 LinkedHashMap中,給出來put和get方法,每次put圖片時計算快取中所有圖片總大小,跟maxSize進行比較,大於maxSize,就將最久新增的圖片移除;反之小於maxSize就新增進來。(400行程式碼不到...)

6、layout、Merge、ViewStub的作用。

  • merge: 它可以刪減多餘的層級,優化UI。例如你的主佈局檔案是垂直佈局,引入了一個垂直佈局的include,這是如果include佈局使用的LinearLayout就沒意義了,使用的話反而減慢你的UI表現。這時可以使用標籤優化。

  • ViewStub: 標籤最大的優點是當你需要時才會載入,使用他並不會影響UI初始化時的效能。各種不常用的佈局想進度條、顯示錯誤訊息等可以使用標籤,以減少記憶體使用量,加快渲染速度。是一個不可見的,大小為0的View。

相關文章