Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >sqlite在Android上的一个bug:SQLiteCantOpenDatabaseException when nativeExecuteForCursorWindow

sqlite在Android上的一个bug:SQLiteCantOpenDatabaseException when nativeExecuteForCursorWindow

作者头像
梦里茶
发布于 2018-10-09 03:19:45
发布于 2018-10-09 03:19:45
88200
代码可运行
举报
文章被收录于专栏:梦里茶室梦里茶室
运行总次数:0
代码可运行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  

12-14 19:51:30.346 17770-18098/com.company.product W/System.err: com.company.product.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file (code 14)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:913)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:819)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:159)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:147)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.AbstractCursor.moveToPosition(AbstractCursor.java:218)

12-14 19:51:30.346 17770-18098/com.company.product W/System.err:     at com.company.product.database.sqlite.AbstractCursor.moveToFirst(AbstractCursor.java:258)

先给出结论,

这是sqliteAndroid系统上的一个bug,在需要建立索引的sql语句频繁执行时,会发生这个异常。

(如果你是在SQLiteDatabase执行open()时看到的这个exception,那应该是线程冲突的问题,跟这篇文章讲的不是同一个)

根本原因是sqlite临时文件目录不可用。

解决方案是第一次建立连接时设置临时文件目录。

在项目里遇到了这样一个奇怪的crash,长期占据各个版本crash上报榜首,但在开发中一直不能重现。

在许多查DB的代码路径里,都会在moveToFirst(),getCount()等需要执行fillWindow的地方出现这个crash。

网络上的解决方案:

谷歌搜索SQLiteCantOpenDatabaseException,多是一些执行SQLiteDatabase open()时线程冲突的问题,与我们这个问题不同。

跟这个问题相关的回答屈指可数,一直没找到解决方案,最相关的两种回答来自github:

https://github.com/Raizlabs/DBFlow/issues/380

https://github.com/dxopt/OpenFilesLeakTest/blob/master/bugs-show/AbstractCursor.moveToFirst.md

第一个链接与我们的情况相符,但是没有根本的解决方案,只有try – catch

第二个链接讲的是FD泄露导致打不开文件,于是我排查了app中各种泄露的地方,并且写了一个计算文件句柄数的上报工具,发现用户发生此类crash时,FD都不超过256,低于系统对单个进程默认FD数量1024的限制。排除这个可能。

(但有些时候也有可能是由这个问题引发的,可以用StrictMode detectLeak去排查)

于是先尝试在一些可能触发这个Exception的地方try-catch

再分析用户日志,发现try – catch住这个Exception后是可以继续执行一些DB查询的,

于是全都上了try – catch

重现路径

分析用户日志,发现用户的一些共性,由于业务保密限制这里总结一下,共性是DB中数据量很大,并且查询中有大量的子查询。

于是尝试重现这个问题:

在数据量很大的情况下,多次查询就会重现。

可以重现的话就可以开始打log了。

为了在sqlite native层打log,编译sqlite,使用sqlite3_log来输出自己想观察的信息。

首先我们可以看到sqlite的log

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
12-14 19:51:30.346 17770-18098/com.company.package E/SQLiteLog: (14) cannot open file at line 32440 of [bda77dda96]

12-14 19:51:30.346 17770-18098/com.company.package E/SQLiteLog: (14) os_unix.c:32440: (30) open(./etilqs_3P2SKRP0Ge6cj3T) -

