반응 네이티브에서 WebView를 통해 반환 비콜백 메서드 처리

亚连
풀어 주다: 2018-06-04 11:42:09
원래의
2160명이 탐색했습니다.

이 글에서는 주로 React-native WebView 반환 처리에 대한 자세한 설명을 소개합니다(콜백이 아닌 방법으로 해결 가능). 이제 공유하여 참고용으로 드립니다.

1. 서문

프로젝트에는 자주 변경되는 페이지 내용이 있습니다. 이러한 페이지를 해결하려면 웹 페이지를 사용하는 것이 좋습니다.

RN 프로젝트에 공개 웹 페이지를 제공하세요. 웹 콘텐츠인 경우 이 인터페이스로 이동하여 표시하세요.

현재 문제는 웹페이지에 첫 번째 수준 페이지와 두 번째 수준 페이지가 있으며 탐색 모음의 Return 키를 처리하도록 설계된다는 것입니다(Android의 Return 키 처리). .

이 문제의 해결 방법은 RN 공식 홈페이지에서 확인하실 수 있습니다. onNavigationStateChange 콜백 메소드를 사용하여 현재 탐색 상태를 기록하면 이전 페이지로 돌아갈지, 아니면 이 웹페이지를 종료하고 앱의 다른 인터페이스로 돌아갈지 결정할 수 있습니다.

그러나 웹페이지 구현이 React인 경우에는 페이지가 점프할 때 onNavigationStateChange 콜백 메소드에 콜백이 없다는 것을 알게 될 것입니다! ! ! 얼마나 뚱뚱해! !

처음에는 웹 주소를 콜백을 받을 수 있는 바이두로 바꾸려고 했으나 모든게 잘 돌아가긴 했지만 우리 링크로 바꾸니 안 되서 누구 탓인가 싶어 백엔드에 전가했습니다. React를 잘못 썼습니다.

지난 프로젝트가 빡빡했고 소스 코드를 자세히 살펴볼 시간이 없었기 때문에 웹 페이지에서 js를 사용하여 앱을 다시 호출하는 솔루션이 완성되지 않았습니다. 현재 탐색 상태를 알려줍니다.

이제 소스 코드를 읽고 실제 이유를 알아내는 시간을 조금 갖게 되었습니다.

2. 이유

이 문제의 원인과 해결 방법을 분석해 보겠습니다.

1. 먼저 소스코드의 위치를 ​​찾습니다.

node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactviewswebview

no de_modulesreact-nativeLibrariesComponentsWebView

디렉토리 구조는 다음과 같습니다.

2. nippet (JAVA 터미널)

RN의 실제 실행 코드는 모두 네이티브 코드입니다. 따라서 WebView 구성 요소와 같은 일부 이벤트 콜백은 실제로 네이티브 코드의 콜백에 의해 트리거됩니다. 다음과 같이

(ReactWebViewManager.java) rn 버전 0.47.1

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我们在写Android原生代码时,监听网页加载情况使用的工具。
   protected static final String REACT_CLASS = "RCTWebView"; //定义的原生组件名,在后面JS中会对应到。

  //...

  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回调方法,此处只举一例
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //自己定义的时间,dispatch后,事件会传给js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  //...
 }
로그인 후 복사

(ReactWebViewManager.java) rn 버전 0.43.3 , 다양한 RN 버전에는 코드 조정이 있으므로 RN을 업그레이드할 때 신중한 회귀 테스트가 필요합니다. 필수의 .

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我们在写Android原生代码时,监听网页加载情况使用的工具。
   protected static final String REACT_CLASS = "RCTWebView"; //定义的原生组件名,在后面JS中会对应到。

  //...

  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回调方法,此处只举一例
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //自己定义的时间,dispatch后,事件会传给js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  @Override
  public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { //坑在这,这里就是导航有变化的时候会回调在这个版本是有这个处理的,但是不知道在哪个版本删掉了 -.-
   super.doUpdateVisitedHistory(webView, url, isReload);

   dispatchEvent(
     webView,
     new TopLoadingStartEvent(
       webView.getId(),
       createWebViewEvent(webView, url)));
  }

  //...
 }
로그인 후 복사

(TopLoadingStartEvent.java) Callback JS Event

public class TopLoadingStartEvent extends Event<TopLoadingStartEvent> {
 public static final String EVENT_NAME = "topLoadingStart";  //对应方法是onLoadingStart, 因为对RN的结构不熟悉,在此处花了很长时间研究是怎么对应的,最后找到了定义对应的文件
 private WritableMap mEventData;

 public TopLoadingStartEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }

 @Override
 public String getEventName() {
  return EVENT_NAME;
 }

 @Override
 public boolean canCoalesce() {
  return false;
 }

 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }

 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}
로그인 후 복사

(node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactuimanagerUIManagerModuleConstants.java)

이 파일은 여기에 해당하는 관계

/**
 * Constants exposed to JS from {@link UIManagerModule}.
 */
/* package */ class UIManagerModuleConstants {

 /* package */ static Map getDirectEventTypeConstants() {
  return MapBuilder.builder()
    .put("topContentSizeChange", MapBuilder.of("registrationName", "onContentSizeChange"))
    .put("topLayout", MapBuilder.of("registrationName", "onLayout"))
    .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError"))
    .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish"))
    .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart"))
    .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange"))
    .put("topMessage", MapBuilder.of("registrationName", "onMessage"))
    .build();
 }
}
로그인 후 복사

