android常用的必備基礎知識

首先從四大組件說起: 
Activity: 
生命週期: 
activity三種狀態:運行(運行在最前端)、停止(不可見,完全被覆蓋)、暫停(可見,但前端還有其他activity) 
生命週期相關的方法:onCreate-onStart-onResume-onPause-onStop-onDestory-onRestart 
切換時如果要保存數據, 可以重寫: onSaveInstanceState(); 
恢復數據時, 重寫: onRestoreInstanceState(); 
啓動模式: 
1.standard:默認的,每次調用startActivity()啓動時都會創建一個新的activity放在棧頂 
2.singleTop:啓動Activity時,指定Activity不在任務棧棧頂就創建,如在棧頂,則不會創建,會調用onNewInstance(),複用已經存在的實例。 
3.singleTask:在任務棧裏面只允許一個實例,如果啓動activity不存在就創建,如果存在直接跳轉到指定Activity所在位置。 
4.singleInstance:開啓一個新的任務棧來存放這個Activity的實例,此模式開啓的Activity是運行在自己單獨的任務棧中的。

2.BroadcastReceiver廣播接收者 
有序廣播用過調用abortBroadcast()方法來中斷,接收者之間可以傳遞數據 
動態註冊廣播register,取消unregister 
廣播接收者打開Activity,需要設置一下Intent.FLAG_ACTIVITY_NEW_TASK 
應爲廣播接收者是沒有Activity任務棧的 
sendOrderBroadcast()發送有序廣播 
1)靜態註冊:在AndroidManifest.xml註冊,android不能自動銷燬廣播接收器,也就是說當應用程序關閉後,還是會接收廣播。 
2)動態註冊:在代碼中通過registerReceiver()手工註冊.當程序關閉時,該接收器也會隨之銷燬。當然,也可手工調用unregisterReceiver()進行銷燬。

3.Service服務 
Service中的生命週期方法(Context調用執行): 
1)startService() 如果沒創建就先onCreate()再startCommand(), 如果已創建就只執行startCommand(); 
2)stopService() 執行onDestroy() 
3)bindService() 如果沒有創建就先onCreate()再onBind() 
4)unbindService() 如果服務是在綁定時啓動的, 先執行onUnbind()再執行onDestroy(). 如果服務在綁定前已啓動, 那麼只執行onUnbind(); 
3、開啓服務的2種方式 
2種不同開啓方式的區別: 
1)startService: 
開啓服務,服務一旦開啓,就長期就後臺運行,即使調用者退出來,服務還會長期運行; 
資源不足時,被殺死,資源足夠時,又會復活; 
2)bindService: 
綁定服務,綁定服務的生命週期會跟調用者關聯起來,調用者退出,服務也會跟着銷燬; 
通過綁定服務,可以間接的調用服務裏面的方法(onBind返回IBinder); 
4、服務混合調用生命週期 
一般的調用順序: 
①、start -> stop 開啓 –> 結束 
②、bind -> unbind 綁定(服務開啓) -> 解綁(服務結束) 
混合調用: 
①、start –> bind -> stop->unbind->ondestroy 通常不會使用這種模式 
開啓 –> 綁定 –> 結束(服務停不了)->解除綁定(服務纔可停掉) 
②、start –> bind -> unbind -> stop 經常使用 
開啓 –> 綁定 –> 解綁(服務繼續運行)-> stop(不用時,再停止服務) 
這樣保證了服務長期後臺運行,又可以調用服務中的方法

4、內容提供者 
這個問的非常少,但是一旦問道還是要知道怎麼回事的。 
ContentProvider: 
數據庫中有對應的增刪改查的方法,如果要讓別的應用程序訪問,需要有一個路徑uri 
通過content:路徑對外暴露,uri寫法:content://主機名/表名 
1)ContentProvider:內容提供者 
把一個應用程序的私有數據(如數據庫)信息暴露給別的應用程序,讓別的應用程序可以訪問; 
在數據庫中有對應的增刪改查的方法,如果要讓別的應用程序訪問,需要有一個路徑uri: 
通過content:// 路徑對外暴露,uri寫法:content://主機名/表名 
2)ContentResolver:內容解析者 
根據內容提供者的路徑,對數據進行操作(crud); 
3)ContentObserver:內容觀察者 
可以理解成android系統包裝好的回調,數據發送變化時,會執行回調中的方法; 
ContentResolver發送通知,ContentObserver監聽通知; 
當A的數據發生變化的時候,A就會顯示的通知一個內容觀察者,不指定觀察者,就會發消息給一個路徑 
ContentProvider和sql的實現上的區別: 
1,Contentprovider屏蔽了數據存儲的細節,內部實現對用戶完全透明,用戶只需要i、關心操作數據的uri就可以了,ContentProvider可以實現不同app之間的共享 
2,Sql也有增刪改查的方法,但是sql只能查詢本應用下的數據庫。而Comtentprovider還可以去增刪改查本地文件.xml文件的讀取等

