模板版本:v0.4.0

react-native-inappbrowser-reborn

本项目基于 react-native-inappbrowser-reborn 开发。

该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-inappbrowser-reborn。 版本所属关系如下:

三方库名称 三方库版本 发布信息 支持RN版本 Autolink 编译API版本 社区基线版本 npm地址
@react-native-ohos/react-native-inappbrowser-reborn ~ 3.9.0 Gitcode Releases 0.82.* API12+ 3.7.0 Npm Address
@react-native-ohos/react-native-inappbrowser-reborn ~ 3.8.0 Gitcode Releases 0.77.* API12+ 3.7.0 Npm Address
@react-native-ohos/react-native-inappbrowser-reborn ~ 3.7.1 Gitcode Releases 0.72.* API12+ 3.7.0 Npm Address
@react-native-oh-tpl/react-native-inappbrowser-reborn <=3.7.0-0.0.4@deprecated Github Releases(deprecated) 0.72.* API12+ 3.7.0 Npm Address

1. 安装与使用

进入到工程目录并输入以下命令:

npm

npm install @react-native-ohos/react-native-inappbrowser-reborn

yarn

yarn add @react-native-ohos/react-native-inappbrowser-reborn

下面的代码展示了这个库的基本使用场景:

使用时 import 的库名不变。

import React, {useState } from 'react';
import {  StyleSheet, Text, Button ,ScrollView,View,StatusBarStyle,} from 'react-native';
import {InAppBrowser} from 'react-native-inappbrowser-reborn'
import {tryDeepLinking,openLink} from './utils'

export default function BrowserDemo() {
  const [text, setText] = useState('');
  const [url, setUrl] = useState('https://reactnative.dev');

  const onOpenLink = async() => {
    let resut = await openLink(url,{});
    setText(JSON.stringify(resut))
  }

  const onTryDeepLinking = async() => {
    let result = await tryDeepLinking();
    setText(JSON.stringify(result))
    return result;
  }

  const onIsAvailable = async () => {
    let isAvailable = await InAppBrowser.isAvailable();
    return isAvailable
  }

  const close =() => {
    openLink(url, {});
    setTimeout(function(){
      console.info('-----------------')
      InAppBrowser.close();
    },10000)
  }

  const closeAuth =() => {
    tryDeepLinking();
    setTimeout(function(){
      console.info('-----------------')
      InAppBrowser.closeAuth();
    },10000)
  }

  const warmup = () => {
    InAppBrowser.warmup();
  }

  const mayLaunchUrl = () => {
    InAppBrowser.mayLaunchUrl(url,[]);
  }
  
  return (
    <View style={styles.container}>
      <View style={styles.titleArea}>
        <Text style = {styles.title}>BrowserDemo</Text>
      </View>
      <View style = {styles.inputArea}>
        <Text style={styles.baseText}>
          {text}
        </Text>
      </View>
      <ScrollView style={styles.scrollView}>
        <View style={ { flexDirection: 'column'}}>
          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>onOpenLink</Text>
             <Button title='运行' color='#841584' onPress={onOpenLink}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>onTryDeepLinking</Text>
             <Button title='运行' color='#841584' onPress={onTryDeepLinking}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>onIsAvailable</Text>
             <Button title='运行' color='#841584' onPress={onIsAvailable}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>close</Text>
             <Button title='运行' color='#841584' onPress={close}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>tryDeepLinking</Text>
             <Button title='运行' color='#841584' onPress={tryDeepLinking}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>closeAuth</Text>
             <Button title='运行' color='#841584' onPress={closeAuth}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>warmup</Text>
             <Button title='运行' color='#841584' onPress={warmup}></Button>
          </View>

          <View style ={styles.baseArea}>
             <Text style= {{flex:1}}>mayLaunchUrl</Text>
             <Button title='运行' color='#841584' onPress={mayLaunchUrl}></Button>
          </View>
        </View>
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    width: '100%',
    height: '100%',
    flexDirection: 'column',
    alignItems: 'center',
    backgroundColor: '#F1F3F5',
  }, 
  baseText: {
    fontWeight: 'bold',
    textAlign:'center',
    fontSize:16
  },

  titleArea:{
    width:'90%',
    height:'8%',
    alignItems:'center',
    flexDirection:'row',
  },

  title: {
    width:'90%',
    color:'#000000',
    textAlign:'left',
    fontSize: 30,
  },

  scrollView: {
    width:'90%',
    marginHorizontal: 20,
  },

  inputArea: {
    width:'90%',
    height:'10%',
    borderWidth:2,
    borderColor:'#000000',
    marginTop:8,
    justifyContent:'center',
    alignItems:'center',
  },
  baseArea: {
    width:'100%',
    height:60,
    borderRadius:4,
    borderColor:'#000000',
    marginTop:8,
    backgroundColor:'#FFFFFF',
    flexDirection: 'row',
    alignItems:'center',
    paddingLeft:8,
    paddingRight:8
  }

});

