ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [flutter] runtimeError: Out of bounds memory access
    flutter/flutter 에러 2023. 6. 9. 11:55

    runtimeError: Out of bounds memory access

     

    unity 로 만든 프로젝트를 webgl 로 빌드 시키고 웹 환경에서 동작시킨 후 해당 웹 페이지를 flutter 로 웹뷰 이용해서 사용하도록 만들었습니다.

    전체 webgl 파일은 6개 정도이고 웹 메인 화면에 각각 구동할 수 있도록 버튼으로 만들어서 배치했습니다.

    그런데 한 두개 실행할 때는 이상없이 잘 되는데 세번째부터 unity로 만든 webgl이 제대로 구동되지 않았습니다.

    해당 웹뷰는 ipad air 5세대 에서 실행했으며 로그를 출력해보니 Out of bounds memory access 가 나타납니다.

     

    메모리 사용량을 초과하는 이유 때문에 나타나는 에러 같은데 pc 에서 실행했을때는 이상없이 나타났으나 안드로이드 갤럭시탭, 애플 아이패드에서 실행이 안되거나 무한 로딩 현상이 나오네요.

     

    유니티에서? 웹쪽에서? flutter 에서? 어디에서 해당 내용을 수정해야할 지 고민좀 하다가 flutter 웹뷰에서만 나타나는 현상이기 때문에 flutter 쪽의 코드에서 해결책을 찾아봤습니다.

    보다보니까 웹뷰를 생성만 하고 해제하는 내용이 빠져있었습니다.

    기본 예제형태로 사용하다 보니 한 번 선언해서 계속 사용하도록 되어있네요.

     

    제가 사용한 플러터 웹뷰 패키지는 아래와 같습니다.

    https://pub.dev/packages/webview_flutter

     

    webview_flutter | Flutter Package

    A Flutter plugin that provides a WebView widget on Android and iOS.

    pub.dev

     

    기본 예제 설명처럼 controller 에 객체 생성해서 사용하도록 했는데 unity 로 실행하는 모델링 파일이 크다 보니까 메모리에 누적되는 용량이 너무 커져서 문제가 생겼던것 같습니다.

    // 기본 생성 예제 코드
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {
            // Update loading bar.
          },
          onPageStarted: (String url) {},
          onPageFinished: (String url) {},
          onWebResourceError: (WebResourceError error) {},
          onNavigationRequest: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(Uri.parse('https://flutter.dev'));

     

     

    기본 생성 예제 코드를 토대로 함수를 하나 새로 생성하고 호출 될때마다 webview controller를 생성하고 해당 webview controller를 return해주도록 하였습니다.

    WebViewController webViewController() {
        // iOS media player background 재생할 수 있도록 설정 추가
        late final PlatformWebViewControllerCreationParams params;
    
        if(WebViewPlatform.instance is WebKitWebViewPlatform) {
          params = WebKitWebViewControllerCreationParams(
              allowsInlineMediaPlayback: true,
              mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{}
          );
        } else {
          params = const PlatformWebViewControllerCreationParams();
        }
    
        WebViewController wbController = WebViewController.fromPlatformCreationParams(params);
        // 기본 설정은 javascriptMode.disabled 이기 때문에 자바스크립트를 사용하기 위해 unrestricted 사용
        wbController.setJavaScriptMode(JavaScriptMode.unrestricted);
        wbController.setBackgroundColor(const Color(0x00000000));
        wbController.setNavigationDelegate(
          NavigationDelegate(
            onProgress: (int progress) {
              setState(() {});
              print('progress 진행중 : $progress');
            },
            onPageStarted: (String url) {},
            onPageFinished: (String url) {},
            onWebResourceError: (WebResourceError error) {
              print('error 메세지 : $error');
            },
            onNavigationRequest: (NavigationRequest request) {
              // if (request.url.startsWith('https://www.youtube.com/')) {
              //   return NavigationDecision.prevent;
              // }
              return NavigationDecision.navigate;
            },
          ),
        );
        wbController.setBackgroundColor(Colors.black);
        wbController.clearCache();
        wbController.clearLocalStorage();
        wbController.addJavaScriptChannel( // webview javascript 에서 flutter 로 전달
            'myScript',
            onMessageReceived: (JavaScriptMessage message) {
              print('받은 메세지 : ${message.toString()}');
              print('받은 메세지 : ${message.message}');
              setState(() {
                title = message.message;
              });
            }
          );
        return wbController;
      }

     

    initState() 와 버튼이 눌릴때 마다 새로 호출될 수 있도록 아래와 같은 코드를 생성하였습니다.

    버튼이 눌릴때마다 controller 변수가 초기화 될 수 있도록 controller = null 를 입력하고,

    controller 변수는 late 를 사용해서 WebViewController? controller; 를 전역으로 사용할 수 있도록 하였습니다.

    ElevatedButton(
        style: ElevatedButton.styleFrom(
            backgroundColor: selectedBtn == 'mf' ? const Color.fromARGB(255, 0, 176, 240) : const Color.fromARGB(255, 1, 18, 38),
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10.0)
            ),
    
            side: const BorderSide(
                color: Color.fromARGB(255, 0, 176, 240),
                width: 1
            ),
          alignment: Alignment.center,
        ),
        onPressed: (){
          setState(() {
            selectedBtn = 'mf';
            controller = null;
            title = metaverseFactoryTitleString;
            webviewURL = metaverseURL;
          });
        },
        child: const Text(
            'test',
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold
            ),
          textAlign: TextAlign.center,
        )
    ),

     

    그리고 body:  부분에서는 controller 가 null 상태인지 체크해서 적절하게 입력되도록 하였습니다.

    아래는 관련된 코드입니다.

      loadUrl(String url) {
        controller = webViewController();
        controller?.loadRequest(Uri.parse(url));
      }
      
      ...
      
      
      @override
      Widget build(BuildContext context) {
        return SafeArea(
        ...
        body: controller == null ? loadUrl(webviewURL) : WebViewWidget(controller: controller!)
            )
          ),
        );
      }

     

    유니티에서 Dispose() 메서드를 사용해서 할까 하다가 당장 떠오른 방법이 이렇게 하는거라서 적용해봤습니다.

     

    호출할 때마다 초기화 시키고 새로 만들어서 보여지도록 하니까 메모리 에러 없이 잘 동작되었습니다.

     

    댓글

Designed by Tistory.