如何sqlite3 数据库升级 sqlite

如何升级 sqlite_百度知道
如何升级 sqlite
提问者采纳
ndroid SQLite 客户端数据库升级 一,前言 没有采用Android自身提供的那一套数据库操作方式。而是想对SQLite数据库文件有更全面的控制,包括随时导出数据库文件修改表结构,增删数据等等。这样一来虽然在开放中得到不少便利,但是也带来了数据库升级的一些问题。 后来不得已采用了一种方案,可以解决问题,现将方案的全部实现细节记录下来。最后也会提出一些我认为有问题的地方。
二,数据库文件拷贝 程序不负责数据库的创建,SQLite数据库文件是在外部创建好的。程序启动阶段拷贝进SD卡。以达到对数据库结构的全面控制。 数据库文件存放位置见附件图片。 Java代码
1. public void copyDBFile() {
// 数据库路径
if (!FileOperator.checkFile(SysConst.DB_PATH)) {
boolean result = FileOperator.write2Sdcard(activity,
R.raw.scpip_collection, SysConst.DB_PATH);
Debug.log(&无数据库文件,首次拷贝& + result);
if (!result) {
throw new IllegalAccessError(activity
.getString(R.string.copy_db_exception));
// 拷贝成功,更新数据库版本
PackageInfo info = activity.getPackageManager()
.getPackageInfo(activity.getPackageName(), 0);
// 当前程序版本号,在AndroidManifest.xml中定义
int versionCode = info.versionC
Config.saveDbVer(versionCode);
Debug.log(&拷贝成功& + result);
} catch (NameNotFoundException e) {
Debug.e(e);
// 数据库已存在的情况
if (dbUpdate.needUpdate() ) {
activity.showProgress(&数据库升级中,请稍后&, false);
new Thread() {
public void run() {
Debug.log(&update db&);
dbUpdate.updateDb();
handler.sendEmptyMessage(0);
} catch (Exception e) {
Debug.e(e);
handler.sendEmptyMessage(-1);
}.start();
其中有几个要点: 1、检测数据库文件是否已经存在。不存在则从raw文件夹复制数据库文件拷贝至SD卡中指定目录。 2、数据库版本是根据应用的versionCode相同。拷贝数据库后,会把当前versionCode写入数据库的表中。 Xml代码
1. &manifest
xmlns:android=&http //schemas android com/apk/res/android&
package=&com.xxx&
android:versionCode=&2&
android:versionName=&1.01.001&&
&uses-sdk android:minSdkVersion=&8& /&
versionCode在AndroidManifest.xml文件中。 在这种方案下,实际上是由versionCode控制数据库版本,versionName控制程序版本。 3、SD卡指定目录已经存在数据库文件的情况,则读取其中保存的数据库版本号,与versionCode对比,从而确定是否需要升级数据库。代码如下: Java代码
1. public boolean needUpdate() {
int currVer = Config.getDbVer();
return currVer & getAppVersion();
public int getAppVersion(){
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0);
// 当前程序版本号,在AndroidManifest.xml中定义
return info.versionC
} catch (NameNotFoundException e) {
Debug.e(e);
三,升级数据库 包括三个步骤: 1、从程序中拷贝新数据库文件至SD卡指定目录,命名为temp.db。 Java代码
1. String temp = SysConst.DB_FOLDER + &temp.db&;
2. boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);
2、分别获取两个数据源。 Java代码
1. //原数据库文件
2. BaseDao sd = new BaseDao();
3. //新数据库文件
4. BaseDao nd = new BaseDao(temp);
对于SQLite数据库来讲,数据源就是数据库文件。BaseDao是自己封装的,关键在于要可以配置不同的数据源 具体实现的相关代码如下: Java代码
1. private String dbP
3. public BaseDao() {}
5. public BaseDao(String dbPath) {
this.dbPath = dbP
9. ublic SQLiteDatabase getDb() {
return SQLiteDatabase.openDatabase(SysConst.DB_PATH, null,
SQLiteDatabase.OPEN_READWRITE);
14. public SQLiteDatabase getDb(String dbPath) {
return SQLiteDatabase.openDatabase(dbPath, null,
SQLiteDatabase.OPEN_READWRITE);
这样就可以根据文件,获取不同的SQLiteDatabase 对象。
3、传输数据 把原数据库中的数据查询出来,插入到新数据库中。 Java代码
1. public &E&void transfer(BaseDao sd,BaseDao nd,Class&E& cls) throws Exception{
List&E& list = sd.find(cls, null);
nd.batchInsert(list);
这里有两个要点, 第一,使用了自行封装的ORM。 第二,使用了SQLite批量插入,增加写入效率。代码如下: Java代码
1. @Override
public &T& void batchInsert(List&T& datas) throws Exception {
SQLiteDatabase dba =
if(dbPath == null){
dba = getDb();
dba = getDb(dbPath);
int size = datas.size();
dba.beginTransaction();
for (int i = 0; i & i++) {
DaoHelper helper = new DaoHelper(datas.get(i));
String tableName = helper.getTableName();
String sql = &select 1 from & + tableName + & where &
+ helper.getPkCol() + &=&;
Object id = helper.getPkValue();
if (id instanceof String) {
sql = sql + &'& + id + &'&;
sql = sql +
c = dba.rawQuery(sql, null);
穿川扁沸壮度憋砂铂棘 if (c != null ? (c.getCount() == 1) : false) {
c.close();
if(c != null){
c.close();
SqlArgs sa = helper.prepareInsert();
dba.execSQL(sa.getSql(), sa.getArgs());
dba.insert(helper.getTableName(), &&, helper.getInsertContent());
dba.setTransactionSuccessful();
dba.endTransaction();
} finally {
dba.close();
4、写入当前数据库版本,即从程序获得的versionCode。 5、删除原数据库文件,重命名temp.db。 完整过程如下: Java代码
1. public void updateDb() throws Exception {
//1,写入新的数据库文件,命名为temp
String temp = SysConst.DB_FOLDER + &temp.db&;
boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);
Debug.log(&s1:& + s1);
//原数据库文件
BaseDao sd = new BaseDao();
//新数据库文件
BaseDao nd = new BaseDao(temp);
//转移数据
//此处代码略
//删除原数据库文件,重命名临时数据库文件
if(FileOperator.delSdcardFile(SysConst.DB_PATH)){
File file = new File(temp);
file.renameTo(new File(SysConst.DB_PATH));
//此时更新数据库版本
来自团队:
其他类似问题
为您推荐:
sqlite的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁Android中SQLite版本升级_小组_ThinkSAAS
Android中SQLite版本升级
Android中SQLite版本升级
Android中SQLite版本升级
网上有很多文章,但是感觉写的都不是很明白
定义DbOpenHelper类继承SQLiteOpenHelper
重写onCreate和onUpgrade方法
onDowngrade是降级的方法,应该不会用到
定义两个属性
// 数据库名称,如果是null就会创建一个在内存中的数据库,当然内存被清理数据也就消失了
private static final String DATABASE_NAME ="test.db";
// 数据库版本,必须大于1,不然抛异常
private static final int DATABASE_VERSION = 1;
最重要的方法是(网上都没有说):
public DbOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
一,有了前提就可以说说执行过程
1,在使用数据库的时候就会调用DbOpenHelper构造方法
2,猜测(懒,没有看源码)SQLite会根据DATABASE_NAME进行判断,如果不存在就创建,同时记录版本号。然后调用onCreate方法
3,如果DATABASE_NAME存在,就检查版本号,如果版本号一致就继续执行
4,如果版本号不一致就执行onUpgrade方法
二,onUpgrade方法(自己定义规则,版本号是连续)
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 1是初始化版本,所以不用考虑
// 不能使用else if, 是为了解决跨版本的问题比如1直接升级到5需要执行2,3,4,5
// newVersion在这个方法没有用到,可以做个判断newVersion == DATABASE_VERSION
// 如果不等就抛异常(SQLite内部出现错误了,这种应该不可能出现)
int currVersion = oldV
if (currVersion == 2) {
// TODO执行相应的数据库变更代码
currVersion = 3;
if (currVersion == 3) {
// TODO执行相应的数据库变更代码
currVersion = 4;
if (currVersion == 4) {
// TODO执行相应的数据库变更代码
currVersion = 5;
if (currVersion == 5) {
// TODO执行相应的数据库变更代码
三,如何保留原始数据,创建临时表,初始化值之类的都属于sql范畴,就不说了。
PHP开发框架
服务器环境
ThinkSAAS商业授权:
ThinkSAAS为用户提供有偿个性定制开发服务
ThinkSAAS将为商业授权用户提供二次开发指导和技术支持
手机客户端
ThinkSAAS接收任何功能的Iphone(IOS)和Android手机的客户端定制开发服务
让ThinkSAAS更好,把建议拿来。全民编程开始了,你还在等什么?快来加入我们吧
已有账号,请
,或用以下账号登录
400-058-0010
& 如果APP版本更新后修改了Sqlite数据库的表结构,如何在保留...
悬赏10分如果APP版本更新后修改了Sqlite数据库的表结构,如何在保留用户原有数据的情况下更新数据库
共 1 个回复
1.首先我们需要把原来的数据库表重命名一下public static final String SQL_ALTER_TABLE_NAME = "alter table "
+ A + " rename to temp_A";原来的表结构是:private static final String SQL_CREATE_TABLE = "create table
if not exists "
+ A + "(id integer primary key autoincrement,...)";2.然后把备份表temp_A中的数据copy到新创建的数据库表A中,这个表A没发生结构上的变化public static final String INSERT_SUBSCRIBE = "select 'insert into A (field1, field2...)
values ('''||field1||''','''||field2||''',...)'
as insertSQL from temp_A";3.此时临时表中的数据已经全部复制到了表A中,4.删除备份表public static final String DELETE_TEMP_TABLE = "delete from temp_A ";
public static final String DROP_TEMP_TABLE = "drop table if exists temp_A";5.然后把数据库版本号改为比之前高的版本号,在OnUpgrade方法中执行上述语句就行.
才能回复哦……
4650个问题
<p class="more" style="color:#9个回答
<p class="more" style="color:#5个回答
<p class="more" style="color:#2个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
<p class="more" style="color:#个回答
&2015 优才学院—中国IT职业教育O2O创新品牌 京ICP备号
&&北京市石景山区实兴大街30号院7号楼(京西科技金融大厦)9层SQLite在Android中的使用 - ImportNew
严谨一点说,SQLite在Android设备中可以被当做是一种数据存储方法或者干脆就是一个数据库
正如其他大多数平台一样,Android 也提供了几种方法用来保存数据,使得这些数据即使在程序结束以后依然不会丢失。这些方法有:文本文件-可以保存在应用程序自己的目录下(【译者注】安装的每个app都会在/data/data/目录下创建个文件夹,名字和应用程序中AndroidManifest.xml文件中的package一样),也可以保存在SDcard中;Preferences也是一种经常使用的数据存储方法,因为它们对于用户而言是透明的,并且从应用安装的时候就存在了;另外,如果放宽点说的话,Assets也可以用来存储一些只读数据。Assets是指那些在assets目录下的文件,这些文件在你将你的应用编译打包之前就要存在,并且可以在应用程序运行的时候被访问到。以后我会更加详细的聊聊这些方法的细节。
然而,有时候我们需要对保存的数据进行一些复杂的操作,或者数据量很大,超出了文本文件和Preference的性能能hold住的范围,所以需要一些更加高效的方法来管理。这时就需要一个移动平台上的数据库闪亮登场了。
从Android1.5(代号Cupcake)开始,Android就自带SQLite(版本3.5.9+)了。如果你对不熟悉的话,就把它当成是一个独立的,无需服务进程,支持事务处理,可以使用SQL语言的数据库。尽管SQLite也有它的不足之处,但是在Android开发者的武器库里,可以算是个杀手锏了。
本文中,我主要介绍在Android中使用SQLite的方法,着重介绍它的管理操作,具体而言,就是创建和更新(update)(【译者注】这里说的更新操作不是说使用update语句更新数据库数据的操作,而是修改数据库结构的操作,本文中的update和upgrade都是这个意思,为避免混淆,后注原英文使用动词),而不是那些运行时的操作。
管理SQLite
我们可以从创建一个继承自SQLiteOpenHelper的类来管理SQLite开始探讨这一话题,这个类有一个构造方法和另外两个必须实现的方法,onCreate和onUpgrade方法.
很自然的,这些方法中第一个被执行的就是构造方法,在构造函数中调用父类的构造方法,同时传入四个参数:
Context, 这表示应用程序的上下文,在构造函数中保存住,对以后的其他操作有用。
数据库名称,就是个文件名,表示数据库物理文件名称的字符串。
游标factory,如果提供的话,可以用来创建游标。
数据库版本,这是你的数据库的版本(用一个整数表示),稍后我会讨论这个参数的细节。初始值为1。
在我们的例子中,我们的四个参数如下面代码所示:
class DB extends SQLiteOpenHelper {
final static int DB_VERSION = 1;
final static String DB_NAME = &mydb.s3db&;
public DB(Context context) {
super(context, DB_NAME, null, DB_VERSION);
// Store the context for later use
this.context =
构造函数做两件事情,首先,检查数据库是否存在,如果不存在,则调用onCreate方法创建数据库。然后,如果数据库已经存在了,那么就检查数据库版本是否和构造函数中传入的数据库版本值一致,从而决定数据库是不是已经更新(updated)过了,如果需要更新,则调用onUpgrade方法。
另外,如上所述,我们已经知道onCreate方法只有当数据库不存在的时候才会被调用,因此如果你想在程序安装以后第一次运行时做什么操作的话,这个方法倒不失为一个很方便的手段,你可以在这个方法中调用任何其他方法,比如说许可协议说明对话框。
让我们回头看看数据库本身,因为本文只是一个说明性质的文章,因此这里我只是创建一个简单的雇员信息数据库,创建数据库的SQL脚本如下所示:
CREATE TABLE employees (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
ext TEXT NOT NULL,
mob TEXT NOT NULL,
age INTEGER NOT NULL DEFAULT &#039;0&#039;
我们可以很容易的用hard coding的方式将创建脚本写死在代码中,一行对应一行,代码如下:
public void onCreate(SQLiteDatabase database) {
database.execSQL(?
&CREATE TABLE employees ( _id INTEGER PRIMARY KEY &
+ &AUTOINCREMENT, name TEXT NOT NULL, ext TEXT NOT NULL, &
+ &mob TEXT NOT NULL, age INTEGER NOT NULL DEFAULT &#039;0&#039;)&);
但正如你所想的那样,当数据库大小达到了某个值,或者复杂性达到了某个程度时,这种做法就会非常不灵活,因此理想的做法是将SQL脚本放到一个asset文件中。如果这样做的话,你需要写一个方法从assets目录中读取SQL脚本,然后执行它:
public void onCreate(SQLiteDatabase database) {
executeSQLScript(database, &create.sql&);
private void executeSQLScript(SQLiteDatabase database, string dbname){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte buf[] = new byte[1024];
AssetManager assetManager = context.getAssets();
InputStream inputStream =
inputStream = assetManager.open(dbname);
while ((len = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, len);
outputStream.close();
inputStream.close();
String[] createScript = outputStream.toString().split(&;&);
for (int i = 0; i & createScript. i++) {
String sqlStatement = createScript[i].trim();
// TODO You may want to parse out comments here
if (sqlStatement.length() & 0) {
database.execSQL(sqlStatement + &;&);
} catch (IOException e){
// TODO Handle Script Failed to Load
} catch (SQLException e) {
// TODO Handle Script Failed to Execute
如果是为了创建一个简单数据库的话,这种方法比简单的逐行执行SQL语句来的复杂的多,但是一旦数据库结果变得更复杂或者你想预先写好创建脚本时,这种方法将会使你获益良多。同时你也看到我将执行SQL语句的代码抽象到了一个独立executeSQLScript方法中,这样它可以在其他情况下复用,这一点我在本文后面的代码会证实到的。
(【译者注】这里原作者没有说创建了DB类以后该如何使用DB类来创建数据库,但既然这是个入门级的说明文,应该假设读者没有使用过SQLite,在下一小节的代码中有这样的代码:
DB db = new DB(this);
SQLiteDatabase qdb = db.getReadableDatabase();
也可以使用getWritableDatabase(); 得到可以写入的数据库,这里Android会自己根据需要创建数据库,如果数据库文件不存在的话就创建,如果数据库文件已经存在的话,那么Android自己会根据数据库文件中的version信息和DB类构造函数中传入的Version信息对比,如果值不一样的话,会自己调用onUpgrade()方法更新数据库。因此onCreate方法和onUpgrade方法都不是由用户手动调用的。
这两句话可以加在Activity的onCreate方法中,或者onResume方法也是个不错的选择,执行完这句话后,在Android系统的/data/data/package_name/databases目录下就可以看到你创建的数据库文件,文件名就是上面代码中的DB_NAME的值,用DDMS拿出来以后可以用图形化工具打开看看,图形化工具可以使用有免费开源的sqliteman,在/page/4.html下载;另外还有firefox的插件:sqlite manager可以使用)
与数据库交互
现在数据库已经创建好了,下面我想和它进行交互。一个简单的操作步骤:
第一步是打开数据库,有两种方法可以做到这点:使用getReadableDatabase()方法或者getWritableDatabase()方法。前者速度快,占用资源少,可以用来做除了写数据库和修改数据库之外的所有操作;后者主要用来做insert, update等操作。
在Android系统中,查询结果集作为一个Cursor对象返回,可以调用query()或者rawQuery()方法执行一次查询,例如,下面的两个方法返回的结果完全相同:
DB db = new DB(this);
SQLiteDatabase qdb = db.getReadableDatabase();
Cursor recordset1 = ?qdb.query(&mytable&, null, null, null, null, null, null);
Cursor recordset2 = qdb.rawQuery(&SELECT * FROM mytable&, null);
第一个查询调用使用了一堆参数,他们分别是数据表名称,一个列名数组, WHERE子句,选择参数数组,GROUP BY子句,HAVING子句以及ORDER BY子句。可以注意到,把这么多参数都设置为null,其作用和你用通配符代替这些参数的效果是一样的,如果你不需要给这些参数赋值,你干脆就不要用这种包含那么多参数的方法。
这里大多数参数对于熟悉SQL语句的人来说相当的直白。不过这个选择参数数组需要一点点说明,它是一个字符串数组,在查询方法中,WHERE子句中可以包含‘?’,然后在查询时,所有问号依次被选择参数数组中的值替换,比如选择参数数组中的第一个值替换掉WHERE子句中第一个‘?’。
再看看rawQuery()方法,它只需要两个参数,第一个是SQL查询语句,第二个是选择参数数组-它的作用和query方法中的一样。选择参数数组一般和复杂的查询一起使用,比如说使用到JOIN操作的时候。(【译者注】这里原作者说到做连接操作,我们知道sqlite仅支持左连接,而且当left join时,连接条件不在where子句中,因此这里应该指select x1, x2 from tables1, tables 2 where table1.?=tables2.? 这样的连接操作。实际上,我觉得这样的设计应该是为了不需要每次查询都要拼接查询语句字符串,比如说写个select * from tablename where name = ?, 这个?每次查询都不一样,这样可以通过选择参数数组中的值来替换?,而不需要每次都”select * from tablename where name = ‘“+Michael+”’” 这样拼接字符串,Java中拼接字符串的代价是比较高的,特别是查询条件比较多的时候,连续的几次字符串+操作会创建一堆String对象)。
数据库更新(Upgrades)
再回到数据库管理上来,让我们看一下有点小复杂的情况,数据库更新。经过一段时间的使用和开发,应用程序往往会发生变化,也许会添加新的功能,也许做了某些优化。这些变化也许需要数据库结构发生变化,并且在数据库更新代码中,通过数据库版本这个值来反映出这次更新。
在更新数据库时有个潜在问题,那就是有可能导致先前版本的数据库数据丢失。另外,一旦我们的应用版本超过了两个,我们不能武断的假设用户总是已经更新到最新的版本了,比如说现在发布的版本是version3.0,那么不能假设所有用户都已经更新到version2.0了,所以我们的更新操作不能是简单的从一个版本更新到下一本版本。
那么怎么处理这种问题呢?我们已经知道当有一个新版本数据库的时候,onUpgrade()方法就会被调用,所以理想的做法是我们在这个方法中判断数据库版本,决定执行一段或者多段更新脚本。
让我们看一下我们的例子中,在2.0版本中要做哪些修改:
电话号码格式标准化(分机号,手机号),将电话号码存到一张独立的“numbers”数据表。
在雇员数据表中增加一个薪水字段。
以版本1数据库为更新点,可以通过下面的SQL脚本实现更新数据库操作,并且将原来版本中的数据导入到新版本数据库中。
CREATE TABLE numbers (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
employid INTEGER NOT NULL,
number TEXT NOT NULL,
ntype INTEGER NOT NULL DEFAULT &#039;0&#039;
CREATE INDEX employid ON numbers(employid);
INSERT INTO numbers (employid, number, ntype) SELECT _id, ext, 0? FROM
INSERT INTO numbers (employid, number, ntype) SELECT _id, mob, 1? FROM
CREATE TABLE temp (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
salary INTEGER NOT NULL DEFAULT &#039;0&#039;
INSERT INTO temp (_id, name) SELECT _id, name FROM
DROP TABLE
ALTER TABLE temp RENAME TO
显然,对数据库结构的修改越复杂,SQL脚本写的就越复杂。同时在对SQL的支持上,SQLite与其他数据库相比有更多限制,因此有些时候你需要做些workaround绕过这些限制,举个例子,在上面的更新(update)脚本中,我不得不创建一张临时表作为SQLite不支持DROP COLUMN语句的workaround。
现在已经有更新(upgrade)数据库的SQL脚本了,下一步是就如何在onUpgrade方法中调用SQL脚本,下面给一个实现方法:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion & oldVersion) {
switch (oldVersion) {
executeSQLScript(database, &update_v2.sql&);
executeSQLScript(database, &update_v3.sql&);
在这段代码中有两个地方值得注意。:第一件是我在代码里判断新数据库的版本号是不是比旧数据库版本号大,因为onUpdategrade()方法只要两个版本不一致时都会被调用,所以也有可能导致回滚到旧版本的情况。我们的代码不希望看到这种情况,但是实际上应该考虑这种情况并且加上相应的处理代码。
第二件是在case分支语句中没有break。这是因为每个分支中的SQL语句都是简单的从一个版本更新(updates)到下一个版本,这样的话,如果要从版本1更新(upgrade)到版本3的时候,首先会执行从版本1更新(upgrade)到版本2的脚本,然后再执行从版本2更新(upgrade)到版本3的脚本。如果数据库已经是版本2了,那么只会执行版本2到版本3的更新(upgrade)脚本。
这样的话,每次你更新(upgrade)数据库时,只需要考虑从最近的版本更新到新版本需要对数据库结构做哪些修改,写出相应脚本,就可以处理从任何一个版本升级的情况了。当然,在Java代码中我们还需要更新DB_VERION的值还有其他受数据库结构变化影响的代码。
在Android平台的数据持久化方法中,SQLite有着很好数据存储和管理能力,是个不错的选择。然而,和使用其他任何一种数据库一样,需要小心管理,特别是当数据库结构发生变化时更需要小心再小心。
最后,将部分应用逻辑写到SQL脚本中(【译者注】比如在本文例子中从旧数据库中读取数据存到新数据表中这些操作都是用SQL脚本完成的)并且保存到文件中是个简单高效的方法。这种方法让开发人员不需要在程序中写非常复杂的代码处理每次更新(upgrade),可以将主要精力放在应用的业务逻辑上。
英文原文:,编译: -
译文地址:
【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】
关于作者:
一个不甘寂寞,热爱Java技术,喜欢Android平台,略懂密码技术,技术兴趣广泛,有些闷骚的胖子码农.
可能感兴趣的文章
ConcurrentHashMap感觉可以完全代替么。。
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
&#8211; 好的话题、有启发的回复、值得信赖的圈子
&#8211; 写了文章?看干货?去头条!
&#8211; 为IT单身男女服务的征婚传播平台
&#8211; 优秀的工具资源导航
&#8211; 活跃 &#038; 专业的翻译小组
&#8211; 国内外的精选博客文章
&#8211; UI,网页,交互和用户体验
&#8211; JavaScript, HTML5, CSS
&#8211; 专注Android技术分享
&#8211; 专注iOS技术分享
&#8211; 专注Java技术分享
&#8211; 专注Python技术分享
& 2016 ImportNew}

我要回帖

更多关于 sqlite版本升级 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信