新建utils.ts文件:

import {Alert,Platform,} from 'react-native';
import {InAppBrowser} from 'react-native-inappbrowser-reborn'

export interface options {
  dismissButtonStyle?: 'done' | 'close' | 'cancel',
  preferredBarTintColor?:string,
  preferredControlTintColor?:string
}

export const openLink = async (
url: string,
statusBarStyle: options,
animated = true,
) => {
    let result  = await InAppBrowser.open(
        url,
        {
          dismissButtonStyle:statusBarStyle.dismissButtonStyle,
          preferredBarTintColor:statusBarStyle.preferredBarTintColor,
          preferredControlTintColor:statusBarStyle.preferredControlTintColor
        }
    );
    Alert.alert('Response', JSON.stringify(result));
    return result;
}

export const getDeepLink = (path = '') => {
    const scheme = 'my-demo';
    const prefix =
      Platform.OS === 'android' ? `${scheme}://demo/` : `${scheme}://`;
    return prefix + path;
};

export const tryDeepLinking = async () => {
    const loginUrl = 'https://proyecto26.github.io/react-native-inappbrowser/';
    const redirectUrl = getDeepLink();
    const url = `${loginUrl}?redirect_url=${encodeURIComponent(redirectUrl)}`;
    try {
      if (await InAppBrowser.isAvailable()) {
        const result = await InAppBrowser.openAuth(url, redirectUrl, {
          // iOS Properties
          ephemeralWebSession: false,
        });
        //await sleep(800);
        return result
      } else {
        return 'InAppBrowser is not supported :/'
      }
    } catch (error) {
      console.error(error);
    }
    return 'Something’s wrong with the app'
};
是否支持autolink RN框架版本
~3.9.0 Yes 0.82
~3.8.0 No 0.77
~3.7.1 Yes 0.72
<= 3.7.0-0.0.4@deprecated No 0.72

使用AutoLink的工程需要根据该文档配置,Autolink框架指导文档:https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过ManualLink配置。

ManualLink: 此步骤为手动配置原生依赖项的指导

首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony

2.1. Overrides RN SDK

为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 oh-package.json5 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。

关于该字段的作用请阅读官方说明

{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}

2.2. 引入原生端代码

目前有两种方法:

  • 通过 har 包引入;
  • 直接链接源码。

方法一:通过 har 包引入(推荐)

har 包位于三方库安装路径的 `harmony` 文件夹下。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
    "@react-native-ohos/react-native-inappbrowser-reborn": "file:../../node_modules/@react-native-ohos/react-native-inappbrowser-reborn/harmony/inappbrowser.har"
  }

点击右上角的 sync 按钮

或者在命令行终端执行:

cd entry
ohpm install

方法二:直接链接源码

如需使用直接链接源码,请参考[直接链接源码说明](./link-source-code.md)

2.3. 配置 CMakeLists 和引入 InappbrowserRebornPackage

打开 entry/src/main/cpp/CMakeLists.txt,添加:

+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")

# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-inappbrowser-reborn/src/main/cpp" ./inappbrowser-reborn)
# RNOH_END: manual_package_linking_1

# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_inappbrowser_reborn)
# RNOH_END: manual_package_linking_2

打开 entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "InappbrowserRebornPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        std::make_shared<RNOHGeneratedPackage>(ctx),
+       std::make_shared<InappbrowserRebornPackage>(ctx)
    };
}

2.4. 在 ArkTs 侧引入 RNInAppBrowserPackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

  ...
+ import { RNInAppBrowserPackage } from '@react-native-ohos/react-native-inappbrowser-reborn/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
+   new RNInAppBrowserPackage(ctx),
  ];
}

2.5. 必要的配置项

该模块的内容无法通过autolink自动生成,始终需要手动配置。

2.5.1. 配置Entry(该模块始终需要手动配置)

1.在 entry/src/main/ets/entryability 下创建 BrowserManagerAbility.ets

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

export default class BrowserManagerAbility extends UIAbility {

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/BrowserManagerPage', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

2.在 entry/src/main/module.json5注册BrowserManagerAbility

"abilities":[{
    "name": "BrowserManagerAbility",
    "srcEntry": "./ets/entryability/BrowserManagerAbility.ets",
    "description": "$string:EntryAbility_desc",
    "icon": "$media:icon",
    "startWindowIcon": "$media:startIcon",
    "startWindowBackground": "$color:start_window_background",
    "visible": true,
  }
...
]

3.在 entry/src/main/ets/pages 下创建 BrowserManagerPage.ets

import { BrowserPage } from '@react-native-ohos/react-native-inappbrowser-reborn/Index'

@Entry
@Component
struct ChromeTabsManagerPage {
  build() {
    Row(){
      Column(){
        BrowserPage();
      }
      .width('100%')
    }
    .height('100%')
  }

}

4.在 entry/src/main/resources/base/profile/main_pages.json 添加配置

{
 "src": [
  "pages/Index",
  "pages/BrowserManagerPage"
 ]
}

5.如果需要预热应用内浏览器客户端,使其启动速度显著加快,可以将以下内容添加到BrowserManagerAbility

import { RNInAppBrowserModule } from '@react-native-ohos/react-native-inappbrowser-reborn/ts';

export default class BrowserManagerAbility extends UIAbility {

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    RNInAppBrowserModule.start();
  }
}

