React Native Android原生模块使用(获取Android手机通讯录的数据)

使用React Native开发App时会遇到需要用到一些Android原生模块,比如:访问相册、通讯录、日历等等。下面主要是以获取Android手机通讯录的数据为例,讲解React Native Android原生模块的使用。

注:数据格式可自行做修改。

React Native Android原生模块的主要流程

 在这里我将构建React Native Android原生模块的流程划分为两大步:

      1.编写Android原生模块的相关Java代码;

      2.注册与导出React Native原生模块

编写Android原生模块的相关Java代码

我们需要用到Android Studio。 首先我们用Android Studio打开React Native项目根目录下的android目录。项目初始化成功之后在Android Studio的工具栏中可以看到一个名为“app”的一个可运行的模块(注:用Android Studio第一次打开这个Android项目的时候,Android Studio会下载一些此项目所需要的依赖,比如项目所依赖的Gradle版本等)

接下来呢,我们开始编写Java代码。

1.创建一个ContactInfo.java(用于实现获取Android手机通讯录数据的功能)

import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import android.text.TextUtils;
import android.widget.Toast;

import com.facebook.react.bridge.Promise;
import com.nativedemo.utils.PermissionUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.ref.WeakReference;


public class ContactInfo {
    private static WeakReference<Activity> mActivity;
    private static Promise mPromise;

