반응형

 데이터베이트(database)에 데이터를 저장하는 것은 마치 주소록 정보같이, 반복되고 구조화된 데이터에 가장 이상적인 방법입니다.

이번 시간은 여러분이 일반적인 SQL 데이터베이스를 잘 알고 있다고 가정하고 설명할 것이며 안드로이드에서의 SQLite 데이터베이스(database)를 시작하는 데 도움이 되고자 합니다. 여러분이 Android 에서 데이터베이스(database)를 사용하는데 필요한 API 들은android.database.sqlite 패키지안에 있습니다.



스키마(Schema) 정의와 Contract (Define a Schema and Contract)

-----------------------------------------------------

 SQL 데이터베이스의 주요 법칙중 하나는 스키마(schema)입니다. 스키마(schema)는 데이터 베이스가 어떻게 구성되어 있는가에 대한 공식적인 선언입니다. 스키마는 여러분이 여러분의 데이터베이스(database)를 생성하는데 사용하는 SQL 문을 나타냅니다. 여러분은 명시적이고 자기 문서화 하는 방법으로 스키마의 레이아웃을 지정하는 Contract 클래스로 알려진 companion 클래스을 생성하는데 유용한 것을 찾게 될 겁니다. 


contract 클래스는 URIs, 테이블, 컬룸(Column)에 대한 이름을 정의한 상수에 대한 컨테이너(container) 입니다. contract 클래스는 같은 패키지 안에 있는 모든 다른 클래스를 통틀어 같은 상수를 사용하도록 합니다. 이것은 여러분인 하나의 장소(위치)에서 컬룸(Column)의 이름을 변경하도록 하며, 그것을 가지고 있는 있는 여러분의 코드 전체에 변경 사항이 반영되도록 합니다.


contract 클래스를 조직하는 좋은 방법은 클래스의 루트(root)레벨에 여러분의 전체 데이터베이스(database)를 전역(global)으로 정의해 놓는 것입니다. 그런 다음 컬룸(column)을 열거한 각 테이블에 대한 이너 클래스(inner class)을 만듭니다.


Note : BaseColumns 인터페이스(interface)를 구현함으로서, 여러분의 이너클래스(inner class)는 cursor adapter 같은 일부 안드로이드 클래스들이 가지고 있을 것으로 기대하고 있는 _ID라고 불려지는 주요 키(프라이머리 키 : primary key)를 상속할 수 있습니다. 이것이 반드시 필요하지는 않지만, 이를 통해 여러분이 Android 프레임워크(framework)와 조화롭게 데이터베이스를 작업할 수 있도록 도와 줍니다.



아래는 단일 테이블에 대해 테이블 이름과 컬룸(column) 이름을 정의하는 부분의 예제입니다.

 public final class FeedReaderContract {

    // To prevent someone from accidentally instantiating the contract class,

    // give it an empty constructor.

    public FeedReaderContract() {}


    /* Inner class that defines the table contents */

    public static abstract class FeedEntry implements BaseColumns {

        public static final String TABLE_NAME = "entry";

        public static final String COLUMN_NAME_ENTRY_ID = "entryid";

        public static final String COLUMN_NAME_TITLE = "title";

        public static final String COLUMN_NAME_SUBTITLE = "subtitle";

        ...

    }

}




SQL Helper를 사용하여 데이터베이스 생성하기

(Create a Database Using a SQL Helper)

--------------------------------------

여러분이 데이터베이스의 모양을 정의할 때, 여러분은 데이터베이스(database)와 테이블(table)을 생성하고 유지하는 메소드들을 구현해야만 합니다. 아래 테이블을 만들고 삭제하는 몇가지 전형적인 명세서(statment)이 있습니다.

 private static final String TEXT_TYPE = " TEXT";

private static final String COMMA_SEP = ",";

private static final String SQL_CREATE_ENTRIES =

    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +

    FeedEntry._ID + " INTEGER PRIMARY KEY," +

    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +

    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +

    ... // Any other options for the CREATE command

    " )";


private static final String SQL_DELETE_ENTRIES =

    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;


 여러분이 단말기의 내부저장소(internal storage)에 저장하는 파일처럼, 안드로이드는 여러분의 데이터베이스(database)를 어플과 관련된 프라이빗(private) 디스크(disk)의 공간에 저장합니다. 여러분의 데이터는 안전합니다. 왜냐하면, 기본적으로 이 영역은 다른 어플들이 접근할 수 없기 때문입니다.



유용한 API들은 SQLiteOpenHelper 클래스에서 사용할 수 있습니니다. 여러분이 데이터베이스(database)를 참조하는 인스턴스를 얻기위해 이 클래스를 사용할때, 시스템은 데이터베이스를 생성하고 업데이트하는 명령같이 오래 걸리는 명령들을 수행할 수도 있기에 오직 데이터베이스가 필요할 때만 사용되며 startup하는 동안에는 수행하지 않습니다. 여러분이 해야할 모든 것은 getWirteableDatabase() 또는 getReadableDatabase()를 호출하는 것입니다.


Note : getWriteableDatabase() 또는 getReadableDatabase() 역시 오래 걸릴 수 있게 때문에, AsyncTask 또는IntentService 와 같이 백그라운드 스레드(Background Thread)안에서 호출해야 합니다.


SQLiteOpenHelper 를 사용하기 위해, onCreate()onUpgrade() 그리고 onOpen() 콜백 메소드들을 오버라이드(override)한 하위클래스(subclass)를 생성합니다. 여러분이 onDowngrade() 를 구현할지도 모르지만, 꼭 요구 되지는 않습니다.



아래는 위에 소개된 명령 중 일부를 사용하는 SQLiteOpenHelper의 구현에 대한 예제입니다.

 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);

    }

}


여러분의 데이터베이스에 접속하기 위해서는, SQLiteOpenHelper의 하위클래스(subclass)을 인스턴스화 합니다.

  FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());




데이터베이스에 정보 넣기( Put Information into a Database)

-------------------------------------------------

ContentVauses 객체를 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_ENTRY_ID, id);

values.put(FeedEntry.COLUMN_NAME_TITLE, title);

values.put(FeedEntry.COLUMN_NAME_CONTENT, content);


// Insert the new row, returning the primary key value of the new row

long newRowId;

newRowId = db.insert(

         FeedEntry.TABLE_NAME,

         FeedEntry.COLUMN_NAME_NULLABLE,

         values);


