Dữ liệu trong Assets

Một phần của tài liệu Bài giảng lập trình di động android (Trang 121 - 133)

Android Assets là thư mục chứa dữ liệu đầu vào. Chẳng hạn như âm thanh, hình ảnh, cơ sở dữ liệu hoặc các tập tin khác,... Những tập tin này sẽ không được biên dịch khi ứng dụng được đóng gói.

Để truy xuất được những tập tin trong thư mục assets, ta phải sử dụng bộ quản lý asset do Android cung cấp. Lớp này tên là AssetManager.

AssetManager assetManager = Context.getAssets();

AssetManager là lớp cung cấp quyền truy cập vào các tập tin không biên dịch của ứng dụng, cho phép bạn mở và đọc file nguyên gốc dưới dạng một luồng byte. Chẳng hạn, để truy xuất font chữ lưu trong thư mục Asset ta sử dụng như sau:

Typeface arial = Typeface.createFromAsset(assetManager, “font/arial.ttf”);

textView.setTypeface(arial);

Ví dụ, sau đây sẽ cho ta cái nhìn tổng quát về cách thức sử dụng AssetManager để truy xuất tập tin trên thư mục assets.

public void loadAssetsData() {

AssetManager assetManager = getAssets(); // To get names of all files inside the "Files" folder try {

String[] files = assetManager.list("Files"); for(int i=0; i<files.length; i++){

txtFileName.append("\n File :"+i+" Name => "+files[i]);} } catch (IOException e1) {

e1.printStackTrace(); }

ThS. Bùi Trung Úy 122 // To load text file

