Tìm hiểu về các API lưu trữ dữ liệu cục bộ của Android

Hãy tận dụng những sở thích, SQLite và các API bộ nhớ trong và ngoài

Khả năng lưu trữ dữ liệu cục bộ trên thiết bị di động là một chức năng quan trọng cho các ứng dụng di động cần duy trì thông tin cần thiết qua nhiều lần chạy-ứng dụng hoặc vòng đời của ứng dụng. Là nhà phát triển, bạn luôn phải lưu trữ thông tin như các sở thích của người dùng hoặc các cấu hình của ứng dụng. Bạn cũng phải quyết định xem bạn cần phải khai thác lưu trữ trong hoặc ngoài, tùy thuộc vào các đặc điểm, ví dụ như trông thấy khả năng truy cập, hoặc nếu bạn cần phải xử lý các kiểu dữ liệu có cấu trúc, phức tạp hơn. Hãy đồng hành cùng bài viết này để tìm hiểu về các API lưu trữ dữ liệu của Android, đặc biệt là các sở thích, SQLite và các API bộ nhớ trong và ngoài.

C. Enrique Ortiz, Nhà phát triển và tác giả, IBM

C. Enrique Ortiz là một kỹ sư công nghệ, nhà phát triển và tác giả về di động từ rất lâu. Ông viết blog tại http://mobilityweblog.com" và là người sáng lập chương Austin của MobileMonday.



22 04 2011

Các từ viết tắt thường dùng

  • ADT: Android Development Tools - Các công cụ phát triển Android.
  • API: Application program interface - Giao diện lập trình ứng dụng.
  • IDE: Integrated development environment - Môi trường phát triển tích hợp.
  • JDK: Java Development Kit - Bộ dụng cụ phát triển Java.
  • JSON: JavaScript Object Notation - Ký pháp đối tượng JavaScritpt.
  • SDK: Software Development Kit - Bộ công cụ của nhà phát triển phần mềm.
  • SQL: Structured Query Language - Ngôn ngữ truy vấn có cấu trúc.
  • UI: User interface - Giao diện người dùng.
  • XML: Extensible Markup Language - Ngôn ngữ đánh dấu mở rộng.

Các điều kiện cần có trước

Để đồng hành cùng bài viết này, bạn cần những kỹ năng và các công cụ sau đây:

  • Kiến thức cơ bản về công nghệ Java™ và cách sử dụng Eclipse (hoặc IDE yêu thích của bạn).
  • JDK (phiên bản cần thiết là 5 hoặc 6).
  • Eclipse (phiên bản 3.4 hoặc 3.5).
  • SDK của Android và Trình cắm thêm ADT.

Để tải về và có thông tin thiết lập, hãy xem Tài nguyên ở cuối bài viết này.


Ứng dụng mẫu

Để làm nổi bật các khía cạnh lưu trữ cục bộ về việc phát triển ứng dụng Android, tôi trình bày một ứng dụng mẫu cho phép bạn thử nghiệm việc thực hiện các kiểu API. Mã nguồn có sẵn để tải về. Ứng dụng này hỗ trợ các hành động trong Hình 1.

Hình 1. Các trường hợp sử dụng (use cases)
Biểu đồ các trường hợp sử dụng trong ứng dụng mẫu

Hình 1 liệt kê các trường hợp sử dụng sau:

  • Quản lý và lưu trữ các sở thích.
  • Nạp thông tin từ các tài sản ứng dụng.
  • Xuất khẩu thông tin vào bộ nhớ trong, bộ nhớ ngoài và cơ sở dữ liệu cục bộ.
  • Đọc thông tin từ bộ nhớ trong và cơ sở dữ liệu cục bộ.
  • Xóa thông tin đã lưu trữ.
  • Xem thông tin trên màn hình.

Bạn sử dụng lưu trữ cục bộ trong ứng dụng xuyên suốt bài viết này, như sau:

  • Các sở thích được nắm bắt từ người sử dụng, được lưu trữ cục bộ và được sử dụng trong toàn bộ ứng dụng.
  • Một hình ảnh của người sử dụng được lấy ra từ các tài sản của ứng dụng nội bộ, được lưu trữ trong bộ nhớ trong và bộ nhớ ngoài cục bộ và được biểu hiện trên màn hình.
  • Một danh sách những người bạn trong JSON được lấy từ các tài sản của ứng dụng. Nó được phân tích cú pháp và được lưu trữ trong bộ nhớ trong cục bộ, bộ nhớ ngoài và cơ sở dữ liệu quan hệ và được biểu hiện trên màn hình.

