android常用的数据保存方式有文件、sharepreferences、数据库、网络、contentprovider集中方式。

文件存储方式,经常使用在缓存整个页面数据,比如电子书内容、html数据等。

sharepreferrences存储方式,实质也就是xml文件存储的封装,常用于存储配置参数数据。当然也可以用文件存储+Properties来存储参数数据。

网络,就是将数据存储在网络空间上。

contentprovider主要作用是统一数据存储方式,实现数据共享,以后有机会仔细分析下。

数据库的方式,常用在存储一系列的结构复杂的数据,轻量级的数据库SQlit使用起来还是比较简单,但是总想能像hibernate似的框架可以进行下封装,实现orm并且可以实现简单的rcud。这里就介绍下一个分装过程,源码下载在后面。

一、封装数据库的类结构

android sqlite数据库封装 实现crud

结构比较简单:BaseBean.java--实体类的基类

DBConfig.java---数据库的参数,包括数据库名、要创建的表列表、数据库版本等

IssContentProvider.java--继承了ContentProvider,定义了数据库的初始化和操作

DBFactory--数据库工厂类

Table.java----定义Table的annotation

TableColumn.java--定义表的列的annotation和属性

TableUtil.java--书库操作工具类,主要是获得表和根据标签拼装sql语句

二、封装数据库的使用过程

由于封装后的数据库使用比较简单,就跟配置好hibernate之后使用似的,所以咱们先看下咱们使用,不理解的地方,等分析了整个实现过程后就行清晰了。

1、要实现orm,肯定要定义带标签的实体类,当然是继承BaseBean类。

2、要将数据库参数传递给DBConfig,并初始化。

3、数据库操作类,通过contentprovideder实现crud。

用例子看下
1,定义实体类

[java] view plain copy 在CODE上查看代码片
  1. publicclassSmartDownloadBeanextendsBaseBean<SmartDownloadBean>{
  2. @TableColumn(type=TableColumn.Types.TEXT,isIndex=true,isNotNull=true)
  3. publicStringdownpath;
  4. @TableColumn(type=TableColumn.Types.INTEGER)
  5. publicintthreadid;
  6. @TableColumn(type=TableColumn.Types.INTEGER)
  7. publicintdownlength;
  8. @Override
  9. publicSmartDownloadBeanparseJSON(JSONObjectjsonObj){
  10. returnnull;
  11. }
  12. @Override
  13. publicJSONObjecttoJSON(){
  14. //TODOAuto-generatedmethodstub
  15. returnnull;
  16. }
  17. @Override
  18. publicSmartDownloadBeancursorToBean(Cursorcursor){
  19. this.downpath=cursor.getString(cursor.getColumnIndex("downpath"));
  20. this.threadid=cursor.getInt(cursor.getColumnIndex("threadid"));
  21. this.downlength=cursor.getInt(cursor.getColumnIndex("downlength"));
  22. returnthis;
  23. }
  24. @Override
  25. publicContentValuesbeanToValues(){
  26. ContentValuesvalues=newContentValues();
  27. if(!TextUtils.isEmpty(downpath)){
  28. values.put("downpath",downpath);
  29. }
  30. if(!TextUtils.isEmpty(threadid+"")){
  31. values.put("threadid",threadid);
  32. }
  33. if(!TextUtils.isEmpty(downlength+"")){
  34. values.put("downlength",downlength);
  35. }
  36. returnvalues;
  37. }
  38. }

实体类通过标签,定义了对应表的列名、及列的属性

2、定义要创建的表、数据名等参数

[java] view plain copy 在CODE上查看代码片
  1. /**
  2. *数据库配置
  3. **/
  4. publicclassSssProviderextendsIssContentProvider{
  5. @Override
  6. publicvoidinit(){
  7. //数据库相关参数设置
  8. DBConfigconfig=newDBConfig.Builder()
  9. .addTatble(SmartDownloadBean.class)
  10. .setName("sss.db").setVersion(2)
  11. .setAuthority("com.sss").build();
  12. IssDBFactory.init(getContext(),config);
  13. }
  14. }

要定义都个表的话,再addTatble(Bean.class)即可。

3、调用数据库的工具类

