일부 서비스는 안드로이드가 부팅될 때 자동으로 실행될 필요가 있다. 



안드로이드 폰은 부팅이 끝나면 액션이 'android.intent.action.BOOT_COMPLETED'인 인텐트를 브로드캐스트한다. 그러므로 이 인텐트 브로트캐스트를 받을 수 있는 BroadcastReceiver가 필요하다. 

public class GPSLoggerServiceManagerextends BroadcastReceiver {
  @Override
  public void onReceive(Context ctx, Intent intent) {
   if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
     ComponentName cName = new ComponentName(ctx.getPackageName(), GPSLogger);
     ComponentName svcName = ctx.startService(new Intent().setComponent(cName);
     if (svc == null) {
       Log.e(TAG, "Could not start service " + cName.toString());
     }
   } else {
     Log.e(TAG, "Received unexpected intent " + intent.toString());
   }
  }
}

여기서 가장 핵심은 onReceive() 메소드이다. 원하는 인텐트가 브로드캐스트되면 onReceive() 메소드가 호출된다. 

그리고 리시버는 manifest 파일에 선언되어있어야 한다.

LocationLoggerServiceManager"
   android:enabled="true"
   android:exported="false"
   android:label="LocationLoggerServiceManager" >
  
   
  


또한 이 클래스는 보안 설정에 선언할필요가 있는 특정 이벤트 브로드캐스트를 들어야 하기 때문에 manifest 파일에RECEIVE_BOOT_COMPLETED퍼미션이 있어야 한다.


위와 같이 추가해주면 부팅이 끝나고서비스가 자동으로 실행되게 된다. 

-------
* 리플을 보고 더 이해하기 쉽게 전체적인 코드의 프레임웍을 첨부한다.

/src/.../BootSvc.java

package app.arsviator;
...
public class BootSvc extends Service {
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
    
  @Override
  public void onCreate() {
    super.onCreate();
        
    Log.i("BOOTSVC", "Service started at the BOOT_COMPLETED.");
  }
}



/src/.../BRcvr.java

package app.arsviator;
...
public class BRcvr extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {    
    if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
      Log.i("BOOTSVC", "Intent received");    

      ComponentName cn = new ComponentName(context.getPackageName(), BootSvc.class.getName());
      ComponentName svcName = context.startService(new Intent().setComponent(cn));
      if (svcName == null) 
        Log.e("BOOTSVC", "Could not start service " + cn.toString());
    }
  }
}


AndroidManifest.xml


http://schemas.android.com/apk/res/android"
      package="app.nautes"
      android:versionCode="1"
      android:versionName="1.0">
    
      
      
         android:enabled="true"
         android:exported="false"
         android:label="Broadcast Receiver" >
        
          
        
      
    


이 프로그램을 에뮬레이터에 설치한 다음 에뮬레이터를종료했다가 AVD Manager에서 다시 시작시키고 부팅이 끝난 다음 logcat을 보면다음과같다.

09-07 09:31:30.631: INFO/SurfaceFlinger(52): Boot is finished (22889 ms)
09-07 09:31:30.661: INFO/ARMAssembler(52): generated scanline__00000177:03515104_00000A01_00000000 [ 55 ipp] (79 ins) at [0x481508:0x481644] in 5894000 ns
09-07 09:31:30.750: INFO/ActivityManager(52): Start proc com.android.email for broadcast com.android.email/com.android.exchange.BootReceiver: pid=165 uid=10008 gids={3003, 1015}
09-07 09:31:30.860: DEBUG/HomeLoaders(105):   ----> items cloned, ready to refresh UI
09-07 09:31:31.230: DEBUG/ddm-heap(165): Got feature list request
09-07 09:31:32.790: DEBUG/dalvikvm(52): GC freed 14273 objects / 744904 bytes in 444ms
09-07 09:31:33.130: INFO/ActivityThread(165): Publishing provider com.android.email.provider: com.android.email.provider.EmailProvider
09-07 09:31:33.191: INFO/ActivityThread(165): Publishing provider com.android.email.attachmentprovider: com.android.email.provider.AttachmentProvider
09-07 09:31:33.300: DEBUG/Exchange(165): BootReceiver onReceive
09-07 09:31:33.360: DEBUG/EAS SyncManager(165): !!! EAS SyncManager, onCreate
09-07 09:31:33.430: INFO/ActivityManager(52): Start proc com.android.mms for broadcast com.android.mms/.transaction.MmsSystemEventReceiver: pid=176 uid=10013 gids={3003, 1015}
09-07 09:31:33.730: DEBUG/ddm-heap(176): Got feature list request
09-07 09:31:33.810: DEBUG/EAS SyncManager(165): !!! EAS SyncManager, onStartCommand
09-07 09:31:33.870: DEBUG/EAS SyncManager(165): !!! EAS SyncManager, stopping self
09-07 09:31:33.930: DEBUG/Eas Debug(165): Logging: 
09-07 09:31:34.031: DEBUG/EAS SyncManager(165): !!! EAS SyncManager, onDestroy
09-07 09:31:34.410: DEBUG/MediaScannerService(149): start scanning volume internal
09-07 09:31:35.750: INFO/ActivityManager(52): Start proc app.nautes for broadcast app.nautes/.BRcvr: pid=195 uid=10078 gids={1015}
09-07 09:31:36.280: DEBUG/ddm-heap(195): Got feature list request
09-07 09:31:36.290: DEBUG/dalvikvm(29): GC freed 280 objects / 10704 bytes in 526ms
09-07 09:31:36.590: DEBUG/dalvikvm(29): GC freed 50 objects / 2224 bytes in 224ms
09-07 09:31:36.650: INFO/BOOTSVC(195): Intent received
09-07 09:31:36.740: INFO/BOOTSVC(195): Service started at the BOOT_COMPLETED.
09-07 09:31:36.770: DEBUG/dalvikvm(29): GC freed 2 objects / 48 bytes in 174ms
09-07 09:31:37.931: DEBUG/dalvikvm(105): GC freed 4158 objects / 281904 bytes in 199ms
09-07 09:31:38.360: DEBUG/MediaScanner(149): opendir /system/media/ failed, errno: 2
09-07 09:31:38.360: DEBUG/MediaScanner(149):  prescan time: 1474ms
09-07 09:31:38.360: DEBUG/MediaScanner(149):     scan time: 55ms
09-07 09:31:38.360: DEBUG/MediaScanner(149): postscan time: 1ms
09-07 09:31:38.360: DEBUG/MediaScanner(149):    total time: 1530ms
09-07 09:31:38.390: DEBUG/MediaScannerService(149): done scanning volume internal
09-07 09:31:39.180: DEBUG/dalvikvm(105): GC freed 3308 objects / 168792 bytes in 169ms


