1. 程式人生 > >iOS資料庫離線快取思路和網路層封裝

iOS資料庫離線快取思路和網路層封裝

關於XLNetworkApi

XLNetworkApi的一些功能和說明:

  • 使用XLNetworkRequest做一些GET、POST、PUT、DELETE請求,與業務邏輯對接部分直接以陣列或者字典的形式返回。
  • 以及網路下載、上傳檔案,以block的形式返回實時的下載、上傳進度,上傳檔案引數通過模型XLFileConfig去存取。
  • 通過繼承於XLDataService來將一些資料處理,模型轉化封裝起來,於業務邏輯對接返回的是對應的模型,減少Controllor處理資料處理邏輯的壓力。

  • 自定義一些回撥的block

    /**
    請求成功block
    */
    typedef void (^requestSuccessBlock)(id
    responseObj); /** 請求失敗block */ typedef void (^requestFailureBlock) (NSError *error); /** 請求響應block */ typedef void (^responseBlock)(id dataObj, NSError *error); /** 監聽進度響應block */ typedef void (^progressBlock)(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
  • XLNetworkRequest.m部分實現

    #import "XLNetworkRequest.h"
    #import "AFNetworking.h"
    @implementation XLNetworkRequest
    + (void)getRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler {
    
      AFHTTPRequestOperationManager *manager = [self getRequstManager];
    
      [manager GET:url parameters:params success:^(AFHTTPRequestOperation * _Nonnull operation, id
    _Nonnull responseObject) { successHandler(responseObject); } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) { XLLog(@"------請求失敗-------%@",error); failureHandler(error); }]; }
  • 下載部分程式碼

    //下載檔案,監聽下載進度
    + (void)downloadRequest:(NSString *)url successAndProgress:(progressBlock)progressHandler complete:(responseBlock)completionHandler {
    
      NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
      AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfiguration];
    
      NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
      NSProgress *kProgress = nil;
    
      NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:&kProgress destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
    
          NSURL *documentUrl = [[NSFileManager defaultManager] URLForDirectory :NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    
          return [documentUrl URLByAppendingPathComponent:[response suggestedFilename]];
    
      } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error){
          if (error) {
              XLLog(@"------下載失敗-------%@",error);
          }
          completionHandler(response, error);
      }];
    
      [manager setDownloadTaskDidWriteDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDownloadTask * _Nonnull downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
    
          progressHandler(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    
      }];
      [downloadTask resume];
    }
  • 上傳部分程式碼

    //上傳檔案,監聽上傳進度
    + (void)updateRequest:(NSString *)url params:(NSDictionary *)params fileConfig:(XLFileConfig *)fileConfig successAndProgress:(progressBlock)progressHandler complete:(responseBlock)completionHandler {
    
      NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:url parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
    
          [formData appendPartWithFileData:fileConfig.fileData name:fileConfig.name fileName:fileConfig.fileName mimeType:fileConfig.mimeType];
    
      } error:nil];
    
      //獲取上傳進度
      AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    
      [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
    
          progressHandler(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    
      }];
    
      [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
          completionHandler(responseObject, nil);
      } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
    
          completionHandler(nil, error);
          if (error) {
              XLLog(@"------上傳失敗-------%@",error);
          }
      }];
    
      [operation start];
    }
  • XLDataService.m部分實現

    + (void)getWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseBlock)responseDataBlock {
          [XLNetworkRequest getRequest:url params:param success:^(id responseObj) {
          //陣列、字典轉化為模型陣列
    
          dataObj = [self modelTransformationWithResponseObj:responseObj modelClass:modelClass];
          responseDataBlock(dataObj, nil);
    
      } failure:^(NSError *error) {
          responseDataBlock(nil, error);
      }];
    }
  • (關鍵)下面這個方法提供給繼承XLDataService的子類重寫,將轉化為模型的程式碼寫在這裡,相似業務的網路資料請求都可以用這個子類去請求資料,直接返回對應的模型陣列。
    /**
    陣列、字典轉化為模型
    */
    + (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass {
         return nil;
    }
    關於離線資料快取
    當用戶進入程式的展示頁面,有三個情況下可能涉及到資料庫存取操作,簡單畫了個圖來理解,思路比較簡單,主要是一些存取的細節處理。
  • 進入展示頁面


    進入頁面.png
  • 下拉重新整理最新資料


    下拉重新整理.png
  • 上拉載入更多資料


    上拉載入更多.png
  • 需要注意的是,上拉載入更多的時候,每次從資料庫返回一定數量的資料,而不是一次性將資料全部載入,否則會有記憶體問題,直到資料庫中沒有更多資料時再發生網路請求,再次將新資料存入資料庫。這裡儲存資料的方式是將伺服器返回每組資料的字典歸檔成二進位制作為資料庫欄位直接儲存,這樣儲存在模型屬性比較多的情況下更有優勢,避免每一個屬性作為一個欄位,另外增加了一個idStr欄位用來判斷資料的唯一性,避免重複儲存。
    首先定義一個工具類XLDataBase來做資料庫相關的操作,這裡用的是第三方的FMDB。
#import "XLDataBase.h"
#import "FMDatabase.h"
#import "Item.h"
#import "MJExtension.h"

@implementation XLDataBase

static FMDatabase *_db;

+ (void)initialize {

    NSString *path = [NSString stringWithFormat:@"%@/Library/Caches/Data.db",NSHomeDirectory()];
    _db = [FMDatabase databaseWithPath:path];
    [_db open];
    [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_item (id integer PRIMARY KEY, itemDict blob NOT NULL, idStr text NOT NULL)"];
}

//存入資料庫
+ (void)saveItemDict:(NSDictionary *)itemDict {
    //此處把字典歸檔成二進位制資料直接存入資料庫,避免新增過多的資料庫欄位
    NSData *dictData = [NSKeyedArchiver archivedDataWithRootObject:itemDict];

    [_db executeUpdateWithFormat:@"INSERT INTO t_item (itemDict, idStr) VALUES (%@, %@)",dictData, itemDict[@"id"]];
}

//返回全部資料
+ (NSArray *)list {

    FMResultSet *set = [_db executeQuery:@"SELECT * FROM t_item"];
    NSMutableArray *list = [NSMutableArray array];

    while (set.next) {
        // 獲得當前所指向的資料

        NSData *dictData = [set objectForColumnName:@"itemDict"];
        NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:dictData];
        [list addObject:[Item mj_objectWithKeyValues:dict]];
    }
    return list;
}

//取出某個範圍內的資料
+ (NSArray *)listWithRange:(NSRange)range {

    NSString *SQL = [NSString stringWithFormat:@"SELECT * FROM t_item LIMIT %lu, %lu",range.location, range.length];
    FMResultSet *set = [_db executeQuery:SQL];
    NSMutableArray *list = [NSMutableArray array];

    while (set.next) {
        NSData *dictData = [set objectForColumnName:@"itemDict"];
        NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:dictData];
        [list addObject:[Item mj_objectWithKeyValues:dict]];
    }
    return list;
}