Ứng dụng mẫu định nghĩa các lớp trong Bảng 1.

Bảng 1. Các lớp của ứng dụng mẫu
LớpMô tả
MainActivityHoạt động chính; hầu hết các mã mẫu tập trung ở đây.
FriendBiểu diễn một người bạn.
AppPreferenceActivityMàn hình và Hoạt động Các sở thích.
DBHelperMột lớp trợ giúp để quản lý cơ sở dữ liệu SQLite.

Ứng dụng ví dụ sử dụng hai kiểu dữ liệu. Kiểu đầu tiên là các sở thích của ứng dụng được lưu trữ như các cặp giá trị tên. Đối với các sở thích, hãy định nghĩa các thông tin sau đây:

  • Một tên tệp, được dùng để nạp và lưu trữ danh sách các tên của những người bạn.
  • Một tên tệp, được dùng để nạp và lưu trữ một hình ảnh cho người sử dụng.
  • Một cờ, nếu được đặt, chỉ báo tự động xóa tất cả dữ liệu được lưu khi khởi động ứng dụng.

Kiểu dữ liệu thứ hai là một danh sách những người bạn. Ban đầu danh sách những người bạn được trình bày theo định dạng JSON API biểu đồ của Facebook (Facebook Graph API JSON), bao gồm một mảng các đối tượng name (tên) và friend (người bạn) (xem Liệt kê 1).

Liệt kê 1. Danh sách những người bạn (theo định dạng Facebook Graph API JSON)
{
   "data": [
      {
         "name": "Edmund Troche",
         "id": "500067699"
      }
   ]
}

Định dạng đơn giản ở trên làm cho đối tượng Friend (người bạn) và lược đồ cơ sở dữ liệu đơn giản. Liệt kê 2 cho thấy lớp Friend.

Liệt kê 2. Lớp Friend
package com.cenriqueortiz.tutorials.datastore;

import android.graphics.Bitmap;

/**
 * Represents a Friend
 */
public class Friend {
    public String id;
    public String name;
    public byte[] picture;
    public Bitmap pictureBitmap;;
}

Ngoài ID (mã định danh) và tên, ứng dụng mẫu cũng duy trì các tham chiếu tới hình ảnh người bạn. Mặc dù ứng dụng mẫu chưa sử dụng các tham chiếu đó, bạn có thể dễ dàng mở rộng ứng dụng mẫu để lấy hình ảnh từ Facebook và hiển thị nó trong màn hình chính.

Lược đồ cơ sở dữ liệu bao gồm một bảng duy nhất để lưu trữ thông tin của người bạn. Bảng này có ba cột:

  • ID hoặc khóa duy nhất.
  • ID của Facebook.
  • Tên của người bạn.

Liệt kê 3 cho thấy câu lệnh SQL để khai báo bảng quan hệ tương ứng.

Liệt kê 3. Bảng cơ sở dữ liệu người bạn
db.execSQL("create table " + TABLE_NAME + " (_id integer primary key autoincrement, " 
+ " fid text not null, name text not null) ");

Dựa trên thông tin này, bạn có thể hiển thị tên của trên màn hình chính và bằng cách sử dụng ID, bạn có thể lấy thêm thông tin chi tiết cho người dùng được chọn. Trong ứng dụng mẫu này, bạn chỉ hiển thị tên. Việc lấy thêm thông tin còn dành lại cho bạn tự thử nghiệm. Lưu ý rằng bạn có thể dễ dàng thay đổi mã để trực tiếp đi vào Facebook.


Lưu trữ các sở thích của ứng dụng

Phần này trình bày API các sở thích (API Preferences) và các màn hình. API Android cung cấp một số cách để xử lý các sở thích. Có một cách tiếp cận là trực tiếp sử dụng SharedPreferences (Các sở thích chia sẻ) và sử dụng thiết kế màn hình riêng của bạn và quản lý của các sở thích. Cách tiếp cận thứ hai là sử dụng một PreferenceActivity (Hoạt động các sở thích). Một PreferenceActivity tự động chăm lo về các sở thích được hiển thị trên màn hình như thế nào (theo mặc định chúng trông giống như các sở thích của hệ thống) và tự động lưu trữ hoặc sao lưu các sở thích khi người dùng tương tác với từng sở thích thông qua việc sử dụng SharedPreferences.

