Android与iOS开发对比系列】之 数据存储

Android与iOS开发对比系列】之 数据存储

写在前面的话

相比Android和iOS,我觉得Android的数据存储更开放一些。Android天生就可以使用多Java I/O;并且天生开放的特性,开发者可以直接在SD卡中读写文件,自由度比较高。缺点吗,也是因为太开放,所以Android的相册和文件夹都惨不忍睹。

Android本身自带Java的反射和注解,很早就有了ORM数据库。这里解释一下,ORM就是 Object Relation Mapping, 对象关系映射。 通过建立对象来生成数据库字段,大大简化了代码。

Android的ORM我用过 GreenDaoActiveAndroid , GreenDao需要写Java项目来生成,而且lib包也比较多。后来我用ActiveAndroid,类似于Ruby的ORM存储方式,用起来比较顺手。

IOS有Core Data, 但是用起来比较复杂。我一开始是直接使用 FMDB ,因为是SQLite的封装,还需要写SQLite代码,写起来还是比较麻烦。 当然,现在有了 MagicalRecord ,类似ORM数据库的Core Data封装, 能简化不少代码。


Android的4种数据存储方式

1 使用SharedPreferences存储数据;

SharedPreferences是用来存储一些Key/Value类似的成对的基本数据类型。

它只能存储基本数据类型,也即int, long, boolean, String, float。

IOS相对的就是NSUserDefaults;

下面是示例代码:

void WriteSharedPreferences(String strName,String strPassword){
    SharedPreferences user = getSharedPreferences(“user_info”,Context.MODE_PRIVATE);
    user.edit();
    user.putString(“NAME”, strName);
    user.commit();
}

void ReadSharedPreferences(){
    String strName,strPassword;
    SharedPreferences   user = getSharedPreferences(“user_info”,Context.MODE_PRIVATE);
    strName = user.getString(“NAME”,””);
}

SharedPreferences是采用了XML格式将数据存储到设备中,在DDMS中的File Explorer中的/data/data//shares_prefs下.

SharedPreferences同样是沙盒机制:只能在同一个包内使用,不能在不同的包之间使用。

<?xml version=”1.0″ encoding=”UTF-8″?>
<map>
<string name=”NAME”>XXXX</string>
</map>

2 文件存储数据;

Internal Storage内部存储空间

这里是指手机内置的存储空间,称为内部存储。使用内部存储主要有二个方式,一个是文件操作,一个是文件夹操作.

Context提供了两个方法来打开数据文件里的文件IO流

FileInputStream openFileInput(String name);
FileOutputStream(String name , int mode).

Context还提供了如下几个重要的方法:

getDir(String name , int mode); //在应用程序的数据文件夹下获取或者创建name对应的子目录
File getFilesDir();  //获取该应用程序的数据文件夹得绝对路径
String[] fileList(); //返回该应用数据文件夹的全部文件   

Context.openFileOutput(String fileName, int mode)生成的文件自动存储在/data/data/Package Name/files目录下,其全路径是/data/data/Package Name/files/fileName

注意下,这里的参数fileName不可以包含路径分割符(如”/”).

内部存储空间应该用来保存比较重要的数据,apk被卸载时,apk在内部存储空间的文件数据将被删除。

示例代码:

