Android 學習札記

Activity

啓動模式:使用標籤android:launchMode進行設置 即android:launchMode=["multiple" | "singleTop" | "singleTask" | "singleInstance"]

standardsingleTop可以被實例化多次,屬於不同的task,可位於activity棧的任何位置。一般使用方法startActivity()來啓動,如果intent對象包含FLAG_ACTIVITY_NEW_TASK,在這情況下可以選擇不同task。standard是默認模式,不需要配置launchMode。它們之間的區別還在於standard啓動時始終都會創建一個新的實例,singleTop也會將intent發送新的實例。不過,如果activity棧頂已經有了要創建的Activity的實例,則將intent發送給該實例(通過onNewIntent() 調用),而不會創建新的實例。如果已經存在實例但不是在棧頂或者在別的task的棧頂,則會創建新的實例。singleTop模式,可用來解決棧頂多個重複相同的Activity的問題。換言之,standard可以有多個相同的實例,且可以疊加,singleTop可以有多個實例,但是不允許多個相同Activity疊加。singleTop適合用於接收通知欄啓動顯示頁面的情況

singleTask模式和singleInstance模式在新task中創建一個實例,如果實例已經存在則將intent發送給該實例(通過onNewIntent() 調用),而不會創建新的實例。只能屬於一個Task,總是位於activity棧頂,同一時間內只能保持一個實例。區別在於singleTask允許在task中存在別的activity("standard" and "singleTop"activities) 而singleInstance則獨佔一個task,如果啓動別的activity,則會分配在另外的task裏(相當於使用intent裏的FLAG_ACTIVITY_NEW_TASK來啓動activity,不過仍然要檢查是否存在和這個activity相同taskAffinity值的Task) (另外singleInstance模式下onActivityResult將會失去作用,它的resultCode會直接返回Activity.RESULT_CANCELED)

Task

Task可以認爲是一個棧,可放入多個Activity。比如啓動一個應用,那麼Android就創建了一個Task,然後啓動這個應用的入口Activity,就是intent-filter中配置爲main和launch的那個(利用該屬性可實現一個APK文件部署產生多個應用安裝的效果)。這個Activity是根(Root)Activity,可能會在它的界面調用其他Activity,這些Activity如果按照啓動模式,intent的參數以及taskAffinity屬性的不同,可能在這個Task中,也可能在另外的Task棧中。比如地圖app,用戶看過地圖按回退鍵的時候,會退棧回到調用地圖的Activity。對用戶來說,並不覺得在操作多個應用。這就是Task的作用。所以特別地,singleTask用於一個task中共享一個Activity的場合singleInstance用於多個Task共享一個Activity的場合(最典型的場合就是不同的應用程序調用地圖app的導航功能,這個地圖導航界面實際上同一個activity,另外鬧鐘程序基本上也是基於這個模式,保證activity的獨立唯一性)。standardsingleTop則適用於大多數activity的場合

taskAffinity屬性

用於指定activity的Task歸屬,即出了它希望進入的Task,屬性值相同的activity屬於同一個Task,Task的taskAffinity值由這個Task的root activity的taskAffinity值決定。默認情況下app的所有activity都有相同的taskAffinity值。利用這個值可以讓不同的應用程序使用相同的Task。如果設置爲空字符串則activity不屬於任何Task。如果不設置,則activity繼承application的taskAffinity的值,默認值就是<manifest>標籤的包名。

allowTaskReparenting屬性

通常情況下,一個activity啓動後,和它相關聯的Task也就啓動,並且它的生命週期將會在這個Task中完成。使用這個屬性值可以強制一個activity在不可見時重新宿主到另外一個具有相同affinity值的Task中。即當一個activity的這個屬性設置爲true時,它進入後臺後,如果有一個和它有相同taskAffinity值的Task進入前臺時,它會重新宿主到該task中並顯示出來。由於啓動模式"singleTask" 和"singleInstance" 只能存在於一個Task中,因此重新宿主就只侷限於"standard" 和"singleTop" 模式。

應用場合舉例:電子郵件信息包含一個網頁的鏈接,點擊鏈接後顯示該頁面,這個頁面是由browser應用定義的,但是卻成爲電子郵件task的一部分。所以當瀏覽器task處於激活狀態,這個activity應該重新宿主到browser應用並顯示出來,而且重新啓動電子郵件時,它不應該再出現