insert() 메소드의 첫번째 인자는 간단한 테이블 이름입니다. 두번째 인자는 컬룸(Column)의 이름을 제공합니다. 프레임워크(framework)는 ContentValues 가 비어있는 상황에서는 이곳에 NULL을 삽입할 수 있습니다.(만약 여러분이 넣을 값이 없어서, 이 곳을 null 로 대신하면 프레임워크(framework)는 row를 삽입하지 않습니다.)




데이터베이스에서 정보 읽기( Read Information from a Database)

-----------------------------------------------------

데이터 베이스로부터 정보를 읽기위해 query() 메소드를 사용하며, 이것에 여러분이 읽기를 원하는 컬룸(column)과 기준 selection을 전달 합니다. 이 메소드는 insert()  update()의 요소를 혼합한 것으로, 여러분이 삽입했던 데이터에 비해서 여러분이 원하는 데이터만 얻어오기 위해 컬룸(column) 목록중 원하지 않는 나머지는 제외합니다. 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_UPDATED,

    ...

    };


// How you want the results sorted in the resulting Cursor

String sortOrder =

    FeedEntry.COLUMN_NAME_UPDATED + " DESC";


Cursor c = 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

    );


Cursor 안에 있는 row 를 보기위해서는 항상 값을 읽기 시작하기 전에 Cursor 이동 메소드중 하나를 호출해야만 합니다. 일반적으로 여러분은 query의 결과안에 있는 첫번째 데이터의 "읽기 위치"에 Cursor가 놓여지도록 moveToFirst() 메소드를 호출하여 시작하도록 합니다. 각 각의 row에 대해, 여러분은 Cursor의 get 메소드들 중 하나(getString() 또는 getLong() 같은 )을 호출함으로서 컬룸(Column)의 값을 읽을 수 있습니다. get 메소드들 각각을 사용할 때, 여러분은 반드시 여러분이 원하는 column의 인덱스(index) 위치를 전달해야만 합니다. 컬룸(Column)의 인덱스(index)는 getColumnIndex() 또는 getColumnIndexOrThrow()를 호출해서 얻어낼 수 있습니다. 예:

 cursor.moveToFirst();

long itemId = cursor.getLong(

    cursor.getColumnIndexOrThrow(FeedEntry._ID)

);



데이터베이스로부터 정보 삭제하기(Delete Information from a Database)

-----------------------------------------------------------

DB테이블로부터 row들을 삭제하기 위해서는, row를 식별할 수 있는 기준 selection(일종의 검색 조건)을 제공해야만 합니다. 데이터베이스 API는 SQL 인젝션(injection :SQL문의 취약성)을 방지하기 위해 기준 seletion 을 생성하는 것에 대한 메커니즘(mechanism)을 제공합니다. 이 메커니즘은 selection을 selection 절(clause)과 selection 인자들(arguments)로 특성을 분할합니다. selection 절은 형태를 보면 Column들을 정의하고 또한 Column 테스트와 결합할 수 있습니다. selection 인자들은seletion 절에 연결되어 테스트할(검색) 조건에 대응되는 값입니다. 그 결과는 정규 SQL 문과 같이 처리되지 않기 때문에 SQL 인젝션(injection)에 대해 영향을 받지 않습니다.


 // Define 'where' part of query.

String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";

// Specify arguments in placeholder order.

String[] selectionArgs = { String.valueOf(rowId) };

// Issue SQL statement.

db.delete(table_name, selection, selectionArgs);




데이터베이스 갱신하기( Update a Database)

------------------------------------

여러분이 데이터베이스의 값중 일부를 수정해야 하는 경우에는 Update() 메소드를 사용합니다.


DB테이블을 업데이트 하는 것은 insert()의 컨텐츠 값(contents value)구문과 delete()의 where구문을 결합한 것입니다.

 SQLiteDatabase db = mDbHelper.getReadableDatabase();


// New value for one column

ContentValues values = new ContentValues();

values.put(FeedEntry.COLUMN_NAME_TITLE, title);


// Which row to update, based on the ID

String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";

String[] selectionArgs = { String.valueOf(rowId) };


int count = db.update(

    FeedReaderDbHelper.FeedEntry.TABLE_NAME,

    values,

    selection,

    selectionArgs);


반응형
반응형

내부 저장장치에 파일 저장하기(Save a File on Internal Storage)

---------------------------------------------------

 내부 저장소(Internal Storage)에 파일을 저장 할 때, 여러분은 아래의 두개 메소드중 하나를 호출함으로서 파일을 저장할 알맞은 디렉토리를 가지고 있는 File 인스턴스를 얻을 수 있습니다.


getFileDir()

   여러분의 앱이 저장된 내부 저장소의 디렉토리(internal directory)를 나타내고 있는 File 인스턴스(instance)를 리턴합니다.


getCacheDir()

   앱의 임시 캐시(cache) 파일을 저장하고 있는 내부 저장소의 디렉토리를 나타내고 있는 File 인스턴스를 리턴합니다. 파일이 더 이상 필요하지 않으면 각 파일을 삭제해야 하며 1MB와 같이, 주어진 시간에 사용되는 메모리의 총양에 대한 합리적인 크기 제한을 구현합니다. 만약 시스템이 저장공간이 부족해 지기 시작하면, 어떠한 경고도 없이 여러분의 캐시(cache) 파일을 제거 할 수도 있습니다.


 이들 디렉토리중 하나에 새로운 파일을 생성하기 위해서는, File() 생성자함수를 호출하면서 파라미터로 여러분의 내부 저장 디렉토리를 지정하는 위의 메소드중 하나를 사용해서 File 객체를 전달합니다. 예:

 File file = new File(context.getFilesDir(), filename);


 위를 대체해서, 여러분은 앱의 내부 저장 디렉토리의 파일에 쓰기(저장)을 하도록 하는 FileOutputStream객체를 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();

 }


또한, 만약 일부의 파일들을 캐시메모리에 저장할 필요가 있다면, 여러분은 createTempFile()로 대신하여 사용해야 합니다. 예를 들어, 아래의 매소드는 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;

}


Note : 앱이 저장되는 내부 저장 디렉토리는 안드로이드 파일 시스템의 특정 지역안에 앱의 패키지 이름으로 지정됩니다. 기술적으로, 다른 앱은 여러분이 읽을 수 있도록 설정하기만 했다면 여러분 앱의 내부 저장 파일을 읽을 수 있습니다. 어쨋든, 다른 앱 역시 여러분 앱의 패키지 이름과 파일 이름을 알아야만 합니다. 다른 앱들은 여러분이 명시적으로 여러분의 파일을 읽고 쓸 수 있도록 설정하지 않은 상태에서 여러분의 내부 디렉토리를 탐색하거나 읽고 쓸수 없습니다. 그래서 여러분이 내부 저장소에 있는 여러분의 파일에 대해MODE_PRIVATE로 하기만하면, 다른 앱들은 절대로 여러분의 파일에 접속할 수 없습니다.