FileOutputStream  output = Context.openOutputFile(filename, Context.MODE_PRIVATE);
output.write(data);// use output to write whatever you like
output.close();
FileInputStream input = Context.openInputFile(filename);
input.read();
input.close();
public String read() {
        try {
            FileInputStream inStream = this.openFileInput("message.txt");
            byte[] buffer = new byte[1024];
            int hasRead = 0;
            StringBuilder sb = new StringBuilder();
            while ((hasRead = inStream.read(buffer)) != -1) {
                sb.append(new String(buffer, 0, hasRead));
            }

            inStream.close();
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void write(String msg){
        // 步骤1:获取输入值
        if(msg == null) return;
        try {
            // 步骤2:创建一个FileOutputStream对象,MODE_APPEND追加模式
            FileOutputStream fos = openFileOutput("message.txt",
                    MODE_APPEND);
            // 步骤3:将获取过来的值放入文件
            fos.write(msg.getBytes());
            // 步骤4:关闭数据流
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

External Storage外部存储空间

就是读写sdcard上的文件

其中读写步骤按如下进行:

1 调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,且应用程序具有读写SD卡的权限,如下代码将返回true

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

2 调用Environment.getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录,或者使用”/mnt/sdcard/”目录

3 使用IO流操作SD卡上的文件

需要权限:

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

示例代码:

// 文件写操作函数
    private void write(String content) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定义File类对象
            if (!file.getParentFile().exists()) { // 父文件夹不存在
                file.getParentFile().mkdirs(); // 创建文件夹
            }
            PrintStream out = null; // 打印流对象用于输出
            try {
                out = new PrintStream(new FileOutputStream(file, true)); // 追加文件
                out.println(content);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close(); // 关闭打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用户
            Toast.makeText(this, "保存失败,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
    }

    // 文件读操作函数
    private String read() {

        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定义File类对象
            if (!file.getParentFile().exists()) { // 父文件夹不存在
                file.getParentFile().mkdirs(); // 创建文件夹
            }
            Scanner scan = null; // 扫描输入
            StringBuilder sb = new StringBuilder();
            try {
                scan = new Scanner(new FileInputStream(file)); // 实例化Scanner
                while (scan.hasNext()) { // 循环读取
                    sb.append(scan.next() + "\n"); // 设置文本
                }
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (scan != null) {
                    scan.close(); // 关闭打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用户
            Toast.makeText(this, "读取失败,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
        return null;
    }

3 SQLite数据库存储数据;

这里不详细说了,SQLite3都是通用的。可以参考学习:w3school, 我iOS写SQLite关系表把这个又学了一遍。

简单代码如下:

 db.executeSQL(String sql);
 db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集
 db.insert(String table, String nullColumnHack, ContentValues values);
 db.update(String table, Contentvalues values, String whereClause, String whereArgs);
 db.delete(String table, String whereClause, String whereArgs);

4 使用ContentProvider存储数据;

Android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等),可以向其他应用共享其数据。

我以前使用过ContentProvider和SQLiteDatabase CursorLoader相结合的方式,github地址

这里代码就不详细介绍了。


补充

Preference,File, DataBase这三种方式分别对应的目录是

/data/data/Package Name/Shared_Pref,

/data/data/Package Name/files,

/data/data/Package Name/database

iOS的5种数据存储方式

iOS都是沙盒存储,数据都在app的目录下。

1 NSUserDefaults

与Android的SharedPreferences原理相同。

NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary

保存数据:

NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];

NSString *name [email protected]”default string“;
[defaults setObject:firstName forKey:@"name"];

UIImage *image=[[UIImage alloc]initWithContentsOfFile:@"photo.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 100);//UIImage对象转换成NSData
[defaults setObject:imageData forKey:@"image"];

[defaults synchronize];//用synchronize方法把数据持久化到standardUserDefaults数据库

读取数据:

NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];

NSString *name = [defaults objectForKey:@"name"];//根据键值取出name

NSData *imageData = [defaults dataForKey:@"image"];
UIImage *Image = [UIImage imageWithData:imageData];//NSData转换为UIImage

2 NSKeyedArchiver归档

采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议。

对象对应的类必须提供encodeWithCoder:和initWithCoder:方法。对对象进行编码和解码。

下面的例子是从这篇博客中看到的:http://blog.csdn.net/dqjyong/article/details/7669252

例如对Possession对象归档保存。

定义Possession:
@interface Possession:NSObject<NSCoding>{//遵守NSCoding协议
       NSString *name;//待归档类型
}
@implementation Possession
-(void)encodeWithCoder:(NSCoder *)aCoder{
            [aCoder encodeObject:name forKey:@"name"];
}
-(void)initWithCoder:(NSCoder *)aDecoder{
            name=[[aDeCoder decodeObjectforKey:@"name"] retain];
}

归档操作:

如果对Possession对象allPossession归档保存,只需要NSCoder子类NSKeyedArchiver的方法archiveRootObject:toFile: 即可。

NSString *path = [self possessionArchivePath];
[NSKeyedArchiver archiveRootObject:allPossessions toFile: path ];

解压操作:

同样调用NSCoder子类NSKeyedArchiver的方法unarchiveRootObject:toFile: 即可
allPossessions = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];

3 SQLite

这个不多说了,我具体就是直接看FMDB的文档。


4 CoreData

简述一下 Core Data数据持久化是对SQLite的一个升级,它是ios集成的.

我们在CoreData中使用的几个类。

1 NSManagedObjectModel(被管理的对象模型)

相当于实体,不过它包含 了实体间的关系

2 NSManagedObjectContext(被管理的对象上下文)

操作实际内容

作用:插入数据 查询 更新 删除

3 NSPersistentStoreCoordinator(持久化存储助理)

相当于数据库的连接器

4 NSFetchRequest(获取数据的请求)

相当于查询语句

5 NSPredicate(相当于查询条件)

6 NSEntityDescription(实体结构)

7 后缀名为.xcdatamodel的包

示例代码,封装好的CoreData管理类CoreDataManager.h:

#import <foundation foundation.h="">
#import "News.h"
#define TableName @"News"

@interface CoreDateManager : NSObject

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

//插入数据
- (void)insertCoreData:(NSMutableArray*)dataArray;
//查询
- (NSMutableArray*)selectData:(int)pageSize andOffset:(int)currentPage;
//删除
- (void)deleteData;
//更新
- (void)updateData:(NSString*)newsId withIsLook:(NSString*)islook;

@end</foundation>
#import "CoreDateManager.h"

@implementation CoreDateManager

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

#pragma mark - Core Data stack

// Returns the managed object context for the application.
// If the context doesn‘t already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

// Returns the managed object model for the application.
// If the model doesn‘t already exist, it is created from the application‘s model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NewsModel" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn‘t already exist, it is created and the application‘s store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"NewsModel.sqlite"];

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _persistentStoreCoordinator;
}

#pragma mark - Application‘s Documents directory

// Returns the URL to the application‘s Documents directory.获取Documents路径
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

//插入数据
- (void)insertCoreData:(NSMutableArray*)dataArray
{
    NSManagedObjectContext *context = [self managedObjectContext];
    for (News *info in dataArray) {
        News *newsInfo = [NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context];
        newsInfo.newsid = info.newsid;
        newsInfo.title = info.title;
        newsInfo.imgurl = info.imgurl;
        newsInfo.descr = info.descr;
        newsInfo.islook = info.islook;

        NSError *error;
        if(![context save:&error])
        {
            NSLog(@"不能保存:%@",[error localizedDescription]);
        }
    }
}

//查询
- (NSMutableArray*)selectData:(int)pageSize andOffset:(int)currentPage
{
    NSManagedObjectContext *context = [self managedObjectContext];

    // 限定查询结果的数量
    //setFetchLimit
    // 查询的偏移量
    //setFetchOffset

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    [fetchRequest setFetchLimit:pageSize];
    [fetchRequest setFetchOffset:currentPage];

    NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    NSError *error;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    NSMutableArray *resultArray = [NSMutableArray array];

    for (News *info in fetchedObjects) {
        NSLog(@"id:%@", info.newsid);
        NSLog(@"title:%@", info.title);
        [resultArray addObject:info];
    }
    return resultArray;
}

//删除
-(void)deleteData
{
    NSManagedObjectContext *context = [self managedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setIncludesPropertyValues:NO];
    [request setEntity:entity];
    NSError *error = nil;
    NSArray *datas = [context executeFetchRequest:request error:&error];
    if (!error && datas && [datas count])
    {
        for (NSManagedObject *obj in datas)
        {
            [context deleteObject:obj];
        }
        if (![context save:&error])
        {
            NSLog(@"error:%@",error);
        }
    }
}
//更新
- (void)updateData:(NSString*)newsId  withIsLook:(NSString*)islook
{
    NSManagedObjectContext *context = [self managedObjectContext];

    NSPredicate *predicate = [NSPredicate
                              predicateWithFormat:@"newsid like[cd] %@",newsId];

    //首先你需要建立一个request
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:TableName inManagedObjectContext:context]];
    [request setPredicate:predicate];//这里相当于sqlite中的查询条件,具体格式参考苹果文档

    //https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pCreating.html
    NSError *error = nil;
    NSArray *result = [context executeFetchRequest:request error:&error];//这里获取到的是一个数组,你需要取出你要更新的那个obj
    for (News *info in result) {
        info.islook = islook;
    }

    //保存
    if ([context save:&error]) {
        //更新成功
        NSLog(@"更新成功");
    }
}
@end

5 Plist

Plist (NSArray\NSDictionary) 也可以来存储数据。

全名 Property List,属性列表文件,它是一种用来存储串行化后的对象的文件。属性列表文件的扩展名为.plist ,因此通常被称为 plist文件。文件是xml格式的。

详细可参考http://blog.csdn.net/mad1989/article/details/8560796

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 20:43:30

Android与iOS开发对比系列】之 数据存储的相关文章

【Android与IOS开发对比系列】之 回调机制

[Android与IOS开发对比系列]之 回调机制 Android和IOS的回调的实现原理基本相同,只是具体命名不同而已. 本文将总结 IOS的Delegate和block, Android的interface和Handler. IOS 协议 委托Delegate是协议的一种,通过@protocol声明.委托类的作用,一是传值,二是传事件. 举个例子: 要实现选相册图片, 依据图: C类是委托类,定义了一个传值方法. A类显示一个图片,点击按钮,进入B类选取图片. 当在B类选完图片,将实现met

iOS开发简单高效的数据存储

学习交流讨论请关注新浪微博:极客James 在iOS开发过程中,不管是做什么应用,都会碰到数据保存的问题,你是用什么方法来持久保存数据的?这是在几乎每一次关于iOS技术的交流或讨论都会被提到的问题,而且大家对这个问题的热情持续高涨.本文主要从概念上把"数据存储"这个问题进行剖析,并且结合各自特点和适用场景进行全面抛析.. 一.NSUserDefaults NSUserDefaults被设计用来存储设备和应用的配置信息,它通过一个工厂方法返回默认的.也是最常用到的实例对象.这个对象中储存

【对比Android与IOS开发系列】——Activity与UIViewController

对比开发系列 --Activity与UIViewController Activity与UIViewController Android的Activity和Fragment是最基本的界面组成,而IOS是UIViewController.几乎所有的View和空间都会放在Activity和UIViewController中. 在之上有不少扩展的: Android: FragmentActivity, AppCompatActivity IOS: UITableViewController, UICo

从Android到iOS开发——(1)、objective-c与java语法对比

从Android到iOS开发--(1).objective-c与java语法 对比 从6月开始,因为有iOS项目要做,就开始了iOS开发之旅,截止今天,已经做了2个项目,给我的感受是iOS开发整体要比Android简单,但是开发细节上两者却又同样的烦恼.那么要想开发iOS,那么到底选择objective-c or swift,我的建议是前者,后者可以在swift2.0和xcode7 以后大家去学习会比较好些.好了,不废话了,下面咱们来看下objective-c和java语法上的比较,希望能让ja

iOS开发UINavigation系列二——UINavigationItem

iOS开发UINavigation系列二--UINavigationItem 一.引言 UINavigationItem是导航栏上用于管理导航项的类,在上一篇博客中,我们知道导航栏是通过push与pop的堆栈操作来对item进行管理的,同样,每一个Item自身也有许多属性可供我们进行自定制.这篇博客,主要讨论UINavigationItem的使用方法. UINavigationBar:http://my.oschina.net/u/2340880/blog/527706. 二.来说说UINavi

iOS开发网络篇—JSON数据的解析

iOS开发网络篇—JSON数据的解析 iOS开发网络篇—JSON介绍 一.什么是JSON JSON是一种轻量级的数据格式,一般用于数据交互 服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外) JSON的格式很像OC中的字典和数组 {"name" : "jack", "age" : 10} {"names" : ["jack", "rose", "jim

iOS开发网络篇—XML数据的解析

iOS开发网络篇—XML数据的解析 iOS开发网络篇—XML介绍 一.XML简单介绍 XML:全称是Extensible Markup Language,译作“可扩展标记语言” 跟JSON一样,也是常用的一种用于交互的数据格式,一般也叫XML文档(XML Document) XML举例 <videos> <video name="小黄人 第01部" length="30" /> <video name="小黄人 第02部&qu

iOS开发之十六进制颜色数据转化为UIColor对象

1.若从服务器返回的颜色字符串数据为 hexColor:"09B57A" hexColor分为三部分:09.B5.7A 分别对应三色值 R.G.B 十六进制 十进制 00 0 01 1 ... ... 09 9 0A 10 0B 11 ... ... 0F 15 10 16 11 17 12 18 ... ... 1F 31 ...   FF 255         代码如下: 1 - (UIColor *)getColor:(NSString *)hexColor 2 { 3 uns

iOS开发UINavigation系列三——工具栏UIToolBar

iOS开发UINavigation系列三--工具栏UIToolBar iOS中除了UINavinationBar之外,还有工具栏UIToolBar可以供我们使用,工具栏和导航栏十分类似,只是功能更加简单,工具栏中也有UIBarButtonItem按钮,在前两篇博客中,对导航栏和导航项都进行的讨论,地址如下: UINavigationBar:http://my.oschina.net/u/2340880/blog/527706 UINavigationItem:http://my.oschina.