返回博客首页
← 所有文章

使用 AdMob 货币化您的 NativeScript 应用(第 2 部分 - Android)

2015 年 12 月 16 日 — 作者 Nikolay Diyanov

或者如何在 iOS 和 Android 都涉及的情况下创建一个简单的模块

这是“使用 AdMob 货币化您的 NativeScript 应用”博客的第二部分。在第一部分中,我讨论了如何在 JavaScript 中轻松访问和使用原生框架的原生 API,并以 iOS 的 AdMob 库为例。为此,您只需要现有的 AdMob 文档,并结合一些简单的规则将 Objective-C API 调用转换为 JavaScript。

然而,流行的移动平台有两个——iOS 和 Android。因此,如果我也涵盖 Android 则会很公平。此外,在本文的后面,随着事情变得越来越有趣,并且我们即将实现跨平台的 AdMob 使用,我将向您展示在 NativeScript 项目中分离和隔离原生 API 调用的推荐方法。因此,让我们开始吧。

googleads-bannerview-android googleads-interstitial-android

就像在 iOS 案例中一样,我们将重点关注这两种类型的广告

  • 横幅广告 - 这些广告出现在应用程序底部,不会中断用户体验
  • 插页式广告 - 这些广告会不时出现,占据整个屏幕
按照上面的文档链接,并在NativeScript 文档的帮助下,以下是如何在 NativeScript 应用中插入 AdMob SDK。

先决条件

我们在最新版本的 NativeScript 中进行了重要的更新,允许调用私有类的 Java 方法。这将允许使用 Android AdMob API。因此,请使用以下命令检查您的版本:

tns --version

它应该显示 1.5.2

如果不是这种情况,请按照这篇文档文章更新您的 NativeScript 版本。

关于 AdMob SDK 本身,它作为 Google Play 服务的一部分进行分发。因此,我扩展了我的nativescript-google-mobile-ads-sdk 插件,使其依赖于 Google Play 服务。

我们将在博客第 1 部分中创建的相同应用中启用横幅广告和插页式广告。因此,请确保您首先从这些应用中卸载旧版本的插件

tns plugin remove nativescript-google-mobile-ads-sdk

然后重新添加它

tns plugin add nativescript-google-mobile-ads-sdk

现在是编码时间了!

使用横幅广告

就像在 iOS 案例中一样,我们的 Android 横幅广告将托管在占位符creatingView 事件中。考虑到现有的 iOS 实现,我们的代码如下所示

function creatingView(args) {
    if(platformModule.device.os == platformModule.platformNames.ios) {
        bannerView = GADBannerView.alloc().initWithAdSize(kGADAdSizeSmartBannerPortrait);
        args.view = bannerView;
    }
    else {
        var bannerView = new com.google.android.gms.ads.AdView(args.object._context);
        bannerView.setAdSize(com.google.android.gms.ads.AdSize.SMART_BANNER);
        args.view = bannerView;
    }
}
 
exports.creatingView = creatingView;

现在是时候在页面的 loaded 事件中设置 bannerView 的一些重要属性了

bannerView = placeholder.android;
bannerView.setAdUnitId("ca-app-pub-3940256099942544/6300978111");
 
var MyAdListener = com.google.android.gms.ads.AdListener.extend(
{
    onAdLeftApplication: function() {
        // 当用户因点击广告而离开应用时执行某些操作
        console.log("Leaving the app, bye bye!");
    }
});
var listener = new MyAdListener();
bannerView.setAdListener(listener);
 
var adRequest = new com.google.android.gms.ads.AdRequest.Builder();
adRequest.addTestDevice(com.google.android.gms.ads.AdRequest.DEVICE_ID_EMULATOR);
var requestBuild = adRequest.build();
bannerView.loadAd(requestBuild);

为页面的 loaded 事件调用的 pageLoaded 函数的完整源代码将是

function pageLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
 
    var placeholder = page.getViewById("bannerView");
    var bannerView;
 
    if(platformModule.device.os == platformModule.platformNames.ios) {
        bannerView = placeholder.ios;
        bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716";
        bannerView.strongDelegateRef = bannerView.delegate = GADBannerViewDelegateImpl.new();
        bannerView.rootViewController = page.ios;
        var request = GADRequest.request();
        request.testDevices = [kGADSimulatorID];
        bannerView.loadRequest(request);
    }
    else {
        bannerView = placeholder.android;
        bannerView.setAdUnitId("ca-app-pub-3940256099942544/6300978111");
         
        var MyAdListener = com.google.android.gms.ads.AdListener.extend(
        {
            onAdLeftApplication: function() {
                // 当用户因点击广告而离开应用时执行某些操作
                console.log("Leaving the app, bye bye!");
            }
        });
        var listener = new MyAdListener();
        bannerView.setAdListener(listener);
         
        var adRequest = new com.google.android.gms.ads.AdRequest.Builder();
        adRequest.addTestDevice(com.google.android.gms.ads.AdRequest.DEVICE_ID_EMULATOR);
        var requestBuild = adRequest.build();
        bannerView.loadAd(requestBuild);
    }
}
 
exports.pageLoaded = pageLoaded;

这里有趣的是使用了AdListener,它是在 Java 中的一个抽象类。当这种类型的对象附加到我们的横幅广告时,它允许我们处理与横幅广告相关的各种有趣事件——例如,当横幅广告被点击并且应用程序转到后台,转而显示与广告相关的目标网页的浏览器。

以下是结果



横幅 NativeScript 应用的完整 iOS + Android 源代码可以在GitHub上找到。

使用插页式广告

再说一遍……插页式广告……这些“干扰用户体验”的广告……让我们看看它们是如何实现的。

首先,我们应该在页面的 loaded 事件中创建插页式广告,或者换句话说,在 pageLoaded 函数中创建

var interstitial = new com.google.android.gms.ads.InterstitialAd(page._context);
interstitial.setAdUnitId("ca-app-pub-3940256099942544/1033173712");
 
var MyAdListener = com.google.android.gms.ads.AdListener.extend(
{
    onAdClosed: function() {
        loadAndroidAd(interstitial);
    },
    onAdLeftApplication: function() {
        // 当用户因点击广告而离开应用时执行某些操作
        console.log("Leaving the app, bye bye!");
    }
});    
var listener = new MyAdListener();     
interstitial.setAdListener(listener);
 
loadAndroidAd(interstitial);
         
frameModule.topmost().currentPage.interstitial = interstitial;

正如您所看到的,我们再次利用附加到插页式广告的 AdListener 对象。实际上,在插页式广告的情况下,不仅仅是我们自己的场景可能需要使用 AdListener,而且 Google 的建议也使其成为必要。因为,我们应该在关闭上一个插页式广告时立即准备下一个广告请求,这发生在 onAdClosed 函数中。并且,这是 loadAndroidAd 的实现

function loadAndroidAd(interstitial) {
    var adRequest = new com.google.android.gms.ads.AdRequest.Builder();
    adRequest.addTestDevice(com.google.android.gms.ads.AdRequest.DEVICE_ID_EMULATOR);
    var requestBuild = adRequest.build();          
    interstitial.loadAd(requestBuild);
}

最后,我们应该显示插页式广告。在我们的示例案例中,我们是在按钮点击时执行此操作。在实际场景中,这将发生在应用程序中的两个重要事件之间,例如两个游戏关卡之间。确保不要过度打扰您的最终用户。 :)

function buttonTapped(args) {
    var interstitial = frameModule.topmost().currentPage.interstitial;
 
    if(platformModule.device.os == platformModule.platformNames.ios) {
        if(interstitial.isReady) {
            interstitial.presentFromRootViewController(args.object.page.frame.ios.controller);
        }
    }
    else {
        if (interstitial.isLoaded()) {
            interstitial.show();
        }
    }
}

点击“TAP”按钮查看结果



插页式广告应用程序的完整 iOS + Android 源代码可以在 GitHub 上找到。

但是……等等!所有这些平台检查……这太疯狂了!我们能做些什么呢?是的,我们可以!

如果您检查上面给出的 GitHub 代码库,您可能会发现那里的代码不太美观。在我们的案例中,我们只有几个平台检查,但在具有非常丰富的原生 SDK 的实际项目中,平台检查可能会使您的代码变得丑陋。如果您像我一样喜欢简洁的代码,那么您可以将 iOS 和 Android 原生 API 调用隔离到一个模块中。

