1. 程式人生 > >Android Launcher分析和修改9——Launcher啟動APP流程

Android Launcher分析和修改9——Launcher啟動APP流程

  本來想分析AppsCustomizePagedView類,不過今天突然接到一個臨時任務。客戶反饋說機器介面的圖示很難點選啟動程式,經常點選了沒有反應,Boss說要優先解決這問題。沒辦法,只能看看是怎麼回事。今天分析一下Launcher啟動APP的過程。從使用者點選到程式啟動的流程,下面針對WorkSpace上的快捷圖示點選啟動流程進行分析。(如果分不清WorkSpace是什麼或者不知道快捷方式和其他圖示區別,請看我前面的Launcher分析文章)

PS:新建的QQ群,有興趣可以加入一起討論:Android群:322599434

  下面我們先看看Launcher啟動APP的大概流程:

(鑑於很多轉載文章的人把作者資訊都刪除了,只能在圖片上加入水印,不會給大家閱讀造成影響)

  上面就是手指觸控式螢幕幕開始,到點選響應的流程。Launcher裡面因為有滑動、拖曳、點選等手勢操作,所以區分了很多流程判斷。最後呼叫Launcher.java裡面的onClick()方法響應點選,啟動程式。下面我們針對關鍵流程做分析。

1、WorkSpace觸控

  前面我們分析Launcher的配置檔案時就說過,Launcher外面的介面主要就是通過WorkSpace來顯示的。它是一個ViewGroup的自定義類。下面我們先看看WorkSpace的onInterceptTouchEvent做了什麼。


   public boolean onInterceptTouchEvent(MotionEvent ev) { if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter");
     //對ACTION_DOWN和ACTION_UP做一些標記處理。
switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mXDown
= ev.getX(); mYDown = ev.getY(); break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: if (mTouchState == TOUCH_STATE_REST) { final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); if (!currentPage.lastDownOnOccupiedCell()) { onWallpaperTap(ev); } } } if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent call super InterceptTouch"); //呼叫父類的onInterceptTouchEvent,這裡是呼叫了SmoothPagedView return super.onInterceptTouchEvent(ev); }

   在WorkSpace裡面並沒有攔截訊息,主要是呼叫父類的方法,也就是PagedView的onInterceptTouchEvent()方法。因為WorkSpace的直接父類SmoothPagedView也是繼承了PagedView類,有關PagedView的onInterceptTouchEvent()方法,我在前面的文章已經分析過。這裡不做多說,不瞭解的朋友可以看看(點這裡)。

 2、CellLayout的onInterceptTouchEvent()方法

CellLayout也是一個繼承了ViewGroup的類,主要用來顯示桌面控制元件。剛開始分析Launcher的時候,我們分析配置檔案的時候也說過WorkSpace就是由5個CellLayout組成的。因此我們點選WorkSpace裡面圖示的時候,自然會呼叫CellLayout裡面的東西。CellLayout裡面的onInterceptTouchEvent()做的事情並不多:

  public boolean onInterceptTouchEvent(MotionEvent ev) 
    {
        if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent enter");

        final int action = ev.getAction();

        if (action == MotionEvent.ACTION_DOWN) 
        {
       //清除所有觸控標記 clearTagCellInfo(); }      //mInterceptTouchListener是WorkSpace的onTouch方法回撥,下面會分析
    //掌握這點很重要,因為onInterceptTouchEvent的返回值直接決定了觸控事件的傳遞方向 mythou
if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) { //截斷Touch傳輸,直接處理 ,返回true會直接呼叫onTouchEvent處理。 mythou if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return true Intercept msg"); return true; } if (action == MotionEvent.ACTION_DOWN) { setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY()); } if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent return false");
     //注意這裡返回的是false ,onInterceptTouchEvent的返回值決定了觸控事件的傳遞方式。mythou
return false; }

  這裡需要注意的是onInterceptTouchEvent()的返回值,如果是返回true,觸控訊息會直接被CellLayout的onTouchEvent()處理,一般點選啟動程式返回的都是false,因為訊息最後是TextView處理(workspace上的快捷方式都是TextView的子類)。Android的觸控訊息傳遞機制和訊息攔截機制,需要好好理解好才能明白Launcher的觸控事件處理。因為個人覺得Launcher裡面事件處理層次還是比較多,如果對Android的事件傳遞機制理解不深,就很難理解Launcher的事件處理。如果對這方面不瞭解的朋友,可以查查網上相關資料。後面有空我也會寫一篇深入分析Android觸控事件處理的文章。

 3、WorkSpace的onTouch()事件

WorkSpace裡面還處理了onTouch事件,這裡的onTouch事件是因為WorkSpace使用了View.OnTouchListener介面,所以實現了onTouch事件的回撥。

public boolean onTouch(View v, MotionEvent event) 
{
  return (isSmall() || !isFinishedSwitchingState());
}

  onTouch裡面其實沒做什麼事情,就是根據兩個方法返回值,判斷onTouch是返回false還是返回true,返回值是什麼決定了觸控事件的傳遞方向,上面已經說過了。這裡的onTouch是給WorkSpace裡面的裝載的View使用的,也就是CellLayout。我們可以看看每次呼叫WorkSpace的onChildViewAdded()方法,會設定CellLayout的onTouch監聽器。

public void onChildViewAdded(View parent, View child) { if (!(child instanceof CellLayout)) 
{
  
throw new IllegalArgumentException("A Workspace can only have CellLayout children."); }
   CellLayout cl
= ((CellLayout) child);
   //設定onTouch的監聽器,CellLayout的onInterceptTouchEvent()方法會根據onTouch監聽器判斷是否需要攔截onTouch事件。 cl.setOnInterceptTouchListener(
this); cl.setClickable(true); cl.setContentDescription(getContext().getString( R.string.workspace_description_format, getChildCount())); }

