본 포스팅은 DroidKaigi 2017 ~ Android定期実行処理入門 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
kazy(kazuki yoshida)
유저의 조작과 직접 관계없이 일정 간격으로 작업를 수행한다
“요리 기록”
BroadcastReceiver로 NEW_PICTURE 이벤트를 받으면 되지 않는가?
정기 실행 처리를 하고 싶은 경우, 모두 어떻게 하고 있습니까?
public class AlarmReceiver extends BroadcastReceiver {
private static Intent createIntent(Context context) {
return new Intent(context, AlarmReceiver.class);
}
public static PendingIntent createPendingIntent(Context context) {
return PendingIntent.getBroadcast(context, 0, createIntent(context), 0);
}
@Override
public void onReceive(Context context, Intent intent) {
// 여기에서 Service를 실행한다 등등..
}
}
# AndroidManifest.xml
<receiver
android:name=".AlarmReceiver"
android:exported="false" />
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//..
PendingIntent intent = AlarmReceiver.createPendingIntent(this);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, fiveSecondsLater(), intent);
}
}
고성능을 의식한 모던 작업 관리 구조
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters job) {
// 여기에 작업을 적는다
return false;
}
@Override
public boolean onStopJob(JobParameters job) {
return false;
}
}
<service
android:name=".MyJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 여기는 메인 스레드
tooHeavyJob()
.subscribeOn(Schedulers.io())
.subscribe(result -> {
// 뭔가 갱신 처리 등을 한다
jobFinished(params, false);
// 작업이 실패한 경우는 true를 전달하면 다시 Schedule
});
// 동기 처리의 경우는 false/비동기 처리의 경우는 true를 전달한다
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
// 작업이 완료하기 전에 시스템이 취소하면 호출된다
return false;
}
}
public class MyJobService extends JobService {
private static final int PERIODIC_JOB_ID = 1;
public static void setPeriodicSchedule(Context context) {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(PERIODIC_JOB_ID,
new ComponentName(context, MyJobService.class))
.setPeriodic(TimeUnit.MINUTES.toMillis(30))
.setPersisted(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
scheduler.schedule(jobInfo);
}
public static void cancelPeriodicSchedule(Context context) {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.cancel(PERIODIC_JOB_ID);
}
}
PersistableBundle bundle = new PersistableBundle();
JobInfo jobInfo = new JobInfo.Builder(JOB_TAG,
new ComponentName(this, MyJobService.class))
.setExtras(bundle)
.build();
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
PersistableBundle bundle = params.getExtras();
// PersistableBundle는 Schedule시에 영속하되므로
// 여기에서 다시 적어도 다음의 job에는 반영되지 않는다
return false;
}
}
public static boolean isPending(Context context, int serviceId) {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
List<JobInfo> pendingJobList = scheduler.getAllPendingJobs();
for (JobInfo jobInfo : pendingJobList) {
if (serviceId == jobInfo.getId()) {
return true;
}
}
return false;
}
adb shell dumpsys jobscheduler
Settings:
<단말 설정>
Started users: [0]
Registered 48 jobs:
JOB #1000/800 : com.android.server.pm.BackgroundDexOptService
<JobInfo에 전달한 Schedule 조건>
JOB #1000/20536: com.android.server.backup.FullBackupJob
<JobInfo에 전달한 Schedule 조건>
[중반은 생략]
Job history:
-5m54s389ms STOP: u0a69 com.example.BackgroundService
-5m51s543ms STOP: u0a83 com.example.AuthService
-21s184ms START: u0a15 com.example.ServiceName
-20s940ms STOP: u0a15 com.example.ServiceName
JobSchduler 개발에서 주의해야 할 함정 이야기
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) {
// 예약되어 있는지 확인한다.
}
}
<receiver android:name=".receiver.PackageReplacedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
https://stackoverflow.com/questions/33235754/jobscheduler-posting-jobs-twice-not-expected
괜찮은 구조를 원하지만, 5.0미만에도 대응하고 싶다
https://android-developers.googleblog.com/2015/05/a-closer-look-at-google-play-services-75.html
public class MyTaskService extends GcmTaskService {
@Override
public void onInitializeTasks() {
// 앱이 삭제되거나 업데이트된 경우 Schedule이 삭제되므로
// 여기에 다시 Schedule하는 코드를 작성 (처음에는 불리지 않는다)
}
@Override
public int onRunTask(TaskParams taskParams) {
// 여기에 처리를 작성한다 (Worker 스레드)
return GcmNetworkManager.RESULT_SUCCESS;
}
}
<service
android:name=".MyTaskService"
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>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// do something..
GcmNetworkManager gcmNetworkManager = GcmNetworkManager.getInstance(this);
OneoffTask task = new OneoffTask.Builder()
.setService(MyTaskService.class)
.setTag(TASK_TAG)
.setExecutionWindow(0L, 3600L)
.setPersisted(true)
.build();
gcmNetworkManager.schedule(task);
}
}
GCM Network Manager 대체 라이브러리
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters job) {
Log.d("MyJobService", "onStartJob");
return false;
}
@Override
public boolean onStopJob(JobParameters job) {
return false;
}
}
<service
android:name=".MyJobService"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
</intent-filter>
</service>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job myJob = dispatcher.newJobBuilder()
.addConstraint(Constraint.ON_ANY_NETWORK)
.setService(MyJobService.class)
.setTag("my-unique-tag")
.setLifetime(Lifetime.FOREVER)
.build();
dispatcher.mustSchedule(myJob);
}
}
JobDispatcher와 NetworkManager 어느 쪽을 사용해야 할 것인가
어플 -> Job의 시작 조건을 Intent에 채워서 보낸다 -> play-sercies
어플 <- intent-filter를 통해 호출 (com.google.android.gms.gcm.ACTION_TASK_READY ) <- play-sercies
어플 -> Job의 시작 조건을 Driver에 전달한다 -> Driver
어플 <- intent-filter를 통해 호출 (com.firebase.jobdispatcher.ACTION_EXECUTE) ) <- play-sercies
어플 -> GooglePlayDriver -> play-sercies
어플 <- GooglePlayDriver <- play-sercies
GooglePlayDriver -> Job의 시작 조건을 Intent에 채워서 보낸다 -> play-sercies
GooglePlayDriver <- intent-filter를 통해 호출 (com.google.android.gms.gcm.ACTION_TASK_READY ) <- play-sercies
https://github.com/firebase/firebase-jobdispatcher-android/issues/32
https://developer.android.com/topic/performance/background-optimization.html
https://blog.evernote.com/tech/2015/10/26/unified-job-library-android/
comments powered by Disqus
Subscribe to this blog via RSS.