今天记录一下我的一个React Native Demo:
具体实现了对Android原生UI的封装和Android原生模块的封装,并且集成腾讯云点播的Android SDK。
功能:第一个页面显示腾讯云点播的视频播放控件,并播放一段视频。点击视频控件跳转到第二个页面。
步骤:
一、创建React Native项目;
react-native init TestDemo
二、封装Android原生模块;
在TestDemo/android下创建一个GoToActivity.java文件,此类继承ReactContextBaseJavaModule,实现其中的方法。
三、封装Android原生UI;
在TestDemo/android下创建一个自定义View,命名为CustomView;
再创建一个ReactCustomViewManager.java文件,此类继承SimpleViewManager<CustomView>,泛型是我上面自定义的View,目的就是封装这个自定义View,给React Native使用。
四、把写好的原生模块、原生UI进行注册;
在TestDemo/android下创建一个MyReactPackage.java文件,此类继承ReactPackage,实现其中的方法,在createNativeModules方法中注册原生模块,在createViewManagers方法中注册原生UI。然后把MyReactPackage注册到MyApplication中getPackages方法中。
五、下载腾讯云点播Android SDK(下载地址:http://download-1252463788.cossh.myqcloud.com/RTMPSDKAndroid2.0.2.2801.zip);
将SDK的jniLibs文件夹拷贝到TestDemo/android/app/src/main/下;
导入jar包,在Android Studio工程中找到刚才的jniLibs目录,展开目录,可以看到txrtmpsdk.jar,点击右键选择“Add As Library...”;
在AndroidManifest.xml中配置APP的权限,音视频类APP一般需要以下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.RECORD_AUdio" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.Camera"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
六、在自定义View--CustomView的布局中加入腾讯云点播的播放控件com.tencent.rtmp.ui.TXCloudVideoView;
七、在JS端封装原生模块、原生UI,然后写一个页面进行测试。
源代码如下:
MainActivity.java
package com.yb; import android.content.Intent; import android.os.Bundle; import android.util.Log; import com.facebook.react.ReactActivity; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "yb"; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void getMqttMessage(String mqttMessage) { Log.d("123pp",mqttMessage); startActivity(new Intent(this,SecondActivity.class)); } @Override protected void onDestroy() { EventBus.getDefault().unregister(this); super.onDestroy(); } }
MainApplication.java
package com.yb; 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 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 MyReactPackage() ); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this,/* native exopackage */ false); } }
GoToActivity.java
package com.yb; import com.facebook.react.ReactActivity; import android.widget.Toast; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import org.greenrobot.eventbus.EventBus; import java.util.Map; import java.util.HashMap; public class GoToActivity extends ReactContextBaseJavaModule { private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG"; public GoToActivity(ReactApplicationContext reactContext) { super(reactContext); } /** * getName方法。这个函数用于返回一个字符串名字,就是js中的模块名 */ @Override public String getName() { return "GoToActivity"; } /** * 返回了需要导出给JavaScript使用的常量 */ @Override public Map<String,Object> getConstants() { final Map<String,Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY,Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY,Toast.LENGTH_LONG); return constants; } /** * 导出给js使用的方法,需要使用注解@ReactMethod。方法的返回类型必须为void */ @ReactMethod public void show(String message,int duration) { Toast.makeText(getReactApplicationContext(),message,duration).show(); EventBus.getDefault().post(message); } }
CustomView.java
package com.yb; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.RelativeLayout; import android.widget.TextView; import com.tencent.rtmp.ui.TXCloudVideoView; /** * Created by Auser on 2017/5/2. */ public class CustomView extends RelativeLayout { private TextView textView; private TXCloudVideoView txCloudVideoView; public CustomView(Context context) { super(context); init(context); } public CustomView(Context context,AttributeSet attrs) { super(context,attrs); init(context); } public CustomView(Context context,AttributeSet attrs,int defStyleAttr) { super(context,attrs,defStyleAttr); init(context); } private void init(Context context) { LayoutInflater.from(context).inflate(R.layout.customview_layout,this,true); textView = (TextView) this.findViewById(R.id.tv01); textView.setText("我是一个CustomView"); textView.setTextColor(Color.parseColor("#ff0000")); txCloudVideoView = (TXCloudVideoView) this.findViewById(R.id.video_view); } public void setText(String txt) { this.textView.setText(txt); } public String getText() { return this.textView.getText().toString(); } public TXCloudVideoView getTxCloudVideoView() { return txCloudVideoView; } public void setTxCloudVideoView(TXCloudVideoView txCloudVideoView) { this.txCloudVideoView = txCloudVideoView; } }
MyReactPackage.java
ReactCustomViewManager.java
package com.yb; import android.support.annotation.Nullable; import android.util.Log; import android.webkit.WebView; import android.webkit.WebViewClient; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.annotations.ReactProp; import com.tencent.rtmp.TXLivePlayer; /** * Created by YiBing on 2017/4/28. */ public class ReactCustomViewManager extends SimpleViewManager<CustomView> { ThemedReactContext context; public static final String REACT_CLASS = "RCTCustomView"; @Override public String getName() { return REACT_CLASS; } @Override protected CustomView createViewInstance(ThemedReactContext reactContext) { this.context = reactContext; CustomView customView = new CustomView(reactContext); return customView; } @ReactProp(name = "url") public void setUrl(CustomView customView,@Nullable String url) { Log.e("TAG","setUrl"); customView.setText(url); TXLivePlayer txLivePlayer = new TXLivePlayer(context); txLivePlayer.setPlayerView(customView.getTxCloudVideoView()); txLivePlayer.startPlay(url,TXLivePlayer.PLAY_TYPE_VOD_MP4); } }
SecondActivity.java
package com.yb; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.tencent.rtmp.ui.TXCloudVideoView; import com.tencent.rtmp.TXLivePlayer; public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); } }
activity_second.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv01" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Second Activity" android:gravity="center_horizontal"/> </RelativeLayout>
customview_layout.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv01" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Hello world!" android:gravity="center_horizontal"/> <com.tencent.rtmp.ui.TXCloudVideoView android:id="@+id/video_view" android:layout_below="@id/tv01" android:layout_marginTop="8dp" android:layout_width="match_parent" android:layout_height="300dp" android:layout_centerInParent="true" android:visibility="gone"/> </RelativeLayout>
CustomView.js
/** * Created by YiBing on 2017/4/28. * react-native: 0.43.3 * react-native-cli: 2.0.1 */ import { PropTypes } from 'react'; import { requireNativeComponent,View } from 'react-native'; var iface = { name: 'CustomView',propTypes: { url: PropTypes.string,...View.propTypes // include the default view properties },}; module.exports = requireNativeComponent('RCTCustomView',iface);
GoToActivity.js
/** * Created by YiBing on 2017/4/28. */ 'use strict'; import { NativeModules } from 'react-native'; export default NativeModules.GoToActivity; // 以前的ES版本的写法。 // var {NativeModules} = require('react-native'); // module.exports = NativeModules.MyToast;
index.android.js
import React,{ Component } from 'react'; import { AppRegistry,StyleSheet,Text,View,ListView,TouchableOpacity,Image,ToastAndroid,} from 'react-native'; import GoToActivity from './GoToActivity'; import CustomView from './CustomView'; var video_url = "http://www.zxx.net.cn:8080//dmmm/vedio/201612270541078235250/201612270541078235250.mp4"; export default class yb extends Component { render() { return ( <View style={styles.container}> <View style={{width:'100%',height:50,borderWidth:2,borderColor:'#f00',justifyContent:'center',alignItems:'center',}}> <Text style={{textAlign:'center',}}>腾讯云点播测试</Text> </View> <TouchableOpacity onPress={() => GoToActivity.show("Go To SecondActivity",ToastAndroid.SHORT)} style={{width:'100%',flex:1,borderWidth:10,borderColor:'#00f'}}> <CustomView url={video_url} style={{width:'100%',height:'100%'}} /> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},welcome: { fontSize: 20,textAlign: 'center',margin: 10,instructions: { textAlign: 'center',color: '#333333',marginBottom: 5,}); AppRegistry.registerComponent('yb',() => yb);