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

第四章 保存数据

许多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 mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues 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 row
long 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 Cursor
String 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 column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the title
String 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方法中。

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值