react-native 之与"android原生模块"交互

前端之家收集整理的这篇文章主要介绍了react-native 之与"android原生模块"交互前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

使用react-native写移动端项目,怎么能够少的了与原生模块的交互。否则,混合开发,那不是白瞎了这个名字喽。

使用react-native 与android原生模块交互方式

1,使用回调的Callback方式实现与android原生模块交互
2,使用JavaScript的Promise方式实现与android原生模块交互
3,使用react-native的DeviceEventEmitter 方式实现与android原生模块交互

在描述三种交互方式之前,先描述一下我的交互逻辑。这里我通过从react-native的js代码中启用安卓原生代码,打开相册并获取其中一张照片并回调到js页面方法中并进行接收、赋值、更新页面
原生代码打开相册并获取图片地址功能我引入了github上的一个第三方库,在react-native项目的android/app/build.gradle中进行依赖处理,然后编写原生java代码逻辑调用打开相册功能,并实现react-native中的js与java的互相访问调用来实现交互。

原生模块的逻辑实现

编写原生逻辑
暴露原模块接口向js
注册原生模块
导出、导入react-native原生模块

step导入原生的android模块

首先导入原生的android模块项目代码到Android Studio,像这样

build成功之后,引入相册功能的gralde依赖到android/app/build.gradle,像这样加入
compile 'com.github.LuckSiege.PictureSelector:picture_library:v2.1.4'//引入相机功能第三方
然后在android/build.gradle中配置maven {url 'https://jitpack.io'}当然这些在第三方依赖库有使用说明,我就不多描述了。说一点略微重要的,在使用第三方库时候,她们使用的依赖方式是implementation 而不是 compile这个是你的IDE高版本的时候方法,这是如果在使用引入方式是compile无法正常通过编译,那么你可以使用该库的之前版本,可到Branch中查看。只是为了解决Filed to resolve:com.android.support:support-xx:xx找不到该版本号这种问题。

若在刚导入了原生代码,build上有问题请查看博客,或许会有帮助。

step编写原生的android模块代码

/** * @Title:ImageCrop * @Package:com.imoocchapterone * @Description:选择剪切图片的接口 * @Auther:YJH * @Email:yuannunhua@gmail.com * @Date:2018/6/1415:28 */
public interface ImageSelector {

    /** * @param errorCallback * @param successCallback */
    void chooseImgCallback(Callback successCallback,Callback errorCallback);

    /** * @param promise */
    void chooseImgPromise(Promise promise);
}
/** * @Title:ImageCropImp * @Package:com.imoocchapterone * @Description:选择剪切图片的具体类 * @Auther:YJH * @Email:yuannunhua@gmail.com * @Date:2018/6/1415:30 */
public class ImageSelectorimpl implements ImageSelector,ActivityEventListener {

    private Activity mActivity;
    private Promise mPromise;
    private Callback errorCallback;
    private Callback successCallback;

    private ImageSelectorimpl(Activity activity) {
        this.mActivity = activity;
    }


    /** * 功能获取 ImageSelectorimpl 对象的实例 * @param activity * @return */
    public static ImageSelectorimpl of(Activity activity) {
        return new ImageSelectorimpl(activity);
    }

    /** * 使用Promise回调方式 * 功能:实现ImageCrop接口中的方法 * @param promise */
    @Override
    public void chooseImgPromise(Promise promise) {
        this.mPromise = promise;
        getSelectAlbum();
    }

    /** * 使用Callback回调方式 * 功能:实现ImageCrop接口中的方法 * @param promise */
    @Override
    public void chooseImgCallback(Callback successCallback,Callback errorCallback) {
        this.successCallback = successCallback;
        this.errorCallback = errorCallback;
        getSelectAlbum();
    }

    public void updateActivity(Activity activity) {
        this.mActivity = activity;
    }


    /** * 功能:实现ActivityEventListener接口中的方法 * @param activity * @param requestCode * @param resultCode * @param data */
    @Override
    public void onActivityResult(Activity activity,int requestCode,int resultCode,Intent data) {
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case PictureConfig.CHOOSE_REQUEST:
                    // 图片选择结果回调
                    selectList = PictureSelector.obtainMultipleResult(data);
// successCallback.invoke(selectList.get(0).getPath());//Callback回调
                    mPromise.resolve(selectList.get(0).getPath());//mPromise回调
                    break;
            }
        } else {
            mPromise.reject("no img get.");//mPromise回调
// errorCallback.invoke(CODE_ERROR_CROP,"裁剪失败");//Callback回调
        }
    }

    /** * 功能:实现ActivityEventListener接口中的方法 * * @param intent */
    @Override
    public void onNewIntent(Intent intent) {
// 无操作
    }

    private int chooseMode = PictureMimeType.ofAll();
    private List<LocalMedia> selectList = new ArrayList<>();

    //打开相册功能
    public void getSelectAlbum() {
        // 进入相册 以下是例子:用不到的api可以不写
        PictureSelector.create(mActivity)
                .openGallery(chooseMode)//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
                .theme(R.style.picture_default_style)//主题样式(不设置为默认样式) 也可参考demo values/styles下 例如:R.style.picture.white.style
                .maxSelectNum(1)// 最大图片选择数量 int
                .minSelectNum(1)// 最小选择数量 int
                ...
                .forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code

    }
}

