偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

瀏覽器為什么能喚起App的Activity?

系統(tǒng) 瀏覽器
我們沒(méi)有主動(dòng)聲明Activity的class,那么系統(tǒng)是怎么為我們找到對(duì)應(yīng)的Activity的呢?其實(shí)這里和正常的Activity啟動(dòng)流程是一樣的,無(wú)非是if / else的實(shí)現(xiàn)不同而已。

[[425568]]

本文轉(zhuǎn)載自微信公眾號(hào)「咸魚(yú)正翻身」,作者M(jìn)Dove。轉(zhuǎn)載本文請(qǐng)聯(lián)系咸魚(yú)正翻身公眾號(hào)。

疑問(wèn)的開(kāi)端

大家有沒(méi)有想過(guò)一個(gè)問(wèn)題:在瀏覽器里打開(kāi)某個(gè)網(wǎng)頁(yè),網(wǎng)頁(yè)上有一個(gè)按鈕點(diǎn)擊可以喚起App。

這樣的效果是怎么實(shí)現(xiàn)的呢?瀏覽器是一個(gè)app;為什么一個(gè)app可以調(diào)起其他app的頁(yè)面?

說(shuō)到跨app的頁(yè)面調(diào)用,大家是不是能夠想到一個(gè)機(jī)制:Activity的隱式調(diào)用?

隱式啟動(dòng)原理

當(dāng)我們有需要調(diào)起其他app的頁(yè)面時(shí),使用的API就是隱式調(diào)用。

比如我們有一個(gè)app聲明了這樣的Activity:

  1. <activity android:name=".OtherActivity" 
  2.     android:screenOrientation="portrait"
  3.     <intent-filter> 
  4.         <action android:name="mdove"/> 
  5.         <category android:name="android.intent.category.DEFAULT"/> 
  6.     </intent-filter> 
  7. </activity> 

其他App想啟動(dòng)上邊這個(gè)Activity如下的調(diào)用就好:

  1. val intent = Intent() 
  2. intent.action = "mdove" 
  3. startActivity(intent) 

我們沒(méi)有主動(dòng)聲明Activity的class,那么系統(tǒng)是怎么為我們找到對(duì)應(yīng)的Activity的呢?其實(shí)這里和正常的Activity啟動(dòng)流程是一樣的,無(wú)非是if / else的實(shí)現(xiàn)不同而已。

接下來(lái)咱們就回顧一下Activity的啟動(dòng)流程,為了避免陷入細(xì)節(jié),這里只展開(kāi)和大家相對(duì)“耳熟能詳”的類和調(diào)用棧,以串流程為主。

跨進(jìn)程

首先我們必須明確一點(diǎn):無(wú)論是隱式啟動(dòng)還是顯示啟動(dòng);無(wú)論是啟動(dòng)App內(nèi)Activity還是啟動(dòng)App外的Activity都是跨進(jìn)程的。比如我們上述的例子,一個(gè)App想要啟動(dòng)另一個(gè)App的頁(yè)面,至少涉及3個(gè)進(jìn)程。

注意沒(méi)有root的手機(jī),是看不到系統(tǒng)孵化出來(lái)的進(jìn)程的。也就是我們常見(jiàn)的為什么有些代碼打不上斷點(diǎn)。

追過(guò)startActivity()的同學(xué),應(yīng)該很熟悉下邊這個(gè)調(diào)用流程,跟進(jìn)幾個(gè)方法之后就發(fā)現(xiàn)進(jìn)到了一個(gè)叫做ActivityTread的類里邊。

ActivityTread這個(gè)類有什么特點(diǎn)?有main函數(shù),就是我們的主線程。

很快我們能看到一個(gè)比較常見(jiàn)類的調(diào)用:Instrumentation:

  1. // Activity.java 
  2. public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { 
  3.     mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); 
  4.     // 省略 

注意mInstrumentation#execStartActivity()有一個(gè)標(biāo)黃的入?yún)?,它是ActivityThread中的內(nèi)部類ApplicationThread。

ApplicationThread這個(gè)類有什么特點(diǎn),它實(shí)現(xiàn)了IApplicationThread.Stub,也就是aidl的“跨進(jìn)程調(diào)用的客戶端回調(diào)”。