//通過一組資料的唯一標識判斷資料是否存在
+ (BOOL)isExistWithId:(NSString *)idStr
{
    BOOL isExist = NO;

    FMResultSet *resultSet= [_db executeQuery:@"SELECT * FROM t_item where idStr = ?",idStr];
    while ([resultSet next]) {
        if([resultSet stringForColumn:@"idStr"]) {
            isExist = YES;
        }else{
            isExist = NO;
        }
    }
    return isExist;
}
@end
  • 一些繼承於XLDataService的子類的資料庫儲存和模型轉換的邏輯程式碼
#import "GetTableViewData.h"
#import "XLDataBase.h"

@implementation GetTableViewData

//重寫父類方法
+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass {
    NSArray *lists = responseObj[@"data"][@"list"];
    NSMutableArray *array = [NSMutableArray array];
    for (NSDictionary *dict in lists) {
        [modelClass mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
            return @{ @"ID" : @"id" };
        }];
        [array addObject:[modelClass mj_objectWithKeyValues:dict]];

        //通過idStr先判斷資料是否儲存過,如果沒有,網路請求新資料存入資料庫
        if (![XLDataBase isExistWithId:dict[@"id"]]) {
            //存資料庫
            NSLog(@"存入資料庫");
            [XLDataBase saveItemDict:dict];
        }
    }
    return array;
}
  • 下面是一些控制器的程式碼實現:
#import "ViewController.h"
#import "GetTableViewData.h"
#import "Item.h"
#import "XLDataBase.h"
#import "ItemCell.h"
#import "MJRefresh.h"
#define URL_TABLEVIEW @"https://api.108tian.com/mobile/v3/EventList?cityId=1&step=10&theme=0&page=%lu"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
{
    NSMutableArray *_dataArray;
    UITableView *_tableView;
    NSInteger _currentPage;//當前資料對應的page
}
@end