如果您检查项目 node_modules 文件夹下的 tns-core-modules,您会注意到一个模块主要由 modulename.ios.js、modulename.android.android.js、modulename-common.js 和 package.json 组成。好吧,正如您可能猜到的,modulename.ios.js 包含对原生 iOS API 的 JavaScript 调用,而 modulename.android.js 包含对原生 Android API 的 JavaScript 调用。最重要的是,我们有 modulename-common.js 来公开统一的 API,这样您就不必自己费心调用原生 API 了。

NativeScript 足够智能,可以在 iOS 设备上运行时仅加载 modulename.ios.js,在 Android 设备上运行时仅加载 modulename.android.js。

横幅广告

使用上面讨论的功能,让我们创建 bannerview.ios.js 和 bannerview.android.js 并将相应的 API 调用放在那里,从而避免在我们的 main-page.js 文件中进行平台检查。

iOS

对于 bannerview.ios.js 文件,我们将公开两个静态方法 - 第一个方法将创建横幅实例,第二个方法将设置一些重要的横幅属性

function createBanner(args) {
    var bannerView = GADBannerView.alloc().initWithAdSize(kGADAdSizeSmartBannerPortrait);
    return bannerView;
}
 
function loadBanner(placeholder) {
    bannerView = placeholder.ios;
    bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716";
    bannerView.strongDelegateRef = bannerView.delegate = GADBannerViewDelegateImpl.new();
    bannerView.rootViewController = placeholder.page;
    var request = GADRequest.request();
    request.testDevices = [kGADSimulatorID];
    bannerView.loadRequest(request);
}
 
var GADBannerViewDelegateImpl = (function (_super) {
    __extends(GADBannerViewDelegateImpl, _super);
    function GADBannerViewDelegateImpl() {
        _super.apply(this, arguments);
    }
    GADBannerViewDelegateImpl.prototype.adViewWillLeaveApplication = function (bannerView) {
        // 当用户因点击广告而离开应用时执行某些操作
        console.log("Leaving the app, bye bye!");
    };
    GADBannerViewDelegateImpl.ObjCProtocols = [GADBannerViewDelegate];
    return GADBannerViewDelegateImpl;
})(NSObject);
 
exports.createBanner = createBanner;
exports.loadBanner = loadBanner;

这就是 iOS 部分。现在到 Android 部分。

Android

在 Android 中,我们应该创建相同的静态方法,这次使用 Android 原生 API 调用填充

function createBanner(args) {
    var bannerView = new com.google.android.gms.ads.AdView(args.object._context);
    bannerView.setAdSize(com.google.android.gms.ads.AdSize.SMART_BANNER);
     
    return bannerView;
}
 
function loadBanner(placeholder) {
    bannerView = placeholder.android;
    bannerView.setAdUnitId("ca-app-pub-3940256099942544/6300978111");
     
    var MyAdListener = com.google.android.gms.ads.AdListener.extend(
    {
        onAdLeftApplication: function() {
            // 当用户因点击广告离开应用程序时执行某些操作
            console.log("Leaving the app, bye bye!");
        }
    });
    var listener = new MyAdListener();
    bannerView.setAdListener(listener);
     
    var adRequest = new com.google.android.gms.ads.AdRequest.Builder();
    adRequest.addTestDevice(com.google.android.gms.ads.AdRequest.DEVICE_ID_EMULATOR);
    var requestBuild = adRequest.build();
    bannerView.loadAd(requestBuild);
}
 
exports.createBanner = createBanner;
exports.loadBanner = loadBanner;

main-page.js

这两个文件中方法声明相同的事实使我们能够在 main-page.js 文件中简单地调用它们,并且 NativeScript 将根据应用程序运行的平台选择相应的文件(bannerview.ios.js 或 bannerview.android.js)。以下是 main-page.js 的简化版本

var vmModule = require("./main-view-model");
var bannerModule = require("./bannerview");
 
function pageLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
 
    var placeholder = page.getViewById("bannerView");
    bannerModule.loadBanner(placeholder);
}
 
function creatingView(args) {
    args.view = bannerModule.createBanner(args);
}
 
exports.pageLoaded = pageLoaded;
exports.creatingView = creatingView;

请注意,我们在顶部加载了 bannerview 模块。

您可以在 GitHub 上获取 bannerview 模块的完整源代码。

插页式广告

让我们对插页式广告执行相同的操作。首先,创建两个 js 文件 - interstitial.ios.js 和 interstitial.android.js。

