題記 —— 執劍天涯,從你的點滴積累開始,所及之處,必精益求精,便是折騰每一天。java
重要消息android
本文章將講述 1.使用dio發送基本的get請求 2.使用dio發送get請求的傳參方式 3.解析響應json數據 4.使用dio發送post請求並提交FormData參數 5.使用dio發送post請求並提交json參數 6.使用dio上傳文件並實現進度監聽 7.使用dio下載文件並實現進度監聽 8.配製dio的攔截器 9.配製dio網絡代理抓包 10.配製dio請求header以及公共請求參數 11.dio 取消網絡請求ios
dio用來在flutter跨平臺開發中訪問網絡的框架,在使用的時候,咱們首先是引入依賴json
dio: 3.0.9
複製代碼
//get請求無參數
void getRequestFunction1() async {
///建立Dio對象
Dio dio = new Dio();
///請求地址 獲取用戶列表
String url = "http://192.168.0.102:8080/getUserList";
///發起get請求
Response response = await dio.get(url);
///響應數據
var data = response.data;
setState(() {
result = data.toString();
});
}
複製代碼
數據響應結果xcode
{
"code": 200,
"data": [
{
"id": 3,
"userName": "測試人員",
"realName": "張三",
"age": 22
}
],
"message": "請求成功"
}
複製代碼
斷點調試以下 服務器
///get請求有參數
///根據用戶ID來獲取用戶信息
void getRequestFunction2() async {
///用戶id
int userId =3;
///建立 dio
Dio dio = new Dio();
///請求地址
///傳參方式1
String url = "http://192.168.0.102:8080/getUser/$userId";
///傳參方式2
String url2 = "http://192.168.0.102:8080/getUser?userId=$userId";
///傳參方式 3
String url3 = "http://192.168.0.102:8080/getUser";
Map<String,dynamic> map = Map();
map["userId"]= userId;
///發起get請求
Response response = await dio.get(url3,queryParameters: map);
///響應數據
Map<String,dynamic> data = response.data;
/// 將響應數據解析爲 UserBean
UserBean userBean = UserBean.fromJson(data);
}
}
複製代碼
在上述代碼中,傳參方式1與傳參方式2是在請求連接中拼接參數,請求方式3是將參數放在一個 map 中,而後經過 Dio 的queryParameters 來配製參數,上述返回的數據結構爲網絡
{
"code": 200,
"data": {
"id": 3,
"userName": "測試人員",
"realName": "張三",
"age": 22
},
"message": "請求成功"
}
複製代碼
斷點調試 數據結構
對於這裏使用到的數據模型 UserBean 對象來講class UserBean{
String userName;
String realName;
int age;
int id;
static UserBean fromJson(Map<String,dynamic> rootData){
///解析第一層
Map<String,dynamic> data = rootData["data"];
///解析第二層
UserBean userBean = new UserBean();
userBean.id = data["id"];
userBean.age = data["age"];
userBean.userName= data["userName"];
userBean.realName = data["realName"];
return userBean;
}
}
複製代碼
對於 UserBean 中的數據解析以下圖所示app
FormData 將提交的參數 name與value進行組合,實現表單數據的序列化,從而減小表單元素的拼接 也能夠這樣來描述:FormData 接口提供了一種表示表單數據的鍵值對的構造方式,經過FormData發出的請求編碼類型被設爲 "multipart/form-data",而在網絡請求訪問中,經過 Content-Type 來記錄這個值,能夠理解爲Content-Type 表示具體請求中的媒體類型信息。框架
而咱們在實際開發中經常使用的 Content-Type以下
multipart/form-data
application/json JSON數據格式
application/x-www-form-urlencoded 表單數據格式
複製代碼
下面咱們將使用 dio 來發起一個post請求,提交參數的格式爲 FromData
void postRequestFunction() async {
///建立Dio
Dio dio = new Dio();
///發送 FormData:
FormData formData = FormData.fromMap({"name": "張三", "age": 22});
String url ="http://192.168.200.68:8080/registerUser";
///發起 post 請求 如這裏的註冊用戶信息
Response response = await dio
.post(url, data: formData);
result = response.data.toString();
setState(() {});
}
複製代碼
抓包所得以下
咱們也能夠看到參數的格式 在這裏咱們能夠看到 Content-type 是 text/plain 而並非咱們上面所說的 multipart/form-data ,這是由於在經過Dio 的 FormData 封裝參數時,會進行一步默認的設置以下圖所示下面咱們使用 dio 發起一個post請求,提交json格式的參數
///post請求發送json
void postRequestFunction2() async{
String url = "http://192.168.0.102:8080/registerUser2";
///建立Dio
Dio dio = new Dio();
///建立Map 封裝參數
Map<String,dynamic> map = Map();
map['userName']="小明";
map['userAge']=44;
///發起post請求
Response response = await dio.post(url,data: map);
var data = response.data;
}
複製代碼
抓包所得以下
從上圖中,咱們能夠看到 Content-Type 標識了傳參方式是以 json 格式來發送的,下圖中咱們能夠看到具體的參數///手機中的圖片
String localImagePath ="/storage/emulated/0/Download/17306285.jpg";
///上傳的服務器地址
String netUploadUrl = "http://192.168.0.102:8080/fileupload";
///dio 實現文件上傳
void fileUplod() async{
///建立Dio
Dio dio = new Dio();
Map<String ,dynamic> map = Map();
map["auth"]="12345";
map["file"] = await MultipartFile.fromFile(localImagePath,filename: "xxx23.png");
///經過FormData
FormData formData = FormData.fromMap(map);
///發送post
Response response = await dio.post(netUploadUrl, data: formData,
///這裏是發送請求回調函數
///[progress] 當前的進度
///[total] 總進度
onSendProgress: (int progress, int total) {
print("當前進度是 $progress 總進度是 $total");
},);
///服務器響應結果
var data = response.data;
}
複製代碼
經過斷點調試
這裏的上傳圖片請求接口返回了圖片的保存路徑,咱們打開本地服務器的目錄///當前進度進度百分比 當前進度/總進度 從0-1
double currentProgress =0.0;
///下載文件的網絡路徑
String apkUrl ="";
///使用dio 下載文件
void downApkFunction() async{
/// 申請寫文件權限
bool isPermiss = await checkPermissFunction();
if(isPermiss) {
///手機儲存目錄
String savePath = await getPhoneLocalPath();
String appName = "rk.apk";
///建立DIO
Dio dio = new Dio();
///參數一 文件的網絡儲存URL
///參數二 下載的本地目錄文件
///參數三 下載監聽
Response response = await dio.download(
apkUrl, "$savePath$appName", onReceiveProgress: (received, total) {
if (total != -1) {
///當前下載的百分比例
print((received / total * 100).toStringAsFixed(0) + "%");
// CircularProgressIndicator(value: currentProgress,) 進度 0-1
currentProgress = received / total;
setState(() {
});
}
});
}else{
///提示用戶請贊成權限申請
}
}
複製代碼
Android權限目前分爲三種:正常權限、危險權限、特殊權限
正常權限 直接在AndroidManifest中配置便可得到的權限。大部分權限都歸於此。 危險權限,Android 6.0以後將部分權限定義於此。 危險權限不只須要須要在AndroidManifest中配置,還須要在使用前check是否真正擁有權限,以動態申請。
在ios中,使用xcode打開本目錄
選中Xcode 工程中的 info.plist文件,右鍵選擇Open As - Source Code,將權限配置的代碼copy到裏面便可,鍵值對中的內容可按項目需求相應修改。
<!-- 相冊 -->
<key>NSPhotoLibraryUsageDescription</key>
<string>須要您的贊成,APP才能訪問相冊</string>
<!-- 相機 -->
<key>NSCameraUsageDescription</key>
<string>須要您的贊成,APP才能訪問相機</string>
<!-- 麥克風 -->
<key>NSMicrophoneUsageDescription</key>
<string>須要您的贊成,APP才能訪問麥克風</string>
<!-- 位置 -->
<key>NSLocationUsageDescription</key>
<string>須要您的贊成, APP才能訪問位置</string>
<!-- 在使用期間訪問位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>App須要您的贊成, APP才能在使用期間訪問位置</string>
<!-- 始終訪問位置 -->
<key>NSLocationAlwaysUsageDescription</key>
<string>App須要您的贊成, APP才能始終訪問位置</string>
<!-- 日曆 -->
<key>NSCalendarsUsageDescription</key>
<string>App須要您的贊成, APP才能訪問日曆</string>
<!-- 提醒事項 -->
<key>NSRemindersUsageDescription</key>
<string>須要您的贊成, APP才能訪問提醒事項</string>
<!-- 運動與健身 -->
<key>NSMotionUsageDescription</key>
<string>須要您的贊成, APP才能訪問運動與健身</string>
<!-- 健康更新 -->
<key>NSHealthUpdateUsageDescription</key>
<string>須要您的贊成, APP才能訪問健康更新 </string>
<!-- 健康分享 -->
<key>NSHealthShareUsageDescription</key>
<string>須要您的贊成, APP才能訪問健康分享</string>
<!-- 藍牙 -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>須要您的贊成, APP才能訪問藍牙</string>
<!-- 媒體資料庫 -->
<key>NSAppleMusicUsageDescription</key>
<string>須要您的贊成, APP才能訪問媒體資料庫</string>
複製代碼
在 flutter 項目目錄中,咱們也能夠打開 info.plist 文件配置,以下圖所示
在這裏使用的是 permission_handler 插件來申請權限的permission_handler: ^4.3.0
複製代碼
申請權限代碼以下
///PermissionGroup.storage 對應的是
///android 的外部存儲 (External Storage)
///ios 的Documents` or `Downloads`
checkPermissFunction() async {
if (Theme.of(context).platform == TargetPlatform.android) {
///安卓平臺中 checkPermissionStatus方法校驗是否有儲存卡的讀寫權限
PermissionStatus permission = await PermissionHandler() .checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
///無權限那麼 調用方法 requestPermissions 申請權限
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler() .requestPermissions([PermissionGroup.storage]);
///校驗用戶對權限申請的處理
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
複製代碼
申請好權限後,咱們須要肯定下來儲存卡的路徑,在這裏使用的是 path_provider 插件
path_provider: 1.6.0
複製代碼
///獲取手機的存儲目錄路徑
///getExternalStorageDirectory() 獲取的是 android 的外部存儲 (External Storage)
/// getApplicationDocumentsDirectory 獲取的是 ios 的Documents` or `Downloads` 目錄
Future<String> getPhoneLocalPath() async {
final directory = Theme.of(context).platform == TargetPlatform.android
? await getExternalStorageDirectory() : await getApplicationDocumentsDirectory();
return directory.path;
}
複製代碼
_setupPROXY(Dio dio) {
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(HttpClient client) {
client.findProxy = (uri) {
///這裏的 192.168.0.102:8888就是咱們的代理服務地址
return "PROXY 192.168.0.102:8888";
};
client.badCertificateCallback =
(X509Certificate cert, String host, int port) {
return true;
};
};
}
複製代碼
在實際應用開發中,咱們會有像 token、appVersionCode 等等這些每一個接口請求都須要傳的參數 ,稱之爲公共請求參數,那麼在這裏 dio 的請求中咱們能夠考慮這樣來配製
String application = "V 1.2.2";
int appVersionCode = 122;
///[url]網絡請求連接
///[data] post 請求時傳的json數據
///[queryParameters] get請求時傳的參數
void configCommonPar(url,data,Map<String, dynamic> queryParameters){
///配製統一參數
if (data != null) {
data['application'] = application;
data['appVersionCode'] = appVersionCode.toString();
} else if (queryParameters != null) {
queryParameters['application'] = application;
queryParameters['appVersionCode'] = appVersionCode.toString();
} else {
///url中有可能拼接着其餘參數
if (url.contains("?")) {
url += "&application=$application&appVersionCode=$appVersionCode";
} else {
url += "?application=$application&appVersionCode=$appVersionCode";
}
}
}
}
複製代碼
咱們在建立 Dio對象時,會初始化一個 BaseOptions 來建立 Dio
BaseOptions options = BaseOptions();
///請求header的配置
options.headers["appVersionCode"]=406;
options.headers["appVersionName"]="V 4.0.6";
options.contentType="application/json";
options.method="GET";
options.connectTimeout=30000;
///建立 dio
Dio dio = new Dio(options);
複製代碼
咱們也能夠在每次發送 get 、post 等不一樣的請求時,經過 dio 獲取到 默認的 options 而後修改一下
void getRequestFunction2() async {
///用戶id
int userId = 3;
///建立 dio
Dio dio = new Dio();
///請求地址
///傳參方式1
String url = "http://192.168.0.102:8080/getUser/$userId";
///在這裏修改 contentType
dio.options.contentType="application/json";
///請求header的配置
dio.options.headers["appVersionCode"]=406;
dio.options.headers["appVersionName"]="V 4.0.6";
///發起get請求
Response response = await dio.get(url);
...
}
複製代碼
實際開發中,例如咱們退出一個頁面時,若是網絡請求沒完成,就會行成內存泄露,因此須要在頁面消毀時,取消網絡請求,或者是在下載一個文件時,時間太長了,用戶點擊取消,就須要取消網絡鏈接
///建立取消標誌
CancelToken cancelToken = new CancelToken();
void getRequestFunction2() async {
///用戶id
int userId = 3;
///建立 dio
Dio dio = new Dio();
///請求地址
///傳參方式1
String url = "http://192.168.0.102:8080/getUser/$userId";
///發起get請求 並設置 CancelToken 取消標誌
Response response = await dio.get(url,cancelToken: cancelToken);
...
}
複製代碼
那麼當咱們須要手動取消這個網絡請求時,只須要調用以下方法
///取消網絡請求
if(cancelToken!=null&&!cancelToken.isCancelled){
cancelToken.cancel();
cancelToken=null;
}
複製代碼
須要注意的是,一個 cancelToken 只能對就一個網絡請求。
完畢