로그를 보면 부팅이 끝난 다음 서비스가 시작된걸확인할 수 있다.
 
 
 
 
주요 소스
 
AndroidManifest.xml   
 
     android:permission="android.permission.RECEIVE_BOOT_COMPLETED">  
   
       
           
           
     
  
   
 
   
 



BootReceiver.java
 public class BootReceiver extends BroadcastReceiver{  
   
     @Override  
     public void onReceive(Context context, Intent intent) {  
         Intent i = new Intent(context, MyActivity.class);    
         i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
         context.startActivity(i);    
     }  
 }

부팅시에 시작되는 BroadcastReceiver에서 시작시키고 싶은 Activity를 인텐트로 지정해서 시작시키면 됩니다.
 
 
주의 : 반드시 filter를 AndroidManifest.xml에서 추가 시켜야 한다.

'안드로이드' 카테고리의 다른 글

Notification Manager  (0) 2011.01.27
Android: gdb를 이용한 Dalvik VM 디버깅  (0) 2011.01.27
Fingerprint 추적 방법  (0) 2011.01.27
디바이스의 고유 정보값 얻어오기  (0) 2011.01.27
타이틀 바 꾸미기  (0) 2011.01.27

1. ps 명령어

현재 실행되는 프로세스의 상태를 나타내주는 명령어이다.

ps명령어는 기본적으로 해당 사용자 소유의 프로세스만 보여준다. 아무런 옵션을 주지 않았을 때 출력되는 필드는 다음과 같다. (출력되는 내용은 시스템마다 차이가 있다.)

  • PID: 프로세스 아이디
  • TTY: 프로세스와 연결된 터미널 포트
  • TIME: 프로세스에서 사용한 CPU시간
  • CMD: 명령어
ps명령어를 사용할 때 -aux 옵션을 주어서 사용하는 경우가 많은데 이 옵션의 의미는 다음과 같다.
-a: 모든 사용자의 프로세스를 출력하는 옵션
-u: 자세한 정보를 출력하는 옵션
-x: 제어터미널이 없는 프로세스도 출력하는 옵션
  • USER: 프로세스 사용자
  • %CPU: CPU 사용 비율
  • %MEM: 메모리 사용 비율
  • VSZ: 가상 메모리 사용량
  • RSS: 실제 메모리 사용량
  • STAT: 현재 프로세스의 상태 
              R: 실행중 혹은 실행할수 있는 상태
              S: 수면 상태
              I: 휴식상태
              T: 정지상태
              Z: 좀비 프로세스
              W: 스왑 아웃된 상태
              P: 페이지 대기
              D: 디스크 대기
              N: nice로 실행 우선순위가 낮아진 상태
  • START: 프로세스가 시작된 시각 혹은 날짜
그외에 여러 옵션이 있다. 나머지는 찾아보시라...

-j: 작업중심의 형태로 출력한다. 
-l: 자세한 형태의 정보를 출력하는 옵션
  • F: 프로세스와 관련된 플래그
  • S: 프로세스 상태 (위의 STAT필드와 동일)
  • PPID: 부모 프로세스의 PID
  • C: 단기간 CPU사용률
  • PRI: 실제 실행 우선순위
  • NI: 프로세스의 nice 수치
  • WCHAN: 프로세스가 기다리고 있는 이벤트


참고:
UNIX power tools, o'reilly


Android 동작중에 달빅이 리셋되는 경우가 있다.
이 경우 fingerprint 메세지가 나오는데 이것을 분석하는 방법입니다.
우선 fingerprint가 저장된 로그를 저장해 둔다.

---------------------------------------------------------------------------------------------------------------------------------
07-14 17:00:05.909: INFO/DEBUG(1198): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
07-14 17:00:05.909: INFO/DEBUG(1198): Build fingerprint: 'XXXXX/XXXX/XXXXX/8x50:2.1-update1/ERE27/eng.xxxxx.20100712.092719:userdebug/test-keys'
07-14 17:00:05.909: INFO/DEBUG(1198): pid: 1354, tid: 2191  >>> system_server <<<
07-14 17:00:05.909: INFO/DEBUG(1198): signal 11 (SIGSEGV), fault addr 00000000
07-14 17:00:05.909: INFO/DEBUG(1198):  r0 00000001  r1 00000007  r2 00000002  r3 0069b920
07-14 17:00:05.909: INFO/DEBUG(1198):  r4 007061b0  r5 00000000  r6 00000001  r7 6d07bb88
07-14 17:00:05.909: INFO/DEBUG(1198):  r8 00100000  r9 6d3647e1  10 38361000  fp 00000000
07-14 17:00:05.909: INFO/DEBUG(1198):  ip 6d07b05c  sp 38460df0  lr 6d035c57  pc 6d035c58  cpsr 20000030
---------------------------------------------------------------------------------------------------------------------------------

1. 우선 컴파일 된 경로의 root를 간다. [cmd 창 연다]
 보통 아래 처럼 경로가 있는데 android 상위 폴더에서 처리한다.

Project -
    android
    modem

2. 저장시킬 로그를 project 폴더에 복사한다.

3. "parse_mapfile.pl" 파일을 생성하고 아래 내용을 저정한다.

---------------------------------------------------------------------------------------------------------------------------------
##########################################################
#
#!/usr/bin/perl
#
# Searches android logfiles for android stack dumps and 
# generates a calltrace.
#
# Syntax : perl ./parse_mapfile.pl  
# is option and is not needed if calling script 
#  from /build folder e.g.
#
#  For emulator builds
#  perl ./scripts/parse_mapfile.pl generic
#  
#  For dream builds
#  perl ./scripts/parse_mapfile.pl dream
#
#
##########################################################

use strict;

sub findsym;

my $mydroid_root = "../mydroid"; #default
my $logfile = $ARGV[0];
my $target = "generic"; # default emulator

if ($ARGV[1])
{
   $target = $ARGV[1];
}

if ($ARGV[2])
{
   $mydroid_root = $ARGV[2];
}

print("target=$target\n");
print("mydroid_root=$mydroid_root\n");

my $nm="$mydroid_root/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/arm-eabi/bin/nm";
my $sympath = "$mydroid_root/out/target/product/$target/symbols";
my $sympath_alt = "$mydroid_root/out/target/product/$target/";
my $parsing_syms = 0;
my $pos;
my $i;

open LOGFILE, "<", $logfile or die $!;

# Search for stack dump

