浅析郭婶儿子--LitePal框架(一)

最近看了郭神的LitePal框架感觉愣牛逼,牛逼之余,也很好奇他是如何实现的,好奇心害死猫啊!跟随大神脚步,看源码.

1.在使用LitePal框架的时候,在项目的assets目录下面新建一个litepal.xml文件,其中的内容包括数据库的名称,版本,以及映射,那它如何去把这些内容映射进去的?

先贴一下litepal.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<litepal>

    <!-- 数据库名 -->
    <dbname value="demo" >
    </dbname>
    <!-- 数据库版本 -->

    <version value="1" >
    </version>
    <!-- 映射模型 -->

    <list>
        <mapping class="com.sdufe.thea.guo.model.News" >
        </mapping>
    </list>

</litepal>

下面是litepal.xml对应函数中的属性代码:

/*
 * Copyright (C)  Tony Green, Litepal Framework Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.litepal.parser;

import java.util.ArrayList;
import java.util.List;

import org.litepal.exceptions.InvalidAttributesException;
import org.litepal.util.Const;
import org.litepal.util.SharedUtil;

import android.text.TextUtils;

/**
 * The object model for the litepal.xml file. Once database connection happens,
 * LitePal will try to analysis the litepal.xml, and read all the attribute into
 * the LitePalAttr model for further usage.
 *
 * @author Tony Green
 * @since 1.0
 */
public final class LitePalAttr {

	/**
	 * Static litePalAttr object.
	 */
	private static LitePalAttr litePalAttr;

	/**
	 * The version of database.
	 */
	private int version;

	/**
	 * The name of database.
	 */
	private String dbName;

	/**
	 * The case of table names and column names and SQL.
	 */
	private String cases;

	/**
	 * All the model classes that want to map in the database. Each class should
	 * be given the full name including package name.
	 */
	private List<String> classNames;

	/**
	 * Do not allow new a LitePalAttr object. Makes it a singleton class.
	 */
	private LitePalAttr() {
	}

	/**
	 * Provide a way to get the object of LitePalAttr class.
	 *
	 * @return the singleton object of LitePalAttr
	 */
	public static LitePalAttr getInstance() {
		if (litePalAttr == null) {
			synchronized (LitePalAttr.class) {
				if (litePalAttr == null) {
					litePalAttr = new LitePalAttr();
				}
			}
		}
		return litePalAttr;
	}

	public int getVersion() {
		return version;
	}

	void setVersion(int version) {
		this.version = version;
	}

	public String getDbName() {
		return dbName;
	}

	void setDbName(String dbName) {
		this.dbName = dbName;
	}

	/**
	 * Get the class name list. Always add table_schema as a value.
	 *
	 * @return The class name list.
	 */
	public List<String> getClassNames() {
		if (classNames == null) {
			classNames = new ArrayList<String>();
			classNames.add("org.litepal.model.Table_Schema");
		} else if (classNames.isEmpty()) {
			classNames.add("org.litepal.model.Table_Schema");
		}
		return classNames;
	}

	/**
	 * Add a class name into the current mapping model list.
	 *
	 * @param className
	 *            Full package class name.
	 */
	void addClassName(String className) {
		getClassNames().add(className);
	}

	public String getCases() {
		return cases;
	}

	void setCases(String cases) {
		this.cases = cases;
	}

	/**
	 * Before application build the connection with database, check the fields
	 * in LitePalAttr. If all of the fields are passed, the connection will be
	 * continued.If anyone of them doesn't pass, an exception will be thrown.
	 *
	 * @return If all of the fields are passed, return true. If dbname is
	 *         undefined, or version is less than 1, or version is earlier than
	 *         current version, throw InvalidAttributesException
	 *
	 * @throws InvalidAttributesException
	 */
	public boolean checkSelfValid() {
		if (TextUtils.isEmpty(dbName)) {
			throw new InvalidAttributesException(
					InvalidAttributesException.DBNAME_IS_EMPTY_OR_NOT_DEFINED);
		}
		if (!dbName.endsWith(Const.LitePal.DB_NAME_SUFFIX)) {
			dbName = dbName + Const.LitePal.DB_NAME_SUFFIX;
		}
		if (version < 1) {
			throw new InvalidAttributesException(
					InvalidAttributesException.VERSION_OF_DATABASE_LESS_THAN_ONE);
		}
		if (version < SharedUtil.getLastVersion()) {
			throw new InvalidAttributesException(
					InvalidAttributesException.VERSION_IS_EARLIER_THAN_CURRENT);
		}
		if (TextUtils.isEmpty(cases)) {
			cases = Const.LitePal.CASES_LOWER;
		} else {
			if (!cases.equals(Const.LitePal.CASES_UPPER)
					&& !cases.equals(Const.LitePal.CASES_LOWER)
					&& !cases.equals(Const.LitePal.CASES_KEEP)) {
				throw new InvalidAttributesException(cases
						+ InvalidAttributesException.CASES_VALUE_IS_INVALID);
			}
		}
		return true;
	}

}

