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源码我们可以看到就给我们提供了两个方法:

这里写图片描述

 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)

这里写图片描述

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

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

改为:

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

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

copy一下源码这两个文件:
这里写图片描述

然后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.
 * <p>
 * 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<String, Object> getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.<String, Object>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<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>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<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>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方法:

 <NativeWebView
                ref={RCT_WEBVIEW_REF}
                key="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 (
            <View style={styles.container}>
                <Text>sssss</Text>
                <ProgressBarAndroid
                    style={{width: '100%'}}
                    indeterminate={false}
                    progress={this.state.progress/100}
                    styleAttr={'Horizontal'}
                />
                <CusWebView
                    style={styles.instructions}
                    source={{uri: '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});
                        }
                    }}
                />
            </View>
        );
    }
}

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

最后运行我们的代码:
这里写图片描述

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

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

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

猜你喜欢

转载自blog.csdn.net/vv_bug/article/details/79089306