Android에서 되풀이 작업 예약


122

앱이 포 그라운드에있는 한 전용 서버로 프레즌스를 보내는 반복적 인 작업이있는 앱을 디자인하고 있습니다.

웹 검색에서 몇 가지 다른 접근 방식을 보았고이를 수행하는 가장 좋은 방법이 무엇인지 알고 싶었습니다.

서버 호출을 예약하는 가장 좋은 방법은 무엇입니까?

내가 본 옵션은 다음과 같습니다.

  1. 타이머 .

  2. ScheduledThreadPoolExecutor .

  3. 서비스 .

  4. AlarmManager가있는 BroadcastReciever .

당신의 의견은 무엇입니까?

편집 :
이것이 필요한 이유는 모든 사용자 작업을 원격 서버로 보내는 채팅 기반 앱 때문입니다.
즉, 사용자가 메시지를 입력하고, 사용자가 메시지를 읽고, 사용자가 온라인 상태이며, 사용자가 오프라인 상태입니다.

이것은 매 간격마다 내가하고있는 일을 서버에 보내야한다는 것을 의미합니다. 다른 사람들과 대화방을 열었 기 때문에 그들은 내가하고있는 일을 알아야합니다.

whatsapp 메시지 피드백 메커니즘과 유사합니다. 메시지가 전달 된 것 같습니다.

수정 # 2 : Android 교육 의 vitals 섹션 에서 읽을 수있는 것처럼 배터리 소모 문제를 방지하기 위해
거의 항상 JobSchedulerAPI (또는 FirebaseJobDispatcher하위 API)를 통해 반복 작업을 예약해야합니다.

수정 # 3 :
FirebaseJobDispatcher는 더 이상 사용되지 않으며 JobScheduler의 기능도 통합 하는 Workmanager 로 대체되었습니다 .


2
AlarmManager가있는 BroaccastReceiver는 사용하기 매우 간단합니다. 내가 시도한 위의 대안 중 유일한 것입니다.

1
ScheduledThreadPoolExecutor를 통해 Timer를 사용할 이유는 거의 없습니다. 이것은 둘 이상의 백그라운드 스레드를 허용하고 더 나은 해상도 (ms 해상도에만 유용함)를 가지며 예외 처리를 허용하므로 더 유연합니다. AlarmManager와 관련 하여이 게시물 은 차이점에 대한 정보를 제공합니다.
assylias

짧은 실행 수명주기의 경우, 즉 현재 포 그라운드에있는 활동에서 30 초마다 작업을 수행하려면 ScheduledThreadPoolExecutor (또는 Timer)를 사용하는 것이 더 효율적입니다. 장기간 실행되는 수명주기 (예 : 백그라운드 서비스에서 1 시간마다 일부 작업 수행)의 경우 AlarmManager를 사용하면 안정성이 향상됩니다.
yorkw

전송을 예약해야하는 이유는 무엇입니까? 앱 설명에서 실시간으로 보내지 않으시겠습니까?
iTech

사용자가 시간 제한을 사용하여 온라인 상태라고 가정하기 때문입니다. 즉, 지난 X 시간 동안 "존재"또는 "입력"메시지를받지 못했다면 자동으로 수행하지 않는 것으로 간주합니다
thepoosh 2013

답변:


164

확실하지는 않지만 내 지식에 따라 내 의견을 공유합니다. 내가 틀렸다면 항상 최선의 대답을 받아들입니다.

알람 관리자

알람 관리자는 알람 수신기의 onReceive()방법이 실행 되는 동안 CPU wake lock을 유지합니다 . 이렇게하면 방송 처리를 마칠 때까지 전화기가 잠들지 않습니다. 일단 onReceive()반환, 알람 Manager는이 웨이크 잠금을 해제합니다. 이것은 경우에 따라 onReceive()방법이 완료 되는 즉시 전화기가 절전 모드로 전환됨을 의미합니다 . 알람 수신기가를 호출 Context.startService()하면 요청한 서비스가 시작되기 전에 전화기가 절전 모드로 전환 될 수 있습니다. 이를 방지하려면 BroadcastReceiverService서비스를 사용할 수있을 때까지 전화가 계속 실행되도록하기 위해 별도의 웨이크 잠금 정책을 구현해야합니다.

