成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

系統 瀏覽器
我們沒有主動聲明Activity的class,那么系統是怎么為我們找到對應的Activity的呢?其實這里和正常的Activity啟動流程是一樣的,無非是if / else的實現不同而已。

[[425568]]

本文轉載自微信公眾號「咸魚正翻身」,作者MDove。轉載本文請聯系咸魚正翻身公眾號。

疑問的開端

大家有沒有想過一個問題:在瀏覽器里打開某個網頁,網頁上有一個按鈕點擊可以喚起App。

這樣的效果是怎么實現的呢?瀏覽器是一個app;為什么一個app可以調起其他app的頁面?

說到跨app的頁面調用,大家是不是能夠想到一個機制:Activity的隱式調用?

隱式啟動原理

當我們有需要調起其他app的頁面時,使用的API就是隱式調用。

比如我們有一個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想啟動上邊這個Activity如下的調用就好:

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

我們沒有主動聲明Activity的class,那么系統是怎么為我們找到對應的Activity的呢?其實這里和正常的Activity啟動流程是一樣的,無非是if / else的實現不同而已。

接下來咱們就回顧一下Activity的啟動流程,為了避免陷入細節,這里只展開和大家相對“耳熟能詳”的類和調用棧,以串流程為主。

跨進程

首先我們必須明確一點:無論是隱式啟動還是顯示啟動;無論是啟動App內Activity還是啟動App外的Activity都是跨進程的。比如我們上述的例子,一個App想要啟動另一個App的頁面,至少涉及3個進程。

注意沒有root的手機,是看不到系統孵化出來的進程的。也就是我們常見的為什么有些代碼打不上斷點。

追過startActivity()的同學,應該很熟悉下邊這個調用流程,跟進幾個方法之后就發現進到了一個叫做ActivityTread的類里邊。

ActivityTread這個類有什么特點?有main函數,就是我們的主線程。

很快我們能看到一個比較常見類的調用: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()有一個標黃的入參,它是ActivityThread中的內部類ApplicationThread。

ApplicationThread這個類有什么特點,它實現了IApplicationThread.Stub,也就是aidl的“跨進程調用的客戶端回調”。

此外mInstrumentation#execStartActivity()中又會看到一個大名鼎鼎的調用:

  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

我們點擊去getService()會看到一個標紅的IActivityManager的類。

它并不是一個.java文件,而是aidl文件。

所以ActivityManager.getService()本質返回的是“進程的服務端”接口實例,也就是:

ActivityManagerService

public class ActivityManagerService extends IActivityManager.Stub

所以執行到這就轉到了系統進程(system_process進程)。省略一下代碼細節,看一下調用棧:

從上述debug截圖,看一看到此時已經拿到了我們的目標Activitiy的相關信息。

這里簡化一些獲取目標類的源碼,直接引入結論:

PackageManagerService

這里類相當于解析手機內的所有apk,將其信息構造到內存之中,比如下圖這樣:

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

啟動新進程

打開目標Activity的前提是:目標Activity的進程啟動了。所以第一次想要打開目標Activity,就意味著要啟動進程。

啟動進程的代碼就在啟動Activity的方法中:

resumeTopActivityInnerLocked->startProcessLocked。

這里便引入了另一個另一個大名鼎鼎的類:ZygoteInit。這里簡單來說會通過ZygoteInit來進行App進程啟動的。

ApplicationThread

進程啟動后,繼續回到目標Activity的啟動流程。這里依舊是一系列的system_process進行的轉來轉去,然后IApplicationThread進入目標進程。

注意看,在這里再次通過IApplicationThread回調到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. // 執行Callback 
  18. public void execute(ClientTransaction transaction) { 
  19.     final IBinder token = transaction.getActivityToken(); 
  20.     executeCallbacks(transaction); 

這里所謂的CallBack的實現是LaunchActivityItem#execute(),對應的實現:

  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); 

此時就轉到了ActivityThread#handleLaunchActivity(),也就轉到了咱們日常的生命周期里邊,調用棧如下:

上述截圖的調用鏈中暗含了Activity實例化的過程(反射):

  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.  

瀏覽器啟動原理

Helo站內的回流頁就是一個標準的,瀏覽器喚起另一個App的實例。

交互流程

html標簽有一個屬性href,比如:。

我們常見的一種用法:。也就是點擊之后跳轉到百度。

因為這個是前端的標簽,依托于瀏覽器及其內核的實現,跳轉到一個網頁似乎很“順其自然”(不然叫什么瀏覽器)。

當然這里和android交互的流程基本一致:用隱式調用的方式,聲明需要啟動的Activity;然后傳入對應的協議(scheme)即可。比如:

前端頁面:

  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"> 啟動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> 

推理實現

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

很明顯不可能,做了一套移動端的操作系統,然后讓瀏覽器過來實現,是不是有點殺人誅心。

所以大概率能猜測出來,應該是手機中的瀏覽器app做的處理。我們就基于這個猜想去看一看瀏覽器.apk的實現。

瀏覽器實現

基于上邊說的/data/system/packages.xml文件,我們可以pull出來瀏覽器的.apk。

然后jadx反編譯一下Browser.apk中WebView相關的源碼:

我們可以發現對href的處理來自于隱式跳轉,所以一切就和上邊的流程串了起來。

尾聲

 

結尾留個小問題:如果我自己寫個WebView去load一個前端頁面,能隱式跳轉嗎?

 

責任編輯:武曉燕 來源: 咸魚正翻身
相關推薦

2017-07-20 14:13:38

前端瀏覽器Native App

2012-06-04 10:35:55

FirefoxChrome瀏覽器

2011-02-22 09:50:21

2022-02-28 21:15:42

火狐火狐瀏覽器瀏覽器

2019-02-13 23:03:06

IE瀏覽器微軟

2024-04-10 09:05:37

2009-06-15 08:37:08

微軟Windows 7操作系統

2021-08-30 09:57:40

2016-08-18 14:29:21

瀏覽器Vendor Prefvendor-pref

2021-08-06 10:10:47

Safari開發者瀏覽器

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瀏覽器設計

2009-03-04 11:16:03

RABSoft瀏覽器控制電腦

2017-01-05 18:57:19

2012-03-20 11:41:18

海豚瀏覽器

2012-03-20 11:31:58

移動瀏覽器

2012-03-19 17:25:22

2012-03-20 11:07:08

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区二区三区在线 | 国产精品视频一区二区三区不卡 | www日韩欧美 | 国产精品久久在线 | 午夜免费看 | 蜜桃日韩 | 国产精品乱码一区二区三区 | www.国产| 中文字幕精品一区二区三区精品 | 亚洲精品久久久一区二区三区 | a级片www| 午夜国产一级片 | 伊人网在线播放 | 久久69精品久久久久久久电影好 | 日韩精品成人av | 日韩av一区二区在线 | 国产精品久久久久久久久久 | 韩国主播午夜大尺度福利 | 日韩在线观看一区二区三区 | 欧美精品日韩精品国产精品 | 亚洲一区二区三区 | 国产成人jvid在线播放 | 欧美精品国产一区二区 | 成人国产一区二区三区精品麻豆 | 亚洲+变态+欧美+另类+精品 | 欧美xxxx网站 | 99看片网 | 九九热在线精品视频 | 欧美精三区欧美精三区 | 九九在线| 中文字幕日本一区二区 | 91精品国模一区二区三区 | 精品久久久久久久久久 | 亚洲在线一区二区 | 久久精品一 | 成人在线视频免费观看 | av中文字幕在线观看 | 8x国产精品视频一区二区 | 欧美不卡在线 | a欧美| 99精品欧美一区二区三区 |