Android SDK

오픈소스 비즈니스 컨설팅
둘러보기로 가기 검색하러 가기

스마트폰인 Android의 SDK를 정리 한다.

Android Architecture

system-architecture.jpg

Intent

Android - Intent

Activity

  • Android Activity의 정의
  • Application에서 각 화면의 처리를 담당하는 모듈로 일반적으로 Intent를 통한 요청으로 실행됨
  • Activity의 크기는 기본적으로 Status Bar 영역을 제외한 화면 전체임
  • Activity의 이름은 "a~z 0~9 _ ." 문자만 사용할 수 있음
public class android.app.Activity extends ContextThemeWrapper
    implements android.view.LayoutInflater.Factory,
               android.view.Window.Callback,
               android.view.KeyEvent.Callback,
               android.view.View.OnCreateContextMenuListener,
               ComponentCallbacks
  • Activity Lifecycle

activity_lifecycle.png

onCreate(~)
  • protected void onCreate(Bundle savedInstanceState)
  • startActivity(Intent intent)) 함수에 의해서 실행이 된다.
  • 저장된 상태값을 사용하여 Activity의 초기화를 한다.
onStart()
  • protected void onStart()
  • onStart() 함수가 호출되면 Activity가 Background(화면에 보이지 않음)에서 실행 대기 한다.
onRestart()
  • protected void onRestart()
onResume()
  • protected void onResume()
  • Activity가 Foreground(화면에 보임)에서 사용자의 입력을 처리하여야 할 때 호출 된다.
  • onResume()가 호출되면 해당 Activity가 제어권을 가진다.
onPause()
  • protected void onPause()
  • Activity가 Background(화면에 보이지 않음)에서 실행되어야 할 때 호출 된다.
  • onPause()가 호출되면 해당 Activity가 제어권을 잃는다.
  • Activity의 상태값을 저장 한다.
onStop()
  • protected void onStop()
onDestroy()
  • protected void onDestroy()
  • Finish() 함수를 통해 언제든지 Activity를 종료할 수 있다.
onActivityResult(~)
  • protected void onActivityResult(int requestCode, int resultCode, Intent data)
  • requestCode : startActivityForResult(~) 함수가 호출될 때 두번째 인자로 전달한 요청 코드
  • 호출 받은 Activity에서 setResult(resultCode, data) 또는 setResult(resultCode)를 설정하여야 한다.
  • startActivityForResult(Intent, requestCode) 함수를 사용하여 Activity가 호출 하였을 경우, 해당 Activity가 종료되면 OnActivityResult(~) 함수가 호출 된다.
  • Activity 내역 보기
cd c:/appl/androidSDK/tools/
adb shell dumpsys activity
  • 화면 회전 금지
android:screenOrientation="portrait"

Activity간 연동

하나의 Activity에서 다른 Activity를 호출할 때, Intent를 사용하여 값을 전달할 수 있다. 마찬가지로 호출된 Activity는 Intent를 사용하여 값을 반환할 수 있다. 여기서는 startActivityForResult를 사용하여 두 Activity간 연동하는 방법을 살펴 보자.

호출하는 Activity를 A Activity라고 하고 호출받는 Activity를 B Activity라고 해보자.

  • A Activity에서 B Activity 호출
  • A Activity 구현
  • startActivityForResult(Intent intent, int requestCode) : requestCode로 어떤 activity에서 호출했는지 확인 가능
Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
intent.putExtra("data", "전달하는 값");               //--- 전달할 값 저장
startActivityForResult(intent, 1);
  • B Activity 구현
String data = getIntent().getStringExtra("data");     //--- 전달받은 값
  • B Activity에서 A Activity로 반환
  • B Activity 구현
  • setResult(resultCode) 또는 setResult(resultCode, Intent data)으로 결과 반환
  • resultCode : Activity.RESULT_OK, Activity.RESULT_CANCELED 등
  • 주의 : "이전 메뉴" 메뉴를 선택하면 Activity.RESULT_CANCELED 가 반환 된다.
  • 일반적으로 protected void onPause() 에서 구현 한다.
Intent result = new Intent();
result.putExtra("result", "반환하는 값");             //--- 반환하는 값 저장
setResult(Activity.RESULT_CANCELED, result);
  • A Activity 구현