異步加載網絡數據(AsyncTask) 
AsyncTask類,這個類中的任務是運行在後臺線程中的,並可以將結果放在UI線程中去處理 
它定義了三種泛型,分別是Params、Progress和Result,分別表示請求的參數、任務的進度和獲取的結果數據 
執行過程:1.onPreExecte():執行在主線程。這步操作是用於準備好任務的,作爲任務加載的準備工作。建議在這個方法中彈出一個提示框。 
2.doInBackground():執行在子線程中。需要將請求的參數傳遞進來,發送給服務器,並將獲取到的數據返回,數據回傳給下一步的onProgressUpdate()中進行進度更新。 
3.onProgressUpdate():進度更新 
4.onPostExectue():界面更新 
實現原理: 
1.線程池的創建:默認創建一個線程池ThreadPoolExecutor,並默認創建出5個線程放入到線程池中,最多可放128個線程,且這個線程池是公共的唯一一份。 
2.任務的執行:執行run方法,執行完run方法後,會調用scheduleNext()不斷的從雙端隊列中輪詢,獲取下一個任務並繼續放到一個子線程中執行,直到異步任務執行完畢。 
3.消息的處理:在AsyncTask中維護了一個InternalHandler的類,這個類是繼承Handler的,獲取的數據是通過handler進行處理和發送的。其中handleMessage方法中,將消息傳遞給onProgressUpdate()進行進度的更新,也可以將結果發送到主線程中,進行界面的更新了。

LisView優化: 
ListView如何提高其效率 
1.複用ContentView 
2.自定義靜態類viewholder 
3.使用分頁加載 
4.使用weakReference引用ImageView對象 
listView可以顯示多種類型的條目: 
Listview顯示的每個條目都是通過baseAdapter的getView來展示的,理論上我們完全可以讓每個條目都是不同類型的view,除此之外adapter還提供了getViewTypeCount()和getItemViewType(int position)兩個方法。在getview方法中我們可以根據不同的viewtype加載不同的佈局文件。

ListView中的數據分批及分頁加載: 
設置ListView的滾動監聽器:setOnScrollListener 
1.在監聽器中有兩個方法:滾動狀態發生變化的方法onScrollStateChanged和listView被滾動時調用的方法 
2.在滾動狀態發生改變的方法有三個狀態:觸摸滑動、慣性滾動、靜止狀態 
3.對不同的狀態進行處理:分批加載數據,只關心靜止狀態:如果最後一個可見條目就是數據適

配器裏的最後一個,此時可加載更多的數據。

ListView圖片優化 
1.不要直接拿路徑去循環decodeFile();使用Option保存圖片大小,不要加載圖片到內存 
2.拿到圖片一定要經過邊界壓縮 
3.在listView去圖片是也不要直接拿個路徑去取圖片,而是以WeakReference代替強引用 
4.在getView中做圖片轉換時,產生的中間變量一定及時釋放 
異步加載圖片基本思想: 
1.先從內存緩存中獲取圖片顯示(內存緩衝) 
2.獲取不到的話從SD卡里獲取(SD卡緩存) 
3.都獲取不到的話從網絡下載圖片並保存到SD卡同時加入內存並顯示 
圖片錯位問題: 
本質是因爲listView使用了緩存convertView。可見則顯示,不可見則不顯示。在imageLoader裏有個imageViews的map對象,用於保存當前顯示區域圖像對應的url集,在顯示前判斷處理一下即可。 
內存緩衝機制: 
首先限制內存圖片緩衝的堆內存大小,每次有圖片往緩存里加時判斷是否超過限制大小,超過的話取最少使用的圖片並將其移除 
二級緩存:從LinkedHashMap裏移除的緩存放在SoftReference裏 
LinkedHashMap緩存在沒有移除出去之前是不會被GC回收的,而SoftRefernce裏的圖片緩存在沒有其他引用保存時隨時都會被GC回收。 
ListView的其他優化: 
1.儘量避免在BaseAdapter中使用static來定義全局靜態變量 
2.儘量使用getApplicationContext 
3.儘量避免在ListView適配器中使用線程