以上是在原生代码中去实现操作原生模块逻辑的java代码
其中在类ImageSelectorimpl.class中实现了接口ActivityEventListener,并由此重写其中方法onActivityResult(..),作为用户执行startActivityForResult(..)程序的回调。关键的是,在onActivityResult(..)中拿到原生逻辑的Activity回调,是要将结果回调到js代码逻辑页面以提供使用。由此需要有设置对方法onActivityResult(..)的监听。则后面会通过addActivityEventListener(..)方法来实现。

/** * @Title:ImageSelectorModule * @Package:com.imoocchapterone * @Description:实现对react-native的接口暴露 * @Auther:YJH * @Email:yuannunhua@gmail.com * @Date:2018/6/1415:59 */
public class ImageSelectorModule extends ReactContextBaseJavaModule implements ImageSelector {

    private ImageSelectorimpl mImageSelectorimpl;

    public ImageSelectorModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "ImageSelector";
    }


    /** * 功能:向React Native暴露的接口,并执行数据交互操作 */
    @ReactMethod
    @Override
    public void chooseImgCallback(Callback successCallback,Callback errorCallback) {
        if(null == mImageSelectorimpl){
            mImageSelectorimpl = ImageSelectorimpl.of(getCurrentActivity());
            getReactApplicationContext().addActivityEventListener(mImageSelectorimpl);
        }
        mImageSelectorimpl.chooseImgCallback(successCallback,errorCallback);
    }

    @ReactMethod
    @Override
    public void chooseImgPromise(Promise promise) {
        if(null == mImageSelectorimpl){
            mImageSelectorimpl = ImageSelectorimpl.of(getCurrentActivity());
            getReactApplicationContext().addActivityEventListener(mImageSelectorimpl);
        }

// mImageSelectorimpl.updateActivity(getCurrentActivity());
        mImageSelectorimpl.chooseImgPromise(promise);
    }


}

当然对于ImageSelectorModule.classImageSelectorimpl.class其实可以合成一个类并同时继承类ReactContextBaseJavaModule和实现接口ActivityEventListener也是可以的。暂不多说。。
在这个类中继承了一个抽象类ReactContextBaseJavaModule,实现了在其抽象父类BaseJavaModule中的关键方法getName()。返回一个字符串,当然这个返回值的名字,你可以随便起,但时请记住,这个字符串名字最后是要与你在js中调用组件NativeModules中的一个子组件名字是要保持一致的。后面会分析到!
在这个类中继承了一个抽象类ReactContextBaseJavaModule,通过注解标识@ReactMethod实现对js暴露。
在这个类中继承了一个抽象类ReactContextBaseJavaModule,看其源码

public abstract class ReactContextBaseJavaModule extends BaseJavaModule {
 ...

  /** * Subclasses can use this method to access catalyst context passed as a constructor */
  protected final ReactApplicationContext getReactApplicationContext() {
    return mReactApplicationContext;
  }

  /** * Get the activity to which this context is currently attached,or {@code null} if not attached. * * DO NOT HOLD LONG-LIVED REFERENCES TO THE OBJECT RETURNED BY THIS METHOD,AS THIS WILL CAUSE * MEMORY LEAKS. * * For example,never store the value returned by this method in a member variable. Instead,call * this method whenever you actually need the Activity and make sure to check for {@code null}. */
  protected @Nullable final Activity getCurrentActivity() {
    return mReactApplicationContext.getCurrentActivity();
  }
}

关键的两个方法getReactApplicationContext()getCurrentActivity()
方法getCurrentActivity()从注释上看,就是拿到一个activity来供我们操作。
方法getReactApplicationContext()从注释上看,”子类可以使用此方法访问作为构造函数传递上下文”。其实就是为了提供将原生模块注册react-native组件NativeModule的队列并绑定接口ReactPackage所使用的
看下面代码的具体实现

/** * @Title:ImageReactPackage * @Package:com.imoocchapterone * @Description:注册、导出react-native的原生模块<格式是基本固定的> * @Auther:YJH * @Email:yuannunhua@gmail.com * @Date:2018/6/1416:44 */
public class ImageReactPackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ImageSelectorModule(reactContext));
        return modules;

    }

  ...
}

实现注册,且只有在实现了接口ReactPackage才有资格注册

public class MainApplication extends Application implements ReactApplication {

 ...
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),new ImageReactPackage()
      );
    }
  };

...
}

以上就是在android模块引入第三方类库,并实现可与react-native的js进行交互的基础逻辑配置。
创建一个js类ImageSelector,导出NativeModules.ImageSelector备后面使用。NativeModules我们看到是个组件,此时ImageSelector也既有可能成为了组件类型。并且ImageSelector这个单词的形成是在原生代码模块的类ImageSelectorModule.class中的方法getName()中得到!并且ImageSelector调用暴露java接口方法说明js 类 ImageSelector好像继承(具备)了java 抽象类 ReactContextBaseJavaModule功能。即从继承java 抽象类 ReactContextBaseJavaModule的类作为调用的交互入口。

