Android 开发培训(03)--Android开发核心(二)

来源:互联网 发布:淘宝发货提醒短信模板 编辑:程序博客网 时间:2024/05/17 07:11

第四章 保存数据

许多app程序都要保存数据,即使是app处onPause状态也需要。

很多实用的app会保存用户的设置,以及其它的大量信息,有些还保存在数据库中。

这章介绍以下几个内容

保存键值对在shared preferences文件中

在android系统中保存任意的文件

使用SQLite进行保存数据

1. 使用SharedPreferences类保存小数据

一个SharePreferences对象含有一个键值对,提供简单的方法进行读写操作。

getSharedPreferences()

getPreferences()

函数获取相关的share实例

下面这个例子在一个fragment中执行,

通过R.string.preference_file_key获取一个一个sharedPref实例,这个实例采用MODE_PRIVATE方式,表示只有这个程序才能使用这个文件。

Context context = getActivity();SharedPreferences sharedPref = context.getSharedPreferences(        getString(R.string.preference_file_key), Context.MODE_PRIVATE);
2. 往Shared Preferences中写入数据

需要调用Sharedpreferences.Editor函数

下面这里例子把newHighScore放进去

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPref.edit();editor.putInt(getString(R.string.saved_high_score), newHighScore);editor.commit();
3. 从SharedPreferences中读取数值

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);int defaultValue = getResources().getInteger(R.string.saved_high_score_default);long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);

第五章 保存文件

Android系统的文件系统和其它平台的系统基本上是一样的,主要通过File类完成。

这个类适用于一些比较大的文件,比如网络上交互的图片,以及保存其它比较大的文件。

1. 选择内或者外存储

内存储和外存储来源于android早期的时候,那时候有一个内置的存储器还有一个可移动的sd卡,后来有些手机没有sd卡,但是现在这个习惯还是保留下来。

内存储和外存储有些简单区别,用户可以自己去查资料。

如果需要写到外存储中,需要在manifest中申明以下的权限。

<manifest ...>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    ...</manifest>
现在基本上所有的app都默认有读权限,在未来的版本中需要申明READ_EXTERNAL_STORAGE权限,所以现在也最好把它加上去。

2. 保存文件在内存储中

getFileDir()

getCacheDir();

这两个函数返回内置的文件目录,西药及时清楚不需要的文件,因为系统一旦处于低内存的时候,会可能删掉相关的数据。

可以通过以下的方法创建一个文件:

File file = new File(context.getFilesDir(), filename);
可以通过openFileOutput()函数得到一个文件输出流,然后写入相关的文件。

String filename = "myfile";String string = "Hello world!";FileOutputStream outputStream;try {  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);  outputStream.write(string.getBytes());  outputStream.close();} catch (Exception e) {  e.printStackTrace();}
如果你想通过Url获取的图片想要暂时保存,可以用下面的代码片段;