while (
{
    my($logline) = $_;
    my $search_start="Build fingerprint:";
    my $search_stop="stack:";
    chomp($logline);
        
    $pos++;
    if($logline =~ m/$search_start/) 
    {
        print("#######################################################################################\n");
        print("# Found stackdump @ line $pos....generating calltrace\n\n");
        $parsing_syms = 1;
        $i = 0;
    }

    if ($parsing_syms)
    {
        if($logline =~ m/(#.*)pc (.*) (.*)/g)
        {       
            findsym($2, $3);
        }
    }

    if($logline =~ m/$search_stop/) 
    {
        $parsing_syms = 0;      
        print("\n#\n#######################################################################################\n"); 
    }
}


sub findsym
{
    my $mapfile = "syms.map";
    my $hprev_sym;
    my $hprev_addr = 0;
    my $hcur_addr;
    my $hcur_sym = 0; 
    my $binary=@_[1];
    my $mappath;
    my $symbol = @_[0];
    my $cmd;
    my $prev_sz;
    my @line1;
    my $cur;
    my $offset;
    my $pos = 0;
    my $symfile;
    
    chomp($nm);
    chomp($binary);

    # remove and CR/LF
    $binary =~ s/\r|\n//g;

    $symfile = "$sympath$binary";

    if (! -e $symfile)
    {
        # try alternate location
        $symfile = "$sympath_alt$binary";
        #print("Couldn't find $binary, try alternate path\n");
    }

    if (! -e $symfile)
    {
        print("could not find binary, exit");
        exit 1;
    }
    
    my $my_cmd="$nm $symfile | sort > $mapfile";

    system($my_cmd);

    open MAPFILE, "<", $mapfile or die $!;

    while ()   
    {
        my($line) = $_;

        chomp($line);

        # split line on any nbr spaces
        @line1 = split(/ +/, $line);

        # search all code segments
        {
            $hcur_addr = $line1[0];
            $hcur_sym = $line1[2];

            $cur = hex($symbol);
            $a = hex $hprev_addr;
            $b = hex $hcur_addr;
            $prev_sz = $b-$a;

            #print "lr=$cur, a=$a(0x$hprev_addr), b=$b(0x$hcur_addr)\n";

            if (($cur >= $a) && ($cur <= $b))
            { 
                $offset = $cur-$a;
                print sprintf("%25s @ 0x%08x (fn pos %04d/%04d) [0x%08x] $binary\n", 
                           $hprev_sym, $a, $offset, $prev_sz, $cur);
                $i++;
            }
        }
        $hprev_sym = $hcur_sym;
        $hprev_addr = $hcur_addr;
    }    
    close MAPFILE;
    
    my $my_cmd="rm $mapfile";
    system($my_cmd);
}

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

4. cmd창에서 아래의 형식으로 명령을 수행한다.
perl parse_mapfile.pl

만약 모델 명 XXTT1, android 폴더명이 "android", fingerprint로그가 저장된 파일명이
"log.log"라면 아래와 같이 해준다.
perl parse_mapfile.pl Log.log XXTT1 android
 
5. 위와 같이 실행후 나온 결과를 분석한다. call stack이 찍힌다.
수행 결과 => 숫자들이 알아볼 수 있도록 변경된 것을 확인 할 수 있습니다.
#######################################################################################
# Found stackdump @ line 161....generating calltrace
             attachThread @ 0x00035bbc (fn pos 0156/0184) [0x00035c58] /system/lib/libdvm.so
      AttachCurrentThread @ 0x00035c80 (fn pos 0004/0012) [0x00035c84] /system/lib/libdvm.so
_ZN8broadcomL19handleTestModeEventEitP11tBTL_PARAMS @ 0x0005c698 (fn pos 0034/0364) [0x0005c6ba] /system/lib/libandroid_runtime.so
btl_ifc_notify_local_event @ 0x000641ac (fn pos 0040/0108) [0x000641d4] /system/lib/libandroid_runtime.so
 ctrl_socket_disconnected @ 0x00064560 (fn pos 0106/0160) [0x000645ca] /system/lib/libandroid_runtime.so
        process_ctrl_data @ 0x00064684 (fn pos 0078/0248) [0x000646d2] /system/lib/libandroid_runtime.so
         monitor_ctrl_fds @ 0x0006477c (fn pos 0010/0024) [0x00064786] /system/lib/libandroid_runtime.so
       main_client_thread @ 0x000647e0 (fn pos 0226/0324) [0x000648c2] /system/lib/libandroid_runtime.so
           __thread_entry @ 0x0000fd70 (fn pos 0048/0060) [0x0000fda0] /system/lib/libc.so
           pthread_create @ 0x0000f7b4 (fn pos 0188/0612) [0x0000f870] /system/lib/libc.so
#
#######################################################################################

출처 : http://blog.naver.com/PostView.nhn?blogId=kkamci25&logNo=10098930924&viewDate=&currentPage=1&listtype=0

안드로이드는 아이폰과 달리 기기마다 특성이 다르고 덕분에 어떤 디바이스에서는 잘 되는데 어떤 디바이스에서는 잘안되고 이런 문제가 허다하더군요. 이 문제를 어떻게 해결하지 못하면 앞으로 디바이스 종류는 더 다양해 질텐데 개발자들의 한숨만 늘어날수도 있겠습니다.

아무튼 다양한 디바이스에서 잘 돌아가게 만드는것이 중요한데 개발을 하다보면 한계에 봉착합니다. 무엇이냐면 어떤 디바이스에서는 죽어도 안되는 경우입니다. 아니면 아예 다른 방법으로 접근해야 간신히 문제가 해결되는 독특한 디바이스들이 있습니다. 이경우 어떻게 해야 할까요.

물론 디바이스의 이름을 알아내서 이때만 예외처리를 하는 방법도 있겠습니다. 비참하지만 능력 부족의 한계ㅠㅠ 아무튼 이런 경우에 디바이스에서 어떤 정보를 가져올 수 있는지 확인해 보았습니다. Build라는 클래스의 정보들을 읽어보시면 원하는 정보를 얻어올 수 있습니다.

package kr.pe.theeye;


import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;


public class ModelNameTest extends Activity
{
   
/** Called when the activity is first created. */
   
@Override
   
public void onCreate(Bundle savedInstanceState)
   
{
       
super.onCreate(savedInstanceState);
        setContentView
(R.layout.main);
       
       
Log.d("ModelNameTest", "BOARD: " + Build.BOARD);
       
Log.d("ModelNameTest", "BRAND: " + Build.BRAND);
       
Log.d("ModelNameTest", "CPU_ABI: " + Build.CPU_ABI);
       
Log.d("ModelNameTest", "DEVICE: " + Build.DEVICE);
       
Log.d("ModelNameTest", "DISPLAY: " + Build.DISPLAY);
       
Log.d("ModelNameTest", "FINGERPRINT: " + Build.FINGERPRINT);
       
Log.d("ModelNameTest", "HOST: " + Build.HOST);
       
Log.d("ModelNameTest", "ID: " + Build.ID);
       
Log.d("ModelNameTest", "MANUFACTURER: " + Build.MANUFACTURER);
       
Log.d("ModelNameTest", "MODEL: " + Build.MODEL);
       
Log.d("ModelNameTest", "PRODUCT: " + Build.PRODUCT);
       
Log.d("ModelNameTest", "TAGS: " + Build.TAGS);
       
Log.d("ModelNameTest", "TIME: " + Build.TIME);
       
Log.d("ModelNameTest", "TYPE: " + Build.TYPE);
       
Log.d("ModelNameTest", "USER: " + Build.USER);
   
}
}



사용자 삽입 이미지
위는 시뮬레이터에서 실행한 결과입니다. 시뮬레이터의 정보들을 확인해 보실 수 있습니다.

사용자 삽입 이미지
위는 디자이어에서 실행해본 결과입니다. 곳곳에서 보이는 bravo를 보니 아무래도 디자이어의 코드명은 브라보가 아니었을까 하는 생각이 드는군요.

출처 : http://theeye.pe.kr/entry/how-to-get-my-devices-extra-information

'안드로이드' 카테고리의 다른 글

서비스를 부팅시 시작시키기 (Start Service at Boot Time)  (0) 2011.01.27
Fingerprint 추적 방법  (0) 2011.01.27
타이틀 바 꾸미기  (0) 2011.01.27
웹킷 브라우저  (0) 2011.01.27
안드로이드 2.0 Service API 변화  (0) 2011.01.27
이름
mmap, munmap - 
파일이나 장치를 메모리로 대응시키거나 대응을 푼다. 
사용법
#include 
#include 
#ifdef _POSIX_MAPPED_FILES 

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset); 

int munmap(void *start, size_t length); 

#endif 

설명
mmap 
함수는 fd로 지정된 파일(또는 다른 객체)에서 offset을 시작으로 length 바이트만큼을 start 주소로 대응시키도록 한다. 이 주소는 단지 그 주소로 했으면 좋겠다는 정도이며, 보통 0으로 지정한다. mmap는 지정된 영역이 대응된 실제 시작 위치를 반환한다. prot 인자는 원하는 메모리 보호 모드를 설정한다. 해당 비트는 다음과 같다. 
PROT_EXEC 
페이지는 실행될 수 있다. 
PROT_READ 
페이지는 읽을 수 있다. 
PROT_WRITE 
페이지는 쓰여질 수 있다. 
PROT_NONE 
페이지는 접근될 수 없다. 
flags 
인자는 대응된 객체의 타입, 대응 옵션들, 대응된 페이지 복사본에 대한 수정이 그 프로세스에만 보일 것인지 다른 참조하는 프로세스와 공유할 것인지를 설정한다. 해당 비트들은 다음과 같다. 

MAP_FIXED 
지정된 주소 이외의 다른 주소를 선택하지 않는다. 지정된 주소가 사용될 수 없다면, mmap은 실패할 것이다. 만일 MAP_FIXED가 지정되면, start는 페이지 크기의 배수이어야 한다. 이 옵션은 사용하지 않는 것이 좋다. 
MAP_SHARED 
이 객체를 대응시키는 다른 모든 프로세스와 이 대응 영역을 공유한다. 
MAP_PRIVATE 
개별적인 copy-on-write 대응을 만든다. (다른 프로세스와 대응 영역을 공유하지 않는다.) 
MAP_SHARED
 MAP_PRIVATE 중 하나는 반드시 명시해야 한다. 

위 세 플래그는 POSIX.1b에 규정되어 있다.(공식적으로 POSIX.4) 또한 리눅스에는 MAP_DENYWRITE, MAP_EXECUTABLE 그리고 MAP_ANON(YMOUS)도 있다. 

munmap 
시스템 콜은 지정된 주소 공간에 대한 대응을 푼다. 범위내의 주소에 대한 참조 계수를 늘려서 유효하지 않는 메모리 참조로 만든다. 



반환값
성공시, mmap은 대응된 영역의 포인터를 반환한다. 에러시, MAP_FAILED(-1)이 리턴되며, errno는 적당한 값으로 설정된다. 성공시, munmap 0을 리턴하며, 실패시 -1이 리턴되며, errno가 설정된다 (보통EINVAL이 설정된다). 
에러
EBADF 
fd
가 유효한 파일 기술자가 아니다 (그리고 MAP_ANONYMOUS가 설정되어 있지 않다). 
EACCES 
MAP_PRIVATE
가 설정되었지만, fd가 읽을 수 있도록 열려있지 않다. 또는 MAP_SHARED PROT_WRITE가 설정되었지만, fd가 쓸 수 있도록 열려있지 않다. 
EINVAL 
start
 length offset가 적당하지 않다 (, 너무 크거나 PAGESIZE 경계로 정렬되어 있지 않다). 
ETXTBUSY 
MAP_DENYWRITE
가 설정되었으나 fd로 지정된 객체가 쓰기 위해 열려있다. 
EAGAIN 
파일이 잠겨있거나, 너무 많은 메모로가 잠겨있다. 
ENOMEM 
사용할 수 있는 메모리가 없다. 

호환
SVr4, POSIX.1b (formerly POSIX.4), 4.4BSD. Svr4 
문서에는 ENXIO ENODEV 에러 코드가 추가되있다. 
관련 항목
getpagesize(2), msync(2), shm_open(2), B.O. Gallmeister, POSIX.4, O'Reilly, 
페이지. 128-129 389-391. 
역자
정강훈 , 2000 5 14

'기타' 카테고리의 다른 글

[Hudson] 자동 빌드 설정 방법  (0) 2012.08.10
VPN  (0) 2011.05.03
SVN GUI TOOL[SVN Tool - RapidSVN 사용법]  (0) 2011.02.09
SVN  (0) 2011.02.09
Source Insight 3.5 에서 한글 주석 읽는 방법  (0) 2011.01.16

간단한 예제:

1. onCreate 에서 

requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
            
        setContentView(R.layout.main);  // setContentView 가 반드시 이 위치에 있어야 정상 동작함.
            
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

// title bar 에 사용자 정의된 것들임.
        imageview = (ImageView)findViewById(R.id.favicon);
        textview2 = (TextView)findViewById(R.id.right_text);
        progress1 = (ProgressBar)findViewById(R.id.progress2);
            
        imageview.setImageResource(R.drawable.app_web_browser_sm);


2. custom_title.xml


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

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/screen" android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <ImageView android:id="@+id/favicon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:scaleType="center" />
   
 
<ProgressBar android:id="@+id/progress2"
            style="?android:attr/progressBarStyleHorizontal"
            android:gravity="center_vertical"
            android:paddingRight="5dip"
            android:layout_width="200dip"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/favicon"/>
        
    <TextView android:id="@+id/right_text"
        android:gravity="center_vertical"
        style="?android:attr/windowTitleStyle"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent" android:paddingRight="5dip"
        android:layout_toRightOf="@id/favicon"/>

 
<ImageButton 
android:id="@+id/stop"
android:layout_width="30dip"
         android:layout_height="fill_parent"
        android:src="@drawable/icon"
         android:layout_toRightOf="@id/progress2"
        />
 
</RelativeLayout>

3. snapshot

title bar 만 보면 imageview, progressbar, textview, imagebutton 으로 사용자 구성이 된 것임.



'안드로이드' 카테고리의 다른 글

Fingerprint 추적 방법  (0) 2011.01.27
디바이스의 고유 정보값 얻어오기  (0) 2011.01.27
웹킷 브라우저  (0) 2011.01.27
안드로이드 2.0 Service API 변화  (0) 2011.01.27
Handler  (0) 2011.01.27

안드로이드에 내장된 브라우저는 구조가 굉장히 복잡하기 때문에 android.webkit이라는 독립적인  담겨있다. 그리고 WebView라는 위젯을 사용해 간단하게 화면에 HTML을 표시하는 정도로  수 있고, 아니면 들어 있는 기능을 총동원해 강력한 브라우징 기능을  수도 있다.

 

1. 단순한 브라우저

   간단한 기능만 사용한다면 WebView 역시 안드로이드의 다른  별반 다를게 없다. 그저 레이아웃에 추가하고, 자바 코드에서 URL만 지정해주면  URL의 내용을 바로 표시해준다.

 

       layout/main.xml의 예제

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
     >
     <WebView android:id="@+id/webkit"
            android:layout_width="fill_parent" 
              
      />
</LinearLayout>

        자바코드 예제

import android.app.Activity;
import android.os.Bundle;
import 

public class BrowserDemo1 extends Activity {
      WebView  
 
      @Override
      public void onCreate(Bundle  {
           super.onCreate(icicle);
          setContentView(R.layout.main);
           browser=(WebView)findViewById(R.id.webkit);
  
           browser.loadUrl("http://commonsware.com");
    }
}

           사용할 수 있는 권한을 확보하도록 AndroidMainifest.xml 파일을 수정해야 한다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.commonsware.android.webkit">
 <uses-permission android:name="android.permission.INTERNET" />
 <application>
 <activity android:name=".BrowserDemo1" android:label="BrowserDemo1">
   <intent-filter>
    <action android:name="android.intent.action.MAIN" />
   <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
  </activity>
 </application>
</manifest>

         - WebView 위젯에서는 자바스크립트가 기본적으로  있다. 자바스크립트를 동작하도록 활성화하려면 WebView 인스턴스의 getSettings().setJavaScriptEnabled(true) 메소드를 호출하면 된다.

 

2. 원하는 내용 표시

    WebView 안에 원하는 내용을 표시하는 방법 두가지

   - loadUrl() 메소드를 사용해 WebView에 URL을 넘겨주고 해당하는 페이지의  가져와 표시하도록 하는 방법

    - loadData() 메소드에는  HTML을 직접 전달한다.

 

    WebView 위젯을  좋은 경우

    - 애플리케이션 패키지와 함께 설치된  화면에 표시하고자 할 때

    - 다른 작업을  결과로 받아온 HTML 형태의 문자열을 표시할 때

     ex) Atom 피트에서 뽑아낸 본문 내용을 화면에 표시할 때

   - 안드로이드에 내장된 위젯 대신 HTML 만으로 애플리케이션 화면을  경우

 

    loadData() 메소드 설명

   - loadData() 메소드의 가장 간단한 형태는 본문내용, MIME 타입,  인코딩 등의 내용을 모두 문자열로 지정한다.(MIME 타입은 text/html이며, 인코딩은 UTF-8을 

 

  browser.loadData("<html><body>Hello, world!</body></html>",
      "text/html", "UTF-8");

3. 브라우저 내비게이션

   WebView에는 기본적으로 브라우저 내비게이션 툴바가 포함돼 있지  앞으로, 뒤로 등의 기능을 갖고 있는 내비게이션 툴바를 별로 사용할  없거나 화면을 최대한 활용해야 하는 경우에는 툴바가 없는 편이 낫다.  브라우저 내비게이션 기능을 사용자에게 제공하려면 내비게이션 기능과 관련된 인터페이스를 개바자가  만들어야 하는 단점도 있다.

 

   WebView 위젯에는  같은 다양한 브라우져 내비게이션 기능이 내장

   - reload() 메소드 : 현재 표시하는 페이지 내용을 새로 고침

   -  메소드 : 브라우저 URL 기록 가운데 바로 이전 주소로 되돌아간다.

   - goForward() 메소드 : 브라우저 URL 기록 가운데 바로  주소로 나아간다.

   - goBackOrForward() 메소드 : 브라우저의 URL  앞뒤 원하는 방향으로 이동할 수 있다. 넘겨주는 값이 0보다 작으면  숫자만큼 뒤로 돌아가고, 0보다 크면 해당 숫자만큼 앞으로 나아간다.

  - canGoBackOrForward() 메소드 : 넘겨주는 숫자값만큼 브라우저 URL 기록을 따라  수 있는지 확인한다.

   - clearCache() 메소드 : 브라우저가  갖고 있던 파일을 제거한다.

   - clearHistory() 메소드는  URL 기록을 제거한다.

 

4. WebViewClient

   WebView 위젯을 사용해 애플리케이션 내부 데이터를 표현하려면,  표시된 HTML 링크를 플릭하는 등의 상황에서 제어권을 넘겨받아야 할 필요가 

    - 특정 작업에 대한 제어권을 확보하려면  인스턴스를 하나 생성해 WebView 위젯의 setWebViewClient() 메소드에 넘겨줘야 한다.

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 java.util.Date;

public class BrowserDemo3 extends Activity {
     WebView browser;
 
      @Override
      public  onCreate(Bundle icicle) {
            super.onCreate(icicle);
           setContentView(R.layout.main);
           browser=(WebView)findViewById(R.id.webkit);
            browser.setWebViewClient(new Callback());
 
            loadTime();
    }
 

    // 현재 시각을  HTML 형태로 생성해 WebView 위젯에 적용
    void  {
            String page="<html><body><a href=\"clock\">"
                                  +new Date().toString()
                                  +"</a></body></html>";
      
           browser.loadDataWithBaseURL("x-data://base", page,
                                                      "text/html", "UTF-8", null);
     }

 

    private class Callback extends WebViewClient {

        // 링크 클릭 이벤트 처리

         public boolean shouldOverrideUrlLoading(WebView view, String url)  
                loadTime();
   
               return(true);
        }
    }
}

5. 설정과 

    데스트탑에서 사용하던 웹브라우저를 보면 각종  옵션이 가득하다. 수많은 설정이나 옵션과 툴바 컨트롤 사이에서 기본 글꼴을  자바스크립트의 동작 상태를 지정하는 등 다양한 기능에 대해 설정값을 지정할  있다.

 

    WebSetting 클래스의 설정 메소드

     - setDefaultFontSize() 메소드 : setTextSize() 메소드를 사용해 기본  크기를 지정할 수 있다.

     - setJavaScriptEnabled() 메소드  자바스크립트를 켜거나 끌 수 있다.

     - setUserAgent()  : 0값을 넘겨주면 WebView에서 대상 웹사이트에 스스로가 휴대폰용 브라우저라는 사실을  통해 알려준다.


출처 : http://nopd.textcube.com/?indexTimestamp=1264487399&c=5

'안드로이드' 카테고리의 다른 글

디바이스의 고유 정보값 얻어오기  (0) 2011.01.27
타이틀 바 꾸미기  (0) 2011.01.27
안드로이드 2.0 Service API 변화  (0) 2011.01.27
Handler  (0) 2011.01.27
hardware keyboard의 존재 여부 및 상태 확인  (0) 2011.01.27

개인적으로 Service 는 사용하는 일은  관리가 어렵고 생각 못한 문제가 많이 발생해서 서 늘 고생하고 있는 부분이였습니다만, 구글 쪽에서도 관련되어 여러가지 고민이 많았는지, 2.0 이후 부터는 Service API 에 많은 변화가 생겼네요. 구글 Android Developer 블로그에 Android 2.0 에서사 변경된 Service API 에 관한 글이 있어서 번역해 보았습니다. 원래 번역글을 블로그에 올려두고 있었습니다만, 안드로이드 펍 분들에게도 혹시 도움이 되지 않을까 해서 올려봅니다.

원문 : http://android-developers.blogspot.com/2010/02/service-api-changes-starting-with.htm


Service API changes starting with Android 2.0
  • Service.setForeground() 가 더 이상 작동하지 않음. (Deprecated)
  • 상황에 따라 더 이상 필요하지 않은 Service 가 죽지 않는 경우가 많았는데, 이런한 에외사항을 보다 잘 처리할 수 있도록 새로운 API 를 제공함.
  • 사용자들이 현재 시스템에서 작동 중인 Service 를 확인하고 관리할 수 있는 UI 를 제공.
Background on services

  서비스 API 는 어플리케이션이 백그라운드에서 특정 작업을 수행할 수 있게 해주는.안드로이드의 핵심 매커니즘의 하나이다. 안드로이드 플랫폼 특성상, 화면상에 표시되지 않은 개별 어플리케이션은 시스템이 종료시키고, 필요한 경우 다시 시작할 수 있다고 여겨진다. 따라서 특정 어플리이션이 백그라운드에서도 특정 일을 수행할 필요가 있을 경우, Service 를 시작해야 하며, 시스템은 Service 를 구동중인 어플리케이션은 강제로 종료시키지 않는다. (최대한...)

   Service 는 매우 유용한 수단이지만, 언제나 권한에는 책임이 뒤 따른다. (power comes some responsibility). Service 를 가동하고 있는 어플리케이션은 시스템 자원을 소모기 때문에 전반적인 시스템 성능에 문제를 일으킬 수 있다. 따라서 개발자들은 정말로 필요한 경우에만 Service 를 사용해야 하며, 필요없는 Service 가 백그라운드에 남아있지 않도록 주의를 기울여야 한다. (서비스 릭 버그를 내지 말아야 한다...)

Redesigning Service.setForeground()

 Androi 1.6 안정화 기간동에, 너문 많은 어플리케이션이 Service.setForeground() API 를 사용하기 때문에 발생하는 문제들이 발견되었다.  이 API가 호출 되면,  시스템은 특정 Service가 구동중인 프로세스가 포그라운드 프로세스로 간주한다. (우선 순위 부여)  따라서, 시스템이 메모리 부족등의 예외 상황을 처리하게 매우 어렵게 만든다. 

  이러한 문제점이 발견된 시점이 너무 늦었기에, 1.6 버전 SDK는 이 API에 대하여 큰 변화 없이 공개되었지만, 2.0 에서는 중요한 변경사항이 이루어졌다. Service.setForeground() 는 이제 작동하지 않는다. 따라서 기존의 API 를 대체해서 2.0 버전에서는 두가지  새로운 API 가 추가되었다. 이제 Service 가 포그라운드로 진입하고자 할  경우, 사용자가 해당 사실을 확인하고 필요한 경우, 포그라운드에서 작동중인 서비스를 정지 시킬 수 있도록, Notification 을 보내야 한다. (주> 음악 재생 서비스 와 같이, 상태바에 특정 메세지를 표시하도록...)
 
public final void startForeground(int id, Notification notification);

public final void stopForeground(boolean removeNotification);
 
 시스템은 Service 가 포그라운드에서 작동 중이라면, 어떠한 형태든 Notification 이 남아 있음을 확신 할 수 있고 따라서, Service 와 연관된 Notification 상태를 쉽게 관리 할 수 있다.  당연히, 많은 개발자들이 2.0 뿐만 아니라 이전 플랫폼에서도 작동하는 Service 코드를 작성하고 싶어한다. 아래와 같이 가능한 경우에 한해서 새롭게 추가된 API 를 호출하는 방법으로 예전 버전과 2.0 이후 버전에서 모두 작동하는 코드를 작성 할 수 있다.

private static final Class[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class[] mStopForegroundSignature = new Class[] {
boolean.class};

private NotificationManager mNM;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];

@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
}

/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
try {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
}
return;
}

