react-native集成android原生

项目组用react-native开发app,前阵子有新需求,需要用android原生实现,将原生集成到react-native项目中调用

主要涉及到:

1)rn的js页面调用android原生(跳转到原生页面)

2)rn跳转到原生时带值过去

3)原生执行结束后,通知rn反应

demo地址

1.添加原生模块

  1. 1)创建TestModule1.java,在这里暴露原生模块名称,提供给rn调用

public class TestModule1 extends ReactContextBaseJavaModule {    
    public TestModule1(ReactApplicationContext reactContext) {        
        super(reactContext);    
    } 
    //注意:返回的名称为JS中调用的模块名称    
    @Override    
    public String getName() {
        return "TestModule1";    
    }
//可以在js中结合模块名调用该方法。可以在这里实现页面跳转//参数 name:目标activity路径//参数params:RN传递给原生的参数    
    @ReactMethod    
    public void startActivityFromJS(String name, String params){        
        try{            
            Activity currentActivity = getCurrentActivity();                
            if(null!=currentActivity){                
                Class toActivity = Class.forName(name);    
                //目标activity                
                Intent intent = new Intent(currentActivity,toActivity);                
                currentActivity.startActivity(intent);            
            }        
        }catch(Exception e){            
            throw new JSApplicationIllegalArgumentException("不能打开Activity : "+e.getMessage());        
        }    
    }
//注意:startActivityFromJS、dataToJS方法添加RN注解(@ReactMethod),否则该方法将不被添加到RN中}复制代码

1.2)创建TestRectPackge1.java,在这里初始化模块 

public class TestRectPackge1 implements ReactPackage {    
    @Override    
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {    
        //在这里声明module        
        return Arrays.<NativeModule>asList(new TestModule1(reactContext));    
    }    
    @Override    
    public List<Class<? extends JavaScriptModule>> createJSModules() {      
        return Collections.emptyList();    
    }    
    @Override    
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {        
    return Collections.emptyList();    
    }
}复制代码

1.3)创建TestActivity1.java,在这里使用android原生规则写页面(res资源配置)
页面有两种写法
1.3.1)在activity中调用android自带组件.
TestActivity1.java

public class TestActivity1 extends Activity implements View.OnClickListener {    
    @Override    
    protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);        
        setContentView(createView());    
    }    
    //hello world页面    
    private View createView() {        
        LinearLayout ll= new LinearLayout(this);        
        ll.setGravity(Gravity.CENTER);        
        ll.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));        
        // 设置文字        
        TextView mTextView = new TextView(this);        
        mTextView.setText("hello world");        
        LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(       
        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);        
        // 在父类布局中添加它,及布局样式        
        ll.addView(mTextView, mLayoutParams);        
        return ll;    
    }    
    @Override        
    public void onClick(View v) {    }}复制代码

1.3.2)在res/layout目录下,声明xml文件,组件和页面写在这里,activity可以直接调用它
查到资料是:src/main/res目录下的文件夹有固定含义,放在layout目录下的xml,编译时会被自动放入R.layout中,可根据文件名引入
这里将activity_demo.xml放到res/layout目录下:

<?xml version="1.0" encoding="utf-8"?>    
<LinearLayout    
    xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent"    
    android:orientation="vertical">    
    <TextView        
        android:gravity="center"        
        android:layout_width="match_parent"        
        android:layout_height="wrap_content"        
        android:layout_marginBottom="36dp"        
        android:layout_marginTop="36dp"        
        android:text="Hello World!"        
        android:textSize="36sp"        
        android:textStyle="bold"    />    
    <Button        
        android:id="@+id/button_opencad"        
        android:layout_gravity="center"        
        android:layout_width="wrap_content"        
        android:layout_height="wrap_content"        
        android:text="OpenCAD"    />
    </LinearLayout>复制代码

activity调用页面 TestActivity1.java  

import com.mypj2.R;    
//手动引入,重要
    public class TestActivity1 extends Activity implements View.OnClickListener {    
        @Override    
        protected void onCreate(Bundle savedInstanceState) {        
            super.onCreate(savedInstanceState);        
            setContentView(R.layout.activity_demo);        
            View goBack = findViewById(R.id.button_opencad);    
    }    
    @Override    
    public void onClick(View v) {    }}复制代码

在这里遇到的坑:
i.项目主路径:在src/main/AndroidManifest.xml中配置。<manifest package="com.mypj1">声明项目主路径是com.mypj1;activity声明中包含内容<activity><intent-filter><action android:name="android.intent.action.main"></intent-fiter><activity>,那么相应配置的java为项目入口文件
ii.R包找不到问题:TestActivity1.java没有放在我们项目主目录下,而是在主目录下创建子目录,放到子目录中。运行的时候编译不通过,控制台提示R包找不到,项目主目录为com.mypj1,后面通过在TestActivity1.java中import com.mypj1.R;编译通过,看起来好像是R文件默认在主目录下生成
2.配置原生模块
2.1)在项目入口java文件中添加该模块。我们项目入口文件为MainApplication.java,在入口文件getPackages方法中添加1.2)中创建的TestReactPackage1()

 @Override    