Để đơn giản hóa ứng dụng mẫu, hãy sử dụng một PreferenceActivity để quản lý các sở thích và màn hình sở thích (xem Hình 2). Màn hình các sở thích hiển thị hai phần: Các tài sản và các giá trị cài đặt tự động. Trong Các tài sản, bạn có thể nhập các tên tệp cho cả hai tùy chọn Danh sách những người bạn và Hình ảnh. Trong Các giá trị cài đặt tự động, bạn có thể đánh dấu chọn một hộp kiểm tra để xóa thông tin lúc khởi động.

Hình 2. Màn hình Các sở thích khi được cài đặt
Ảnh chụp màn hình của màn hình Các sở thích khi được triển khai thực hiện

Trong Hình 2, cách bố trí đã được định nghĩa bằng cách sử dụng cách tiếp cận khai báo thông qua XML (thay vì lập trình); XML khai báo được ưa thích vì nó giữ mã nguồn sạch sẽ và dễ đọc.Liệt kê 4 cho thấy khai báo XML cho giao diện người dùng Các sở thích (Preferences UI).

Liệt kê 4. Khai báo XML của màn hình Các sở thích
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/prefs_screen"
        android:key="preferencescreen"
    >
    <PreferenceCategory android:title="Assets">
        <EditTextPreference 
            android:key="@string/prefs_assetname_friendslist_key"
            android:title="Friends List" 
            android:summary="Please enter filename"
            android:defaultValue="friends.txt"
        />
        <EditTextPreference 
            android:key="@string/prefs_assetname_picture_key"
            android:title="Picture" 
            android:summary="Please enter filename"
            android:defaultValue="pict2.jpg"
        />
    </PreferenceCategory>

    <PreferenceCategory android:title="Auto Settings">
        <CheckBoxPreference
            android:key="@string/prefs_autodelete_key"
            android:title="Delete at Startup" 
            android:summary="Check to clear at startup"
            android:defaultValue="false"
        />
    </PreferenceCategory>
</PreferenceScreen>

PreferenceScreen (Màn hình các sở thích) bao gồm hai cá thể EditTextPreference, một là CheckBoxPreference và hai nhóm thể loại theo như định nghĩa của PreferenceCategory (một cho Asset và một cho Auto Settings).

Trong ứng dụng mẫu này, thiết kế yêu cầu màn hình các sở thích được gọi ra bằng cách sử dụng một mục trình đơn. Để làm việc này, hãy sử dụng một thông báo Intent để gọi Hoạt động của Màn hình Các sở thích (Preference Screen Activity) có tên là AppPreferenceActivity (xem Liệt kê 5). Lưu ý rằng tôi không trình bày Intent hoạt động cụ thể như thế nào. Xem phần Tài nguyên để biết thêm thông tin về các Intent.

Liệt kê 5. AppPreferenceActivity
/*
 * AppPreferenceActivity is a basic PreferenceActivity
 * C. Enrique Ortiz | http://CEnriqueOrtiz.com
 */
package com.cenriqueortiz.tutorials.datastore;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class AppPreferenceActivity extends PreferenceActivity {

    /**
     * Default Constructor
     */
    public AppPreferenceActivity() {}

   /** 
    * Called when the activity is first created. 
    *   Inflate the Preferences Screen XML declaration.
    */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.prefs); // Inflate the XML declaration
    }   

}

Trong ứng dụng mẫu này, hãy gọi Intent như trong Liệt kê 6, từ bên trong trình xử lý mục Trình đơn.

Liệt kê 6. Gọi hoạt động Preferences bằng cách sử dụng một Intent
/**
 * Invoked when a menu item has been selected
 */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {

        // Case: Bring up the Preferences Screen
        case R.id.menu_prefs: // Preferences
            // Launch the Preference Activity
            Intent i = new Intent(this, AppPreferenceActivity.class);
            startActivity(i);
            break;

        case R.id.menu...:
            :
            break;

    }
    return true;
}

Ngoài ra, bạn phải định nghĩa tất cả các Intent trong tệp XML AndroidManifest như trong Liệt kê 7.

Liệt kê 7. Định nghĩa Intent trong tệp AndroidManifest.xml
:

<application android:icon="@drawable/icon" android:label="@string/app_name">

    :
    :
    
    <activity 
        android:name="AppPreferenceActivity" 
        android:label="Preferences">
    </activity>  

     :

</application>

Nhớ lại rằng PreferenceActivity sử dụng SharedPreferences để lưu trữ tự động các sở thích khi người dùng tương tác với màn hình các sở thích. Sau đó ứng dụng sử dụng các sở thích này khi nó thực thi để thực hiện các nhiệm vụ khác nhau của nó. Liệt kê 8 cho thấy cách sử dụng trực tiếp SharedPreferences để nạp các sở thích đã lưu, bạn có thể tham khảo mã mẫu đi kèm về cách sử dụng các sở thích đã nạp trong xuyên suốt toàn bộ mã mẫu như thế nào. Ngoài ra, Liệt kê 8 cũng cho thấy cách lưu trữ trực tiếp các sở thích bằng SharedPreferences khi sử dụng một Editor (trình soạn thảo) trong trường hợp bạn thích tự quản lý các sở thích của mình (và không thông qua PrefenceActivity).

Liệt kê 8 cho thấy cách sử dụng SharedPreferences để nạp các sở thích đã lưu trữ và cách thay đổi các sở thích đã lưu bằng cách sử dụng một Editor.

Liệt kê 8. Sử dụng SharedPreferences
/////////////////////////////////////////////////////////////
// The following methods show how to use the SharedPreferences
/////////////////////////////////////////////////////////////

/**
 * Retrieves the Auto delete preference
 * @return the value of auto delete
 */
public boolean prefsGetAutoDelete() {
    boolean v = false;
    SharedPreferences sprefs = 
       PreferenceManager.getDefaultSharedPreferences(appContext); 
    String key = appContext.getString(R.string.prefs_autodelete_key);
    try {
        v = sprefs.getBoolean(key, false);
    } catch (ClassCastException e) {
    }
    return v;
}   

/**
 * Sets the auto delete preference
 * @param v the value to set
 */
public void  prefsSetAutoDelete(boolean v) {
    SharedPreferences sprefs = 
    PreferenceManager.getDefaultSharedPreferences(appContext); 
    Editor e = sprefs.edit();
    String key = appContext.getString(R.string.prefs_autodelete_key);               
    e.putBoolean(key, v);
    e.commit();
}

Tiếp theo, bạn sẽ thấy cách sử dụng một cơ sở dữ liệu để lưu dữ liệu.


Sử dụng các cơ sở dữ liệu SQLite

Android cung cấp sự hỗ trợ cho cơ sở dữ liệu quan hệ cục bộ thông qua SQLite. Bảng này (được định nghĩa trong các liệt kê sau đây) tóm tắt các lớp cơ sở dữ liệu quan trọng được sử dụng trong ứng dụng mẫu.

Đối với ứng dụng mẫu, một lớp DBHelper được dùng để gói một số phép toán của cơ sở dữ liệu (xem Liệt kê 9).

Liệt kê 9. Lớp DBHelper
package com.cenriqueortiz.tutorials.datastore;

import java.util.ArrayList;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

Một số hằng số được định nghĩa cho phiên bản cơ sở dữ liệu, tên cơ sở dữ liệu và tên bảng (xem Liệt kê 10).

Liệt kê 10. Khởi tạo lớp DBHelper
    private SQLiteDatabase db;
    private static final int DATABASE_VERSION = 1;
    private static final String DB_NAME = "sample.db";
    private static final String TABLE_NAME = "friends";

    /**
     * Constructor
     * @param context the application context
     */
    public DBHelper(Context context) {
        super(context, DB_NAME, null, DATABASE_VERSION);
        db = getWritableDatabase();
    }

Phương thức onCreate() được gọi khi đã sẵn sàng để tạo ra cơ sở dữ liệu. Trong phương thức này, các bảng được tạo ra (xem Liệt kê 11).

Liệt kê 11. Tạo bảng cơ sở dữ liệu
    /**
     * Called at the time to create the DB.
     * The create DB statement
     * @param the SQLite DB
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(
                "create table " + TABLE_NAME + " (_id integer primary key autoincrement, 
" + " fid text not null, name text not null) ");
    }

Phương thức insert() được MainActivity gọi ra khi xuất khẩu thông tin tới cơ sở dữ liệu (xem Liệt kê 12).

Liệt kê 12. Chèn một hàng
    /**
     * The Insert DB statement
     * @param id the friends id to insert
     * @param name the friend's name to insert
     */
    public void insert(String id, String name) {
        db.execSQL("INSERT INTO friends('fid', 'name') values ('"
                + id + "', '"
                + name + "')");
    }