这就是郭哥代码中对应xml文件中的属性了,那如何映射进去的呢?它自己应该不能平白无故的就对应上了,继续看源码

解析xml文件中出现了这么一个函数:

/**
	 * Analyze litepal.xml, and store the analyzed result in LitePalParser. Use
	 * DomParse to parse the configuration file as default. SAXParser and
	 * XmlPullParser is also optional, but not visible to developers.
	 */
	public static void parseLitePalConfiguration() {
		if (parser == null) {
			parser = new LitePalParser();
		}
		parser.useSAXParser();
	}

从函数名上就猜到使用了SAX解析xml,也不能胡乱猜,继续看郭哥的源码,赶紧去useSAXParser()看看到底是如何实现的

/**
	 * Use SAXParser to parse the litepal.xml file. It will get the parsed
	 * result from LitePalContentHandler and stored in the instance of
	 * LitePalAttr.
	 *
	 * Note while analyzing litepal.xml file, ParseConfigurationFileException
	 * could be thrown. Be careful of writing litepal.xml file, or developer's
	 * application may be crash.
	 */
	void useSAXParser() {
		LitePalContentHandler handler = null;
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			XMLReader xmlReader = factory.newSAXParser().getXMLReader();
			handler = new LitePalContentHandler();
			xmlReader.setContentHandler(handler);
			xmlReader.parse(new InputSource(getConfigInputStream()));
			return;
		} catch (NotFoundException e) {
			throw new ParseConfigurationFileException(
					ParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);
		} catch (SAXException e) {
			throw new ParseConfigurationFileException(
					ParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);
		} catch (ParserConfigurationException e) {
			throw new ParseConfigurationFileException(
					ParseConfigurationFileException.PARSE_CONFIG_FAILED);
		} catch (IOException e) {
			throw new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);
		}
	}

是的,你没有猜错,上面的就是SAX解析xml的格式了,使用SAX解析xml差不多就是这么个格式,不同的就在那个handler了,当然郭神的代码相当规范,向大神学习,要想知道他是怎么解析litepal.xml还是继续看handler的实现吧!这里只贴主要代码,不能弄得很长,长了就不太好了哈!

