React Native开发专题介绍

今天我们继续来看一下原生模块的一些特性例如:回调方法函数,Promises,多线程,事件发送到JavaScript,监听生命周期事件,获取Activity Result等相关的特性。当前所讲解内容适配Android开发。

本示例代码地址:https://github.com/jiangqqlmj/NativeModules1

刚创建的React Native技术交流4群(458982758),欢迎各位大牛,React Native技术爱好者加入交流!同时博客右侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

前面我们已经学习过了封装原生模块给React Native前端进行调用(点击进入查看详情),现在我们一起来分别看一下其他的特性:

(二)回调方法

原生模块有一种特殊的参数那就是回调函数,在绝大多数情况下该用来提供一个回调方法进行传值给JavaScript。具体该怎么样提供回调方法呢?还记得前面的文章中讲到原生模块封装给RN前端调用不?被调用的方法使用@ReactMethod注解。YES这边也差不多。我们来看一下官方的实例怎么样写的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UIManagerModule extends ReactContextBaseJavaModule {
 
...
 
   @ReactMethod
   public void measureLayout(
       int tag,
       int ancestorTag,
       Callback errorCallback,
       Callback successCallback) {
     try {
       measureLayout(tag, ancestorTag, mMeasureBuffer);
       float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]);
       float relativeY = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]);
       float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]);
       float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]);
       successCallback.invoke(relativeX, relativeY, width, height);
     } catch (IllegalViewOperationException e) {
       errorCallback.invoke(e.getMessage());
     }
   }
 
...

然后该方法在JavaScript中进行如下调用:

?
1
2
3
4
5
6
7
8
9
10
UIManager.measureLayout(
   100,
   100,
   (msg) => {
     console.log(msg);
   },
   (x, y, width, height) => {
     console.log(x + ':' + y + ':' + width + ':' + height);
   }
);

这样原生模块只会调用回调方法一次,但是该可以保持让callback进行调用。

[注意].该回调方法在调用之后不会立即有返回,因为该为桥接方式的异步调用。该通过消息循环完成。

具体的实例代码在文章顶部的项目地址中,实例如下:

实例使用方法在之前的项目中ToastCustomModule添加measureLayout方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.modules.custom;
 
import android.widget.Toast;
 
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.uimanager.IllegalViewOperationException;
 
import java.util.HashMap;
import java.util.Map;
 
/**
  * 当前类注释:测试原生Toast模块
  * 项目名:android
  * 包名:com.modules.custom
  * 作者:江清清 on 16/3/31 10:18
  * 邮箱:jiangqqlmj@163.com
  * QQ: 781931404
  * 来源:<a href="http://www.lcode.org">江清清的技术专栏</>
  */
public class ToastCustomModule extends ReactContextBaseJavaModule {
 
     private static final String DURATION_SHORT= "SHORT" ;
     private static final String DURATION_LONG= "LONG" ;
     public ToastCustomModule(ReactApplicationContext reactContext) {
         super (reactContext);
     }
     @Override
     public String getName() {
         return "ToastCustomAndroid" ;
     }
     @Override
     public Map<String, Object> getConstants() {
         final Map<String, Object> constants = new HashMap<>();
         constants.put(DURATION_SHORT, Toast.LENGTH_SHORT);
         constants.put(DURATION_LONG, Toast.LENGTH_LONG);
         return constants;
     }
 
     /**
      * 该方法用于给JavaScript进行调用
      * @param message
      * @param duration
      */
     @ReactMethod
     public void show(String message, int duration) {
         Toast.makeText(getReactApplicationContext(), message, duration).show();
     }
 
     /**
      * 这边只是演示相关回调方法的使用,所以这边的使用方法是非常简单的
      * @param errorCallback       数据错误回调函数
      * @param successCallback     数据成功回调函数
      */
     @ReactMethod
     public void measureLayout(Callback errorCallback,
                               Callback successCallback){
         try {
             successCallback.invoke(100, 100, 200, 200);
         } catch (IllegalViewOperationException e) {
             errorCallback.invoke(e.getMessage());
         }
     }
}

具体前端JavaScript文件中的调用方式如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
  * Sample React Native App
  */
 