public File getTempFile(Context context, String url) {    File file;    try {        String fileName = Uri.parse(url).getLastPathSegment();        file = File.createTempFile(fileName, null, context.getCacheDir());    } catch (IOException e) {        // Error while creating file    }    return file;}
3. 保存文件在外存储中

因为外存储可能不存在,所以你在使用之前需要确认它外存储是否能获取。通过调用以下函数确认

getExternalStorageState()

如果获取的状态是MEDIA_MOUTED,你就可以在外存储中进行文件的读取和写入操作。

比如以下的代码片段就是一个包裹函数,检出外存储的读写状态

/* 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;}

可以保存为两种类型的文件,一种是public file其它app也可以获取到文件,比如相机拍的照片。

一种是private file,只有保存的app才能读取,在app卸载的时候相关的文件就会删除。

如果你想在外存储中得到public file

可以通过以下函数调用获取

getExternalStoragePublicDirectory()

public File getAlbumStorageDir(String albumName) {    // Get the directory for the user's public pictures directory.    File file = new File(Environment.getExternalStoragePublicDirectory(            Environment.DIRECTORY_PICTURES), albumName);    if (!file.mkdirs()) {        Log.e(LOG_TAG, "Directory not created");    }    return file;}

如果你想获取一个只有本身app能读取的文件的话,可以通过下面这个函数函数调用

getExternalFilesDir()

public File getAlbumStorageDir(Context context, String albumName) {    // Get the directory for the app's private pictures directory.    File file = new File(context.getExternalFilesDir(            Environment.DIRECTORY_PICTURES), albumName);    if (!file.mkdirs()) {        Log.e(LOG_TAG, "Directory not created");    }    return file;}

指定文件的类型很重要,这可以让系统根据类型而把public file放到不同的文件目录中,比如歌曲会放到music文件夹。

4. 询问可用空间

需要提前知道保存文件的空间是否足够,可以提前调用getFreeSpace()或者getTotalSpace()函数,这些函数会返回当前系统的可用空间,这些信息也可以用来设置一个使用阈值,如果不够用的话会返回一个IOException的异常。

在保存文件的时候最好调用这个函数,然后一旦出现的话就可以捕捉IOException.比如你在将png文件转化为jpeg文件格式的时候,最好提前知道文件空间的大小。

5. 在你不需要文件的时候,你应该调用delete函数方法删除文件,最简单的方式

myFile.delete();

如果文件在内存储中,你可以通过Context获取文件的位置,然后调用deleteFile()函数。

myContext.deleteFile(fileName);

当卸载程序的时候,系统会删除所有的内置存储文件,以及所有通过getExternalFilesDir()获取的文件路径。


第六章    保存到SQL数据库中

最理想的保存结构体数据的方式保存到结构体中,比如联系人信息。这篇文章假设你已经熟悉SQL数据,android中表示数据库信息的在android.database.sqlite包中。

1. 定义一个数据库

首先是数据库的头,它定义了数据库怎么存储的结构。

你可能需要创建一个类似于Contact的类来表示所有的信息。

一个很好的定义方式就是把Contact类至于整个数据库的根部,这样所有的其它的类都可以访问。

内部类继承了一个_ID可以在cursor adaptors的时候使用,但是它不是不是必须的。

比如下面就定义了一个表格

public final class FeedReaderContract {    // To prevent someone from accidentally instantiating the contract class,    // make the constructor private.    private FeedReaderContract() {}    /* Inner class that defines the table contents */    public static class FeedEntry implements BaseColumns {        public static final String TABLE_NAME = "entry";        public static final String COLUMN_NAME_TITLE = "title";        public static final String COLUMN_NAME_SUBTITLE = "subtitle";    }}
2. 使用SQL Helper创建一个数据库

一旦你定义你的数据的存储结构,就可以通过实现某些函数进行创建和维护数据库,这里有些常用的创建和删除表格的语句。

private static final String SQL_CREATE_ENTRIES =    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +    FeedEntry._ID + " INTEGER PRIMARY KEY," +    FeedEntry.COLUMN_NAME_TITLE + " TEXT," +    FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";private static final String SQL_DELETE_ENTRIES =    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
android会将数据存储在私有的磁盘中,你的数据是安全的,因为这部分其它的程序不能获取。

SQLiteQpenHelper有很多有用的函数,当你使用这个类获取数据库的引用的时候,系统其实做了很多工作去创建,当有需要的时候,系统也会去更新数据库,所有的操作你只需要调用

getWritableDatabase() 和getReadableDatabase()函数。

因为数据库是长期运行的,需要确保在后台进程中也能够通过getWritableDatabase()和getReadableDatabase()访问。

为了使用SQLiteOpenHelper类,需要创建一个它的子类,这个类需要重写onCreate(), onUpgrade() 和onOpen()回调函数。如果需要你还可以重写onDowngrade()函数。

比如下面的例子:

public class FeedReaderDbHelper extends SQLiteOpenHelper {    // If you change the database schema, you must increment the database version.    public static final int DATABASE_VERSION = 1;    public static final String DATABASE_NAME = "FeedReader.db";    public FeedReaderDbHelper(Context context) {        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }    public void onCreate(SQLiteDatabase db) {        db.execSQL(SQL_CREATE_ENTRIES);    }    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        // This database is only a cache for online data, so its upgrade policy is        // to simply to discard the data and start over        db.execSQL(SQL_DELETE_ENTRIES);        onCreate(db);    }    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {        onUpgrade(db, oldVersion, newVersion);    }}
为了获取数据库,下一步是实例话这个子类

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
4. 往数据库中放数据

主要是使用ContentValues的insert方法实现

// Gets the data repository in write modeSQLiteDatabase db = mDbHelper.getWritableDatabase();// Create a new map of values, where column names are the keysContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);// Insert the new row, returning the primary key value of the new rowlong newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
insert的第一参数是表格的名称。

第二个参数告诉数据库在ContentValues为空的时候需要怎么做,如果你指定了一行的名字,这个数据库就会在里面插入一列。

5. 从数据库中读取信息

为了从数据库中读取数据,这里需要使用query方法,传递你想要的数据的信息给这个函数,这个函数最终的结构会返回一个Cursor类对象。

SQLiteDatabase db = mDbHelper.getReadableDatabase();// Define a projection that specifies which columns from the database// you will actually use after this query.String[] projection = {    FeedEntry._ID,    FeedEntry.COLUMN_NAME_TITLE,    FeedEntry.COLUMN_NAME_SUBTITLE    };// Filter results WHERE "title" = 'My Title'String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";String[] selectionArgs = { "My Title" };// How you want the results sorted in the resulting CursorString sortOrder =    FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";Cursor cursor = db.query(    FeedEntry.TABLE_NAME,                     // The table to query    projection,                               // The columns to return    selection,                                // The columns for the WHERE clause    selectionArgs,                            // The values for the WHERE clause    null,                                     // don't group the rows    null,                                     // don't filter by row groups    sortOrder                                 // The sort order    );

为了查看一行的信息,可以调用moveToNext函数,因为cursor的起始位置总是-1,所以开始阅读的时候就是调用这个函数,直到返回最后一个数据。

对于每一行,都可以调用Cursor的getString和getLong()函数。对于每个get函数,都需要传递列的位置信息,通过调用getColumnIndex方法,或者getColumnIndexOrThrow方法。

当遍历完结果的时候,需要调用close方法释放资源。

List itemIds = new ArrayList<>();while(cursor.moveToNext()) {  long itemId = cursor.getLong(      cursor.getColumnIndexOrThrow(FeedEntry._ID));  itemIds.add(itemId);}cursor.close();
6. 从数据库中删除信息

为了删除表格中的一行,需要提供有效的信息确认这一行在哪里。数据库接口提供了一个机制。这个机制把数据库的条目和选择的参数分开。条目定义了需要查阅的列,同时允许你查看行。如下示例

// Define 'where' part of query.String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";// Specify arguments in placeholder order.String[] selectionArgs = { "MyTitle" };// Issue SQL statement.db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
7. 更新数据库

当你改变数据库的值的时候,需要调用update方法更新数据库。

update结合了insert函数很delete函数。

SQLiteDatabase db = mDbHelper.getWritableDatabase();// New value for one columnContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);// Which row to update, based on the titleString selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";String[] selectionArgs = { "MyTitle" };int count = db.update(    FeedReaderDbHelper.FeedEntry.TABLE_NAME,    values,    selection,    selectionArgs);
8. 断开与数据库的连接

因为getWritableDataBase 和getReadableDataBase对耗时会比较长,你需要断开数据库的连接一旦你不需要访问它的时候,通常的数据断开的写在调用Activity的destroy方法中。

@Overrideprotected void onDestroy() {    mDbHelper.close();    super.onDestroy();}