/**
	 * Start analysis the litepal.xml file. Set all the parsed value into the
	 * LitePalAttr model.
	 */
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes)
			throws SAXException {
		if (LitePalParser.NODE_DB_NAME.equalsIgnoreCase(localName)) {
			for (int i = 0; i < attributes.getLength(); i++) {
				if (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {
					litePalAttr.setDbName(attributes.getValue(i).trim());
				}
			}
		} else if (LitePalParser.NODE_VERSION.equalsIgnoreCase(localName)) {
			for (int i = 0; i < attributes.getLength(); i++) {
				if (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {
					litePalAttr.setVersion(Integer.parseInt(attributes.getValue(i).trim()));
				}
			}
		} else if (LitePalParser.NODE_MAPPING.equalsIgnoreCase(localName)) {
			for (int i = 0; i < attributes.getLength(); i++) {
				if (LitePalParser.ATTR_CLASS.equalsIgnoreCase(attributes.getLocalName(i))) {
					litePalAttr.addClassName(attributes.getValue(i).trim());
				}
			}
		} else if (LitePalParser.NODE_CASES.equalsIgnoreCase(localName)) {
			for (int i = 0; i < attributes.getLength(); i++) {
				if (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {
					litePalAttr.setCases(attributes.getValue(i).trim());
				}
			}
		}
	}

上面的就是handler的解析内容了,根据开始元素解释开始元素,个人猜测,既然是用SAX解析xml,就可以有多个Lite标签,所以我觉得可以使用LitePal框架可以建多个数据库,不过LitePal是继承自SQLite数据库,一般一个app应该不会有很多数据库吧,本来就很小!当然源代码中还有用Pull解析的xml,这里也贴一下代码

/**
	 * Use XmlPullParser to parse the litepal.xml file. It will store the result
	 * in the instance of LitePalAttr.
	 *
	 * Note while analyzing litepal.xml file, ParseConfigurationFileException
	 * could be thrown. Be careful of writing litepal.xml file, or developer's
	 * application may be crash.
	 */
	void usePullParse() {
		try {
			LitePalAttr litePalAttr = LitePalAttr.getInstance();
			XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
			XmlPullParser xmlPullParser = factory.newPullParser();
			xmlPullParser.setInput(getConfigInputStream(), "UTF-8");
			int eventType = xmlPullParser.getEventType();
			while (eventType != XmlPullParser.END_DOCUMENT) {
				String nodeName = xmlPullParser.getName();
				switch (eventType) {
				case XmlPullParser.START_TAG: {
					if (NODE_DB_NAME.equals(nodeName)) {
						String dbName = xmlPullParser.getAttributeValue("", ATTR_VALUE);
						litePalAttr.setDbName(dbName);
					} else if (NODE_VERSION.equals(nodeName)) {
						String version = xmlPullParser.getAttributeValue("", ATTR_VALUE);
						litePalAttr.setVersion(Integer.parseInt(version));
					} else if (NODE_MAPPING.equals(nodeName)) {
						String className = xmlPullParser.getAttributeValue("", ATTR_CLASS);
						litePalAttr.addClassName(className);
					} else if (NODE_CASES.equals(nodeName)) {
						String cases = xmlPullParser.getAttributeValue("", ATTR_VALUE);
						litePalAttr.setCases(cases);
					}
					break;
				}
				default:
					break;
				}
				eventType = xmlPullParser.next();
			}
		} catch (XmlPullParserException e) {
			throw new ParseConfigurationFileException(
					ParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);
		} catch (IOException e) {
			throw new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);
		}
	}

当然知道了解析方法,知道了属性对应的函数,那他是如何找到的litepal.xml的呢?不急,我们继续看

/**
	 * Iterates all files in the root of assets folder. If find litepal.xml,
	 * open this file and return the input stream. Or throw
	 * ParseConfigurationFileException.
	 *
	 * @return The input stream of litepal.xml.
	 * @throws IOException
	 */
	private InputStream getConfigInputStream() throws IOException {
		AssetManager assetManager = LitePalApplication.getContext().getAssets();
		String[] fileNames = assetManager.list("");
		if (fileNames != null && fileNames.length > 0) {
			for (String fileName : fileNames) {
				if (Const.LitePal.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {
					return assetManager.open(fileName, AssetManager.ACCESS_BUFFER);
				}
			}
		}
		throw new ParseConfigurationFileException(
				ParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);
	}

以上就是他找到本地litepal.xml的方法啦,通过getAssets()读到本地的asset里面的文件,那文件名跟Const.LitePal.CONFIGURATION_FILE_NAME对比,相同的话就读到本地文件了,当然我开始也有个疑问,我是否可以随便起个名字呢?这里当然不可以,因为asset可以有很多文件,他不知道要用哪一个,这里郭哥直接写死了,文件名只能叫litepal.xml

public static final String CONFIGURATION_FILE_NAME = "litepal.xml";

看到这,你应该大概连接到郭哥是怎么操作litepal.xml,理一理,首先你使用LitePal框架引进包之后,需要建一个litepal.xml文件,郭哥通过getAsset()读到本地asset文件夹的文件名,通过跟litepal.xml文件名对比,获得里面的内容,在使用SAX解析本地的文件,获得文件中的内容,赋值到LitePalAttr里.

2.在LitePal框架的使用中,需要配置AndroidManifest.xml,在application中加入android:name="org.litepal.LitePalApplication" 那我们不妨从LitePalApplication开始看

public class LitePalApplication extends Application {

	/**
	 * Global application context.
	 */
	private static Context mContext;

	/**
	 * Construct of LitePalApplication. Initialize application context.
	 */
	public LitePalApplication() {
		mContext = this;
	}

	/**
	 * Get the global application context.
	 *
	 * @return Application context.
	 * @throws GlobalException
	 */
	public static Context getContext() {
		if (mContext == null) {
			throw new GlobalException(GlobalException.APPLICATION_CONTEXT_IS_NULL);
		}
		return mContext;
	}

	@Override
	public void onLowMemory() {
		super.onLowMemory();
		mContext = getApplicationContext();
	}

}

郭哥这里为了方便用户多次调用context,将context配置到AndroidManifest.xml,并在LitePalApplication中给context赋值,这里需要注意一下,郭哥为了防止context为空,所以在onLowMemory给context赋值.大神不愧是大神,这么机智的保护措施.

今天就到这,欲知后事如何,请听下回解说!

时间: 2024-08-06 15:59:05

浅析郭婶儿子--LitePal框架(一)的相关文章

浅析郭婶儿子--LitePal框架(三)

毕业半年了,Android也学习了半年了,零零散散的学了一些,漫无目的,于是看了看郭哥的<<第一行代码>>,真心觉得不错,对于没有系统培训过Android的来说,是一本很好的书籍,忘了谁问过我怎么学Android,说实在的我也不知道,我也是个小菜,但是看过这本书之后,推荐去看一看,对于新手来说是本很好地资料,对于接触过的人来说可以好好玩完善你的知识体系,当然没人都有自己的认识,以上只是代表我的个人观点. 今天我们继续接上回讲说LitePal框架源码浅析,本博客只是浅析,更详细的移步

关于郭婶Litepal框架,保存和修改返回值提示成功,但是数据库的值没改变的问题

如题,一直在使用郭婶的litepal,真的很方便的一个框架,郭婶的博客中也讲的很详细,上手很快,不过,今天在我的项目中发现了这么一个问题,就是我update后,返回了1,说明我更新成功了一条数据,但是,我再次打开的时候,发现那条数据没有变,我又试了试保存,也有同样的问题,但是有时候又是正常的,通过查看源码发现,郭婶的数据库里面是有做过优化的,直接删了两个条件,也就是去掉优化,结果就立刻解决了,不过应该还有更好的办法,我时间来不及就直接删掉了优化的部分. DataHandler中有个判断在917-

Owin+ASP.NET Identity浅析系列(三)框架结构分析

在今天,读书有时是件"麻烦"事.它需要你付出时间,付出精力,还要付出一份心境.--仅以<Owin+ASP.NET Identity浅析系列>来祭奠那逝去的-- 前两篇博客仅仅说了下功能如何实现,这篇博客来分析IdentityModels.IdentityConfig.Startup.Auth类文件在Owin+ASP.NET Identity框架中起到的作用 IdentityModels类文件存放的是用户.角色.数据库上下文等实体类 IdentityConfig类文件存放的是

Android乱弹onLowMemory()和onTrimMemory()

今天看郭哥的LitePal框架的源码,刚打开LitePalApplication里面的源码看到了这样一幕 @Override public void onLowMemory() { super.onLowMemory(); mContext = getApplicationContext(); } 不太懂郭哥的意思.之前依稀记得有人说起onLowMemory()和onTrimMemory(),于是乎,我就去查了查源码,这篇博客就来乱弹一下onLowMemory()和onTrimMemory()

【转载】DXUT11框架浅析(4)--调试相关

原文:DXUT11框架浅析(4)--调试相关 DXUT11框架浅析(4)--调试相关 1. D3D8/9和D3D10/11的调试区别 只要安装了DXSDK,有个调试工具DirectX ControlPanel,如下图所示.这里可以将Direct3D 9设置为调试运行时(Debug D3D9 Runtime)或零售运行时(RetailD3D9 Runtime).注意这里的设置是全局的,如果改成调试运行时,则所有用到D3D9的程序都会进入调试模式,这会使这些程序运行的很慢. 从Vista开始系统自己

Litepal 数据库操作框架的使用 (火)

LitePal是GitHub上一款开源的Android数据库框架. 它採用了对象关系映射(ORM)的模式,将平时开发时最经常使用的一些数据库功能进行了封装.使得开发人员不用编写一行SQL语句就能够完毕各种建表.増删改查的操作. 并且LitePal非常"轻",jar包大小不到100k,并且近乎零配置. 核心:1)从表中读取到数据,展现给用户:2)把数据存储到表中. 特点:1)全然不须要使用sqlite语句; 2)以面向对象的方式操作数据库. Github地址:https://github