외부 저장장치에 파일 저장하기(Save a File on External Storage)

---------------------------------------------------

 사용자가 PC에 있는 저장장치를 사용하거나 외부 저장장치(External Storage)로 제공된 SD card 를 제거햇을 때와 같이 외부 저장장치를 사용할 수 없을 지도 모르게 때문에 여러분은 장치에 접속하지 전에 저장장치가 사용할 수 있는지 항상 점검해야만 합니다. 여러분은 getExternalStorageState() 메소드를 호출함으로서 외부 저장장치(External Storage)의 상태를 요청할 수 있습니다. 만약 이 메소드로부터 리턴된 상태가 MEDIA_MOUNTED 라면, 여러분은 파일을 이 장치에 읽고 쓸 수 있습니다. 예를 들어, 아래의 메소드들은 저장장치의 사용가능여부를 알아내는데 매우 유용합니다.

 /* 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 files

  다른 앱들과 사용자에게 자유롭게 사용할 수 있도록 되어 있는 파일입니다. 사용자가 앱을 언인스톨(uninstall)할 때, 이 파일들은 여전히 사용자가 사용할 수 있는 상태로 남아 있습니다.


 예: 앱에 의한 사진 캡쳐 또는 또다른 다운로드 된 파일들



Private files

  사용자가 앱을 제거할 때 당연하게 앱에 속하며 삭제되어야 할 파일입니다. 비록 기술적으로는 이 파일들이 외부저장장치에 있기 때문에 다른 앱들과 사용자에 의해 접속될 수 있어야 하지만, 실제로는 여러분의 앱 밖으로 값을 제공하지 않는 파일입니다. 사용자가 여러분의 앱을 언인스톨(uninstall)할 때, 시스템은 앱의 외부 프라이빗(Private) 디렉토리안에 있는 모든 파일들을 삭제합니다. 


 예: 앱에 의해 다운로드된 추가적인 리소스들 또는 임시 미디어(Media) 파일들


  

만약 여러분이 외부 저장장치(External Storage)에 파일을 저장하길 원한다면, getExternalStoragePublicDirectory() 메소드를 사용하여 외부 저장장치의 적절한 디렉토리를 나타내는 File 인스턴스(instance)를 얻어야 합니다. 이 메소드는 여러분이 저장하길 원하는 파일의 타입(Type)을 지정하는 인자(argument)를 가집니다( DIRECTORY_MUSIC 또는 DIRECTORY_PICTURE 등). 그래서 다른public 파일들과 디렉토리안에서 논리적으로 조직되어 집니다. 예 :

 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;

}


만약 미리 지정된 하위 디렉토리(sub directory)중에 여러분의 파일에 알맞은 이름이 없다면, 위의 메소드를 대신해서 여러분은getExternalFilesDir()의 호출로 대신하고 null 을 전달합니다. 이렇게 하면 외부 저장소에서 여러분 앱의 프라이빗(Private) 디렉토리에 대한 루트(root) 디렉토리을 리턴합니다.


 getExternalFilesDir()로 받아온 디렉토리안에 만든 디렉토리는 사용자가 앱을 언인스톨할 때 자동으로 지워진다는 것을 기억하시기 바랍니다. 만약 여러분이 저장한 파일이 사용자가 앱을 언인스톨한 후에도 여전히 남아있기를 원한다면( 여러분의 앱이 카메라이고 사용자가 사진을 유지하고 싶어할때와 같은 경우) 여러분은 반드시 getExternalStoragePublicDirectory()를 사용하셔야 합니다.


 여러분이 getExternalStoragePublicDirectory()을 사용하여 공유된 파일이든 또는 getExternalFilesDir()를 사용하여 프라이빗하고 사용하는 앱의 파일이든 상관없이, 디렉토리의 이름은 DIRECTORY_PICTURE 처럼 API 상수에 의해 제공되어진 것이라는 것이 중요합니다. 이 디렉토리의 이름들은 파일이 시스템에 의해 제대로 처리된다는 것을 보장합니다. DIRECTORY_RINGTONES 안에 저장된 음악 파일들은 시스템미디어 스캐너에 의해 음악대신 벨소리로 분류되어 집니다.



자유 공간 요청하기(Query Free Space)

-------------------------------

만약 여러분이 저장하는 데이터의 양이 얼마인지 미리 알고 있다면, 여러분은 getFreeSpace() 또는 getTotalSpace()를 호출하여IOException없이 충분한 공간을 사용할 수 있는지 확인할 수 있습니다. 이 메소드들은 각 각 저장장치의 현재 사용가능 공간과 전체 공간을 알려줍니다. 이 정보는 일정 임계치 값 이상의 저장공간이 꽉 차는 것을 회피하는데 유용합니다.


Note : 여러분에게 여러분의 파일을 저장하기 전에 사용할 수 있는 공간의 총양에 대한 체크할 필요가 없습니다. 만약 여러분이 사용해야 하는 공간이 정확히 얼마인지 알지 못할 때는 이렇게 하는 것이 필요할 수 도 있습니다. 예를들어, 만약 PNG 이미지를 JPEG으로 변경함으로서 여러분이 이전에 저장한 파일의 인코딩(encoding)이 변경되어 진다면, 여러분은 사전에 파일의 사이즈를 알지 못합니다.




파일 삭제하기(Delete a File)

-----------------------

여러분은 파일이 더이상 필요하지 않으면 항상 삭제해야만 합니다. 파일을 지우는 가장 정확한 방법은 파일을 열었던 참조변수에게delete()를 호출하도록 하는 것입니다.


 myFile.delete();


만약 파일을 내부 저장장치에 저장했다면, 여러분은 Context 에게 위치를 요청할 수 있으며 deleteFile()을 호출함으로서 파일을 삭제할 수 있습니다.


 myContext.deleteFile(fileName);




Note : 사용자가 앱을 언인스톨 할때, 안드로이드 시스템은 아래의 항목을 삭제합니다.

° 내부 저장장치에 있는 앱의 모든 파일들

° getExternalFilesDir() 를 사용해서 외부 저장장치에 저장한 앱의 모든 파일들


어쨋든 여러분은 getCacheDir()로 만든 모든 캐시된 파일들은 수동으로 직접 삭제 해야만 하며, 정기적으로 더 이상 필요하지 않은 다른 파일들을 삭제해야 합니다. 

반응형
반응형

 안드로이드는 다른 플랫폼의 디스크 기반 파일시스템과 유사한 파일시스템을 사용합니다. 이번시간에는 File 클래스의 API를 사용하여 파일을 읽고 쓰기위한 안드로이드의 파일 시스템 작업방법을 소개하겠습니다.


 File 객체는 건너띄는 것 없이 처음부터 끝까지 순서대로 많은 양의 데이터를 읽고 쓰는데 적합합니다. 예를 들어, 이미지 파일이나 네트워크를 통해 변경되어지는 것들을 저장하는데 좋습니다.


 이번 시간에는 여러분의 앱에 기본 파일 관련 작업을 수행하기 위한 방법을 보여드리겠습니다. 이 포스트는 여러분이 기본적인 리눅스의 파일시스템과 java.io 패키지의 표준 파일 입/출력 API 알고 있는 것으로 가정하고 설명할 것입니다.


 

내부 또는 외부 저장장치 선택 (Choose Internal or External Storage)

------------------------------------------------------

 모든 안드로이드 단말기들은 두개의 파일 저장 영역을 가지고 있습니다. : "Internal" 과  "External" 저장장치

이 저장소의 이름들은 안드로이드 이전부터 만들어져 있던 것들입니다. 대부분의 단말기들은 내장되어 있는 비휘발성 메모리(내부 메모리Internal Storage)가 제공되어 있으며, 추가적으로 SD card(External Storage)와 같은 제거할 수있는(removable) 저장장치를 가지고 있습니다. 일부 단말기들은 "internal"과 "external" 파티션(partition)으로 영구 저장장치의 공간을 나누기도 합니다. 그래서 제거할수 있는 외부 저장소가 없더라도, 항상 두개의 공간을 가지게 됩니다. 그리고 API의 동작은 외부 저장소가 제거될 수 있든 아니든 상관없이 똑같이 동작합니다. 아래의 목록은 각각의 저장공간에 대한 특징을 요약한 것입니다.



내부 저장장치(Internal Storage)

° 이 장치는 항상 사용할 수 있습니다.

° 이곳에 저장된 파일을 기본적으로는 오직 여러분의 앱에 의해서만 접속할 수 있습니다.

°사용자가 여러분의 앱을 언인스톨(uninstall) 할 때, 시스템은 내부저장소로부터 앱의 파일들을 모두 제거합니다.


 내부 저장소는 사용자든 다른 앱들이든 여러분의 파일에 접속할 수 없도록 하고 싶을 때 사용하기 좋은 곳입니다.



외부 저장장치(External Storage)

° 이장치는 항상 사용할 수 는 없습니다. 왜냐하면 사용자가 USB 저장장치처럼 외부저장소를 마운트(mount)할 수도 잇고 어떤 경우에는 단말기에서 제거할 수도 있기 때문입니다.

° 이 장치는 기본적으로 world-readable 입니다. 그래서 이곳에 저장된 파일은 여러분의 제어 밖에서(동의없이) 읽어질 수도 있습니다.

° 사용자가 여러분의 앱을 언인스톨(uninstall)할 때, 시스템은 여러분이 getExternalFilesDir()로 만든 디렉토리에 파일을 저장했을 때만 자동으로 제거됩니다.


 외부 저장소는 파일의 접속 제한이 필요하지 않으며 다른 앱들이나 사용자가 컴퓨터를 사용하여 접속하도록 공유하고 싶을 때 사용하기 좋은 곳입니다.


Tip : 기본적으로 앱들은 내부 저장소(Internal Storage)에 인스톨(install)되지만, 메니페스트 안에 android:installLocation 속성을 통해 저장위치를 지정할 수 있습니다. 그래서 여러분의 앱이 외부 저장장치(External Storage)에 인스톨(install)되도록 할 수 있습니다. 사용자는 내부 저장소(Internal Storage) 보다 큰 외부 저장소(External Storage)를 가지고 있으며 APK(Android Package)의 용량이 매우 클 때 이 옵션의 진가를 느끼게 됩니다.




외부 저장장치에 대한 퍼미션 취득( Obtain Permissions for External Storage)

-------------------------------------------------------------

 외부 저장장치에 저장하기 위해서는 반드시 메니페스트 파일안에 WRITE_EXTERNAL_STORAGE 퍼미션을 요청해야만 합니다.

 <manifest ...>

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

    ...

</manifest>


Caution : 요즘은, 모든 앱들이 특별한 퍼미션없이도 읽을 수 있도록 되어 있습니다. 어쨋든, 이것은 나중에 새로운 버전이 나올 때 변경될 수도 있습니다. 만약 여러분의 앱이 외부 저장소를 읽을 필요가 있다면(저장은 하지 않고), 여러분은 READ_EXTERNAL_STORAGE퍼미션을 선언할 필요가 있을 겁니다. 여러분의 앱이 사용자가 기대한 작업을 지속적으로 하도록 하기 위해서는 나중에 변경되기 전에 이 퍼미션을 지금 선언하는 것이 좋습니다.

 <manifest ...>

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

    ...

</manifest>

  어쨋든, 만약 여러분의 앱이 WRITE_EXTERNAL_STORAGE 퍼미션을 사용한다면, 그것을 묵시적으로 외부 저장소에서 읽을 수도 있는 퍼미션을 받은 것으로 봅니다.


여러분이 내부 저장소에 파일을 저장할 때는 어떤 퍼미션도 필요하지 않습니다. 여러분의 어플은 항상 내부 저장소의 디렉토리를 읽고 쓸수 있는 퍼미션을 가지고 있습니다.

반응형
반응형

 만약 여러분이 저장하고자 하는 key-values 의 컬렉션(collection)이 비교적 작다면 여러분은 SharedPreference API 를 사용하시면 됩니다. SharedPreference 객체는 key-value 쌍을 포함하고 있는 파일을 나타내며 그것들을 읽고 쓰는 단수한 메소드를 제공합니다. 각 각의 SharedPreference 파일은 프레임워크(framework)에 의해 관리되며 프라이빗(private) 이거가 공유(Shared) 될 수 도 있습니다.


 이번 시간에는 여러분에게 단순한 값들을 저장하고 읽어오기 위해 SharedPreferences 를 사용하는 방법에 대해 보여드리고자 합니다.


 Note : SharedPreference API들은 오직 key-value 쌍을 읽고 쓰는 용도입니다. 여러분의 앱 설정(App Settings)에 대한 사용자 인터페이스를 만들때 사용하는 Preferences와 혼동하지 마세요(심지어 SharedPreference를 사용해서 앱 설정을 했다 하더라도).Preference API들에 대한 추가 정보를 원하시면 개발자사이트의 Settings 가이드를 참고하시기 바랍니다.


 

SharedPreference 핸들(Handle) 얻어오기 (Get a Handle to a SharedPreferences)

------------------------------------------------------------------

 여러분은 아래 두개의 메소드를 호출하여 새로운 Shared Preference를 만들거나 이미 존재하는 것에 연결할 수 있습니다.


° getSharedPreferences() - 만약 여러분이 첫번째 파라미터로 지정한 이름에 의해 식별이 되는 다중(여러개의 MultipleSharedPreference 파일을 만들 필요가 있다면 이것을 사용합니다. 여러분은 앱안의 어떤 Context에서든 이것을 호출할 수 있습니다.


° getPreferences() - 만약 액티비티에 대해 오직 한개의 Shared Preference 파일을 만들거라면 액티비티에서 이것을 사용합니다. 왜냐하면 이것은 액티비티에 소속되어 있는 기본 Shared Preference 파일을 주기때문입니다.  그래서 여러분은 이름은 제공할 필요가 없습니다.



 예를 들어보죠, 아래 코드는 Fragment 안에서 실행되어 집니다. 문자열 리소스 R.string.preference_file_key에 의해 식별되어진Shared Preferences 파일에 접속합니다. 그리고 private 모드로 열기 때문에 이 파일은 오직 여러분의 앱에서만 접속할 수 있습니다.

 Context context = getActivity();

 SharedPreferences sharedPref = context.getSharedPreferences(

        getString(R.string.preference_file_key), Context.MODE_PRIVATE);


 SharedPreference 파일의 이름을 지을 때, 여러붕은 "com.example.myapp.PREFERENCE_FILE_KEY" 와 같이 앱안에서 유일하게 식별되는 이름을 사용해야만 합니다.


 만약 여러분이 액티비티에 대해 오직 하나의 SharedPreference 파일만 필요하다면 위를 대신하여 getPreferences() 메소드를 사용할 수도 있습니다.

  SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);


 Caution : 만약 MODE_WORLD_READABLE 이나 MODE_WORLD_WRITEABLE 로 SharedPreferences 파일을 만들었다면, 식별자를 알고 있는 어떤 앱에서도 여러분의 데이터에 접속할 수 있게 됩니다.



Shared Preferences에 쓰기(Write to Shared Preferences)

------------------------------------------------

 Shared Preferences 파일에 쓰기 위해서는, SharedPreferences 객체의 edit() 메소드를 호출함으로서SharedPreferences.Editor 객체를 만들어 내야 합니다.


 putInt()와 putString() 같은 메소드를 통해 여러분이 쓰고자 하는 키(key)와 값(value)들을 전달합니다. 그런다음, 변경 사항들을 저장하기 위해 commit() 메소드를 호출합니다. 예:


 SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

 SharedPreferences.Editor editor = sharedPref.edit();

 editor.putInt(getString(R.string.saved_high_score), newHighScore);

 editor.commit();



SharedPreferences로부터 읽기(Read from Shard Preferences)

---------------------------------------------------

 Shared Preferences 파일에서 값을 가져오기 위해서는, getInt() getString()같은 메소드에 여러분의 원하는 값(value)의 키(key)를 제공하면 되고 선택적으로 혹시 제공한 key 값이 없다면 리턴될 기본 값을 부여합니다. 예:

 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);


반응형
반응형

이전 Notification 에서 사운드와 진동을 추가하고 알림창을 클릭했을 때 새로운 액티비티로 전환되록 하여 알림의 세부내용을 확인하도록 하는 소스코드입니다.



버튼을 클릭하면 상태표시줄에 알림(Notification)이 발생하고 사운드와 진동(3초)이 발생합니다. 알림창을 확인(클릭)하면 새로운 액티비티로 전환하며 알림은 자동 삭제됩니다.

 


메인화면                                      알림 발생(상태표시줄)                  상태표시줄에 남아있는 알림 아이콘

    

  

 



상태바를 드래그하면 보이는 알림창      상태바를 클릭(알림 확인)               새로운 액티비티(ByNotificationActivity)

    





Layout 파일

 activity_main.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context="${relativePackage}.${activityClass}" >


    <Button android:id="@+id/button"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="generate Notification"

        android:onClick="mOnClick"/>


</RelativeLayout>


 



소스파일

 

 MainActivity.java

public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

switch( v.getId() ){

case R.id.button:

//알림(Notification)을 관리하는 NotificationManager 얻어오기

NotificationManager manager= (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

//알림(Notification)을 만들어내는 Builder 객체 생성

//만약 minimum SDK가 API 11 이상이면 Notification 클래스 사용 가능

//한번에 여러개의 속성 설정 가능

NotificationCompat.Builder builder= new NotificationCompat.Builder(this)

.setSmallIcon(android.R.drawable.ic_dialog_alert)  //상태표시줄에 보이는 아이콘 모양

.setTicker("Notify")                                     //알림이 발생될 때 잠시 보이는 글씨

.setContentText("Title")                                //알림창에서의 제목

.setContentText("Contents");                          //알림창에서의 글씨

//상태바를 드래그하여 아래로 내리면 보이는 알림창(확장 상태바)의 아이콘 모양 지정

builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_dialog_info));

//알림에 사운드 기능 추가

Uri soundUri= RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_NOTIFICATION);

builder.setSound(soundUri);

//알림에 진동 기능 추가 

//진동 추가시에는 AndroidManifest 파일에 uses-permission 을 통해 사용권한 받아야함  "android.permission.VIBRATE"

builder.setVibrate(new long[]{0,3000}); // pattern의 첫번째 파라미터는 wait시간, 두번째는 진동시간(단위 ms)

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

//알림을 확인했을 때(알림창 클릭) 다른 액티비티(ByNitificationActivity) 실행

//클릭했을 때 시작할 액티비티에게 전달하는 Intent 객체 생성

Intent intent= new Intent(this, ByNotificationActivity.class);

//클릭할 때까지 액티비티 실행을 보류하고 있는 PendingIntent 객체 생성

PendingIntent pending= PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

builder.setContentIntent(pending);   //PendingIntent 설정 

builder.setAutoCancel(true);         //클릭하면 자동으로 알림 삭제

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

Notification notification= builder.build();    //Notification 객체 생성

manager.notify(0, notification);    //NotificationManager가 알림(Notification)을 표시, id는 알림구분용           

break;

}

}

}

   




두번째 액티비티의 소스파일과 레이아웃파일


 ByNotificationActivity.java

 activity_by_notification.xml

 public class ByNotificationActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_by_notification);

}

}




 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context="${relativePackage}.${activityClass}" >


    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="The Activity by Notification" />


</RelativeLayout>



네트워크 사용하기 위해서는 사용 권한(uses-permission)이 필요합니다.

여러분 프로젝트의 메니페스트 파일에 퍼미션을 추가합니다.


 AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.notification"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="11"

        android:targetSdkVersion="19" />


    <!-- 진동사용 허가 -->

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

        <activity

            android:name=".ByNotificationActivity"

            android:label="@string/title_activity_by_notification" >

        </activity>

    </application>


</manifest>

 

반응형
반응형

 버튼을 클릭하면 상태표시줄에 알림(Notification)이 발생하는 소스코드입니다.


메인화면                                     알림 발생(상태표시줄)                  상태표시줄에 남아있는 알림 아이콘

    


상태바를 드래그하면 보이는 알림창(확장상태바)             상태바의 지우기 버튼을 누르면 알림이 삭제됨

             




Layout 파일

 activity_main.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context="${relativePackage}.${activityClass}" >


    <Button android:id="@+id/button"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="generate Notification"

        android:onClick="mOnClick"/>


</RelativeLayout>


 



소스파일

 

 MainActivity.java

public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

switch( v.getId() ){

case R.id.button:

//알림(Notification)을 관리하는 NotificationManager 얻어오기

NotificationManager manager= (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

//알림(Notification)을 만들어내는 Builder 객체 생성

//API 11 버전 이하도 지원하기 위해 NotificationCampat 클래스 사용

//만약 minimum SDK가 API 11 이상이면 Notification 클래스 사용 가능 

NotificationCompat.Builder builder= new NotificationCompat.Builder(this);

//Notification.Builder에게 Notification 제목, 내용, 이미지 등을 설정//////////////////////////////////////


 builder.setSmallIcon(android.R.drawable.ic_dialog_email);//상태표시줄에 보이는 아이콘 모양

builder.setTicker("Notification"); //알림이 발생될 때 잠시 보이는 글씨


//상태바를 드래그하여 아래로 내리면 보이는 알림창(확장 상태바)의 아이콘 모양 지정

builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_input_add));

builder.setContentTitle("Contents Title");    //알림창에서의 제목

builder.setContentText("Contents TEXT");   //알림창에서의 글씨


///////////////////////////////////////////////////////////////////////////////////////////////////////

Notification notification= builder.build();   //Notification 객체 생성

manager.notify(1, notification);             //NotificationManager가 알림(Notification)을 표시

break;

}

}

}

   

반응형
반응형

 네이버에서 제공하는 오픈 API 를 파싱해서 단순하게 텍스트뷰(TextView)에 출력하는 소스 코드입니다.


XmlPullParser 를 이용한 파싱 입니다. 


네이버 검색 오픈 API중에 '지역' 검색을 하는 예제 소스입니다. 

네이버 개발자센터에서 검색키를 발급받으셔야 합니다. 검색키 발급방법은 다른 포스트를 참고하시기 바랍니다.

 



메인화면                                                                   검색값 입력(스타벅스)  

                




네이버 XML data 문자열로 출력 ( Scrolling 가능 )

               





Layout 파일

 activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:padding="10dp">

    

    <LinearLayout 

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:orientation="horizontal"

        android:weightSum="10">

        

        <EditText android:id="@+id/edit"

        android:layout_width="0dp"

        android:layout_height="match_parent"

        android:layout_weight="8"

        android:textSize="12sp"

        android:hint="enter text to search"/>

    

    <Button android:id="@+id/button"

        android:layout_width="0dp"

        android:layout_height="match_parent"

        android:layout_weight="2"

        android:text="search"

        android:textSize="12sp"

        android:onClick="mOnClick"/>

        

    </LinearLayout>    

    

    <ScrollView 

        android:layout_width="match_parent"

        android:layout_height="match_parent">

    

        <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:textSize="8sp"        

        android:textStyle="bold"/>

        

    </ScrollView>     


</LinearLayout>






소스파일

 

 MainActivity.java

public class MainActivity extends Activity {

EditText edit;

TextView text;

XmlPullParser xpp;

String key=" 본인의 개발자 검색 키 "//Naver 개발자센터 검색 키

String data;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

edit= (EditText)findViewById(R.id.edit);

text= (TextView)findViewById(R.id.text);

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

switch( v.getId() ){

case R.id.button:

//Android 4.0 이상 부터는 네트워크를 이용할 때 반드시 Thread 사용해야 함

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

data= getXmlData(); //아래 메소드를 호출하여 XML data를 파싱해서 String 객체로 얻어오기

//UI Thread(Main Thread)를 제외한 어떤 Thread도 화면을 변경할 수 없기때문에

//runOnUiThread()를 이용하여 UI Thread가 TextView 글씨 변경하도록 함

runOnUiThread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

text.setText(data);  //TextView에 문자열  data 출력

}

});

}

}).start();

break;

}

}//mOnClick method..

//XmlPullParser를 이용하여 Naver 에서 제공하는 OpenAPI XML 파일 파싱하기(parsing)

String getXmlData(){

StringBuffer buffer=new StringBuffer();

String str= edit.getText().toString(); //EditText에 작성된 Text얻어오기

String location = URLEncoder.encode(str); //한글의 경우 인식이 안되기에 utf-8 방식으로 encoding..

String queryUrl="http://openapi.naver.com/search"   //요청 URL

   +"?key="+key                        //key 값

   +"&target=local"                     //검색서비스 api명세

   +"&query="+location                 //지역검색 요청값

   +"&display=10"                      //검색 출력 건수  10~100

   +"&start=1";                         //검색 시작 위치  1~1000

try {

URL url= new URL(queryUrl); //문자열로 된 요청 url을 URL 객체로 생성.

InputStream is= url.openStream();  //url위치로 입력스트림 연결

XmlPullParserFactory factory= XmlPullParserFactory.newInstance();

XmlPullParser xpp= factory.newPullParser();

xpp.setInput( new InputStreamReader(is, "UTF-8") );  //inputstream 으로부터 xml 입력받기

String tag;

xpp.next();

int eventType= xpp.getEventType();

while( eventType != XmlPullParser.END_DOCUMENT ){

switch( eventType ){

case XmlPullParser.START_DOCUMENT:

buffer.append("start NAVER XML parsing...\n\n");

break;

case XmlPullParser.START_TAG:

tag= xpp.getName();    //테그 이름 얻어오기

if(tag.equals("item")) ;// 첫번째 검색결과

else if(tag.equals("title")){

buffer.append("업소명 : ");

xpp.next();

buffer.append(xpp.getText()); //title 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

else if(tag.equals("category")){

buffer.append("업종 : ");

xpp.next();

buffer.append(xpp.getText()); //category 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

else if(tag.equals("description")){

buffer.append("세부설명 :");

xpp.next();

buffer.append(xpp.getText()); //description 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

else if(tag.equals("telephone")){

buffer.append("연락처 :");

xpp.next();

buffer.append(xpp.getText()); //telephone 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

else if(tag.equals("address")){

buffer.append("주소 :");

xpp.next();

buffer.append(xpp.getText()); //address 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

else if(tag.equals("mapx")){

buffer.append("지도 위치 X :");

xpp.next();

buffer.append(xpp.getText()); //mapx 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("  ,  ");          //줄바꿈 문자 추가

}

else if(tag.equals("mapy")){

buffer.append("지도 위치 Y :");

xpp.next();

buffer.append(xpp.getText()); //mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 

buffer.append("\n");          //줄바꿈 문자 추가

}

break;

case XmlPullParser.TEXT:

break;

case XmlPullParser.END_TAG:

tag= xpp.getName();    //테그 이름 얻어오기

if(tag.equals("item")) buffer.append("\n"); // 첫번째 검색결과종료..줄바꿈

break;

}

eventType= xpp.next();

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

buffer.append("end NAVER XML parsing...\n");

return buffer.toString(); //StringBuffer 문자열 객체 반환

}//getXmlData method....

}//MainActivity class..

  



 네트워크 사용하기 위해서는 사용 권한(uses-permission)이 필요합니다.

여러분 프로젝트의 메니페스트 파일에 퍼미션을 추가합니다.


 AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.xmlpullparsernaver"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="8"

        android:targetSdkVersion="19"/>


    <!-- 네트워크 사용허가 요청  -->

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>


</manifest>


 

반응형
반응형

res 폴더에 있는 XML 파일을 파싱(parsing)하는 소스코드 입니다.


XmlResourceParser 를 이용한 파싱 입니다.



  

메인화면                                                           res폴더의 XML data 읽기

            




XML data 파일 ( res/xml/movies.xml )

 movies.xml

 저장 위치
 

 <?xml version="1.0" encoding="UTF-8"?>

<Movie>

    <Title>

        Alien

        <Genre>

            SF, action, advanture

        </Genre>        

    </Title>

    

    <Title>

        avatar

        <Genre>

            SF, action, advanture

        </Genre>        

    </Title>

    

    <Title>

        Notting Hill

        <Genre>

            romance, melo

        </Genre>        

    </Title>

    

    <Title>

        Nightmare

        <Genre>

            horror, thriller

        </Genre>        

    </Title>    

    

</Movie>


 





Layout 파일

 activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <EditText android:id="@+id/edit"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:padding="5dp"

        android:inputType="text"

        android:hint="input text"/>

    

    <Button android:id="@+id/btn_save"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="save file into External(SDcard)"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_load"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="load file from External(SDcard)"

        android:onClick="mOnClick"/>

    

    <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:padding="5dp"

        android:text="Show data from file"/>


</LinearLayout>





소스파일

 

 MainActivity.java

 public class MainActivity extends Activity {

XmlResourceParser xrp;

TextView text;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

text= (TextView)findViewById(R.id.text);

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

switch( v.getId() ){

case R.id.button:

Resources res= getResources(); //res 폴더 관리자(Resources 객체) 얻어오기

xrp= res.getXml(R.xml.movies); //XmlResourcesParser 얻어오기

String data= getXmlData(); //아래 메소드를 호출하여 XML data를 파싱해서 String 객체로 얻어오기

text.setText(data);    //TextView에 문자열  data 출력

break;

}

}//mOnClick method..

//XmlResourceParser를 이용하여 res/xml 폴더에 있는 movies.xml 파일 파싱하기(parsing)

String getXmlData(){

StringBuffer buffer=new StringBuffer();

String name;

String text;

try {

xrp.next();

int eventType= xrp.getEventType(); //XML의 이벤트 타입 얻어오기..

while( eventType != XmlResourceParser.END_DOCUMENT ){

// XmlResourceParser의 5가지 이벤트 타입

switch( eventType){

case XmlResourceParser.START_DOCUMENT:

buffer.append("Start XML Parsing ......\n\n");

break;

case XmlResourceParser.START_TAG:

name= xrp.getName();

if(name.equals("Movie")) buffer.append("--Movie List Start--\n\n");

else if(name.equals("Title")) buffer.append("Title : ");

else if(name.equals("Genre")) buffer.append("Genre : ");

break;

case XmlResourceParser.TEXT:

buffer.append(xrp.getText());

break;

case XmlResourceParser.END_TAG:

name= xrp.getName();

if(name.equals("Movie")) buffer.append("--Movie List End--\n");

else if(name.equals("Title")) buffer.append("\n");

break;

case XmlResourceParser.END_DOCUMENT:

break;

}

eventType= xrp.next(); //다음 이벤트타입 얻어오기

}//while

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

buffer.append("\n End XML Parsing ......");

return buffer.toString(); //StringBuffer 문자열 객체 반환

}//getXmlData method....

}//MainActivity class..

 









반응형
반응형

EditText 에 입력한 글씨를 외부 메모리(Internal Storage) 즉, SDcard에 저장하고 다시 읽어오는 소스입니다.


먼저, 여러분의 테스터 단말기(실제 스마트폰 또는 에뮬레이터)에 SDcard가 있는지 확인하시기 바랍니다.




메인화면                                        SDcard에 저장                               SDcard에서 읽어오기

          


Layout 파일

 activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <EditText android:id="@+id/edit"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:padding="5dp"

        android:inputType="text"

        android:hint="input text"/>

    

    <Button android:id="@+id/btn_save"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="save file into External(SDcard)"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_load"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="load file from External(SDcard)"

        android:onClick="mOnClick"/>

    

    <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:padding="5dp"

        android:text="Show data from file"/>


</LinearLayout>



소스파일

 

 MainActivity.java

 public class MainActivity extends Activity {

EditText edit;

TextView text;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

edit= (EditText)findViewById(R.id.edit);

text= (TextView)findViewById(R.id.text); 

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

String state= Environment.getExternalStorageState(); //외부저장소(SDcard)의 상태 얻어오기

File path;    //저장 데이터가 존재하는 디렉토리경로

File file;     //파일명까지 포함한 경로

switch(v.getId()){

case R.id.btn_save:  //External Storage(SDcard)에 file 저장하기

if(!state.equals(Environment.MEDIA_MOUNTED)){ // SDcard 의 상태가 쓰기 가능한 상태로 마운트되었는지 확인

Toast.makeText(this, "SDcard Not Mounted", Toast.LENGTH_SHORT).show();

return;

}

String data= edit.getText().toString();  //EditText의 Text 얻어오기

//SDcard에 데이터 종류(Type)에 따라 자동으로 분류된 저장 폴더 경로선택

//Environment.DIRECTORY_DOWNLOADS : 사용자에의해 다운로드가 된 파일들이 놓여지는 표준 저장소

path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

file= new File(path, "Data.txt"); //파일명까지 포함함 경로의 File 객체 생성

try {

//데이터 추가가 가능한 파일 작성자(FileWriter 객체생성)

FileWriter wr= new FileWriter(file,true); //두번째 파라미터 true: 기존파일에 추가할지 여부를 나타냅니다.

PrintWriter writer= new PrintWriter(wr); 

writer.println(data);

writer.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//소프트 키보드 없애기

InputMethodManager imm= (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);

break;

case R.id.btn_load:  //External Storage(SDcard)에서 file 읽어오기

// SDcard 의 상태가 읽기/쓰기가 가능하거나 또는 읽기만 가능한 상태로 마운트되었는지 확인

if( !(state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) ){

Toast.makeText(this, "SDcard Not Mounted", Toast.LENGTH_SHORT).show();

return;

}

StringBuffer buffer= new StringBuffer();

path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

file= new File(path, "Data.txt");

try {

FileReader in= new FileReader(file);

BufferedReader reader= new BufferedReader(in);

String str= reader.readLine();

while( str!=null ){

buffer.append(str+"\n");

str= reader.readLine();//한 줄씩 읽어오기

}

text.setText(buffer.toString());

reader.close();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

break;

}

}

}

 



 외부 저장소를 사용하기 위해서는 사용 권한(uses-permission)이 필요합니다.

여러분 프로젝트의 메니페스트 파일에 퍼미션을 추가합니다.


 AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.externalstorage"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="8"

        android:targetSdkVersion="19" />

    

    <!-- 외부저장소(SDcard) 사용허가 요청  -->

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>


</manifest>






저장된 위치 확인

(Eclipse 에서 DDMS의 mnt/sdcard/Download/)

- 외부 메모리(SDcard)에 위치해 있기 때문에 앱을 제거해도 파일은 여전히 남아있게 됩니다.





Tip. 사용자가 원하는 디렉토리에 저장하기

 기본 SDcard의 디렉토리가 아닌 사용자가 원하는 디렉토리를 사용하고 싶다면 아래 코드로 디렉토리 pah를 만드시면 됩니다.

 //SDcard의 절대 경로 얻어오기( /mnt/sdcard )

 String absolutePath= Environment.getExternalStorageDirectory().getAbsolutePath();

 // 커스텀 디렉토리로서 "/custDir" 디렉토리 추가  ( /mnt/sdcard/custDir/ )

 String newPath= absolutePath+"/custDir";

 File path= new File(newPath); //새로운 디렉토리로 File 객체 생성

 if( !path.exists() ) path.mkdir(); //만약 처음 디렉토리를 만들면 존재하지 않기때문에 디렉토리를 생성( make Directory )

  .......



사용자 정의 디렉토리 사용시 저장 위치( /mnt/sdcard/custDir/ )




























반응형
반응형

 EditText 에 입력한 글씨를 내부 메모리(Internal Storage)에 저장하고 다시 읽어오는 소스입니다.



메인화면                                       내부메모리에 저장                            내부메모리에서 읽어오기

         



Layout 파일

 activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <EditText android:id="@+id/edit"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:padding="5dp"

        android:inputType="text"

        android:hint="input text"/>

    

    <Button android:id="@+id/btn_save"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="save file into Internal"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_load"

        android:layout_width="match_parent"

        android:layout_height="50dp"

        android:text="load file from Internal"

        android:onClick="mOnClick"/>

    

    <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:padding="5dp"

        android:text="Show data from file"/>


</LinearLayout>



소스파일

 

 MainActivity.java

 public class MainActivity extends Activity {

EditText edit;

TextView text;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

edit= (EditText)findViewById(R.id.edit);

text= (TextView)findViewById(R.id.text); 

}

//Button을 클릭했을 때 자동으로 호출되는 callback method....

public void mOnClick(View v){

switch(v.getId()){

case R.id.btn_save: //Internal Storage에 file 저장하기

String data= edit.getText().toString(); //EditText에서 Text 얻어오기

edit.setText("");

try {

//FileOutputStream 객체생성, 파일명 "data.txt", 새로운 텍스트 추가하기 모드

FileOutputStream fos=openFileOutput("data.txt", Context.MODE_APPEND); 

PrintWriter writer= new PrintWriter(fos);

writer.println(data);

writer.close();

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//소프트 키보드 없애기

InputMethodManager imm= (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);

break;

case R.id.btn_load: //file 에서 읽어오기

StringBuffer buffer= new StringBuffer();

try {

//FileInputStream 객체생성, 파일명 "data.txt"

FileInputStream fis=openFileInput("data.txt");

BufferedReader reader= new BufferedReader(new InputStreamReader(fis));

String str=reader.readLine();//한 줄씩 읽어오기

while(str!=null){

buffer.append(str+"\n");

str=reader.readLine();

}

text.setText(buffer.toString());

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

break;

}

}

}



 

저장된 위치 확인 (Eclipse 에서 DDMS의 본인 앱위치)

- 본인의 앱 폴더 안에 있으므로 앱을 제거하면 파일도 같이  제거됩니다.











반응형

+ Recent posts