InputStream input; try {

input = assetManager.open("helloworld.txt"); int size = input.available();

byte[] buffer = new byte[size]; input.read(buffer);

input.close();

// byte buffer into a string String text = new String(buffer) txtView.setText(text); } catch (IOException e) { e.printStackTrace(); } // To load image try {

InputStream ims = assetManager.open("android_logo_small.jpg"); // create drawable from stream

Drawable d = Drawable.createFromStream(ims, null);

// set the drawable to imageview imageView.setImageDrawable(d); } catch(IOException ex) { e.printStackTrace(); } }

5.2. Dữ liệu trong SharedPreferences

SharedPreferences là một cơ chế cho phép bạn lưu trữ và đọc dữ liệu bằng các cặp khóa-giá trị (key-value), dữ liệu nó có thể lưu là ở dạng nguyên thuỷ như: int, float, string, boolean, long. Dữ liệu của Shared Preferences sẽ được lưu cục bộ ở trong

phạm vi ứng dụng, chính vì thế nếu xoá ứng dụng hoặc xoá dữ liệu của ứng dụng thì

dữ liệu này cũng sẽ bị xóa.

Đầu tiên, để lưu dữ liệu bạn cần khởi tạo một biến đối tượng kiểu Shared Preferences như sau:

SharedPreferences sharedPrefs = getSharedPreferences(<SharedName>, Context.MODE_PRIVATE);

Trong đó:

ThS. Bùi Trung Úy 123

Context.MODE_PRIVATE: là chế độ bảo mật dữ liêu trong Android, khi bạn để như vậy có nghĩa là bạn chỉ cho ứng dụng hiện tại truy cập vào file Shared Preferences này và không một ứng dụng nào có quyền truy cập vào được.

Tiếp theo, tạo đối tượng editor từ biến sharedPrefs đã tạo ở trên, mục đích là để có thể mở file và đưa dữ liệu vào:

SharedPreferences.Editor editor = sharedPrefs.edit(); Để đưa dữ liệu vào chúng ta sử dụng như sau: editor.put<X>(String key, value)

Trong đó:

<X>: là kiểu dữ liệu bạn đưa vào, với Shared Preferences bạn có thể lưu ở nhưng kiểu dữ liệu như : float, string, int, boolean, long.

key: là tên đặt cho biến bạn sẽ lưu xuống. value: giá trị cần lưu.

Ví dụ:

SharedPreferences sharedPrefs = getSharedPreferences(“appSettings”, Context.MODE_PRIVATE);

SharedPreferences.Editor editor = sharedPrefs.edit(); editor.putString("appName", "HelloAndroid"); editor.putBoolean("FirstLaunch", true); editor.putInt("HighScore",100); editor.putFloat("Mark", 9.5f); editor.putLong("Sound",90); editor.commit(); // hoặc: editor.apply();

Sau khi đã put dữ liệu xong thì bạn gọi hàm commit() hoặc là apply() để xác nhận những thay đổi. Sự khác nhau giữa commit() và apply() là:

commit(): hoạt động theo cơ chế đồng bộ, nếu như bạn khởi tạo 2 editor để chỉnh sửa dữ liệu thì editor nào thực hiện trước sẽ làm trước và cái nào đến sau sẽ làm sau. Và bạn sẽ được thông báo là true hay false nếu như thành công hoặc thất bại.

apply() : hoạt động theo cơ chế không đồng bộ, và dù có thành công hay không thì sẽ không nhận được kết quả trả về.

Thc hành vi shared Preferences:

Đây là một ví dụ về màn hình thiết lập thông số của một trò chơi trên Android, trước khi chơi bạn lựa chọn các thông số như độ sáng, mức độ âm lượng, và độ khó. Sau khi chơi xong bạn tắt trò chơi và có thể tiếp tục chơi vào ngày hôm sau. SharedPreferences cho phép bạn lưu lại các các thông số đã thiết lập trước đó, để cho phép khi chơi lại các thiết lập đó có thể sử dụng mà không cần phải thiết lập lại.

ThS. Bùi Trung Úy 124

Đọc dữ liệu settings từ Shared preferences:

private void loadGameSetting() {

SharedPreferences sharedPrefs = this.getSharedPreferences("gameSettings", Context.MODE_PRIVATE);

if(sharedPreferences!= null) {

int brightness = sharedPrefs.getInt("brightness", 90); int sound = sharedPrefs.getInt("sound",95);

int checkedRadioButtonId = sharedPrefs.getInt("checkedRadioButtonId", R.id.radioButton_medium); this.seekBarSound.setProgress(sound); this.seekBarBrightness.setProgress(brightness); this.radioGroupDiffLevel.check(checkedRadioButtonId); } }

Lưu dữ liệu setting vào Shared preferences.

public void saveGameSettings() {

SharedPreferences sharedPrefs= this.getSharedPreferences("gameSettings", Context.MODE_PRIVATE);

SharedPreferences.Editor editor = sharedPrefs.edit();

editor.putInt("brightness", this.seekBarBrightness.getProgress()); editor.putInt("sound", this.seekBarSound.getProgress());

// ID của RadioButton đang được chọn.

int checkedRadioButtonId = radioGroupDiffLevel.getCheckedRadioButtonId(); editor.putInt("checkedRadioButtonId", checkedRadioButtonId);

// Save. editor.apply(); }

ThS. Bùi Trung Úy 125

Việc lưu trữ dữ liệu Shared preferences là trong suốt với người dùng. Trên thực tế chúng được lưu trong một file xml nằm trên bộ nhớ trong, trong vùng nhớ chỉ có thể truy cập được bởi ứng dụng (/data/data/ {package-name} /shared_prefs/ gameSettings.xml), và với máy ảo Android ta có thể xem được cụ thể các giá trị này.

5.3. Lưu trữ dữ liệu trên bộ nhớ SD

Trong trường hợp cần lưu lại dữ liệu tương đối phức tạp hơn (khó có thể lưu lại dạng key-value trong shared preference), ta có thể dùng hệ thống file. Trong Android, để làm việc (nhập/xuất) với file, ta có thể sử dụng các lớp của gói java.io. Trong phần này ta sẽ xem cách làm việc với file trong bộ nhớ trong lẫn bộ nhớ ngoài.

5.3.1. Sử dụngbộ nhớ trong

Android Internal Storage là nơi lưu trữ các dữ liệu cá nhân của từng ứng dụng, mà các dữ liệu này được lưu trữ và sử dụng cho riêng ứng dụng đó. Các ứng dụng khác không thể truy cập vào được. Thông thường khi ứng dụng bị gỡ bỏ khỏi thiết bị Android, các file dữ liệu liên quan cũng bị xóa bỏ theo. Các files của ứng dụng sẽ được lưu trong thư mục riêng tư /data/data/{package_name}.

Một đặc điểm khi bạn làm việc với các file dữ liệu ở bộ nhớ trong là bạn chỉ cần dùng tên file đơn giản mà không phải thêm đường dẫn.

- Mở file ghi dữ liệu.

String simpleFileName ="note.txt";

FileOutputStream out = openFileOutput(simpleFileName, MODE_PRIVATE);

MODE_PRIVATE: Đây là chế độ mặc định, file ghi ra chỉ được sử dụng bởi ứng dụng tạo ra nó, hoặc chia sẻ với cùng User ID.

MODE_APPEND : Chế độ nối thêm dữ liệu vào file nếu nó đã tồn tại. - Mở file đọc dữ liệu:

ThS. Bùi Trung Úy 126

String simpleFileName = "note.txt";

FileInputStream in = this.openFileInput(simpleFileName); Ví dụ đoạn code mẫu đọc/ghi dữ liệu:

Lưu dữ liu vào b nh trong:

private void saveData() {

String data = this.editText.getText().toString(); try {

// Mở một luồng ghi file.

FileOutputStream out = this.openFileOutput(simpleFileName, MODE_PRIVATE); // Ghi dữ liệu. out.write(data.getBytes()); out.close(); Toast.makeText(this,"File saved!",Toast.LENGTH_SHORT).show(); } catch (Exception e) { Toast.makeText(this,"Error:"+ e.getMessage(),Toast.LENGTH_SHORT).show(); } } Đọc dữ liệu từ bộ nhớ trong:

private void readData() { try {

// Mở một luồng đọc file.

FileInputStream in = this.openFileInput(simpleFileName);

BufferedReader br= new BufferedReader(new InputStreamReader(in)); StringBuilder sb= new StringBuilder();

String s= null;

while((s= br.readLine())!= null) { sb.append(s).append("\n"); } this.textView.setText(sb.toString()); } catch (Exception e) { Toast.makeText(this,"Error:"+ e.getMessage(),Toast.LENGTH_SHORT).show(); } } 5.2.2. Sử dụngbộ nhớ ngoài

Android External Storage là nơi lưu trữ dữ liệu ngoài của Android, các file dữ liệu mà bạn lưu trữ tại đây có thể được truy xuất bởi ứng dụng khác.

Để đọc ghi dữ liệu trên bộ lưu trữ ngoài bạn phải khai báo quyền sử dụng trong file AndroidManifest.xml như sau:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

ThS. Bùi Trung Úy 127

Sử dụng các phương tĩnh của lớp Environment bạn có thể lấy được các thông tin về thư mục của các bộ lưu trữ ngoài.

Phương thức Trả về Environment.getDataDirectory() /data Environment.getDownloadCacheDirectory() /cache Environment.getExternalStorageState() mounted Environment.getDownloadCacheDirectory() /cache Environment.getRootDirectory() /system

Ví dụ: Thiết kế ứng dụng theo mẫu và thực hiện đọc ghi dữ liệu trên bộ nhớ ngoài.

Code mẫu đọc/ghi dữ liệu trên bộ nhớ ngoài: public String fileName = “Note.txt”;

Ghi d liu vào file trên b nh ngoài:

private void writeFile() { // Thư mục gốc của SD Card.

File extStore = Environment.getExternalStorageDirectory(); String path = extStore.getAbsolutePath() + "/" + fileName; String data = editText.getText().toString();

try {

File myFile = new File(path); myFile.createNewFile();

FileOutputStream fOut = new FileOutputStream(myFile);

OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut); myOutWriter.append(data);

ThS. Bùi Trung Úy 128 fOut.close();

Toast.makeText(getApplicationContext(), fileName + " saved", Toast.LENGTH_LONG).show(); } catch (Exception e) {

e.printStackTrace(); }

}

Đọc d liu t file trên b nh ngoài:

private void readFile() { // Thư mục gốc của SD Card.

File extStore = Environment.getExternalStorageDirectory(); String path = extStore.getAbsolutePath() + "/" + fileName; String s = "";

String fileContent = ""; try {

File myFile = new File(path);

FileInputStream fIn = new FileInputStream(myFile); BufferedReader myReader = new BufferedReader( new InputStreamReader(fIn));

while ((s = myReader.readLine()) != null) { fileContent += s + "\n"; } myReader.close(); this.textView.setText(fileContent); } catch (IOException e) { e.printStackTrace(); } }

Liệt kê thư mục trên b nh ngoài:

private void listExternalStorages() { StringBuilder sb = new StringBuilder(); sb.append("Data Directory: ").append("\n - ")

.append(Environment.getDataDirectory().toString()).append("\n"); sb.append("Download Cache Directory: ").append("\n - ")

.append(Environment.getDownloadCacheDirectory().toString()).append("\n"); sb.append("External Storage State: ").append("\n - ")

.append(Environment.getExternalStorageState().toString()).append("\n"); sb.append("External Storage Directory: ").append("\n - ")

.append(Environment.getExternalStorageDirectory().toString()).append("\n"); sb.append("Download Cache Directory: ").append("\n - ")

.append(Environment.getDownloadCacheDirectory().toString()).append("\n"); sb.append("Root Directory: ").append("\n - ")

.append(Environment.getRootDirectory().toString()).append("\n"); this.textView.setText(sb.toString());

}

ThS. Bùi Trung Úy 129

Trong phần trước ta đã tìm hiểu cách lưu dữ liệu vào file và vào shared preferences. Tuy nhiên, với loại dữ liệu quan hệ thì sử dụng cơ sở dữ liệu quan hệ sẽ thuận tiện hơn rất nhiều. Ví dụ ta cần lưu trữ kết quả kiểm tra của các sinh viên trong trường học, dùng cơ sở dữ liệu sẽ cho phép chúng ta truy vấn kết quả học tập của sinh viên theo các tiêu chí khác nhau, việc thêm, bớt, thay đổi thông tin thông qua các câu

truy vấn SQL cũng dễ dàng hơn nhiều so với việc thao tác trên file. Android sử dụng

hệ cơ sở dữ liệu SQLite.

1. SQLite là gì?

SQLite là một cơ sở dữ liệu quan hệ, mã nguồn mở và được tích hợp sẵn trên Android. SQLite thường được sử dụng trong các ứng dụng ở local, như các ứng dụng Danh bạ, Tin nhắn, Ghi chú, Quản lý thông tin cá nhân, các tùy chọn thiết lập trong phần mềm,…

Trong Android, cơ sở dữ liệu (CSDL) do một ứng dụng tạo ra sẽ chỉ được truy

xuất bởi ứng dụng đó, và file CSDL sẽ nằm trong bộ nhớ trong dành riêng cho ứng

dụng (/data/data/{package-name}/databases/).

2. Các bước làm vic vi SQLLite

Nói chung, bạn cần tạo một lớp tiện ích để làm việc với cơ sở dữ liệu SQLite, lớp này nên mở rộng từ lớp SQLiteOpenHelper. Có 2 phương thức quan trọng mà bạn cần phải ghi đè (override) nó là onCreate() và onUpgrade().

- onCreate() - là nơi mà chúng ta cần phải viết để tạo bảng. Nó được gọi khi cơ sở dữ liệu được tạo ra.

- onUpgrade() - phương thức này được gọi khi cơ sở dữ liệu được nâng cấp như thay đổi cấu trúc bảng, thêm ràng buộc cho cơ sở dữ liệu, v..v.

Cấu trúc của lớp tiện ích này (có tên DatabaseHelper) như sau: public class DatabaseHelper extends SQLiteOpenHelper {

// ....

public DatabaseHelper(Context context) {

super(context, DATABASE_NAME, null, DATABASE_VERSION); }

@Override

public void onCreate(SQLiteDatabase db) { // Script to create table.

String script = "CREATE TABLE " + TABLE_NAME + …; // Execute script.

db.execSQL(script); }

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // Drop table

db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); // Recreate

onCreate(db); }

ThS. Bùi Trung Úy 130 // ...

}

Một thói quen tốt thường được các lập trình viên kinh nghiệm sử dụng là tập trung tất cả mã lệnh truy cập đến CSDL vào một lớp riêng để thao tác trên CSDL và việc truy xuất này sẽ trở nên trong suốt với môi trường ngoài.

Trong ví dụ sau ta sẽ tạo một CSDL tên là AppDB.sqllite, chứa một bảng duy nhất là contacts, bảng này chứa các trường _id, name và email. Ta tạo lớp tiện ích DatabaseHelper như sau:

package com.example.helloandroid; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log;

public class DatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "DatabaseHelper"; // Phiên bản

private static final int DATABASE_VERSION = 1; // Tên cơ sở dữ liệu.

private static final String DATABASE_NAME = "AppDB.sqllite"; // Tên bảng

private static final String TABLE_NAME = "contacts"; private static final String FIELD_ID ="_id";

private static final String FIELD_NAME ="name"; private static final String FIELD_EMAIL = "email"; public DatabaseHelper(Context context) {

super(context, DATABASE_NAME, null, DATABASE_VERSION); }

@Override

public void onCreate(SQLiteDatabase db) { try {

// Script tạo bảng.

String sqlStr = "CREATE TABLE " + TABLE_NAME + "("

+ FIELD_ID + " INTEGER PRIMARY KEY," + FIELD_NAME + " TEXT not null," + FIELD_EMAIL + " TEXT not null" + ")";

// Chạy lệnh tạo bảng. db.execSQL(sqlStr); } catch (SQLException e) { e.printStackTrace(); } }

ThS. Bùi Trung Úy 131 @Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { try {

// Hủy (drop) bảng cũ nếu nó đã tồn tại.

db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); // Tạo lại lại bảng mới onCreate(db); } catch (SQLException e) { e.printStackTrace(); } }

//---insert a contact into the database---

public long insertContact(String name, String email) throws SQLException {

SQLiteDatabase db = this.getWritableDatabase(); ContentValues args = new ContentValues(); args.put(FIELD_NAME, name);

args.put(FIELD_EMAIL, email); // Chèn một dòng dữ liệu vào bảng.

long id = db.insert(TABLE_NAME, null, args); // Đóng kết nối database.

db.close(); return id; }

//---deletes a particular contact---

public boolean deleteContact(long id) throws SQLException {

SQLiteDatabase db = this.getWritableDatabase();

boolean res = db.delete(TABLE_NAME, FIELD_ID + "=" + id, null) > 0; db.close();

return res; }

//---retrieves a particular contact---

public void getContact(long id) throws SQLException {

SQLiteDatabase db = this.getReadableDatabase();

Cursor cursor = db.query(true, TABLE_NAME, new String[] {FIELD_ID, FIELD_NAME, FIELD_EMAIL}, FIELD_ID + "=" + id, null, null, null, null, null); /*Cursor cursor = db.query(TABLE_NAME, new String[] { FIELD_ID,

FIELD_NAME, FIELD_EMAIL }, FIELD_ID + "=?", new String[] { String.valueOf(id) }, null, null, null, null); */ if (cursor != null) { cursor.moveToFirst(); // Hiển thị contact displayContact(cursor); } // Đóng kết nối database. db.close(); }

ThS. Bùi Trung Úy 132 //---updates a contact---

public boolean updateContact(long rowId, String name, String email) {

SQLiteDatabase db = this.getWritableDatabase(); ContentValues args = new ContentValues(); args.put(FIELD_NAME, name);

args.put(FIELD_EMAIL, email);

boolean res = db.update(TABLE_NAME, args, FIELD_ID + "=" + rowId, null) > 0; // Đóng kết nối database.

db.close(); return res; }

//---retrieves all the contacts---

public int getAllContacts() throws SQLException {

SQLiteDatabase db = this.getWritableDatabase();

/*Cursor cursor = db.query(TABLE_NAME, new String[] {FIELD_ID, FIELD_NAME, FIELD_EMAIL}, null, null, null, null, null);*/

// Select All Query

String selectQuery = "SELECT * FROM " + TABLE_NAME; Cursor cursor = db.rawQuery(selectQuery, null); // contacts count

int count = cursor.getCount(); // Duyệt trên con trỏ

if (cursor.moveToFirst()) { do { // Hiển thị contact displayContact(cursor); } while (cursor.moveToNext()); } cursor.close(); return count; }

public void displayContact(Cursor c) {

Log.d(TAG, "Contact details: " + "id: " + c.getString(0) + "\n" + "Name: " + c.getString(1) + "\n" + "Email: " + c.getString(2)); }

}

Trước tiên ta khai báo các hằng số: tên CSDL, tên bảng, tên các trường để dễ dàng truy xuất và thay đổi trong quá trình phát triển. Ngoài ra ta cũng khai báo phiên bản của CSDL trong ứng dụng và viết sẵn câu truy vấn dùng để tạo CSDL.

private static final String DATABASE_NAME = "AppDB.sqllite"; private static final String TABLE_NAME = "contacts";

ThS. Bùi Trung Úy 133 private static final String FIELD_NAME ="name";

private static final String FIELD_EMAIL = "email";

Trong lớp DatabaseHelper trợ giúp cho việc tạo CSDL và nâng cấp cấu trúc khi có sự thay đổi trong các phiên bản tiếp theo. Lớp này kế thừa từ lớp SQLiteOpenHelper. Hàm dựng của lớp này sẽ gọi hàm dựng của lớp cha với tên và phiên bản CSDL của ứng dụng:

super(context, DATABASE_NAME, null, DATABASE_VERSION); Ngoài ra trong lớp này ta ghi đè 2 hàm:

- Hàm onCreate(): được gọi để khởi tạo CSDL trong lần đầu tiên chạy ứng dụng, trong hàm này ta tiến hành thực thi câu lệnh tạo CSDL ở trên.

- Hàm onUpgrade(): được gọi khi ta nâng cấp ứng dụng và thay đổi giá trị của phiên bản CSDL. Trong ví dụ ở trên, khi có thay đổi phiên bản này, ta xóa CSDL cũ và tạo lại cái mới.

Ngoài ra ta cũng viết thêm các hàm để tạo mới bản ghi, cập nhật bản ghi, lấy tất cả bản ghi, lấy bản ghi theo id,…

Sau khi có lớp DatabaseHelper này, việc truy xuất CSDL trở nên tương đối đơn giản, đoạn mã dưới đây minh họa các thao tác thêm, bớt, truy vấn CSDL:

//--- thêm một bản ghi ---

Một phần của tài liệu Bài giảng lập trình di động android (Trang 121 - 133)

Tải bản đầy đủ (PDF)

(133 trang)