近期,我們根據友盟移動統計分析平臺的部分數據,對中國移動應用發展現狀進行了研究和分析,并且通過對廣大移動應用開發者的調查透視了國內APP開發者的現狀。希望能夠為移動互聯網創業者提供最有價值的參考!
2011年3月——2012年3月 TOP100應用增長趨勢
從2011年的3月份到今年的3月份,移動應用無論是活躍用戶還是日啟動次數都有了十足的增長。我們按照應用的累計安裝量作為排序標準,選取了TOP100的應用作為統計樣本,研究后發現活躍用戶和日啟動均比去年的3月份增長了5倍之多。可見越來越多的用戶開始接受并享用移動互聯網為人們生活帶來的便利。
用戶地理分布&聯網方式&運營商分布
關于中國移動互聯網用戶的地理分布,廣東、江蘇、北京、浙江和福建五省或者直轄市排在了前五名的位置,占據了全國用戶份額的40.7%。在2011年第三季度的時候,我們也發布一份數據報告,顯示用戶份額前五的省份或者直轄市是廣東、江蘇、浙江、北京和上海,占據全國用戶份額的44.6%。另外,2011年第二季度前五名省份或直轄市所占總份額是49.4%。不難看出,移動互聯網向二線城市蔓延的趨勢依然是持續并且不可逆轉的。
關于聯網方式和運營商,2G上網依然是一半上網用戶的選擇,占比51.2%。使用3G和WIFI的用戶占比分別為14.6%和34.2%。聯通和電信憑借其3G套餐和優惠購機業務,市場份額已經分別占據了20%和9.5%。
國內移動應用開發者現狀
友盟一直致力于為國內移動開發者提供最專業的服務,現在已經服務超過20000名開發者和開發團隊,為他們提供專業的統計分析、應用聯盟和開發組件產品。為了更好的服務移動互聯網創業,我們在2012年第一季度邀請了廣大移動開發者進行了一次全面的問卷調查。調查的幾個重要結論如下:
文中涉及到的實例有一部分是屬于平臺自帶的application(例如:撥號程序等),另 外也有Google產品線中的一些有代表性的應用(例如:Google Map等)。建議大家親自利用Emulator或者Android-powered device測試實例中的效果,這樣可以幫助更加清晰的理解一些模塊的含義。(注意:可能會因為硬件對于某些功能無法提供支持,所以有一些實例可能無法在 你的測試機中正常瀏覽)
以上這四個模塊對于理解這篇文章非常重要,下邊就來逐一的簡單介紹其具體的含義和用法(也可以通過其鏈接直接查看官方文檔)。
Applications
任何一個Android Application基本上是由一些Activities組 成,當用戶與應用程序交互時其所包含的部分Activities具有緊密的邏輯關系,或者各自獨立處理不同的響應。這些Activities捆綁在一起成 為了一個處理特定需求的Application, 并且以“.apk”作為后綴名存在于文件系統中。Android平臺默認下的應用程序 例如:Email、Calendar、Browser、Maps、Text Message、Contacts、Camera和Dialer等都是一個個獨立的Apps。
Activities
上邊已經提到Activities是構成Applications的主要組成部分,其實可以 更為具體的理解為Application僅僅是一個抽象的標簽,它將系統內一部分Activities關聯在一起,協同完成用戶的特定需求。安裝 Application的過程也可以簡單理解為將其所包裹的Activities導入到當前的系統中,如果系統中已經存在了相同的Activities, 那么將會自動將其關聯,而不會重復安裝相同的Activities,避免資源的浪費。Application卸載的過程也會檢查當前所關聯的 Activities是否有被其它Application標簽所關聯,如果僅僅是提供當前的Application使用,那么將會徹底被移除,相反則不做 任何操作。
用戶與Application的交互行為大部分都是通過GUI來完成,在Android平臺 可以有兩種方式定義GUI,其中可以利用XML來預置靜態的GUI元素,或者在Activity類的內部動態定義GUI元素。這兩種不同的方法都是由 Activity作為驅動和響應用戶交互事件的主體。當啟動Application之后,至少需要一個包含有GUI信息的Activity實例被創建。
Activity的主體包括兩個主要部分,其中一個是Content(data),另外一個是響應用戶交互事件的行為。列舉一個Dialer例子的截圖,其中包括四個部分:Dialer主界面、通訊錄、查看聯系人信息和添加新聯系人。
下面列舉了更多比較有代表性的Applications和其所包含的Activities:
Application基本上是由四個模塊組成:Activity、Service、Content Provider 和 Broadcast Receiver,其中Activity是實現應用的主體。
操作應用程序時,有時需要調用多個Activities來完成需求,例如:發送郵件程序,首 先是進入郵件主界面,然后啟動一個新的Activity用于填寫新郵件內容,同時可以調出聯系人列表用于插入收件人信息等等。在這個操作過程中 Android平臺有一個專門用于管理Activities堆棧的機制,其可以方便的線性記錄Activities實例,當完成某個操作時,可以通過這個 導航功能返回之前的Activity(通過按操作臺的“Back”)。
每次啟動新的Activity都將被添加到Activity Stack。用戶可以方便的返回上一個Activity直到Home Screen,到達Home Screen后,將無法再繼續查看堆棧記錄(俗話說:到頭了- Androidres.com)。如果當前Task被中止(Interrupting the task),返回到系統主界面后啟動了其它操作,當希望返回到前一個Task繼續執行時,只需要再次通過主界面的Application launcher或者快捷方式啟動這個Task的Root Activity便可返回其中止時的狀態繼續執行。
相對于Views、Windows、Menus和Dialogs而言,Activity是唯 一可被記錄在History stack中的數據,所以當你所設計的應用程序需要用戶由A界面進入到次一級界面B,當完成操作后需要再次返回A,那么必須考慮將A看作為 Activity,否則將無法從歷史堆棧中返回。
Tasks
在Android平臺上可以將Task簡單的理解為由多個Activities共同協作完成某一項應用,而不管Activities具體屬于哪個 Application。通過下邊的圖示可以更清晰的理解Applications、Tasks、Activities三者之間的關系 (Androidres.com提供):
Activities可以被看作為是獨立存在于系統資源中,而且是作為實現具體應用的主體,Task將一些Activity關聯起來實現一個更復雜的應用,單獨或者多個Tasks可以被定義為一個Application。
通常實現一個Task都會存在一個Root Activity,但并不是所有情況都如此,通過Application launcher、Home screen 的快捷方式或者 由 “Recent Tasks”(長時間按住Home鍵) 最近使用過的Task記錄中啟動。當從一個Activity中啟動另外一個Activity時,Back鍵將作用于返回前一個Activity,與此同時 新開啟的Activity將被添加到Activity Stack中。
這里有兩個被表示為Task的例子:
- Interrupting the Task
這是Task一個非常重要的特性,用戶可以實時中止當前為完成的Task,新開啟一個不同的Task,當新Task完成操作后,依然可以返回當上一 次中止的Task繼續完成余下操作。這個特性大大方便了同時運行多個Tasks,并且可以方便的在他們之間切換。這里有兩種方式可以從當前Task跳轉為 其它Task(應用這兩種方式切換Task,都允許返回到Task最初中止前的狀態)。
當然,除了這兩種方式以外,還有另外一個特殊情況,算作為第三種方式來啟動一個新的Task:Activity本身被定義為一個Task。例如: Maps和Browser就是屬于第三種情況的Application,通過郵件中的一個地址來啟動Maps Activity作為一個新的Task,或者通過郵件中的鏈接啟動Browser來啟動一個新的Task。當處在這種情況下,Back按鍵被觸發后,將返 回到上一個Task(郵件),因為這些新的Tasks并不是通過Home Screen中的Application launcher或者快捷方式來啟動。
請大家一定首先理解之前所提及的內容,如果對某些概念依然含混不清,請及時查閱更多資料(官方文檔是最好的學習資料),否則無法快速理解接下來將要講述的例子,甚至喪失閱讀興趣。
接下來,將通過一些有代表性的實例了解關于Applications、Activities、Activities stack、Tasks和Intent等一些模塊的最基本原理。從各個角度分析系統對于用戶在不同模式下操作的反應原理。
從Home啟動一個Activity
絕大部分的Application都由此啟動(也有一些Application是通過其它 Application啟動)。具體的方式有兩種,其一是從系統的Application Launcher啟動,另一種是直接由Home Screen的快捷方式。啟動Application后,Root Activity會顯示在當前窗口,并可直接供用戶操作界面元素。官方給出了一個有關這個過程的圖示,其實我感覺這個描述的還不夠直觀,湊合著用吧。大體 的過程是由Home下啟動Email Application,在這個應用程序中可以直接提供給用戶操作的是List Messages Activity,Home Activity切換為后臺運行。
應用Back或Home鍵離開當前Activity的區別
應用Back或者Home都可以離開當前Activity(基于Application的Root Activity),Home activity重新切換到foreground,然而二者最根本的區別在于用戶是否還需要保留當前Activity的state。
- Back:
將會終止(Destroy)當前正在運行的Activity,返回到之前的Activity(如果是 Root Activity,那么將會直接返回到Home Activity)。官方給出了一個相關過程的圖示,當用戶正在操作List Messages Activity時,下拉郵件列表(改變了Scrolling狀態),通過Back鍵返回到Home Activity之后,當再次通過Email Icon啟動 List Messages Activity時,將會看到列表處在初始位置。通過這個演示可以了解到通過Back鍵離開當前Activity時,無法暫時保留住其State數據,當 再次啟動時相當于重新創建了一個實例。
-Home:
利用Home取代Back返回的方式,當前Activity將被切換到Background,而不是被Destroied。這樣的好吃是可以暫時保 留這個Activity的State信息,當再次通過Application launcher或者快捷方式啟動時,可以返回到最后離開的狀態。對比在Back中引用的例子,當再次由Home返回到Activity時,將會看到最后 一次操作所記錄的Scroll狀態,而不是默認的初始位置。
Exception(例外情況)
前邊列舉了兩種典型的情況,同時還存在一些例外的情況,某些Activity從Background被“召喚”到foreground之后依然是相 當于重新創建了新實例,其有區別于前邊所論述的結果。即便是暫時保存在Background模式下(沒有被Destroied),其State數據也將丟 失。例如:Contacts 和 Gallery 等。當用戶啟動了Contact應用程序,并點選某個條目查看詳細信息,如果通過Home鍵返回后,再次重復啟動Contact應用程序時,看到的并不是 之前所打開的特定條目的詳細信息,而是初始的默認界面。這個例子說明不是所有情況下通過Home鍵返回后都可以保存當前Activity的State信 息。
另外一種是與Back鍵有關的特殊情況。前邊提及到大部分的Activity通過Back鍵返回到Home Activity時,其自身將被徹底銷毀,默認情況下Activity響應Back按鍵的方法被定義了Destroy行為。但對于某些特別情況,開發者可 以根據需求將相應Back按鍵事件的行為重新“override”,撤消默認的Destroy行為。音樂播放器是與其相關的一個典型應用,當用戶在播放器 的Root Activity中觸發Back按鍵后,轉為Background模式下繼續播放當前的音樂,同時Home Activity轉為Foreground。
Activity的復用
在多個不同的Applications中,當遇到有相同目的應用時,會涉及到Activity的復用性問題,這在開發過程中是一個非常普遍的情況。 復用性一直被眾多開發機構強調為節約成本,優化資源的最有效的機制。對于移動應用平臺更加看重資源的最優化利用,復用性的應用在Android平臺上無處 不在,通過兩個比較基礎的例子來具體的說明。
- Contacts利用Gallery獲得圖像資源
眾所周知Contacts是手機中最常用的應用程序,主要用于存儲當前用戶的聯系人信息,其中需要包含聯系人的頭像信息。在Android平臺中的圖像信息是由Gallery管理,所以Contacts必然需要復用Gallery Activity來獲取相應的圖像信息。
針對于Android或者其它平臺開發應用程序都需要有良好的復用性意識,這個需要貫穿于項目的整個開發過程。包括如何利用當前系統的現有資源,或 者考慮到將來可能會被其它應用程序用于完成特定的需求。當用戶正在調用的Intent filter不唯一時,系統將彈出一個供用戶選擇的對話框,這的確是一個完美的解決方法。
- 利用Messaging擴展Gallery共享功能
用戶通過Gallery查看當前系統中的圖像資源,每次單獨打開一幅圖像資源都可以通過Menu -> Share將當前的資源以附件形式插入新創建的Messaging中,并且以正常發送信息的方式將其共享給收件人。如果取消當前的共享行為,只需要通過 Back按鍵返回到Gallery Activity。相比較前一個例子的區別在于,Message Activity完成發送或者被取消操作,其不會返回任何信息。
以上兩個例子分別講解了利用一系列的Activities來完成某一項需求,并且它們都調用了外部的Application資源。
Replacing an Activity
目前要介紹的內容是關于在不同的Applications中,有相同Intent filter屬性的Activities可相互間替換,這對于習慣Windows等操作系統的用戶比較不容易理解。其實如果您足夠細心,就可以發現之前的例子中有關于這里所提及情況。
通常遇到這種情況發生時,一般都是因為外部具有相同功能的Activity A 在處理問題的能力方面要優于當前Application中默認的操作行為Activity B,系統會拋出一個可供選擇的對話框,用戶根據主觀判斷來選擇最優的方式處理當前任務。通過一個比較容易理解的實例來說明整個過程,建議“動手能力強”的 同學可以通過模擬器親自嘗試。
例如:用戶在當前系統下加載了最新的Phone Ringtone Activity,取名為Rings Extended。如果用戶通過Setting -> Sounds&Display -> Phone Ringtone 來設置當前的鈴音屬性時,將會彈出一個包含有系統默認的Phone Ringtone Activity 和最新加載的Rings Extended兩種可供選擇的操作應用,同時在對話框中還提供了一種可以直接啟動系統默認的操作方式選項。如果用戶選擇了Rings Extended,那么其將會被載入當前的線程中替代原有的默認操作行為,可以根據下面的圖示來增強理解。
多任務同時運行(Multitasking)
在之前的板塊有專門提到關于Home和Back兩種切換到Home Screen的方法和它們之間的差異性,這個章節將會重點涉及到系統可以同時處理多個實時運行的任務。如果用戶正處于某個Application A開啟狀態時,通過Home按鍵切換回Home Activity的同時保留了此前Application A運行的狀態信息,可以開啟新程序的同時,也可以再次將Application A切換回Foreground。
接下來通過一個有關Map應用的實例更加具體的了解其所涵蓋的過程。
首先的起始階段分為三個步驟,
第一步,由Application Launcher啟動Map應用程序,并且搜索一個具體的地理位置。假設當前的網絡環境非常不理想,需要花費一定的時間Download地圖數據。
第二步,當系統需要花費較長時間加載當前地圖信息數據時,保持當前Activity的狀態,返回Home Activity啟動其它的Applicaton,地圖Activity切換到Background,而并不會中斷加載進度(依然保持網絡連接)。
注意:以上是Activity在默認條件下的反應行為,其切換為Background狀態后直接觸發onStop()事件,開發者可以重新定義其方法。例如:強制Activity在轉為Background狀態下,終止網絡連接。
第三步,當前Map activity已經切換到Background狀態下運行,Home Activity切換到Foreground。這時用戶啟動Calender activity,其將自動轉為Foreground狀態,同時獲得操作焦點。
將以上三個步驟用圖示的方式表述:
最后,退出當前Calender activity返回到Home,再次通過Maps圖標將其處在Background狀態的實例切換到Foreground。
通過上邊的例子看出用戶通過Application Launcher同時運行多個Tasks,代表系統具備多任務處理機制 - Running multiple tasks。
啟動Application的兩種不同方式
每個App都需要提供至少一個Entry point(翻譯成“入口點”有點別扭,干脆保留原樣)供用戶或者系統調用其所關聯的Activities,Application launcher中的小圖標就是每個單獨App的Entry Point。另外App也可以相互間通過Activity作為Entry Point來啟動,可以將App所包含的每個Activity看作為潛在的Entry point。
系統中的Phone Application同樣具有兩個Entry Points:Contacts和Dialer。下邊的圖示中可以了解到用戶通過Application launcher啟動Contacts Activity,選擇其中某一個聯系人之后,調用Dialer Activity撥打其所提供的電話號碼。
Intents
在現實世界中大家每時每刻都會與周圍的環境發生互動,這個互動的過程首先要確定一種意識,例 如:感覺到口渴,需要水分補充。這種意識會引導自己以習慣的方式解決口渴問題,采用的方式可以多種多樣,吃冰淇淋、喝水、嚼樹葉等。類似于口渴的意識形態 被抽象為Intent,并將其看作是一種對象,這就是Android響應“意識”的方式。
在Android平臺上,用戶的操作行為是由各種不同的事件組成,系統會將每個事件都抽象為 Intent對象,尋找解決這項需求的具體方法。抽象的Intent對象有兩種形式,第一種是“明確”的Intent(Explicit Intent),在初始化的時候已經為這個Intent關聯了特定的Activity。第二種是“不明確”的Intent(Implicit Intent),代表這個Intent沒有明確關聯Activity,當它被拋出后,系統在眾多Activities中根據Intent filter來尋找與其匹配的處理方法。如果存在多個結果,用戶可以根據需要選擇合適的處理方法。
引用一個具體的例子,單擊一個mailto:info@androidres.com鏈接后,這個被拋出的Intent屬于 Implicit Intent ,系統抓取了解決這個Intent的結果,將所有的結果供用戶選擇(Gmail或者Email):
下邊給出更多系統默認的Intent關聯列表:
Intent對象包含兩個元素:
1)Action :例如 查看、編輯、撥打電話、查看圖像資源等等。
2)Data:提供給某種行為的具體數據。加工果汁飲料,需要提供水果(黑心店除外)。
參照官網的解釋:Intent Class 和 Intent Filters。
Tasks相互間切換
依然是應用實例來說明這個切換的過程。在這個例子中,用戶編輯一個短消息,并且插入圖像附件,但是在發送之前啟動Calendar,隨后切換回短消息編輯界面,最后發送信息。
1)啟動第一個Task:Messaging App,Home > Messaging > New Message > Menu > Attach > Picture。插入圖片的步驟需要調用Gallery Activity,它是一個獨立的外部程序。
接下來啟動另外一個Task,由于沒有直接從當前的Activity運行Calendar,所以需要切換到Home。
2)啟動另外一個Application(Calendar):Home > Calendar
3)查看Calendar完成后,將Messaging由Background切換到Foreground模式,其中還包括了添加附件,并最終發送消息。
至此,對于Android平臺中兩個比較核心元素: Activities和Tasks 的介紹基本告一段落,以后也許會有更多關于這方面的討論,希望得到您的關注。另外,有些朋友或許已經看過官方的原文,而本站也再次有幸得到了您的通讀,如 果在某些概念或者論述內容上存在遺漏或者誤解,那么真誠的希望能夠獲得指正和幫助。
但是對于VIEW來說,我們如果不改變DRAW,不需要繼承,所以如果想響應事件,則需要
當一個視圖(如一個按鈕)被觸摸時,該對象上的 onTouchEvent() 方法會被調用。不過,為了偵聽這個事件,你必須擴展這個類并重寫該方法。很明顯,擴展每個你想使用的視圖對象(只是處理一個事件)是荒唐的。這就是為什么視圖類也包含了一個嵌套接口的集合,這些接口含有實現起來簡單得多的回調函數。這些接口叫做事件偵聽器 event listeners ,是用來截獲用戶和你的界面交互動作的“門票”。
1.只有一個ACTIVITY得情況:
當鼠標鍵按下時(即觸摸)
首先觸發dispatchTouchEvent
然后觸發onUserInteraction
再次onTouchEvent
如果是點擊的話,緊跟著下列事件(點擊分倆步,ACTION_DOWN,ACTION_up)
觸發dispatchTouchEvent
再次onTouchEvent
當ACTION_up事件時不會觸發onUserInteraction(可查看源代碼)
當鍵盤按下時
首先觸發dispatchKeyEvent
然后觸發onUserInteraction
再次onKeyDown
如果按下緊接著松開,則是倆步
緊跟著觸發dispatchKeyEvent
然后觸發onUserInteraction
再次onKeyUp
注意與觸摸不同,當松開按鍵時onUserInteraction也會觸發。
Activity.dispatchTouchEvent(MotionEvent) - 這允許你的活動可以在分發給窗口之前捕獲所有的觸摸事件。
(同理 dispatchKeyEvent)
onUserInteraction :Called whenever a key, touch, or trackball event is dispatched to the
* activity.
2.activity里有一個LAYOUT,在布局里有個按鈕。
如果在按鈕上觸發一個CLICK事件
首先觸發ACTIVITY的dispatchTouchEvent
然后觸發ACTIVITY的onUserInteraction
然后觸發LAYOUT的dispatchTouchEvent
然后觸發LAYOUT的onInterceptTouchEvent
然后觸發BUTTON的onTouch(這是一個ACTION_DOWN事件)
緊跟著是一個ACTION_UP事件
觸發ACTIVITY的dispatchTouchEvent
注意不再觸發ACTIVITY的onUserInteraction,因為他對ACTION_UP不起作用。
然后觸發LAYOUT的dispatchTouchEvent
然后觸發LAYOUT的onInterceptTouchEvent
然后觸發BUTTON的onTouch
最后觸發BUTTON的onClick.
如果你在ONTOUCH事件里返回true,消費了此事件,那么ONCLICK將不會被響應
但是如果你不寫ONCLICK事件,而ONTOUCH事件返回FLASE
那么最終事件序列:
11-23 17:19:44.313: INFO/activity(803): dispatchTouchEvent
11-23 17:19:44.313: INFO/activity(803): onUserInteraction
11-23 17:19:44.322: INFO/LinearLayout(803): dispatchTouchEvent
11-23 17:19:44.333: INFO/LinearLayout(803): onInterceptTouchEvent
11-23 17:19:44.341: INFO/button(803): onTouch
11-23 17:19:44.441: INFO/activity(803): dispatchTouchEvent
11-23 17:19:44.451: INFO/LinearLayout(803): dispatchTouchEvent
11-23 17:19:44.451: INFO/LinearLayout(803): onInterceptTouchEvent
11-23 17:19:44.461: INFO/button(803): onTouch
即事件不會再向上傳遞,估計是ONCLICK有默認響應不處理,而ONCLICK不會有返回值。
但是如果是繼承了一個VIEW而且又覆寫了onTouchEvent,他返回FALSE
當觸摸事件發生時
11-23 17:25:59.691: INFO/activity(831): dispatchTouchEvent
11-23 17:25:59.691: INFO/activity(831): onUserInteraction
11-23 17:25:59.701: INFO/LinearLayout(831): dispatchTouchEvent
11-23 17:25:59.701: INFO/LinearLayout(831): onInterceptTouchEvent
11-23 17:25:59.701: INFO/button(831): onTouch
11-23 17:25:59.701: INFO/Button(831): onTouchEvent
11-23 17:25:59.701: INFO/LinearLayout(831): onTouchEvent
11-23 17:25:59.701: INFO/activity(831): onTouchEvent
11-23 17:25:59.822: INFO/activity(831): dispatchTouchEvent
11-23 17:25:59.822: INFO/activity(831): onTouchEvent
奇怪的是,ONCLICK事件不再發生。
VIEW和ACTIVITY本身都有相應鍵盤事件的ONKEYUP和ONKEYDOWN
對于VIEW來說,你可以setOnKeyListener(new OnKeyListener(){
@Override
public boolean onKey(
來響應鍵盤事件,如果你既對VIEW寫了這個偵聽,又覆寫了ONKEYUP,DOWN事件,那么首先
進入ONKEY
比如用反向鍵把焦點切換到按鈕上
事件序列:
11-23 17:23:14.392: INFO/activity(803): dispatchKeyEvent
11-23 17:23:14.404: INFO/activity(803): onUserInteraction
11-23 17:23:14.412: INFO/LinearLayout(803): dispatchKeyEvent
11-23 17:23:14.412: INFO/button(803): onKey
11-23 17:23:14.422: INFO/activity(803): onKeyUp
當按回車時
11-23 17:35:55.692: INFO/activity(831): dispatchKeyEvent
11-23 17:35:55.713: INFO/activity(831): onUserInteraction
11-23 17:35:55.722: INFO/LinearLayout(831): dispatchKeyEvent
11-23 17:35:55.732: INFO/button(831): onKey
11-23 17:35:55.813: INFO/activity(831): dispatchKeyEvent
11-23 17:35:55.824: INFO/activity(831): onUserInteraction
11-23 17:35:55.831: INFO/LinearLayout(831): dispatchKeyEvent
11-23 17:35:55.831: INFO/button(831): onKey
11-23 17:35:55.953: INFO/button(831): onClick
最后補充下:不需要那么復雜,如果對于控件想響應他的觸摸事件,如果繼承
則覆寫ONTOUCHEVENT,如果不覆寫則偵聽ONTOUCH
如果覆寫了ONTOUCHEVENT,則不再響應ONCLICK事件
如果寫了ONCLICK,則不要再搞ONTOUCHEVENT了
所以ONCLICK和ONTOUCHEVENT二取一,另外如果有ONTOUCH偵聽,那么此函數要先于其他函數執行
對于ONCLICK來說,要從頭走倆便ONTOUCH,一次是DOWN,一次是UP
同樣的對于ONKEY,以及ONKEYUP和DOWN
如果既覆寫了ONKEYUODOWN,又SET了偵聽ONKEY,那么ONKEY要優先執行,對于一個回車按鍵觸發時
11-24 09:55:18.601: INFO/activity(951): dispatchKeyEvent
11-24 09:55:18.611: INFO/activity(951): onUserInteraction
11-24 09:55:18.621: INFO/LinearLayout(951): dispatchKeyEvent
11-24 09:55:18.641: INFO/button(951): onKey
11-24 09:55:18.711: INFO/activity(951): dispatchKeyEvent
11-24 09:55:18.756: INFO/activity(951): onUserInteraction
11-24 09:55:18.775: INFO/LinearLayout(951): dispatchKeyEvent
11-24 09:55:18.791: INFO/button(951): onKey
11-24 09:55:18.822: INFO/button(951): onClick
漫長的編譯開始了,當然成功不會一蹴而就的,不出所料,錯誤出現了
ost C: adb <= system/core/adb/fdevent.c
host Executable: adb (out/host/linux-x86/obj/EXECUTABLES/adb_intermediates/adb)
out/host/linux-x86/obj/STATIC_LIBRARIES/libzipfile_intermediates/libzipfile.a(centraldir.o): In function `memset':
/usr/include/bits/string3.h:82: warning: memset used with constant zero length parameter; this could be due to transposed parameters
true
Install: out/host/linux-x86/bin/adb
host C++: atree <= build/tools/atree/atree.cpp
host C++: atree <= build/tools/atree/files.cpp
host C++: atree <= build/tools/atree/fs.cpp
host Executable: atree (out/host/linux-x86/obj/EXECUTABLES/atree_intermediates/atree)
true
Install: out/host/linux-x86/bin/atree
host C++: bb2sym <= development/emulator/qtools/bb2sym.cpp
host C++: bb2sym <= development/emulator/qtools/trace_reader.cpp
development/emulator/qtools/trace_reader.cpp: In function ‘char* ExtractDexPathFromMmap(const char*)’:
development/emulator/qtools/trace_reader.cpp:1012: error: invalid conversion from ‘const char*’ to ‘char*’
development/emulator/qtools/trace_reader.cpp:1015: error: invalid conversion from ‘const char*’ to ‘char*’
make: *** [out/host/linux-x86/obj/EXECUTABLES/bb2sym_intermediates/trace_reader.o] 錯誤 1
繼續求教于,Google和百度吧,原來是gcc版本的問題
$gcc --version
gcc (Ubuntu 4.4.1-4ubuntu9) 4.4.1
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
研究了一下發現問題主要出在Ubuntu10.4預置了gcc的版本是4.4,該版本編譯時對語法要求比較高,因此無法編譯源代碼,解決方法就是將gcc-4.4降級成gcc-4.3
具體操作:
sudo apt-get remove gcc-4.4(卸載gcc-4.4)
sudo apt-get remove g++-4.4( 卸載g++-4.4)
sudo apt-get install gcc-4.3(安裝gcc-4.3)sudo apt-get install g++-4.3(安裝g++-4.3)
安裝完4.3版本后,執行gcc --version后會發現版本仍然是4.4,因為gcc已經和4.4版本進行了鏈接,因此需要對gcc重新進行鏈接
具體操作:sudo ln -f /usr/bin/gcc-4.3 gcc
sudo ln -f/usr/bin/g++-4.3 g++
這樣就可以用4.3版本的gcc和g++將原來的覆蓋掉,重新進入android源碼目錄執行make就可以正常編譯~~
Q. I'm getting the following error while compiling application under CentOS / RHEL / Fedora Linux 64 bit edition:
/usr/include/gnu/stubs.h:7:27: error: gnu/stubs-32.h:
No such file or directory
How do I fix this error?
A. You need to install the glibc-devel package. It contains the object files necessary for developing programs which use the standard C libraries (which are used by nearly all programs). If you are developing programs which will use the standard C libraries, your system needs to have these standard object files available in order to create the executables.
Install glibc-devel if you are going to develop programs which will use the standard C libraries.
# apt-get install g++-multilib
1.tigertian@ubuntu:~/Developer/android_src$ apt-file search wxprec.h
wx2.4-headers: /usr/include/wx/wxprec.h
wx2.6-headers: /usr/include/wx-2.6/wx/wxprec.h
wx2.8-headers: /usr/include/wx-2.8/wx/wxprec.h
2.sudo apt-get install wx2.4
wx-config --cflags
前幾天下載了Android 2.3.1的源代碼并在Ubuntu 10.04(32位)上編譯通過。這篇文章簡要記錄了下載、編譯的過程。
關于搭建Android開發環境的文章已經有很多,本文只簡要介紹一下,做為備忘。
[ 編譯前的準備 ]
這一步安裝獲取源代碼以及編譯所需要的軟件,使用如下命令:
$ sudo aptitude install git-core gnupg flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl libncurses5-dev zlib1g-dev
sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev java-common unixodbc
另外,參考別人編譯Android 2.3的經驗,安裝了下列軟件包:
$ sudo apt-get install lib64z1-dev libc6-dev-amd64 g++-multilib lib64stdc++6
注意:32 bit Required Packages 為
git-core gnupg sun-java5-jdk flex bison gperf build-essential zip curl zlib1g-dev libsdl-dev libesd0-dev libwxgtk2.6-dev libncurses5-dev
而64 bit 下的 Required Packages 為 git-core gnupg flex bison gperf build-essential zip curl sun-java6-jdk zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z1-dev
deb http://archive.canonical.com/ubuntu lucid partner deb-src http://archive.canonical.com/ubuntu lucid partner
然后安裝Java 6 JDK:
$ sudo aptitude install sun-java6-jdk
接下來下載repo工具,這是Google提供的一個Python腳本,方便管理多個Git版本庫:
$ cd ~ $ mkdir bin $ curl http://android.git.kernel.org/repo >~/bin/repo $ chmod a+x ~/bin/repo
記得把repo加到你的路徑中,方便以后使用。編輯~/.bashrc,加入下面一行:
PATH=$PATH:~/bin export PATH
然后用命令. ~/.bashrc,以后就可以直接使用repo命令了。
接下來獲取Android 2.3.1的源代碼:
$ mkdir mydroid $ cd mydroid $ repo init -u git://android.git.kernel.org/platform/manifest.git -b android-2.3.1_r1 $ repo sync
[ 編譯Android ]
接下來開始編譯:
$ make -j`grep '^processor' /proc/cpuinfo | wc -l`
上面的命令中,-j參數告訴make啟動多個并行任務進行編譯,在支持多核的CPU上能加快編譯速度。如果你知道你CPU是幾核的,可以直接把這部分替換成-j2(雙核)。
我在編譯的過程中遇到下面的錯誤:
************************************************************
You are attempting to build on a 32-bit system.
Only 64-bit build environments are supported beyond froyo/2.2.
************************************************************
這是因為在Makefile中檢測了CPU的字長。我直接把build/core/main.mk中下面的話注釋掉:
#ifneq (64,$(findstring 64,$(build_arch))) #$(warning ************************************************************) #$(warning You are attempting to build on a 32-bit system.) #$(warning Only 64-bit build environments are supported beyond froyo/2.2.) #$(warning ************************************************************) #$(error stop) #endif
接下來又遇到下面的錯誤:
Docs droiddoc: out/target/common/docs/api-stubs
Could not load ‘clearsilver-jni’
java.library.path = out/host/linux-x86/lib
make: *** [out/target/common/docs/api-stubs-timestamp] Error 45
make: *** Waiting for unfinished jobs….
Could not load ‘clearsilver-jni’
java.library.path = out/host/linux-x86/lib
make: *** [out/target/common/docs/doc-comment-check-timestamp] Error 45
這是由于clearsilver在編譯時如果檢測到使用Java JDK 6,就使用64位編譯。要避開此錯誤,需要修改下面四個文件:
把這四個Makefile中的下列語句注掉即可:
# This forces a 64-bit build for Java6 # Comment by Easwy # LOCAL_CFLAGS += -m64 # LOCAL_LDFLAGS += -m64
然后在external/clearsilver目錄中執行一下make clean,然后回到項目根目錄,繼續make即可。
當編譯完成時,生成的image文件放在out/target/product/generic目錄中。
需要將gcc編譯環境設置為4.3版本,否則會出現const char* 到 char*無法轉換的問題。
http://m.tkk7.com/TiGERTiAN/archive/2011/01/22/343372.html
錯誤:
development/simulator/wrapsim/DevAudio.c:11: fatal error: alsa/asoundlib.h: No such file or directory
compilation terminated.
make: *** [out/debug/host/linux-x86/pr/sim/obj/SHARED_LIBRARIES/libwrapsim_intermediates/DevAudio.o] Error 1
解決辦法:
$ apt-file search alsa/asoundlib.h
libasound2-dev: /usr/include/alsa/asoundlib.h
So that’s it, asoundlib.h is in the package libasound2-dev.
apt-file需要使用apt-get install apt-file安裝一下。
in egl.cpp, at the line 554 : const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion)); just remove the 'const' and retry.
查找資料,確定下面的修改步驟(-表示刪除行,+表示添加行):
1、修改build/core目錄下的main.mk文件,修改策略為:
ifeq ($(BUILD_OS),linux)
build_arch := $(shell uname -m)
-ifneq (64,$(findstring 64,$(build_arch)))
+ifneq (i686,$(findstring i686,$(build_arch)))
$(warning ************************************************************)
$(warning You are attempting to build on a 32-bit system.)
$(warning Only 64-bit build environments are supported beyond froyo/2.2.)
2、修改下列文件:
/external/clearsilver/cgi/Android.mk
/external/clearsilver/cs/Android.mk
/external/clearsilver/java-jni/Android.mk
/external/clearsilver/util/Android.mk
4個文件的修改策略相同,為:
# This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
接下來的編譯可能會遇到很多因為缺少相應模塊而產生的錯誤,請首先安裝下列軟件模塊:
bison
sun-java6-jdk
g++ (build-essential)
zlib1g-dev
flex
libncurses-dev
gperf
然后,就是make之后漫長的等待...
There is a lot of confusion surrounding the work flow in the Android source tree, so allow me to simplify:
$ source build/envsetup.sh
$ lunch 1
$ make
If you have a multi-core system, you can build with make -jN
where N is twice the number of cores on your machine. This should speed up the first build considerably.
$ ./out/host/<your-machine-type>/bin/emulator
On my system <your-machine-type>
is linux-x86
.
NOTE: The emulator knows where to find system and data images as a result of running lunch 1
above. This sets the environment variable ANDROID_PRODUCT_OUT
to point to the target directory. For this example, it should be out/target/product/generic/
.
source build/envsetup.sh
above. For example, if you modify the Email app and just want to rebuild it:
$ mmm packages/apps/Email
$ adb remount
$ adb sync
Which will copy the regenerated Email.apk
file into the emulator’s /system/app
folder, triggering the PackageManager
to automatically reinstall it.
frameworks/base/core/res/res/
you could regenerate framework-res.apk
with:
$ mmm frameworks/base/core/res
Or if you modified even the framework itself you could run:
$ ONE_SHOT_MAKEFILE="frameworks/base/Android.mk" make framework
This is a special variation of mmm
which is used to build frameworks/base/core/java
.
To sync these changes you must restart the running framework and sync, as with this handy sequence:
$ adb remount
$ adb shell stop
$ adb sync
$ adb shell start
http://source.android.com/using-eclipse
This document also describes how to use Eclipse for development. Any IDE should work with the proper finagling though. Just note that the IDE won’t really by an integrated environment, the final output of APKs, system.img
, and even the generation of R.java
files will have to be done by make
!
A note about the processes in Android:
system_process
houses all things under frameworks/base/services
. This includes the PackageManagerService, StatusBarService, etc. It has many, many threads (one for each service, and then one main UI thread), so be wary when debugging.com.android.acore
hosts Launcher (home), Contacts, etc. You can determine the apps/providers that run here by looking forandroid:process="android.process.acore"
in the various AndroidManifest.xml
files in packages/.Also remember that the “framework” (under frameworks/base/core/java
) is not hosted by any one process. It is a library used by most processes, so to debug code there you can usually use a simple demo app that takes advantage of whatever you changed and debug that app’s process. A useful trick for setting up your debug connection is to call Debug.waitForDebugger()
during some startup part of an application or system service.