先簡單的描述一下在廣告方面遇到的問題.
開發(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