此外mInstrumentation#execStartActivity()中又會(huì)看到一個(gè)大名鼎鼎的調(diào)用:

  1. public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { 
  2.     // 省略... 
  3.     ActivityManager.getService() 
  4.         .startActivity(whoThread, who.getBasePackageName(), intent, 
  5.                 intent.resolveTypeIfNeeded(who.getContentResolver()), 
  6.                 token, target != null ? target.mEmbeddedID : null
  7.                 requestCode, 0, null, options); 
  8.     return null

我們點(diǎn)擊去getService()會(huì)看到一個(gè)標(biāo)紅的IActivityManager的類。

它并不是一個(gè).java文件,而是aidl文件。

所以ActivityManager.getService()本質(zhì)返回的是“進(jìn)程的服務(wù)端”接口實(shí)例,也就是:

ActivityManagerService

public class ActivityManagerService extends IActivityManager.Stub

所以執(zhí)行到這就轉(zhuǎn)到了系統(tǒng)進(jìn)程(system_process進(jìn)程)。省略一下代碼細(xì)節(jié),看一下調(diào)用棧:

從上述debug截圖,看一看到此時(shí)已經(jīng)拿到了我們的目標(biāo)Activitiy的相關(guān)信息。

這里簡(jiǎn)化一些獲取目標(biāo)類的源碼,直接引入結(jié)論:

PackageManagerService

這里類相當(dāng)于解析手機(jī)內(nèi)的所有apk,將其信息構(gòu)造到內(nèi)存之中,比如下圖這樣:

小tips:手機(jī)目錄中/data/system/packages.xml,可以看到所有apk的path、進(jìn)程名、權(quán)限等信息。

啟動(dòng)新進(jìn)程

打開(kāi)目標(biāo)Activity的前提是:目標(biāo)Activity的進(jìn)程啟動(dòng)了。所以第一次想要打開(kāi)目標(biāo)Activity,就意味著要啟動(dòng)進(jìn)程。

啟動(dòng)進(jìn)程的代碼就在啟動(dòng)Activity的方法中:

resumeTopActivityInnerLocked->startProcessLocked。

這里便引入了另一個(gè)另一個(gè)大名鼎鼎的類:ZygoteInit。這里簡(jiǎn)單來(lái)說(shuō)會(huì)通過(guò)ZygoteInit來(lái)進(jìn)行App進(jìn)程啟動(dòng)的。

ApplicationThread

進(jìn)程啟動(dòng)后,繼續(xù)回到目標(biāo)Activity的啟動(dòng)流程。這里依舊是一系列的system_process進(jìn)行的轉(zhuǎn)來(lái)轉(zhuǎn)去,然后IApplicationThread進(jìn)入目標(biāo)進(jìn)程。

注意看,在這里再次通過(guò)IApplicationThread回調(diào)到ActivityThread。

  1. class H extends Handler { 
  2.     // 省略 
  3.     public void handleMessage(Message msg) { 
  4.         switch (msg.what) { 
  5.             case EXECUTE_TRANSACTION: 
  6.                 final ClientTransaction transaction = (ClientTransaction) msg.obj; 
  7.                 mTransactionExecutor.execute(transaction); 
  8.                 // 省略 
  9.                 break; 
  10.             case RELAUNCH_ACTIVITY: 
  11.                 handleRelaunchActivityLocally((IBinder) msg.obj); 
  12.                 break; 
  13.         } 
  14.         // 省略... 
  15.     } 
  16.  
  17. // 執(zhí)行Callback 
  18. public void execute(ClientTransaction transaction) { 
  19.     final IBinder token = transaction.getActivityToken(); 
  20.     executeCallbacks(transaction); 

這里所謂的CallBack的實(shí)現(xiàn)是LaunchActivityItem#execute(),對(duì)應(yīng)的實(shí)現(xiàn):

  1. public void execute(ClientTransactionHandler client, IBinder token, 
  2.         PendingTransactionActions pendingActions) { 
  3.     ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, 
  4.             mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, 
  5.             mPendingResults, mPendingNewIntents, mIsForward, 
  6.             mProfilerInfo, client); 
  7.     client.handleLaunchActivity(r, pendingActions, null); 

此時(shí)就轉(zhuǎn)到了ActivityThread#handleLaunchActivity(),也就轉(zhuǎn)到了咱們?nèi)粘5纳芷诶镞叄{(diào)用棧如下:

上述截圖的調(diào)用鏈中暗含了Activity實(shí)例化的過(guò)程(反射):

  1. public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 
  2.  
  3.     return (Activity) cl.loadClass(className).newInstance(); 
  4.  

瀏覽器啟動(dòng)原理

Helo站內(nèi)的回流頁(yè)就是一個(gè)標(biāo)準(zhǔn)的,瀏覽器喚起另一個(gè)App的實(shí)例。

交互流程

html標(biāo)簽有一個(gè)屬性href,比如:。

我們常見(jiàn)的一種用法:。也就是點(diǎn)擊之后跳轉(zhuǎn)到百度。

因?yàn)檫@個(gè)是前端的標(biāo)簽,依托于瀏覽器及其內(nèi)核的實(shí)現(xiàn),跳轉(zhuǎn)到一個(gè)網(wǎng)頁(yè)似乎很“順其自然”(不然叫什么瀏覽器)。

當(dāng)然這里和android交互的流程基本一致:用隱式調(diào)用的方式,聲明需要啟動(dòng)的Activity;然后傳入對(duì)應(yīng)的協(xié)議(scheme)即可。比如:

前端頁(yè)面:

  1. <head> 
  2.   <meta charset="UTF-8"
  3.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  4. </head> 
  5. <body> 
  6. <a href="mdove1://haha"> 啟動(dòng)OtherActivity </a> 
  7. </body> 

android聲明:

  1. <activity 
  2.     android:name=".OtherActivity" 
  3.     android:screenOrientation="portrait"
  4.     <intent-filter> 
  5.         <data 
  6.             android:host="haha" 
  7.             android:scheme="mdove1" /> 
  8.         <action android:name="android.intent.action.VIEW" /> 
  9.         <category android:name="android.intent.category.BROWSABLE" /> 
  10.         <category android:name="android.intent.category.DEFAULT" /> 
  11.     </intent-filter> 
  12. </activity> 

推理實(shí)現(xiàn)

瀏覽器能夠加載scheme,可以理解為是瀏覽器內(nèi)核做了封裝。那么想要讓android也能支持對(duì)scheme的解析,難道是由瀏覽器內(nèi)核做處理嗎?

很明顯不可能,做了一套移動(dòng)端的操作系統(tǒng),然后讓瀏覽器過(guò)來(lái)實(shí)現(xiàn),是不是有點(diǎn)殺人誅心。

所以大概率能猜測(cè)出來(lái),應(yīng)該是手機(jī)中的瀏覽器app做的處理。我們就基于這個(gè)猜想去看一看瀏覽器.apk的實(shí)現(xiàn)。

瀏覽器實(shí)現(xiàn)

基于上邊說(shuō)的/data/system/packages.xml文件,我們可以pull出來(lái)瀏覽器的.apk。

然后jadx反編譯一下Browser.apk中WebView相關(guān)的源碼:

我們可以發(fā)現(xiàn)對(duì)href的處理來(lái)自于隱式跳轉(zhuǎn),所以一切就和上邊的流程串了起來(lái)。

尾聲

 

結(jié)尾留個(gè)小問(wèn)題:如果我自己寫(xiě)個(gè)WebView去load一個(gè)前端頁(yè)面,能隱式跳轉(zhuǎn)嗎?

 

責(zé)任編輯:武曉燕 來(lái)源: 咸魚(yú)正翻身
相關(guān)推薦

2017-07-20 14:13:38

前端瀏覽器Native App

2012-06-04 10:35:55

FirefoxChrome瀏覽器

2011-02-22 09:50:21

2019-02-13 23:03:06

IE瀏覽器微軟

2024-04-10 09:05:37

2022-02-28 21:15:42

火狐火狐瀏覽器瀏覽器

2009-06-15 08:37:08

微軟Windows 7操作系統(tǒng)

2021-08-30 09:57:40

2016-08-18 14:29:21

瀏覽器Vendor Prefvendor-pref

2021-08-06 10:10:47

Safari開(kāi)發(fā)者瀏覽器

2013-01-11 09:51:03

瀏覽器

2009-03-23 08:52:51

2010-04-05 21:57:14

Netscape瀏覽器

2022-01-04 21:36:33

JS瀏覽器設(shè)計(jì)

2009-03-04 11:16:03

RABSoft瀏覽器控制電腦

2017-01-05 18:57:19

2012-03-20 11:31:58

移動(dòng)瀏覽器

2012-03-19 17:25:22

2012-03-20 11:41:18

海豚瀏覽器

2012-03-20 11:07:08

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)