Android学习——涉及意识形态的LitePal源码分析

原创技术博客,请认准Azzssss的原文http://www.cnblogs.com/Azzssss/p/4147704.html

这两天项目终于上线了,松了一口气,虽然还是很不稳定,见一步走一步吧。反正总算能抽出时间来写博客了。在项目中用到了LitePal,LitePal是什么鬼东西呢?它的Github主页是什么介绍的:“LitePal是一个开源的android库,允许开发者极其方便地去使用sqlite数据库。通过它,你甚至可以不写一行SQL语句来完成大部分的数据库操作,包括创建和更新表,crud操作,聚合函数等等。LitePal的配置同样很简单,5分钟就可以整合进你的项目。”

我第一次知道LitePal是通过郭林的博客,这家伙好牛,我学android起就看他的博客了,郭先生一连写了八篇博客介绍LitePal,相见恨晚啊。这篇博客主要是想深入了解LitePal的源码,如果还没使用过LitePal,建议先移步郭先生的博客,这是他关于LitePal的第一篇博客《Android数据库高手秘籍(零)——前言》。

现在,先看看LitePal的save操作,太简单了,忍不住再次称赞一下。

public class Album extends DataSupport {

    private String name;

    private float price;

    private List<Song> songs = new ArrayList<Song>();

    // generated getters and setters.
    ...
}

Album album = new Album();
album.setName("album");
album.setPrice(10.99f);
album.save();

是不是很简单?想起自己写过的sqlite语句是不是嗷的一声昏过去了?这里我先讲一下大概的思路吧,是这样实现的,每一个实体类都要继承DataSupport这个类,通过反射找到实体类中所有的属性(只能是基本数据类型或者String),通过反射将对象属性的值put到ContentValues里面,然后就调用原生SQLiteDatabase的方法insert进去。当然,这里面还涉及到关联数据的东西,这个有点复杂,稍后分析。

下面我们看看,这个save操作究竟进行了什么,走起~

我们点进去DataSupport的save方法,发现是这样的(我是不是应该画个类图啊)

public synchronized boolean save() {   //调用了saveThrows()方法   代码上的注释是这么说的,如果这个是一条新的记录,就会在数据库中create,否则就更新现有的数据。
        try {       //它怎么区分这条数据是否在已经在数据库中了?答案是根据JavaBean里地id或者_id属性(必须为int类型或者long类型),这是一个约定,如果实体类
            saveThrows();  //中有这个属性,这个id的值将会由数据库创建并在调用save方法后自动指派给它(原理我稍后看看),并且判断这个id属性是否有值区分insert和update操作
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

接下来我们看saveThrows()方法

public synchronized void saveThrows() {
        SQLiteDatabase db = Connector.getDatabase();      //获取数据库
        db.beginTransaction();                    //这个估计是开启事务?先放着
        try {
            SaveHandler saveHandler = new SaveHandler(db);    //这是关键咯
            saveHandler.onSave(this);
            clearAssociatedData();                  //不知道这个是什么鬼,清理外键数据啥的
            db.setTransactionSuccessful();
        } catch (Exception e) {
            throw new DataSupportException(e.getMessage());
        } finally {
            db.endTransaction();
        }
    }

这里LitePal把储存的操作封装在SaveHandler里面了,其实增删改查的操作还分别封装了DeleteHandler,UpdateHandler,QueryHandler,这四个类继承自DataHandler

SaveHandler的onSave方法是这样的

void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
            NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String className = baseObj.getClassName();                  //获取类名,例子里就是Album类名,其实调用的就是getClass().getName();
        List<Field> supportedFields = getSupportedFields(className);          //找出这个实体类中的所有属性,但是只支持基本数据类型和String类型 
        Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);      //这里涉及到关联数据,先不理
        if (!baseObj.isSaved()) {       //判断模型已经创建与否,也就是说这条数据是否在数据库中了,就一行return baseObjId > 0,所以说实体类的id值不要自己手动去设
            analyzeAssociatedModels(baseObj, associationInfos);            //关联数据相关,等一下再分析
            doSaveAction(baseObj, supportedFields);
            analyzeAssociatedModels(baseObj, associationInfos);             //关联数据相关,等一下再分析
        } else {
            analyzeAssociatedModels(baseObj, associationInfos);            //关联数据相关,等一下再分析
            doUpdateAction(baseObj, supportedFields);                  //如果baseObjId不为空,更新
        }
    }

先说一下doSaveAction

private void doSaveAction(DataSupport baseObj, List<Field> supportedFields)
            throws SecurityException, IllegalArgumentException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        ContentValues values = new ContentValues();
        beforeSave(baseObj, supportedFields, values);          //beforeSave  主要功能是通过反射把实体类的数据存入ContentValues,动态地获取数据类型,并调用相                                                  //应的put方法
        long id = saving(baseObj, values);                //保存数据到表中,并且返回一个long类型的id
        afterSave(baseObj, supportedFields, id);             //afterSave
}