ScrollView和ListView的衝突問題: 
在ScrollView添加ListView會導致listView控件顯示不全,兩個控件的滾動事件衝突導致。通過listView中的item數量去計算listView的顯示高度。

熟悉XML/Json解析數據,以及數據存儲方式 
數據存儲方式包括:File、SharedPreference、XML/Json、數據庫、網絡 
XML/Json解析數據

Handler機制和事件分發機制 
Message:消息,由MessageQueue統一列隊,終由Handler處理 
Handler:處理者,負責Message發送消息及處理。Handler通過與Looper進行溝通,從而使用Handler時,需要實現handlerMessage方法來對特定的Message進行處理 
MessageQuene:消息隊列,用來存放Handler發送過來的消息,按照先入先出規則執行。 
Looper:消息泵,不斷從MessageQueue中取出Message執行。因此,一個線程中的MessaeQueue需要一個Looper進行管理。 
耗時操作,比如網絡請求、文件處理、多媒體處理需要在子線程中操作,手機顯示的刷新頻率60Hz,一秒鐘刷新60次,沒16.7毫秒刷新一次,爲了不丟幀,主線程處理代碼最好不要超過16毫秒。 
Handler消息機制 
在主線程中 Android 默認已經調用了 Looper.preper()方法,調用該方法的目的是在 Looper 中創建MessageQueue 成員變量並把 Looper 對象綁定到當前線程中。當調用 Handler 的 sendMessage(對象)方法的時 
候就將 Message 對象添加到了 Looper 創建的 MessageQueue 隊列中,同時給 Message 指定了 target 對象,其實這個 target 對象就是 Handler 對象。主線程默認執行了 Looper.looper()方法,該方法從 Looper 的成員變量MessageQueue 中取出 Message,然後調用 Message 的 target 對象的 handleMessage()方法。這樣就完成了整個消息機制。

事件分發機制 
onTouch和onTouchEvent 
這兩個方法都是在View的dispathTouchEvent中調用的,onTouch優先於onTouchEvent執行,在onTouch中返回true將事件消費掉,onTouchEvent將不會再執行。 
依次下發,下發的過程是調用View的dispatchTouchEvent方法實現的。簡單來說,就是viewgroup遍歷包含者的子view,調用每個View的dispatchTouchEvent方法,而當子view爲viewgroup時,又會調用viewgroup的dispatchTouchEvent方法繼續調用內部的view的dispatchTouchEvent方法。 
touch事件分發有兩個主角:viewGroup和view。 
viewgroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent 
view包含dispatchTouchEvent、onTouchEvent兩個 
onInterceptTouchEvent有兩個作用:1.攔截down事件的分發 2.中止Up和move事件向目標view傳遞,使得目標view所在的viewgroup捕獲up和move事件 
觸摸事件是有action_Down、Action_Move、Action_Up組成,其中一次完整的觸 
摸事件中,Down和Up都只有一個,Move有若干個,可以爲0個。

自定義控件 
1.自定義組合控件 
聲明一個view對象,繼承相對佈局,或者線性佈局或者其他的viewGroup 
在自定義的view對象裏面重寫它的構造方法,在構造方法裏面把佈局初始化 
根據業務需要添加一些api方法,擴展自定義組合控件 
在佈局文件裏面可以自定義一些屬性 
聲明自定義屬性的命名空間 
在res目錄下的values目錄下創建attrs.xml的文件聲明我們寫的屬性 
在佈局文件中寫自定義的屬性 
使用自定義的屬性 
2.view的繪製過程 
mesarue()過程:爲整個view樹計算實際的大小 
view : mMeasuredHeight高和mMeasureWidth()寬 
viewGroup:重寫onMeasure()方法,遍歷measure()過程,通過調用父類ViewGroup類裏面的measureChildWithMargins()方法去實現 
layout()根據子視圖的大小以及佈局參數將view樹放到合適的位置上 
view: 設置該view視圖位於父視圖的座標抽即mLeft,mTop,mLeft,mBottom,回調onLayout方法 
viewGroup:遍歷每個子視圖childView,調用該子視圖的layout()方法去設置它的座標值 
3.draw()繪製過程 
viewRoot對象的performTraversals()方法調用draw()方法發起繪製view樹不需要全部重繪只需要繪製需要繪製的部分

