一:介紹
React Native (簡稱RN)是Facebook於2015年4月開源的跨平臺移動應用開發框架,是Facebook早先開源的JS框架 React 在原生移動應用平臺的衍生產物,目前支援iOS和安卓兩大平臺。RN使用Javascript語言,類似於HTML的JSX,以及CSS來開發移動應用,因此熟悉Web前端開發的技術人員只需很少的學習就可以進入移動應用開發領域。
在React Native移動平臺專案開發中,除了React Native 提供的封裝好的部分外掛和原聲組建外,在實際的專案中還需要使用到很多其他的外掛,比如網路請求、資料庫、相機、相簿、通訊錄、視訊播放器、瀏覽器、藍芽連線、圖片處理、訊息推送、地圖、統計、埋點等等APP開發中需要用到的功能,都為IDE開發平臺提供封裝好的外掛,以便專案開發使用。
另外,這些博文都是來源於我日常開發中的技術總結,在時間允許的情況下,我會針對技術點分別分享iOS、Android兩個版本,如果有其他技術點需要,可在文章後留言,我會盡全力幫助大家。這篇文章重點介紹網路請求外掛的開發與使用
原始碼Demo獲取方法
如果需要React Native網路請求外掛原始碼demo,歡迎關注 【網羅開發】微信公眾號,回覆【65】便可領取。
網羅天下方法,方便你我開發,所有文件會持續更新,歡迎關注一起成長!
二:實現思路分析
網路請求外掛是需要實現前端與服務端的資料互動,其中包括GET請求、POST請求、檔案上傳、單/多張圖片上傳、檔案下載等功能。這些功能將通過封裝後的方法暴漏出來,通過RN介面提供給Javascript開發使用。
具體的實現思路如下:
-
新建NetWorkPlugin類,實現RCTBridgeModule協議
-
新增RCT_EXPORT_MODULE()巨集
-
新增React Native跟控制器
-
宣告被JavaScript 呼叫的方法
-
匯入AFNetworking請求庫
-
新建NetworkHelper類,封裝實現網路請求功能
-
實現GET請求
-
實現POST請求
-
實現檔案上傳
-
實現單/多張圖片上傳
-
實現檔案下載
-
Javascript呼叫瀏覽器方法
三:實現原始碼分析
1. 新建NetWorkPlugin類,實現RCTBridgeModule協議
新建繼承NSObject的NetWorkPlugin類,並實現RCTBridgeModule協議
// NetWorkPlugin.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <UIKit/UIKit.h>
@interface NetWorkPlugin : NSObject<RCTBridgeModule>
@end
複製程式碼
2. 新增RCT_EXPORT_MODULE()巨集
為了實現RCTBridgeModule協議,NetWorkPlugin的類需要包含RCT_EXPORT_MODULE()巨集。 並在這個巨集裡面新增一個引數“NetWorkPlugin”用來指定在 JavaScript 中訪問這個模組的名字。 如果你不指定,預設就會使用這個 Objective-C 類的名字。 如果類名以 RCT 開頭,則 JavaScript 端引入的模組名會自動移除這個字首。
// NetWorkPlugin.m
#import "NetWorkPlugin.h"
@implementation NetWorkPlugin
RCT_EXPORT_MODULE(NetWorkPlugin);
@end
複製程式碼
3. 新增React Native跟控制器
如果不新增React Native跟控制器,view將不能正常顯示出來,實現方法如下:
// NetWorkPlugin.m
#import <React/RCTUtils.h>
複製程式碼
引入<React/RCTUtils.h>之後,在檢視初始化或者顯示的時候,按照如下方法呼叫即可
UIViewController *vc = RCTPresentedViewController();
複製程式碼
4. 宣告被JavaScript 呼叫的方法
React Native需要明確的宣告要給 JavaScript 匯出的方法,否則 React Native 不會匯出任何方法。下面通過舉例來展示宣告的方法,通過RCT_EXPORT_METHOD()巨集來實現:
// NetWorkPlugin.m
#import "NetWorkPlugin.h"
#import <React/RCTUtils.h>
@implementation NetWorkPlugin
RCT_EXPORT_MODULE(NetWorkPlugin);
RCT_EXPORT_METHOD(post:(NSDictionary *)arguments
withCompletionHandler:(RCTResponseSenderBlock)completion
failureHandler:(RCTResponseSenderBlock)failure)
{
NSLog(@"POST網路請求執行方法");
}
@end
複製程式碼
5. 匯入AFNetworking請求庫
網路請求使用的第三方庫是AFNetworking,這個庫很常見,也比較常用,就不做過多的描述,可手動匯入也可使用cocoapods自動匯入,匯入之後在.m檔案中引入標頭檔案。
6. 新建NetworkHelper類,封裝實現網路請求功能
新建繼承NSObject的NetworkHelper類,定義列舉型別來判斷網路狀態:
typedef NS_ENUM(NSUInteger, NetworkStatusType) {
/** 未知網路*/
NetworkStatusUnknown,
/** 無網路*/
NetworkStatusNotReachable,
/** 手機網路*/
NetworkStatusReachableViaWWAN,
/** WIFI網路*/
NetworkStatusReachableViaWiFi
};
複製程式碼
定義網路狀態的Block
typedef void(^NetworkStatus)(NetworkStatusType status);
複製程式碼
實時獲取網路狀態,通過Block回撥實時獲取(此方法可多次呼叫)
+ (void)networkStatusWithBlock:(NetworkStatus)networkStatus;
複製程式碼
7. 實現GET請求
宣告GET請求方法:
/**
*
* @param URL 請求地址
* @param parameters 請求引數
* @param success 請求成功的回撥
* @param failure 請求失敗的回撥
*
* @return 返回的物件可取消請求,呼叫cancel方法
*/
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
parameters:(id)parameters
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure;
複製程式碼
8. 實現POST請求
宣告POST請求方法:
/**
*
* @param URL 請求地址
* @param parameters 請求引數
* @param success 請求成功的回撥
* @param failure 請求失敗的回撥
*
* @return 返回的物件可取消請求,呼叫cancel方法
*/
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure;
複製程式碼
POST請求具體的方法實現如下:
+ (NSURLSessionTask *)POST:(NSString *)URL
parameters:(id)parameters
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure {
NSString *AllReplaceURL = [self replaceURL:URL];
[self setAFHTTPSessionManagerProperty:^(AFHTTPSessionManager *sessionManager) {
[sessionManager.requestSerializer setQueryStringSerializationWithBlock:^NSString * _Nonnull(NSURLRequest * _Nonnull request, id _Nonnull parameters, NSError * _Nullable __autoreleasing * _Nullable error) {
return parameters;
}];
}];
NSURLSessionTask *sessionTask = [_sessionManager POST:AllReplaceURL parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (_isOpenLog) {NSLog(@"responseObject = %@",[self jsonToString:responseObject]);}
[[self allSessionTask] removeObject:task];
success ? success(responseObject) : nil;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (_isOpenLog) {NSLog(@"error = %@",error);}
[[self allSessionTask] removeObject:task];
failure ? failure(error) : nil;
}];
sessionTask ? [[self allSessionTask] addObject:sessionTask] : nil ;
return sessionTask;
}
複製程式碼
9. 實現檔案上傳
宣告檔案上傳方法:
/**
*
* @param URL 請求地址
* @param parameters 請求引數
* @param names 檔案對應伺服器上的欄位
* @param filePaths 檔案本地的沙盒路徑
* @param progress 上傳進度資訊
* @param success 請求成功的回撥
* @param failure 請求失敗的回撥
*
* @return 返回的物件可取消請求,呼叫cancel方法
*/
+ (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
parameters:(id)parameters
names:(NSArray<NSString *> *)names
filePaths:(NSArray<NSString *> *)filePaths
progress:(HttpProgress)progress
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure;
複製程式碼
檔案上傳具體的方法實現如下:
+ (NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
parameters:(id)parameters
names:(NSArray<NSString *> *)names
filePaths:(NSArray<NSString *> *)filePaths
progress:(HttpProgress)progress
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure {
NSString *AllReplaceURL = [self replaceURL:URL];
NSURLSessionTask *sessionTask = [_sessionManager POST:AllReplaceURL parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSError *error = nil;
for (NSUInteger i = 0; i < filePaths.count; i++) {
NSString *name = names[i];
NSString *filePath = filePaths[i];
[formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:name error:&error];
}
(failure && error) ? failure(error) : nil;
} progress:^(NSProgress * _Nonnull uploadProgress) {
//上傳進度
dispatch_sync(dispatch_get_main_queue(), ^{
progress ? progress(uploadProgress) : nil;
});
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (_isOpenLog) {NSLog(@"responseObject = %@",[self jsonToString:responseObject]);}
[[self allSessionTask] removeObject:task];
success ? success(responseObject) : nil;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (_isOpenLog) {NSLog(@"error = %@",error);}
[[self allSessionTask] removeObject:task];
failure ? failure(error) : nil;
}];
sessionTask ? [[self allSessionTask] addObject:sessionTask] : nil ;
return sessionTask;
}
複製程式碼
10. 實現單/多張圖片上傳
宣告單/多張圖片上傳方法:
/**
*
* @param URL 請求地址
* @param parameters 請求引數
* @param name 圖片對應伺服器上的欄位
* @param images 圖片陣列
* @param fileNames 圖片檔名陣列, 可以為nil, 陣列內的檔名預設為當前日期時間"yyyyMMddHHmmss"
* @param imageScale 圖片檔案壓縮比 範圍 (0.f ~ 1.f)
* @param imageType 圖片檔案的型別,例:png、jpg(預設型別)....
* @param progress 上傳進度資訊
* @param success 請求成功的回撥
* @param failure 請求失敗的回撥
*
* @return 返回的物件可取消請求,呼叫cancel方法
*/
+ (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
images:(NSArray<UIImage *> *)images
fileNames:(NSArray<NSString *> *)fileNames
imageScale:(CGFloat)imageScale
imageType:(NSString *)imageType
progress:(HttpProgress)progress
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure;
複製程式碼
圖片經過等比壓縮後得到的二進位制檔案,預設圖片的檔名, 若fileNames為nil就使用,單/多張圖片上傳具體的方法實現如下:
+ (NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
parameters:(id)parameters
name:(NSString *)name
images:(NSArray<UIImage *> *)images
fileNames:(NSArray<NSString *> *)fileNames
imageScale:(CGFloat)imageScale
imageType:(NSString *)imageType
progress:(HttpProgress)progress
success:(HttpRequestSuccess)success
failure:(HttpRequestFailed)failure {
NSString *AllReplaceURL = [self replaceURL:URL];
NSURLSessionTask *sessionTask = [_sessionManager POST:AllReplaceURL parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
for (NSUInteger i = 0; i < images.count; i++) {
NSData *imageData = UIImageJPEGRepresentation(images[i], imageScale ?: 1.f);
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *imageFileName = NSStringFormat(@"%@%ld.%@",str,i,imageType?:@"jpg");
[formData appendPartWithFileData:imageData
name:name
fileName:fileNames ? NSStringFormat(@"%@.%@",fileNames[i],imageType?:@"jpg") : imageFileName
mimeType:NSStringFormat(@"image/%@",imageType ?: @"jpg")];
}
} progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_sync(dispatch_get_main_queue(), ^{
progress ? progress(uploadProgress) : nil;
});
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (_isOpenLog) {NSLog(@"responseObject = %@",[self jsonToString:responseObject]);}
[[self allSessionTask] removeObject:task];
success ? success(responseObject) : nil;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (_isOpenLog) {NSLog(@"error = %@",error);}
[[self allSessionTask] removeObject:task];
failure ? failure(error) : nil;
}];
sessionTask ? [[self allSessionTask] addObject:sessionTask] : nil ;
return sessionTask;
}
複製程式碼
11. 實現檔案下載
宣告檔案下載方法:
/**
*
* @param URL 請求地址
* @param fileDir 檔案儲存目錄(預設儲存目錄為Download)
* @param progress 檔案下載的進度資訊
* @param success 下載成功的回撥(回撥引數filePath:檔案的路徑)
* @param failure 下載失敗的回撥
*
* @return 返回NSURLSessionDownloadTask例項,可用於暫停繼續,暫停呼叫suspend方法,開始下載呼叫resume方法
*/
+ (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
fileDir:(NSString *)fileDir
progress:(HttpProgress)progress
success:(void(^)(NSString *filePath))success
failure:(HttpRequestFailed)failure;
複製程式碼
在下載過程中可以獲取到下載進度,下載流程為:快取目錄拼接完成,開啟檔案管理器,建立Download目錄,拼接檔案路徑,返回檔案位置的URL路徑。檔案下載具體的方法實現如下:
+ (NSURLSessionTask *)downloadWithURL:(NSString *)URL
fileDir:(NSString *)fileDir
progress:(HttpProgress)progress
success:(void(^)(NSString *))success
failure:(HttpRequestFailed)failure {
NSString *AllReplaceURL = [self replaceURL:URL];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:AllReplaceURL]];
__block NSURLSessionDownloadTask *downloadTask = [_sessionManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
dispatch_sync(dispatch_get_main_queue(), ^{
progress ? progress(downloadProgress) : nil;
});
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSString *downloadDir = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:fileDir ? fileDir : @"Download"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createDirectoryAtPath:downloadDir withIntermediateDirectories:YES attributes:nil error:nil];
NSString *filePath = [downloadDir stringByAppendingPathComponent:response.suggestedFilename];
return [NSURL fileURLWithPath:filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
[[self allSessionTask] removeObject:downloadTask];
if(failure && error) {failure(error) ; return ;};
success ? success(filePath.absoluteString /** NSURL->NSString*/) : nil;
}];
[downloadTask resume];
downloadTask ? [[self allSessionTask] addObject:downloadTask] : nil ;
return downloadTask;
}
複製程式碼
12. Javascript呼叫瀏覽器方法
現在從 Javascript 裡可以這樣呼叫這個方法:
import { NativeModules } from "react-native";
const NetWorkPlugin = NativeModules.NetWorkPlugin;
NetworkPlugin.post({url:"http://192.168.1.1:8080/ApiSystem/login",params:{name:"15842137500",login_type:"0",password:"000000"},headers:{}},(msg) => {
Alert.alert(JSON.stringify(msg));
},(err) => {
Alert.alert(JSON.stringify(err));
});
複製程式碼
希望可以幫助大家,如有問題可加QQ技術交流群: 668562416
如果哪裡有什麼不對或者不足的地方,還望讀者多多提意見或建議
如需轉載請聯絡我,經過授權方可轉載,謝謝
本篇已同步到個人部落格:FBY展菲
歡迎關注我的公眾號:網羅開發