import React, {
   AppRegistry,
   Component,
   StyleSheet,
   Text,
   View,
   TouchableHighlight,
   ToastAndroid,
} from 'react-native' ;
var { NativeModules } = require( 'react-native' );
class CustomButton extends React.Component {
   render() {
     return (
       <TouchableHighlight
         style={styles.button}
         underlayColor= "#a5a5a5"
         onPress={ this .props.onPress}>
         <Text style={styles.buttonText}>{ this .props.text}</Text>
       </TouchableHighlight>
     );
   }
}
//onPress={()=>NativeModules.ToastCustomAndroid.show("我是ToastCustomAndroid弹出消息",NativeModules.ToastCustomAndroid.SHORT)}
class ModulesDemo extends Component {
   render() {
     return (
       <View>
         <Text style={styles.welcome}>
           自定义弹出Toast消息
         </Text>
         <CustomButton
           text= "点击弹出Toast消息"
           onPress={()=>NativeModules.ToastCustomAndroid.measureLayout((msg) => {
                     console.log(msg);
                   },
                    (x, y, width, height) => {
                     console.log(x + '坐标,' + y + '坐标,' + width + '宽,' + height+ '高' );
                   })}
         />
       </View>
     );
   }
}
const styles = StyleSheet.create({
   welcome: {
     fontSize: 20,
     textAlign: 'center' ,
     margin: 10,
   },
    button: {
     margin:5,
     backgroundColor: 'white' ,
     padding: 15,
     borderBottomWidth: StyleSheet.hairlineWidth,
     borderBottomColor: '#cdcdcd' ,
   },
});
 
AppRegistry.registerComponent( 'ModulesDemo' , () => ModulesDemo);

运行截图效果:

(三)Promises