/** * Created by YJH on 2018/6/14. */
import {NativeModules} from 'react-native';
export default NativeModules.ImageSelector;

当然有导出,只在我们需要使用的地方导入就可以使用了
import ImageSelector from '../../nativeJs/ImageSelector';

1使用回调的Callback方式实现与android原生模块交互

在一个组件的按钮被点击时调用方法

...

onPress={() => this.onSelectPicturesCallback()}

...

/**
* Callback 方式
* 功能获取使用原生打开相册并获取图片的回调地址方式
*/
onSelectPicturesCallback() {
   ImageSelector.chooseImgCallback((result) => {
        console.log(result);
        this.setState({
             imgUrl: result
        })
   },(error) => {
        console.log(error);
        this.setState({
            imgUrl: error
        })
  })
}

...

通过这种方式可以在state中成功更新的imgUrl值。

2使用JavaScript的Promise方式实现与android原生模块交互

...

onPress={() => this.onSelectPicsFucX()}

...

/** * Promise 方式1 * 功能获取使用原生打开相册并获取图片的回调地址方式 */
onSelectPicsFuc1(){
    ImageSelector.chooseImgPromise().then((result) => {
        this.setState({
            imgUrl: result
        })
    }).catch(error => {
        console.log(error);
        this.setState({
             imgUrl: error
        })
    });
}
/** * Promise 方式2 * 功能获取使用原生打开相册并获取图片的回调地址方式 */
async onSelectPicsFuc2() {
    var _promise =  await ImageSelector.chooseImgPromise();
    _promise.then((result) => {
         this.setState({
             imgUrl: result
         })
    }).catch(error => {
        console.log(error);
        this.setState({
             imgUrl: error
        })
   });
}

/** * Promise 方式3 * 功能获取使用原生打开相册并获取图片的回调地址方式 */
async onSelectPicsFuc3() {
     var imgPromise = await ImageSelector.chooseImgPromise();
     this.setState({
         imgUrl: imgPromise
     })
}

...

可以看到这里展示了使用promise获取与原生模块交互的逻辑回调。并使用了三方种方法来实现!
其中方式1方式3可以获得Promise对象并在state中更新的imgUrl值。但是方式2是不能实现的,而且是错误并提醒报错的。因为使用了asycn/await方式之后,返回的是一个内容结果,而不是Promise对象。
注意,被桥接的原生方法的最后一个参数是一个Promise对象,那么该JS方法会返回一个Promise对象。且,不需在js中传入promise对象参数。

3使用react-native的DeviceEventEmitter 方式实现与android原生模块交互

使用以上两种方式回去与原生模式的交互回调,只能执行一次。一次启动,带动一次回调,然后结束!如果想要在原生模块向js发送多次事件。那么,就要用到事件发射器。她可实现,原生模块可以在没有被直接调用的情况下就可以往JavaScript发送消息事件。好的实现一下
在js类中实现

...

componentDidMount() {
   //注册扫描监听
   DeviceEventEmitter.addListener('onScanningResult',this.onScanningResult);
}

onScanningResult = (eventName,params) => {

   //收到原生代码的发射通知,弹出toast提示
   this.toast.show(eventName,DURATION.LENGTH_SHORT);
};

componentWillUnmount() {
   DeviceEventEmitter.removeListener('onScanningResult',this.onScanningResult);//移除扫描监听
}
...

在原生模块实现

public interface ImageSelector {

   ...

    void sendEventMethod(String eventName,@Nullable WritableMap params);
    void sendEventMethod(ReactContext reactContext,String eventName,@Nullable WritableMap params);
}
public class ImageSelectorimpl implements ImageSelector,ActivityEventListener {

  ...
    /** * 功能:发射器实现通知功能的-具体方法 * @param reactContext * @param eventName * @param params */
    @Override
    public void sendEventMethod(ReactContext reactContext,@Nullable WritableMap params) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName,params);
    }
    @Override
    public void sendEventMethod(String eventName,@Nullable WritableMap params) {
    }
   ...
}
public class ImageSelectorModule extends ReactContextBaseJavaModule implements ImageSelector {

    ...

    /** * 功能:发射器实现通知功能方法 * @param eventName * @param params */
    @Override
    public void sendEventMethod(String eventName,@Nullable WritableMap params) {
        this.sendEventMethod(reactContext,eventName,params);
    }

    @Override
    public void sendEventMethod(ReactContext reactContext,@Nullable WritableMap params) {
        mImageSelectorimpl.sendEventMethod(reactContext,params);
    }
}

只是在原先的代码添加了写方法,来实现可多次进行发射器发送通知功能

ImageSelectorModule实例.sendEventMethod(String eventName,@Nullable WritableMap params);

参考资料:
https://www.hellojava.com
https://stackoverflow.com/questions/
https://github.com/crazycodeboy/RNStudyNotes/blob/
http://www.lcode.org/react-native
http://www.devio.org/2017/01/22/React-Native-Android

猜你在找的React相关文章