編輯:關於Android編程
當然,你可以去學習如何創建一個Xposed模塊。所以你可以閱讀這篇教程(官方教程)去學習怎樣解決這個問題。這不僅僅講解如何新建模塊、如何編寫模塊,我們要往更深處思考,為什麼按照這些步驟,為什麼要新建這個類。如果你是“TL博士”那樣的人,那麼可以直接閱讀這一章節。如果你想看完整個教程那麼你需要很好的理解能力。你將會花費時間去閱讀這篇文章,因為你不能但靠自己解決任何的問題。
你可以在Github中找到重新創建紅色時鐘的例子,它包括狀態欄時鐘的顏色改為紅色和添加一個笑臉,我選擇這個例子,因為它是一個相當小,但很容易看到變化。此外,它使用了一些由框架提供的基本方法。
在你修改之前,你應該思考一下Xposed是如何工作的(如果你覺得太無聊可以跳過這一節)。方法如下:
有個進程叫做“Zygote”。這是Android運行環境的的核心。每一個應用程序啟動都是通過它fork出來的。當手機被啟動就會執行/init.rc這個腳本,這個進程會啟動/system/bin/app_process,然後他會調加載所需的類並調用初始化函數。
現在說說Xposed什麼時候開始啟動。當您安裝了Xposed,它會把修改的app_process可執行文件復制到/system/bin中。這個修改過的啟動進程增加了一個額外的jar包到classpath路徑,並在某些地方調用那裡的方法。例如,在虛擬機剛剛創建後,在Zygote的main方法調用後。我們的jar包也可以Zygote裡面工作。
這個jar包位於:/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar和它的源代碼可以在這裡找到(here)。綜觀類XposedBridge,你可以看到的main方法。這就是我上面寫的,這個類會在進程啟動之前被調用。在那時候執行一些初始化和模塊的加載(我會在後面講解模塊的加載)。
Xposed真正的能力在於可以hook函數的調用。當您修改完反編譯後的APK,你可以直接插入命令或者修改命令。但是您將需要重新編譯/簽名APK,並且重新安裝這個安裝包。你可以用Xposed的hooks,你不用修改內部函數的任何代碼(這無法清晰地定義什麼叫“沒有修改”這個詞語)。相反,你可以注入java裡最小的單元(每一句代碼)到函數的前面和後面,這可以清晰地解決問題。
XposedBridge有一個私有的本地方法叫hookMethodNative。此方法同樣的會在擴展的app_process進程中應用。它會將方法類型改變為“native”並且將這方法的實現替換為它自己本地的泛型方法。這意味著任何時候調用這個被hook的方法,方法調用者並不知道已經被native方法給代替了java層的方法。在該方法中,在XposedBridge的方法handleHookedMethod會被調用,用來傳遞需要的參數和自身的引用。並且這個方法負責調用給這個函數注冊過的回調方法。他可以改變傳進來的參數,可以更改實例變量和靜態變量、可以調用其他方法、處理一下返回值,或者跳過任何你想跳過的代碼。這是非常靈活的。
好了,理論講解完了。現在,讓我們創建一個模塊!
模塊是正常的應用程序,只需用一些特殊的元數據和文件。因此,首先創建一個新的Android項目。我假設你之前已經做到了這一點。如果不是,官方文檔非常詳細。當被問及對於SDK中,我選擇了4.0.3(API15)。我建議你試試這個,因為已經做過測試了。你不需要創建一個activity,因為修改沒有任何用戶界面。回答完這個問題之後,你應該有一個空白的項目。
現在我們開始講解如何加載Xposed模塊。需要按以下幾個步驟來進行的。
XposedInstaller根據模塊列表查找模塊程序的詳細信息是通過meta-data這個標簽的。您可以這樣創建它:AndroidManifest.xml=>Application=>ApplicationNodes(在底部)=>Add=>MetaData。這個名字應該是xposedmodule和值為true。讓資源為空。然後重復相同的操作填寫xposedminversion(見下文)和xposeddescription(你的模塊的一個非常簡短的描述)。XML源代碼現在看起來像這樣:
接下來,讓項目認識XposedBridgeAPI。你可以從這裡下載XposedBridgeApi-
可以選擇庫引用的方式。但是確保你的API類被正確地編譯到APK文件中,否則你會得到一個IllegalAccessError。通過引用libs文件(有“s”),通過Eclipse的簡單設置可以不用把XposedBridgeApi-
現在,您可以創建一個類的模塊。我的被命名為“Tutorial”,並在包de.robv.android.xposed.mods.tutorial裡面:
用於第一步驟中,我們將只做一些記錄以顯示被加載的模塊。一個模塊可以有幾個入口點。選擇哪一個取決於您要修改的內容。您可以在Android系統啟動時、當一個app將要被加載時、一個app的資源將要被初始化時,通過Xposed調用你模塊中的一個函數。
本教程往下一點,您將學習必要的更改需要在一個特定的應用程序中完成,所以讓我們一起去看看“當一個新的應用程序被載入的時候”的入口點。所有入口點都標有IXposedMod的子接口。在這種情況下,你需要實現IXposedHookLoadPackage這個函數。它實際上只是一個方法加上一個參數,它提供了有關環境的實現模塊的更多信息。在我們的例子中,我們記錄的加載應用程序的名稱:
這個log方法將日志輸出到標准logcat窗口中和/data/data/de.robv.android.xposed.installer/log/debug.log文件中。(通過它很容易了解XposedInstaller)
現在需要關心的事情是仍然丟失了XposedBridge的一些類包含的入口點的追蹤。這是通過一個名為xposed_init的文件,在assets文件夾中用它的名字創建一個新的文本文件。在這個文件中,每一行包含一個完全限定的類名,就像這樣,這裡的是:de.robv.android.xposed.mods.tutorial.Tutorial
保存文件。然後運行你的項目為Android應用程序。因為這是你第一次安裝它,在使用它之前您需要啟用它。打開Xposed安裝的應用程序,並確保你已經安裝了框架。然後去了“模塊”選項卡。你應該找到你的應用程序在裡面。選中該復選框來啟用它。然後重新啟動。你不會看到有任何的變化,但如果你檢查日志,你應該看到的東西是這樣的:
瞧!它這工作了。現在你有一個Xposed模塊。它僅僅只有寫log的作用。。。
好了,現在開始這一話題,可以是非常不同,這取決於你想要做什麼。如果你以前有修改過的APK,你可能知道我在說什麼。在一般情況下,你首先需要獲得有關目標的實現的一些細節。在本教程中,目標是在狀態欄的那個時鐘。它有助於知道狀態欄和一部分SystemUI的細節。因此,讓我們開始搜索。
可能性之一:反編譯。這將給你准確的實現,但是它是難以閱讀和理解的,因為你得到smali格式的代碼。可能性二:獲取AOSP源(here)並且浏覽他。但這官方ROM可能與你的不一樣,但在這種情況下,它是一個類似甚至相同的實現。第一,我想看看AOSP,看看是否是一樣的。如果我需要更多的細節,看看實際的反編譯代碼。
你可以尋找與“時鐘”類名稱或包含該字符串的類。下一步就是,尋找他所使用的資源和布局。如果您下載了官方AOSP的代碼,就可以開始在這裡開始尋找:frameworks/base/packages/SystemUI。你會發現不少地方出現“時鐘”。這是正常的,的確會有不同的方式來實現修改。請記住,你僅僅可以hook方法。所以,你必須要找到一個可以在他之前、之後、或全部替換可以插入一些代碼的地方。你應該hook住盡可能具體的方法,而不是那些會被調用上千次的方法,去避免性能問題和意想不到的副作用。
在這種情況下,您可能會發現這個layout布局res/layout/status_bar.xml包含了一個自定義視圖類:com.android.systemui.statusbar.policy.Clock。多個想法可能會現在你的頭腦中。文字顏色的定義是通過textAppearance屬性,所以最簡單的方法就是改變它,將會改變外觀的定義。然而,這可能有效也可能無效(因為它可能存在於更深的native代碼中)。更換布局狀態欄將是可能的,但是你們只可以做最小的變化去更改他,相反,看看這個類。有一個叫updateClock方法,它看上去會被每分鐘調用去更新時間
看起來完美的修改,因為它是這似乎是唯一設置文本時鐘的非常具體的方法。假如我們改變了這個clock的顏色或者字體,那麼任何調用這個方法的都會受此影響。就達成我們的需求了,我們立刻行動.
(單獨的文本顏色,這裡有一種更好的方式.看到“修改布局”的例子在"Replacingresources".)
什麼是我們已經知道的?我們找到一個方法:updateClock在com.android.systemui.statusbar.policy.Clock,我們要攔截它。我們發現這個類是在SystemUIsources裡面,所以它只能對SystemUI的進程有效。修改一些其他的歸屬於框架的類是會任何地方都有效的。如果我們試圖獲得任何信息和直接引用此類在handleLoadPackage方法中,這會失敗的,因為它們在不同的進程中。所以,當一個指定的包將被加載時,讓我們來實現一個執行特定代碼的條件。
使用參數,我們可以很容易地檢查,我們是否選中正確的包。一旦我們證實,我們可以從包中獲得這個類通過也是從這個變量引用的類加載器。現在我們來看看在com.android.systemui.statusbar.policy.Clock類及其updateClock方法,可以告訴XposedBridge把它hook了:
findAndHookMethod是helper類的一個方法。請注意,它是靜態導入的,如果你配置了它描述的鏈接頁面就會自動添加。此方法通過ClassLoader在ClassLoader包中查找Clock類。然後,它會在裡面尋找updateClock方法。如果這種方法有任何參數,那你就必須列出這些參數的類型。不同的情況不一樣的處理,但我們的方法沒有任何參數,可以跳過這個假設。作為最後一個參數,你需要提供XC_MethodHook類的實現。對於較小的改動,就可以使用一個匿名類。如果你有太多的代碼,最好創建一個普通的類,只在這裡創建實例。隨後,helper將盡一切方法hook住以上的函數。
你可以重寫XC_MethodHook的兩個方法。您可以同時覆蓋,甚至不做操作,但後者是完全沒有意義的。這兩個方法是beforeHookedMethod和afterHookedMethod。這不是太難猜測,這兩個方法會在原始的方法的之前和之後執行。您可以使用beforeHookedMethod方法來評價/篡改方法調用的參數(通過param.args),甚至阻止調用原來的方法(發送自己的結果)。afterHookedMethod方法可以用來做基於原始方法的結果的事情。您還可以用它來操縱結果。當然,你可以添加自己的代碼,它將會准確地在原始方法的前或後執行。
(如果你想完全取代方法,看看子類XC_MethodReplacement相反,你只需要覆蓋replaceHookedMethod)
XposedBridge保留著一個記錄了每個已經hook了的函數的注冊回調函數的列表。那些具有最高優先級(如hookMethod定義)會首先調用。原始方法始終是優先級最低的。所以,假如你hook了一個函數並注冊了回調A(PRIO高點)和B(PRIO默認值),那麼每當hook的方法被調用,控制流將是這樣的:A.before->B.before->原始的方法->B.after->A.after。因此,A修改了的參數,B是可以看到的,這樣可以在傳遞給原始方法之前多步地改變它。原方法的結果首先會被B處理,但是這個原始方法最終返回的結果是由A來決定的。
好了,你現在有一個每次updateClock調用時,都會被調用的方法,而且可以精確到原始方法的前後(你已經在SystemUI的進程裡面了)。現在,讓我們來修改一些東西。
首先要檢查:我們有沒有得到具體的時鐘對象?是的,我們有,它在param.thisObject參數裡。因此,如果該方法被myClock.updateClock()調用,然後param.thisObject將會使myClock這個對象。
下一步:我們可以做什麼用的時鐘?這個Clock類是不可以利用的,你可以不轉換param.thisObject變成類(甚至不要去嘗試)。然而,它繼承自TextView的。所以,你可以使用像的setText,gettext和setTextColor的方法,一旦你已經把Clock引用映射成TextView。這些改變應該在原始方法調用後去設置新的時間。由於在方法調用前沒有事做,我們就不考慮beforeHookedMethod。調用(empty)"super"方法是沒有必要的。所以不要重寫這方法。
這是完整的源代碼:
現在安裝/重新啟動您的應用程序。正如你在運行之前已經在XposedInstaller啟用了它,你就不需要再來一次了,重新啟動就足夠了。不過,如果你想使用它停用這個紅色時鐘的例子。兩者都使用缺省的優先級給他們的updateClock處理程序,那麼你不知道哪一個會勝出(它實際上取決於處理方法的字符串表示形式,但並不依賴於此)。
我知道,這個教程很長。但我希望你現在不僅可以實現一個綠色的時鐘,還可以實現和這個完全不同的東西。找到好的方法來hook是一個經驗上的問題,所以開始的東西比較容易。嘗試剛開始就多使用日志功能去確保被調用的是預期的事件。現在:玩得開心!
1,組件圖提供了wifi打開/關閉時,一些相關模塊的依賴關系。 2,簡介:wifi打開: 由jni依賴的libnetutils.so提供wifi驅動的加載。由n
當傳統的手工打包方式遇上同一應用,多渠道/多包名及多種引導頁/icon等等差異時,就變成了苦不堪言的純體力活了。 但有了Gradle這一切不再是問題了,Gradle使得這
Android 中與 Touch 事件相關的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEv
CircleImageView實現圓形頭像代碼分享,供大家參考,具體內容如下一、創建屬性文件(attrs.xml)具體操作:1、在項目的values文件底下創建一新的屬性