Phương thức deleteAll() được MainActivity gọi ra khi xóa cơ sở dữ liệu. Nó xóa bảng (xem Liệt kê 13).

Liệt kê 13. Xóa bảng cơ sở dữ liệu
    /**
     * Wipe out the DB
     */
    public void clearAll() {
        db.delete(TABLE_NAME, null, null);
    }

Hai phương thức SELECT ALL (Chọn tất cả) được cung cấp: cursorSelectAll(), trả về một con trỏ và listSelectAll(), trả về một ArrayList của các đối tượng Friend. Các phương thức này được MainActivity gọi ra khi nạp thông tin từ cơ sở dữ liệu (xem Liệt kê 14).

Liệt kê 14. Chạy một phương thức Select All để trả về một ArrayList
    /**
     * Select All returns a cursor
     * @return the cursor for the DB selection
     */
    public Cursor cursorSelectAll() {
        Cursor cursor = this.db.query(
                TABLE_NAME, // Table Name
                new String[] { "fid", "name" }, // Columns to return
                null,       // SQL WHERE
                null,       // Selection Args
                null,       // SQL GROUP BY 
                null,       // SQL HAVING
                "name");    // SQL ORDER BY
        return cursor;
    }

Phương thức listSelectAll() trả về hàng đã chọn trong một thùng chứa ArrayList được MainActivity sử dụng để ràng buộc nó vào MainScreen ListView (xem Liệt kê 15).

Liệt kê 15. Chạy một phương thức Select All để trả về một con trỏ
    /**
     * Select All that returns an ArrayList
     * @return the ArrayList for the DB selection
     */
    public ArrayList<Friend> listSelectAll() {
        ArrayList<Friend> list = new ArrayList<Friend>();
        Cursor cursor = this.db.query(TABLE_NAME, new String[] { "fid", "name" }, 
null, null, null, null, "name");
        if (cursor.moveToFirst()) {
            do {
                Friend f = new Friend();
                f.id = cursor.getString(0);
                f.name = cursor.getString(1);
                list.add(f);
            } while (cursor.moveToNext());
        }
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
        return list;
    }

Phương thức onUpgrade() được gọi nếu phát hiện ra một phiên bản cơ sở dữ liệu thay đổi (xem Liệt kê 16).

