2016년 9월 28일 수요일

[안드로이드] 7.0(누가) 백그라운드 최적화

안드로이드에서 7.0버전을 출시하면서 백그라운드를 최적화하기 위해 다음의 2가지 사항을 변경했다.
  • 매니페스트 파일에 등록된 CONNECTIVITY_ACTION(android.net.conn.CONNECTIVITY_CHANGE) 브로드캐스트 수신을 무시한다.(앱이 백그라운드에서 이 브로드캐스트를 더이상 받지 못한다.) 단, Context.registerReceiver()메서드를 이용해 메인스레드에서 등록된 리시버는 앱이 실행중일 때, 똑같이 CONNECTIVITY_ACTION 브로드캐스트를 받을 수 있다.
  • 앱은 더이상 ACTION_NEW_PICTUREACTION_NEW_VIDEO 브로드캐스트를 보내거나 받을 수 없다. 이는 targetSdkVersion에 상관없이 모든 앱에 영향을 끼친다.
안드로이드 7.0을 대응해야 한다면, 반드시 위의 두가지 사항을 확인해서 대응해 주어야 한다. 애응하지 않으면 어떤 부작용이 있을지 알 수 없다.

CONNECTIVITY_ACTION 제한

targetSdkVersion을 24로 설정한 앱의 경우, 매니페스트에 설정한 CONNECTIVITY_ACTION 브로드캐스트를 더 이상 받지 못하게 된다. 이 상황은 백그라운드에서 CONNECTIVITY_ACTION 처리를 하고있는 앱이라면, 반드시 확인해봐야 한다.
Note : 위에서도 언급했지만, 앱이 실행중일 때에는 Context의 registerReceiver() 메서드를 이용해 CONNECTIVITY_ACTION 수신 리시버를 등록하고, 계속 브로드캐스트 받을 수 있다.

네트워크 Job 스케쥴링

안드로이드 5.1 이상

