去廣告APP學習筆記2——APP的功能實現

#去廣告APP學習筆記2 ###By 月澤java

PrefsFragment模塊中的刷新方法refresh()調用了CheckBoxAdapter,CheckBoxAdapter裏實現了對去廣告操做點擊事件的響應。linux

在CheckBoxAdapter中,url過濾的點擊事件響應爲:android

urlFilter.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        CheckBox cb = (CheckBox) v;
                        boolean value = cb.isChecked();
                        pref.edit()
                            .putBoolean(key + "_url", value)
                            .commit();
                    }
                });

其中web

pref = PreferenceManager.getDefaultSharedPreferences(mContext);

因此選中url過濾的Checkbox事後,會改變PrefsFragment中的SharedPreferenceapi

選中Checkbox,點擊Dialog中的Button確認:app

AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                    builder.setTitle(mContext.getString(R.string.title_settings))
                           .setIcon(R.drawable.ic_launcher)
                           .setView(checkBoxView)
                           .setCancelable(false)
                           .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dlg, int id) {
                                    dlg.dismiss();
                                }
                           })
                           .show();

確認後關閉對話框ide

到這裏,視圖上的流程就結束了,接下來是功能上的流程函數

##去廣告功能的實現過程學習

這裏要先講zygoteui

###zygote

zygote的啓動由linux的祖先init啓動,在android中,應用程序的入口是ActivityThead中的main函數,而建立APP進程 靠的是zygote。在android中提到zygote,主要兩塊,一個是C/C++編寫的zygote,主要用來爲應用和SystemService fork 進程的。一個是java編寫的zygote接口,負責爲應用和service調用C/C++ zygote的接口執行fork,從而建立VM進程。

####zygote在android中主要有兩個做用: 1.創建運行時環境並啓動虛擬機

2.爲應用程序建立DVM進程

總結

在android中SystemService的啓動是在Zygote進程建立好後進行的,而且由Zygote進程創建好DVM運行環境,加載ZygoteInit的main函數,最終調用Zygote的本地方法forkSystemServer,並執行linux的fork方法建立SystemServer進程。 應用程序的進程也是由Zygote建立的,在ActivityManagerService中的startProcessLocked中調用了Process.start()方法。並經過鏈接調用Zygote的native方法forkAndSpecialize,執行fork任務。 應用進程和服務進程位於不一樣的進程中,他們之間是經過IPC進行數據傳遞的。

###使用Xposed提供的hook技術去廣告

在assets建立xposed_init文件,指定XposedHook入口類

tw.fatminmin.xposed.minminguard.Main

Main.java實現IXposedHookLoadPackage接口的handleLoadPackage方法,使用XSharedPreferences獲取保存的數據, 在全部android.app.Activity類及其子類的onCreate方法後面使用XposedHelpers注入去廣告方法XC_MethodHook():

Class<?> activity = XposedHelpers.findClass("android.app.Activity", lpparam.classLoader);
XposedBridge.hookAllMethods(activity, "onCreate", new XC_MethodHook() {
   @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        Context context = (Context) param.thisObject;
        
        if(pref.getBoolean(packageName, false)) {
            adNetwork(packageName, lpparam, false, context);
            appSpecific(packageName, lpparam);
            UrlFiltering.removeWebViewAds(packageName, lpparam, false);
            
            nameBasedBlocking(packageName, lpparam);
            
        }
        else {
            adNetwork(packageName, lpparam, true, context);
        }
    }  
});

其中,adNetwork(packageName, lpparam, false, context);和appSpecific(packageName, lpparam);爲 去除固定廣告商的廣告,會在hook住廣告代碼後插入調用Main.removeAdView方法

而UrlFiltering.removeWebViewAds(packageName, lpparam, false);的做用是找出不屬於已列出廣告商的廣告

首先找到可能含有廣告的類,在 public class UrlFiltering 中:

Class<?> adView = findClass("android.webkit.WebView", lpparam.classLoader);

對這個類裏的全部loadurl、loadData和loadDataWithBaseURL方法進行hook:

XposedBridge.hookAllMethods(adView, "loadUrl", new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    
    String url = (String) param.args[0];
    adExist = urlFiltering(url, "", param, packageName, test);
    if(adExist && !test) {
        param.setResult(new Object());
        }
    }
});
            
XposedBridge.hookAllMethods(adView, "loadData", new XC_MethodHook() {

    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

    String data = (String) param.args[0];
    adExist = urlFiltering("", data, param, packageName, test);
    if(adExist && !test) {
    param.setResult(new Object());
        }
    }

});

XposedBridge.hookAllMethods(adView, "loadDataWithBaseURL", new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
    String url = (String) param.args[0];
    String data = (String) param.args[1];
    adExist = urlFiltering(url, data, param, packageName, test);
    if(adExist && !test) {
        param.setResult(new Object());
        }
    }
});

經過以上代碼,hook了android.webkit.WebView類以及它的子類的loadUrl、loadData和loadDataWithBaseURL方法。 在這些方法被調用前,先執行了添加的代碼。在添加的代碼裏面,接收了傳遞給原方法的url參數,調用了urlFiltering() 來處理過濾

static private boolean urlFiltering(String url, String data, MethodHookParam param, String packageName, boolean test) {

array = url.split("[/\\s):]");

for(String hostname : array) {

    hostname = hostname.trim();

    if(hostname.contains(".") &&  hostname.length() > 5 && hostname.length() < 50) {

        if(Main.urls.contains(hostname)) {

            Util.log(packageName, "Detect Ads(url) with hostname: " + hostname + " in " + packageName);
            if(!test) {
                param.setResult(new Object());
                Main.removeAdView((View) param.thisObject, packageName, false);
                return true;
            }
            break;
        }
    }
}

urlFiltering()把接收到的url參數解析分段,並和initZygote方法中,從用modulePath下host/output_file裏解析出來 的urls進行對比,有相同的就調用Main.java中的removeAdView()方法,去除AdView。

initZygote方法中得到urls

在host/output_file收集了大量的已知url,在initZygote方法中得到:

public void initZygote(StartupParam startupParam) throws Throwable {
        
    pref = new XSharedPreferences(MY_PACKAGE_NAME);
    Util.pref = pref;
        
    MODULE_PATH = startupParam.modulePath;

    res = XModuleResources.createInstance(MODULE_PATH, null);
    byte[] array = XposedHelpers.assetAsByteArray(res, "host/output_file");
    String decoded = new String(array);
    String[] sUrls = decoded.split("\n");

    urls = new HashSet<String>();
    for(String url : sUrls) {
        urls.add(url);
    }
}

廣告View的去除 removeAdView()

調用removeAdView()時,把接收參數所在對象的View發送過去

Main.removeAdView((View) param.thisObject, packageName, false);

在removeAdView()中對View的高度設置爲0,就不顯示廣告了,若是被去除的View有父類,對它的父類也調用removeAdView()

public static void removeAdView(final View view, final String packageName, final boolean apiBased) {

    if(convertPixelsToDp(view.getHeight()) > 0 && convertPixelsToDp(view.getHeight()) <= 55) {

        LayoutParams params = view.getLayoutParams();
        params.height = 0;
        view.setLayoutParams(params);
    }
    ViewTreeObserver observer= view.getViewTreeObserver();
    observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            float heightDp = convertPixelsToDp(view.getHeight());
            if(heightDp <= 55) {

                LayoutParams params = view.getLayoutParams();
                params.height = 0;
                view.setLayoutParams(params);
            }
        }
    });

    if(view.getParent() != null && view.getParent() instanceof ViewGroup) {
        removeAdView((View)view.getParent(), packageName, apiBased);
    }
}
相關文章
相關標籤/搜索