// Fall back on the old API.
setForeground(true);
mNM.notify(id, notification);
}

/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
}
return;
}

// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
setForeground(false);
}

Service lifecycle changes

 Service 들이 필요하지 않은 경우에도 불구하고, 자신을 포그라운드 프로세스로 설정하는 문제를 제외하고도,  백그라운드에서 너무 많은 수의 Service 들이 돌아감에 따라, 각각의 Service 들이 한정된 메모리 자원을 차지하게 위해 서로 경쟁하게 되면 또 다른 문제가 발생할 수 있다. 안드로이드 플랫폼과 Service 간의 상호 작용 과정에서, 특정 어플리케이션이 코드상으로는 별다른 문제 없이 작성된 경우에도, Service 가 정상 종료되지 않고 백그라운드에 남아있을 수 있다. 예를 들어 다음과 같은 상황이다.
  1. Application 이 StartService 를 호출 한다.

  2. Service 의 onCreate, onStart() 함수가 호출 되고, 특정한 작업을 수행하기 위해 백그라운드 스레드가 생성된다.

  3. 시스템이 메모리 부족으로 인해 현재 작동중인 서비스를 강제로 종료 한다.

  4. 잠시 후에, 메모리 상황이 호전되어 Service 가 재시작한다. 이 때, Service 의 onCreate 함수는 호출 되지만, onStart 는 호출 되지 않는다. (startService 는 호출 되지 않았음으로..)

 
 결국, Service 는 생성된 채로 남아있게 된다. Service 는 어떠한 일을 수행 하지도 않으며 (무슨 일을 수행해야할 지 알 수 없음으로...), 어떤 시점에 종료되어야 하는지도 알 수 없다.

 이러한 문제를 해결하기 위해 안드로이드 2.0 에서 Service 의 onStart() 는 더이상 사용되지 않는다. (이전 버전과의 호환성 문제때문에 작동 자체는 하지만...) 대신 Service.onStartCommand() 콜백이 사용된다. 이 콜백 함수는 시스템이 Service 를 좀 더 잘 관리 할 수 있도록 해주는데, 기존 API 와는 달리  onStartCommand() 는 결과값을 반환한다. 안드로이드 플랫폼은 이 결과값을 기반으로 만일 작동중인 서비스가 강제로 종료될 경우, 어떠한 일을 수행해야 하는지 판단한다.
  • START_STICKY  는 기본적으로 이전과 동일하게 작동한다. 이전과의 차이점은 다음과 같다.  기존에 프로세스가 강제 종료된 후 Service 가 재 시작 될 때, onStart() 콜백이 호출 되지 않았지만, START_STICKY 형태로 호출된 Service 는 null Intent 가 담긴 onStartCommand() 콜백 함수가 호출된다. 이 모드를 사용하는 Service 는 null Intent 로 onStartCommand() 콜백 함수가 호출되는 경우를 주의깊게 처리해야 한다. 
  • START_NOT_STICKY 모드로 시작된 Service는  안드로이드 플랫폼에 의해 프로세스가 강제로 종료되는 경우, 다시 시작되지 않고 종료된 상태로 남게된다. 이러한 방식은 특정 Intent 로 주어진 명령을 수행하는 동안에만 Service 가 실행되면 되는 경우에 적당하다 예를 들어, 매15분마다 네트워크 상태를 체크하기 위해 실행되는 Service 를 생각해 보면, 만일 이 Service 가 작업도중 강제로 종료될 경우, 이 Service 는 재시작하기 보다는 정지된 상태로 남겨두고, 15분 후에 새롭게 시작되도록 하는 것이 최선이다. 
  • START_REDELIVER_INTENT  는 START_NOT_STICKY 와 유사하다. 단, 프로세스가 강제로 종료되는 경우 (stopSelf() 가 호출 되기 전에 종료되는 경우), Intent 가 다시 전달 되어 Service 가 재시작 한다.  (단, 여러차례 시도한 후에도, 작업이 종료되지 않으면, Service 는 재시작되지 않는다.) 이 모드는 전달받은 명령을 반드시 수행해야 하는 Service 에 유용하다. 
 기존에 존재하는 어플리케이션과의 호환성을 위해서, 이전 버전을 기준으로 제작된 어플리케이션은  START_STICKY_COMPATIBILITY 모드로 작동한다.  (null intent 를 보내지 않고 기존과 동일하게 작동). 단 API 버전 5  이상을 기준으로 작성된 어플리케이션들은 기본적으로 START_STICKY 모드로 작동 하며, 개발자는 반드시 onStart() 나 onStartCommand() 가 null intent 를 가지고 호출되는 경우를 염두해 두어야 한다. 

 개발자들은 아래에 제시된 코드를 활용하여, 손쉽게 이전 버전과 새로운 버전 양쪽 모두에서 작동하는 Service 를 구현할 수 있다. 

    // This is the old onStart method that will be called on the pre-2.0
    // platform.  On 2.0 or later we override onStartCommand() so this
    // method will not be called.
    @Override
    public void onStart(Intent intent, int startId) {
        handleStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handleStart(intent, startId);
        return START_NOT_STICKY;
    }

    void handleStart(Intent intent, int startId) {
        // do work


New "running services" user interface

 * 이 후 이어지는 내용은 새롭게 추가된 Running Service 를 확인 할 수 있는 UI 어플리케이션에 관한 내용이라 대충 대충 건너 띄도록 하겠습니다.

 안드로이드 2.0 부터는 새롭게 "Running Services" Activity 가 어플리케이션 세팅에 추가 되었다. 사용자는 목록에 표시된 Service 를 터치해서 종료할 수 있다.

 화면 하단부에는 현재 시스템의 메모리 상황에 대한 정보가 표시된다. 

  • Avail: 38MB+114MB in 25 은 현재 38MB 의 공간이 바로 사용가능하며, 114MB 의 메모리 공간은 언제든지 종료시킬 수 있는 25개의 백그라운드 프로세스에 의해 점유되고 있음을 알려준다. 
  • Other: 32MB in 3 은 현재 3개의 임의로 종료되서는 않되는 포그라운드 프로세스에 의해 32MB 의 공간이 사용되고 있음을 알려준다. 
 대부분의 사용자들에게 이 새로운 UI 는 기존의 Task Killer 어플리케이션에 비해 사용하기 편리하며, 강제로 Service 를 종료시키는 것이 아니기 때문에 문제를 일으킬 소지가 훨씬 적다. 

 개발자들에게는 자신이 작성한 어플리케이션이 잘 작동하는지 (특히 Service 관리라는 측면에서) 확인 할 수 있게 해주는 유용한 툴이 될 수있다. 또한 어플리케이션이 구동한 Service 가 사용자에 의해 어느시점에서든지 종료 할 수 있다는 점을 염두해 두어야 한다. 안드로이드의 Service 는 매우 강력한 도구이지만, 어플리케이션 개발자들이 전체 폰의 성능에 큰 악영향을 끼칠 수 있는 문제를 가장 많이 일으키는 도구이기도 한다. 


 
 

'안드로이드' 카테고리의 다른 글

타이틀 바 꾸미기  (0) 2011.01.27
웹킷 브라우저  (0) 2011.01.27
Handler  (0) 2011.01.27
hardware keyboard의 존재 여부 및 상태 확인  (0) 2011.01.27
안드로이드 개발 팁  (0) 2011.01.27

Handler란?

  한 스레드는 그 내부의 연산만 가능하며 다른 스레드의 UI를 건드릴 수 없습니다.
 그런데 만약 스레드들이 서로 영향을 줄 수 없다면 스레드의 존재 이유가 없을 것입니다.

  이를 해결하기 위해서 서로 다른 스레드 간의 참조를 위해서 스레드 간에 통신할 
 수 있는 장치
를 만들었는데 그것이 핸들러[Handler]입니다.
 핸들러는 스레드 간에 메시지 객체나 러너블 객체를 통해 통신할 수 있는 장치이며,하나
 의 핸들러는 하나의 스레드와 관련을 맺습니다. 핸들러는 자신이 생성된 스레드에 짝이
 되며 다른 스레드와 통신을 수행하게 됩니다.

Handler의 메시지 수령
  핸들러에 메시지가 도착하게 되면 아래의 메서드가 호출됩니다.
 public void handleMessage(Message msg)
 인수로 메시지 객체를 전달 받는데 이는 스레드 간에 통신을 해야 할 내용에 관한 객체입
 니다. 몇가지 정보가 추가 될 수 있기 때문에 여러 개의 필드를 가지고 있습니다.

 메시지 필드 : 설명
 1. int what : Message의 ID
 2. int arg1 : Message가 보낼 수 있는 첫번째 정보.
 3. int arg2 : Message가 보낼 수 있는 두번째 정보.
 4 Object obj : Integer로 표현 불가능 할 경우 객체를 보냄.
 5. Messenger replyTo : 응답을 받을 객체를 지정.

Handler의 메시지 전송
  Message를 전송할 때는 다음의 메서드를 사용합니다.
 1. boolean Hanler.sendEmptyMessage(int what);
    - Message 의 ID에 해당하는 값을 전달할 때 사용함.
 2. boolean Handler.sendMessage(Message msg);
     - ID만으로 불가능하고 좀 더 내용이 있는 정보를 전송할 때 사용.
 3. boolean sendMessageAtFrontOfQueue(Message msg);
     - 메시지가 큐에 순서대로 쌓여서 FIFO(First In Frist Out)형태로 처리되지만,
       이 메서드를 사용하면 노래방에서 우선 예약 하듯이 사용가능합니다.

Handler의 발송과 수령 예제
//Handler 시작 부분
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        BackThread thread = new BackThread();
        thread.setDaemon(true);
        thread.start();
    }