참고 : 알람 관리자는 애플리케이션이 현재 실행되고 있지 않더라도 특정 시간에 애플리케이션 코드를 실행하려는 경우를위한 것입니다. 정상적인 타이밍 작업 (틱, 타임 아웃 등)의 경우 Handler를 사용하는 것이 더 쉽고 효율적입니다.

시간제 노동자

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

        }}, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));

Timer에 의해 해결되는 몇 가지 단점이 있습니다 ScheduledThreadPoolExecutor. 그래서 최선의 선택이 아닙니다

ScheduledThreadPoolExecutor .

java.util.Timer또는 ScheduledThreadPoolExecutor(선호)를 사용 하여 백그라운드 스레드에서 정기적으로 발생하는 작업을 예약 할 수 있습니다.

후자를 사용한 샘플은 다음과 같습니다.

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

scheduler.scheduleAtFixedRate
      (new Runnable() {
         public void run() {
            // call service
         }
      }, 0, 10, TimeUnit.MINUTES);

그래서 나는 선호했다 ScheduledExecutorService

그러나 응용 프로그램이 실행되는 동안 업데이트가 발생하면 Timer다른 답변에서 제안한 것처럼를 사용할 수 있거나 최신 ScheduledThreadPoolExecutor. 응용 프로그램이 실행 중이 아닌데도 업데이트되는 경우 AlarmManager.

알람 관리자는 애플리케이션이 현재 실행되고 있지 않더라도 특정 시간에 애플리케이션 코드를 실행하려는 경우를위한 것입니다.

응용 프로그램이 꺼져있을 때 업데이트 할 계획이라면 10 분마다 한 번 씩은 매우 자주 발생하므로 전력을 너무 많이 소비 할 수 있습니다.


주기적인 작업을 위해이 방법을 시도하고 있지만 작동하지 않는 것 같습니다. stackoverflow.com/questions/27872016/…
dowjones123

n 초마다 상태를 확인하는 것과 같은 간단한 작업은 타이머가 수행합니다.
IgorGanapolsky 2015-06-16

1
@ Maid786 한 주 간격이나 일 단위로 작업 (예 : 알림 보내기)을 수행하려면 무엇을 사용해야합니까? 알람 관리자가이를 위해 너무 많은 백그라운드 계산 또는 처리를 수행합니까?
Chintan Shah

30

시간제 노동자

javadocs 에서 언급했듯이 ScheduledThreadPoolExecutor를 사용하는 것이 좋습니다.

ScheduledThreadPoolExecutor

사용 사례에 여러 작업자 스레드가 필요하고 휴면 간격이 작은 경우이 클래스를 사용합니다. 얼마나 작습니까? 글쎄요, 15 분 정도입니다. AlarmManager시작이 시간 간격을 예약하고 작은 수면 간격이 클래스를 사용할 수 있음을 시사하는 것으로 보인다. 마지막 진술을 뒷받침 할 데이터가 없습니다. 직감입니다.

서비스

서비스는 VM에 의해 언제든지 종료 될 수 있습니다. 반복 작업에 서비스를 사용하지 마십시오. 반복되는 작업은 서비스를 시작할 수 있으며 이는 완전히 다른 문제입니다.

AlarmManager가있는 BroadcastReciever

더 긴 수면 간격 (> 15 분)의 경우 이것이 갈 길입니다. AlarmManager이미 일정 AlarmManager.INTERVAL_DAY이 잡힌 후 며칠 후에 작업을 트리거 할 수 있음을 나타내는 상수 ( )가 있습니다. 또한 코드를 실행하기 위해 CPU를 깨울 수도 있습니다.

타이밍 및 작업자 스레드 요구 사항에 따라 이러한 솔루션 중 하나를 사용해야합니다.