上面第二點CellLayout的onInterceptTouchEvent()方法的分析裡面,return ture的時候,判斷的依據mInterceptTouchListener和mInterceptTouchListener.onTouch()方法的返回值就是從這裡設定的,他們也就是呼叫了WorkSpace的onTouch方法。

4、BubbleTextView

   BubbleTextView是繼承了TextView的子類,在Launcher裡面所有的WorkSpace的快捷方式都是使用BubbleTextView繪畫的,它的作用相當於一個按鈕。在分析BubbleTextView的觸控響應前,我們先看看Launcher裡面如何建立快捷方式。前面的文章我分析過Launcher載入介面資料的流程,不過沒有仔細分析如何建立相關物件,下面我們先看看WorkSpace的快捷方式如何建立:

View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) 
{
  BubbleTextView favorite
= (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
  favorite.applyFromShortcutInfo(info, mIconCache);
  //注意這裡設定了onClickListener OWL favorite.setOnClickListener(
this); return favorite; }

上面就是建立快捷方式的方法,這裡面我們需要注意的是,BubbleTextView呼叫了SetOnClickListener()方法,設定點選監聽器。監聽器的回撥函式就是Launcher.java類裡面的onCLick方法。因為Launcher類繼承了View.OnClickListener介面,當然Launcher裡面還繼承了其他的介面,例如觸控、長按的Listener。下面我們先看看BUbbleTextView的onTouchEvent方法:

public boolean onTouchEvent(MotionEvent event) 
  { // 呼叫TextView的onToucEvent方法,主要是獲取返回值,這個返回值作用很大,會響應點選事件的回撥。 // 可以把這返回值打印出來看看。當點選啟動程式時,這裡肯定會返回true。
     //原因上面已經說了很多次,這裡不再囉嗦 OWL  
boolean result = super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //下面這裡主要是根據觸控狀態值,做快捷方式的狀態顯示,例如是否需要顯示按下狀態。 if (mPressedOrFocusedBackground == null) { mPressedOrFocusedBackground = createGlowingOutline( mTempCanvas, mPressedGlowColor, mPressedOutlineColor); } if (isPressed())
         { mDidInvalidateForPressedState
= true; setCellLayoutPressedOrFocusedIcon(); } else { mDidInvalidateForPressedState = false; } mLongPressHelper.postCheckForLongPress(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!isPressed()) { mPressedOrFocusedBackground = null; }           //這裡根據狀態判斷是否取消長按的觸控 mLongPressHelper.cancelLongPress(); break; }
     //返回值十分重要,直接影響是否會掉用onCLick方法。
return result; }

上面就是BubbleTextView的onTouchEvent的方法,上面強調很多次返回值,這裡如果是點選流程,會返回true。然後執行onClick方法。