Handler和android線程

Message

用於描述一個消息體,包含兩個整型域和一個對象域,可以通過handler來發送。創建時最好使用Mesage.obtain()或Handler.obtainMessage()。

MessageQueue

底層保存Message的隊列(以鏈表的方式串聯起來的),從屬於特定Looper(在Looper的構造過程中會去創建一個MessageQueue),通過Looper來調度。Message不是以直接的方式添加到MessageQueue中,而是通過和Looper相關聯的Handler來實現消息進入隊列。

Looper
線程的消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。默認情況下創建的線程不會自動創建Looper和MessageQueue,如果想讓該線程具有消息隊列和消息循環,需要調用prepare()來創建,然後運行loop(),來實現消息循環直到loop被停止(loop方法內部是個死循環)。

通過Loop.myLooper()得到當前線程的Looper對象,通過Loop.getMainLooper()可以獲得當前進程的主線程的 Looper對象

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class LooperThread extends Thread {  
  2.          public Handler mHandler;  
  3.          public void run() {   
  4.             Looper.prepare();  
  5.             mHandler = new Handler() {   
  6.                           public void handleMessage(Message msg) {   
  7.                                  // process incoming messages here   
  8.                           }   
  9.              };    
  10.             Looper.loop(); //讓Looper開始工作,從消息隊列裏取消息,處理消息  
  11.          }   
  12. }  

總結:Looper是android線程消息循環機制的封裝 ,消息循環是一個很有用的線程方式,往消息隊列裏添加數據(通過Handler添加),然後異步的等待消息的返回。當消息隊列爲空的時候就會掛起線程,等待新的消息的加入。當需要子線程不定期的執行一些不需要和應用界面交互的的任務時,就需要靠Loop來讓線程掛起等待消息,這種情況最好用HandlerThread來創建這個工作線程。

Handler
handler 可以發送和處理消息以及Runnable對象,每個handler實例與一個單獨線程以及線程的MessageQueue關聯。實際上是和線程的Looper對象關聯。只要線程有Looper就可以通過handler往消息隊列里加入消息。
主要用途1)定時發送message和Runnable對象2)在不同的線程中處理並執行一個action。

Handler的作用就是把消息加入Looper的MessageQueue中,並分發和處理該消息隊列中的消息。構造Handler的時候可以指定一個Looper對象,如果不指定則利用當前線程的Looper(實際上是通過Looper.myLooper()來獲取當前線程中的消息循環)。比如Handler對象以主線程的Looper創建,那麼調用 Handler的sendMessage等接口,將會把消息放入主線程的消息隊列。並且將會在Handler主線程中調用該handler 的handleMessage接口來處理消息。這樣在主線程中就可以更新UI了。

常見異常1)ViewRootImpl實現類中的方法checkThread拋出的非UI線程更新UI異常;2)子線程創建handler時沒有指定Looper對象,一種是子線程沒有新建Looper,另一種是由於線程併發導致還沒有構造出來就使用Looper對象。

總結:handler是android提供用來更新UI的一套機制,也是消息處理機制,可以發送消息,處理消息。(即android封裝的消息創建傳遞,處理機制),這種機制是爲了解決多線程併發更新UI時需要同步的問題,如使用加鎖則會引起系性能下降。

HandlerThread

創建時包含loop的線程。應用程序可能包含多個線程,並使用handler實現線程間通信,默認情況線程沒有消息循環,需要通過Looper.prepare()和,Looper.loop()來實現。但線程併發時,有可能Looper對象還未創建好就被使用,從而引發異常。這是可以通過使用HandlerThread來解決。通過源碼可知,HandlerThread的ru()方法中創建了Looper並進行加鎖處理。 換言之,這個類對Java的Thread做了很多便利Android系統的封裝。

使用步驟1)實例化一個HanderThread(即創建一個含loop的線程);2)獲取該線程的looper,使用方法getLooper();;3)使用該Looper對象創建一個Handler對象;最後即可使用該Handler對象和該線程通信。還要記住不需要該線程時使用quit()方法來退出該線程。

