新浪微博客户端(60)-离线缓存微博数据

Posted 夜行过客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了新浪微博客户端(60)-离线缓存微博数据相关的知识,希望对你有一定的参考价值。

 

很多应用在第一次加载时会读取前一次浏览的历史微博数据,只有当用户手动触发下拉刷新之后,才会去加载新的微博数据。

1.集成FMDB

FMDB是在sqlite3的C语言查询函数基础上封装的一套OC的API,因此在使用之前需要首先导入libsqlite3.tbd.

2. 添加FMDB库

3. 参考代码:

DJStatusDBHelper.h

#import <Foundation/Foundation.h>

@class DJStatus;
@interface DJStatusDBHelper : NSObject

/** 保存单条微博 */
+ (void)saveStatus:(NSDictionary *)statusDictionary;
/** 保存多条微博 */
+ (void)saveMutableStatus:(NSArray *)statusArray;

/** 返回指定个数的微博,默认为20 */
+ (NSArray *)statusesWithFixNums;

/** 返回比maxId小于或等于的微博 */
+ (NSArray *)statusesWithMaxId:(NSString *)max_id;

/** 返回比since_Id大的微博 */
+ (NSArray *)statusesWithSinceId:(NSString *)since_id;

@end

DJStatusDBHelper.m

#import "DJStatusDBHelper.h"
#import "FMDatabase.h"
#import "DJStatus.h"

static FMDatabase *_db;


@implementation DJStatusDBHelper

// 在这个类第一次被实例化的时候创建数据库并打开对应的数据表
+ (void)initialize {

    NSString *db_path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"status.sqlite"];
    
    _db = [FMDatabase databaseWithPath:db_path];
    
    // 1. 打开数据库
    [_db open];
    
    // 2. 创表
    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS t_status(id integer PRIMARY KEY, status blob NOT NULL, idstr text NOT NULL);"];
    [_db executeUpdate:sql];
}


/** 保存单条微博 */
+ (void)saveStatus:(NSDictionary *)statusDictionary {

    // 这里需要注意,如果要将对象保存到SQLite数据库,那么这个对象必须转换为NSData对象,且该对象必须实现NSCoding协议
    NSData *statusData = [NSKeyedArchiver archivedDataWithRootObject:statusDictionary];
    
    [_db executeUpdateWithFormat:@"INSERT INTO t_status(status,idstr) VALUES(%@,%@);",statusData,statusDictionary[@"idstr"]];

}


/** 保存多条微博 */
+ (void)saveMutableStatus:(NSArray *)statusArray {

    for (int i = 0; i < statusArray.count; i++) {
        NSDictionary *statusDict = statusArray[i];
        [self saveStatus:statusDict];
    }
    
}


/** 返回指定个数的微博 */
+ (NSArray *)statusesWithFixNums {
    
    FMResultSet *set = [_db executeQueryWithFormat:@"SELECT * FROM t_status ORDER BY idstr DESC LIMIT 20;"];
    NSMutableArray *statusDictArray = [NSMutableArray array];
    while (set.next) {
        NSData *statusData = [set objectForColumnName:@"status"];
        NSDictionary *statusDict = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];
        [statusDictArray addObject:statusDict];
    }
    return statusDictArray;
}


/** 返回比since_Id大的微博 */
+ (NSArray *)statusesWithSinceId:(NSString *)since_id {
    
    FMResultSet *set = [_db executeQueryWithFormat:@"SELECT * FROM t_status WHERE idstr > %@ ORDER BY idstr DESC LIMIT 20;",since_id];
    NSMutableArray *statusDictArray = [NSMutableArray array];
    while (set.next) {
        NSData *statusData = [set objectForColumnName:@"status"];
        NSDictionary *statusDict = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];
        [statusDictArray addObject:statusDict];
    }
    return statusDictArray;
    
}


/** 返回比maxId小于或等于的微博 */
+ (NSArray *)statusesWithMaxId:(NSString *)max_id {

    FMResultSet *set = [_db executeQueryWithFormat:@"SELECT * FROM t_status WHERE idstr <= %@ ORDER BY idstr DESC LIMIT 20;",max_id];
    NSMutableArray *statusDictArray = [NSMutableArray array];
    while (set.next) {
        NSData *statusData = [set objectForColumnName:@"status"];
        NSDictionary *statusDict = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];
        [statusDictArray addObject:statusDict];
    }
    return statusDictArray;
}

@end

DJHomeViewController.m

- (void)pullToRefresh:(MJRefreshHeader *)header {

    // 判断是否是第一次刷新。根据statusFrame是否有值,如果有,则代表已经加载过数据;若没有,则代表首次加载
    if (self.statusFrames.count) {
        [self loadRefreshStatusesFromNetwork:header];
    } else {
        // 尝试从数据库中加载数据
        [self loadRefreshStatusesFromDatabase:header];
    }
    
}



/** 尝试从数据库中加载数据 */
- (void)loadRefreshStatusesFromDatabase:(MJRefreshHeader *)header {

    DJLog(@"下拉刷新--从数据库");
    
    NSArray *statuses = [DJStatusDBHelper statusesWithFixNums];
    
    if (statuses.count) {
        
        // 1. 将刷新获取到的新数据添加到总数组的头部
        NSArray *newStatuses = [DJStatus mj_objectArrayWithKeyValuesArray:statuses];
        NSArray *newStatusFrames = [self statusFramesWithStatus:newStatuses];
        NSRange range = NSMakeRange(0, newStatusFrames.count);
        NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:range];
        [self.statusFrames insertObjects:newStatusFrames atIndexes:indexSet];
        
        // 2. 刷新TableView
        [self.tableView reloadData];
        
        // 3. 结束刷新
        [header endRefreshing];
        
        // 4. 清除未读微博数量
        [self clearAllBadgeTips];
        
        // 5. 提示当前刷新微博数量
        [self showRefreshStatusesNums:newStatusFrames.count];
        
    } else {
        DJLog(@"下拉刷新--数据库没有,切换到网络");
        
        // 尝试从网络上加载数据
        [self loadRefreshStatusesFromNetwork:header];
    }
    
}


