2013년 2월 14일 목요일

[Android][GAE] Upload a file to GAE server in Android Using HTTP POST

Google App Engine에서는 다양한 형태의 data object(text, image, sound, etc.)를 저장하기 위해 Blobstore 라는 service를 제공한다. 저장되는 파일의 max size는 32MB 이다.

상세내용은 아래의 링크를 참고하면 된다.
Google App Engine Blob Page (Python)

여기에서 제공되는 sample code의 일부를 아래에 발췌했다.
class MainHandler(webapp2.RequestHandler):
 def get(self):
  upload_url = blobstore.create_upload_url('/upload')
  self.response.out.write('<html><body>')
  self.response.out.write('<form action="%s" method="POST" 
       enctype="multipart/form-data">' % upload_url)
  self.response.out.write("""Upload File: <input type="file" name="file"> <br>
       <input type="submit" name="submit" value="Submit"> </form></body></html>""")

class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
 def post(self):
  upload_files = self.get_uploads('file')  # 'file' is file upload field in the form
  blob_info = upload_files[0]
  self.redirect('/serve/%s' % blob_info.key())
HTTP POST request를 통해서 선택한 file을 blob store에 저장하는 코드이다. 여기서 주의깊게 봐야 할 부분은 데이터가 저장되는 공간에 대한 url을 upload_url = blobstore.create_upload_url('/upload')를 통해 dynamic 하게 생성한다는 것이다. 한번 생성된 url은 10분 동안 유효하다. 즉, 10분 동안은 이 url을 blob store에 file을 저장하기위해 어디서든 사용할 수 있다는 뜻이다. upload_files = self.get_uploads('file')에서는 파일을 실제로 저장한다. blob_info.key()는 저장된 방금 저장된 file을 access 하기 위한 정보를 담고 있다.

위의 코드가 실행된 web page에서 소스보기를 하면 upload_url로 어떤 값이 설정됐는지 확인 할 수 있다.
다음과 같은 형태를 볼 수 있는데, action="http://xxx.appspot.com/_ah/upload/AMmfu6ZqsMW_ ... cs32MZ/" 여기서 " " 안의 값이 upload_url이다.

이 값을 사용해서 android에서 text file을 upload해 보는 코드를 아래와 같이 작성해 보았다.
static void fileup() {
 HttpClient httpclient = new DefaultHttpClient();
 HttpPost httppost = new HttpPost(upload_url); 
 try {
  MultipartEntity entity = new MultipartEntity();
  entity.addPart("file", new FileBody(new File(fileNameToSend, "text/plain"));
  httppost.setEntity(entity);
  HttpResponse response = httpclient.execute(httppost);
 } catch (ClientProtocolException e) {
 } catch (IOException e) {
 }
}
코드의 일부는 http://hc.apache.org/ 에서 제공하는 http library를 사용하였다.

upload된 파일은 Google App Engine의 Blob Viewer메뉴에서 확인할 수 있다.
Android에서 GAE의 blobstore에 저장이 잘 되는것은 확인 했지만, upload url을 android상에서 바로 생성할 수 없기 때문에 조금 다른 방식이 필요하다.
To be continued...

2013년 2월 13일 수요일

[Android] Automatic Running a Service after booting up

어떠한 background service가 사용자에 의해서 실행이 된 후에 폰이 재 부팅이 되는 일이 발생한다면, 사용자는 한번 실행 시켜둔 서비스가 부팅 후에도 자동으로 실행이 될 것으로 기대할 수 있다. 이런 경우, 폰의 부팅이 완료되고 난 시점에 사용자의 설정상태(폰 종료 전 서비스를 실행시켰었는지)를 확인하여 만약 그 상태가 on 이었다면 자동으로 서비스를 실행 시켜줘야 한다.

이를 위해선 크게 두가지를 해야한다.
1. 폰이 부팅이 된 시점을 알고, 그때에 일련의 작업을 처리할 수 있어야 한다.
2. 사용자의 서비스 실행 여부를 알 수 있어야 한다.

첫째로, android 시스템은 폰 부팅이 완료되고 나면 android.intent.action.BOOT_COMPLETED intent를 broadcast 한다. 이 intent를 받을려면 manifest 파일에 receiver를 등록하고 해당 intent를 추가해야 한다. App 내에서 code로 broadcast receiver/intent를 등록할 수 없는 이유는 app이 실행되기 전에 해당 intent를 받을 수 있어야 하기 때문이다. (코드로 등록한다는 것은 사용자가 앱을 실행해서 해당 루틴이 수행이 되어야 한다는 것을 전재로 한다.)

BOOT_COMPLETED intent를 받는 receiver code는 아래와 같다. onReceiver 루틴에서 부팅이 끝난 후 필요한 일을 처리하면 된다.
public class BootStartupReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){
   // After booting up, this routine is called
   // If s service was on before turned off, let's start here again
   ...
  }
 }
}
BOOT_COMPLETED intent를 받기 위해 AndroidManifest.xml 파일에 아래와 같이 permission과 receiver를 등록한다.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

...


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


이제 부팅이 끝나고 난 시점에 무언가를 처리할 수 있는 준비가 되었다. 그럼 두번째로 폰 종료 전 사용자의 서비스 실행여부에 대한 상태를 알 수 있어야 한다. 폰 재부팅 후에도 값을 유지하기 위해선 정보가 non-volatile 메모리, 즉 flash에 저장이 되어야 한다. Android에서는 이를 위해 크게 세가지 정도의 방법을 제공한다.
1. Preference
2. File System
3. SQLite DB
하지만, 여기서 저장해야 할 값은 boolean type의 간략한 정보이므로 preference를 이용하는게 적절하겠다.

사용자가 서비스를 on 할때, 아래와 같이 shared preference에 상태값을 저장한다.
public static final String PREF_FILE_NAME = "PrefFile";

// Read saved value
SharedPreferences preferences = getSharedPreferences(PREF_FILE_NAME, MODE_PRIVATE);
boolean serviceOn = preferences.getBoolean("serviceOn", false);
if(serviceOn){
 // Do something
}
폰 부팅 후, 저장된 상태값을 다시 읽어올 때는 아래와 같은 루틴을 사용하면 되겠다.
// Save setting
SharedPreferences preferences = getSharedPreferences(PREF_FILE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("serviceOn", isChecked);
editor.commit();

요약하면, 사용자가 background service를 실행할 때 그 상태를 preference로 저장하고, 폰이 재 부팅후 부팅 완료가 되는 시점에 preference에 저장해 두었던 값을 다시 읽어와서 그 상태가 on이면 자동으로 background service를 실행한다.

추가로, 화면이 켜져 있는 동안 혹은 사용자가 lock 화면을 해제한 동안만 서비스가 동작하게 하고싶다면, 아래의 intent를 받아서 적절히 처리하면 되겠다.
1. Intent.ACTION_SCREEN_ON
2. Intent.ACTION_SCREEN_OFF
3. Intent.ACTION_USER_PRESENT : 사용자가 lock 화면 해제시 발생
SCREEN ON/OFF의 경우는 AndroidManifest.xml파일에 등록하는 방법은 없다. 코드상에서 등록을 해야지만 수신할 수 있다. 3번의 경우는 코드와 manifest에 등록하는 방법 두가지가 모두 존재하지만 1, 2, 3번 의 경우 모두 필요할 때 코드상에서 추가하도록 하는게 적절해 보인다.
(3번의 경우, manifest로 등록하기 위해선 android.intent.action.USER_PRESENT 사용해야 한다.)