【问题标题】:Flutter AdMob | The app starts to lag after implementing ads颤振 AdMob |该应用程序在实施广告后开始滞后
【发布时间】:2021-09-08 04:04:03
【问题描述】:

我正在开发一个有声读物收听应用程序。我有我的主页,它由 3 个页面小部件组成,这些小部件通过页面视图链接,我可以通过滑动浏览这些页面。在每一页上,我都放置了一个来自google_mobile_ads 包的横幅广告。问题是,如果我添加这 3 个横幅,我的应用程序开始滞后很多,我确实检查了我的应用程序的发布版本,它仍然滞后。为了更好地了解情况,我将解释我的广告加载是如何工作的。 我有一个文件,其中存储有关这些横幅广告的类:

import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter/material.dart';

late AnchoredAdaptiveBannerAdSize adaptiveBannerSize;

class AdWidgets {
  static Container? libraryPag;
  static Container? homePag;
  static Container? settingsPag;
}

void _setLoaded() {
  AdState.loaded = true;
}

class AdState {
  static bool loaded = false;
  late Future<InitializationStatus> initialization;
  AdState(this.initialization);
  String get bannerAdUnitId => 'ca-app-pub-3940256099942544/6300978111';
  BannerAdListener get adListener => _adListener;


  BannerAdListener _adListener = BannerAdListener(
    onAdLoaded: (ad) {
      _setLoaded();
    }
  );
}

有一个 AdState 类,我通过向它们提供 BannerAdListenerAdUnitId 来加载我的横幅广告,bool loaded 用于显示这些横幅的小部件中,以便他们知道广告是否已加载并可以正常显示。

接下来我有一个 Stateful Widget Content,我用它来浏览我的应用程序路线:

class _ContentState extends State<Content> with SingleTickerProviderStateMixin {

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    final adState = Provider.of<AdState>(context);
    adState.initialization.then((status) {
      AdWidgets.libraryPag = Container(
          width: MediaQuery.of(context).size.width,
          height: adaptiveBannerSize.height.toDouble(),
          child: AdWidget(
            ad: BannerAd(
                adUnitId: adState.bannerAdUnitId,
                size: adaptiveBannerSize,
                request: AdRequest(),
                listener: adState.adListener
            )
              ..load(),
          )
      );
      AdWidgets.homePag = Container(
        width: 320,
        height: 100,
        child: AdWidget(ad: BannerAd(
            adUnitId: adState.bannerAdUnitId,
            size: AdSize.largeBanner,
            request: AdRequest(),
            listener: adState.adListener
        )
          ..load()),
      );
      AdWidgets.settingsPag = Container(
          width: 320,
          height: 250,
          child: AdWidget(ad: BannerAd(
              adUnitId: adState.bannerAdUnitId,
              size: AdSize.mediumRectangle,
              request: AdRequest(),
              listener: adState.adListener
          )
            ..load()
          )
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: Settings.theme,
      builder: (context, value, _) {
        SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
          statusBarColor: Colors.transparent,
          statusBarIconBrightness: Settings.theme.value == 'Dark' ? Brightness.light : Brightness.dark,
        ));
        return WillPopScope(
            onWillPop: () async {
              if (bottomBarIndex.value != 1) {
                moveHome();
              }
              return false;
            },
            child: Scaffold(
              resizeToAvoidBottomInset: false,
              body: Navigator(
                key: Content.contentNavigatorKey,
                initialRoute: '/',
                onGenerateRoute: (RouteSettings settings) {
                  Widget builder;
                  switch (settings.name) {
                    case '/':
                      builder = MainPage();
                      break;
                    case '/bookPage':
                      Book book = settings.arguments as Book;
                      builder = BookPage(book: book);
                      bottomBarIndex.value = -1;
                      break;
                    case '/addBook':
                      builder = AddBookPage();
                      bottomBarIndex.value = -1;
                      break;
                    case '/changeCover':
                      Book book = settings.arguments as Book;
                      builder = ChangeCoverPage(book: book);
                      bottomBarIndex.value = -1;
                      break;
                    default:
                      throw Exception('Invalid route: ${settings.name}');
                  }
                  return PageRouteBuilder(
                      transitionDuration: Duration(milliseconds: 300),
                      transitionsBuilder:
                          (context, animation, secAnimation, child) {
                        animation = CurvedAnimation(
                            parent: animation, curve: Curves.easeIn);
                        return FadeTransition(
                          opacity: animation,
                          child: child,
                        );
                      },
                      pageBuilder: (context, animation, secAnimation) {
                        return builder;
                      });
                },
                onPopPage: (route, result) {
                  return route.didPop(result);
                },
              ),
            ));
      },
    );
  }
}

在 didChangeDependencies 方法中,我初始化了 adState(在 Monetizing apps with Flutter 官方教程中完成的方式),然后将三个横幅小部件分配给来自 AdWidgets 自定义类的 static Container 中的每一个。然后我就随意显示那些容器,例如:

class LibraryPage extends StatefulWidget{
  const LibraryPage({Key? key}) : super(key: key);

  @override
  _LibraryPageState createState() => _LibraryPageState();
}

class _LibraryPageState extends State<LibraryPage> with TickerProviderStateMixin  {