[java] view plain copy 在CODE上查看代码片
  1. /**
  2. *操作数据库的utils
  3. *
  4. *@authordllik2013-11-23
  5. */
  6. publicclassDBUtils{
  7. publicstaticUriURI_SMARTDOWNLOAD=IssContentProvider.buildUri(SmartDownloadBean.class);
  8. /**
  9. *获取每条线程已经下载的文件长度
  10. *
  11. *@paramcontext
  12. *@paramdownpath
  13. *@return
  14. */
  15. publicstaticMap<Integer,Integer>querySmartDownData(Contextcontext,Stringdownpath){
  16. ContentResolvermResolver=context.getContentResolver();
  17. Cursorcursor=mResolver.query(URI_SMARTDOWNLOAD,null,"downpath=?",newString[]{
  18. downpath
  19. },null);
  20. Map<Integer,Integer>data=newHashMap<Integer,Integer>();
  21. while(cursor.moveToNext()){
  22. SmartDownloadBeanbean=newSmartDownloadBean();
  23. bean.cursorToBean(cursor);
  24. data.put(bean.threadid,bean.downlength);
  25. }
  26. cursor.close();
  27. returndata;
  28. }
  29. /**
  30. *保存每条线程已经下载的文件长度
  31. *
  32. *@paramcontext
  33. *@parampath
  34. *@parammap
  35. */
  36. publicstaticvoidinsertSmartDown(Contextcontext,Stringpath,Map<Integer,Integer>map){
  37. ContentResolvermResolver=context.getContentResolver();
  38. for(Map.Entry<Integer,Integer>entry:map.entrySet()){
  39. SmartDownloadBeanbean=newSmartDownloadBean();
  40. bean.downpath=path;
  41. bean.downlength=entry.getValue();
  42. bean.threadid=entry.getKey();
  43. mResolver.insert(URI_SMARTDOWNLOAD,bean.beanToValues());
  44. }
  45. }
  46. /**
  47. *实时更新每条线程已经下载的文件长度
  48. *
  49. *@paramcontext
  50. *@parampath
  51. *@parammap
  52. */
  53. publicstaticvoidupdateSmartDown(Contextcontext,Stringpath,Map<Integer,Integer>map){
  54. ContentResolvermResolver=context.getContentResolver();
  55. for(Map.Entry<Integer,Integer>entry:map.entrySet()){
  56. SmartDownloadBeanbean=newSmartDownloadBean();
  57. bean.downpath=path;
  58. bean.downlength=entry.getValue();
  59. bean.threadid=entry.getKey();
  60. mResolver.update(URI_SMARTDOWNLOAD,bean.beanToValues(),"downpath=?andthreadid=?",
  61. newString[]{
  62. bean.downpath,bean.threadid+""
  63. });
  64. }
  65. }
  66. /**
  67. *当文件下载完成后,删除对应的下载记录
  68. *
  69. *@paramcontext
  70. *@parampath
  71. */
  72. publicstaticvoiddeleteSmartDown(Contextcontext,Stringpath){
  73. ContentResolvermResolver=context.getContentResolver();
  74. mResolver.delete(URI_SMARTDOWNLOAD,"downpath=?",newString[]{
  75. path
  76. });
  77. }
  78. }

三、数据库的封装过程

看下实体类的基类

[java] view plain copy 在CODE上查看代码片
  1. publicabstractclassBaseBean<T>implementsSerializable{
  2. privatestaticfinallongserialVersionUID=-804757173578073135L;
  3. @TableColumn(type=TableColumn.Types.INTEGER,isPrimary=true)
  4. publicstaticfinalString_ID="_id";
  5. /**
  6. *将json对象转化为Bean实例
  7. *
  8. *@paramjsonObj
  9. *@return
  10. */
  11. publicabstractTparseJSON(JSONObjectjsonObj);
  12. /**
  13. *将Bean实例转化为json对象
  14. *
  15. *@return
  16. */
  17. publicabstractJSONObjecttoJSON();
  18. /**
  19. *将数据库的cursor转化为Bean实例(如果对象涉及在数据库存取,需实现此方法)
  20. *
  21. *@paramcursor
  22. *@return
  23. */
  24. publicabstractTcursorToBean(Cursorcursor);
  25. /**
  26. *将Bean实例转化为一个ContentValues实例,供存入数据库使用(如果对象涉及在数据库存取,需实现此方法)
  27. *
  28. *@return
  29. */
  30. publicabstractContentValuesbeanToValues();
  31. @SuppressWarnings("unchecked")
  32. publicTparseJSON(Gsongson,Stringjson){
  33. return(T)gson.fromJson(json,this.getClass());
  34. }
  35. publicContentValuestoValues(){
  36. ContentValuesvalues=newContentValues();
  37. try{
  38. Class<?>c=getClass();
  39. Field[]fields=c.getFields();
  40. for(Fieldf:fields){
  41. f.setAccessible(true);
  42. finalTableColumntableColumnAnnotation=f.getAnnotation(TableColumn.class);
  43. if(tableColumnAnnotation!=null){
  44. if(tableColumnAnnotation.type()==TableColumn.Types.INTEGER){
  45. values.put(f.getName(),f.getInt(this));
  46. }elseif(tableColumnAnnotation.type()==TableColumn.Types.BLOB){
  47. values.put(f.getName(),(byte[])f.get(this));
  48. }elseif(tableColumnAnnotation.type()==TableColumn.Types.TEXT){
  49. values.put(f.getName(),f.get(this).toString());
  50. }else{
  51. values.put(f.getName(),f.get(this).toString());
  52. }
  53. }
  54. }
  55. }catch(IllegalArgumentExceptione){
  56. e.printStackTrace();
  57. }catch(IllegalAccessExceptione){
  58. e.printStackTrace();
  59. }
  60. returnvalues;
  61. }
  62. }