Liệt kê 16. Phát hiện xem có thay đổi phiên bản cơ sở dữ liệu không
    /**
     * Invoked if a DB upgrade (version change) has been detected
     */
    @Override
    /**
     * Invoked if a DB upgrade (version change) has been detected
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, 
       int oldVersion, int newVersion) {
        // Here add any steps needed due to version upgrade
        // for example, data format conversions, old tables 
        // no longer needed, etc
    }
}

Xuyên suốt MainActivity, DBHelper được sử dụng khi bạn xuất khẩu thông tin đến cơ sở dữ liệu, nạp thông tin từ cơ sở dữ liệu và khi bạn xóa cơ sở dữ liệu. Việc đầu tiên là khởi tạo DBHelper khi đã tạo MainActivity. Các nhiệm vụ khác được thực hiện tại phương thức onCreate() bao gồm việc khởi tạo các khung nhìn màn hình khác nhau (xem Liệt kê 17).

Liệt kê 17. Phương thức MainActivity onCreate() khởi tạo cơ sở dữ liệu
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    appContext = this;
    setContentView(R.layout.main);
    dbHelper = new DBHelper(this);
    listView = (ListView) findViewById(R.id.friendsview);
    friendsArrayAdapter = new FriendsArrayAdapter(
       this, R.layout.rowlayout, friends);
    listView.setAdapter(friendsArrayAdapter);
    :
    :
}

Liệt kê 18 cho thấy cách nạp danh sách những người bạn từ các tài sản và cách phân tích cú pháp và chèn nó vào trong cơ sở dữ liệu.

Liệt kê 18. MainActivity đang chèn vào trong cơ sở dữ liệu
String fname = prefsGetFilename();
if (fname != null && fname.length() > 0) {
    buffer = getAsset(fname);
    // Parse the JSON file
    String friendslist = new String(buffer);
    final JSONObject json = new JSONObject(friendslist);
    JSONArray d = json.getJSONArray("data");
    int l = d.length();
    for (int i2=0; i2<l; i2++) {
        JSONObject o = d.getJSONObject(i2);
        String n = o.getString("name");
        String id = o.getString("id");
        dbHelper.insert(id, n);
    }
    // Only the original owner thread can touch its views                           
    MainActivity.this.runOnUiThread(new Runnable() {
        public void run() {
            friendsArrayAdapter.notifyDataSetChanged();
        }
    });         
}

Liệt kê 19 cho thấy cách thực hiện một SELECT ALL (Chọn tất cả) và cách ràng buộc dữ liệu vào ListView của màn hình chính.

Liệt kê 19. Select All của MainActivity và ràng buộc dữ liệu vào ListView
final ArrayList<Friend> dbFriends = dbHelper.listSelectAll();
if (dbFriends != null) {
    // Only the original owner thread can touch its views                           
    MainActivity.this.runOnUiThread(new Runnable() {
        public void run() {
            friendsArrayAdapter = 
            new FriendsArrayAdapter(
                MainActivity.this, R.layout.rowlayout, dbFriends);
            listView.setAdapter(friendsArrayAdapter);
            friendsArrayAdapter.notifyDataSetChanged();
        }
    });
}

Tiếp theo, hãy xem xét việc sử dụng API lưu trữ trong với ứng dụng ví dụ.


Sử dụng lưu trữ trong của thiết bị cho dữ liệu riêng tư

Với API lưu trữ dữ liệu, bạn có thể lưu trữ dữ liệu bằng cách sử dụng lưu trữ trong. Thông tin có thể riêng tư và bạn có tùy chọn cho phép các ứng dụng khác có quyền đọc hoặc viết vào nó. Phần này trình bày API để lưu trữ dữ liệu cá nhân khi sử dụng android.content.Context.openFileInput, openFileOutput, và getCacheDir() để lưu dữ liệu vào bộ nhớ nhanh (cache) thay vì lưu trữ nó lâu bền.

Đoạn mã được hiển thị trong Liệt kê 20 cho thấy cách đọc từ bộ nhớ riêng bên trong. Việc sử dụng phương thức openFileOutput() với MODE_PRIVATE (Chế độ_ riêng tư) để làm cho lưu trữ thành riêng tư.

Liệt kê 20. Đọc từ lưu trữ riêng cục bộ
/**
 * Writes content to internal storage making the content private to 
 * the application. The method can be easily changed to take the MODE 
 * as argument and let the caller dictate the visibility: 
 * MODE_PRIVATE, MODE_WORLD_WRITEABLE, MODE_WORLD_READABLE, etc.
 * 
 * @param filename - the name of the file to create
 * @param content - the content to write
 */