  late TabController _tabController;
  late AppBar appBar;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
    appBar = AppBar(
      brightness: Settings.theme.value == 'Dark' ? Brightness.dark : Brightness.light,
      shadowColor: Settings.theme.value == 'Dark' ? Color.fromRGBO(0, 0, 0, 0.1) : Color.fromRGBO(0, 0, 0, 0.5),
      backgroundColor: Settings.colors[2],
      title: Text('Library'),
      bottom: TabBar(
        controller: _tabController,
        labelPadding: EdgeInsets.zero,
        labelStyle: TextStyle(
            fontFamily: 'Montserrat',
            fontWeight: FontWeight.w500
        ),
        labelColor: Settings.colors[3],
        indicator: UnderlineTabIndicator(
            borderSide: BorderSide(color: Settings.colors[3], width: 2)
        ),
        tabs: [
          Tab(
            text: 'Reading',
          ),
          Tab(
            text: 'New',
          ),
          Tab(
            text: 'Read',
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Settings.colors[1],
      resizeToAvoidBottomInset: false,
      appBar: appBar,
      body: Column(
        children: [
          if (AdWidgets.libraryPag != null && AdState.loaded)
            AdWidgets.libraryPag!
          else
            Container(
              width: MediaQuery.of(context).size.width,
              height: adaptiveBannerSize.height.toDouble(),
              color: Settings.colors[0],
              child: Center(
                  child: Text(
                    'Ad not loaded',
                    style: TextStyle(
                        color: Settings.colors[4],
                        fontFamily: 'Open Sans'),
                  )),
            ),
          ScrollConfiguration(
            behavior: MyBehavior(),
            child: SizedBox(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height - appBar.preferredSize.height - appBar.bottom!.preferredSize.height - adaptiveBannerSize.height,
              child: TabBarView(
                controller: _tabController,
                children: [
                  Category(category: 'reading'),
                  Category(category: 'new'),
                  Category(category: 'read')
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

如您所见,我检查AdWidgets.libraryPag 是否不为空,广告是否已加载,如果为真,我会显示我的广告。

主要问题是 - 当我从一个页面导航到另一个页面或向下滚动时,即使在发布版本中它也非常滞后,而且我的底部导航栏也开始闪烁很多。此外,当我在我的应用程序中执行任何类型的操作时,我会在控制台日志中收到一堆消息,上面写着D/AudioManager(30711): getStreamVolume isRestricted mode = 0。我的猜测是广告重新加载过于频繁会导致一些性能问题。

这是我的控制台日志的一小部分:

W/ContentCatcher(30711): Failed to notify a WebView
W/Choreographer(30711): Already have a pending vsync event.  There should only be one at a time.
W/Choreographer(30711): OPTS_INPUT: First frame was drawed before optimized, so skip!
W/ContentCatcher(30711): Failed to notify a WebView
W/ContentCatcher(30711): Failed to notify a WebView
W/Choreographer(30711): Already have a pending vsync event.  There should only be one at a time.
W/Choreographer(30711): Already have a pending vsync event.  There should only be one at a time.
W/ContentCatcher(30711): Failed to notify a WebView
W/ContentCatcher(30711): Failed to notify a WebView
W/Choreographer(30711): OPTS_INPUT: First frame was drawed before optimized, so skip!
W/Choreographer(30711): OPTS_INPUT: First frame was drawed before optimized, so skip!
W/ContentCatcher(30711): Failed to notify a WebView
I/Ads     (30711): Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("B0B49AF69923DB48675A21F6F88D2525")) to get test ads on this device.
D/AudioManager(30711): getStreamVolume isRestricted mode = 0
D/AudioManager(30711): getStreamVolume isRestricted mode = 0

如何解决这个性能问题?

【问题讨论】:

    标签: android flutter dart admob ads


    【解决方案1】:

    请参考this关于问题的回答, Android 10 之前的 AndroidView 应该有更好的性能。如果是这种情况,也许我们可以检查设备操作系统并使用 AndroidView 而不是 PlatformViewLink。

    atrope 从 repo google_mobile_ads 中创建分支,并使用此技巧为低于 Android 10 的设备操作系统提供更好的性能

    在你的 pubspec.yaml 中使用这个 repo 而不是官方的 google_ads

      google_mobile_ads:
        git:
          url: https://github.com/SuaMusica/googleads-mobile-flutter.git
          path: packages/google_mobile_ads
          ref: feature/suamusica
    

    特别感谢atrope

    【讨论】:

      【解决方案2】:

      问题不在于您的实现或代码。这是因为该包使用androidView,当由android低于10的设备处理时非常昂贵。您可以像这样强制flutter切换到platformviewlink,它会暂时解决问题。

       if (Platform.isAndroid) {
      androidInfo = await DeviceInfoPlugin().androidInfo;
      final isAndroidOld = (androidInfo.version.sdkInt ?? 0) < 29; //Android 10
      useHybridComposition = remoteConfig.getBool(
        isAndroidOld
            ? RemoteConfigKey.useHybridCompositionOlderOS
            : RemoteConfigKey.useHybridCompositionNewerOS,
      );
      if (isAndroidOld && useHybridComposition) {
        await PlatformViewsService.synchronizeToNativeViewHierarchy(false);
      }
      

      }

      【讨论】:

      • 谢谢,但是我应该把这段代码放在哪里,我使用了 Mahmoud Abu Elheja 解决方案,它就像一个魅力。但我不想依赖非官方的软件包,所以如果你能帮助我,我将不胜感激。
      • 这应该在你的应用开始时添加到你的主函数中
      • 你用的什么插件?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2021-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-21
      相关资源
      最近更新 更多