说明几点:1、用到了泛型,因为定义的数据实体类有多个

2、实现序列化,实体类数据通过Intent进行传递

3、定义了一个主键id

数据库参数类

[java] view plain copy 在CODE上查看代码片
  1. publicclassDBConfig{
  2. finalArrayList<Class<?extendsBaseBean<?>>>tableList;
  3. finalStringdbName;
  4. finalintdbVersion;
  5. finalStringauthority;
  6. finalArrayList<String>tableNameList;
  7. privateDBConfig(finalBuilderbuilder){
  8. tableList=builder.tableList;
  9. dbName=builder.dbName;
  10. dbVersion=builder.dbVersion;
  11. authority=builder.authority;
  12. tableNameList=newArrayList<String>();
  13. for(Class<?extendsBaseBean<?>>c:tableList){
  14. Stringname=TableUtil.getTableName(c);
  15. tableNameList.add(name);
  16. }
  17. }
  18. publicstaticclassBuilder{
  19. privateArrayList<Class<?extendsBaseBean<?>>>tableList;
  20. privateStringdbName;
  21. privateintdbVersion;
  22. privateStringauthority="com.iss.mobile";
  23. publicBuilder(){
  24. tableList=newArrayList<Class<?extendsBaseBean<?>>>();
  25. }
  26. publicBuildersetName(Stringname){
  27. dbName=name;
  28. returnthis;
  29. }
  30. publicBuildersetVersion(intversion){
  31. dbVersion=version;
  32. returnthis;
  33. }
  34. publicBuilderaddTatble(Class<?extendsBaseBean<?>>table){
  35. tableList.add(table);
  36. returnthis;
  37. }
  38. publicBuildersetAuthority(Stringauthority){
  39. this.authority=authority;
  40. returnthis;
  41. }
  42. publicDBConfigbuild(){
  43. returnnewDBConfig(this);
  44. }
  45. }
  46. }

通过该类,来设置数据库的参数,在初始化数据库的时候用到。

内容提供者类,初始化和操作数据库