@implementation ViewController
#pragma mark Life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self createTableView];
    _dataArray = [NSMutableArray array];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSRange range = NSMakeRange(0, 10);
    //如果資料庫有資料則讀取,不傳送網路請求
    if ([[XLDataBase listWithRange:range] count]) {
        [_dataArray addObjectsFromArray:[XLDataBase listWithRange:range]];
        NSLog(@"從資料庫載入");
    }else{
        [self getTableViewDataWithPage:0];
    }
}

#pragma mark UI
- (void)createTableView {
    _tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.rowHeight = 100.0;
    [self.view addSubview:_tableView];

    _tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
        [self loadNewData];
    }];
    _tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
        [self loadMoreData];
    }];
}

#pragma mark GetDataSoure
- (void)getTableViewDataWithPage:(NSInteger)page {
    NSLog(@"傳送網路請求!");
    NSString *url = [NSString stringWithFormat:URL_TABLEVIEW, page];
    [GetTableViewData getWithUrl:url param:nil modelClass:[Item class] responseBlock:^(id dataObj, NSError *error) {
        [_dataArray addObjectsFromArray:dataObj];
        [_tableView reloadData];
        [_tableView.mj_header endRefreshing];
        [_tableView.mj_footer endRefreshing];
    }];
}

- (void)loadNewData {
    NSLog(@"下拉重新整理");
    _currentPage = 0;
    [_dataArray removeAllObjects];
    [self getTableViewDataWithPage:_currentPage];
}

- (void)loadMoreData {
    NSLog(@"上拉載入");
    _currentPage ++;
    NSRange range = NSMakeRange(_currentPage * 10, 10);
    if ([[XLDataBase listWithRange:range] count]) {
        [_dataArray addObjectsFromArray:[XLDataBase listWithRange:range]];
        [_tableView reloadData];
        [_tableView.mj_footer endRefreshing];
        NSLog(@"資料庫載入%lu條更多資料",[[XLDataBase listWithRange:range] count]);
    }else{
        //資料庫沒更多資料時再網路請求
        [self getTableViewDataWithPage:_currentPage];
    }
}

#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ItemCell *cell = [ItemCell itemCellWithTableView:tableView];
    cell.item = _dataArray[indexPath.row];
    return cell;
}
@end

最後附上程式碼的下載地址,重要的部分程式碼中都有相應的註釋和文字列印,執行程式可以很直觀的表現。

相關推薦

iOS資料庫離線快取思路網路封裝——原理

資料展示型的頁面做離線快取可以有更好的使用者體驗,使用者在離線環境下,或是網路載入異常的情況下,仍然可以獲取一些已經快取的資料進行頁面的顯示,以避免頁面空白。 進行資料快取操作,首選SQLite+FM

iOS資料庫離線快取思路網路封裝

關於XLNetworkApi XLNetworkApi的一些功能和說明: 使用XLNetworkRequest做一些GET、POST、PUT、DELETE請求,與業務邏輯對接部分直接以陣列或者字典的形式返回。以及網路下載、上傳檔案,以block的形式返回實時的下載、上傳進度,上傳檔案引數通過模型XLFi

資料庫離線快取展示資料專項練習(一)

資料庫離線快取展示資料專項練習 View view = inflater.inflate(R.layout.fragment2, container, false); listView = view.findViewById(R.id.listview); MyHelper helper

計算機網路:傳輸網路

傳輸層介紹 傳輸層是客戶程式和伺服器程式之間的聯絡人,是一個程序之間的連線。 傳輸層服務 首先了解一下傳輸層所提供的服務。 程序到程序通訊 TCP/IP協議簇的5層模型: 其中,網路層負責計算機層次的通訊(主機到主機通訊),網路層協議只把

傳輸 網路的區別

在協議棧中,傳輸層位於網路層之上,傳輸層協議為不同主機上執行的程序提供邏輯通訊,而網路層協議為不同主機提供邏輯通訊。這個區別很微妙,但是卻非常重要。讓我們用一家人作為類比來說明一下這個區別。   設想一下有兩所房子,一個位於東海岸而另一個位於西海岸,每所房子裡

計算機網路:傳輸網路(3)

傳輸層介紹 傳輸層是客戶程式和伺服器程式之間的聯絡人,是一個程序之間的連線。 傳輸層服務 首先了解一下傳輸層所提供的服務。 程序到程序通訊 TCP/IP協議簇的5層模型: 其中,網路層負責計算機層次的通訊(主機到主機通訊),網路層協議只把報文傳遞到目的

資料包分析——資料鏈路 網路

        在資料鏈路層提供的兩個相鄰端點之間的資料幀的傳送功能上,進一步管理網路中的資料通訊,將資料設法從源端經過若干個中間節點傳送到目的端,從而向運輸層提供最基本的端到端的資料傳送服務。 主要內容有: 1、虛電路分組交換和資料報分組交換 2、路由選擇演算法 3、阻塞控制方法 4、X.25協議 5、

