SQLite 사용 예제

|

안드로이드 SQLite 예제 코드입니다. 직접 코드를 이용해서 Database를 생성할 수도 있고, SQLiteManager와 같은 외부 Tool을 이용해서 Database를 생성할 수도 있습니다.

저는 외부 Tool을 사용하는 것을 선호하긴 하지만(나중에 수정이나 유지 보수가 쉬워서), 장단점이 있는 것 같습니다. 이번 포스팅에서는 코드를 이용해서 Database를 사용하는 예제를 올려보도록 하겠습니다.

안드로이드에서는 SQLite를 좀 더 사용하기 쉽도록 Helper 클래스를 제공하고 있습니다. Helper 클래스를 이용하여 Database를 생성하거나 변경, 업그레이드 등을 쉽게 할 수 있습니다. 다음과 같은 코드를 이용해서 Database를 생성하고, Table들을 생성할 수 있습니다.


SnowSQLiteOpenHelper.java

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

public class SnowSQLiteOpenHelper extends SQLiteOpenHelper {

  public static final String TABLE_MY_WALLET = "TABLE_MY_WALLET";
  public static final String TABLE_BEACON_HISTORY = "TABLE_BEACON_HISTORY";

  public SnowSQLiteOpenHelper(Context context) {
    super(context, "snowdeer.db", null, 1);
  }

  public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE " + TABLE_MY_WALLET
        + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," +
        "privateKey TEXT, publicKey TEXT, address TEXT);");
    db.execSQL("CREATE TABLE " + TABLE_BEACON_HISTORY
        + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," +
        "inputTime TEXT, beaconId TEXT, rssi INTEGER);");
  }

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_MY_WALLET);
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_BEACON_HISTORY);

    onCreate(db);
  }
}


그리고 SQL의 CRUD(Create, Retrieve, Update, Delete) 기능을 수행하는 Manager 클래스를 하나 추가합니다. 테이블(Table) 별로 따로 두는 것이 더 바람직하지만,  여기서는 편의를 위해 하나의 클래스에 모두 모아놓았습니다. (Database 규모에 따라 모두 모아놓는 것이 편리할 때도 있습니다.)


SnowDBManager.java

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

import com.lnc.prototype.data.BeaconLog;
import com.lnc.prototype.data.Wallet;

import java.util.ArrayList;

public class SnowDBManager {

  private static final SnowDBManager mInstance = new SnowDBManager();

  private SnowDBManager() {}

  public static SnowDBManager getInstance() {
    return mInstance;
  }

  public ArrayList<Wallet> getWalletList(Context context) {
    ArrayList<Wallet> resultList = new ArrayList<>();

    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);
    try {

      SQLiteDatabase db = dbHelper.getReadableDatabase();
      Cursor cursor = db.rawQuery(
          "SELECT _id, privateKey, publicKey, address FROM "
              + SnowSQLiteOpenHelper.TABLE_MY_WALLET,
          null);

      while(cursor.moveToNext()) {
        int id = cursor.getInt(0);
        String privateKey = cursor.getString(1);
        String publicKey = cursor.getString(2);
        String address = cursor.getString(3);

        Wallet wallet = new Wallet(id, privateKey, publicKey, address);
        resultList.add(wallet);
      }
      cursor.close();
    } catch(Exception e) {
      e.printStackTrace();
    }

    dbHelper.close();