안드로이드 5.1(API 21) 버전에서 새로 추가된 JobScheduler 관련 API를 이용해 처리할 수 있다. JobInfo.builder 로 jobInfo인스턴스를 생성할 때, setRequiredNetworkType() 메서드를 이용해 네트워크 타입을 지정하고 생성한 후에 그 인스턴스를 스케쥴에 등록한다. 그러면, 스케쥴러가 해당 네트워크 타입이 되었을 때, Job을 수행한다. 다음 예제코드는 네트워크 타입이 WIFI 일때의 JobInfo를 등록하는 내용이다.
public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
JobScheduler js =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(
MY_BACKGROUND_JOB,
new ComponentName(context, MyJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
js.schedule(job);
}
위의 예제에서 네트워크 타입이 UNMETERED(무과금 -> WIFI) 가 되면, JobInfo를 만들때 등록했던 JobService 클래스의 onStartJob()메서드로 콜백된다. (JobService 클래스 참고)

안드로이드 5.1 미만

안드로이드 5.1(API 21)버전 미만에서는 JobScheduler 클래스를 사용할 수 없으므로, 구글플레이 서비스를 이용해 구현된 GcmNetworkManager 클래스를 이용해야 한다. GcmNetworkManager는 Task 추상클래스의 하위 클래스 객체를 스케쥴링하게된다. Task 클래스 하위 인스턴스는 다음과 같이 스케쥴링한다.
     OneoffTask myTask = new OneoffTask.Builder()
.setService(MyGcmTaskService.class)
.setRequiredNetwork(NETWORK_STATE_UNMETERED)
.setTag("test-upload")
.build();
GcmNetworkManager.getInstance(this).schedule(myTask);
해당 태스크의 순서가 되면, GcmTaskService 클래스의 onRunTask() 메서드로 콜백된다. (GcmTaskService 클래스 참고) 단, GcmTaskService 클래스가 콜백을 받으려면 다음과 같이 매니페스트에 등록되어야 한다.
     <service android:name=".MyUploadService"
android:exported="true"
<!-- 다른 코드로부터 이 서비스가 실행되지 않도록 하기 위해서 추가 -->
android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
<!-- 이 서비스가 콜백을 받기 위해 추가 -->
<intent-filter>
<action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
</intent-filter>
</service>

앱이 실행중일 때, Network Connectivity 모니터링하기

앱 실행중에 브로드캐스트 리시버로 CONNECTIVITY_ACTION을 수신하는 방법 외에도, ConnectivityManager 클래스의 registerNetworkCallback()메서드를 이용해, 해당 상태에서의 콜백을 받을 수 있다. (이 메서드는 API 21에서 추가되었다.)
NetworkRequest.Builder 를 이용하여 NetworkRequest 인스턴스를 생성한 후에, 그 인스턴스를 registerNetworkCallback()메서드의 첫번째 인자로 넘기고, 두번째 인자로는 첫번째 인자에 명시된 상태로 네트워크가 변할 때, 실행될 콜백 메서드를 넘긴다. 앱이 종료될 때, unregisterNetworkCallback()메서드를 호출해 주어야 한다.

NEW_PICTURE / NEW_VIDEO 제한

안드로이드 7.0에서는 ACTION_NEW_PICTUREACTION_NEW_VIDEO 브로드캐스트를 주고받을 수 없도록 변경되었다. 이러한 현상은, 어떤 애플리케이션이 새로운 이미지나 비디오를 프로세싱할 때, 이 액션을 등록한 모든 리시버에게 브로드캐스트 되어 많은 앱들이 깨어나야만 했다.

새로운 JobInfo 메서드

안드로이드 7.0에서는 Content URI의 변화를 연결하기 위해 JobInfo API를 확장하여 다음의 메서드들을 추가하였다.
  • JobInfo.TriggerContentUri() : content URI의 변화를 감지하기 위해 요구되는 파라미터들을 캡슐화한다.
  • JobInfo.Builder.addTriggerContentUri() : TriggerContentUri 객체를 JobInfo로 전달한다. ContentObserver는 캡슐화된 content URI를 모니터링한다. 만약 Job과 관련된 TriggerContentUri가 여러개 있으면, 시스템은 한개의 URI가 변하더라도 콜백을 전달한다. 어떤 URI의 하위 컨텐트의 변화를 감지하고 싶으면, TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS 플래그를 추가하면 된다. 이 플래그는, ContentResolver.registerContentObserver()메서드의 매개변수인 notifyForDescendants와 대응하는 역할을 한다.
Note : TriggerContentUri() 메서드는 setPeriodic()setPersisted() 메서드와 같이 사용할 수 없다. 컨텐트의 변경을 계속 감지하려면, JobService가 콜백을 끝내기 전에 새로운 JobInfo 인스턴스를 만들어 스케쥴링한다.
다음 예제는 MEDIA_URI가 변경될떄를 감지하기 위해 스케쥴링하는 코드이다.
public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
JobScheduler js =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(
MY_BACKGROUND_JOB,
new ComponentName(context, MediaContentJob.class));
builder.addTriggerContentUri(
new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
js.schedule(builder.build());
}

새로운 JobParameter 메서드

안드로이드 7.0에서는 JobParameter를 확장하여, Job 콜백이 왔을 때 해당 컨텐트의 권한이나 URI같은 유용한 정보를 볼 수 있도록 확장되었다.
  • Uri[] getTriggeredContentUris() : 이 Job에서 연결된 URI를 배열로 반환한다. 만약 연결된 URI가 없다면 null을 리턴한다.
  • String[] getTriggeredContentAuthorities() : 이 Job에서 연결된 URI들의 권한ㅇ르 배열로 반환한다. 반환된 결과가 null이 아니라면, getTriggeredContentUris() 메서드를 사용하면 된다.
다음 예제는 변경된 URI의 권한과 URI를 JobService.onStartJob() 메서드에서 받아서 기록하는 코드이다.
@Override
public boolean onStartJob(JobParameters params) {
StringBuilder sb = new StringBuilder();
sb.append("Media content has changed:\n");
if (params.getTriggeredContentAuthorities() != null) {
sb.append("Authorities: ");
boolean first = true;
for (String auth :
params.getTriggeredContentAuthorities()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(auth);
}
if (params.getTriggeredContentUris() != null) {
for (Uri uri : params.getTriggeredContentUris()) {
sb.append("\n");
sb.append(uri);
}
}
} else {
sb.append("(No content)");
}
Log.i(TAG, sb.toString());
return true;
}

댓글 없음:

댓글 쓰기