最近看了郭神的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>
/* * 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(); }
/** * 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); } }
/** * 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); } }
/** * 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); }
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赋值.大神不愧是大神,这么机智的保护措施.
今天就到这,欲知后事如何,请听下回解说!
原文地址:http://blog.csdn.net/elinavampire/article/details/41758993