Android插件化突破應(yīng)用市場無法上廣告的問題

先簡單的描述一下在廣告方面遇到的問題.
開發(fā)一款A(yù)pp有了一定的用戶量之后通常會想接入第三方廣告來實現(xiàn)變現(xiàn),
然而在很多市場不讓這類帶廣告的App上架,除非接的是他們家的廣告.

在這里我只能呵呵了.這點困難就想難倒我們.

那接下來ShowTime.怎么做呢?
沒錯,就是插件化.
以廣點通廣告為例
這里我使用的是360開源的RePlugin,具體介紹和使用方法請看官方文檔.

一.RePlugin插件接入指南
第 1 步:添加 RePlugin Plugin Gradle 依賴
在項目根目錄的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-plugin-gradle 依賴:

 buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.0'
        ...
    }
}

第 2 步:添加 RePlugin Plugin Library 依賴
在 app/build.gradle 中應(yīng)用 replugin-plugin-gradle 插件索烹,并添加 replugin-plugin-lib 依賴:

apply plugin: 'replugin-plugin-gradle'

dependencies {
    compile 'com.qihoo360.replugin:replugin-plugin-lib:2.2.0'
    ...
}

接下來您就可以像正常接入廣告那樣端礼,開發(fā)插件。生成出來的是APK目代,既可以“安裝到設(shè)備”翎迁,又可以“作為插件”使用栋猖。

二.RePlugin主程序接入指南
第 1 步:添加 RePlugin Host Gradle 依賴
在項目根目錄的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-host-gradle 依賴:

buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.0'
        ...
    }
}

第 2 步:添加 RePlugin Host Library 依賴
在 app/build.gradle 中應(yīng)用 replugin-host-gradle 插件,并添加 replugin-host-lib 依賴:

apply plugin: 'replugin-host-gradle'
repluginHostConfig {
    useAppCompat = true
}
dependencies {
    ...
    compile 'com.qihoo360.replugin:replugin-host-lib:2.1.7'
}

第 3 步:配置 Application 類

public class App extends Application{
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        RePlugin.App.attachBaseContext(this);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        RePlugin.App.onCreate();
    }
}

三.宿主App調(diào)用插件廣告
1.編譯插件廣告,將生成的xx.apk包重命名xx.jar
將 xx.jar放到宿主App的 assets/plugins 目錄下 , Replugin將會自動獲取該內(nèi)置插件

2.處理廣點通開屏廣告
由于廣點通開屏廣告的展示點擊都由SDK封裝處理了.
我們這里采用的方式是,由宿主跳轉(zhuǎn)到插件的閃屏頁,在插件中完成請求,展示,點擊結(jié)束后回到宿主的主頁面.
(1)宿主跳轉(zhuǎn)到插件Activity

    try {
        String config = AssetsUtils.readText(MainActivity.this, "ad_config.json");
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("adPlugin", "com.plugin.ad.LogoActivity"));
        intent.putExtra("EXTRA_CONFIG", config);
        intent.putExtra("EXTRA_POI", POI_FIRST);
        RePlugin.startActivity(MainActivity.this, intent);
    } catch (Throwable e) {
        e.printStackTrace();
    }

(2)插件開屏廣告請求處理,就按正常的廣告邏輯走

(3)插件回到宿主的主頁面

 private void intoMainPage() {
        //TODO 打開宿主應(yīng)用
        Intent intent = new Intent();
        intent.setClassName("com.wifi.robot", "com.wifi.robot.ui.SecondActivity");
        startActivity(intent);
        finish();
    }

(4)宿主的清單文件中添加必要配置,否則廣告無反應(yīng)

  <!-- 廣點通廣告 -->
        <service
            android:name="com.qq.e.comm.DownloadService"
            android:exported="false" />
        <activity
            android:name="com.qq.e.ads.ADActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize" />

注意 : 盡量使宿主和插件的包名一致,已避免廣告無收益

3.處理廣點通原生廣告
廣點通原生廣告不同于開屏廣告,其展示曝光和點擊曝光都由自己處理.
我們只能通過反射的方案去請求廣告

(1)在插件中先對廣告請求做一層封裝

package com.plugin.ad.managers;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import com.plugin.ad.listeners.ILoadListener;
import com.plugin.ad.models.ADModel;
import com.plugin.ad.models.ModelManager;
import com.plugin.ad.utils.JsonUtil;

/**
 * 廣告請求管理類
 * Created by wong on 17-8-16.
 */
public class LoadManager {
    /**
     * 初始化
     *
     * @param config
     */
    private static void onInit(String config) {
        ADModel adModel = JsonUtil.getInstance().fromJson(config, ADModel.class);
        ModelManager.operationNativeConfig(adModel.place_ids);
        ModelManager.putAdKey("gdt", adModel.gdt_key);
    }

    /**
     * 請求原生廣告
     *
     * @param context
     * @param config
     * @param poi
     * @param listener
     */
    public static void requestNativeAD(Context context, String config, int poi, final ILoadListener listener) {
        onInit(config);
        LoadGdtManager.loadAD(context,poi,listener);
    }
}

 /**
     * 請求廣告
     *
     * @param context  必須是該插件的Context
     * @param poi
     * @param listener
     */
    public static void loadAD(Context context, final int poi, final ILoadListener listener) {
        NativeAD nativeAD = new NativeAD(context, getGdtKey(), getGdtId(poi), new NativeAD.NativeAdListener() {
            @Override
            public void onADLoaded(List<NativeADDataRef> list) {
                if (null != list && !list.isEmpty()) {
                    listener.gdtNativeSuccess(poi, list.get(0));
                } else {
                    listener.failure("Empty");
                }
            }

            @Override
            public void onNoAD(int i) {
                listener.failure("onNoAD->" + i);
            }

            @Override
            public void onADStatusChanged(NativeADDataRef nativeADDataRef) {
            }

            @Override
            public void onADError(NativeADDataRef nativeADDataRef, int i) {

            }
        });
        nativeAD.loadAD(1);
    }