5、onClick()方法

  最後點選會呼叫Launcher裡面的onClick方法,不管你是點選快捷方式、資料夾、還是AllAPP的按鈕,都是從這裡響應回撥。

//Edited by mythou
//http://www.cnblogs.com/mythou/
    
  public void onClick(View v) {
    //..........

        Object tag = v.getTag();
        if (tag instanceof ShortcutInfo) {
            //這裡點選開啟我們上面分析的快捷方式圖示。開啟的方式也是呼叫startActivity,
       // 只是這裡呼叫的startActivitySafely對startActivity進行了封裝 mythou
     
final Intent intent = ((ShortcutInfo) tag).intent; int[] pos = new int[2]; v.getLocationOnScreen(pos); intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; mWaitingForResume.setStayPressed(true); }
     //資料夾點選響應 }
else if (tag instanceof FolderInfo) { if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; handleFolderClick(fi); }
     //AllAPP按鈕點選響應 }
else if (v == mAllAppsButton) { if (isAllAppsVisible()) { showWorkspace(true); } else { onClickAllAppsButton(v); } } }

  上面給出了最後點選啟動快捷方式圖示的方式,最後呼叫的也是startActivity,不過Launcher對它進行了封裝,裡面加入異常處理,包括是否啟動成功等操作。而且裡面分開了startActivity和startActivityForResult兩種方式。但是最後啟動的時候,還是呼叫了我們平時使用的啟動Activity的方法。下面給出我加入列印訊息後的程式碼流程跟蹤截圖。

6、總結

  Launcher裡面有關點選啟動程式的方法流程就是上述所說的那樣,當然,裡面有些方法判斷執行了很多方法,這裡不做詳細分析,又興趣的朋友可以跟我一樣加入一些列印訊息,跟蹤Log分析程式碼流程。裡面還有一個相當重要的部分就是如何區分是頁面滑動還是按鈕點選。文章開頭說的問題就是因為把點選事件判斷為頁面滑動,導致經常點選按鈕的時候執行了滑動頁面的操作,導致使用者覺得點選按鈕都沒反應。我最後的修改方法是修改了判斷是否進行頁面滑動的方法,解決了該問題。

  今天就說到這裡,這個流程分析個人覺得還是有點複雜,因為涉及了Android觸控事件的傳遞方式,如果不瞭解這個,無法理解Launcher是如何處理這些事件的最後歡迎各位轉載Launcher的分析文章,但請不要修改文章內容,並附上原文連結和作者資訊。

Launcher分析系列文章:

Edited by mythou

相關推薦

Android Launcher分析修改9——Launcher啟動APP流程

  本來想分析AppsCustomizePagedView類,不過今天突然接到一個臨時任務。客戶反饋說機器介面的圖示很難點選啟動程式,經常點選了沒有反應,Boss說要優先解決這問題。沒辦法,只能看看是怎麼回事。今天分析一下Launcher啟動APP的過程。從使用者點選到程式啟動的流程,下面針對WorkSpac

Android Launcher分析修改3 Launcher啟動初始化

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android Launcher分析修改3——Launcher啟動初始化