    return resultList;
  }

  public void addWallet(Context context, Wallet item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();

      ContentValues row = new ContentValues();

      row.put("privateKey", item.privateKey);
      row.put("publicKey", item.publicKey);
      row.put("address", item.address);

      db.insert(SnowSQLiteOpenHelper.TABLE_MY_WALLET, null, row);

      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void updateWallet(Context context, Wallet item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      String filter = "_id = " + item.id;
      ContentValues row = new ContentValues();

      row.put("privateKey", item.privateKey);
      row.put("publicKey", item.publicKey);
      row.put("address", item.address);

      db.update(SnowSQLiteOpenHelper.TABLE_MY_WALLET, row, filter, null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void deleteWallet(Context context, Wallet item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      String filter = "_id = " + item.id;

      db.delete(SnowSQLiteOpenHelper.TABLE_MY_WALLET, filter, null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void clearWallet(Context context) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();

      db.delete(SnowSQLiteOpenHelper.TABLE_MY_WALLET, "", null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public ArrayList<BeaconLog> getBeaconLogList(Context context) {
    ArrayList<BeaconLog> resultList = new ArrayList<>();

    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);
    try {

      SQLiteDatabase db = dbHelper.getReadableDatabase();
      Cursor cursor = db.rawQuery(
          "SELECT _id, inputTime, beaconId, rssi FROM "
              + SnowSQLiteOpenHelper.TABLE_BEACON_HISTORY,
          null);

      while(cursor.moveToNext()) {
        int id = cursor.getInt(0);
        String inputTime = cursor.getString(1);
        String beaconId = cursor.getString(2);
        int rssi = cursor.getInt(3);

        BeaconLog beaconLog = new BeaconLog(id, inputTime, beaconId, rssi);
        resultList.add(beaconLog);
      }
      cursor.close();
    } catch(Exception e) {
      e.printStackTrace();
    }

    dbHelper.close();

    return resultList;
  }

  public void addBeaconLog(Context context, BeaconLog item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();

      ContentValues row = new ContentValues();

      row.put("inputTime", item.inputTime);
      row.put("beaconId", item.beaconId);
      row.put("rssi", item.rssi);

      db.insert(SnowSQLiteOpenHelper.TABLE_BEACON_HISTORY, null, row);

      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void updateBeaconLog(Context context, BeaconLog item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      String filter = "_id = " + item.id;
      ContentValues row = new ContentValues();

      row.put("inputTime", item.inputTime);
      row.put("beaconId", item.beaconId);
      row.put("rssi", item.rssi);

      db.update(SnowSQLiteOpenHelper.TABLE_BEACON_HISTORY, row, filter, null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void deleteBeaconLog(Context context, BeaconLog item) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      String filter = "_id = " + item.id;

      db.delete(SnowSQLiteOpenHelper.TABLE_BEACON_HISTORY, filter, null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }

  public void clearBeaconLog(Context context) {
    SnowSQLiteOpenHelper dbHelper = new SnowSQLiteOpenHelper(context);

    try {
      SQLiteDatabase db = dbHelper.getWritableDatabase();

      db.delete(SnowSQLiteOpenHelper.TABLE_BEACON_HISTORY, "", null);
      db.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
    dbHelper.close();
  }
}


그리고 테스트는 다음과 같은 코드를 이용해서 간단하게 해볼 수 있습니다.

사용 예제

public class DatabaseTestFragment extends Fragment {

  private static final String TAG = "snowdeer";
  private TextView mLogView;
  private StringBuilder mLogBuilder = new StringBuilder();
  private Handler mHandler = new Handler();

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_database_test,
        container, false);

    view.findViewById(R.id.btn_get).setOnClickListener(mOnClickListener);
    view.findViewById(R.id.btn_add).setOnClickListener(mOnClickListener);
    view.findViewById(R.id.btn_update).setOnClickListener(mOnClickListener);
    view.findViewById(R.id.btn_delete).setOnClickListener(mOnClickListener);
    view.findViewById(R.id.btn_clear).setOnClickListener(mOnClickListener);

    view.findViewById(R.id.btn_clear_log).setOnClickListener(mOnClickListener);

    mLogView = (TextView) view.findViewById(R.id.log);
    mLogBuilder.setLength(0);

    log("RPC Test Screen");

    return view;
  }

  private void log(String message) {
    mLogBuilder.append("\n");
    mLogBuilder.append(message);
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        if(mLogView != null) {
          mLogView.setText(mLogBuilder.toString());
        }
      }
    });
    Log.i(TAG, "[snowdeer] " + message);
  }

  private View.OnClickListener mOnClickListener = new View.OnClickListener() {

    @Override
    public void onClick(View view) {
      switch(view.getId()) {
        case R.id.btn_get:
          getBeaconList();
          break;

        case R.id.btn_add:
          addBeaconLog();
          break;

        case R.id.btn_update:
          break;

        case R.id.btn_delete:
          break;

        case R.id.btn_clear:
          clearBeaconLog();
          break;

        case R.id.btn_clear_log:
          clearLog();
          break;
      }
    }
  };

  private void clearLog() {
    mLogBuilder.setLength(0);
    mLogView.setText("");
  }

  private void addBeaconLog() {
    BeaconLog beaconLog = new BeaconLog("2016-10-28 17:50", "BeaconId", 150);
    LncDBManager.getInstance().addBeaconLog(getActivity(), beaconLog);
  }

  private void getBeaconList() {
    ArrayList<BeaconLog> list = LncDBManager.getInstance()
        .getBeaconLogList(getActivity());
    log("list size : " + list.size());
    for(int i = 0; i < list.size(); i++) {
      BeaconLog item = list.get(i);
      log((i + 1) + ". (" + item.inputTime + ", "
          + item.id + ", " + item.rssi + ")");
    }
  }

  private void clearBeaconLog() {
    LncDBManager.getInstance().clearBeaconLog(getActivity());
  }
}

Log Class

|

안드로이드 Log 기능을 Wrapping한 클래스 코드입니다. 안드로이드에서 기본으로 제공하는 Log를 써도 되지만 나중에 일괄적으로 visible/hidden을 적용하거나 특수한 Prefix 등을 붙인다거나 등 좀 더 편리하게 쓰기 위해서는 별도로 Log 클래스를 두는 것이 편리합니다.


Log 클래스 코드

package snowdeer.util;

public class Log {

  private static final String TAG = "snowdeer";
  private static final String PREFIX = "[snowdeer] ";
  private static boolean useLog = true;

  public static void v(String tag, String message) {
    if(useLog) {
      android.util.Log.v(tag, PREFIX + message);
    }
  }

  public static void d(String tag, String message) {
    if(useLog) {
      android.util.Log.d(tag, PREFIX + message);
    }
  }

  public static void i(String tag, String message) {
    if(useLog) {
      android.util.Log.i(tag, PREFIX + message);
    }
  }

  public static void w(String tag, String message) {
    if(useLog) {
      android.util.Log.w(tag, PREFIX + message);
    }
  }

  public static void e(String tag, String message) {
    if(useLog) {
      android.util.Log.e(tag, PREFIX + message);
    }
  }

  public static void v(String message) {
    android.util.Log.v(TAG, message);
  }

  public static void d(String message) {
    android.util.Log.d(TAG, message);
  }

  public static void i(String message) {
    android.util.Log.i(TAG, message);
  }

  public static void w(String message) {
    android.util.Log.w(TAG, message);
  }

  public static void e(String message) {
    android.util.Log.e(TAG, message);
  }
}

한/영 전환 단축키

|

한/영 전환 단축키

Mac을 처음 사용하는 사람들은 한/영 전환을 어떻게 하는지 몰라서 당황하는 경우가 많습니다.

한/영 전환 단축키는 다음과 같습니다.

  • Control + Command
  • Sierra 이상에서는 Caps lock으로도 사용 가능

Estimote Beacon 스캔

|

안드로이드에서 Estimote Beacon을 스캔하는 간단한 예제 코드입니다.  더 자세한 정보는 여기를 참고하시면 됩니다.


SDK 다운로드

그리고 Estimote SDK는 여기(GitHub)에서 다운 받을 수 있습니다.


Dependency 추가

Android Studio 기준으로, 일단 build.gradle에 Estimote Beacon SDK에 대한 dependency를 추가합니다.


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'

    // for Estimote SDK
    compile 'com.estimote:sdk:0.11.1@aar'
}


예제 코드

그런 다음, 다음과 같은 코드로 주위에 있는 Estimote Beacon들을 간단하게 스캔할 수 있습니다.

package snowdeer.sample.estimote;

import android.graphics.Bitmap;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import com.estimote.sdk.BeaconManager;
import com.estimote.sdk.EstimoteSDK;
import com.estimote.sdk.Nearable;
import com.estimote.sdk.SystemRequirementsChecker;
import com.estimote.sdk.connection.scanner.ConfigurableDevicesScanner;
import com.estimote.sdk.connection.scanner.DeviceType;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

  private static final String TAG = "snowdeer";
  private TextView mLogView;
  private StringBuilder mLogBuilder = new StringBuilder();
  private Handler mHandler = new Handler();

  private BeaconManager mBeaconManager;
  private ConfigurableDevicesScanner mDeviceScanner;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(R.id.btn_start_scan).setOnClickListener(mOnClickListener);
    findViewById(R.id.btn_stop_scan).setOnClickListener(mOnClickListener);
    findViewById(R.id.btn_clear_log).setOnClickListener(mOnClickListener);

    mLogView = (TextView) findViewById(R.id.log);
    mLogBuilder.setLength(0);

    EstimoteSDK.initialize(getApplicationContext(),
        "estimotebeaconsample-ivb", "45f6fc31fd613624b0f24df41b121db6");

    mBeaconManager = new BeaconManager(getApplicationContext());
    mDeviceScanner = new ConfigurableDevicesScanner(getApplicationContext());
  }

  @Override
  protected void onResume() {
    super.onResume();
    SystemRequirementsChecker.checkWithDefaultDialogs(this);
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
  }

  private void log(String message) {
    mLogBuilder.append("\n");
    mLogBuilder.append(message);
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        if(mLogView != null) {
          mLogView.setText(mLogBuilder.toString());
        }
      }
    });
  }

  private View.OnClickListener mOnClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
      switch(view.getId()) {
        case R.id.btn_start_scan:
          startToScan();
          break;
        case R.id.btn_stop_scan:
          stopToScan();
          break;
        case R.id.btn_clear_log:
          clearLog();
          break;
      }
    }
  };

  private void startToScan() {
    log("start to scan beacons()");

    mDeviceScanner.setOwnDevicesFiltering(false);
    ArrayList<DeviceType> deviceTypeList = new ArrayList<DeviceType>();
    deviceTypeList.add(DeviceType.LOCATION_BEACON);
    deviceTypeList.add(DeviceType.NEARABLE);
    deviceTypeList.add(DeviceType.PROXIMITY_BEACON);
    mDeviceScanner.setDeviceTypes(deviceTypeList);
    mDeviceScanner.scanForDevices(
        new ConfigurableDevicesScanner.ScannerCallback() {

          @Override
          public void onDevicesFound(
              List<ConfigurableDevicesScanner.ScanResultItem> devices) {
            log("onDevicesFound : " + devices.size());
            for(int i = 0; i < devices.size(); i++) {
              ConfigurableDevicesScanner.ScanResultItem item = devices.get(i);
              log((i + 1) + ". Device ID : " + item.device.deviceId);
              log("  - Device Type : " + item.device.type);
              log("  - RSSI : " + item.rssi);
            }

            log("");
          }
        });
  }

  private void stopToScan() {
    log("stop to scan beacons()");
    mDeviceScanner.stopScanning();
  }

  private void clearLog() {
    mLogBuilder.setLength(0);
    mLogView.setText("");
  }
}

