Android 跨進程渲染

本項目用於驗證 Android 是否能夠跨進程渲染 View,最終實現了在子進程創建WebView,主進程顯示的功能。

一、跨進程渲染的意義

  • 有一些組件比如 WebView 如果在主進程初始化,會大大增加主進程的內存佔用。
  • 部分組件設計時只支持特定進程,但是又想在別的進程顯示該組件的View
  • 一些其它限制…

二、跨進程渲染的實現

1.提供一個Surface

跨進程渲染,需要一個Surface對象,本項目中使用SurfaceView提供。
Surface實現了Parcelable接口,也就意味着它可以跨進程傳遞。

具體邏輯參考:ProcessSurfaceView,通過bindService拉起子進程Service後,將Surface通過接口傳遞給了子進程。

// bindService 拉起子進程
private fun bindService() {
    val intent = Intent(context, RemoteDrawService::class.java)
    context.bindService(intent, this@ProcessSurfaceView, Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT)
}

// 子進程連接後,拿到 AIDL 接口傳遞 Surface 到子進程
override fun onServiceConnected(p0: ComponentName?, iBinder: IBinder?) {
    Log.i(TAG, "onServiceConnected")
    if (iBinder == null) {
        Log.e(TAG, "onServiceDisconnected: iBinder is null.")
        return
    }
    iRemoteDraw = IRemoteDraw.Stub.asInterface(iBinder)
    setSurfaceToRemote()
}

ps:SurfaceView退後台需要額外處理,本項目中是在退後台後重新創建了渲染的View。

2.子進程創建View

收到主進程的Surface後,需要創建VirtualDisplayPresentation

val displayManager = service.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val dm = service.resources.displayMetrics
val virtualDisplay = displayManager.createVirtualDisplay("", dm.widthPixels, dm.heightPixels, dm.densityDpi, surface, 0)
val presentation = Presentation(service, virtualDisplay.display)
presentation.setContentView(createView())
presentation.show()

至此,就實現了跨進程渲染。效果如下:
實現效果

3.點擊事件處理

由於渲染的View是在另外一個進程,所以收不到點擊事件。這裡的實現方案是將SurfaceView的點擊事件跨進程傳遞到子進程。

override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
    return iRemoteDraw?.dispatchTouchEvent(event) ?: false
}

由於是跨進程傳遞,事件的處理效率肯定變低了。因此跨進程渲染適合那些不怎麼依賴點擊事件的顯示,比如播放視頻。

三、優點和缺點

可見跨進程渲染的顯示還是相對簡單的,系統提供了相應的能力。使用時,需要注意其的限制。
1.優點

  • 將渲染交給了其它進程,降低了本進程內存佔用。
  • 有一些業務場景只能通過跨進程渲染實現。

2.缺點

  • 需要額外處理點擊事件。包括如果需要跟渲染對象進行交互,都需要提供跨進程接口,處理效率變低。
  • 複雜程度變高。
Tags: