2013년 1월 16일 수요일

[Android] Top Activity Monitoring using AlarmManager

Android에서는 현재 실행중인 top activity에 대한 정보를 얻기위해선 ActivityManager를 이용하여 아래와 같이 직접 해당 내용을 읽어와야 한다.
ActivityManager am=null;
am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
String currentPackage = am.getRunningTasks(1).get(0).topActivity.getPackageName();
물론, AndroidManifest.xml파일에 아래와 같은 권한을 미리 추가해 둬야 한다.
<uses-permission android:name="android.permission.GET_TASKS" />

Android에서는 top activity의 변화를 알려주는 broadcast intent가 없기때문에, top activity의 변화를 monitoring 하기위해선 주기적으로 top activity의 변화를 polling해 봐야 한다. 주기적으로 수행 되는 일련의 작업을 위해선 AlarmManager를 이용하는 것이 가장 적절해 보인다. (자바에서 사용되던 TimerTask를 이용한 방법은 동작의 신뢰성이 보장이 되지 않는 다고 한다.)

아래는 AlarmManager를 이용해서 주기적으로 top activity의 변화를 확인하는 코드이다.

AlarmManagerBroadcastReceiver class:
public class AlarmManagerBroadcastReceiver extends BroadcastReceiver {
 private static String mPreviousPackage;
 
 private PendingIntent getPI(Context context)
 {
  Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
  PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
  return pi;
 }
 
 public void start(Context context, long interval) {
  AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
  long triggerAtMillis = System.currentTimeMillis();
  am.setRepeating(AlarmManager.RTC, triggerAtMillis, interval, getPI(context));
 }
 
 public void stop(Context context) {
  AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
  am.cancel(getPI(context));
 }
 
 @Override
 public void onReceive(Context context, Intent intent) {
  // The routine is periodically called by AlarmManager
  // Write code for a periodic work, here !!!
  ActivityManager am=null;
  am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  String currentPackage = am.getRunningTasks(1).get(0).topActivity.getPackageName();
  if(!currentPackage.equals(mPreviousPackage)) {
   System.out.println("Top Activity Changed : pakcagename = " + currentPackage);
   mPreviousPackage = currentPackage;
  }
 }
}
그리고 AndroidManifest.xml에 아래와 같이 receiver를 등록한다. 만약 등록하지 않으면 설정한 알람메시지를 수신하지 못한다.



Main routine:
Context mAppContext=null;
AlarmManagerBroadcastReceiver mTaskMonitor; 
final long INTERVAL_MILLIS = 2000; // 2 seconds
...
mAppContext = getApplicationContext();
mTaskMonitor = new AlarmManagerBroadcastReceiver();
Start periodic monitoring:
mTaskMonitor.start(mAppContext, INTERVAL_MILLIS);
Stop periodic monitoring:
mTaskMonitor.stop(mAppContext);

매 2초마다 top activity의 변화를 확인해 보는 것이 적절한지는 아직 의문이 드나, 너무 길게 설정하는 경우는 Activity의 변화를 놓치는 경우도 발생할 수 있을 것 같다. 그리고, 해당 루틴은 LCD가 ON이 되고 난 후, ACTION_USER_PRESENT(사용자가 lock 화면을 해제한 후) 조건 하에서만 실행이 되도록 해야 한다. LCD OFF 상에서는 사용자의 개입이 없는 관계로 top activity의 변화는 존재하지 않기 때문이다. 이런 관점에서 보면 잦은 polling 일지라도, CPU를 직접 깨우는 일이 발생하지는 않게 되므로 battery의 소모에는 특별한 영향은 주지 않을 것으로 보인다.