    public static void init(Activity activity) {
        if (activity == null) return;
        mActivity = new WeakReference<>(activity);
    }
    //获取通讯录
    public static void getContactInfo(Context context, Promise promise){
        if (context==null||mActivity==null){
            promise.reject("获取失败");
            return;
        }
        //权限检查(大于等于6.0版本需要动态添加)
        if(PermissionUtils.checkPermission(mActivity.get(),Manifest.permission.READ_CONTACTS)
                && PermissionUtils.checkPermission(mActivity.get(),Manifest.permission.READ_EXTERNAL_STORAGE)){
            String contactInfoStr=getAllContactInfo(context);
            if (TextUtils.isEmpty(contactInfoStr)){
                promise.reject("获取失败");
            }else {
                promise.resolve(contactInfoStr);
            }
        }else {//请求权限(大于等于6.0版本需要动态添加)
            mPromise=promise;
            PermissionUtils.requestPermission(mActivity.get(),new String[]{Manifest.permission.READ_CONTACTS,
                    Manifest.permission.READ_EXTERNAL_STORAGE});
        }

    }
    //获取所有通讯录信息
    private static String getAllContactInfo (Context context) {
        SystemClock.sleep(3000);
       // ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
        JSONArray jsonArray=new JSONArray();
        // 1.获取内容解析者
        ContentResolver resolver = context.getContentResolver();
        // 2.获取内容提供者的地址:com.android.contacts
        // raw_contacts表的地址 :raw_contacts
        // view_data表的地址 : data
        // 3.生成查询地址
        Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri date_uri = Uri.parse("content://com.android.contacts/data");
        // 4.查询操作,先查询raw_contacts,查询contact_id
        // projection : 查询的字段
        Cursor cursor = resolver.query(raw_uri, new String[]{"contact_id"}, null, null, null);
        try {
            // 5.解析cursor
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    // 6.获取查询的数据
                    String contact_id = cursor.getString(0);
                    // cursor.getString(cursor.getColumnIndex("contact_id"));//getColumnIndex
                    // : 查询字段在cursor中索引值,一般都是用在查询字段比较多的时候
                    // 判断contact_id是否为空
                    if (!TextUtils.isEmpty(contact_id)) {//null   ""
                        // 7.根据contact_id查询view_data表中的数据
                        // selection : 查询条件
                        // selectionArgs :查询条件的参数
                        // sortOrder : 排序
                        // 空指针: 1.null.方法 2.参数为null
                        Cursor c = resolver.query(date_uri, new String[]{"data1",
                                        "mimetype"}, "raw_contact_id=?",
                                new String[]{contact_id}, null);
                        //HashMap<String, String> map = new HashMap<String, String>();
                        JSONObject object=new JSONObject();
                        // 8.解析c
                        if (c != null) {
                            while (c.moveToNext()) {
                                // 9.获取数据
                                String data1 = c.getString(0);
                                String mimetype = c.getString(1);
                                // 10.根据类型去判断获取的data1数据并保存
                                if (mimetype.equals("vnd.android.cursor.item/phone_v2")) {
                                    // 电话
                                    object.put("phone", data1);
                                } else if (mimetype.equals("vnd.android.cursor.item/name")) {
                                    // 姓名
                                    object.put("name", data1);
                                }
                            }
                        }
                        // 11.添加到集合中数据
                       // list.add(map);
                        jsonArray.put(object);
                        // 12.关闭cursor
                        if (c != null) {
                            c.close();
                        }
                    }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            // 12.关闭cursor
            if (cursor != null) {
                cursor.close();
            }
        }
        return jsonArray.toString();
    }
    //权限回调
    public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PermissionUtils.RC_REQUEST_PERMISSIONS) {
            for (int i = 0, j = permissions.length; i < j; i++) {
                if(TextUtils.equals(permissions[i],Manifest.permission.READ_CONTACTS)||
                        TextUtils.equals(permissions[i],Manifest.permission.READ_EXTERNAL_STORAGE)){
                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        getContactInfo(mActivity.get(),mPromise);
                    }else {
                        if(mActivity==null)return;
                        Toast.makeText(mActivity.get(),"没有使用权限",Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    }

}

获取手机通讯录,需要添加READ_CONTACTSREAD_EXTERNAL_STORAGE权限。Android6.0版本以下的添加权限在AndroidManifest.xml文件中添加如下:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>

Android6.0以上版本,部分权限则需动态添加,ContactInfo.java代码示例中有添加。其中涉及到的PermissionUtils.java文件代码如下:

(注:下面只是进行简单的封装,对于权限的管理需自行去查阅)

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;

public class PermissionUtils {
    public static final int RC_REQUEST_PERMISSIONS=6006;
    //检查是否拥有某一权限
    public static boolean checkPermission(Context context,String permission){
        int result = ContextCompat.checkSelfPermission(context, permission);
        return result == PackageManager.PERMISSION_GRANTED;

    }
    //请求获取权限
    public static void requestPermission(Activity activity, String[] permissions){
        ActivityCompat.requestPermissions(activity,permissions, RC_REQUEST_PERMISSIONS);
    }
}

2.在MainActivity文件中(具体路径:android/app/src/main/java/com/your-app-name/MainActivity.java)添加如下代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ContactInfo.init(this);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    ContactInfo.onRequestPermissionsResult(requestCode,permissions,grantResults);
}

备注:红色区域的代码,示例中主要用于权限的添加使用

3.创建ContactInfoModule.java文件,是一个继承了ReactContextBaseJavaModule的Java类,实现一些JavaScript所需的功能,代码如下:

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class ContactInfoModule extends ReactContextBaseJavaModule {
    public ContactInfoModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

    @ReactMethod
    public void getAllContactInfo(Promise promise){
        ContactInfo.getContactInfo(getReactApplicationContext(),promise);
    }
}

ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个模块。

要导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void

下面的参数类型在@ReactMethod注明的方法中,会被直接映射到它们对应的JavaScript类型。

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。(示例代码中使用的是Promises,参见下面更多的描述)

注册与导出React Native原生模块

1.创建ContactInfoReactPackage.java文件,为了向React Native注册我们刚才创建的原生模块,我们需要实现ReactPackageReactPackage主要为注册原生模块所存在,只有已经向React Native注册的模块才能在js模块使用。

public class ContactInfoReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new ContactInfoModule(reactContext));
        return modules;
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

2.接下来呢,我们还需要在android/app/src/main/java/com/your-app-name/MainApplication.java中注册我们的ContactInfoReactPackage

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

