789 งานเร่งกับ React Native (012.1 )

olDuelo
5 min readJan 10, 2017

--

ห่างหายไปหลายอาทิตย์ นอกจากติดปีใหม่แล้ว ยังติดงานที่เร่งสุดยอดๆๆๆ T-T กว่าจะหาเวลามาเขียนบทความได้…

Intro

ช่วงระยะเวลาที่ผ่านมา มีโอกาสได้แนะนำหลายๆท่าน ในเรื่องการใช้ RN ในการพัฒนาAPP ซึ่งส่วนใหญ่ก็สนใจ แต่ก็ยังมีความกังวนว่า

  • RN จะทำงานได้เหมือนกับNative code ไหม?
  • แล้ว RN มีข้อจำกัดอะไรไหม?

เรื่องนี้ทาง facebook ก็คิดเผื่อมาให้แล้วครับ ถ้าวันนึงRNเกิดทำตามแบบที่เราต้องการไม่ได้หรือเราต้องการนำRNไปเรียกใช้ Hardware(HW) ที่มี SDK มาให้ ก็ให้RN เรียกใช้งาน Native Function ที่เขียนโดย Native Code เลยสิจะไปยากอะไร!

Android

ในการสร้าง Native Modules นี้จะทำ function เพื่อตรวจความเร็วอินเทอร์เน็ตที่เป็น Native Function แล้วให้RNเรียกใช้

Native Modules

ในส่วนของ Native Modules นี้คือการสร้าง Native Function ด้วย Native Code(Java) เพื่อให้ RN สามารถเชื่อมต่อเรียกใช้งาน Native Modules ต่างๆได้อย่างสะดวก โดยมีขั้นตอน 4ขั้นตอนคือ

1. Create Module

ขั้นตอนนี้จะเป็นการสร้าง Native Function ที่ต้องการ

เริ่มต้นด้วยการเปิดทำการเปิด Project RN ด้วย Android Studio ถ้ายังไม่เคยทำแนะนำให้อ่าน บทความนี้

Create Class

หลังจากนั้นสร้าง Class เพื่อไว้ใส่ function ที่ต้องการให้RN เรียกใช้

โดยตั้งชื่อClass ว่า “CheckSpeedModule”

หลังจากนั้น เปิดไฟล์ “CheckSpeedModule” ที่พึ่งสร้างไป จะได้ดังภาพ

ต่อมาให้ทำการ extends

extends ReactContextBaseJavaModule

เมื่อ extends ReactContextBaseJavaModule แล้วจะต้องทำการ import class

import com.facebook.react.bridge.ReactContextBaseJavaModule;

และทำการ implement methods

ต่อมาทำการสร้าง constructor

Create Function

ตรงส่วนนี้เราจะมาสร้าง Function ที่ไว้ทำงานตามที่เราต้องการแล้วให้ RN เรียกใช้

ถ้าใครต้องการเรียกใช้ SDK หรือทำงานNative Function อื่นๆก็เขียนตรงนี้แหละ

เริ่มต้นสร้าง function ชื่อ CheckSpeedNow เป็นรูปแบบ public void

public void CheckSpeedNow(){

}

ต่อมาให้ใส่ @ReactMethod ไว้เหนือfunction ด้วยเพื่อให้สามารถเรียกใช้ได้จาก RN

ต่อมาเรามาดูประเภทตัวแปรที่สามารถใช้ได้กัน โดยด้านซ้ายคือ ประเภทในฝั่งNative Code ด้านขาวคือประเภทตัวแปรของRN

เช่น ถ้า Native Code รับหรือส่งค่าเป็น Double ทาง RN ก็จะได้รับหรือส่งค่าเป็นNumber

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

ทีนี้ใน CheckSpeedNow จะมีการรับค่า URL จาก RN มาเพื่อใช้ทดสอบความเร็วก็จะได้เป็น