android 線程

Activity的UI線程,在ActivityThread類中啓動,會創建Looper(在public static void main()中創建),所以在Activity中創建的Handler默認是關聯了UI線程的Looper。

新建的工作線程,默認是不創建Looper的,需要自己的run()方法中創建,如果需要用Handler來接收別的線程發送的消息的話(這種情況最好用HandlerThread來新建工作線程)。

非UI線程更新UI:

1)UI更新時,會通過接口ViewParent的實現類ViewRootImpl來判斷是否是UI線程在進行更新UI的操作。實現接口的方法invalidateChild().該方法最終會有checkThread的行爲。如果不是UI線程,則拋出“Only the original thread that created a view hierarchy can touch its views.”

2)ViewRootImpl的初始化實在Activity的回調onResume中完成的。在onResume中需要將各類顯示控件附着在window上,用的是接口ViewManager的addView方法
該接口的實現類是WindowManagerGlobal,值得注意的是該類的實例是通過單例模式來獲取,這也很容易理解,在整個系統中,對window的管理和控制只需要一個對象。這個單例對象的addView方法中對ViewRootImpl進行了初始化。
結論:在ViewRootImpl初始化之前,不會對更新UI進行線程檢查,此時非UI線程可以更新UI而不會引發異常!
Service
是一個不需要和用戶交互而運行在後頭的應用程序組件。有兩種啓動方法Context.startService() 和Context.bindService().    
1)不是一個單獨的進程,不會啓動自己的進程除非另外指定(在AndroidManifest.xml中可以使用標籤android:process來指定),否它運行在和應用相同的進程中;2)不是一個線程,不能做耗時的工作。
因此會阻塞主線程的操作應該啓動新線程來完成。IntentService是Service的標準實現類,並且有自己工作線程,使用它是個不錯的選擇。
通過Context.startService()啓動的Service,一旦啓動就在後臺運行,就算啓動它的組件已被銷燬也一樣。這種方式通常用於單任務,不需要返回結果的情況。
通過Context.bindService().啓動的Service,提供了與組件交互的接口,可以發送請求,返回結果,甚至可以跨進程通信。多個組件可以綁定一個Service(第一次綁定的時候獲取IBinder對象,之後系統將同一個IBinder對象傳遞給綁定者),綁定後才能運行,解除綁定是就會被銷燬(所有綁定都解除時,不需要顯示的去停止一個綁定的Service)
綁定時必須提供一個IBinder作爲交互的接口。
1)繼承Binder類,適用於綁定者位於同一個進程中,不需要IPC的時候;
2)使用messager,需要IPC時使用,隊列模式不支持多線程(Service和Client各種分別用各自的Handler構造一個Messenger,客戶端用IBinder對象構造另一個Messenger作爲通信的信使);
3)使用AIDL,多線程,實現較爲複雜。
標籤android:process可以指定Service所運行的進程(The name of the process where the service is to run),通常情況下一個應用程序的所有組件都運行在同一個進程下,進程名爲應用程序的包名。<application>process屬性可以給組件指定一個不同的默認進程,但是每個組件自己的這個屬性會覆蓋<application>的這樣就可以把應用程序放在多個進程中(spread your application across multiple processes.)
這個值如果以冒號“:”開頭,則在需要的時候系統會創建一個應用程序私有的新進程(Service就運行在整個進程中),如果以小寫字母開頭,則Service運行在以這個名字命名的全局進程中(如果有權限的話),這樣就允許不同應用程序的組件共享進程,提升資源使用率。

IntentService

繼承自Service,可以處理異步請求,每個Intent會啓動一個工作線程來處理,任務結束後即停止。不會阻塞主線程,但是一次只能處理一個任務,使用隊列方式管理待處理的Intent。處理完所有請求後自動停止Service(stops itself when it runs out of work.)。
使用方法:繼承IntentService並實現onHandleIntent(Intent),然後客戶端通過startService(Intent)來調用。