看了上面的回调函数的使用,大家有没有发现上面的写法还有有一些繁琐的?OK  当然原生模块还可以支持使用Promise,这样可以简化我们编写的代码。如果大家搭配使用ES2016标准的async/await的语法使用会更加好。如果被桥接的原生方法的最后一个参数是一个Promise对象,那么该JS方法会返回一个Promise对象。下面我们使用Promise对象来进行重构之前的回调函数方法。具体官方代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UIManagerModule extends ReactContextBaseJavaModule {
 
...
 
   @ReactMethod
   public void measureLayout(
       int tag,
       int ancestorTag,
       Promise promise) {
     try {
       measureLayout(tag, ancestorTag, mMeasureBuffer);
 
       WritableMap map = Arguments.createMap();
 
       map.putDouble( "relativeX" , PixelUtil.toDIPFromPixel(mMeasureBuffer[0]));
       map.putDouble( "relativeY" , PixelUtil.toDIPFromPixel(mMeasureBuffer[1]));
       map.putDouble( "width" , PixelUtil.toDIPFromPixel(mMeasureBuffer[2]));
       map.putDouble( "height" , PixelUtil.toDIPFromPixel(mMeasureBuffer[3]));
 
       promise.resolve(map);
     } catch (IllegalViewOperationException e) {
       promise.reject(e);
     }
   }
 
...

那么现在你可以声明一个async的方法用来返回Promise对象,方法内部使用await关键字进行等待其返回。注意该方法还是异步的。具体代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function measureLayout() {
   try {
     var {
       relativeX,
       relativeY,
       width,
       height,
     } = await UIManager.measureLayout(100, 100);
 
     console.log(relativeX + ':' + relativeY + ':' + width + ':' + height);
   } catch (e) {
     console.error(e);
   }
}
 
measureLayout();
(四)线程使用

根据官方说明,该模块在将来有可能会发生变化。但是如果在主线程有一些耗时的操作,那么该操作需要被分配到工作子线程进行执行,最后通过回调方法进行获取执行结果即可。

(五)发送事件到JavaScript中

值得注意的是原生模块可以在没有被直接调用的情况下就可以往JavaScript发送消息事件。最简单的方式就是使用RCTDeviceEventListener接口,该可以通过ReactContext如下点来获取:

?
1
2
3
4
5
6
7
8
9
10
11
12
...
private void sendEvent(ReactContext reactContext,
                        String eventName,
                        @Nullable WritableMap params) {
   reactContext
       .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
       .emit(eventName, params);
}
...
WritableMap params = Arguments.createMap();
...
sendEvent(reactContext, "keyboardWillShow" , params);

之后JavaScript模块可以通过Subscribable mixin的addListenerOn进行注册以及接受消息。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { DeviceEventEmitter } from 'react-native' ;
...
 
var ScrollResponderMixin = {
   mixins: [Subscribable.Mixin],
 
   componentWillMount: function () {
     ...
     this .addListenerOn(DeviceEventEmitter,
                        'keyboardWillShow' ,
                        this .scrollResponderKeyboardWillShow);
     ...
   },
   scrollResponderKeyboardWillShow: function (e: Event) {
     this .keyboardWillOpenTo = e;
     this .props.onKeyboardWillShow && this .props.onKeyboardWillShow(e);
   },

你也可以直接使用DeviceEventEmitter模块来监听事件

?
1
2
3
4
5
6
7
...
componentWillMount: function () {
   DeviceEventEmitter.addListener( 'keyboardWillShow' , function (e: Event) {
     // handle event.
   });
}
...

(六)通过startActivityForResult启动Activity,获取Activity Result值回调信息  

如果你是通过startActivityForResult方法进行启动Activity的话,那么你可以需要获取activity的回调信息,那么你就需要在当前的Activity中重写onActivityResult方法了。为了实现这一目标,当前的模块类必须实现ActivtyEventListener接口,然后当前模块注册该接口,如下所示:

?
1
reactContext.addActivityEventListener( this );

接着你可以重写onActivityResult方法

?
1
2
3
4
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
   // Your logic here   写的你业务逻辑代码
}

下面我们来进行实现一个简单的图片选择器模块,该图片选择器模块提供pickImage方法给JavaScript,该会返回被选中的图片的地址。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class ImagePickerModule extends ReactContextBaseJavaModule implements ActivityEventListener {
 
   private static final int IMAGE_PICKER_REQUEST = 467081;
   private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST" ;
   private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED" ;
   private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER" ;
   private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND" ;
 
   private Promise mPickerPromise;
 
   public ImagePickerModule(ReactApplicationContext reactContext) {
     super (reactContext);
 
     // Add the listener for `onActivityResult`
     reactContext.addActivityEventListener( this );
   }
 
   @Override
   public String getName() {
     return "ImagePickerModule" ;
   }
 
   @ReactMethod
   public void pickImage(final Promise promise) {
     Activity currentActivity = getCurrentActivity();
 
     if (currentActivity == null ) {
       promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist" );
       return ;
     }
 
     // Store the promise to resolve/reject when picker returns data
     mPickerPromise = promise;
 
     try {
       final Intent galleryIntent = new Intent(Intent.ACTION_PICK);
 
       galleryIntent.setType( "image/*" );
 
       final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image" );
 
       currentActivity.startActivityForResult(chooserIntent, PICK_IMAGE);
     } catch (Exception e) {
       mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);
       mPickerPromise = null ;
     }
   }
 
   // You can get the result here
   @Override
   public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
     if (requestCode == IMAGE_PICKER_REQUEST) {
       if (mPickerPromise != null ) {
         if (resultCode == Activity.RESULT_CANCELED) {
           mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled" );
         } else if (resultCode == Activity.RESULT_OK) {
           Uri uri = intent.getData();
 
           if (uri == null ) {
             mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found" );
           } else {
             mPickerPromise.resolve(uri.toString());
           }
         }
 
         mPickerPromise = null ;
       }
     }
   }
}
(七)监听生命周期事件

如果我们需要监听Activity的生命周期,例如:onResume,onPause等等方法,我们通过实现ActivityEventListenr接口可以完成类似的实现。如果要实现这样的功能,那么当前模块类需要实现LifcycleEventListener接口并且在该模块类的构造函数中进行注册该接口。

?
1
reactContext.addLifecycleEventListener( this );

然后我们可以使用如下的代码进行监听Activity的生命周期啦

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onHostResume() {
     // Actvity `onResume`
}
 
@Override
public void onHostPause() {
     // Actvity `onPause`
}
 
@Override
public void onHostDestroy() {
     // Actvity `onDestroy`
}
(八)最后总结

今天我们主要讲解学习了原生模块的一些特性例如:回调方法函数,Promises,多线程,事件发送到JavaScript,监听生命周期事件,获取Activity Result等相关的特性。当前所讲解内容适配Android开发。大家有问题可以加一下群React Native技术交流4群(458982758).或者底下进行回复一下。

