앞선 포스팅에서는 Internal/External 저장소에 사용자의 데이터를 저장하는 방법에 대해 살펴 보았습니다.
오늘은 데이터베이스를 활용하여 원하는 데이터를 저장하는 방법에 대해 알아보겠습니다.
Intenrnal/External 저장소에 저장하는 것 보다 데이터베이스를 이용하여 저장할 때는, 동일한 형태의 데이터를 저장할 수 있다는 점에서 더 편리합니다.
회원정보를 저장할 경우가 대표적인 예인데, 이 경우 회원의 이름, 성별, 전화번호, 주소 등등이 각각 DB의 속성(Attribute) 가 될 것이고,
실제 들어가는 데이터 (김태희, 여, 010-0000-0000, 경기도.. ) 들이 값(Value) 이 될 것입니다.
1. SQLiteOpenHelper 를 이용한 DB 생성
SQLiteOpenHelper Class 는 Database 를 생성하고 해당 Database 의 Version 을 관리합니다.
해당 Class 를 사용하기 위해서 Database가 처음 생성될 때 불리는 onCreate(..) 와 Database 가 Upgrade 될 경우에 불리는 onUpgrade(..) callback method 를 구현해 주어야 하며, 필요에 따라 onOpen(..) 함수와 onDowngrade(..) 함수도 구현하여 사용할 수 있습니다.
저는 SQLiteOpenHelper를 상속 받은 MySQLiteOpenHelper 를 생성해서 간단하게 onCreate/onUpgrade 만 구현해 주었습니다.
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
private final String TAG = "MySQLiteOpenHelper";
public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String sql = "create table student (_id integer primary key autoincrement, name text, age integer, address text)";
sqLiteDatabase.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
String sql="drop table if exists student";
sqLiteDatabase.execSQL(sql);
onCreate(sqLiteDatabase);
}
}
onCreate 에서는 Database를 생성해 주었고, 각 Field 는 name, age, address 로 구성될 수 있도록 하였습니다.
"create table student (_id integer primary key autoincrement, name text, age integer, address text)";
위 Query 로 DB생성 후 실제 만들어진 DB 를 열어보면 다음과 같이 data 가 저장됩니다.
그리고 onUpgrade 함수에는 현재 생성되어있는 Database 를 지우고 onCreate 를 통해 다시 생성될 수 있도록만 구현을 해 두었습니다.
2. DB에 데이터 삽입 및 삭제
DB에 데이터 삽입/삭제를 위해 MyDBHandler 클래스를 생성하였습니다.
MyDBHandler 클래스에서는 DBActivity 로 부터 사용자 정보를 받아 MySQLiteOpenHelper 로 전달 합니다.
데이터를 저장 할 때 ContentValues 를 이용했는데, ContentValues 는 Data 를 Key와 Value의 Set 으로 저장할 수 있습니다.
그래서 Database 에 값을 줄때 "name, 홍길동" ,"age, 24" 등과 같이 Key 와 Value 의 형태로 쉽게 전달 가능합니다.
public class MyDBHandler {
private final String TAG = "MyDBHandler";
SQLiteOpenHelper mHelper = null;
SQLiteDatabase mDB = null;
public MyDBHandler(Context context, String name) {
mHelper = new MySQLiteOpenHelper(context, name, null, 1);
}
public static MyDBHandler open(Context context, String name) {
return new MyDBHandler(context, name);
}
public Cursor select()
{
mDB = mHelper.getReadableDatabase();
Cursor c = mDB.query("student", null, null, null, null, null, null);
return c;
}
public void insert(String name, int age, String address) {
Log.d(TAG, "insert");
mDB = mHelper.getWritableDatabase();
ContentValues value = new ContentValues();
value.put("name", name);
value.put("age", age);
value.put("address", address);
mDB.insert("student", null, value);
}
public void delete(String name)
{
Log.d(TAG, "delete");
mDB = mHelper.getWritableDatabase();
mDB.delete("student", "name=?", new String[]{name});
}
public void close() {
mHelper.close();
}
}
select 함수는 Database 의 전체 내용을 return 해주고, insert/delete 함수는 각각 Database에 데이터를 추가/삭제 시 호출 됩니다.
delete 의 경우 name 을 인자로 받아서 이름이 동일 하면 해당 row 를 모두 삭제하도록 하였습니다.
3. 동작 확인
기존에 작성하였던 MainActivity 에 Database 버튼을 하나 추가한다음에, 이 버튼을 눌렀을 때 Activity 를 전환하여 Database 의 내용을 뿌려주도록 하였습니다.
가장 상단에는 Database 에 내용을 추가할 수 있도록 Editbox 를 3개 두었고, 각각 이름/나이/주소 정보를 저장할 수 있도록 하였습니다.
Update Database 를 누르게 되면 Editbox 에 추가한 내용이 Database 에 업데이트 되고 리스트의 내용이 업데이트 됩니다.
리스트의 각 항목을 롱클릭 하게 되면 해당 Field 가 삭제 됩니다. ^^
여기까지 Database 를 이용한 아주 간단한 사용자 데이터 저장에 대해서 살펴보았습니다.
모든 소스는 https://github.com/bettercho/MyGithub/tree/master/storeuserdata 를 참고하시면 됩니다.
아래와 같이 간단하게 완성이 되었습니다. 각 버튼을 눌렀을 때 Internal/External Storage 에 저장하고, Print 버튼을 눌렀을 때 저장된 내용이 출력되도록 한 예제입니다. 작성된 코드는 https://github.com/bettercho/MyGithub/tree/master/storeuserdata 를 참고하세요.
01-06 13:03:14.034 11277-11277/com.example.codetravel.getmainlooper D/MainActivity: changeButtonText method is called from non-main thread
Looper.myLooper() 함수를 로그로 출력하였을 때 내용은 아래와 같습니다.
Looper (main, tid 1) <== 메인 스레드에서 호출
null <== TestThread에서 호출
이것이 의미하는 것을 알기 위해서 Looper 클래스의 toString()함수를 보도록 하겠습니다. (xref : Looper.java)
Looper (" 스레드 이름", "스레드 ID") 임을 알 수 있습니다.
3. 현재 스레드가 Main thread(UI thread)인지 아닌지 검사하고 싶을 경우
public class MainActivity extends AppCompatActivity {
private String TAG = "MainActivity";
private TestThread mTestThread;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.button);
}
public void onClickButton(View view) {
Log.d(TAG,"onClickButton()" + " " + Thread.currentThread() + " " + Looper.getMainLooper().getThread());
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
Log.d(TAG,"onClickButton() : This thread is main thread!");
}
else {
Log.d(TAG,"onClickButton() : This thread is not main thread!");
}
mTestThread = new TestThread();
mTestThread.start();
}
class TestThread extends Thread {
@Override
public void run() {
Log.d(TAG,"TestThread run()" + " " + Thread.currentThread() + " " + Looper.getMainLooper().getThread());
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
Log.d(TAG,"TestThread run() : This thread is main thread!");
}
else {
Log.d(TAG,"TestThread run() : This thread is not main thread!");
}
while(true) { // ps 명령어로 스레드 ID를 보기 위해서 스레드가 종료되지 않도록 하기 위한 코드
// 아무 동작도 안하고 그냥 살아 있는 스레드
}
}
}
}
로그를확인하기전에코드만으로예측해보면 onClickButton() 함수는 UI thread에서호출되기때문에 "This thread is main thread!" 가출력될것이라는것을예측할수있습니다.
그리고 TestThread 스레드가생성되어 run() 함수가동작하는스레드는메인스레드가아닌백그라운드스레드입니다.