Broadcast
是一種可用於組件間和應用程序間傳輸信息的機制(但一般用於傳輸數據較小,傳輸頻率較低的情況),其特點是發送方和接收方不需要事先知道對方的存在,也就是實現了低耦合。
非跨進程的廣播推薦使用LocalBroadcastManager,它更加高效且安全(可以避免考慮其他應用程序使用你的廣播的情況)
新建一個類繼承BroadcastReceiver,並實現onReceive()方法。
1)靜態註冊,,在AndroidManifest中添加標籤<receiver>(註冊廣播接收器),可通過<intent-filter>來過濾監聽的廣播種類。特點是,應用程序退出後,Receiver依然能收到廣播。換言之,應用程序一檔安裝後,這個廣播接收器就處於活動狀態,通常用於監聽系統狀態的改變(低點,網絡,sim卡彈出等)
2)動態註冊,使用 registerReceiver()註冊,方法的第二個參數intentFilter可過濾廣播種類,unregisterReceiver()取消註冊,如果廣播用於更新UI那麼通常使用這種方法註冊。activity啓動時註冊,不可見時取消註冊(onResume中註冊,在onPause中註銷)。
異步方式:廣播內容放入intent對象, 然後Context.sendBroadcast()廣播出去,接收器以不確定的順序收到廣播
順序方式:Context.sendOrderedBroadcast(),一次向一個接收器發送,每個接收器順序執行,然後發個下一個,中途可以退出不再傳遞下去,使用android:priority來定義屬性優先級別。
BroadcastReceiver只在onReceive()調用期間有效(Once your code returns from this function, the system considers the object to be finished and no longer active.),所以最好不要在BroadcastReceiver中顯示對話框(可以用通知欄)或者綁定一個service(可以startService)。

dip,dp,px

dpi(dots per inch):每英寸所顯示的點數(像素),也可稱爲像素密度。
dip: device independent pixels(設備獨立像素,).不同設備有不同的顯示效果,這個和設備硬件有關,一般我們爲了支持WVGAHVGAQVGA推薦使用這個,不依賴像素。
dp等同於dip,它是一個長度單位,1dp=1/160英寸
px: pixels(像素).不同設備顯示效果相同
sp: scaledpixels(放大像素).主要用於字體顯示best for textsize
pt: point,是一個標準的長度單位,1pt1/72英寸,用於印刷業,非常簡單易用;

當densityDip=160時,1dp=1px。 px = dp*(dpi/160)

(這裏需要注意,在android系統中,這裏的dpi是歸一化後的dpi,能值只有120(low)、160(medium)、240(high)、320(xhigh)四種)。在某些情況下,這會導致顯示上的一些細微差別:

G7(HTC Desire)的屏幕尺寸是3.7英寸,分辨率是480*800,像素密度是252dpi
G10(HTC Desire HD)的屏幕尺寸是4.3英寸,分辨率爲480*800,像素密度是217dpi
假設現在有一個按鈕,它的寬度設爲100dp,由於G7和G10同被 劃分爲hdpi,那麼在這兩個設備上,這個按鈕的實際寬度是:100 * (240 / 160) = 150像素。可由於這兩個設備的實際像素密度(dpi)是不一樣的,所以真實的顯示效果是:這個按鈕在兩個設備上的實際物理尺寸(英寸數)是不一樣大的。

http://zhangkun716717-126-com.iteye.com/blog/1772696
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
基本概念

MVC

(控制器Controller)- 負責轉發請求,對請求進行處理。
(視圖View) - 圖形界面設計。
(模型Model) - 程序應有的功能(實現算法等等)、數據管理和數據庫設計(可以實現具體的功能)。

模型(Model) “數據模型”(Model)用於封裝與應用程序的業務邏輯相關的數據以及對數據的處理方法。“模型”有對數據直接訪問的權力,例如對數據庫的訪問。“模型”不依賴“視圖”和“控制器”,也就是說,模型不關心它會被如何顯示或是如何被操作。但是模型中數據的變化一般會通過一種刷新機制被公佈。爲了實現這種機制,那些用於監視此模型的視圖必須事先在此模型上註冊,從而,視圖可以瞭解在數據模型上發生的改變。(比較:觀察者模式(軟件設計模式))
視圖(View) 視圖層能夠實現數據有目的的顯示(理論上,這不是必需的)。在視圖中一般沒有程序上的邏輯。爲了實現視圖上的刷新功能,視圖需要訪問它監視的數據模型(Model),因此應該事先在被它監視的數據那裏註冊。
控制器(Controller) 控制器起到不同層面間的組織作用,用於控制應用程序的流程。它處理事件並作出響應。“事件”包括用戶的行爲和數據模型上的改變。