ต่อมาในส่วน CheckSpeedNow นี้จะมีการทำงานเป็นแบบ asynchronous คือสั่งให้ function นี้ทำงานไปโดยไม่รอผล แต่เมื่อทำเสร็จแล้วให้บอกว่าเสร็จแล้วและทำการส่งผลมาให้ด้วย

โดยถ้าใน RN ปกติการทำงานแบบ asynchronous จะใช้ Promises เช่นกันในส่วน Native Code ก็ใช้ Promises เหมือนกัน

ดังนั้นก็ทำการเพิ่ม

final Promise promise

ไปในส่วนการรับค่าของ function ด้วย เพื่อบอกให้ RN รู้ว่าในส่วนนี้จะมีการทำงานเป็นแบบ asynchronous

สำหรับ Promises จะมีข้อบังคับคือต้องมีการตอบกลับหลักๆอยู่ 2 ประเภทคือ

resolve()

กับ

reject()

โดย resolve จะเป็นการบอกว่าการทำงานสำเร็จ แล้วทำการส่งข้อมูลที่ได้ไปใน () ของ resolve เช่น

resolve("text")

ส่วน reject เป็นการบอกว่าทำงานผิดพลาด แล้วทำการส่งข้อมูลที่ได้ไปใน () ของ reject ส่วนใหญ่จะส่งข้อผิดพลาดไป เช่น

reject("ERROR 404")

และเมื่อทาง RN ทำ Tyr Catch ไว้แล้วเกิดการ reject ก็จะไปทำงานที่ Catch ทันที

ต่อมาเราจะทำการเขียนcode เพื่อตรวจสอบ internet speed โดยทำการ post ไปที่URL ที่ส่งมาแล้วทำการจับเวลาแล้วนำมาคำนวนหาความเร็วในการรับข้อมูล เมื่อทำเสร็จจะทำการส่งข้อมูลกลับไป ถ้าเกิดerror จะส่งerror ออกไป

@ReactMethod
public void CheckSpeedNow(String Url,final Promise promise){
String UrlJob = Url;
final long startTime = System.currentTimeMillis();
try{
AsyncHttpPost post = new AsyncHttpPost(UrlJob);
AsyncHttpClient.getDefaultInstance().executeString(post, new AsyncHttpClient.StringCallback() {
@Override
public void onCompleted(Exception ex, AsyncHttpResponse source, String result) {
if (ex != null) {
ex.printStackTrace();
return;
}
long endTime = System.currentTimeMillis();
long dataSize = result.length() / 1024;
long takenTime = endTime - startTime;
long s = takenTime / 1000;
double speed = dataSize / s;
WritableMap map = Arguments.createMap();
map.putDouble("speed", speed);
map.putString("mdt", "kbps");
promise.resolve(map);
}
});
}catch (Exception e){
promise.reject("ERROR_", e);
}
}

สามารถอธิบายการทำงานง่ายๆแบบนี้ CheckSpeedNow รับค่าString ชื่อUrl และ Promise ชื่อ promise

แล้วทำกระบวนการต่างๆ เมื่อทำสำเร็จแล้ว ทำการสร้าง WritableMap โดย WritableMap นี้จะมีรูปแบบคล้ายarray ที่เก็บข้อมูลที่ทำการ map type,key,value แล้วสำหรับ Native Code เพื่อส่งกลับไป RN

ดังนั้นจะเห็นว่า เรา map speed ที่เป็น Double และมีค่าเท่ากับ speed และ map mdt ที่เป็น String และมีค่าเท่ากับ “kbps”

สุดท้ายทำการ ส่ง WritableMap ผ่าน promise.resolve และถ้ากระบวนการใด error จะส่ง error ไปทาง promise.reject

Set Name of Module

ต่อมาให้ทำการตั้งชื่อของ Module โดยใส่ที่ใน ส่วน Return ของ function getName โดยเริ่มแรก เป็น null

ให้ทำการเปลี่ยนเป็น CheckSpeedModule

2. Create Package

สำหรับขั้นตอนนี้จพทำการสร้าง Package เพื่อรวมหลายๆ Module ไว้เพื่อใช้ในขั้นตอนต่อไป