12-14 19:51:30.346 17770-18098/com.company.package E/SQLiteLog: (14) statement aborts at 180: [SELECT M.*,…………………

可以看到是打开一个”./etilqs_3P2SKRP0Ge6cj3T”的文件时打开失败。

先查查这个临时文件是什么鬼,

在sqlite3.c搜索前缀etilqs_里可以看到这样的注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
** Temporary files are named starting with this prefix followed by 16 random
** alphanumeric characters, and no file extension. They are stored in the
** OS's standard temporary file directory, and are deleted prior to exit.
** If sqlite is being embedded in another program, you may wish to change the
** prefix to reflect your program's name, so that if your program exits
** prematurely, old temporary files can be easily identified. This can be done
** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
**
** 2006-10-31:  The default prefix used to be "sqlite_".  But then
** Mcafee started using SQLite in their anti-virus product and it
** started putting files with the "sqlite" name in the c:/temp folder.
** This annoyed many windows users.  Those users would then do a 
** Google search for "sqlite", find the telephone numbers of the
** developers and call to wake them up at night and complain.
** For this reason, the default name prefix is changed to be "sqlite" 
** spelled backwards.  So the temp files are still identified, but
** anybody smart enough to figure out the code is also likely smart
** enough to know that calling the developer will not help get rid
** of the file.
*/
#ifndef SQLITE_TEMP_FILE_PREFIX
# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
#endif

总之就是临时文件就对了。

临时文件源码追踪

然后找找这个东西在哪里用的,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
** Create a temporary file name in zBuf.  zBuf must be allocated
** by the calling process and must be big enough to hold at least
** pVfs->mxPathname bytes.
*/
static int unixGetTempname(int nBuf, char *zBuf){
  static const unsigned char zChars[] =
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789";
  unsigned int i, j;
  const char *zDir;

  /* It's odd to simulate an io-error here, but really this is just
  ** using the io-error infrastructure to test that SQLite handles this
  ** function failing. 
  */
  SimulateIOError( return SQLITE_IOERR );

  zDir = unixTempFileDir();
  if( zDir==0 ) zDir = ".";

  /* Check that the output buffer is large enough for the temporary file 
  ** name. If it is not, return SQLITE_ERROR.
  */
  if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){
    return SQLITE_ERROR;
  }

  do{
    sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
    j = (int)strlen(zBuf);
    sqlite3_randomness(15, &zBuf[j]);
    for(i=0; i<15; i++, j++){
      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
    }
    zBuf[j] = 0;
    zBuf[j+1] = 0;
  }while( osAccess(zBuf,0)==0 );
  return SQLITE_OK;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
这里可以留意到一个神奇的东西

zDir = unixTempFileDir();

if( zDir==0 ) zDir = ".";

我们的文件是 ./etilqs_3P2SKRP0Ge6cj3T

所以unixTempFileDir()确实是返回了0

那再看下unixTempFileDir();

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
static const char *unixTempFileDir(void){
  static const char *azDirs[] = {
     0,
     0,
     0,
     "/var/tmp",
     "/usr/tmp",
     "/tmp",
     0        /* List terminator */
  };
  unsigned int i;
  struct stat buf;
  const char *zDir = 0;

  azDirs[0] = sqlite3_temp_directory;
  if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
  if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
  for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
    if( zDir==0 ) continue;
    if( osStat(zDir, &buf) ) continue;
    if( !S_ISDIR(buf.st_mode) ) continue;
    if( osAccess(zDir, 07) ) continue;
    break;
  }
  return zDir;
}

azDirs0是sqlite3_temp_directory,我们没有设置过,

azDirs1和2是环境变量,用sqlite3_log打出来是

即环境变量里没有设置这两个值,

而另外三个目录/var/tmp,/usr/tmp,/tmp在Android系统里都是应用不可写的,

所以会返回0给unixGetTemp,

于是unixGetTemp使用了”.”作为临时文件的目录,

那”.”是哪个目录呢?

使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
system(“ls . >  /sdcard/0.txt”);

结果是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
acct
adb_keys
cache
config
d
data
default.prop
dev
etc
firmware
fstab.qcom
init
init.goldfish.rc
init.qcom.class_core.sh
init.qcom.class_main.sh
init.qcom.rc
init.qcom.sh
init.qcom.usb.rc
init.qcom.usb.sh
init.rc
init.target.rc
init.trace.rc
init.usb.rc
mnt
persist
proc
root
sbin
sdcard
storage
storage_int
sys
system
tombstones
ueventd.goldfish.rc
ueventd.qcom.rc
ueventd.rc
vendor

这特么是根目录!当前工作目录是根目录我也是醉了。。。

所以在根目录创建临时文件一定会失败!

etilqs临时文件创建时机

那为什么平时使用都是正常的呢?

找一找这个临时文件的创建时机:

在unixGetTempname函数里,人为地造一个crash,通过crash堆栈配合addr2line来查看调用栈:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
12-19 21:00:45.633 13680-14105/com.company.package E/SQLiteLog: (14) pagerstress;/data/data/com.company.package/databases/push
12-19 21:00:45.633 13680-14105/com.company.package E/SQLiteLog: (14) pager_write_pagelist
12-19 21:00:46.083 3727-3727/? I/DEBUG:     #00  pc 00037202  /data/app-lib/com.company.package-1/libqmsqlite.so unixGetTempname 32107
12-19 21:00:46.083 3727-3727/? I/DEBUG:     #01  pc 000376a7  /data/app-lib/com.company.package-1/libqmsqlite.so unixOpen 32396
12-19 21:00:46.083 3727-3727/? I/DEBUG:     #02  pc 00015ec5  /data/app-lib/com.company.package-1/libqmsqlite.so sqlite3OsOpen 17420
12-19 21:00:46.083 3727-3727/? I/DEBUG:     #03  pc 0003a16b  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #04  pc 0003e0c7  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #05  pc 00038e75  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #06  pc 00038f55  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #07  pc 00039445  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #08  pc 0003add1  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #09  pc 0003c1f1  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #10  pc 0003d8df  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #11  pc 0004c2e7  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #12  pc 0004e317  /data/app-lib/com.company.package-1/libqmsqlite.so (sqlite3_step+334)
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #13  pc 00063ebd  /data/app-lib/com.company.package-1/libqmsqlite.so (sqlite3_blocking_step+6)
12-19 21:00:46.093 3727-3727/? I/DEBUG:     #14  pc 00012279  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.103 3727-3727/? I/DEBUG:          61e75c04  61ced1f7  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.103 3727-3727/? I/DEBUG:          61e75c24  61ced6ab  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.103 3727-3727/? I/DEBUG:          61e75c50  61d71f4c  /data/app-lib/com.company.package-1/libqmsqlite.so
12-19 21:00:46.113 3727-3727/? I/DEBUG:          61e7610c  61cf016f  /data/app-lib/com.company.package-1/libqmsqlite.so

使用addr2line –C –f –e 加上面14个pc地址,结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pagerOpentemp
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:46566
pagerStress
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:47482
sqlite3PcacheFetchStress
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:40751
btreeGetPage
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:56428
btreeGetUnusedPage
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:56556
allocateBtreePage
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:60283
balance_nonroot
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:61869
sqlite3BtreeInsert
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:62554
sqlite3VdbeExec
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:77746 (discriminator 3)
sqlite3Step
/media/Software/company/qmsqlite/jni/sqlite/sqlite3.c:71550
sqlite3_blocking_step
/media/Software/company/qmsqlite/jni/sqlite/sqlite3_unlock_notify.c:85 (discriminator 1)
nativeExecuteForCursorWindow
/media/Software/company/qmsqlite/jni/sqlite/SQLiteConnection.cpp:994

整理了一发流程图如下:

懒得看图的童鞋还是听我说吧,

先看sqlite的architecture

因为我们crash的地方是查DB的地方,所以拿query操作来解释这个architecture是怎么运行的

先用SQL Command Processor解析sql语句,变成类似汇编的命令给Virtual Machine执行,

我们可以用explain plan select …. 这样的语句来查看virtual machine要执行的命令,比如

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
explain plan select * from A where A.a in (select b from B)