//Handler Class
  class BackThread extends Thread {
 @Override
 public void run() {
  // TODO Auto-generated method stub
  while(true)
  {
   //처리 내용

   //핸들러 메세지 보내는 부분
   mHandler.sendEmptyMessage(0);
   try {
    Thread.sleep(1000);
   } catch (Exception e) {
    // TODO: handle exception
    ;
   }
  }
  };
  //핸들러를 받아서 처리할 내용
  Handler mHandler = new Handler() {
   public void handleMessage(android.os.Message msg)
   {
    if(msg.what == 0){
     //처리할 내용
    }
   };

 }
}


Handler의 객체 전송
  앞선 메서드로 특정 정보를 보낼 수 있지만 메시지를 보내는 대신에 객체를 보낼 수도
 있습니다.
 boolean post(Runnable r)
  핸들러로 다음의 매서드를 통해 Runnable 객체를 보내면 해당 객체의 run 메서드가
 실행됩니다. 이럴 경우 메시지를 받는 쪽은 다른 것을 정의하지 않고 핸들러만 정의하면
 해당 내용을 수행할 수 있습니다.

Handler의 객체 전송 예제
  class BackThread extends Thread {
 @Override
 public void run() {
  // TODO Auto-generated method stub
  while(true)
  {
   //처리 내용
   mHandler.post(new Runnable() {
    
    @Override
    public void run() {
     // TODO Auto-generated method stub
     // 객체를 받아서 처리할 내용
    }
   });
   try {
    Thread.sleep(1000);
   } catch (Exception e) {
    // TODO: handle exception
    ;
   }
  }
 }
 //핸들러를 받아서 처리할 내용
 Handler mHandler = new Handler();
}