protected List<ReactPackage> getPackages() {      
    return Arrays.<ReactPackage>asList(          
        new MainReactPackage(),          
        new TestReactPackage1()        
        //添加的原生模块      );    
}复制代码

 2.2)在配置文件中添加用到的activity
在src/main/AndroidManifest.xml中添加

<activity android:name="com.mypj1.testPlugin1.TestActivity1" />
复制代码

2.3)在RN 的js页面中调用原生模块
在index.android.js中(可以添加到任意js页面)

import {NativeModules} from 'react-native';    //导入NativeModules模块
onPressApp(){           
    NativeModules.TestModule1.startActivityFromJS("com.rnpj1.testPlugin1.TestActivity1",null);
}
<TouchableOpacity onPress={this.onPressApp} >       
    <Text style={{color:'#50B1F8',fontSize:16}}>app打印调用</Text>
</TouchableOpacity>复制代码

配置结束可以启动项目调用
3.RN给原生传值
有时需要从RN页面带值到android页面,在之前的项目上修改
3.1)js调用原生模块时需要传参
在index.android.js中增加

let myData='rn传给原生的值'
NativeModules.PrintModule.startActivityFromJS(com.rnpj1.testPlugin2.TestActivity2",myData);
复制代码

3.2)在TestModule2.java中处理参数,在Testactivity2.java里将参数打印到页面中
TestModule2.java中,RN调用android原生得方法中:

@ReactMethod    
public void startActivityFromJS(String name, String params){        
    try{            
        Activity currentActivity = getCurrentActivity();           
        if(null!=currentActivity){                
            Class toActivity = Class.forName(name);                
            Intent intent = new Intent(currentActivity,toActivity);                
            intent.putExtra("params", params);                
            //在module文件中,把参数放到intent里                
            currentActivity.startActivity(intent);            
        }        
    }catch(Exception e){            
    throw new JSApplicationIllegalArgumentException("不能打开Activity : "+e.getMessage());
   }    
}复制代码

在TestModule2.java要跳转的activity:Testactivity2.java中取值 

private View createView() {        
    LinearLayout ll= new LinearLayout(this);        
    ll.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));        
    // 设置文字        
    TextView myTextView = new TextView(this);        
    myTextView.setText(getIntent().getStringExtra("params"));        
    LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(                
        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);        
    ll.addView(myTextView, mLayoutParams);        
    return ll;    
}复制代码

 4.原生给RN通信/调用的一种方法

有时原生执行过程中希望能触发RN中的某些方法执行,比如:原生执行过程中希望传参给RN,让RN在控制台记录日志
4.1)在js页面调用可以被android原生触发的方法
在index.android.js中

import {DeviceEventEmitter} from 'react-native';
//在页面初始化时绑定触发点名称和执行方法,以下定义了consoleY,供原生触发。功能:将原生传的参数打印到控制台    
componentDidMount(){       
    DeviceEventEmitter.addListener('consoleYS',this.eventHandler);    
};    
componentWillUnmount(){        
    DeviceEventEmitter.removeListener('consoleYS',this.eventHandler);    
}    
eventHandler=(message)=>{      
    console.log('consoleYs:')      
    console.log(message)    
}复制代码

4.2)在原生中调用触发点 ‘consoleYS’
修改TestModule3.java文件

public class TestModule3extends ReactContextBaseJavaModule {    
    private static ReactApplicationContext reactContext;    
    //必须定义    
    public TestModule3(ReactApplicationContext reactContext) {        
        super(reactContext);        
        this.reactContext=reactContext;            
        //必须添加    
    }    
    //自定义方法,触发RN中定义的consoleYS    
    public static void sendEvent(String param){        
        WritableMap eventValue=new WritableNativeMap();        
        eventValue.putString("value",param);        
        if(reactContext!=null){            
            reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("consoleYS",eventValue);        
        }    
    }复制代码

4.3)在activity文件中调用
在TestActivity3.java中

@Override    
protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);        
    setContentView(R.layout.activity_main);        
    PrintModule.sendEvent("1","123456789");        
//调用RN    
}复制代码

  运行项目,进入TestActivity3定义的页面后,发现RN控制台打印 123456  

结束。

题外话:

原生是另一个项目组完成,我们调用,所以需要一起联调。遇到的问题

i)rn的android/app目录下有自己的资源文件夹res,android项目组实现功能时也有自己的res。为了集成后结构保持清晰,在res下为rn创建资源目录,为android原生依照其功能创建目录,资源独立存放,在build.gradle文件中配置,使得编译时这两个资源目录可以合并;原生res中的参数命名时,按照功能取前缀名。如打印模块:print_***,避免参数名重复,编译报错。

ii)开发之前双方应该约定好:sdk版本一致,项目主目录一致,原生模块拥有单独目录,资源命名以功能为前缀。

iii)开发前没有约定好sdk版本,调研对android版本的支持程度,android原生引入了高版本包,集成到rn中报错。这时只能选择尝试将包版本降级


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值