一、概述html
Android提供了5种方式来让用户保存持久化应用程序数据。根据本身的需求来作选择,好比数据是不是应用程序私有的,是否能被其余程序访问,须要多少数据存储空间等,分别是:
① 使用SharedPreferences存储数据 java
② 文件存储数据android
③ SQLite数据库存储数据git
④ 使用ContentProvider存储数据github
⑤ 网络存储数据 web
Android提供了一种方式来暴露你的数据(甚至是私有数据)给其余应用程序 - ContentProvider。它是一个可选组件,可公开读写你应用程序数据。sql
二、SharedPreferences存储数据库
SharedPreference类提供了一个整体框架,使您能够保存和检索的任何基本数据类型( boolean, float, int, long, string)的持久键-值对(基于XML文件存储的“key-value”键值对数据)。数组
一般用来存储程序的一些配置信息。其存储在“data/data/程序包名/shared_prefs目录下。浏览器
xml 处理时Dalvik会经过自带底层的本地XML Parser解析,好比XMLpull方式,这样对于内存资源占用比较好。
2.1 咱们能够经过如下两种方法获取SharedPreferences对象(经过Context):
① getSharedPreferences (String name, int mode)
当咱们有多个SharedPreferences的时候,根据第一个参数name得到相应的SharedPreferences对象。
② getPreferences (int mode)
若是你的Activity中只须要一个SharedPreferences的时候使用。
这里的mode有四个选项:
Context.MODE_PRIVATE
该SharedPreferences数据只能被本应用程序读、写。
Context.MODE_WORLD_READABLE
该SharedPreferences数据能被其余应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE
该SharedPreferences数据能被其余应用程序读和写。
Context.MODE_MULTI_PROCESS
sdk2.3后添加的选项,当多个进程同时读写同一个SharedPreferences时它会检查文件是否修改。
2.2 向Shared Preferences中写入值
首先要经过 SharedPreferences.Editor获取到Editor对象;
而后经过Editor的putBoolean() 或 putString()等方法存入值;
最后调用Editor的commit()方法提交;
//Use 0 or MODE_PRIVATE for the default operation SharedPreferences settings = getSharedPreferences("fanrunqi", 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("isAmazing", true); // 提交本次编辑 editor.commit();
同时Edit还有两个经常使用的方法:
editor.remove(String key) :下一次commit的时候会移除key对应的键值对
editor.clear():移除全部键值对
2.3 从Shared Preferences中读取值
读取值使用 SharedPreference对象的getBoolean()或getString()等方法就好了(没Editor 啥子事)。
SharedPreferences settings = getSharedPreferences("fanrunqi", 0); boolean isAmazing= settings.getBoolean("isAmazing",true);
2.4 Shared Preferences的优缺点
能够看出来Preferences是很轻量级的应用,使用起来也很方便,简洁。但存储数据类型比较单一(只有基本数据类型),没法进行条件查询,只能在不复杂的存储需求下使用,好比保存配置信息等。
三、文件数据存储
当文件被保存在内部存储中时,默认状况下,文件是应用程序私有的,其余应用不能访问。当用户卸载应用程序时这些文件也跟着被删除。
文件默认存储位置:/data/data/包名/files/文件名。
① 调用Context的openFileOutput()函数,填入文件名和操做模式,它会返回一个FileOutputStream对象。
② 经过FileOutputStream对象的write()函数写入数据。
③ FileOutputStream对象的close ()函数关闭流。
例如:
String FILENAME = "a.txt"; String string = "fanrunqi"; try { FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close(); } catch (Exception e) { e.printStackTrace(); }
在 openFileOutput(String name, int mode)方法中
name参数: 用于指定文件名称,不能包含路径分隔符“/” ,若是文件不存在,Android 会自动建立它。
mode参数:用于指定操做模式,分为四种:
Context.MODE_PRIVATE = 0
为默认操做模式,表明该文件是私有数据,只能被应用自己访问,在该模式下,写入的内容会覆盖原文件的内容。
Context.MODE_APPEND = 32768
该模式会检查文件是否存在,存在就往文件追加内容,不然就建立新文件。
Context.MODE_WORLD_READABLE = 1
表示当前文件能够被其余应用读取。
MODE_WORLD_WRITEABLE
表示当前文件能够被其余应用写入。
① 调用openFileInput( ),参数中填入文件名,会返回一个FileInputStream对象。
② 使用流对象的 read()方法读取字节
③ 调用流的close()方法关闭流
例如:
String FILENAME = "a.txt"; try { FileInputStream inStream = openFileInput(FILENAME); int len = 0; byte[] buf = new byte[1024]; StringBuilder sb = new StringBuilder(); while ((len = inStream.read(buf)) != -1) { sb.append(new String(buf, 0, len)); } inStream.close(); } catch (Exception e) { e.printStackTrace(); }
其余一些常常用到的方法:
getFilesDir(): 获得内存储文件的绝对路径
getDir(): 在内存储空间中建立或打开一个已经存在的目录
deleteFile(): 删除保存在内部存储的文件。
fileList(): 返回当前由应用程序保存的文件的数组(内存储目录下的所有文件)。
若是你想在应用编译时保存静态文件,应该把文件保存在项目的 res/raw/ 目录下,你能够经过 openRawResource()方法去打开它(传入参数R.raw.filename),这个方法返回一个 InputStream流对象你能够读取文件可是不能修改原始文件。
InputStream is = this.getResources().openRawResource(R.raw.filename);
有时候咱们只想缓存一些数据而不是持久化保存,可使用getCacheDir()去打开一个文件,文件的存储目录( /data/data/包名/cache )是一个应用专门来保存临时缓存文件的内存目录。
当设备的内部存储空间比较低的时候,Android可能会删除这些缓存文件来恢复空间,可是你不该该依赖系统来回收,要本身维护这些缓存文件把它们的大小限制在一个合理的范围内,好比1MB.当你卸载应用的时候这些缓存文件也会被移除
由于内部存储容量限制,有时候须要存储数据比较大的时候须要用到外部存储,使用外部存储分为如下几个步骤:
首先,要在AndroidManifest.xml中加入访问SDCard的权限,以下:
<!-- 在SDCard中建立与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在使用外部存储时咱们须要检测其状态,它可能被链接到计算机、丢失或者只读等。下面代码将说明如何检查状态:
//获取外存储的状态 String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { // 可读可写 mExternalStorageAvailable = mExternalStorageWriteable = true; } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // 可读 } else { // 可能有不少其余的状态,可是咱们只须要知道,不能读也不能写 }
1、若是 API 版本大于或等于8,使用
getExternalFilesDir (String type)
该方法打开一个外存储目录,此方法须要一个类型,指定你想要的子目录,如类型参数DIRECTORY_MUSIC和 DIRECTORY_RINGTONES(传null就是你应用程序的文件目录的根目录)。经过指定目录的类型,确保Android的媒体扫描仪将扫描分类系统中的文件(例如,铃声被肯定为铃声)。若是用户卸载应用程序,这个目录及其全部内容将被删除。
例如:
File file = new File(getExternalFilesDir(null), "fanrunqi.jpg");
2、若是API 版本小于 8 (7或者更低)
getExternalStorageDirectory ()
经过该方法打开外存储的根目录,你应该在如下目录下写入你的应用数据,这样当卸载应用程序时该目录及其全部内容也将被删除
/Android/data/<package_name>/files/
读写数据:
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录 "/sdcard" File saveFile = new File(sdCardDir,"a.txt"); //写数据 try { FileOutputStream fos= new FileOutputStream(saveFile); fos.write("fanrunqi".getBytes()); fos.close(); } catch (Exception e) { e.printStackTrace(); } //读数据 try { FileInputStream fis= new FileInputStream(saveFile); int len =0; byte[] buf = new byte[1024]; StringBuffer sb = new StringBuffer(); while((len=fis.read(buf))!=-1){ sb.append(new String(buf, 0, len)); } fis.close(); } catch (Exception e) { e.printStackTrace(); } }
咱们也能够在 /Android/data/package_name/cache/目录下作外部缓存。
4、 网络存储数据
HttpUrlConnection
HttpUrlConnection是Java.NET包中提供的API,咱们知道Android SDK是基于Java的,因此固然优先考虑HttpUrlConnection这种最原始最基本的API,其实大多数开源的联网框架基本上也是基于JDK的HttpUrlConnection进行的封装罢了,掌握HttpUrlConnection须要如下几个步骤:
一、将访问的路径转换成URL。
URL url = new URL(path);
二、经过URL获取链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
三、设置请求方式。
conn.setRequestMethod(GET);
四、设置链接超时时间。
conn.setConnectTimeout(5000);
五、设置请求头的信息。
conn.setRequestProperty(User-Agent, Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0));
七、针对不一样的响应码,作不一样的操做(请求码200,代表请求成功,获取返回内容的输入流)
工具类:
public class StreamTools { /** * 将输入流转换成字符串 * * @param is * 从网络获取的输入流 * @return */ public static String streamToString(InputStream is) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } baos.close(); is.close(); byte[] byteArray = baos.toByteArray(); return new String(byteArray); } catch (Exception e) { Log.e(tag, e.toString()); return null; } } }
public static String loginByGet(String username, String password) { String path = http://192.168.0.107:8080/WebTest/LoginServerlet?username= + username + &password= + password; try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod(GET); int code = conn.getResponseCode(); if (code == 200) { InputStream is = conn.getInputStream(); // 字节流转换成字符串 return StreamTools.streamToString(is); } else { return 网络访问失败; } } catch (Exception e) { e.printStackTrace(); return 网络访问失败; } }
public static String loginByPost(String username, String password) { String path = http://192.168.0.107:8080/WebTest/LoginServerlet; try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod(POST); conn.setRequestProperty(Content-Type, application/x-www-form-urlencoded); String data = username= + username + &password= + password; conn.setRequestProperty(Content-Length, data.length() + ); // POST方式,其实就是浏览器把数据写给服务器 conn.setDoOutput(true); // 设置可输出流 OutputStream os = conn.getOutputStream(); // 获取输出流 os.write(data.getBytes()); // 将数据写给服务器 int code = conn.getResponseCode(); if (code == 200) { InputStream is = conn.getInputStream(); return StreamTools.streamToString(is); } else { return 网络访问失败; } } catch (Exception e) { e.printStackTrace(); return 网络访问失败; } }
HttpClient是开源组织Apache提供的Java请求网络框架,其最先是为了方便Java服务器开发而诞生的,是对JDK中的HttpUrlConnection各API进行了封装和简化,提升了性能而且下降了调用API的繁琐,Android所以也引进了这个联网框架,咱们再不须要导入任何jar或者类库就能够直接使用,值得注意的是Android官方已经宣布不建议使用HttpClient了。
一、 建立HttpClient对象
二、建立HttpGet对象,指定请求地址(带参数)
三、使用HttpClient的execute(),方法执行HttpGet请求,获得HttpResponse对象
四、调用HttpResponse的getStatusLine().getStatusCode()方法获得响应码
五、调用的HttpResponse的getEntity().getContent()获得输入流,获取服务端写回的数据
public static String loginByHttpClientGet(String username, String password) { String path = http://192.168.0.107:8080/WebTest/LoginServerlet?username= + username + &password= + password; HttpClient client = new DefaultHttpClient(); // 开启网络访问客户端 HttpGet httpGet = new HttpGet(path); // 包装一个GET请求 try { HttpResponse response = client.execute(httpGet); // 客户端执行请求 int code = response.getStatusLine().getStatusCode(); // 获取响应码 if (code == 200) { InputStream is = response.getEntity().getContent(); // 获取实体内容 String result = StreamTools.streamToString(is); // 字节流转字符串 return result; } else { return 网络访问失败; } } catch (Exception e) { e.printStackTrace(); return 网络访问失败; } }
1,建立HttpClient对象
2,建立HttpPost对象,指定请求地址
3,建立List,用来装载参数
4,调用HttpPost对象的setEntity()方法,装入一个UrlEncodedFormEntity对象,携带以前封装好的参数
5,使用HttpClient的execute()方法执行HttpPost请求,获得HttpResponse对象
6, 调用HttpResponse的getStatusLine().getStatusCode()方法获得响应码
7, 调用的HttpResponse的getEntity().getContent()获得输入流,获取服务端写回的数据
public static String loginByHttpClientPOST(String username, String password) { String path = http://192.168.0.107:8080/WebTest/LoginServerlet; try { HttpClient client = new DefaultHttpClient(); // 创建一个客户端 HttpPost httpPost = new HttpPost(path); // 包装POST请求 // 设置发送的实体参数 List parameters = new ArrayList(); parameters.add(new BasicNameValuePair(username, username)); parameters.add(new BasicNameValuePair(password, password)); httpPost.setEntity(new UrlEncodedFormEntity(parameters, UTF-8)); HttpResponse response = client.execute(httpPost); // 执行POST请求 int code = response.getStatusLine().getStatusCode(); if (code == 200) { InputStream is = response.getEntity().getContent(); String result = StreamTools.streamToString(is); return result; } else { return 网络访问失败; } } catch (Exception e) { e.printStackTrace(); return 访问网络失败; } }
参考:
HttpClient和HttpUrlConnection的两种网络访问方式编写网络代码,须要本身考虑不少,获取数据或许能够,可是若是要将手机本地数据上传至网络,根据不一样的web端接口,须要组织不一样的数据内容上传,给手机端形成了很大的工做量。
目前有几种快捷的网络开发开源框架,给咱们提供了很是大的便利。下面是这些项目Github地址,有文档和Api说明。
android-async-http
5、 SQLite数据库存储数据
Assets保存:用来存储一些只读数据,Assets是指那些在assets目录下的文件,这些文件在你将你的应用编译打包以前就要存在,而且能够在应用程序运行的时候被访问到。
但有时候咱们须要对保存的数据进行一些复杂的操做,或者数据量很大,超出了文本文件和Preference的性能能的范围,因此须要一些更加高效的方法来管理,从Android1.5开始,Android就自带SQLite数据库了。
SQLite它是一个独立的,无需服务进程,支持事务处理,可使用SQL语言的数据库。
一、 ACID事务
ACID: 指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
一个支持事务(Transaction)的数据库,必须要具备这四种特性,不然在事务过程(Transaction processing)当中没法保证数据的正确性,交易过程很可能达不到交易方的要求。
二、 零配置 – 无需安装和管理配置
三、储存在单一磁盘文件中的一个完整的数据库
四、数据库文件能够在不一样字节顺序的机器间自由的共享
五、支持数据库大小至2TB
六、 足够小, 大体3万行C代码, 250K
七、比一些流行的数据库在大部分普通数据库操做要快
八、简单, 轻松的API
九、 包含TCL绑定, 同时经过Wrapper支持其余语言的绑定
http://www.sqlite.org/tclsqlite.html
十、良好注释的源代码, 而且有着90%以上的测试覆盖率
十一、 独立: 没有额外依赖
十二、 Source彻底的Open, 你能够用于任何用途, 包括出售它
1三、支持多种开发语言,C,PHP,Perl,Java,ASP.NET,Python
Activites 能够经过 Content Provider 或者 Service 访问一个数据库。
Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须本身建立数据库,而后建立表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你建立一个数据库,你只要继承 SQLiteOpenHelper 类根据开发应用程序的须要,封装建立和更新数据库使用的逻辑就好了。
SQLiteOpenHelper 的子类,至少须要实现三个方法:
public class DatabaseHelper extends SQLiteOpenHelper { /** * @param context 上下文环境(例如,一个 Activity) * @param name 数据库名字 * @param factory 一个可选的游标工厂(一般是 Null) * @param version 数据库模型版本的整数 * * 会调用父类 SQLiteOpenHelper的构造函数 */ public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } /** * 在数据库第一次建立的时候会调用这个方法 * *根据须要对传入的SQLiteDatabase 对象填充表和初始化数据。 */ @Override public void onCreate(SQLiteDatabase db) { } /** * 当数据库须要修改的时候(两个数据库版本不一样),Android系统会主动的调用这个方法。 * 通常咱们在这个方法里边删除数据库表,并创建新的数据库表. */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号 } @Override public void onOpen(SQLiteDatabase db) { // 每次成功打开数据库后首先被执行 super.onOpen(db); } }
继承SQLiteOpenHelper以后就拥有了如下两个方法:
getReadableDatabase() 建立或者打开一个查询数据库
getWritableDatabase() 建立或者打开一个可写数据库
DatabaseHelper database = new DatabaseHelper(context);//传入一个上下文参数 SQLiteDatabase db = null; db = database.getWritableDatabase();
上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就能够查询或者修改数据库。
SQLiteDatabase类为咱们提供了不少种方法,而较经常使用的方法以下:
(int) delete(String table,String whereClause,String[] whereArgs)
删除数据行
(long) insert(String table,String nullColumnHack,ContentValues values)
添加数据行
(int) update(String table, ContentValues values, String whereClause, String[] whereArgs)
更新数据行
(void) execSQL(String sql)
执行一个SQL语句,能够是一个select或其余的sql语句
(void) close()
关闭数据库
(Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
查询指定的数据表返回一个带游标的数据集。
各参数说明:
table:表名称
colums:列名称数组
selection:条件子句,至关于where
selectionArgs:条件语句的参数数组
groupBy:分组
having:分组条件
orderBy:排序类
limit:分页查询的限制
Cursor:返回值,至关于结果集ResultSet
(Cursor) rawQuery(String sql, String[] selectionArgs)
运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别就是防止SQL注入)
当你完成了对数据库的操做(例如你的 Activity 已经关闭),须要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库链接。
为了建立表和索引,须要调用 SQLiteDatabase 的 execSQL() 方法来执行 DDL 语句。若是没有异常,这个方法没有返回值。
例如,你能够执行以下代码:
db.execSQL("CREATE TABLE user(_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT);");
这条语句会建立一个名为 user的表,表有一个列名为 _id,而且是主键,这列的值是会自动增加的整数。另外还有两列:username( 字符 ) 和 password( 字符 )。 SQLite 会自动为主键列建立索引。
一般状况下,第一次建立数据库时建立了表和索引。要 删除表和索引,须要使用 execSQL() 方法调用 DROP INDEX 和 DROP TABLE 语句。
有两种方法能够给表添加数据。
①可使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于全部不返回结果的 SQL 语句。例如:
String sql = "insert into user(username,password) values ('finch','123456');//插入操做的SQL语句 db.execSQL(sql);//执行SQL语句
②使用 SQLiteDatabase 对象的 insert()。
ContentValues cv = new ContentValues(); cv.put("username","finch");//添加用户名 cv.put("password","123456"); //添加密码 db.insert("user",null,cv);//执行插入操做
①使用SQLiteDatabase 对象的 update()方法。
ContentValues cv = new ContentValues(); cv.put("password","654321");//添加要更改的字段及内容 String whereClause = "username=?";//修改条件 String[] whereArgs = {"finch"};//修改条件的参数 db.update("user",cv,whereClause,whereArgs);//执行修改
该方法有四个参数:
表名;
列名和值的 ContentValues 对象;
可选的 WHERE 条件;
可选的填充 WHERE 语句的字符串,这些字符串会替换 WHERE 条件中的“?”标记,update() 根据条件,更新指定列的值.
②使用execSQL方式的实现
String sql = "update [user] set password = '654321' where username="finch";//修改的SQL语句 db.execSQL(sql);//执行修改
①使用SQLiteDatabase 对象的delete()方法。
String whereClause = "username=?";//删除的条件 String[] whereArgs = {"finch"};//删除的条件参数 db.delete("user",whereClause,whereArgs);//执行删除
②使用execSQL方式的实现
String sql = "delete from user where username="finch";//删除操做的SQL语句 db.execSQL(sql);//执行删除操做
①使用 rawQuery() 直接调用 SELECT 语句
Cursor c = db.rawQuery("select * from user where username=?",new Stirng[]{"finch"}); if(cursor.moveToFirst()) { String password = c.getString(c.getColumnIndex("password")); }
返回值是一个 cursor 对象,这个对象的方法能够迭代查询结果。
若是查询是动态的,使用这个方法就会很是复杂。例如,当你须要查询的列在程序编译的时候不能肯定,这时候使用 query() 方法会方便不少。
②经过query实现查询
query() 方法用 SELECT 语句段构建查询。
SELECT 语句内容做为 query() 方法的参数,好比:要查询的表名,要获取的字段名,WHERE 条件,包含可选的位置参数,去替代 WHERE 条件中位置参数的值,GROUP BY 条件,HAVING 条件。
除了表名,其余参数能够是 null。因此代码可写成:
Cursor c = db.query("user",null,null,null,null,null,null);//查询并得到游标 if(c.moveToFirst()){//判断游标是否为空 for(int i=0;i<c.getCount();i++){ c.move(i);//移动到指定记录 String username = c.getString(c.getColumnIndex("username"); String password = c.getString(c.getColumnIndex("password")); } }
无论你如何执行查询,都会返回一个 Cursor,这是 Android 的 SQLite 数据库游标,使用游标,你能够:
经过使用 getCount() 方法获得结果集中有多少记录;
经过 moveToFirst(), moveToNext(), 和 isAfterLast() 方法遍历全部记录;
经过 getColumnNames() 获得字段名;
经过 getColumnIndex() 转换成字段号;
经过 getString(),getInt() 等方法获得给定字段当前记录的值;
经过 requery() 方法从新执行查询获得游标;
经过 close() 方法释放游标资源;
例如,下面代码遍历 user表:
Cursor result=db.rawQuery("SELECT _id, username, password FROM user"); result.moveToFirst(); while (!result.isAfterLast()) { int id=result.getInt(0); String name=result.getString(1); String password =result.getString(2); // do something useful with these result.moveToNext(); } result.close();