Wi-FI를 이용한 ADB 연결

|

안드로이드 개발을 할 때 ADB 연결은 필수입니다. 보통은 케이블을 이용하면 되는데, 케이블 연결이 번거로운 경우에는 케이블 연결없이 Wi-Fi 기반으로 ADB 연결을 할 수 있습니다.

이런 경우가 얼마냐 있을까 싶지만, USB 연결 포트가 1개밖에 없는 노트북이 종종 있습니다. 대표적으로 제가 쓰고 있는 ‘갤럭시탭 프로 S’를 들 수 있습니다. USB-C 포트가 1개뿐이라 충전 케이블을 꽂으면 다른 케이블을 꽂을 수 없습니다. 그렇다고 USB 허브를 매번 들고 다닐 수도 없는 일이라 개발용으로 쓰기엔 난감합니다.

물론, Wi-Fi 기반으로 연결하긴 하지만 최초에 한 번은 케이블을 이용해서 연결을 해줘야 합니다.


설정 방법

  • 먼저 노트북과 스마트폰을 동일 공유기에 접속시켜 줍니다.
  • 스마트폰을 USB 케이블을 이용해서 노트북에 연결합니다.
  • cmd 명령어를 통해서 다음과 같이 실행합니다.
adb tcpip 5555
restarting in TCP mode port: 5555


  • 그리고 다음 명령어를 통해서 스마트폰의 IP Address를 알아냅니다.
adb shell ifconfig
  • 이제 끝났습니다. 케이블 연결을 해제해도 됩니다.
  • PC에서 스마트폰에 ADB 연결을 하기 위해서는 다음 명령어를 입력하면 됩니다.
adb connect [IP Address]:5555
ex) adb connect 192.168.0.140:5555
  • 현재 ADB 연결이 잘 되어 있는지 확인하기 위해서는 다음 명령어를 입력하면 됩니다.
C:>adb devices
List of devices attached
192.168.0.140:5555 device