클래스의 분리 시 메시지 교환
  앞선 예제처럼 스레드가 이너 클래스로 구현되었을 경우 멤버의 공유가 가능하지만 그렇
 지 않고 분리된 클래스의 경우에는 더 상세한 코드의 구현이 필요합니다.
  이경우 스레드는 전달 받은 핸들러를 자신의 멤버로 따로 저장해야 하고 (공유가 불가능
 하기 때문입니다.) 메시지 객체에 추가 정보를 저장해서 보내야 합니다.

클래스 분리시 메시지 교환 예제
  public class TestSampleProject extends Activity {
 
 int mManinValue = 0;
 int mBackValue = 0;
 TextView mMainText;
 TextView mBackTest;
 
 BackThread thread;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        thread = new BackThread(mHandler );
        thread.setDaemon(true);
        thread.start();
    }
   
    Handler mHandler = new Handler() {
     public void handleMessage(android.os.Message msg)
     {
      if(msg.what == 0)
      {
       //처리할 내용
      }
     };
    };
}

class BackThread extends Thread {
 Handler mHandler;
 
 public BackThread(Handler handler) {
  // TODO Auto-generated constructor stub
  mHandler = handler;
 }
 
 @Override
 public void run() {
  // TODO Auto-generated method stub
  Message msg = new Message();
  msg.what = 0;
  msg.arg1 = 1;
  mHandler.sendMessage(msg);
  try {
   Thread.sleep(1000);
  } catch (Exception e) {
   // TODO: handle exception
  }
 }
}