尊重原创,未经授权不得转载:From Sky丶清(http://www.lcode.org/) 侵权必究!

出处地址:http://www.lcode.org
本文出自:【江清清的技术专栏】
本React Native讲解专题:主要讲解了React Native开发,由基础环境搭建配置入门,基础,进阶相关讲解。

刚创建的React Native技术交流3群(496508742),React Native技术交流4群(458982758)。欢迎各位大牛,React Native技术爱好者加入交流!同时博客右侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

关于React Native各种疑难杂症,问题深坑总结方案请点击查看:

Mac和Windows安装搭建React Native环境教程如下:

(一).基本介绍:
React Native For Android是伟大的互联网公司Facebook与2015年9月15日发布的,该可以让我们广大开发者使用JavaScript和React开发我们的应用,该提倡组件化开发,也就是说React Native给我们提供一个个封装好的组件让开发者来进行使用,甚至我们可以相关嵌套形成新的组件。使用React Native我们可以维护多种平台(Web,Android和IOS)的同一份业务逻辑核心代码来创建原生应用。现阶段Web APP的的体验还是无法达到Native APP的体验,所以这边fackbook更加强调的是learn once,write everywhere,应用前端我们使用js和React来开发不同平台的UI,下层核心模块编写复用的业务逻辑代码,提供应用开发效率。
[特别注意]目前react native在ios上仅支持ios7以上,Android仅支持Android4.1以上。
React Native项目github地址:https://github.com/facebook/react-native
React Native项目官网文档:http://facebook.github.io/react-native/docs/getting-started.html

(二).基础入门:
1.React Native For Android环境配置以及第一个实例
2.React Native开发IDE安装及配置 
3.React Native应用设备运行(Running)以及调试(Debugging)
4.React Native移植原生Android项目
5.React Native进行签名打包成Apk
6.React Native库版本升级(Upgrading)与降级讲解

7.React Native VSCode IDE超强开发插件介绍(智能,代码提醒,运行调试…)

8.React Native特定平台代码说明

9.React Native基础之Linking Libraries链接库配置-适配iOS开发

10.React Native基础之真机设备运行调试应用-适配iOS开发

11.React Native基础之从源代码编译详解-适配Android开发

12.React Native进阶之原生UI组件封装详解-适配Android开发

(三).组件学习:
1.React Native控件之View视图讲解
2.1.React Native配置运行官方例子-初学者的福音(Mac OS X版本)
2.2.React Native配置超详细编译运行React Native官方实例UIExplorer项目(Windows版本)
3.React Native控件之Text组件讲解
4.React Native控件之Image组件讲解与美团首页顶部效果实例
5.React Native控件之TextInput组件讲解与QQ登录界面实现
6.React Native控件之ProgressBarAndroid进度条讲解
7.React Native控件之DrawerLayoutAndroid抽屉导航切换组件讲解
8.React Native控件之ScrollView组件讲解
9.WebStorm开发工具设置React Native代码智能提醒
10.React Native控件之ToolbarAndroid工具栏控件讲解以及使用
11.React Native控件之Switch开关与Picker选择器组件讲解以及使用
12.React Native控件之ViewPagerAndroid讲解以及美团首页顶部效果实例
13.React Native控件之Touchable*系列组件详解
14.React Native控件之ListView组件讲解以及详细实例
15.React Native控件之PullToRefreshViewAndroid下拉刷新组件讲解
16.React Native控件之RefreshControl组件详解
17.React Native控件之WebView组件详解以及实例使用
18.React Native控件之Navigator组件详解以及实例
19.React Native 控件之Cilpboard粘贴板使用详解

20.React Native控件之DatePickerAndroid时间日期选择器组件讲解

21.React Native控件之StatusBar状态栏详解

22.React Native控件之PickerIOS选择器详解-适配iOS开发

22.React Native 控件之SegmentedControlIOS分段组件详解-适配iOS开发

23.React Native控件之SliderIOS滑块组件详解-适配iOS开发

24.React Native控件之TabBarIOS和TabBarIOS.Item组件详解及实例

25.React Native控件之ProgressViewIOS进度加载组件详解及实例

26.React Native控件之ActivityIndicatorIOS进度指示器组件详解及实例

27.React Native控件之TimePickerAndroid时间选择器组件详解及实例

(三).API模块学习:

1.React Native API模块之ToastAndroid详解及使用
2.React Native API模块之Alert弹出框详解及使用
3.React Native API模块之AppState详解

4.React Native API模块之NetInfo(网络信息)使用详解

5.React Native API模块之AsyncStorage(持久化存储)使用详解

6.React Native API模块Dimensions屏幕宽高详解

7.React Native API模块BackAndroid拦截返回键事件处理详解

8.React Native API模块StyleSheet样式表详解

9.React Native API模块PixelRatio设备像素密度详解

10.React Native API模块之AlertIOS弹框详解-适配iOS开发

11.React Native API模块之AppStateIOS运行状态详解-适配iOS开发

12.React Native API模块之ActionSheetIOS可点击弹框详解-适配iOS开发

13.React Native API模块之Vibration控制设备震动详解

13.React Native API模块之AppRegistry应用注册入口详解

14.React Native模块之Linking详解以及实例-Android/iOS双平台通用

(四).React Native进阶:
1.React Native超棒的LayoutAnimation(布局动画)
2.React Native控件之组件封装实例(Button按钮)

3.React Native进阶之原生模块封装基础篇1-适配Android开发

4.React Native进阶之原生模块特性篇详解-适配Android开发

(五).React Native开源项目:

1.Pober Wong_17童鞋为gank.io做的纯React Native项目,开源地址:https://github.com/Bob1993/React-Native-Gank
2.聂风童鞋做的《都看影视》React Native项目,开源地址:https://github.com/changfuguo/doukanmv
3.大大做的《新闻阅读Reading》React Native项目,项目地址:http://www.lcode.org/reading-app-react-native/
4.公子小白做的《亲戚称谓计算器》React Native项目,项目地址:http://www.lcode.org/counterrelative-react-native/
5.﹌海云天♂做的纯React Native iOS开源项目,开源地址:http://www.lcode.org/react-native-lagou/   或者文章地址:http://www.lcode.org/react-native-lagou-source/
6.成都 - just4fun做的纯React Native iOS开源项目,项目地址:http://www.lcode.org/uestc-bbs-react-native/
7.lookingstars做的纯React Native 仿美团iOS开源项目,项目地址:http://www.lcode.org/react-native-meituan-source/
8.race604做的纯React Native 知乎日报开源项目(Android/iOS),开源地址:https://github.com/race604/ZhiHuDaily-React-Native
9.@vczero做的纯React Native豆瓣搜索客户端,开源地址:http://www.lcode.org/react-native-dou-source/
10.@tabalt做的纯React Native新闻客户端,开源地址:http://www.lcode.org/react-native-news-source/

11.@starzhy做的纯React Native码农iOS客户端,项目地址:http://www.lcode.org/react-native-source-manong/

12.@iSimar做的纯React Native Hacker新闻客户端(Android、iOS),项目地址:http://www.lcode.org/react-native-source-hacker/

13.@xiekw2010做的纯React Native Github客户端,兼容Android、iOS平台,开源地址:http://www.lcode.org/react-native-source-gitfeed/

14.@kailuo99做的纯React Native 资讯头条客户端,主要适配iOS平台,项目地址:http://www.lcode.org/react-native-source-zixunapp/

15.@SFantasy做的纯React Native 资讯头条客户端,主要适配iOS平台,项目地址:http://www.lcode.org/react-native-source-weibo/

16.@Kennytian做的纯React Native 仿拉勾网客户端,兼容Android、iOS双平台,项目地址:http://www.lcode.org/react-native-source-lagou-duo/

17.@soliury做的纯React Native CNode论坛客户端,项目地址:http://www.lcode.org/react-native-source-cnode/

18.@zhongjie-chen做的纯React Native 干货集中营客户端,项目地址:http://www.lcode.org/react-native-source-gankio/

19.@MemoryMagic做的纯React Native 知识点记忆客户端,项目地址:http://www.lcode.org/react-native-source-memory/

联系我

技术交流群(QQ)-RN请不要重复加群

  • Android菜鸟新闻群:99787482
  • React Native交流1群:282693535(已满)
  • React Native交流2群:496601483(已满)
  • React Native交流3群:496508742(已满)
  • React Native交流4群:458982758
  • Android开发群3:109244103

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值