iOS

在 interstitial.ios.js 文件中,我们将公开两个静态方法 - 一个创建插页式广告,另一个显示它

var frameModule = require("ui/frame");
 
function createInterstitial() {
    var currentPage = frameModule.topmost().currentPage;
    currentPage.delegate = GADInterstitialDelegateImpl.new();
    currentPage.interstitial = createAndLoadiOSInterstitial();
}
 
function showInterstitial(args) {
    var interstitial = frameModule.topmost().currentPage.interstitial;
 
    if(interstitial.isReady) {
        interstitial.presentFromRootViewController(args.object.page.frame.ios.controller);
    }
}
 
function createAndLoadiOSInterstitial() {
    var interstitial = GADInterstitial.alloc().initWithAdUnitID("ca-app-pub-3940256099942544/4411468910");
    var request = GADRequest.request();
    interstitial.strongDelegateRef = interstitial.delegate = frameModule.topmost().currentPage.delegate;
    request.testDevices = [kGADSimulatorID];
    interstitial.loadRequest(request);
 
    return interstitial;
}
 
var GADInterstitialDelegateImpl = (function (_super) {
    __extends(GADInterstitialDelegateImpl, _super);
    function GADInterstitialDelegateImpl() {
        _super.apply(this, arguments);
    }
    GADInterstitialDelegateImpl.prototype.interstitialDidDismissScreen = function (gadinterstitial) {
        frameModule.topmost().currentPage.interstitial = createAndLoadiOSInterstitial();
    };
    GADInterstitialDelegateImpl.ObjCProtocols = [GADInterstitialDelegate];
    return GADInterstitialDelegateImpl;
})(NSObject);
 
exports.createInterstitial = createInterstitial;
exports.showInterstitial = showInterstitial;

Android

如预期的那样,我们也应该为 Android 创建和公开两个执行相同操作的静态方法

var frameModule = require("ui/frame");
 
function createInterstitial(page) {
 
    var currentPage = frameModule.topmost().currentPage;
    var interstitial = new com.google.android.gms.ads.InterstitialAd(currentPage._context);
    interstitial.setAdUnitId("ca-app-pub-3940256099942544/1033173712");
     
    var MyAdListener = com.google.android.gms.ads.AdListener.extend(
    {
        onAdClosed: function() {
            loadAndroidAd(interstitial);
        },
        onAdLeftApplication: function() {
            // 当用户因点击广告离开应用程序时执行某些操作
            console.log("Leaving the app, bye bye!");
        }
    });    
    var listener = new MyAdListener();     
    interstitial.setAdListener(listener);
 
    loadAndroidAd(interstitial);
             
    currentPage.interstitial = interstitial;
}
 
function showInterstitial(args) {
    var interstitial = frameModule.topmost().currentPage.interstitial;
 
    if (interstitial.isLoaded()) {
        interstitial.show();
    }
}
 
function loadAndroidAd(interstitial) {
    var adRequest = new com.google.android.gms.ads.AdRequest.Builder();
    adRequest.addTestDevice(com.google.android.gms.ads.AdRequest.DEVICE_ID_EMULATOR);
    var requestBuild = adRequest.build();          
    interstitial.loadAd(requestBuild);
}
 
exports.createInterstitial = createInterstitial;
exports.showInterstitial = showInterstitial;

main-page.js

最后,由于代码分离和抽象,以下是 main-page.js 的简化版本

var vmModule = require("./main-view-model");
var platformModule = require("platform");
var frameModule = require("ui/frame");
 
var interstitialModule = require("./interstitial");
 
function pageLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
     
    interstitialModule.createInterstitial();
}
 
function buttonTapped(args) {
    interstitialModule.showInterstitial(args);
}
 
exports.pageLoaded = pageLoaded;
exports.buttonTapped = buttonTapped;

很简单,对吧?从 GitHub 获取该模块化示例的完整源代码。

现在您知道如何通过仅参考该库的文档和 NativeScript 文档来直接使用原生库的 API。而且,您还知道如何在模块中提取原生 API 调用,从而使您的代码保持简洁和整齐。

请继续关注在 {N} 已验证插件市场 中推出的已验证 AdMob 插件的介绍。

有关 NativeScript 的更多信息和更新,请关注 Twitter 上的 @nativescript 或关注 NativeScript 项目