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 กันครับ