@Override protected void onCreate(Bundle savedInstanceState)   { super.onCreate(savedInstanceState); //獲取Application 例項 La

Android Launcher分析修改1——Launcher預設介面配置(default_workspace)

//Edited by mythou // //傳入default_workspace檔案的資源ID和資料庫實力,把xml裡面資料解析,儲存到Launcher資料庫。返回總共解析了多少個標籤。 private int loadFavorites(SQLiteDatabase db, int workspa

Android Launcher分析修改13——實現Launcher編輯模式(1) 桌布更換

  已經很久沒更新Launcher系列文章,今天不分析原始碼,講講如何在Launcher裡面新增桌面設定的功能。目前很多第三方Launcher或者定製Rom都有簡單易用的桌面設定功能。例如小米MIUI的Launcher就有很豐富編輯功能。今天開始會講一下如何實現桌面編輯功能。網上對於實現Launcher一些編

Android Launcher分析修改2——Icon修改、介面佈局調整、桌布設定

<!-- Workspace cell size --> <dimen name="workspace_cell_width_land">88dp</dimen> <dimen name="workspace_cell_width_port">

Android Launcher分析修改8——AllAPP介面拖拽元素(PagedViewWithDraggableItems)

  接著上一篇文章,繼續分析AllAPP列表介面。上一篇文章分析了所有應用列表的介面構成以及如何通過配置檔案修改屬性。今天主要是分析PagedViewWithDraggableItems類,因為在我們分析AppsCustomizePagedView之前,需要先了解PagedViewWithDraggableI

Android Launcher分析修改7——AllApp全部應用列表(AppsCustomizeTabHost)

  今天主要是分析一下Launcher裡面的所有應用列表。Android4.0 Launcher的所有應用列表跟2.X比較大的區別就是多了Widget的顯示。下面會詳細分析Launcher裡面有關所有應用列表配置和程式碼分析。   1、AllApp列表配置檔案 配置AllAPP應用列表介面的配置檔案

Android Launcher分析修改11——自定義分頁指示器(paged_view_indicator)

  Android4.0的Launcher自帶了一個簡單的分頁指示器,就是Hotseat上面那個線段,這個本質上是一個ImageView利用.9.png圖片做,效果實在是不太美觀,用測試人員的話,太醜了。特別是搭配其他風格的圖示和背景,的確不好看。所以打算自己重新寫一個指示器。這個所謂的分頁指示器作用很簡單,

Android Launcher分析修改6——頁面滑動(PagedView)

public boolean onTouchEvent(MotionEvent ev) { if(OWL_DEBUG) Log.d(OWL, "onTouchEvent entering..");      //........switch (action &

Android Launcher分析修改10——HotSeat深入進階

  void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY, boolean insert) { if(OWLLaunche

Android Launcher分析修改5——HotSeat分析

void resetLayout() {    //清空原來的內容 mContent.removeAllViewsInLayout(); //新增AllAPP按鈕,也是一個BubbleTextView物件 Context context = g

Android Launcher分析修改4——初始化載入資料

private void bindWorkspace() {       //通知Launcher開始繫結資料 mHandler.post(new Runnable() { public void run() {

Android Launcher分析修改12——Widget列表資訊收集

public void syncWidgetPageItems(final int page, final boolean immediate) { int numItemsPerPage = mWidgetCountX * mWidgetCountY; //

【轉載】Android Bug分析系列:第三方平臺安裝app啟動後,home鍵回到桌面後點擊app啟動時會再次啟動入口類bug的原因剖析

特殊 返回 androidm android系統 圖片 管理 相關 OS 簡便 前言   前些天,測試MM發現了一個比較奇怪的bug。   具體表現是:   1、將app包通過電腦QQ傳送到手機QQ上面,點擊安裝,安裝後選擇打開app (此間的應用邏輯應該是要觸發 【閃屏頁

Kali Linux Web滲透測試手冊(第二版) - 2.6 - 使用瀏覽器自帶的開發工具來做基本的分析修改

翻譯來自:掣雷小組 成員資訊: thr0cyte,Gr33k,花花,MrTools,R1ght0us,7089bAt, 這個公眾號,一定要關注哦,慢慢會跟上面老哥們一起分享很多幹貨哦~~ 第二章:偵察 介紹 2.1、被動資訊收集 2.2、使用Recon-ng收集資訊 2.3、

Android記憶體分析調優(上)

PID    Vss        Rss        Pss       Uss      cmdline ...... 2319 42068K 42032K 13536K 7028K com.xxx ...... 該命令可以列出當前系統所有程序的記憶體佔用情況。 PID是程序ID。 Vss是佔用的虛擬

Android記憶體分析調優

在前文中討論瞭如果使用adb shell procrank, dumpsys meminfo和showmaps分析程序的記憶體佔用情況。 本文將繼續細化,具體分析導致記憶體過大的dalvik heap。 Dalvik heap分析和優化 Dalkvik heap是最常見的android應用記憶體優化的物件。

Android PackageManagerService分析一:PMS的啟動

從這一章開始,我們來分析Android的PackageManagerService,後面簡稱PMS。PMS用來管理所有的package資訊,包括安裝、解除安裝、更新以及解析AndroidManifest.xml以組織相應的資料結構,這些資料結構將會被PMS、Activity

Android 響應系統分享,不重複啟動app

最近專案中遇到幾個納悶的問題: 1、當在服務程序中發一個通知Notification,點選Notification啟動UI程序的某個Activity,發現MainActivity會再次被調起。 2、MainActivity響應系統分享的意圖時,應用再次重啟一遍app,當進入任務列表檢視時,會