Android感知View的出現

最近在做一個播放組件端上防作弊的效果,播放的時候判斷廣告是否被部分或者完全遮擋了。

比較重要的載體是Window,實際的表現形式是View,所以重點往監控Window和View的創建這樣的思路出發。

目前的話一共想到三種方案:
①監控View的創建:
通過給LayoutInflater 設置factory2來感知View的創建:

LayoutInflater.from(context).setFactory2()

當LayoutInflater調用createViewFromTag時:

 View view;
  	 if (mFactory2 != null) {
  	     view = mFactory2.onCreateView(parent, name, context, attrs);
  	 } else if (mFactory != null) {
  	     view = mFactory.onCreateView(name, context, attrs);
  	 } else {
  	     view = null;
  	 }

給設置的factory2接口方法onCreateView就能感知到。
缺點是場景有限,只有需要解析xml生成View的時候才能感知到,直接new View就不行了。

②監聽Window的創建
創建Window是這麼創建的:

Window w = PolicyManager.makeNewWindow(mContext);
#PolicyManager
public static Window makeNewWindow(Context context) {
    return sPolicy.makeNewWindow(context);
}

而這個spolicy是個靜態接口對象
private static final IPolicy sPolicy;
實現是com.android.internal.policy.impl.Policy
所以我們可以hook這個sPolicy對象,感知makeNewWindow的時機。
缺點:高版本中不用PolicyManager創建Window,相應的代碼處直接變爲new PhoneWindow()

③監聽WindowManager.addView方法
WindowManagerService服務在app本地的代理對象爲WindowMangerImpl,而裏面依賴了WindowManagerGlobal這個對象,它是個單例,但是不是個接口,就不能用動態代理的方式去hook。hook WindowManagerGlobal的好處是兼容性強,這個類從低版本到高版本幾乎沒修改過。

在WindowManagerGlobal有幾個:

private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
        new ArrayList<WindowManager.LayoutParams>();

他們會在調用addView,removeView,updateView時做操作,所以通過hook這些集合也可以感知到addView的出現:寫一個繼承ArrayList的ObserverList,覆寫add的方法,替換回去。

還有一個比較關鍵的是需要知道加進來這個View在屏幕的位置,通過是
view.getLocationOnScreen(int[])
這個方法可以得到view在屏幕的位置,接着拿到view的寬高判斷和我們View的重疊和層級關係。

④跨進程的Dialog,比如crash彈窗:
這個可以根據點擊事件的MotionEvent的flag來判斷是否有遮擋:
https://stackoverflow.com/questions/44197671/detecting-android-screen-overlays-programmatically

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章