开发问题:大胆假设,小心求证(Sundy)
推测的一些存储方式和存储介质
1、XML——SharedPreference
2、Binary File——File
3、关系型数据库——SQLite3
4、网络访问——Network(Socket,Http,Https)
SharedPreference
最基本的操作步骤
1、getSharedPreferences 先得到一个SharedPreference
2、get SharedPrenference.Editor
3、Editor.commit()
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
@Override
protected void onCreate(Bundle state){
super.onCreate(state);
. . .
// Restore preferences
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean silent = settings.getBoolean("silentMode", false);
setSilent(silent);
}
@Override
protected void onStop(){
super.onStop();
// We need an Editor object to make preference changes.
// All objects are from android.context.Context
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", mSilentMode);
// Commit the edits!
editor.commit();
}
}
getSharedPreferences方法的:
第一个参数是名字
第二个参数是模式:0, MODE_APPEND,MODE_PRIVATE, MODE_READABLE, MODE_WRITABLE
MODE_APPEND 能够在之前的基础上进行数据的追加,追加根据key决定
MODE_PRIVATE, MODE_READABLE, MODE_WRITABLE 与xml的使用权限有关
edit()和commit()应当成对出现
获得SharedPreference的对象有三种方法:
1、getSharedPreferences(String name, int mode):这个方法第一个参数需要传入保存数据的xml的名字,可以创建多个
因为Activity继承了ContextWrapper,因此也是通过Activity对象获取,但是属于整个应用程序,可以有多个,
以第一参数的name为文件名保存在系统中
2、getPreferences(int mode):每个Activity只能创建一个,xml以Activity的名字来命名
通过Activity对象获取,获取的是本Activity私有的Preference,保存在系统中的xml形式的文件的名称为这个Activity的名字,
因此一个Activity只能有一个,属于这个Activity
3、PreferenceManager.getDefaultSharedPreference(Context context)
Preference的静态函数,保存PreferenceActivity中的设置,属于整个应用程序,但只有一个,
Android会根据包名和PreferenceAcitivity的布局文件来起一个名字保存
PreferenceActivity
是一个专门用于写settings的系统类,自动持久化数据,继承自ListActivity
可以建一个专门用于PreferenceActivity的layout
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="first_preferencescreen">
<CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" />
<PreferenceScreen
android:key="second_preferencescreen"
android:title="WiFi settings">
<CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" />
... other preferences here ...
</PreferenceScreen>
</PreferenceScreen>
PreferenceScreen不是一个界面组件,而也是一个控制类,附加到Activity上
使用的时候Activity要继承PreferenceActivity类,绑定界面的时候用addPreferenceFromResource方法即可
还有一个addPreferenceFromIntent方法,可以通过设定Intent-filter调用系统的设置或者自定义的设置
想要打开系统相关的设置,在Settings中查找相关的Action,设置Intent,传入addPreferenceFromIntent中
或者直接在Preference中直接设置intent标签即可
如果想设置某一项的相关属性,我们发现findViewById的方法是行不通的,因为SharedSreen系列标签中不能指定id
但是有一个findPreference方法,传入的是SharedSreen系列标签中的key,返回的是一个Preference的对象
’‘
返回值为true则不再向下传递,系统也就不自动进行持久化的操作了,可以自行处理。
文件存储
内部存储
保存
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
If you want to save a static file in your application at compile time, save the file in your project res/raw/ directory.
You can open it with openRawResource(), passing the R.raw.<filename> resource ID.
This method returns an InputStream that you can use to read the file (but you cannot write to the original file).
外部存储
一些权限
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
根据是否MOUNT或者UNMOUNT返回外部文件是否可读可写
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
网络存储
最关键学会HttpClient或者HttpRequest类,都是J2SE中的内容
SQLite3
SQLite(Http://www.sqlite.org/),是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,
它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,
在嵌入式设备中,可能只需要几百K的内存就够了
它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如Tcl,C#,PHP,Java等,
还有ODBC接口,同样比起MySQL、PostoreSQL这两款开源世界著名的数据库关系系统来讲,它的处理速度比它们都亏
SQLite第一个Alpha版本诞生于2000年5月,至今已经有10个年头,SQLite也迎来了一个版本SQLite3已经发布
特征:轻量级、独立性、隔离性、跨平台、多语言接口、安全性
与其他关系型数据库相比,是否支持:
1、标准SQL语句
2、视图
3、存储过程Process
4、主外键关系(7中)
5、事务
6、等等
一般数据库执行操作步骤:
1、建立数据库,建立表结构
2、建立数据库链接DB Connection
3、执行SQL语句
(1)executeSQL——一般执行的是没有返回值的SQL语句
(2)query——一般是给select用的,返回查询结果
4、如果有返回值,建立一个RecordSet或者Cursor,操作结果
5、关闭数据库链接
Android推荐我们使用继承SQLiteOpenHelper类进行数据库操作,在继承的类中的onCreate方法中可以执行sql语句
public class DBOpenHelper extends SQLiteOpenHelper {
private final String DATABASE_NAME = "androidDBStorageSqlite";
private final String TABLE_USER_NAME = "users";
private final String TABLE_CUSTOMER_NAME = "customer";
public final String COLUMN_USERNAME = "userName";
public final String COLUMN_USERADDRESS = "userAddress";
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL("create TABLE " + TABLE_USER_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_USERNAME + " VARCHAR(50) NOT NULL," + COLUMN_USERADDRESS + " VARCHAR(50) NOT NULL)");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
if (oldVersion == 1 && newVersion == 2){
sqLiteDatabase.execSQL("create TABLE " + TABLE_CUSTOMER_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_USERNAME + " VARCHAR(50) NOT NULL," + COLUMN_USERADDRESS + " VARCHAR(50) NOT NULL)");
}
else if (oldVersion == 2 && newVersion == 1){
sqLiteDatabase.execSQL("drop TABLE " + TABLE_CUSTOMER_NAME);
}
}
}
其实SQliteOpenHelper只是一个帮助类,封装了一些数据库操作而已,我们也可以自己实现一个这样的帮助类
SQLiteOpenHelper:A helper class to manage database creation and versio management 用于建立数据库和版本控制的帮助类
还有其他相关的helper类,自己研究
onCreate中参数传入的是SQliteDatabase类的对象,查看api可以知道这个类才是 操作数据库的主要的类
使用SQLiteOpenHelper
public class DBOpenHelper extends SQLiteOpenHelper {
private final String DATABASE_NAME = "androidDBStorageSqlite";
private final String TABLE_NAME = "users";
public final String COLUMN_USERNAME = "userName";
public final String COLUMN_USERADDRESS = "userAddress";
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL("create TABLE " + TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_USERNAME + " VARCHAR(50) NOT NULL," + COLUMN_USERADDRESS + " VARCHAR(50) NOT NULL)");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
}
}
其实只是创建数据库和版本管理,可以使用getReadableDatabase或者getWritableDatabase返回SQLiteDatabase数据库而已
调用
DBOpenHelper dbOpenHelper = new DBOpenHelper(this, TABLE_NAME, null, VERSION);
dbOpenHelper.getWritableDatabase();
得到了返回的SQLiteDatabase对象就可以执行相关的增删改查的操作了
自定义的一个数据库操作类
public class DBOperation {
private final String DATABASE_NAME = "androidDBStorageSqlite";
private final String TABLE_NAME = "users";
public final String COLUMN_USERNAME = "userName";
public final String COLUMN_USERADDRESS = "userAddress";
private Context mContext;
private SQLiteDatabase mSqLiteDatabase;
public DBOperation(Context context){
mContext = context;
}
/**
* 建立及打开数据库
*/
public void openOrCreateDatabase()
{
// 建立链接
mSqLiteDatabase = mContext.openOrCreateDatabase(DATABASE_NAME, mContext.MODE_PRIVATE, null);
//SQLiteDatabase也有相关方法 mSqLiteDatabase = SQLiteDatabase.openOrCreateDatabase();
// 建立表
String firstTableName = mSqLiteDatabase.findEditTable(TABLE_NAME);
if (firstTableName == null || firstTableName.equals("")){
mSqLiteDatabase.execSQL("create TABLE " + TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_USERNAME + " VARCHAR(50) NOT NULL," + COLUMN_USERADDRESS + " VARCHAR(50) NOT NULL)");
} else{
Log.i("error", "table exist");
}
}
/**
* 关闭数据库
*/
public void closeDatabase(){
// 关闭数据库链接
mSqLiteDatabase.close();
mSqLiteDatabase = null;
}
/**
* 下面可以封装各种增删改查的方法
*/
/**
* 插入用户
* @param user 用户实例
* @return
*/
public long insertUser(User user)
{
if (mSqLiteDatabase != null && user != null)
{
// 插入一条记录
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_USERNAME, user.getUserName());
contentValues.put(COLUMN_USERADDRESS, user.getUserAddress());
// insert(java.lang.String table, java.lang.String nullColumnHack, android.content.ContentValues values)
return mSqLiteDatabase.insert(TABLE_NAME, "", contentValues);
}
return -1;
}
public Cursor selectAll(String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
{
if (mSqLiteDatabase != null)
{
return mSqLiteDatabase.query(TABLE_NAME, new String[]{"_id", COLUMN_USERNAME, COLUMN_USERADDRESS},
selection, selectionArgs, groupBy, having, orderBy);
}
return null;
}
}
执行一些语句时,可以使用通配符
在adb shell中可以操作数据库
使用sqlite3 + 数据库名称即可进入数据库操作模式
系统命令都是以.开头的
还可以在google查询sqlite tool,使用例如Sqlite Developer,或者sqlitestudio的视图工具
总结:
1、SQLite是一个全面而又完整支持sql语句的文件型数据库
2、SQLite支持事务,视图,索引,触发器,主外键约束
3、默认的必须给每张表的主键名字为_id
4、SqliteDatabase可以用封装的insert,update,delete,query方法,也可以用execSql,rawSql等原生方法
Cursor的相关方法
思考:
SQLite3数据库引擎在哪儿?
如何在Android源码中找到类似于SQLite3的这种开源的源码
1、一般来讲,集成源代码比只调用.so文件更好
2、下载SQLite3的源码
3、通过SQLite3源码中的文件名到Android源码中找寻
这是一种思路
SQLite3的源文件只有四个
所以可以找到SQLite3数据库的引擎
在源码external的sqlite目录下
而external目录下还有许多其他的引擎
思考:
SQLite3链接方式实际是什么?
一定会调用到native方法,而且有相关驱动
封装好的驱动
之前的版本在libcore文件夹下
现在放到了external下javasqlite目录下
可以通过SQLite Studio等工具生成表,创建表,建立主外键链接,创建视图,创建触发器等
将数据库导出为.sql文件,可以选择直导出数据库结构,也可以同时导出数据库数据
将这些sql语句放入到程序交由SQLiteDatabase或SQLiteOpenHelper处理即可
事务:
要么都执行,要么都不执行
mSqliteDatabase.beginTransaction():开始事务
mSqliteDatabase.setTransactionSuccessful():事务成功
mSqliteDatabase.endTransaction():结束事务
在这两个方法之间放入执行事务的语句即可
一般把事务放在try - catch语句中,endTransaction放在finally语句中