/** 尝试从网络上加载数据*/
- (void)loadRefreshStatusesFromNetwork:(MJRefreshHeader *)header {

    DJLog(@"下拉刷新--从网络");
    
    AFHTTPSessionManager *requestManager = [AFHTTPSessionManager manager];
    
    NSString *urlString = @"https://api.weibo.com/2/statuses/friends_timeline.json";
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"access_token"] = [DJAccountTool account].access_token;
    
    DJStatusCellFrame *statusFrame = [self.statusFrames firstObject];
    if (statusFrame) {
        params[@"since_id"] = statusFrame.status.idstr;
    }
    
    [requestManager GET:urlString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {
        
        //        DJLog(@"%@",responseObject);
        
        // 0. 将刷新获取到的数据先添加到数据库
        [DJStatusDBHelper saveMutableStatus:responseObject[@"statuses"]];
        
        // 1. 将刷新获取到的新数据添加到总数组的头部
        NSArray *newStatuses = [DJStatus mj_objectArrayWithKeyValuesArray:responseObject[@"statuses"]];
        NSArray *newStatusFrames = [self statusFramesWithStatus:newStatuses];
        NSRange range = NSMakeRange(0, newStatusFrames.count);
        NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:range];
        [self.statusFrames insertObjects:newStatusFrames atIndexes:indexSet];
        
        // 2. 刷新TableView
        [self.tableView reloadData];
        
        // 3. 结束刷新
        [header endRefreshing];
        
        // 4. 清除未读微博数量
        [self clearAllBadgeTips];
        
        // 5. 提示当前刷新微博数量
        [self showRefreshStatusesNums:newStatusFrames.count];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [header endRefreshing];
    }];

}




/** 下载加载更多 */
- (void)loadMoreStatuses:(MJRefreshFooter *)footer {
    
    DJStatusCellFrame *statusFrame = [self.statusFrames lastObject];
    if (statusFrame) {
        long long max_id = [statusFrame.status.idstr longLongValue] - 1;
        [self loadMoreStatusesFromDatabaseWithMaxId:[NSString stringWithFormat:@"%lld",max_id] footer:footer];
    } else {
        [self loadMoreStatusesFromNetworkWithMaxId:[NSString stringWithFormat:@"%d",0] footer:footer];
    }
}


/** 载入更多数据从数据库 */
- (void)loadMoreStatusesFromDatabaseWithMaxId:(NSString *)max_id footer:(MJRefreshFooter *)footer{

    DJLog(@"加载更多--从数据库");
    
    NSArray *statuses = [DJStatusDBHelper statusesWithMaxId:max_id];
    if (statuses.count) {
        
        // 1. 将刷新获取到的新数据添加到总数组的尾部
        NSArray *newStatuses = [DJStatus mj_objectArrayWithKeyValuesArray:statuses];
        NSArray *newStatusFrames = [self statusFramesWithStatus:newStatuses];
        [self.statusFrames addObjectsFromArray:newStatusFrames];
        
        // 2. 刷新TableView
        [self.tableView reloadData];
        
        // 3. 结束刷新
        [footer endRefreshing];
        
    } else {
        DJLog(@"加载更多--数据库没有,切换到网络");
        // 尝试从网络获取
        [self loadMoreStatusesFromNetworkWithMaxId:max_id footer:footer];
        
    }

}


/** 载入更多数据从网络 */
- (void)loadMoreStatusesFromNetworkWithMaxId:(NSString *)max_id footer:(MJRefreshFooter *)footer{

    DJLog(@"加载更多--从网络");
    
    AFHTTPSessionManager *requestManager = [AFHTTPSessionManager manager];
    
    NSString *urlString = @"https://api.weibo.com/2/statuses/friends_timeline.json";
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"access_token"] = [DJAccountTool account].access_token;
    DJStatusCellFrame *statusFrame = [self.statusFrames lastObject];
    if (statusFrame) {
        long long max_id = [statusFrame.status.idstr longLongValue] - 1;
        params[@"max_id"] = @(max_id);
    }
    [requestManager GET:urlString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {
        
        // 0.将刷新获取到的数据添加到数据库
        [DJStatusDBHelper saveMutableStatus:responseObject[@"statuses"]];
        
        // 1. 将刷新获取到的新数据添加到总数组的尾部
        NSArray *newStatuses = [DJStatus mj_objectArrayWithKeyValuesArray:responseObject[@"statuses"]];
        NSArray *newStatusFrames = [self statusFramesWithStatus:newStatuses];
        [self.statusFrames addObjectsFromArray:newStatusFrames];
        
        // 2. 刷新TableView
        [self.tableView reloadData];
        
        // 3. 结束刷新
        [footer endRefreshing];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [footer endRefreshing];
    }];
    
}

最终效果:

 

以上是关于新浪微博客户端(60)-离线缓存微博数据的主要内容,如果未能解决你的问题,请参考以下文章

新浪微博客户端(61)-清除图片缓存

关于新浪微博缓存的功能是怎么实现的

如何通过python调用新浪微博的API来爬取数据

基于redis分布式缓存实现(新浪微博案例)

登陆新浪微博

新浪微博怎么用用户名登陆手机版