(2)宿主中反射LoadManager的requestNativeAD()方法
a.拿到插件的ClassLoader

     ClassLoader classLoader = RePlugin.fetchClassLoader("adPlugin");

b.取得需要反射的類

    Class<?> methodClass = classLoader.loadClass("com.plugin.ad.managers.LoadManager");

c.由于請求廣告的requestNativeAD()方法中有一個參數(shù)是接口.
(這里得使用動態(tài)代理)
取得被代理接口

            /**
             * 被代理的接口
             */
            Class<?> callBackClass = classLoader.loadClass("com.plugin.ad.listeners.ILoadListener");
            /**
             * 這個是動態(tài)代理
             * callBackClass : 需要被代理的接口的Class
             * proxListener : 返回的是這個被代理接口的實例
             */
            Object proxListener = LoadCallBackProx.getInstance(classLoader, callBackClass);

d.接下來就是反射請求接口了

            /**
             * callBackClass : 被代理的接口的Class
             * proxListener : 被代理接口的實例
             */
            Method load = methodClass.getDeclaredMethod("requestNativeAD", new Class[]{Context.class, String.class, int.class, callBackClass});
            load.invoke(null, RePlugin.fetchContext("adPlugin"), config, poi, proxListener);

注意傳入的Context必須是插件的Context
e.在動態(tài)代理中取得回調(diào)

public class LoadCallBackProx implements InvocationHandler {
    /**
     * 這里的話直接去獲取對象,
     * 把這個接口的字節(jié)碼對象數(shù)組扔進(jìn)來就可以了
     */
    public static Object getInstance(ClassLoader classLoader, Class<?> interfaces) {
        return Proxy.newProxyInstance(classLoader, new Class[]{interfaces}, new LoadCallBackProx());
    }

    /**
     * @param o
     * @param method  : 具體的方法名稱
     * @param objects : 被代理類的回調(diào)方法參數(shù)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        //判斷是什么方法被調(diào)用了嘛
        String methodName = method.getName();
        if ("gdtNativeSuccess".equals(methodName)) {
            int poi = (Integer) objects[0];
            Object object = objects[1];
            ReAdFactory.putNativeAD(poi, object);
            EventHelper.post(new ReNativeAdEvent(poi, object, true));
        } else if (methodName.equals("failure")) {
            EventHelper.post(new ReNativeAdEvent(false));
        }
        return null;
    }
}

這里我使用了EventBus將回調(diào)的廣告?zhèn)鞯秸埱蟮慕缑嬷?/p>


    /**
     * 原生信息流
     * 插件廣告請求
     *
     * @param event
     */
    @Subscribe
    public void onEventReADEvent(ReNativeAdEvent event) {
        if (event.isSucc) {
            //成功
            try {
                Object object = ReAdFactory.getNativeAD(POI_SECOND);
                //展示
                String title = (String) object.getClass().getMethod("getTitle").invoke(object);
                String iconUrl = (String) object.getClass().getMethod("getIconUrl").invoke(object);
                //反射調(diào)用曝光接口
                object.getClass().getMethod("onExposured", new Class[]{View.class}).invoke(object, findViewById(R.id.activity_main));
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            //失敗
        }
    }

點擊曝光的反射

    /**
     * 點擊原生信息流廣告
     */
    private void clickNativeAD() {
        try {
            Object object = ReAdFactory.getNativeAD(POI_SECOND);
            //調(diào)用點擊曝光接口
            object.getClass().getMethod("onClicked", new Class[]{View.class}).invoke(object, findViewById(R.id.activity_main));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

四.最后,第一次寫文章,歡迎點評
宿主App : https://github.com/AndWong/RePluginHostForAD/tree/master/app
插件App : https://github.com/AndWong/RePluginHostForAD/tree/master/pluginApp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汪榔,一起剝皮案震驚了整個濱河市蒲拉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痴腌,老刑警劉巖雌团,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異士聪,居然都是意外死亡锦援,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門剥悟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灵寺,“玉大人,你說我怎么就攤上這事区岗÷园澹” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵慈缔,是天一觀的道長叮称。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么瓤檐? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任赂韵,我火速辦了婚禮,結(jié)果婚禮上挠蛉,老公的妹妹穿的比我還像新娘祭示。我一直安慰自己,他們只是感情好谴古,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布绍移。 她就那樣靜靜地躺著,像睡著了一般讥电。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上轧抗,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天恩敌,我揣著相機與錄音,去河邊找鬼横媚。 笑死纠炮,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灯蝴。 我是一名探鬼主播恢口,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼穷躁!你這毒婦竟也來了耕肩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤问潭,失蹤者是張志新(化名)和其女友劉穎猿诸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狡忙,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梳虽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灾茁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窜觉。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖北专,靈堂內(nèi)的尸體忽然破棺而出禀挫,到底是詐尸還是另有隱情,我是刑警寧澤逗余,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布特咆,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腻格。R本人自食惡果不足惜画拾,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菜职。 院中可真熱鬧青抛,春花似錦、人聲如沸酬核。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫡意。三九已至举瑰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔬螟,已是汗流浹背此迅。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留旧巾,地道東北人耸序。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像鲁猩,于是被迫代替她去往敵國和親坎怪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容