//--- 호출 받은 앱에서 결과값을 반환했을 때, 처리하는 함수
//--- 주의 : "이전 메뉴" 메뉴를 선택하면 Activity.RESULT_CANCELED 가 반환 된다.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    String result = null;

    super.onActivityResult(requestCode, resultCode, data);
    switch (resultCode) {
    case Activity.RESULT_OK:
        break;
    case Activity.RESULT_CANCELED:
	 if (requestCode == 1) {   //--- requestCode로 어떤 Activity에서 반환되었는지 확인 한다.
            if (data != null) {
  	         result = data.getStringExtra("result");    //--- 반환된 값
	         if ((result != null) && (result.equals("error"))) {
	             finish();
	         }
            }
	  }
	  break;
    }
}

Service

public abstract class android.app.Service extends ContextWrapper implements ComponentCallbacks

  • 정의
서비스는 백그라운드 작업을 처리 하거나 IPC (Inter-Process Communication)을 위한 원격 접속이 가능한 오브젝트를 만든다.
  • Intent 받아 백그라운드 작업을 처리하는 객체
  • Intent를 받아 IPC (Inter-Process Communication)을 위한 원격 접속 처리를 하는 객체

service_lifecycle.png

  • 백그라운드 Service
  • public void onCreate()
  • public void onStart(Intent intent, int startId)
private java.util.TimerTask task = new TimerTask() {
    public void run() {
    }
}

private java.util.Timer timer = new Timer();
timer.schedule(task, 5000);
  • public void onDestroy()
  • IPC용 Service
private final ISimpleMathService.Stub binder = new ISimpleMathService.Stub() { 구현 }
  • public void onCreate()
  • public abstract IBinder onBind(Intent intent)
return this.binder;
  • public void onRebind(Intent intent)
  • public boolean onUnbind(Intent intent)
  • public void onDestroy()
  • Publishing
  • Local
public class ActivityExample extends Activity {
    private ISimpleMathService service;

    private ServiceConnection connection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder iservice) {
            service = ISimpleMathService.Stub.asInterface(iservice);
        }
        public void onServiceDisconnected(ComponentName className) {
            service = null;
        }
    }

    public void onStart() {
        this.bindService(
            new Intent(ActivityExample.this, SimpleMathService.class), 
            connection, 
            Context.BIND_AUTO_CREATE);
    }

    public void onPause() {
        this.unbindService(connection);
    }
}
  • Remote Service
public class SimpleMathService extends Service {
    private final ISimpleMathService.Stub binder = new ISimpleMathService.Stub() { 구현 }

    public abstract IBinder onBind(Intent intent) {
        return this.binder;
    }
}

AIDL

  • Android Interface Definition Language
  • 원격 접속이 가능한 객체를 만들때 사용
  • AIDL을 사용한 IPC 구현


  • AIDL 원격 인터페이스 정의 파일 (~.aidl)
package com.msi.manning.binder;
interface ISimpleMathService {
    int add([in ]int a, out int b, inout int c)
}
  • in(디폴트), out, inout
  • AIDL에서 지원하는 type
Type 설  명 Import 여부
Java primitives boolean, byte, short, int, float, double, long, char 필요 없음
String java.lang.String 필요 없음
CharSequence java.lang.CharSequence 필요 없음
List ArrayList 필요 없음
Map HashMap 필요 없음
Other AIDL interface 기타 AIDL 인터페리스 타입으로 만들어진 것 필요
Parcelable object Android Parcelable interface를 구현한 것 필요
  • $ANDROID_HOME/tools/aidl
ISimpleMathService
ISimpleMathService.Stub : Local
    ISimpleMathService asInterface(IBinder b)
ISimpleMathService.Stub.Proxy : Remote
  • 참고 문헌

BroadcastReceiver

public abstract class android.content.BroadcastReceiver

  • 정의
Activity도 Service도 아니지만 Intent를 받아 간단한 작업을 처리 한다.
  • BroadcastReceiver 호출
  • android.content.Context.sendBroadcast(Intent intent)
  • android.content.Context.sendStickyBroadcast(Intent intent)
  • android.content.Context.sendOrderedBroadcast(Intent intent, String receiverPermission)
  • BroadcastReceiver의 Intent 처리 함수
  • android.content.BroadcastReceiver.onReceive(Context context, Intent intent)
  • Broadcast 중단 : this.abortBroadcast();

ContentProvider 생성

  • public abstract class android.content.ContentProvider implements ComponentCallbacks
public class MyProvider extends ContentProvider {
    public boolean onCreate() {
    }

    private static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    URI_MATCHER.addURI(authority, "widgets", 2);
    URI_MATCHER.addURI(authority, "widgets/#", 1);