2.6. 运行

点击右上角的 sync 按钮

或者在命令行终端执行:

cd entry
ohpm install

然后编译、运行即可。

3. 约束与限制

3.1. 兼容性

本文档内容基于以下版本验证通过:

  1. RNOH: 0.72.96; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
  2. RNOH: 0.72.33; SDK: HarmonyOS NEXT B1; IDE: DevEco Studio: 5.0.3.900; ROM: Next.0.0.71;
  3. RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
  4. RNOH: 0.82.1; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;

4. 属性

"Platform"列表示该属性在原三方库上支持的平台。

"HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

iOS Options

Name Description Type Required Platform HarmonyOS Support
dismissButtonStyle 关闭按钮的样式。[done/close/cancel] String no iOS yes
preferredBarTintColor 用于导航栏和工具栏背景的着色颜色。[white/#FFFFFF] String no iOS yes
preferredControlTintColor 用于导航栏和工具栏上控制按钮的着色颜色。[gray/#808080] String no iOS yes
readerMode 指定 Safari 是否应进入阅读模式(如果可用)的值。[true/false] Boolean no iOS no
animated 是否对呈现进行动画处理。[true/false] Boolean no iOS no
modalPresentationStyle 模态呈现视图控制器的呈现样式。[automatic/none/fullScreen/pageSheet/formSheet/currentContext/custom/overFullScreen/overCurrentContext/popover] String no iOS no
modalTransitionStyle 呈现视图控制器时使用的过渡样式。[coverVertical/flipHorizontal/crossDissolve/partialCurl] String no IOS no
modalEnabled 以模态方式呈现 SafariViewController 或改为推送方式。[true/false] Boolean no iOS no
enableBarCollapsing 确定浏览器的工具栏是否折叠。[true/false] Boolean no iOS no
ephemeralWebSession 防止重用前一个会话的 cookie(仅限 openAuth)。[true/false] Boolean no iOS no
formSheetPreferredContentSize iPad formSheet 模态框的自定义尺寸。[{width: 400, height: 500}] Boolean no iOS no

Android Options

Name Description Type Required Platform HarmonyOS Support
showTitle 设置是否在自定义标签页中显示标题。[true/false] Boolean no Android no
toolbarColor 设置工具栏颜色。[gray/#808080] String no Android no
secondaryToolbarColor 设置辅助工具栏的颜色。[white/#FFFFFF] String no Android no
navigationBarColor 设置导航栏颜色。[gray/#808080] String no Android no
navigationBarDividerColor 设置导航栏分隔线颜色。[white/#FFFFFF] String no Android no
enableUrlBarHiding 启用当用户向下滚动页面时隐藏 URL 栏。[true/false] String no Android no
enableDefaultShare 在菜单中添加默认分享项。[true/false] String no Android no
animations 设置启动和退出动画。[{ startEnter, startExit, endEnter, endExit }] Object no Android no
headers 数据为键值对,将作为 HTTP 请求头发送到提供的 URL。[{ 'Authorization': 'Bearer ...' }] Object no Android no
forceCloseOnRedirection 在新任务中打开自定义标签页,以避免重定向回应用 scheme 时出现问题。[true/false] Boolean no Android no
hasBackButton 设置返回箭头而不是默认的 X 图标来关闭自定义标签页。[true/false] Boolean no Android no
browserPackage 用于处理自定义标签页的浏览器包名。 Boolean no Android no
showInRecents 确定浏览的网站是否应显示为 Android 最近任务/多任务视图中的单独条目。[true/false] Boolean no Android no
includeReferrer 确定是否将您的包名作为引荐来源包含在内以供网站跟踪。[true/false] Boolean no Android no

5. API

"Platform"列表示该属性在原三方库上支持的平台。

"HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

Name Description Type Required Platform HarmonyOS Support
open 打开应用内浏览器。 function no iOS/Android yes
close 关闭当前呈现的浏览器。 function no iOS/Android yes
openAuth 打开身份验证会话。 function no iOS/Android yes
closeAuth 关闭当前的身份验证会话。 function no iOS/Android yes
isAvailable 检测设备是否支持此功能。 function no iOS/Android yes
onStart 用于预热浏览器以加快导航速度,并指示给定的 URL 可能会在将来加载,HarmonyOS中 为 start,功能一致。 function no Android yes
warmup 预热浏览器进程。 function no Android yes
mayLaunchUrl 预加载可能导航的 URL。 function no Android yes

6. 遗留问题

7. 其他

8. 开源协议

本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。