이 기사를 진행하려면 다음과 같은 스킬 및 도구가 필요하다.
- Java™ 기술과 Eclipse(또는 선호하는 IDE) 사용 방법에 대한 기본 지식
- Java Development Kit(버전 5 또는 6이 필요함)
- Eclipse(버전 3.4 또는 3.5)
- Android SDK 및 ADT 플러그인
다운로드 및 설치 정보는 이 기사의 끝에 있는 참고자료를 참조한다.
Android 애플리케이션 개발의 로컬 저장소 측면을 강조하기 위해 필자는 다양한 유형의 API 실행을 테스트할 수 있게 하는 샘플 애플리케이션에 대해 다룬다. 소스 코드를 다운로드할 수 있다. 애플리케이션은 그림 1에 있는 조치를 지원한다.
그림 1. 유스 케이스
그림 1에는 다음과 같은 유스 케이스가 나열된다.
- 환경 설정 관리 및 저장
- 애플리케이션 자산에서 정보 로드
- 내부 메모리, 외부 메모리 및 로컬 데이터베이스로 정보 내보내기
- 내부 메모리 및 로컬 데이터베이스에서 정보 읽어오기
- 저장된 정보 지우기
- 화면에 표시된 정보 확인
다음과 같이 이 기사에서는 전반적으로 애플리케이션에서 로컬 저장소를 사용한다.
- 환경 설정이 사용자로부터 캡처되어 로컬로 저장되고 애플리케이션 전반에서 사용된다.
- 사용자의 사진이 내부 애플리케이션 자산에서 검색되어 로컬 내부 메모리 및 외부 메모리에 저장되고 화면에 렌더링된다.
- JSON으로 된 친구 목록이 애플리케이션 자산에서 검색된다. 이 목록은 구문 분석되어 로컬 내부 메모리, 외부 메모리 및 관련 데이터베이스에 저장되고 화면에 렌더링된다.
샘플 애플리케이션은 표 1에 있는 클래스를 정의한다.
표 1. 샘플 애플리케이션 클래스
| 클래스 | 설명 |
|---|---|
MainActivity | 기본 활동(샘플 코드의 대부분이 속해 있음) |
Friend | 친구를 나타냄 |
AppPreferenceActivity | 환경 설정 활동 및 화면 |
DBHelper | SQLite 데이터베이스의 관리를 위한 헬퍼 클래스 |
예제 애플리케이션은 두 가지 유형의 데이터를 사용한다. 첫 번째 유형은 이름-값 쌍으로 저장되는 애플리케이션 환경 설정이다. 환경 설정의 경우 다음 정보를 정의한다.
- 파일 이름: 친구 이름 목록을 로드하고 저장하는 데 사용됨
- 파일 이름: 사용자의 사진을 로드하고 저장하는 데 사용됨
- 플래그: 애플리케이션 시작 시 저장된 모든 데이터를 자동으로 삭제하기 위해 표시됨(설정된 경우)
두 번째 유형의 데이터는 친구 목록이다. 친구 목록은 이름 및 친구 오브젝트의 배열로 구성된 Facebook Graph API JSON 형식으로 초기에 표시되었다(목록 1 참조).
목록 1. 친구 목록(Facebook Graph API JSON 형식)
{
"data": [
{
"name": "Edmund Troche",
"id": "500067699"
}
]
}
|
이와 같이 단순한 형식은 Friend 오브젝트와 데이터베이스 스키마를
단순하게 만든다. 목록 2에서는 Friend 클래스를 보여 준다.
목록 2.
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;;
}
|
ID와 이름 이외에 샘플 애플리케이션은 친구의 사진에 대한 참조도 보관한다. 샘플 애플리케이션이 이러한 참조를 사용하지 않는 동안 사용자는 샘플 애플리케이션을 쉽게 확장하여 Facebook에서 사진을 검색하여 기본 화면에 표시할 수 있다.
데이터베이스 스키마는 친구 정보를 저장하는 데 필요한 단일 테이블로 구성된다. 이 테이블에는 세 개의 열이 있다.
- 고유 ID 또는 키
- Facebook ID
- 친구 이름
목록 3에서는 해당 관계형 테이블 선언에 대한 SQL문을 보여 준다.
목록 3. 친구 데이터베이스 테이블
db.execSQL("create table " + TABLE_NAME + " (_id integer primary key autoincrement, "
+ " fid text not null, name text not null) ");
|
이 정보를 기반으로 이름을 기본 화면에 표시할 수 있고 ID를 사용하여 선택된 사용자에 대한 추가 세부사항을 검색할 수 있다. 샘플 애플리케이션에서는 이름만 표시한다. 추가 정보 검색 여부는 사용자가 결정한다. Facebook으로 직접 이동하도록 코드를 쉽게 변경할 수 있다.
이 섹션에서는 환경 설정 API와 화면에 대해 설명한다. Android API는 환경 설정을 처리하는 다양한 방법을 제공한다.
한 가지 방법은 SharedPreferences를 직접 사용하고 사용자 고유의 환경 설정 화면 설계 및 관리를 사용하는 것이다.
두 번째 접근 방식은 PreferenceActivity를 사용하는 것이다.
PreferenceActivity는 환경 설정이 화면에 렌더링되는 방식을 자동으로 처리(기본적으로 시스템의 환경 설정과 비슷함)하고
SharedPreferences를 사용하여 사용자가 각각의 환경 설정과 상호작용할 때 환경 설정을 자동으로 저장한다.
샘플 애플리케이션을 단순화하려면 PreferenceActivity를
사용하여 환경 설정과 환경 설정 화면을 관리한다(그림 2 참조).
환경 설정 화면에는 Assets와 Auto Settings라는 두 개의 섹션이 표시된다.
Assets 아래에서 Friends List 및 Picture 옵션의 파일 이름을 입력할 수 있다.
Auto Settings 아래에서 선택란을 체크하여 시작 시 정보를 삭제할 수 있다.
그림 2. 구현된 Preferences 화면
그림 2에서 레이아웃은 프로그래밍 방식 대신 XML을 통해 선언적 접근 방식을 사용하여 정의되었다. 선언적 XML은 소스 코드를 정리하고 가독성을 유지하기 때문에 선호된다. 목록 4에서는 환경 설정 UI에 대한 XML 선언을 보여 준다.
목록 4. 환경 설정 화면 XML 선언
<?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은 EditTextPreference의 두 가지 인스턴스,
CheckBoxPreference 및 PreferenceCategory에 의해 정의되는 두 개의 카테고리
그룹(하나는 Asset을 위한 그룹이고 다른 하나는 Auto Settings를 위한 그룹임)으로 구성된다.
샘플 애플리케이션의 설계에서는 메뉴 항목을 사용하여 환경 설정 화면을 호출하도록
요구한다. 이를 위해 의도 메시지를 사용하여 AppPreferenceActivity라는 환경 설정 화면 활동을 호출한다(목록 5 참조).
의도의 작동 방식에 대해서는 자세히 다루지 않는다. 의도에 대한 자세한 정보는 참고자료를 참조한다.
목록 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
}
}
|
샘플 애플리케이션에서 메뉴 항목 처리기로부터 목록 6과 같이 의도를 호출한다.
목록 6. 의도를 사용하여 환경 설정 활동 호출하기
/**
* 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;
}
|
또한 목록 7과 같이 모든 의도를 AndroidManifest XML 파일에 정의해야 한다.
목록 7. AndroidManifest.xml에서 의도 정의하기
:
<application android:icon="@drawable/icon" android:label="@string/app_name">
:
:
<activity
android:name="AppPreferenceActivity"
android:label="Preferences">
</activity>
:
</application>
|
PreferenceActivity는 사용자가 환경 설정 화면과 상호작용 할 때
SharedPreferences를 사용하여 자동으로 환경 설정을 저장한다고
한 것을 떠올려 본다. 애플리케이션은 실행될 때 이러한 환경 설정을 사용하여
다양한 태스크를 수행한다. 목록 8에서는 SharedPreferences를
직접 사용하여 저장된 환경 설정을 로드하는 방법을 보여 준다. 동반 샘플 코드를 참조하여
샘플 코드 전체에서 로드된 환경 설정이 어떻게 로드되는지 확인할 수 있다.
또한 목록 8에서는 PrefenceActivity를 통하지 않고
환경 설정을 자체적으로 관리하길 원하는 경우 Editor를 사용하는 SharedPreferences를
사용하여 직접 환경 설정을 저장하는 방법을 보여 준다.
목록 8에서는 SharedPreferences를 사용하여 저장된 환경 설정을 로드하고
Editor를 사용하여 저장된 환경 설정에 대한 변경사항을 작성하는 방법에 대해 설명한다.
목록 8.
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();
}
|
다음으로 데이터베이스를 사용하여 데이터를 저장하는 방법에 대해 살펴본다.
Android는 SQLite를 통해 로컬 관계형 데이터베이스에 대한 지원을 제공한다. 다음 목록에 정의된 테이블에는 샘플 애플리케이션에 사용되는 중요 데이터베이스 클래스가 요약되어 있다.
샘플 애플리케이션의 경우 DBHelper 클래스를 사용하여 일부
데이터베이스 조작을 캡슐화한다(목록 9 참조).
목록 9.
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 {
|
데이터베이스 버전, 데이터베이스 이름 및 테이블 이름에 해당하는 다수의 상수가 정의되어 있다(목록 10 참조).
목록 10.
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();
}
|
데이터베이스를 작성할 준비가 되면 onCreate() 메소드가 호출된다.
이 메소드에서 테이블이 작성된다(목록 11 참조).
목록 11. 데이터베이스 테이블 작성하기
/**
* 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) ");
}
|
정보를 데이터베이스에 내보낼 때 MainActivity에 의해 insert() 메소드가 호출된다(목록 12 참조).
목록 12. 행 삽입하기
/**
* 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 + "')");
}
|
데이터베이스를 지울 때 MainActivity에 의해 deleteAll() 메소드가 호출된다.
이 메소드는 테이블을 삭제한다(목록 13 참조).
목록 13. 데이터베이스 테이블 삭제하기
/**
* Wipe out the DB
*/
public void clearAll() {
db.delete(TABLE_NAME, null, null);
}
|
커서를 리턴하는 cursorSelectAll() 메소드와 Friend 오브젝트의 ArrayList를 리턴하는 listSelectAll() 메소드라는 두 가지 SELECT ALL 메소드가 제공된다.
이러한 메소드는 데이터베이스에서 정보를 로드할 때 MainActivity에 의해 호출된다(목록 14 참조).
목록 14.
ArrayList를 리턴하는 Select All 실행하기
/**
* 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;
}
|
listSelectAll() 메소드는 MainActivity에 의해 MainScreen ListView에 바인드하는 데 사용되는 ArrayList 컨테이너 내의 선택된 행을 리턴한다(목록 15 참조).
목록 15. 커서를 리턴하는
Select All 실행하기
/**
* 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;
}
|
데이터베이스 버전 변경이 감지되면 onUpgrade() 메소드가 호출된다(목록 16 참조).
목록 16. 데이터베이스 버전이 변경되는지 감지하기
/**
* 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
}
}
|
MainActivity 전체에서 정보를 데이터베이스에 내보내고
데이터베이스로부터 정보를 로드하고 데이터베이스를 지울 때 DBHelper가
사용된다. 첫 번째 작업은 MainActivity가 작성될 때 DBHelper를 인스턴스화하는 것이다.
onCreate()에 수행된 기타 태스크에는 다른 화면 보기의 초기화가 포함된다(목록 17 참조).
목록 17. 데이터베이스를 초기화하는
MainActivity onCreate()
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);
:
:
}
|
목록 18에서는 자산으로부터 친구 목록을 로드하는 방법과 해당 목록을 구문 분석하여 데이터베이스에 삽입하는 방법을 보여 준다.
목록 18. 데이터베이스에 삽입하는
MainActivity
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();
}
});
}
|
목록 19에서는 SELECT ALL을 수행하는 방법과
데이터를 기본 화면 ListView에 바인드하는 방법을 보여 준다.
목록 19.
MainActivity Select All과 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();
}
});
}
|
다음으로 예제 애플리케이션에 대해 내부 스토리지 API를 사용하는 것에 대해 살펴본다.
개인용 데이터를 위해 디바이스의 내부 스토리지 사용하기
데이터 스토리지 API를 사용하면 내부 스토리지를 통해 데이터를 저장할 수 있다.
정보는 개인용일 수 있으므로 사용자에게는 다른 애플리케이션이 해당 정보에 대한 읽기 또는 쓰기 액세스 권한을 가질 수 있게 하는 옵션이 있다.
이 섹션에서는 데이터를 지속적으로 저장하는 대신 android.content.Context.openFileInput, openFileOutput 및 getCacheDir()를
사용하여 데이터를 캐싱하여 개인 데이터를 저장하는 데 필요한 API에 대해 다룬다.
목록 20에 표시된 스니펫에서는 내부 개인 저장소로부터 읽는 방법을 보여 준다.
스토리지를 개인용으로 만드는 방법은 openFileOutput()를 MODE_PRIVATE과 함께 사용하는 것이다.
목록 20. 로컬 개인 저장소로부터 읽기
/**
* 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();
}
}
|
목록 21에 있는 스니펫에서는 내부 개인 저장소로부터 읽는 방법을 보여 준다. openFileInput()가 사용되는 것을 확인한다.
목록 21. 로컬 개인 저장소로부터 읽기
/**
* 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;
}
|
목록 22에서는 내부 개인 저장소로부터 삭제하는 방법을 보여 준다.
목록 22. 로컬 개인 저장소로부터 삭제하기
/**
* Delete internal private file
* @param filename - the filename to delete
*/
public void deleteInternalStoragePrivate(String filename) {
File file = getFileStreamPath(filename);
if (file != null) {
file.delete();
}
}
|
이제 공용 데이터를 위해 외부 스토리지를 사용하는 방법을 알 수 있다.
데이터 스토리지 API를 사용하면 외부 스토리지를 통해 데이터를 저장할 수 있다.
정보는 개인용일 수 있으므로 사용자에게는 다른 애플리케이션이 해당 정보에 대한 읽기 또는 쓰기 액세스 권한을 가질 수 있게 하는 옵션이 있다.
이 섹션에서는 getExternalStorageState(), getExternalFilesDir(), getExternalStorageDirectory() 및
getExternalStoragePublicDirectory()를 포함한 다수의 API를 사용하여 공용 데이터를 저장하도록 API를 코딩한다.
다음 경로를 공용 데이터에 사용한다. /Android/data/<package_name>/files/.
외부 스토리지를 사용하기 전에 먼저 사용 가능 여부와 쓰기 가능 여부를 확인해야 한다. 다음 두 코드 스니펫에서는 이러한 조건에 대해 테스트하는 데 필요한 헬퍼 메소드를 보여 준다. 목록 23에서는 외부 스토리지를 사용할 수 있는지 테스트한다.
목록 23. 외부 스토리지를 사용할 수 있는지 테스트하기
/**
* 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;
}
|
목록 24에서는 외부 스토리지가 읽기 전용인지 테스트한다.
목록 24. 외부 스토리지가 읽기 전용인지 테스트하기
/**
* 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;
}
|
목록 25에서는 공용 데이터를 저장하기 위해 외부 스토리지에 쓰는 방법을 보여 준다.
목록 25. 외부 메모리에 쓰기
/**
* 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();
}
}
}
|
목록 26에서는 외부 스토리지로부터 읽는 방법을 보여 준다.
목록 26. 외부 메모리로부터 읽기
/**
* 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;
}
|
목록 27 스니펫에서는 외부 메모리로부터 파일을 삭제하는 방법을 보여 준다.
목록 27. 외부 메모리로부터 파일 삭제하기
/**
* 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();
}
}
|
외부 스토리지에 대해 작업하려면 AndroidManifest.xml을 통해 특별한 권한(WRITE_EXTERNAL_STORAGE)을 요청해야 한다(목록 28 참조).
목록 28.
WRITE_EXTERNAL_STORAGE<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
외부 스토리지 API를 사용하면 Pictures, Ringtones 등과 같은 유형을 기반으로 사전정의된 디렉토리에 파일을 저장하여 파일을 공개적으로 저장할 수 있다. 이 접근 방식은 이 기사에서 다루지 않지만 이 접근 방식에 익숙해져야 한다. 또한 외부 스토리지에 있는 파일은 언제든지 사라질 수 있다는 것을 기억한다.
장기 지속성이 필요하지 않은 임시 파일이 있는 경우 해당 파일을 캐시에 저장할 수 있다. 캐시는 중간 크기 또는 작은 데이터(메가바이트 미만)를 저장하는 데 유용한 특별한 메모리이지만 캐시의 컨텐츠는 사용 가능한 메모리에 따라 언제든지 제거될 수 있다는 것을 알고 있어야 한다.
목록 29에서는 내부 메모리에 있는 캐시의 경로를 리턴하는 헬퍼 메소드를 보여 준다.
목록 29. 내부 메모리 캐시의 경로 검색하기
/**
* 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;
}
|
목록 30에서는 외부 메모리에 있는 캐시의 경로를 리턴하는 헬퍼 메소드를 보여 준다.
목록 30. 외부 메모리 캐시의 경로 검색하기
/**
* 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;
}
|
예제 애플리케이션의 사용을 통해 이제 공용 데이터를 위해 디바이스의 외부 스토리지를 사용하는 방법에 대해 적절히 이해하고 있다.
이 기사에서는 환경 설정에서 SQLite와 내부 및 외부 메모리 사용에 이르는 Android 스토리지 API에 대한 내용을 다루었다. 환경 설정 API를 사용하면 애플리케이션이 단순 환경 설정 정보를 수집하고 저장하도록 할 수 있다. SQLite API를 사용하여 더 복잡한 데이터를 저장할 수 있으며 내부 및 외부 스토리지를 사용하여 애플리케이션에 대해 개인용이거나 다른 애플리케이션에 공용으로 사용 가능한 파일을 저장할 수 있다. 세션에서 지속되는 저장된 데이터를 사용하면 네트워크와 연결이 끊긴 경우에도 애플리케이션이 작동할 수 있다. 이제 Android 애플리케이션 개발 시 이러한 유형의 스토리지를 모두 활용하는 데 필요한 전문지식을 가지고 있다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Article source code | android.storage.source.zip | 38KB | HTTP |
교육
- Facebook 모바일 웹 페이지: 휴대전화에 적합하게 형식화된 모든 웹 사이트에 제공된 것과 동일한 API를 사용하여 사용자 자체 모바일 애플리케이션에 Facebook을 통합하는 방법에 대해 살펴보자.
- Create an Application - Facebook 개발자: Facebook 애플리케이션을 등록하자.
- Extended permissions - Facebook 개발자: 애플리케이션이 개인용일 수 있는 사용자 프로파일의 다른 부분에 액세스해야 하거나 애플리케이션이 사용자 대신 Facebook에 컨텐츠를 공개해야 하는 경우 확장 권한을 요청하자.
- 공식적인 Facebook 개발자 문서: 사회적 경험을 창출하여 웹 사이트에서 성장과 개입을 촉진할 수 있도록 하는 강력한 API에 대해 살펴보자.
- Facebook Authentication: Facebook 플랫폼을 사용하여 애플리케이션을 개발할 때의 인증 및 권한 부여에 대해 살펴보자.
- Facebook Developer Roadmap: 이 로드맵을 사용하여 코드 수정이 필요할 수 있는 변경사항에 대한 계획을 수립하자.
- Develop Android applications with Eclipse(Frank Ableson, developerWorks, 2008년 2월): Android 애플리케이션을 개발하는 가장 쉬운 방법은 Eclipse를 사용하는 것이다. 이 developerWorks 튜토리얼에서 이 주제에 대한 모든 내용을 살펴보자.
- Android 개발 소개(Frank Ableson, developerWorks, 2009년 5월): Android 플랫폼에 대한 소개 기사를 읽고 기본적인 Android 애플리케이션을 코딩하는 방법을 살펴보자.
- Networking with Android(Frank Ableson, developerWorks, 2009년 6월): Android의 네트워킹 기능을 탐구하자.
- Working with XML on Android(Michael Galpin, developerWorks, 2009년 6월): Android에서 XML을 처리하는 다양한 옵션과 이러한 옵션을 사용하여 자신만의
Android 애플리케이션을 빌드하는 방법을 배운다.
- Under the Hood of Native Web Apps for Android: 하이브리드 Android 애플리케이션에 관해 배워보자.
- Unlocking Android(Frank Ableson, Manning Publications, 2010년): 이 책에서 Android 개발의 모든 측면을 살펴보자.
- Mobile Design and Development(Brian Fling, O'Reilly Media, 2009년): 이 책에서 모바일 제품을 빌드하는 데 필요한 우수 사례와 실용적 지침, 표준, 기술에
관해 알아보자.
- Data Storage 개발자 안내서: Android 개발자 공식 웹 사이트에 언급된 대로 지속적 애플리케이션 데이터를 저장하는 데 필요한 올바른 솔루션을 선택해 보자.
- Intent: Android 개발자 사이트에서 수행될 조작에 관한 추상적인 설명에 대해 살펴보자.
- Android SDK 문서: Android API 참조에서 최신 정보를 얻자.
- Open Handset Alliance: Android를 후원하는 OHA(Open Handset Alliance)를 방문해보자.
- 필자의 더 많은 기사(C. Enrique Ortiz, developerWorks, 2004년 7월 - 현재): Android, 모바일 애플리케이션, 웹 서비스 및 기타 기술에 대한 기사를 읽어보자.
- developerWorks의 XML 영역: XML 영역에서 기술 향상에 도움이 되는 참고자료를 얻을 수 있다.
- My developerWorks: developerWorks와 관련된 경험을 개인화할 수 있다.
- IBM XML 인증: XML 및 관련 기술에 대한 IBM 인증 개발자가 되는 방법을 찾아볼 수 있다.
- XML 기술 자료: developerWorks XML 영역에서 다양한 기술 관련 기사와 팁, 튜토리얼, 표준 및 IBM Redbook을 볼 수 있다. 또한 더 많은 XML 팁을 읽어본다.
- developerWorks 기술 행사 및 웹 캐스트: 이러한 세션에 참가하여 최신 기술에 대한 정보를 얻을 수 있다.
- Twitter의 developerWorks 페이지: 오늘 가입하여 developerWorks 트윗을 팔로우하자.
- developerWorks
podcasts: 소프트웨어 개발자의 흥미로운 인터뷰와 토론을 확인할 수 있다.
- developerWorks on-demand demos: 입문자를 위한 제품 설치 및 설정 과정에서 숙련된 개발자를 위한 고급 기능의 활용에 이르기까지 다양한 데모를 제공한다.
제품 및 기술 얻기
- Facebook Android SDK(현재 릴리스는 Alpha): 이 라이브러리를 통해 Facebook을 Android 모바일 애플리케이션에 통합해 보자.
- Facebook Platform: Facebook과 웹에서 소셜 애플리케이션을 빌드하는 기능을 확장해 보자.
- Facebook Old Rest API(Graph API의 이전 버전): 단순 HTTP 요청을 통해 프로그래밍 방식으로 Facebook 웹 사이트와 상호작용해 보자.
- Facebook Graph API(현재 버전): 이 핵심 Facebook Platform API에 대해 살펴보자.
- OAuth 2.0 Protocol 스펙(2010년 7월): Facebook Platform에서 지원하는 OAuth 인증에 대해 작업해 보자.
- Android SDK: 공식 Android 개발자 사이트에서 SDK를 다운로드하고 API 참조에 액세스하고 Android 관련 최신 뉴스를 확인해 보자.
1.5 이상의 버전을 사용할 수 있다.
- Android Open Source Project: Android 호환 디바이스를 빌드하기 위해 필요한 오픈 소스 정보 및 소스 코드를 찾아보자.
- JDK 6 Update 21: Java Platform, Standard Edition을 얻을 수 있다.
- Eclipse: 최신 Eclipse IDE를 다운로드하자.
- IBM 제품 평가판: IBM SQA Sandbox의 온라인 시험판을 다운로드하거나 살펴보고 DB2®,
Lotus®, Rational®, Tivoli® 및
WebSphere®의 애플리케이션 개발 도구 및 미들웨어 제품을 사용해 볼 수 있다.
토론
- XML 영역 토론 포럼: 여러 XML 관련 토론에 참여해 볼 수 있다.
- developerWorks 포럼 & 블로그를 통해 developerWorks 커뮤니티에 참여하자.
C. Enrique Ortiz는 오랜 기간 동안 모바일 기술자이자 개발자이자 저자로서 일해 왔다. 블로그 주소는 About Mobility이며 Austin chapter of MobileMonday의 설립자이다.