iOS數據庫離線緩存思路網絡封裝

integer 離線 super mat mode dict 數據緩存 req center 一直想總結一下關於iOS的離線數據緩存的方面的問題,然後近期也簡單的對AFN進行了再次封裝。全部想把這兩個結合起來寫一下。數據展示型的頁面做離線緩存能夠有更好的用戶體驗

iOS網路詳解優化

HTTPHTTP方法HTTP屬於應用層。具有以下方法:GET 最常見HEAD 伺服器只返回頭部。比如可用於瞭解資源情況,看看某個物件是否存在,測試資源是否被修改了。PUT 向伺服器寫入文件POST 寫伺服器提交資料,通常是表單TRACE 允許客戶端在最終將請求傳送給伺服器時,

iOS開發swift版非同步載入網路圖片(帶快取預設圖片)

iOS開發之swift版非同步載入網路圖片     與SDWebImage非同步載入網路圖片的功能相似,只是程式碼比較簡單,功能沒有SD的完善與強大,支援預設新增圖片,支援本地快取。      非同步載入圖片的核心程式碼如下:  func setZYHWebImage(ur

iOS 使用QLPreviewController預覽本地網路檔案

最近在專案中要做一個文件預覽的功能,做的時候用到了iOS原生的QLPreviewController類,在此做個記錄分享 首先引入標頭檔案 #import <QuickLook/QuickLook.h> 遵循代理 QLPreviewControllerDa

神經網路中隱節點數問題的討論

神經網路中隱層數和隱層節點數問題的討論 一 隱層數         一般認為,增加隱層數可以降低網路誤差(也有文獻認為不一定能有效降低),提高精度,但也使網路複雜化,從而增加了網路的訓練時間和出現“過擬合”的傾向。一般來講應設

tensorflow之神經網路:AveragePooling1D、average_pooling1d、Conv1Dconv1d

1.tf.layers.AveragePooling1D Class AveragePooling1D:對一維的輸入作平均池化 引數: pool_size:一個整數或者一個單個整數的tuple/list,表示池化視窗的大小 Strides:一個整數或者一個單個整數的t

iOS應用架構談 網路設計方案

前言 網路層在一個App中也是一個不可缺少的部分,工程師們在網路層能夠發揮的空間也比較大。另外,蘋果對網路請求部分已經做了很好的封裝,業界的AFNetworking也被廣泛使用。其它的ASIHttpRequest,MKNetworkKit啥的其實也都還不錯,但前者已經棄

iOS webView新增網路css網路html

建立webVIew - (UIWebView *)webView { if (!_ webView) { _ webView = [UIWebView new];

PullToRefersh、網路獲取資料新增到adapter資料庫 然後顯示、無網路時從資料庫獲取資料新增到另一個adapter 然後顯示

package liyuanqi.bwie.com.pulltorrfresh; import android.content.Context; import android.net.ConnectivityManager; import android.net

強強聯合!螞蟻金服與新炬網路戰略合作,共同致力於國產資料庫的技術推廣生態建設

小螞蟻說: 螞蟻金服攜手新炬網路將從資料庫專案實施 、升級遷移和整體運維服務

iOS 網路架構設計分享

前些天幫公司做了網路層的重構,當時就想做好了就分享給大家,後來接著做了新版本的需求,現在才有時間整理一下。 之前的網路層使用的是直接拖拽匯入專案的方式匯入了AF,然後還修改了大量的原始碼,時隔2年,AF已經更新換代很多次了,導致整個重構遷移非常的麻煩。不過看著前輩寫的程式碼,肯

iOS應用架構談(3):網路設計方案

前言 網路層在一個App中也是一個不可缺少的部分,工程師們在網路層能夠發揮的空間也比較大。另外,蘋果對網路請求部分已經做了很好的封裝,業界的AFNetworking也被廣泛使用。其它的ASIHttpRequest,MKNetworkKit啥的其實也都還不錯,但前者已經棄坑,後者

TCP/IP FAQ (1):鏈路網路傳輸

   TCP/IP FAQ系列,以經典的4.4BSD-Lite實現為準,參考《TCP/IP協議詳解》3卷 ,加入個人的思考理解,理清主幹,不深究細枝末節,皆在總結基本原理和實現。本篇涵蓋了資料鏈路層、ARP、RARP、IP、ICMP、TCP、UDP方面的問題與解答。 【Data Link】