public void writeInternalStoragePrivate(
        String filename, byte[] content) {
    try {
        //MODE_PRIVATE creates/replaces a file and makes 
        //  it private to your application. Other modes:
        //    MODE_WORLD_WRITEABLE
        //    MODE_WORLD_READABLE
        //    MODE_APPEND
        FileOutputStream fos = 
           openFileOutput(filename, Context.MODE_PRIVATE);
        fos.write(content);
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Đoạn mã trong Liệt kê 21 cho thấy cách đọc từ lưu trữ riêng bên trong; hãy xem cách sử dụng phương thức openFileInput().

Liệt kê 21. Đọc từ lưu trữ riêng bên trong
/**
 * Reads a file from internal storage
 * @param filename the file to read from
 * @return the file content
 */
public byte[] readInternalStoragePrivate(String filename) {
    int len = 1024;
    byte[] buffer = new byte[len];
    try {
        FileInputStream fis = openFileInput(filename);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int nrb = fis.read(buffer, 0, len); // read up to len bytes
        while (nrb != -1) {
            baos.write(buffer, 0, nrb);
            nrb = fis.read(buffer, 0, len);
        }
        buffer = baos.toByteArray();
        fis.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return buffer;
}

Liệt kê 22 cho thấy cách xóa khỏi lưu trữ riêng bên trong.

Liệt kê 22. Xóa khỏi lưu trữ riêng bên trong
/**
 * Delete internal private file 
 * @param filename - the filename to delete
 */
public void deleteInternalStoragePrivate(String filename) {
    File file = getFileStreamPath(filename);
    if (file != null) {
        file.delete();
    }
}

Bây giờ bạn có thể thấy cách sử dụng lưu trữ ngoài cho dữ liệu công cộng.


Sử dụng lưu trữ ngoài cho dữ liệu công cộng

Với các API lưu trữ dữ liệu, bạn có thể lưu trữ dữ liệu bằng cách sử dụng lưu trữ ngoài. Thông tin có thể là riêng tư và bạn có tùy chọn cho phép các ứng dụng khác có quyền đọc hoặc viết vào nó. Trong phần này, bạn viết mã lệnh API để lưu trữ dữ liệu công cộng bằng cách sử dụng một số trong các API bao gồm getExternalStorageState(), getExternalFilesDir(), getExternalStorageDirectory()getExternalStoragePublicDirectory(). Bạn sử dụng đường dẫn sau đây cho dữ liệu công cộng: /Android/data/<package_name>/files/.

Trước khi sử dụng lưu trữ ngoài, bạn phải xem nó có sẵn chưa và có cho phép ghi vào không. Hai đoạn mã sau đây cho thấy các phương thức của trình trợ giúp để kiểm tra các điều kiện như vậy. Liệt kê 23 kiểm tra xem liệu lưu trữ ngoài đã có sẵn chưa.

Liệt kê 23. Kiểm tra xem lưu trữ ngoài đã có sẵn chưa
/**
 * Helper Method to Test if external Storage is Available
 */
public boolean isExternalStorageAvailable() {
    boolean state = false;
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(extStorageState)) {
        state = true;
    }
    return state;
}

Liệt kê 24 kiểm tra xem lưu trữ ngoài là chỉ đọc hay không.

Liệt kê 24. Kiểm tra xem lưu trữ ngoài là chỉ đọc hay không
/**
 * Helper Method to Test if external Storage is read only
 */
public boolean isExternalStorageReadOnly() {
    boolean state = false;
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) {
        state = true;
    }
    return state;
}

Liệt kê 25 cho thấy cách ghi vào lưu trữ ngoài để lưu trữ dữ liệu công cộng.

Liệt kê 25. Viết vào bộ nhớ ngoài
/**
 * Write to external public directory
 * @param filename - the filename to write to
 * @param content - the content to write 
 */
public void writeToExternalStoragePublic(String filename, byte[] content) {

    // API Level 7 or lower, use getExternalStorageDirectory() 
    //  to open a File that represents the root of the external 
    // storage, but writing to root is not recommended, and instead 
    // application should write to application-specific directory, as shown below.

    String packageName = this.getPackageName();
    String path = "/Android/data/" + packageName + "/files/";

    if (isExternalStorageAvailable() && 
       !isExternalStorageReadOnly()) {
        try {
            File file = new File(path, filename);
            file.mkdirs();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(content);
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Liệt kê 26 cho thấy cách đọc từ bộ nhớ ngoài.

Liệt kê 26. Đọc từ bộ nhớ ngoài
/**
 * Reads a file from internal storage
 * @param filename - the filename to read from
 * @return the file contents
 */
public byte[] readExternallStoragePublic(String filename) {
    int len = 1024;
    byte[] buffer = new byte[len];
    String packageName = this.getPackageName();
    String path = "/Android/data/" + packageName + "/files/";

    if (!isExternalStorageReadOnly()) {     
        try {
            File file = new File(path, filename);            
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int nrb = fis.read(buffer, 0, len); //read up to len bytes
            while (nrb != -1) {
                baos.write(buffer, 0, nrb);
                nrb = fis.read(buffer, 0, len);
            }
            buffer = baos.toByteArray();
            fis.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return buffer;
}

Đoạn mã trong Liệt kê 27 cho thấy cách xóa một tệp khỏi bộ nhớ ngoài.

Liệt kê 27. Xóa một tệp khỏi bộ nhớ ngoài
/**
 * Delete external public file 
 * @param filename - the filename to write to
 */
void deleteExternalStoragePublicFile(String filename) {
    String packageName = this.getPackageName();
    String path = "/Android/data/" + packageName + "/files/"+filename;
    File file = new File(path, filename);
    if (file != null) {
        file.delete();
    }
}

Làm việc với bộ nhớ ngoài đòi hỏi phải có quyền hạn đặc biệt, cần phải yêu cầu WRITE_EXTERNAL_STORAGE, thông qua tệp AndroidManifest.xml (xem Liệt kê 28).

Liệt kê 28. WRITE_EXTERNAL_STORAGE
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

API lưu trữ ngoài cho phép bạn lưu trữ công cộng các tệp bằng cách lưu trữ các tệp trong các thư mục được xác định trước dựa vào các kiểu của chúng như các hình ảnh, các nhạc chuông và v.v. Cách tiếp cận này không được trình bày trong bài viết này, nhưng bạn nên biết rõ về nó. Ngoài ra, hãy nhớ rằng các tệp trong lưu trữ ngoài có thể bị xóa bất cứ lúc nào.


Các phương thức có liên quan

Nếu bạn có các tệp tạm thời không cần lưu giữ lâu dài, bạn có thể lưu trữ các tệp đó trong một bộ nhớ nhanh (cache) . Cache là một bộ nhớ đặc biệt có ích để lưu trữ dữ liệu cỡ vừa hoặc nhỏ (ít hơn một megabyte), nhưng bạn phải nhận thức được rằng nội dung của bộ nhớ cache có thể bị xóa bất cứ lúc nào tùy thuộc vào có sẵn bao nhiêu bộ nhớ.

Liệt kê 29 cho thấy một phương thức của trình trợ giúp để trả về đường dẫn đến bộ nhớ lưu trữ nhanh trong bộ nhớ trong.

Liệt kê 29. Lấy ra đường dẫn đến bộ nhớ lưu trữ nhanh của bộ nhớ trong
/**
 * Helper method to retrieve the absolute path to the application 
 * specific internal cache directory on the file system. These files 
 * will be ones that get deleted when the application is uninstalled or when 
 * the device runs low on storage. There is no guarantee when these 
 * files will be deleted.
 * 
 * Note: This uses a Level 8+ API.
 * 
 * @return the absolute path to the application specific cache 
 * directory
 */
public String getInternalCacheDirectory() {
    String cacheDirPath = null;
    File cacheDir = getCacheDir();
    if (cacheDir != null) {
        cacheDirPath = cacheDir.getPath();
    }
    return cacheDirPath;        
}

Liệt kê 30 cho thấy một phương thức của trình trợ giúp để trả về đường dẫn đến bộ nhớ lưu trữ nhanh trong bộ nhớ ngoài.

Liệt kê 30. Lấy ra đường dẫn đến bộ nhớ lưu trữ nhanh của bộ nhớ ngoài
/**
 * Helper method to retrieve the absolute path to the application 
 * specific external cache directory on the file system. These files 
 * will be ones that get deleted when the application is uninstalled or when 
 * the device runs low on storage. There is no guarantee when these 
 * files will be deleted.
 * 
 * Note: This uses a Level 8+ API.
 * 
 * @return the absolute path to the application specific cache 
 * directory
 */
public String getExternalCacheDirectory() {
    String extCacheDirPath = null;
    File cacheDir = getExternalCacheDir();
    if (cacheDir != null) {
        extCacheDirPath = cacheDir.getPath();
    }
    return extCacheDirPath;     
}

Thông qua cách sử dụng ứng dụng ví dụ này, bây giờ bạn đã có một sự hiểu biết tốt về cách sử dụng lưu trữ ngoài của thiết bị cho dữ liệu công cộng.


Kết luận

Bài viết này đã trình bày các API lưu trữ của Android, từ các sở thích đến việc sử dụng SQLite và bộ nhớ trong và bộ nhớ ngoài. Với API các sở thích, bạn có thể cho ứng dụng của bạn để thu thập và lưu trữ thông tin sở thích đơn giản. Khi sử dụng API SQLite, bạn có thể lưu trữ nhiều dữ liệu phức tạp hơn và với lưu trữ trong và lưu trữ ngoài, bạn có thể lưu trữ các tệp riêng tư với ứng dụng hoặc lưu trữ các tệp công cộng có sẵn tới các ứng dụng khác. Dữ liệu đã lưu trữ vẫn còn tồn tại trên các phiên làm việc cho phép ứng dụng của bạn làm việc ngay cả khi ngắt kết nối khỏi mạng. Bây giờ bạn sẽ có chuyên môn để tận dụng lợi thế của tất cả các loại thiết bị lưu trữ khi bạn phát triển ứng dụng Android.


Tải về

Mô tảTênKích thước
Article source codeandroid.storage.source.zip38KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=648327
ArticleTitle=Tìm hiểu về các API lưu trữ dữ liệu cục bộ của Android
publish-date=04222011