Android啓動過程:

bootloader-> kernel->init.rc:(內核啓動的用戶級進程)

啓動 ServiceManager守護進程 和zygote(app_main.cpp)

app_main.cpp–> Andriodruntime.start(“com.android.internal.os.ZygoteInit”);

ZygoteInit.java-> startSystemServer->SystemServer.java

SystemServer.java:main->init1(com.android.server_SystemServer.java->system.init)->int2->ServerThread(Activity manager,Package manager Window manager)


Android應用程序啓動:

虛擬機

通過軟件模擬的具有完整硬件系統功能的,運行在一個完全隔離環境中的完整計算機系統。一種抽象,一種仿真

一種系統虛擬機(VM Ware),一種進程虛擬機(JVM,.NET)

Java虛擬機(jvm):基於棧。

android虛擬機(dalvik): 基於寄存器(一次讀兩個寄存器),省指令,省cpu;沒有使用JIT,

(1)虛擬機很小,使用的空間也小;
(2)早期並沒有使用
JIT(Just-In-Time)技術. 從 Android 2.2 開始, Dalvik 虛擬機也支持 JIT
(3)常量池已被修改爲只使用32位的索引,以簡化解釋器;
(4)它使用自己的字節碼dex,而非Java字節碼。


SQLite:
使用SQLiteOpenHelper來訪問數據庫(也可以用其他方法,這個類封裝了一些方法使用起來比較方便)
,它是一個抽象類,所以必須繼承使用。
SQLiteDatabase(通過SQLiteOpenHelper來獲得它的對象),然後可以進行數據庫的增刪改查。
adb shell 進入linux命令行,進入目錄/data/data/應用程序包名 就是存放數據庫的地方。 使用命令sqlite3 "數據庫名” 可以進入數據庫執行一些數據庫操作以及sql語句調試,比如.schema(查詢當前數據庫存在的表)
數據類型:1.NULL:空值。2.INTEGER:帶符號的整型,具體取決有存入數字的範圍大小。3.REAL:浮點數字,存儲爲8-byte IEEE浮點數。4.TEXT:字符串文本。5.BLOB:二進制對象。SQLite允許忽略數據類型, 但仍然建議在的Create Table語句中指定數據類型,以增強程序的可讀性。(http://www.2cto.com/kf/201109/105639.html)

SD卡:
Environment.getExternalStorageDirectory()得到設備SD卡目錄;
android.permission.WRITE_EXTERNAL_STORAGE訪問SD卡權限;

Content Provider爲存儲和獲取數據提供統一接口,可以在不同的應用程序之間共享數據。每一個ContentProvier都擁有一個公共的URI

文件下載步驟:

新建一個HttpURLConnection對象:

HttpURLConnection urlConn=(HttpURLConnection)url.openConnection();

獲得一個InputStream對象urlConn.getInputStream()

訪問網絡的權限android.permission.INTERNET


異常:

Caused by: java lang IllegalArgumentExcpetion: The key must be an application-specific resource id

一般是由於傳入的參數(int型的id值)不是由系統自動生成的(就是位於gen文件夾中的,gen中的id值可以保證唯一性)所導致。

比如方法setTag(int key, Object tag),其中的參數key必須是由系統自動生成,不能是類的成員變量所聲明的int型變量。

解決辦法可以在strings.xml中定義一個key,通過R.string.***來引用



eclipse使用相關
1.添加多語言文件:
右鍵新建Android XML File,輸入文件名string.xml,選中Values單選框,
把左列表中的Region添加到右邊,並在輸入框裏輸入
cn
把左列表中的Language添加到右邊,並在輸入框裏輸入填入zh

 

Adb相關

cls
@echo off

adb remount  
echo rm apk
adb shell rm /system/app/Contacts.apk 
echo uninstall apk
adb uninstall com.android.contacts 
echo push apk
adb push  ./Contacts.apk /system/app/Contacts.apk 
echo install apk
adb install -r ./Contacts.apk

echo Contacts install done!

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