這個控件中,父視圖使用unspecified dimensions來將它的每個子視圖都測量一次來算出它們到底需要多大尺寸,而這些子視圖沒被限制的尺寸的和太大或太小,所以會用精確數值再次調用measure()(也就是說,如果子視圖不滿意它們獲得的區域大小,那麼父視圖將會干涉並設置第二次測量規則)。其中measure()方法會調用onMeasure()方法。 
代碼中,由於把每行剩餘空間重新分配,會調用了requestLayout()方法,這個方法又會導致measure()和onLayout()方法的再次調用。 
最後你會發現 onMeasure()方法調用了 1次*2*2=4次 onLayout()方法調用了 1次*2 =2次

談一談android的安全機制 
1,在安卓是有文件權限的控制,在清單文件聲明 
2,每個android中每個應用都有自己的/data/data/包名 文件夾,該文件夾只能該應用訪問,而其他應用則無權訪問 
3,Android的代碼混淆保護了開發者的勞動成果

多線程斷點續傳下載 
多線程下載的實現過程: 
1,首先得到下載文件的長度,然後設置本地文件的長度。 
HttpURLConnection.getContentLength(); 
RandomAccessFile file = new RandomAccessFile(“FeiQ.exe”,”rwd”); 
file.setLength(filesize); //設置本地文件的長度 
2,根據文件長度和線程數計算每條線程下載的數據長度和下載位置。 
如:文件的長度爲6M,線程數爲3,那麼,每條線程下載的數據長度爲2M,每條線程開始下載的位置如圖所示。 
3,使用Http的Range頭字段指定每條線程從文件的什麼位置開始下載,下載到什麼位置爲止. 
如:指定從文件的2M位置開始下載,下載到位置4M爲止,代碼如下: 
HttpURLConnection.setRequestProperty(“Range”, “bytes=2097152-4194303”); 
4,保存文件 
使用RandomAccessFile類指定每條線程從本地文件的什麼位置開始寫入數據。 
RandomAccessFile threadfile = new RandomAccessFile(“QQWubiSetup.exe “,”rwd”); 
threadfile.seek(2097152); //從文件的什麼位置開始寫入數據

數據庫操作 
在Android系統,提供了一個SQLiteOpenHelper抽象類,該類用於對數據庫版本進行管理.該類中常用的方法: 
onCreate 數據庫創建時執行(第一次連接獲取數據庫對象時執行) 
onUpgrade 數據庫更新時執行(版本號改變時執行) 
onOpen 數據庫每次打開時執行(每次打開數據庫時調用,在 onCreate,onUpgrade方法之後) 
Android提供了一個名爲SQLiteDatabase的類,該類封裝了一些操作數據庫的API,使用該類可以完成對數據進行添加(Create)、查詢(Retrieve)、更新(Update)和刪除(Delete)操作(這些操作簡稱爲CRUD)。對SQLiteDatabase的學習,我們應該重點掌握execSQL()和rawQuery()方法。 execSQL()方法可以執行insert、delete、update和CREATE TABLE之類有更改行爲的SQL語句; rawQuery()方法用於執行select語句。

aidl:接口聲明語言,跨進程通信 
bindService有一個ServiceConnec接口覆寫onServiceConnect方法,把第二個參數IBinder對象強制轉換爲aidl中的接口類

- 寫服務類

    1. 定義一個接口文件, 聲明一個方法forwardPayMoney方法, 把後綴名修改爲.aidl, 並且把修飾詞去掉. 

    2. 在服務中定義一個內部類MyBinder, 繼承Stub類, 並且把抽象方法forwardPayMoney實現了.

    3. 在onBind方法中把第二部定義的內部類對象MyBinder返回.

- 另一個程序的Activity

    4. 使用隱式的方式綁定服務, 傳遞過去一個連接橋對象.

    5. 把服務程序中的aidl文件拷貝當前工程中, 包名要保留一致.

    6. 在連接橋對象中的onServiceConnected方法中, 把IBinder對象轉換成aidl接口對象

            mAlipayRemoteService = Stub.asInterface(service);

    7. 使用aidl接口對象, 調用接口中的抽象方法, 實際上會調用到遠程服務中內部類中的forwardPayMoney方法.

