안드로이드에서 7.0버전을 출시하면서 백그라운드를 최적화하기 위해 다음의 2가지 사항을 변경했다.
- 매니페스트 파일에 등록된
CONNECTIVITY_ACTION(android.net.conn.CONNECTIVITY_CHANGE)
브로드캐스트 수신을 무시한다.(앱이 백그라운드에서 이 브로드캐스트를 더이상 받지 못한다.) 단,Context.registerReceiver()
메서드를 이용해 메인스레드에서 등록된 리시버는 앱이 실행중일 때, 똑같이CONNECTIVITY_ACTION
브로드캐스트를 받을 수 있다. - 앱은 더이상
ACTION_NEW_PICTURE
,ACTION_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_PICTURE
, ACTION_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;
}