新浪微博客户端(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];
    }];

}

最终效果:

时间: 2024-10-25 00:23:22

新浪微博客户端(60)-离线缓存微博数据的相关文章

(一一六)新浪微博client的离线缓存实现思路

上一节(一一五)利用NSKeyedArchiver实现随意对象转为二进制介绍了将随意对象转化为二进制数据和还原的方法.可用于实现本节介绍的微博数据离线缓存. 通过新浪官方的API能够发现,返回的微博数据例如以下样式: { "statuses": [ { "created_at": "Tue May 31 17:46:55 +0800 2011", "id": 11488058246, "text": &qu

新浪微博客户端(21)-获取当前微博未读数并提示用户

HomeViewController.m - (void)viewDidLoad { [super viewDidLoad]; // 初始化NavigationItem [self initNavigationItem]; // 初始化TitleView [self initTitleView]; // 更新TitleView 显示名称 [self updateTitleViewDisplayName]; // 集成下拉刷新控件 [self setupPullToRefreshView]; //

新浪微博客户端(25)-添加转发微博

DJStatusCell.m /* 转发微博部分 */ /** 转发微博整体 */ @property (nonatomic,weak) UIView *retweetView; /** 转发微博内容 */ @property (nonatomic,weak) UILabel *retweetContentLabel; /** 转发微博图片 */ @property (nonatomic,weak) UIImageView *retweetPhotoView; #pragma mark - 转发

iOS开发:一个高仿美团的团购ipad客户端的设计和实现(功能:根据拼音进行检索并展示数据,离线缓存团购数据,浏览记录与收藏记录的批量删除等)

大致花了一个月时间,利用各种空闲时间,将这个客户端实现了,在这里主要是想记录下,设计的大体思路以及实现过程中遇到的坑...... 这个项目的github地址:https://github.com/wzpziyi1/GroupPurchase 主要实现的功能,用UICollectionViewController展示团购数据,根据拼音进行检索并展示数据,离线缓存团购数据,浏览记录与收藏记录的批量删除,友盟分享的集成,利用UIView+AutoLayout写布局,实现地图定位.自定义大头针等 整个项

Android新浪微博客户端(七)——ListView中的图片异步加载、缓存

原文出自:方杰|http://fangjie.sinaapp.com/?p=193转载请注明出处 最终效果演示:http://fangjie.sinaapp.com/?page_id=54该项目代码已经放到github:https://github.com/JayFang1993/SinaWeibo 一.ListView的图片异步加载 我们都知道对每一个Weibo Item都有用户头像,而且每一条微博还可能带有图片.如果在加载列表的同时加载图片,这样有几个缺点,第一很费事,界面卡住,用户体验很不

PHP 基于laravel框架获取微博数据之一 模拟新浪微博登录

参考资料:http://www.csuldw.com/2016/11/10/2016-11-10-simulate-sina-login/http://blog.csdn.net/fly_leopard/article/details/51148904http://www.tuicool.com/articles/uIJzYff http://blog.csdn.net/u010029983/article/details/46364113等 模拟新浪微博登录是抓取新浪数据的基础,网上的参考资料

抓取新浪微博数据存入MongoDB,避免重复插入微博数据的方法

def getMyDatalist(): #id这个key key = str(u'id').decode('utf-8') #存储旧数据的id列表 old_ids = [] #存储新微博的列表 extr_wb = [] #从MongoDB上获取的数据 old_datalist = weibodata.find() for old in old_datalist: old_ids.append(old[key]) #从微博上抓取新数据 data = client.statuses.home_ti

新浪微博客户端(22)-创建微博Cell

DJStatusCell.h #import <UIKit/UIKit.h> @class DJStatusCellFrame; @interface DJStatusCell : UITableViewCell /** DJStatusCell 的默认构造方法 */ + (instancetype)cellWithTableView:(UITableView *)tableView; @property (nonatomic,strong) DJStatusCellFrame *status

android开发新浪微博客户端 完整攻略 [新手必读]

开始接触学习android已经有3个礼拜了,一直都是对着android的sdk文档写Tutorials从Hello World到Notepad Tutorial算是初步入门了吧,刚好最近对微博感兴趣就打算开发个android版本的新浪微博客户端作为练手项目,并且以随笔的方式详细的记录开发的全过程.本人对java语言以及eclipse Ide都是初次应用基本上属于边学边用,做移动设备上的东西也是第一次,总的来说属于无基础.无经验.无天赋的纯三无人员,还请广大同学们多多给予指点. 开发第一件事情,那