[java] view plain copy 在CODE上查看代码片
  1. publicabstractclassIssContentProviderextendsContentProvider{
  2. publicstaticStringCONTENT_TYPE="vnd.android.cursor.dir/iss.db";
  3. protectedSQLiteDatabasemDB;
  4. publicstaticStringAUTHORITY="com.iss.mobile";
  5. @Override
  6. publicbooleanonCreate(){
  7. init();
  8. IssDBFactoryissDBFactory=IssDBFactory.getInstance();
  9. DBConfigconfig=IssDBFactory.getInstance().getDBConfig();
  10. if(config==null){
  11. thrownewRuntimeException("dbfactorynotinit");
  12. }
  13. AUTHORITY=config.authority;
  14. CONTENT_TYPE="vnd.android.cursor.dir/"+config.dbName;
  15. mDB=issDBFactory.open();
  16. returntrue;
  17. }
  18. publicabstractvoidinit();
  19. publicstaticfinalStringSCHEME="content";
  20. @Override
  21. publicUriinsert(Uriuri,ContentValuesvalues){
  22. StringtableName=getTableName(uri);
  23. longresult=mDB.insert(tableName,null,values);
  24. if(result!=-1){
  25. getContext().getContentResolver().notifyChange(uri,null);
  26. }
  27. returnbuildResultUri(tableName,result);
  28. }
  29. @Override
  30. publicintbulkInsert(Uriuri,ContentValues[]values){
  31. mDB.beginTransaction();
  32. StringtableName=getTableName(uri);
  33. for(ContentValuesvalue:values){
  34. mDB.insert(tableName,null,value);
  35. }
  36. mDB.setTransactionSuccessful();
  37. mDB.endTransaction();
  38. returnvalues.length;
  39. }
  40. @Override
  41. publicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,
  42. StringsortOrder){
  43. StringtableName=getTableName(uri);
  44. returnmDB.query(tableName,projection,selection,selectionArgs,null,null,sortOrder);
  45. }
  46. @Override
  47. publicStringgetType(Uriuri){
  48. returnCONTENT_TYPE;
  49. }
  50. @Override
  51. publicintdelete(Uriuri,Stringselection,String[]selectionArgs){
  52. StringtableName=getTableName(uri);
  53. intresult=mDB.delete(tableName,selection,selectionArgs);
  54. if(result!=0){
  55. getContext().getContentResolver().notifyChange(uri,null);
  56. }
  57. returnresult;
  58. }
  59. @Override
  60. publicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){
  61. StringtableName=getTableName(uri);
  62. intresult=mDB.update(tableName,values,selection,selectionArgs);
  63. if(result!=0){
  64. getContext().getContentResolver().notifyChange(uri,null);
  65. }
  66. returnresult;
  67. }
  68. privateUribuildResultUri(StringtableName,longresult){
  69. finalUri.Builderbuilder=newUri.Builder();
  70. DBConfigconfig=IssDBFactory.getInstance().getDBConfig();
  71. if(config==null){
  72. thrownewRuntimeException("dbfactorynotinit");
  73. }
  74. builder.scheme(SCHEME);
  75. builder.authority(config.authority);
  76. builder.path(tableName);
  77. builder.appendPath(String.valueOf(result));
  78. returnbuilder.build();
  79. }
  80. privateStringgetTableName(Uriuri){
  81. DBConfigconfig=IssDBFactory.getInstance().getDBConfig();
  82. if(config==null){
  83. thrownewRuntimeException("dbfactorynotinit");
  84. }
  85. Stringpath=uri.getLastPathSegment();
  86. if(!config.tableNameList.contains(path)){
  87. thrownewIllegalArgumentException("UnknownURI"+uri);
  88. }
  89. returnpath;
  90. }
  91. publicstaticUribuildUri(Stringpath,Stringid){
  92. finalUri.Builderbuilder=newUri.Builder();
  93. DBConfigconfig=IssDBFactory.getInstance().getDBConfig();
  94. if(config==null){
  95. thrownewRuntimeException("dbfactorynotinit");
  96. }
  97. builder.scheme(SCHEME);
  98. builder.authority(config.authority);
  99. builder.path(path);
  100. builder.appendPath(id);
  101. returnbuilder.build();
  102. }
  103. publicstaticUribuildUri(Stringpath){
  104. finalUri.Builderbuilder=newUri.Builder();
  105. DBConfigconfig=IssDBFactory.getInstance().getDBConfig();
  106. if(config==null){
  107. thrownewRuntimeException("dbfactorynotinit");
  108. }
  109. builder.scheme(SCHEME);
  110. builder.authority(config.authority);
  111. builder.path(path);
  112. returnbuilder.build();
  113. }
  114. publicstaticUribuildUri(Class<?extendsBaseBean<?>>c){
  115. finalStringtableName=TableUtil.getTableName(c);
  116. returnbuildUri(tableName);
  117. }
  118. }

该内容提供者在创建的时候,先执行init()(将要数据库的参数设置好,再将参数传递给工厂类),工厂类根据参数创建数据库mDB = issDBFactory.open();
数据库工厂类:

[java] view plain copy 在CODE上查看代码片
  1. publicclassIssDBFactory{
  2. privatestaticfinalStringTAG=IssDBFactory.class.getSimpleName();
  3. privateDBConfigmConfig;
  4. privateSQLiteDatabasemSQLiteDB;
  5. privateIssDBOpenHelpermDBOpenHelper;
  6. privatefinalContextmContext;
  7. privatestaticIssDBFactoryinstance;
  8. privateIssDBFactory(Contextcontext){
  9. mContext=context;
  10. }
  11. publicstaticvoidinit(Contextcontext,DBConfigdbConfig){
  12. if(instance==null){
  13. instance=newIssDBFactory(context.getApplicationContext());
  14. instance.setDBConfig(dbConfig);
  15. }
  16. }
  17. publicstaticIssDBFactorygetInstance(){
  18. returninstance;
  19. }
  20. publicvoidsetDBConfig(DBConfigdbConfig){
  21. mConfig=dbConfig;
  22. }
  23. publicDBConfiggetDBConfig(){
  24. returnmConfig;
  25. }
  26. publicSQLiteDatabaseopen(){
  27. if(mSQLiteDB==null){
  28. mDBOpenHelper=newIssDBOpenHelper(mContext,mConfig.dbName,null,mConfig.dbVersion);
  29. mSQLiteDB=mDBOpenHelper.getWritableDatabase();
  30. }
  31. returnmSQLiteDB;
  32. }
  33. publicvoidclose(){
  34. if(mDBOpenHelper!=null){
  35. mDBOpenHelper.close();
  36. }
  37. }
  38. publicvoidbeginTransaction(){
  39. if(mSQLiteDB==null){
  40. mSQLiteDB.beginTransaction();
  41. }
  42. }
  43. publicvoidendTransaction(){
  44. if(mSQLiteDB==null&&mSQLiteDB.inTransaction()){
  45. mSQLiteDB.endTransaction();
  46. }
  47. }
  48. publicvoidsetTransactionSuccessful(){
  49. if(mSQLiteDB==null){
  50. mSQLiteDB.setTransactionSuccessful();
  51. }
  52. }
  53. privatefinalclassIssDBOpenHelperextendsSQLiteOpenHelper{
  54. publicIssDBOpenHelper(Contextcontext,Stringname,CursorFactoryfactory,intversion){
  55. super(context,name,factory,version);
  56. }
  57. @Override
  58. publicvoidonCreate(SQLiteDatabasedb){
  59. for(Class<?extendsBaseBean<?>>table:mConfig.tableList){
  60. try{
  61. for(Stringstatment:TableUtil.getCreateStatments(table)){
  62. Log.d(TAG,statment);
  63. db.execSQL(statment);
  64. }
  65. }catch(Throwablee){
  66. Log.e(TAG,"Can'tcreatetable"+table.getSimpleName());
  67. }
  68. }
  69. /**
  70. *初始化数据
  71. */
  72. //initData();
  73. }
  74. @Override
  75. publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
  76. Log.d(TAG,"onUpgrade:"+oldVersion+">>"+newVersion);
  77. for(Class<?extendsBaseBean<?>>table:mConfig.tableList){
  78. try{
  79. db.execSQL("DROPTABLEIFEXISTS"+TableUtil.getTableName(table));
  80. }catch(Throwablee){
  81. Log.e(TAG,"Can'tcreatetable"+table.getSimpleName());
  82. }
  83. }
  84. onCreate(db);
  85. }
  86. }
  87. publicvoidcleanTable(StringtableName,intmaxSize,intbatchSize){
  88. Cursorcursor=mSQLiteDB.rawQuery("selectcount(_id)from"+tableName,null);
  89. if(cursor.getCount()!=0&&cursor.moveToFirst()&&!cursor.isAfterLast()){
  90. if(cursor.getInt(0)>=maxSize){
  91. intdeleteSize=maxSize-batchSize;
  92. mSQLiteDB.execSQL("deletefrom"+tableName+"where_idin("+"select_idfrom"+tableName
  93. +"orderby_id"+"limit"+deleteSize+")");
  94. }
  95. }
  96. cursor.close();
  97. }

看到这用过数据库的就比较清晰了,用到了SQLiteOpenHelper是以内部类的形式实现的,在oncreat里创建了表,在onupgrade里实现了更新表。

其中用到TableUtil.getCreateStatments(table),

数据库工具类:

[java] view plain copy 在CODE上查看代码片
  1. publicclassTableUtil{
  2. publicstaticStringgetTableName(Class<?extendsBaseBean<?>>c){
  3. Stringname=null;
  4. TabletableNameAnnotation=c.getAnnotation(Table.class);
  5. if(tableNameAnnotation!=null){
  6. name=tableNameAnnotation.name();
  7. }
  8. if(TextUtils.isEmpty(name)){
  9. name=c.getSimpleName();
  10. }
  11. returnname;
  12. }
  13. /**
  14. *拼装sql用的建表语句以及索引语句
  15. *
  16. *@paramc
  17. *@return
  18. */
  19. publicfinalstaticList<String>getCreateStatments(Class<?extendsBaseBean<?>>c){
  20. finalList<String>createStatments=newArrayList<String>();
  21. finalList<String>indexStatments=newArrayList<String>();
  22. finalStringBuilderbuilder=newStringBuilder();
  23. finalStringtableName=getTableName(c);
  24. builder.append("CREATETABLE");
  25. builder.append(tableName);
  26. builder.append("(");
  27. intcolumnNum=0;
  28. for(finalFieldf:c.getFields()){
  29. f.setAccessible(true);
  30. finalTableColumntableColumnAnnotation=f.getAnnotation(TableColumn.class);
  31. if(tableColumnAnnotation!=null){
  32. columnNum++;
  33. StringcolumnName=f.getName();
  34. builder.append(columnName);
  35. builder.append("");
  36. if(tableColumnAnnotation.type()==TableColumn.Types.INTEGER){
  37. builder.append("INTEGER");
  38. }elseif(tableColumnAnnotation.type()==TableColumn.Types.BLOB){
  39. builder.append("BLOB");
  40. }elseif(tableColumnAnnotation.type()==TableColumn.Types.TEXT){
  41. builder.append("TEXT");
  42. }else{
  43. builder.append("DATETIME");
  44. }
  45. if(tableColumnAnnotation.isPrimary()){
  46. builder.append("PRIMARYKEY");
  47. }else{
  48. if(tableColumnAnnotation.isNotNull()){
  49. builder.append("NOTNULL");
  50. }
  51. if(tableColumnAnnotation.isUnique()){
  52. builder.append("UNIQUE");
  53. }
  54. }
  55. if(tableColumnAnnotation.isIndex()){
  56. indexStatments.add("CREATEINDEXidx_"+columnName+"_"+tableName+"ON"
  57. +tableName+"("+columnName+");");
  58. }
  59. builder.append(",");
  60. }
  61. }
  62. builder.setLength(builder.length()-2);//removelast','
  63. builder.append(");");
  64. if(columnNum>0){
  65. createStatments.add(builder.toString());
  66. createStatments.addAll(indexStatments);
  67. }
  68. returncreateStatments;
  69. }
  70. }

就两个方法,获取表名,在更新表的时候用到,拼装sql在创建表时候用到。
最后两个标签类:

[java] view plain copy 在CODE上查看代码片
  1. @Retention(RetentionPolicy.RUNTIME)
  2. public@interfaceTable{
  3. Stringname();
  4. }
[java] view plain copy 在CODE上查看代码片
  1. @Retention(RetentionPolicy.RUNTIME)
  2. public@interfaceTableColumn{
  3. publicenumTypes{
  4. INTEGER,TEXT,BLOB,DATETIME
  5. }
  6. Typestype()defaultTypes.TEXT;
  7. booleanisPrimary()defaultfalse;
  8. booleanisIndex()defaultfalse;
  9. booleanisNotNull()defaultfalse;
  10. booleanisUnique()defaultfalse;
  11. }

思路比较简单,就是注意先标签、过滤器、内容提供者的使用就行了。

最后是封装包代码,使用过程没有加,自己加入吧:http://download.csdn.net/detail/xiangxue336/7001299

更多相关文章

  1. Android 与 JS 交互数据上限问题【Refusing to load URL as it e
  2. 在 Android 应用中使用数据库
  3. Android ListView 滚动条的设置详解及实例代码
  4. android 数据传输之JSON
  5. Android NDK c调用java代码
  6. Android P 以及之后版本不支持同时从多个进程使用具有相同数据目
  7. Android Sqlite数据库跨版本升级 保存之前数据

随机推荐

  1. android开发实战!2021年Android技术下半场
  2. Android自定义属性时TypedArray的使用方
  3. Android历史版本
  4. Android(安卓)R- CarAudioService之regis
  5. git新建本地仓库关联远程仓库(无需移动本
  6. Android之Activity概述
  7. Android(安卓)事件分发
  8. Android(安卓)中文API:Signing Your Appli
  9. Android(安卓)6.0 7.0 8.0特性变更
  10. Android学习笔记——蓝牙入门