레이블이 서비스인 게시물을 표시합니다. 모든 게시물 표시
레이블이 서비스인 게시물을 표시합니다. 모든 게시물 표시

2010년 8월 4일 수요일

안드로이드 서비스 디버깅 하기 (Attach debugger for service in Android)

서비스만 있는 어플리케이션을 디버깅 할 때 서비스에는 브레이크 포인트를 잡아 놔도 실제로 그 부분이 실행될 때 브레이크가 걸리지 않는다.




액티비티를 debug run 하면 어플리케이션이 시작될 때 화면에 Waiting for Debugger라는 메시지가 잠시 나타났다 사라지고 이클립스에 Debug perspective가 열리게 된다.


액티비티의 브레이크 포인트에서 실행이 멈추고 개발자의 다음 동작을 기다리게 된다.


하지만 서비스만 있는 어플리케이션의 경우 debug run을 해도 위와 같은 화면이 나오지도 않고 서비스에 잡아 놓은 브레이크 포인트는 멈추지 않고 그냥 지나쳐 버린다. 해결책은 명식적으로 서비스를 디버거에 붙이도록 선언해 주는 것이다. android.os.Debug.waitForDebugger(); 를 호출해 주면 된다.

SoftKeyboard 튜토리얼에 디버거를 사용하려면 아래와 같이 수정해주면 된다.

public class SoftKeyboard extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    Log.d("SoftKeyboard", "onConfigurationChanged()");

    android.os.Debug.waitForDebugger();

    super.onConfigurationChanged(newConfig);

    // do something useful....

  }

onConfigurationChanged의 두번째 줄에서 서비스를 명시적으로 디버거에 연결시켜주기 때문에 브레이크 포인트에서 실행이 멈추게 된다.

* 어플리케이션에 액티비티와 서비스가 같이 들어있는 경우에는 액티비티가 먼저 실행되면서 디버거에 연결하기 때문에 서비스에 따로 waitForDebugger()를 호출하지 않아도 서비스에도 브레이크 포인트를 바로 사용할 수 있다.

2010년 8월 3일 화요일

안드로이드 서비스를 부팅시 시작시키기 (Start Service at Boot Time)

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



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

public class GPSLoggerServiceManager extends 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 파일에 선언되어 있어야 한다.

<receiver android:name=".LocationLoggerServiceManager"
   android:enabled="true"
   android:exported="false"
   android:label="LocationLoggerServiceManager" >
  <intent-filter>
   <action android:name="android.intent.action.BOOT_COMPLETED" />
  </intent-filter>
</receiver>

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

<uses-permission android:name="android.permission.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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="app.nautes"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
      <service android:name="BootSvc"></service>
      <receiver android:name=".BRcvr"
         android:enabled="true"
         android:exported="false"
         android:label="Broadcast Receiver" >
        <intent-filter>
          <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
      </receiver>
    </application>
</manifest>

이 프로그램을 에뮬레이터에 설치한 다음 에뮬레이터를 종료했다가 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


로그를 보면 부팅이 끝난 다음 서비스가 시작된걸 확인할 수 있다.