เริ่มต้นจากสร้าง Class CheckSpeedPackage ที่ implements ReactPackage

implements ReactPackage

ทำการ implement methods

หลังจาก implement เสร็จจะได้ดังภาพ

ต่อมาให้ทำการเพิ่ม NativeModule ในส่วน createNativeModules

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

List<NativeModule> modules = new ArrayList<>();

modules.add(new CheckSpeedModule(reactContext));

return modules;
}

จะเห็นว่าบรรทัดแรกจะทำการสร้าง List<NativeModule> ชื่อว่า modules

บรรทัดต่อมาทำการ เพิ่ม CheckSpeedModule เข้าไปใน modules

บรรทัดต่อมาทำการ return modules

สุดท้ายให้ดู ที่ส่วน createJSModules และ createViewManagers จากเดิมที่เป็นแบบนี้

return null;

ให้เปลี่ยนเป็น

return Collections.emptyList();

3. Use Package

หลังจากทำตาม ขั้นตอนที่ 1 และ 2 ก็จะมาถึงขั้นตอนการนำ Package ไปให้กับ RN รู้จักแล้ว

เริ่มจาก เปิดไฟล์ MainApplication จะพบดังภาพ

ให้ดูในส่วน getPackages ให้ทำการเพิ่ม Package ที่สร้างในขั้นตอนที่ 2 เข้าไป

ทดสอบ Build ถ้าผ่านก็แสดงว่าใช้ได้

4. Access Modules from RN

ขั้นตอนสุดท้ายแล้วคือการการเรียกใช้ในRN

Create export module

เริ่มต้นสร้างไฟล์ CheckSpeedModule.js สำหรับ export module

ต่อมาทำการ import NativeModules จาก react-native

import { NativeModules } from ‘react-native’;

แล้วทำการ exports ที่เราตั้งชื่อไว้ในขั้นตอนที่ 2

module.exports = NativeModules.CheckSpeedModule;

Call Module

ต่อมาจะทำการเรียกใช้ Native Function ที่สร้างไว้ในขั้นตอนก่อนหน้า

เริ่มจากทำการ import CheckSpeedModule

ต่อมาสร้าง function CheckSpeeds

โดยอธิบายได้ดังนี้

  • บรรทัด 66 สร้าง function ชื่อ CheckSpeeds เป็น asynchronous
  • บรรทัด 68–71 คือการเรียกใช้ CheckSpeedModule ชื่อfunction CheckSpeedNow โดยส่ง String “http://speedtest.ftp.otenet.gr/files/test100k.db” ไปให้ แล้วทำการรับข้อมูล speed และ mdt มา จาก promise.resolve
  • บรรทัด 72 แสดงค่าที่รับมา
  • บรรทัด 72 แสดงค่า error ที่ส่งมาจาก promise.reject

ต่อมาทำการทดสอบเรียกใน componentDidMount

if (Platform.OS === 'android') {
this.CheckSpeeds()
}

โดยทำการตรวจสอบให้เรียกใช้งานได้เฉพาะ Android เนื่องจาก เราทำแค่ส่วนของ Android ถ้า ios มาเรียกใช้จะError

Test

สำหรับการทดสอบนั้นจะมีการเพิ่ม log เพื่อให้เห็นว่าส่วนนี้ทำงานเป็น asynchronous จริงๆ โดยทำการเพิ่ม log ดังนี้

ถ้าเป็นการทำงานแบบ synchronous ก็จะทำการแสดงตามลำดับดังนี้

componentDidMount Start
Call CheckSpeeds
ความเร็ว:33:kbps
componentDidMount End

แต่ถ้าทำงานแบบ asynchronous ก็จะแสดงเป็น

componentDidMount Start
Call CheckSpeeds
componentDidMount End
ความเร็ว:33:kbps

จบแล้วก็การทำ Native Modules ของ Android

ครั้งหน้าจะมาดูการทำ Native Modules ของ iOS กันครับ

--

--