    //--- Uri로 MIME TYPE를 반환 한다.
    public String getType(Uri uri) {
        switch (MyProvider.URI_MATCHER.match(uri)) {
        case  2 : return "vnd.android.cursor.dir/vnd.msi.mywidget";
        case  1 : return "vnd.android.cursor.item/vnd.msi.mywidget";
        default : throw new IllegalArgumentException("Unknown URI " + uri);
        }         
    }

    public Uri insert(Uri uri, ContentValues contentvalues) {
        public static Uri CONTENT_URI = Uri.parse("content://authority/widgets");

        //--- 등록된 레코드의 ID (rowId)로 URI를 생성하여 notify 한다.
        Uri result = ContentUris.withAppendedId(CONTENT_URI, rowId);
        this.getContext().getContentResolver().notifyChange(result, null); 
        return result; //--- 등록된 레코드의 URI
    }

    public int update(Uri uri, ContentValues contentvalues, String s, String as[]) {
        //--- 수정된 URI를 notify 한다.
        this.getContext().getContentResolver().notifyChange(uri, null);
        return 수정된 갯수;
    }

    public int delete(Uri uri, String s, String as[]) {
        //--- 삭제된 URI를 notify 한다.
        this.getContext().getContentResolver().notifyChange(uri, null);
        return 삭제된 갯수;
    }

    public Cursor query(Uri uri, String as[], String s, String as1[], String s1) {
        Cursor cur <- SQLiteQueryBuilder()

        //--- 향후의 notify를 위해 조회한 URI를 Cursor에 저장하여 반환 한다.
        cur.setNotificationUri(this.getContext().getContentResolver(), uri);
        return cur;
    }
}
  • AndroidManifest.xml
<provider android:name="MyProvider"
    android:authorities="authority" />
  • 참조
ContentProvider_입출력

Android UI

Android - UI

Android Thread

Android는 Java로 되어 있어 Java에서 제공하는 기능을 사용하여 Thread를 구현할 수 있습니다. 그러나, 안드로이드 화면의 각 요소(View)에 정보를 표시한다든지 하는 UI와 관련된 작업을 하는 경우에는 여러가지 제약 사항이 따릅니다.

Thread에서 UI 처리를 하기 위해서는 반드시 Activity 내에서 구현이 되어야 합니다.

Thread with Post

  • view.post()를 사용하여 UI 처리
new Thread(new Runnable() {
    public void run() {
        view.post(new Runnable() {
            public void run() {
                view.setText("hello");
            }
        });
    }
}).start();

Thread with runOnUiThread

  • Activity의 runOnUiThread()를 사용하여 UI 처리
new Thread(new Runnable() {
    public void run() {
        runOnUiThread(new Runnable() {
            public void run() {
                view.setText("hello");
            }
        });
    }
}).start();

Thread with Handler 1

  • Handler를 사용하여 UI 처리
private Handler handler = new Handler();

new Thread(new Runnable() {
    public void run() {
        handler.post(new Runnable() {
            public void run() {
                view.setText("hello");
            }
        });
    }
}).start();

Thread with Handler 2

  • android.os.Message
  • sendEmptyMessage(int what)
  • sendEmptyMessageAtTime(int what, long time)
  • sendEmptyMessageDelayed(int what, long delay)
  • Runnable
  • postEmptyMessage()
  • android.os.Looper


  • ANR (Application Not Responding, 응답없는 애플리케이션)
  • 5초 동안 사용자의 입력에 대해서 반응을 보이지 않는 애플리케이션
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

private Handler handler = new Handler() {
   public void handleMessage(final Message msg) {
       view.setText(msg.getData().getString("rtcd"));
   }
};

new Thread() {
    public void run() {
         Bundle bundle = new Bundle();
         bundle.putString("rtcd", "OK");

         Message msg = handler.obtainMessage();
         msg.setData(bundle);
         handler.sendMessage(msg);
         //handler.handleMessage(msg);
    }
}.start();

Thread with AsyncTask

Google에서는 AsyncTask라는 것을 제공하여 Android에서 UI 작업과 관련된 Thread의 구현을 손쉽게 할 수 있도록 지원 합니다.

UI Thread를 구현하기 위해서는 해당 Thread가 Activity에서 구현이 되어야 하며, 주고 받는 인자들의 타입이 일치 하여야 합니다. 아래 설명에서는 서로 일치해야 하는 인자들의 경우에는 동일한 색으로 표시를 해서 구분 합니다.

  • AsyncTask 호출 구현
private Activity activity = null;

activity = this;
(new theAsyncThread()).execute(para1, para2, para3);
  • AsyncTask 구현