对应的命令是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 0| Trace| 0| 0| 0| | 00
 1| Goto| 0| 56| 0| | 00
 2| OpenRead| 0| 4| 0| 13| 00
 3| Rewind| 0| 54| 0| | 00
 4| null| 0| 1| 0| | 00
 5| Once| 0| 17| 0| | 00
 6| null| 0| 1| 0| | 00
 7| OpenEphemeral| 4| 1| 0| keyinfo(1,BINARY)| 00
 8| Integer| 10000| 2| 0| | 00
 9| OpenRead| 1| 5| 0| 1| 00
 10| Rewind| 1| 16| 0| | 00
 11| Column| 1| 0| 3| | 00
 12| MakeRecord| 3| 1| 4| b| 00
 13| IdxInsert| 4| 4| 0| | 00
 14| IfZero| 2| 16| -1| | 00
 15| Next| 1| 11| 0| | 01
 16| Close| 1| 0| 0| | 00
 17| Column| 0| 0| 4| | 00
 18| IsNull| 4| 22| 0| | 00
 19| Affinity| 4| 1| 0| b| 00
 20| NotFound| 4| 22| 4| 1| 00
 21| Goto| 0| 39| 0| | 00
 22| null| 0| 5| 0| | 00
 23| Once| 1| 35| 0| | 00
 24| null| 0| 5| 0| | 00
 25| OpenEphemeral| 6| 1| 0| keyinfo(1,BINARY)| 00
 26| Integer| 10000| 6| 0| | 00
 27| OpenRead| 2| 5| 0| 12| 00
 28| Rewind| 2| 34| 0| | 00
 29| Column| 2| 11| 7| | 00
 30| MakeRecord| 7| 1| 4| b| 00
 31| IdxInsert| 6| 4| 0| | 00
 32| IfZero| 6| 34| -1| | 00
 33| Next| 2| 29| 0| | 01
 34| Close| 2| 0| 0| | 00
 35| Column| 0| 1| 4| | 00
 36| IsNull| 4| 53| 0| | 00
 37| Affinity| 4| 1| 0| b| 00
 38| NotFound| 6| 53| 4| 1| 00
 39| Column| 0| 0| 8| | 00
 40| Column| 0| 1| 9| | 00
 41| Column| 0| 2| 10| | 00
 42| Column| 0| 3| 11| | 00
 43| Column| 0| 4| 12| | 00
 44| Column| 0| 5| 13| | 00
 45| Column| 0| 6| 14| | 00
 46| Column| 0| 7| 15| | 00
 47| Column| 0| 8| 16| | 00
 48| Column| 0| 9| 17| | 00
 49| Column| 0| 10| 18| | 00
 50| Column| 0| 11| 19| | 00
 51| Column| 0| 12| 20| | 00
 52| ResultRow| 8| 13| 0| | 00
 53| Next| 0| 4| 0| | 01
 54| Close| 0| 0| 0| | 00
 55| Halt| 0| 0| 0| | 00
 56| Transaction| 0| 0| 0| | 00
 57| VerifyCookie| 0| 3| 0| | 00
 58| TableLock| 0| 4| 0| labels| 00
 59| TableLock| 0| 5| 0| Items| 00
 60| Goto| 0| 2| 0| | 00
 

可以看到其中需要建立索引,IdxInsert,于是在sqlite3VdbeExec中会进入

OP_IdxInsert分支,然后

  会调用sqlite3BtreeInsert,向B树中插入一个节点,

  此时如果pPage满了,会执行balance平衡B树,

  在这里面就会btreeGetPage去获取可用的page,

  获取page的过程最终会执行sqlite3_malloc,为page分配空间,一旦分配失败,就会在fetch处触发pBase == 0的条件,

  于是执行sqlite3PcacheFetchStress,在其中调用pager_write_pagelist时触发pPager->fd == 0的条件(因为page在前面没有分配到空间),

  于是触发pagerOpenTemp,往下执行调用unixGetTempname,得到上面所说的那个不正确的文件路径,

  执行sqlite3Osopen时就会失败。

从上面的分析看出,触发这个路径需要几个条件:

  1. 执行的sql语句需要建立索引,
  2. B树不平衡
  3. 没有设置过环境变量
  4. 分配的内存不足以新建新的page

所以触发条件还是比较严格的。

在unixOpenTempname执行时用一个变量计算临时文件的打开次数,也可以发现确实是一打开这样的文件就会失败(在打开第一个的时候就失败)。

解决方案(Solution)

那么最重要的事情来了,怎么修复呢?

既然是临时文件的目录没有写权限,那就改目录吧!

翻了翻sqlite的一些资料,找到了这样一个programa

http://www.sqlite.org/c3ref/temp_directory.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PRAGMA temp_store_directory = 'your dir'

这个东西仅对当前SqliteConncetion有效,