android中的動畫 
1.View Animation: 視圖動畫/ Frame動畫/屬性動畫:這種動畫是可擴展的,

圖片處理框架 
imageLoader:imageLoaderEngine,cache及 
imageDownloader,imageDecoder,bitmaodisplayer,bitmapProcessor五大模塊 
簡單來說就是imageLoader收到加載及顯示圖片的任務,並將它交給 
imageLoaderEngine,ImageLoaderEngine分發任務到具體線程池去執行,任務通過cache及imageDownloder獲取圖片。 
優點:1,支持下載監聽2,可以在view滾動中暫停圖片加載3.默認實現多種內存緩存算法4.支持本地緩存文件名規則定義 
picasso 
這個庫分爲dispatcher/requestHandler及Downloader,picassoDrawable等模塊 
picasso收到加載及顯示圖片的任務,創建Request並將他交給 
Dispatcher,dispatcher分發任務到具體的requesthandler,任務memoryCache及Handler獲取圖片,圖片獲取成功後通過picassoDrawable顯示到Target中 
優點:1.自帶統計監控功能2.支持優先級處理3.支持延遲加載4.支持飛行模式、併發線程數根據網絡類型而變

圖片緩存處理: 
LruCache類:主要算法原理是把最近使用的對象用強引用存儲在在LinkedHashMap中,並且把最近最少使用的對象在緩存值達到預定值之前從內存中移除。

內存溢出原因: 
1.資源釋放問題 
2.對象內存過大 
3.static 
4.線程導致內存溢出

圖片佔用進程的內存算法簡介 
android中處理圖片的基礎類是Bitmap,顧名思義,就是位圖。佔用內存的算法如下: 
圖片的width*height*Config。 
如果Config設置爲ARGB_8888,那麼上面的Config就是4。一張480*320的圖片佔用的內存就是480*320*4 byte。 
在默認情況下android進程的內存佔用量爲16M,因爲Bitmap除了Java中持有數據外,底層C++的 skia圖形庫還會持有一個SKBitmap對象,因此一般圖片佔用內存推薦大小應該不超過8M。這個可以調整,編譯源代碼時可以設置參數。

Activity的啓動與生命週期的監控 
應用程序被開啓後,是需要開啓並創建Activity,加載相應的view,從而展示出應用程序 
1、Activity是通過startActivity開啓起來的,startActivity是由有Context調用的,其具體的實現類是ContextImpl 
在ContextImpl中的startActivity方法中,會調用ActivityThread的相關方法【mMainThread.getInstrumentation().execStartActivity()】;可以追溯到Instrumentation這個類,其中的execStartActivity()的方法中實現了startActivity的調用:ActivityManagerNative.getDefault().startActivity,由此可以看出是底層進行處理。 
2、ActivityMonitor監控Activity 
當Activity實例創建的時候,就會給Activity配置一個監視器ActivityMonitor,監控Activity的聲明週期: 
在Instrumentation的execStartActivity()的方法中,上來先判斷ActivityMonitor是否爲null:在第一次開啓Activity的時候,ActivityMonitor還是null的,就會調用ActivityManagerNative.getDefault().startActivity(…..),是在操作native底層的信息,從而執行startActivity,再去開啓一個Activity。 
簡單來說,就是通過調用JNI,調用startActivity方法,開啓Activity;創建好了之後,隨即也創建好了Activity的監視器ActivityMonitor 
3、在ActivityMonitor中就有Activity各種生命週期的監控 
①、在newActivity方法中: 
可以通過拿到Activity的字節碼,創建一個Activity,並將這個Activity返回 
還會調用attach方法,傳入ActivityThread的線程 
②、在各種生命週期的方法中,調用activity的各自的生命週期的方法

總結: 
1、通過PackageManagerService將所有用到的資源加載進內存中 
2、在Launcher中,將view等控件加載到ViewGroup中,點擊每個item會有相應的操作 
3、在公開的文檔中是找不到具體調用startActivity的類的,而是由系統完成調用的,實現了Activity的啓動
實際就是通過Context的實現類ContextImpl進行調用的,一步步轉到底層(ActivityManagerNative)實現調用 
4、另一個重要的類就是ActivityMonitor,監控Activity生命週期的;在其newActivity方法中創建了Activity,並調用了attach方法; 
也就是說當一個Activity被創建的時候,就會綁定一個ActivityMonitor,用來監控Activity的生命週期

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章