private class theAsyncThread extends AsyncTask<String, String, String> {
    //--- Thread를 시작하기 전에 호출되는 함수
    protected void onPreExecute() {
        if ((activity != null) && (activity.isFinishing() == false)) {
            Toast.makeText(activity, "Before thread", Toast.LENGTH_SHORT).show();
        }
        super.onPreExecute();
    } 

    //--- Thread의 주요 작업을 처리 하는 함수
    //--- Thread를 실행하기 위해 excute(~)에서 전달한 값을 인자로 받습니다.
    protected String doInBackground(String... arg) {
        int argCnt = 0;
        
        argCnt = arg.length;
        if (argCnt != 3) {
            return "Error";
        }
        
        //--- onProgressUpdate(~) 실행하기 위해서는 아래 함수를 호출 합니다.
        publishProgress("Thread processing.");
        return "OK";
    } 

    //--- doInBackground(~)에서 호출되어 주로 UI 관련 작업을 하는 함수
    protected void onProgressUpdate(String... progress) {
        if ((activity != null) && (activity.isFinishing() == false)) {
            Toast.makeText(activity, progress[0], Toast.LENGTH_SHORT).show();
        }
    }

    //--- Thread를 처리한 후에 호출되는 함수
    //--- doInBackground(~)의 리턴값을 인자로 받습니다.
    protected void onPostExecute(String result) {
        if ((activity != null) && (activity.isFinishing() == false)) {
            Toast.makeText(activity, "After thread", Toast.LENGTH_SHORT).show();
        }
        super.onPostExecute(result);
    }

    //--- AsyncTask.cancel(true) 호출시 실행되어 thread를 취소 합니다.
    protected void onCancelled() {
        super.onCancelled();
    }      
}    
  • 참고 문헌

AndroidManifest.xml

  • Root 폴더에 존재하며 애플리케이션의 환경, Activity, Service, BroadcastReceiver, ContentProvider, Permission 등을 정의
  • <user-permission>, <permission>
<uses-permission android:name="android.permission.CALL_PHONE" />
  • <instrumentation>
  • <application>
  • <activity>, <receiver>, <service>, <provider>

PreferenceScreen

  • Preference 종류
  • CheckBoxPreference
  • EditTextPreference
  • DialogPreference
  • ListPreference
  • RingtonePreference
  • res/xml/settings.xml
  • /data/data/패키지/shared_prefs/패키지_preferences.xml 로 저장됨
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:key="root_key">
  <PreferenceCategory android:key="category_key" android:title="PreferenceCategory1" android:enabled="true">
    <PreferenceScreen android:title="제목 1" android:key="pref_1" android:summary="Preference 설명 1" >
      <EditTextPreference                            //--- EditText
          android:key="key1"                         //--- 저장될 key
          android:title="제목 1"                 
          android:summary="상세 설명 1"
          android:defaultValue="27"
          android:dialogTitle="@string/dialog_title_edittext_preference" />
      <CheckBoxPreference                            //--- CheckBox
          android:key="key2"                         //--- 저장될 key
          android:title="@string/title2"             //--- 제목
          android:summary="@string/summary2"         //--- 상세 설명
          android:defaultValue="true"                //--- Default 값
          android:dependency="parent_checkbox_preference" />
      <ListPreference
          android:key="key3"
          android:title="제목 3"
          android:summary="상세 설명 3"
          android:entries="@array/color"             //--- 화면 표시값
          android:entryValues="@array/color_value"   //--- 저장되는 값
          android:defaultValue="FFFFFFFF"
          android:dialogTitle="@string/dialog_title_edittext_preference" />
      <intent
          android:action="android.intent.action.VIEW"
          android:data="http://www.android.com" />
    </PreferenceScreen>
  </PreferenceCategory>
</PreferenceScreen>
  • Activity
public class Settings extends PreferenceActivity {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.settings);
}

   private ListPreference pref = null;
       pref = (ListPreference) findPreference("maxContacts");
       pref.setSummary(pref.getValue());
       pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
       	public boolean onPreferenceChange(Preference preference, Object newValue) {
       		app.setMaxContacts(Integer.parseInt((String)newValue));
       		pref.setSummary((String)newValue);
       		return true;
       	}
       });
        
       pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 
           public boolean onPreferenceClick(Preference preference) { 
               return false; 
           } 
       }); 

PreferenceScreen screen = (PreferenceScreen)getPreference("root_key");
CheckBoxPreference cb = (CheckBoxPreference)getPreference("key2");
screen.setEnabled(true);
Parent.setSummary("string depending on the selection");
  • 참고 문헌

ADB

참고 문헌

탐색

Android Android SDK Android Market