在第一次建立sqlite连接的时候(我是重写了getReadabelDatabase()方法),设置一下临时文件目录,like this:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static boolean mainTmpDirSet = false;
@Override
    public SQLiteDatabase getReadableDatabase() {
        if (!mainTmpDirSet) {
            boolean rs = new File("/data/data/com.cmp.pkg/databases/main").mkdir();
            Log.d("ahang", rs + "");
            super.getReadableDatabase().execSQL("PRAGMA temp_store_directory = '/data/data/com.cmp.pkg/databases/main'");
            mainTmpDirSet = true;
            return super.getReadableDatabase();
        } 
        return super.getReadableDatabase();
    }

然后再去执行那些繁重的查询,你会发现问题消失了,

并且sqlite3会在不需要这个临时文件时自动删除它,所以你不需要做一套清理逻辑。

于是问题解决!

(转载请注明出处,

  https://cloud.tencent.com/developer/article/1351745

  如果有什么建议,可以评论或者发邮件给我hellosdk@163.com)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-12-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android 平台 Native 代码的崩溃捕获机制及实现
一、背景 在Android平台,native crash一直是crash里的大头。native crash具有上下文不全、出错信息模糊、难以捕捉等特点,比java crash更难修复。所以一个合格的异常捕获组件也要能达到以下目的: 支持在crash时进行更多扩展操作,如: 打印logcat和应用日志 上报crash次数 对不同的crash做不同的恢复措施 可以针对业务不断改进和适应 二、现有的方案 其实3个方案在Android平台的实现原理都是基本一致的,综合考虑,可以基于coffeecatch改进。
腾讯Bugly
2018/03/23
5.8K0
Android Native Crash 分析
版本发布后,收集到到异常上报,有部分记录到是native crash。 而上报的native信息,无法直接定位到错误位置。
艳龙
2021/12/16
6090
linux下的sqlite3的编译安装和
sqlite是嵌入式SQL数据库引擎SQLite(SQLite Embeddable SQL Database Engine)的一个扩展。 SQLite是一个实现嵌入式SQL数据库引擎小型C语言库(C library),实现了独立的,可嵌入的,零配置的SQL数据库引擎。 特性包括:事务操作是原子,一致,孤立,并且持久的,即使在系统崩溃和电源故障之后。零配置——不需要安装和管理。 实现了绝大多数SQL92标准。整个数据库存储在一个单一的文件中。数据库文件可以在不同字节序的机器之间自由地共享。 支持最大可达2T的数据库。字符串和BLOB类型的大小只受限于可用内存。完整配置的少于250KB,忽略一些可选特性的少于150KB。 在大多数常见操作上比流行的客户/服务器数据库引擎更快。 简单易于使用的API。 内建TCL绑定。 另外提供可用于许多其他语言的绑定。具有良好注释的源代码,代码95%有较好的注释。 独立:没有外部依赖。源代码位于公共域,可用于任何用途。 用 SQLite连接的程序可以使用SQL数据库,但不需要运行一个单独的关系型数据库管理系统进程(separate RDBMS process)。 SQLite不是一个用于连接到大型数据库服务器(big database server)的客户端库(client library), 而是非常适合桌面程序和小型网站的数据库服务器。SQLite直接读写(reads and writes directly)在硬盘上的数据库文件。
py3study
2020/01/09
4.5K0
[Android][Framework]带有so的三方应用集成
集成带有So文件的三方应用时,如果不处理so文件,会导致应用打不开的情况,所以针对这些so文件需要做一些额外的处理。
wOw
2020/01/20
3.2K0
Android注册表_手机注册表文件在哪里
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/04
14.9K0
高通 NXP NFC(PN547PN548) 移植流程 android6.0
首先向NXP 的 fae要android 6.0 bring up的代码,如:NFC_NCIHALx_AR0F.4.3.0_M_NoSE
233333
2018/10/09
3.4K0
高通 NXP NFC(PN547PN548) 移植流程 android6.0
Android分区
实现手机必需的通信功能,大家通常所的刷RADIO就是刷写modem分区,在所有适配的ROM中这部分是不动,否则会造成通话不稳定
233333
2018/08/01
1.4K0
Android分区
Android基础开发实践:如何分析Native Crash
Native Crash常常发生在带有Jni代码的APP中,或者系统的Native服务中。作为比较难分析的一类问题,Native Crash其实还是有较多的方法去定位。
天天P图攻城狮
2018/08/21
18.4K5
Android基础开发实践:如何分析Native Crash
DDL操作提示了一个DML操作才会抛的ORA错误?
某张表,有个字段,存在默认值,并且设置了NOT NULL约束,例如,NEED_PO VARCHAR2(1) default 'N' not null,
bisal
2019/12/20
7060
DDL操作提示了一个DML操作才会抛的ORA错误?
某地理位置模拟APP从壳流程分析到破解
在我们拿到一个APP准备破解时一般得安装运行,程序运行后须要注册用户,随便注册一个用户登录,以下是APP须要购买vip才能使用的大概情况。
我是小三
2018/08/08
1.4K0
某地理位置模拟APP从壳流程分析到破解
Android JNI堆栈分析工具简介
MelonTeam
2018/01/04
2.6K0
Android JNI堆栈分析工具简介
一些快速提高Android开发的脚本与技巧(终端篇)
正所谓“工欲善其事必先利其器”,一个好的工具或者技巧能让提升工作效率,起到事半功倍的效果。在这里斗胆列出一些窃以为一些可能快速提高Android日常开发的脚本,希望可以为大家提供一些好的工具,有帮助的思路。
技术小黑屋
2018/09/05
8230
一些快速提高Android开发的脚本与技巧(终端篇)
【Android】NDK开发Crash分析
手机user版本还是userdebug或是eng版本:adb shell getprop ro.build.type
后端码匠
2022/12/05
1.4K0
【Android】NDK开发Crash分析
Android Tombstone 分析
Tombstone是指在分布式系统中用于标记数据已被删除的记录,通常包含删除操作的时间戳和相关信息。
天天Lotay
2024/03/03
1.5K0
Android Tombstone 分析
Android Native Crash问题排查思路
对于Android APP而言,native层Crash相比于Java层更难捕获与定位,因为so的代码通常不可见,而且,一些第三方so的crash或者系统的更难定位,堆栈信息非常少:参考下面的几个native crash实例
看书的小蜗牛
2021/11/24
2.1K0
Android Native Crash问题排查思路
Android tombstone文件是如何生成的
本节内容我们聚焦到androidQ上,分析android中一个用于debug的功能,那就是tombstone,俗称“墓碑”。现实生活中墓碑一般是给死人准备的,而在android系统中“墓碑”则是给进程准备的。
DragonKingZhu
2020/03/24
5.9K1
Android NDK开发入门
JNI (Java Native Interface英文缩写),译为Java本地接口。是Java众多开发技术中的一门技术,意在利用本地代码,为Java程序提供更高效、更灵活的拓展。尽管Java一贯以其良好的跨平台性而著称,但真正的跨平台非C/C++莫属,因为当前世上90%的系统都是基于C/C++编写的。同时,Java的跨平台是以牺牲效率换来对多种平台的兼容性,因而JNI就是这种跨平台的主流实现方式之一。
xiangzhihong
2022/11/30
1.8K0
Android NDK开发基础
NDK即Native Development Kit,是Android上用来开发c/c++的开发工具包。 安装步骤:developer.android.com/studio/proj…
没关系再继续努力
2021/12/03
2K0
Android逆向分析大全
Android程序的特点相比在于使用混淆方式打包,将包名、类名、函数名改成不易看懂的字母,从而使生成的apk小很多(android studio提供了release编译方式,使用proguard混淆),因此反编译apk最多的工作在于重构这些名称,这一点和pc上一致,对于android native程序(jni)则和pc上基本一致,不同之处在于常见的是arm汇编。
bosh123
2020/12/22
3.7K0
安卓开发_数据存储技术_sqlite
一、SQLite SQLite第一个Alpha版本诞生于2000年5月,它是一款轻量级数据库,它的设计目标是嵌入式的,占用资源非常的低,只需要几百K的内存就够了。SQLite已经被多种软件和产品使用 二、SQLite特性 1 2 1、轻量级 3 SQLite和C\S模式的数据库软件不同,它是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。而且那个动态库的尺寸也相当小。 4 2、独立性 5 SQLite数据库的核心引擎本身不
听着music睡
2018/05/18
8770
相关推荐
Android 平台 Native 代码的崩溃捕获机制及实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验