文档章节

去广告APP学习笔记2——APP的功能实现

 明丶泽
发布于 2015/05/17 22:16
字数 1183
阅读 105
收藏 0

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

#去广告APP学习笔记2 ###By 月泽

PrefsFragment模块中的刷新方法refresh()调用了CheckBoxAdapter,CheckBoxAdapter里实现了对去广告操作点击事件的响应。

在CheckBoxAdapter中,url过滤的点击事件响应为:

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

其中

pref = PreferenceManager.getDefaultSharedPreferences(mContext);

所以选中url过滤的Checkbox过后,会改变PrefsFragment中的SharedPreference

选中Checkbox,点击Dialog中的Button确认:

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

确认后关闭对话框

到这里,视图上的流程就结束了,接下来是功能上的流程

##去广告功能的实现过程

这里要先讲zygote

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

© 著作权归作者所有

粉丝 0
博文 2
码字总数 1787
作品 0
程序员
私信 提问
PHP再学习5——RESTFul框架 远程控制LED

0.前言 去年(2013年)2月第一次接触yeelink平台,当时该平台已经运行了一些时间也吸引了不少极客。试想自己也将投身IoT(物联网)行业,就花了些时间研究了它。陆陆续续使用和研究了一年,大...

thinkyoung
2014/12/02
0
0
Kotlin Weekly 中文周报 —— 16

Kotlin 开发中文周报 文章 Android 开发者的一些实用技巧。(github.com) Ravindra 在 DevfestAhm 2017 的演讲。 像 Kotlin 的专家一样测试 。(blog.karumi.com) 将 Kotlin 库发布到 jCente...

DoubleThunder
2017/11/13
0
0
架构学习资料汇总

知名网站架构分析 探索Google App Engine背后的奥秘(1)–Google的核心技术 探索Google App Engine背后的奥秘(2)–Google的整体架构猜想 探索Google App Engine背后的奥秘(3)- Google App Eng...

peter8015
2016/04/22
306
0
笔记 App 领导者——印象笔记

一直以来,我都认为用纸笔记笔记是一种非常有情怀的方式,有一种掌握知识的踏实感。 但是,当到了大学,我有无数种类型的大大小小的知识都需要整理归纳,而不像高中不仅仅局限于数学、英语、...

沉思CONTEMPLATE
2017/11/03
0
0
小蚂蚁学习APP接口开发(9)—— APP版本升级的接口实例

今天的笔记主要来记录最后两个知识要点之一,APP客户端版本升级的接口开发。 APP的开发主要由伟大而神圣的用户来更新,更新的流程应该是这样的: 开启APP——请求初始化接口init.php——检测...

嗜学如命的小蚂蚁
2015/10/21
3.1K
1

没有更多内容

加载失败,请刷新页面

加载更多

如何更改iOS应用程序的名称?

我前几天用一个愚蠢的开发代码名称开始了一个iPhone项目,现在我想改变项目的名称,因为它已经接近完成了。 但是我不知道如何使用Xcode来做这件事,尝试在info.plist文件中更改应用程序的名称...

技术盛宴
28分钟前
3
0
关于win10tensorflow的配置(CPU+GPU)

主要内容 CPU篇 GPU篇 【前期准备与注意事项】 环境:window1064位+python36(37)+CUDA9.0+cuDNN7.6+tensorflow_gpu-1.12.0 软件:anaconda+pycharm 硬件:有nvidia显卡的笔记本或台式(非A...

放只虎归个山
36分钟前
3
0
C#中的多行字符串文字

有没有一种简单的方法可以在C#中创建多行字符串文字? 这是我现在所拥有的: string query = "SELECT foo, bar"+ " FROM table"+ " WHERE id = 42"; 我知道PHP有 <<<BLOCKBLOCK; C#是......

javail
44分钟前
4
0
微信支付之小微商户扫盲!支持信用卡,免营业执照!

微信支付商户申请面向线下小微商户开放,符合条件的微信支付服务商可为小微商户发起接入申请。无需营业执照。 小微商户日收款额度为5万元~30万元 。 信用卡支付日限额为1千;月限额1万。 结算...

吴伟祥
今天
4
0
大话SDWebImage(三)-- 图片下载层

四、图片下载层 SDWebImageDownloader是处理图片下载的类 4.1 图片下载步骤 首先介绍下dispatch_barrier,GCD中的dispatch_barrier目的是在并发队列实现串行的效果,创建下载任务SDWebImageD...

aron1992
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部