3.为了让你的功能从JavaScript端访问起来更为方便,通常我们都会把原生模块封装成一个JavaScript模块。这不是必须的,但省下了每次都从NativeModules中获取对应模块的步骤。这个JS文件也可以用于添加一些其他JavaScript端实现的功能.创建Contacts.js文件,代码如下:

import { NativeModules } from 'react-native';
export default NativeModules.ContactInfo;

现在,在别处的JavaScript代码中可以这样调用你的方法:

import Contacts from './Contacts';
......省略代码
Contacts.getAllContactInfo().then(result => {
    this.setState({
        result: result
    })
}).catch(e => {
    this.setState({
        result: e
    })
});

更多

Callbacks

原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JavaScript。

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里这样使用:

UIManager.measureLayout(
  100,
  100,
  (msg) => {
    console.log(msg);
  },
  (x, y, width, height) => {
    console.log(x + ':' + y + ':' + width + ':' + height);
  }
);

原生模块通常只应调用回调函数一次。但是,它可以保存callback并在将来调用。

请务必注意callback并非在对应的原生函数返回后立即被执行——注意跨语言通讯是异步的,这个执行过程会通过消息循环来进行。

Promises

原生模块还可以使用promise来简化代码,搭配ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后一个参数是一个Promise,则对应的JS方法就会返回一个Promise对象。

我们把上面的代码用promise来代替回调进行重构:

import com.facebook.react.bridge.Promise;

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.getMessage());
    }
  }

...

现在JavaScript端的方法会返回一个Promise。这样你就可以在一个声明了async的异步函数内使用await关键字来调用,并等待其结果返回。(虽然这样写着看起来像同步操作,但实际仍然是异步的,并不会阻塞执行来等待)。

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发送事件通知,最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用,像这样:

...
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模块可以通过使用DeviceEventEmitter模块来监听事件:

import { DeviceEventEmitter } from 'react-native';
...
componentWillMount: function() {
  DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
    // handle event.
  });
}
...

转载于:https://my.oschina.net/djonce/blog/983731

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Native提供了一个称为Native Modules的机制,允许您在React Native应用程序中使用原生代码。因此,您可以使用Java或Kotlin编写原生Android代码,并将其与React Native应用程序集成。以下是一些步骤: 1.创建一个新的Android库项目。 2.在您的React Native项目中创建一个新的Native Module。 3.将您的原生代码添加到Android库项目中。 4.编写Java或Kotlin代码来公开原生方法。 5.在React Native Native Module中使用这些方法。 6.构建并运行您的React Native应用程序。 这里是一个简单的例子,说明如何在React Native应用程序中使用原生Android模块: 1.创建一个新的Android库项目 在Android Studio中,选择“File” > “New” > “New Module”。然后选择“Android Library”并按照向导中的说明创建一个新的Android库项目。 2.在您的React Native项目中创建一个新的Native Module 在React Native项目的根目录下,运行以下命令: ``` react-native create-library MyNativeModule ``` 此命令将创建一个名为MyNativeModule的新目录。在此目录中,您可以添加一个名为MyNativeModule.java的文件。 3.将您的原生代码添加到Android库项目中 将您的原生代码复制到Android库项目中的src/main/java目录中。 4.编写Java或Kotlin代码来公开原生方法 在您的Java或Kotlin类中,使用@ReactMethod注释来标记要公开给React Native原生方法。例如: ``` @ReactMethod public void showToast(String message) { Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_SHORT).show(); } ``` 5.在React Native Native Module中使用这些方法 在您的React Native应用程序中,导入MyNativeModule并调用其方法。例如: ``` import { NativeModules } from 'react-native'; const { MyNativeModule } = NativeModules; MyNativeModule.showToast('Hello, world!'); ``` 6.构建并运行您的React Native应用程序 在React Native应用程序的根目录中,运行以下命令以构建并运行您的应用程序: ``` react-native run-android ``` 这样,您就可以在React Native应用程序中使用原生Android模块了!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值