React-Native webview遇到的坑及android源码解析(一)

前言:因为刚换工作的原因,好久没写博客了,目前一直在做跟rn相关的东西,android已经停滞快半年了,h5也差不多,说多了感觉心累啊,有些东西你不碰的话还真会忘记,真是二者不可兼得额,废话不多说了,最近在看rn的webview,很听很多小伙伴说了,rn的webview很多坑,特别是android!!好吧~那我们就带着小伙伴的需求跟坑一步一步的解析下源码~~

一、获取webview的加载进度,rn中用进度条显示

做过原生android的都知道,如果要监听webview的progress只需要进行如下操作就可以了:

private class MyWebCromeClient extends WebChromeClient {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (newProgress == 100) {
            //加载完毕进度条消失
            progressView.setVisibility(View.GONE);
        } else {
            //更新进度
            progressView.setProgress(newProgress);
        }
        super.onProgressChanged(view, newProgress);
    }
}

是的,在android原生中很简单,但是rn的webview并没有给我们提供这样的需求,看rn的webview源码我们可以看到就给我们提供了两个方法:

React-Native webview遇到的坑及android源码解析(一)_第1张图片

 onLoadingStart={this.onLoadingStart}
 onLoadingFinish={this.onLoadingFinish}

所以这时有小伙伴说了,rn原生webview很坑!!于是马上转换思路了,rn原生没有这方法,那只有让原生写个页面了,rn直接跳转过去,是的,没错!可以这样做,但是就失去一个rn app的含义了,然后还会牵扯出一堆原生路由跟rn路由的坑~ 所以我们得换一个思路了,既然rn的webview没有这方法,我们可以重写一个叫webview2的组件。
第一种解决方法我就不说了~
第二种解决方法:
第一步:我们直接copy一个rn的webview叫CusWebView.js:
(/node_modules/react-native/Libraries/Components/WebView/WebView.android.js)

React-Native webview遇到的坑及android源码解析(一)_第2张图片

copy完后,我们把原本的这行代码:

var RCTWebView = requireNativeComponent('RCTWebView', WebView1, WebView1.extraNativeComponentConfig);

改为:

var RCTWebView = requireNativeComponent('RCTWebView1', WebView1, WebView1.extraNativeComponentConfig);

可以看到,我们只是把RCTWebView改为了RCTWebView1,然而这个RCTWebView1是哪来的呢?对的!原生过来的,所以接下来我们去操作一波android。

copy一下源码这两个文件:
React-Native webview遇到的坑及android源码解析(一)_第3张图片

然后copy两份到我们的工程目录下,我们修改下ReactWebViewManager,为起添加进度条监听:

  @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        。。。。

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
            }
        });

可以看到,我们已经拿到我们的进度newProgress了,所以我们只需要在这个方法回调rn就可以了,那rn组件跟native组件怎么通信呢?(通信原理我后面会一步一步解析)

我们创建一个ProgressMessageEvent.java文件:

/**
 * 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. */ package com.webviewdemo.webview; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.RCTEventEmitter; /** * Event emitted when there is an error in loading. */ public class ProgressMessageEvent extends Event<ProgressMessageEvent> { public static final String EVENT_NAME = "progressMessage"; private double mData; public ProgressMessageEvent(int viewId, double data) { super(viewId); mData = data; } @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) { WritableMap data = Arguments.createMap(); data.putDouble("data", mData); rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data); } }

然后修改下ReactWebViewManager.java的onProgress方法:

 @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        。。。。。。

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                ReactWebViewManager.this.progress = newProgress;
                //发消息到rn端
                dispatchEvent(view,new ProgressMessageEvent(view.getId(),newProgress));
                super.onProgressChanged(view, newProgress);
            }
        });

最后在ReactWebViewManager.java类中重写下getExportedCustomDirectEventTypeConstants方法:

 @Nullable
    @Override
    public Map getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.builder()
                .put("progressMessage", MapBuilder.of("registrationName", "onProgress"))
                .build();
    }

发消息到rn端大概流程就是,native 端发送一个event—>native端会通过组件id找到该组件—>通过event的事件名字(progressMessage)匹配到registrationName对应的onProgress名字–>拿到rn端注册的方法名onProgress—>调用rn端的onProgress方法

然后我们再创建一个ReactWebViewPackage.java文件把ReactWebViewManager放进去:

package com.webviewdemo.webview;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ReactWebViewPackage implements ReactPackage {


    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {

        return Collections.emptyList();
    }

    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList(new ReactWebViewManager());
    }

}

然后我们把ReactWebViewPackage.java添加进application:

package com.webviewdemo;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.webviewdemo.webview.ReactWebViewPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List getPackages() {
            return Arrays.asList(
                    new MainReactPackage(),
                    new ReactWebViewPackage()
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}

然后我们回到rn端,给CusWebView.js提供一个onProgress方法:

 "webViewKey"
                style={webViewStyles}
                。。。。。。
                onProgress={(event) => {
                    this.props.onProgress&&this.props.onProgress(event.nativeEvent.data);
                }}
                。。。。。。
                {...nativeConfig.props}
            />;

最后在我们的App.js文件中引用我们的CusWebview,并加一个进度条:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import {
    Platform,
    StyleSheet,
    Text,
    View,
    ProgressBarAndroid,
} from 'react-native';
import CusWebView from './app/CusWebView';

const instructions = Platform.select({
    ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
    android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
    state = {
        progress: 0,
    };

    render() {
        return (
            
                sssss
                '100%'}}
                    indeterminate={false}
                    progress={this.state.progress/100}
                    styleAttr={'Horizontal'}
                />
                'http://blog.csdn.net/vv_bug/article'}}
                    startInLoadingState={true}
                    automaticallyAdjustContentInsets={false}
                    javaScriptEnabled={true}
                    domStorageEnabled={true}
                    decelerationRate="normal"
                    scalesPageToFit={true}
                    onProgress={(progress) => {
                        if (this.state.progress !== progress) {
                            this.setState({progress});
                        }
                    }}
                />
            
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        width: '100%',
        flex: 1,
    },
});

最后运行我们的代码:
React-Native webview遇到的坑及android源码解析(一)_第4张图片

好啦~! 到这我们就实现了我们的一个小小功能了
总结一下吧:

rn这东西吧,fb不可能满足我们所有的业务需求,遇到问题一定要先尝试一下,其实真正去做的时候也没有我们想象中的那么难,好啦!不装逼了,下面还会继续讲webview的一些坑和一些项目中遇到的业务需求,今天先到这里了~~

欢迎入群,欢迎交流~~~

你可能感兴趣的:(RN学习笔记)