1
그래서 만약 제가 앱을 사용하고 싶고 30 분마다 백업을하고 싶을 것입니다. 하지만 앱을 사용하지 않는 동안 백업하고 싶지 않습니다 (총 낭비가 될 것입니다). Alarmmanager는 재부팅 할 때까지 작업을 계속 반복합니다 (적어도 제가 들었던 것입니다). 어떤 것을 권 하시죠? ScheduledThreadPoolExecutor 또는 Alarmmanager?
hasdrubal

13

나는 이것이 오래된 질문이며 답변을 받았지만 누군가에게 도움이 될 수 있음을 알고 있습니다. 당신의activity

private ScheduledExecutorService scheduleTaskExecutor;

onCreate

  scheduleTaskExecutor = Executors.newScheduledThreadPool(5);

    //Schedule a task to run every 5 seconds (or however long you want)
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // Do stuff here!

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Do stuff to update UI here!
                    Toast.makeText(MainActivity.this, "Its been 5 seconds", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }, 0, 5, TimeUnit.SECONDS); // or .MINUTES, .HOURS etc.

2

인용 일정 반복 알람 - 무역 오프의 이해 문서를 :

앱 수명 이외의 작업을 트리거하는 일반적인 시나리오는 데이터를 서버와 동기화하는 것입니다. 반복 알람을 사용하고 싶을 수있는 경우입니다. 그러나 앱의 데이터를 호스팅하는 서버를 소유하고있는 경우 동기화 어댑터와 함께 Google 클라우드 메시징 (GCM)을 사용하는 것이 AlarmManager보다 나은 솔루션입니다. 동기화 어댑터는 AlarmManager와 동일한 예약 옵션을 모두 제공하지만 훨씬 더 많은 유연성을 제공합니다.

따라서이를 기반으로 서버 호출을 예약하는 가장 좋은 방법은 동기화 어댑터 와 함께 Google 클라우드 메시징 (GCM)사용하는 것 입니다.


1

사용자가 반복하고 싶은 작업을 시간 작업에 생성하고 Custom TimeTask run () 메서드를 추가합니다. 성공적으로 되풀이됩니다.

 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Timer;
 import java.util.TimerTask;

 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.app.Activity;
 import android.content.Intent;

 public class MainActivity extends Activity {

     CheckBox optSingleShot;
     Button btnStart, btnCancel;
     TextView textCounter;

     Timer timer;
     MyTimerTask myTimerTask;

     int tobeShown = 0  ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    optSingleShot = (CheckBox)findViewById(R.id.singleshot);
    btnStart = (Button)findViewById(R.id.start);
    btnCancel = (Button)findViewById(R.id.cancel);
    textCounter = (TextView)findViewById(R.id.counter);
    tobeShown = 1;

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }

    btnStart.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View arg0) {


            Intent i = new Intent(MainActivity.this, ActivityB.class);
            startActivity(i);

            /*if(timer != null){
                timer.cancel();
            }

            //re-schedule timer here
            //otherwise, IllegalStateException of
            //"TimerTask is scheduled already" 
            //will be thrown
            timer = new Timer();
            myTimerTask = new MyTimerTask();

            if(optSingleShot.isChecked()){
                //singleshot delay 1000 ms
                timer.schedule(myTimerTask, 1000);
            }else{
                //delay 1000ms, repeat in 5000ms
                timer.schedule(myTimerTask, 1000, 1000);
            }*/
        }});

    btnCancel.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            if (timer!=null){
                timer.cancel();
                timer = null;
            }
        }
    });

}

@Override
protected void onResume() {
    super.onResume();

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }
}


@Override
protected void onPause() {
    super.onPause();

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

@Override
protected void onStop() {
    super.onStop();

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

class MyTimerTask extends TimerTask {

    @Override
    public void run() {

        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = 
                new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
        final String strDate = simpleDateFormat.format(calendar.getTime());

        runOnUiThread(new Runnable(){

            @Override
            public void run() {
                textCounter.setText(strDate);
            }});
    }
}

}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.