3이 정의되었습니다. 구현된 코드 조각(JS 측)

(node_modulesreact-nativeLibrariesComponentsWebViewWebView.android.js)

아래 코드에서는 onLoadingStart 및 onLoadingFinish만 updateNavigationState를 호출하는 것을 볼 수 있습니다. 웹 페이지 구현이 다음과 같기 때문에 여기서 문제가 발생합니다. 리액트, 페이지가 하나뿐이에요! 따라서 onLoadingStart 및 onLoadingFinish는 한 번만 호출됩니다. 세부정보 페이지를 다시 클릭하면 새 페이지로 이동하지 않고 원본 페이지가 새로 고쳐집니다. 따라서 updateNavigationState 콜백이 없습니다.

class WebView extends React.Component {
 static propTypes = {  //给外部定义的可设置的属性
  ...ViewPropTypes,
  renderError: PropTypes.func,
  renderLoading: PropTypes.func,
  onLoad: PropTypes.func,
  //...
  }

 render() { //绘制页面内容
  //...
  var webView =
   <RCTWebView
    ref={RCT_WEBVIEW_REF}
    key="webViewKey"
    style={webViewStyles}
    source={resolveAssetSource(source)}
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}/>;

  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }

 onLoadingStart = (event) => {
  var onLoadStart = this.props.onLoadStart;
  onLoadStart && onLoadStart(event);
  this.updateNavigationState(event);
 };

 onLoadingFinish = (event) => {
  var {onLoad, onLoadEnd} = this.props;
  onLoad && onLoad(event);
  onLoadEnd && onLoadEnd(event);
  this.setState({
   viewState: WebViewState.IDLE,
  });
  this.updateNavigationState(event);
 };

 updateNavigationState = (event) => {
  if (this.props.onNavigationStateChange) {
   this.props.onNavigationStateChange(event.nativeEvent);
  }
 };
}

var RCTWebView = requireNativeComponent(&#39;RCTWebView&#39;, WebView, {  //对应上面JAVA中的 ‘RCTWebView&#39;
 nativeOnly: { messagingEnabled: PropTypes.bool, }, });


 module.exports = WebView;
로그인 후 복사

2. Solution

이제 원인을 찾았으니 해결은 쉽습니다

Solution: WebView를 맞춤화하고, doUpdateVisitedHistory 처리를 추가하고, 탐색이 변경될 때마다 JS에 알립니다.

1. 아래 그림의 파일을 자체 프로젝트의 Android 코드 디렉터리에 복사합니다.

복사된 Android 디렉터리:

ReactWebViewManager.java에서 여러 곳을 수정해야 합니다.

public class ReactWebViewManager extends SimpleViewManager<WebView> {
 protected static final String REACT_CLASS = "RCTWebView1"; //此处修改一下名字

 protected static class ReactWebViewClient extends WebViewClient {
    @Override
    public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
      super.doUpdateVisitedHistory(webView, url, isReload);

      dispatchEvent(    //在导航变化的时候,dispatchEvent
          webView,
          new TopCanGoBackEvent(
              webView.getId(),
              createCanGoBackWebViewEvent(webView, url)));
    }
 }
}
로그인 후 복사

TopCanGoBackEvent는 특히 탐색 변경 사항을 알리기 위해 내가 직접 추가한 이벤트입니다.

TopCanGoBackEvent.java

public class TopCanGoBackEvent extends Event<TopCanGoBackEvent> {

 public static final String EVENT_NAME = "topChange"; 
 private WritableMap mEventData;

 public TopCanGoBackEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }

 @Override
 public String getEventName() {
  return EVENT_NAME;
 }

 @Override
 public boolean canCoalesce() {
  return false;
 }

 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }

 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}
로그인 후 복사

새 ReactWebViewPage.java를 생성하세요

public class ReactWebViewPackage implements ReactPackage {

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(
        new ReactWebViewManager()
    );
  }
}
로그인 후 복사

그런 다음 MainApplication에서 이 모듈을 추가하세요.

아아아아

위 내용은 Android에서 수정해야 할 사항입니다. iOS에서는 시도해보지 않았습니다.

2. 아래 그림의 파일을 자체 프로젝트의 JS 코드 디렉터리에 복사하고 이름을 변경합니다

JS代码目录:

CustomWebView.android.js 有几个地方需要修改。

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CustomWebView  //此处需要修改名称
 */

var RCT_WEBVIEW_REF = &#39;webview1&#39;; //此处需要修改名称

 render() {
  var webView =
   <NativeWebView
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}
    onChange={this.onChange} //添加方法
   />;

  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }

 onChange = (event) => {  //添加方法
  this.updateNavigationState(event);
 };
}

var RCTWebView = requireNativeComponent(&#39;RCTWebView1&#39;, CustomWebView, CustomWebView.extraNativeComponentConfig); //修改名称

module.exports = CustomWebView; //修改名称
로그인 후 복사

至此就完成自定义WebView模块。也可以解决网页是React实现,不能导航的问题。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中如何解决v-for使用报红并出现警告的问题(详细教程)

在Vuejs中如何实现搜索匹配功能方法(详细教程)

在vue.js中利用select下拉框实现绑定和取值方法

위 내용은 반응 네이티브에서 WebView를 통해 반환 비콜백 메서드 처리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