现在看看这行代码  long id = saving(baseObj, values);  这里点进去,就发现是调用SQLiteDataBase的insert操作了,之前我们不是把通过反射把ContentValues的数据都put好了吗,接着调用SQLiteDataBase的insert方法,这个nullColumnHack参数是为了防止插入空行,底层数据库不允许插入一个空行

public long insert(String table, String nullColumnHack, ContentValues values) 

好了,看看afterSave()方法,插入后的操作

private void afterSave(DataSupport baseObj, List<Field> supportedFields, long id) {
        throwIfSaveFailed(id);                    //如果id为-1,抛出异常,插入数据失败
        assignIdValue(baseObj, getIdField(supportedFields), id);    //指派id给JavaBean,就是那个Album对象
        updateAssociatedTableWithFK(baseObj);               //更新关联的表,先不要理,关联那块另外说
        insertIntermediateJoinTableValue(baseObj, false);        //这个也涉及到关联数据,多对多关系下的中间表操作,先放着
}

好像差不多了,SaveHandler里面还有update的方法没提到,还有一小部分关于关联的操作。先吃个夜宵再写。。。。顺便研究一下怎么画类图,还有怎么把博文迁到我的github博客。看到有错别字请给我提醒一下,不谢。

时间: 2024-08-01 13:15:42

Android学习——涉及意识形态的LitePal源码分析的相关文章

Android 中View的绘制机制源码分析 三

到目前为止,measure过程已经讲解完了,今天开始我们就来学习layout过程,不过在学习layout过程之前,大家有没有发现我换了编辑器,哈哈,终于下定决心从Html编辑器切换为markdown编辑器,这里之所以使用"下定决心"这个词,是因为毕竟Html编辑器使用好几年了,很多习惯都已经养成了,要改变多年的习惯确实不易,相信这也是还有很多人坚持使用Html编辑器的原因.这也反应了一个现象,当人对某一事物非常熟悉时,一旦出现了新的事物想取代老的事物时,人们都有一种抵触的情绪,做技术的

Android 中View的绘制机制源码分析 二

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要讲解了View的measure过程,今天我们就来学习ViewGroup的measure过程,由于ViewGroup只是一个抽象类,所以我们需要以一个具体的布局来分析measure过程,正如我上篇文章说的,我打算使用LinearLayout为例讲解measure过程,如果你还没有读过上篇文章,那么建议你先

Android的软件包管理服务PackageManagerService源码分析

Android系统下的apk程序都是通过名为PackageManagerService的包管理服务来管理的.PacketManagerService是安卓系统的一个重要服务,由SystemServer启动,主要实现apk程序包的解析,安装,更新,移动,卸载等服务.不管是系统apk(/system/app),还是我们手工安装上去的,系统所有的apk都是由其管理的. 以android 4.0.4的源码为例,android4.0.4/frameworks/base/services/java/com/

Android应用程序启动过程——Launcher源码分析

当我们在Launcher界面单击一个应用程序图标时就会启动一个程序,那这一个过程究竟发生了些哪样呢?让我们跟踪Launcher源码来分析一下吧. 先上流程图: step1.追踪Launcher  从源码中我们可以发现Launcher其实也是一个程序,它继承于Activity.找到该文件中的onCreate()方法,代码片段如下: protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceSta

Android应用层View绘制流程与源码分析

Android应用层View绘制流程与源码分析 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(View篇)>文章的3-1

Android图片加载库Picasso源码分析

图片加载在Android开发中是非常重要,好的图片加载库也比比皆是.ImageLoader.Picasso.Glide.Fresco均是优秀的图片加载库. 以上提到的几种图片加载库各有特色.用法与比较,网上已经很多了. 出于学习的角度,个人认为从Picasso入手较好.代码量小,同时API优美,很适合我们学习. 今天笔者就Picasso的源码进行分析,抛出一些图片加载的技术细节供园友参考. PS:建议园友先大致看一下源码. 我们对图片加载的要求 1.加载速度要快 2.资源消耗要低 3.加载图片不

Android视图View绘制流程与源码分析(全)

来源:[工匠若水 http://blog.csdn.net/yanbober] 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(

[Android实例] Scroll原理-附ScrollView源码分析 (转载)

想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内容是画在一个无限大的画布上~ 画的内容有限, 而手机屏幕可以看到的东西更有限~ 但是背景画布是无限的 如果把放大镜的移动比作scroll操作,那么可以理解,这个scroll的距离是无限制的~ 只不过scroll到有图的地方才能看到内容 参考ScrollView理解, 当child内容过长时,有一部分

Java显式锁学习总结之五:ReentrantReadWriteLock源码分析

概述 我们在介绍AbstractQueuedSynchronizer的时候介绍过,AQS支持独占式同步状态获取/释放.共享式同步状态获取/释放两种模式,对应的典型应用分别是ReentrantLock和Semaphore,AQS还可以混合两种模式使用,读写锁ReentrantReadWriteLock就是如此. 设想以下情景:我们在系统中有一个多线程访问的缓存,多个线程都可以对缓存进行读或写操作,但是读操作远远多于写操作,要求写操作要线程安全,且写操作执行完成要求对当前的所有读操作马上可见. 分析