메시지 풀의 사용
  매번 핸들러를 구현할 때마다 new 연산자로 새로 생성한다면 메모리도 계속해서 사용할 것이고 그에 다른 가비지 컬렉션의 작업도 생길 것입니다. 이러면 속도도 느려지는 결과가 발생합니다.
이런 경우를 보완하고자 메시지 풀 이라는 임시 캐쉬를 유지시켜서 빠른 작업이 가능하게 합니다.
static Message obtain(message orig)
static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
 - obtain 메서드는 메시지 풀에서 비슷한 메시지가 있다면 이를 재사용하게 합니다.
void recycle()
 - 사용한 메시지를 풀에 집어 넣는 역할을 하는데 풀에 한번 집어 넣게 되면 시스템이 
   관리하므로 더 이상 관여할 수 없습니다.

메시지 풀의 사용 예제
...
class BackThread extends Thread {
 Handler mHandler;
 
 public BackThread(Handler handler) {
  // TODO Auto-generated constructor stub
  mHandler = handler;
 }
 
 @Override
 public void run() {
  // TODO Auto-generated method stub
  while(true)
  {
   //처리할 내용
   
   Message msg = Message.obtain(mHandler, 0 , 1 , 0);
   mHandler.sendMessage(msg);
   try {
    Thread.sleep(1000);
   } catch (Exception e) {
    // TODO: handle exception
   }
  }
 }
}


출처 : http://blog.naver.com/crowdark7?Redirect=Log&logNo=109201348


1. 존재 여부 확인
context.getResources().getConfiguration().keyboard
Configuration.KEYBOARD_NOKEYS : 없음
Configuration.KEYBOARD_QWERTY : qwerty keyboard 있음
Configuration.KEYBOARD_12KEY : 12key keyboard 있음

2. 사용 여부 확인 (h/w keyboard가 열려 있나?)
context.getResources().getConfiguration().hardKeyboardHidden
Configuration.HARDKEYBOARDHIDDEN_NO : 사용상태(열려 있음)
Configuration.HARDKEYBOARDHIDDEN_Y ES : 사용하지 않는 상태(닫혀 있음)

+ Recent posts