Android中最方便的数据库--LitePal

最近看到了郭神分析的LitePal框架,感觉很强大,试用了一下,确实不错,你习不习惯我不知道,因人而异嘛,感觉很适合我 看完之后,想了想,对我来说吧,实体来个set赋值,要是数据很多的话那可麻烦大了,更何况我现在工作内容基本上都是跟服务器交互,数据吗,就不用说了...一般都是json传送数据,直接用Gson解析,扔到list里面就不管了,所以我这里需要把list内容保存,于是乎,我就下载了源码,看了看,确实很强大,我要的内容都有,并且还很方便 废话少说,先呈上LItePal的源码地址:https

IOC模式及Unity框架文章收藏

1.IoC模式:http://www.cnblogs.com/qqlin/archive/2012/10/09/2707075.html 通过Unity实现IOC容器. 2.深入理解DIP.IoC.DI以及IoC容器 3.理解依赖注入(IOC)和学习Unity 4.你真的了解Ioc与AOP吗? 5.[调侃]IOC前世今生 6.Unity系列 Unity(一):从ObjectBuilder说起 Unity(二):Unity是什么? Unity(三):快速入门 Unity(四):使用场景Ⅰ:建立类型

一起来开发Android的天气软件(三)

距离上一篇一起来开发Android天气软件二的时间又将近半个月了,之间一直因为有事而没有更新实在抱歉,最近会加快更新的步伐,争取在2015年到来前写完这系列的博文,上一章我们已经使用LitePal框架搭建好了我们所需的数据库,这一章的内容将主要完成关于从中国天气网获取数据的网络通信操作,之前有学习过Android开发的同学应该都知道,Android实现互联网通信主要有两种方法,一种使用HTTPURLCONNECTION,一种使用HttpClient的方式,而我们今天将使用不同于以上两种的方式,使