서비스 고급


이 섹션 소개

이전 섹션에서는 서비스의 수명주기와 서비스를 시작하는 두 가지 방법에 대해 배웠습니다. 이 섹션에서는 서비스의 IntentService와 서비스 사용 예에 ​​대해 계속해서 심층적으로 이해합니다. 프론트 엔드 서비스 구현 및 폴링


1. IntentService 사용

이전 섹션에서 서비스를 정의하고 시작하는 방법을 이미 알고 있지만 직접 시간이 많이 걸리는 스레드를 서비스의 onStart() 메소드에 넣으십시오. 이렇게 할 수도 있지만 매우 쉽습니다. ANR 예외(애플리케이션이 응답하지 않음)가 발생하며 Android 공식 도입 서비스에는 다음과 같은 구절이 있습니다. 직접 번역:

1. 서비스는 별도의 프로세스가 아니며 애플리케이션과 동일한 프로세스에 있습니다.
2 서비스는 스레드가 아니므로 시간이 많이 걸리는 작업을 피해야 합니다. Operations in Service

그래서 Android는 위의 문제를 해결하기 위한 대안을 제공합니다. 바로 아래에서 설명할 IntentService입니다. IntentService는 Service를 상속하고 비동기 요청을 처리하는 클래스입니다. 작업자 스레드는 시간이 많이 걸리는 작업을 처리하며 요청된 인텐트 레코드가 대기열에 추가됩니다

워크플로:

클라이언트는 startService(Intent)를 통해 IntentService를 시작합니다. IntentService를 수동으로 제어할 필요는 없습니다. 작업이 완료되면 IntentService가 자동으로 중지됩니다. IntentService는 여러 번 시작할 수 있으며 시간이 많이 걸리는 각 작업은 IntentService에서 작업 대기열 형식으로 처리됩니다. onHandleIntent 콜백 메소드에서 실행되며 한 번에 하나의 작업자 스레드만 실행됩니다. 하나를 실행한 다음에는 두 개를 실행합니다.

그러면 인터넷에 있는 대부분의 코드가 Service와 IntentService와 비교됩니다. 충분히 긴 수면 시간을 정의하고 서비스의 ANR 예외를 시연한 다음 IntentService가 얼마나 좋은지 알아보세요! 여기서는 서비스를 보여주지 않겠습니다. 인터넷에 있는 서비스는 모두 맞춤화된 서비스이고 onStart() 메서드에 있습니다. Thread.sleep(20000)은 ANR 예외를 트리거합니다. 관심이 있는 경우 직접 코드를 작성해 볼 수 있습니다. 여기서는 IntentService의 사용법만 보여드리겠습니다!

TestService3.java

public class TestService3 extends IntentService {
private final String TAG = "hehe";
//부모 클래스의 생성자를 구현해야 합니다.
public TestService3()
{
super("Test Service3")
}

//필수 재정의된 핵심 메소드
@Override
protected void onHandleIntent(Intentintent) {
    //인텐트는 Activity에서 전송되고 식별 매개변수를 전달하며 다양한 매개변수에 따라 다양한 작업을 수행합니다.
  String action =intent.getExtras().getString ("Param"); ​​​​
If (Action.equals ("S1") log.i (태그, "서비스1 시작"); 서비스2 시작");
else if(action.equals("s3")) Log.i(TAG,"서비스 시작3");
                                                                          >
공개 ibinder Onbind G.I(TAG,"onBind");
                                                                           > 만들기();                                           

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.i(TAG,"onStartCommand");  
        return super.onStartCommand(intent, 플래그, startId);  
    }  
  
  
    @Override  
    public void setIntentRedelivery(boolean enabled) {  
        super.setIntentRedelivery(활성화);  
        Log.i(TAG,"setIntentRedelivery");  
    }  
      
    @Override  
    public void onDestroy() {  
        Log.i(TAG,"onDestroy");  
        super.onDestroy();  
    }  
      
}

AndroidManifest.xml注册下Service

<service android:name=".TestService3" android:exported="false">  
    <인텐트 필터 >  
        <action android:name="com.test.intentservice"/>  
    </의도-필터>  
</service>

재MainActivity启动三次服务:

public 클래스 MainActivity 확장 활동 {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
         
        인텐트 it1 = new Intent("com.test.intentservice");  
        번들 b1 = 새 번들();  
        b1.putString("param", "s1");  
        it1.putExtras(b1);  
         
        인텐트 it2 = new Intent("com.test.intentservice");  
        번들 b2 = 새 번들();  
        b2.putString("param", "s2");  
        it2.putExtras(b2);  
         
        인텐트 it3 = new Intent("com.test.intentservice");  
        번들 b3 = 새 번들();  
        b3.putString("param", "s3");  
        it3.putExtras(b3);  ㅋㅋㅋ 예를 들어  
        startService(it1);  
        startService(it2);  
        startService(it3);  
    }  
}

실행 중인 스크린샷:

1.jpg

요약:

백그라운드 작업을 여러 하위 작업으로 나눈 다음 순서대로 실행해야 하는 경우 하위 작업은 (간단히 말하면 비동기 작업입니다.) 이때 여전히 일반 서비스를 정의한 다음 onStart 메소드에서 스레드를 생성한 다음 스레드를 제어하는 ​​것은 매우 번거롭습니다. 이때 IntentService를 커스터마이징하고 onHandleIntent() 메소드에서 관련 작업을 완료해야 합니다!


2.Activity는 Service와 통신합니다

저희의 이전 작업은 Activity를 통해 서비스를 시작하고 중지하는 것이었습니다. 백그라운드 서비스. 서비스의 다운로드 작업 진행 상황을 알고 싶습니다! 그렇다면 확실히 서비스가 필요합니다 Activity와 통신하며 이들 간의 통신 매체는 Service의 onBind() 메소드입니다! 사용자 정의 바인더 객체를 반환하세요!

기본 과정은 다음과 같습니다.

  • 1. 맞춤 서비스에서 Binder 클래스를 사용자 정의한 다음 이 클래스에 노출해야 하는 모든 메서드를 작성합니다!
  • 2. Service 클래스에서 사용자 정의 Binder 클래스를 인스턴스화한 다음 onBind() 메서드를 재정의하여 Binder 개체를 반환합니다!
  • 3. Activity 클래스에서 ServiceConnection 객체를 인스턴스화하고 onServiceConnected() 메서드를 재정의한 다음 Binder 개체를 가져온 다음 관련 메서드를 호출하세요!

3.간단한 프론트 엔드 서비스 구현

이제 우리는 서비스가 일반적으로 나중에 실행된다는 것을 모두 알고 있지만 서비스의 시스템 우선 순위는 다음과 같습니다. 여전히 상대적으로 부족합니다. 시스템 메모리가 부족한 경우 백그라운드에서 실행 중인 서비스를 재활용할 수 있습니다. 이러한 상황에서는 포그라운드 서비스를 사용하여 시스템에 의해 서비스가 종료될 가능성을 약간 줄일 수 있습니다. 물론, 여전히 죽을 가능성은 있습니다... 소위 포그라운드 서비스는 상태 표시줄에 표시되는 알림입니다!

최근 진행했던 프로젝트에서 우연히 이 프론트엔드 서비스를 사용하게 되어서 구현하기도 매우 간단합니다. 공유:

사용자 정의 서비스 클래스에서 onCreate()를 다시 작성한 다음 필요에 따라 알림을 사용자 정의하세요. 사용자 정의 후 startForeground(1, 알림 개체)를 호출하면 됩니다! 핵심 코드는 다음과 같습니다.

public void onCreate()
{
    super.onCreate();
    Notification.Builder localBuilder = new Notification.Builder(this);
    localBuilder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity. 클래스), 0));
    localBuilder.setAutoCancel(false);
    localBuilder.setSmallIcon(R.mipmap.ic_cow_icon);
    localBuilder.setTicker("Foreground Service Start");
    localBuilder.setContentTitle("Socket服务端") ;
    localBuilder.setContentText("정재运行...");
    startForeground(1, localBuilder.getNotification());
}

실행 효과 스크린샷:

2.png


4. 간단한 예약된 백그라운드 스레드 구현

위에서 언급한 프런트엔드 서비스 외에도 실제 개발에서 서비스의 일반적인 사용법이 있습니다. 예약된 작업을 실행하는 것입니다. 예를 들어 폴링이란 클라이언트 상태나 업데이트 정보를 확인하기 위해 가끔씩 서버에 요청하는 것을 의미합니다. 기다리다! Android에는 Timer 클래스와 Alarm 메커니즘을 사용하여 두 가지 타이밍 방법이 제공됩니다.

전자는 오랫동안 백그라운드에서 실행되어야 하는 예약된 작업에 적합하지 않습니다. CPU가 절전 모드로 전환되면 타이머의 예약된 작업이 실행됩니다. 알람은 실행될 수 없습니다. 또한 CPU를 깨우는 기능도 있어야 합니다. 화면 깨우기로 일어나세요!

사용 프로세스:

  • 1단계: 서비스 가져오기:AlarmManager Manager = (AlarmManager) getSystemService(ALARM_SERVICE);
  • 2단계: 설정된 메서드를 통해 예약된 작업 설정int anHour = 2 * 1000 ; 긴 TriggerAtTime = SystemClock.elapsedRealtime() + anHour; Manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,pendingIntent);
  • 3단계: 서비스 정의onStartCommand에서 트랜잭션 스레드를 열어 타이밍 로직을 처리
  • 4단계: 브로드캐스트 정의, 서비스 시작에 사용 마지막으로 AndroidManifest.xml에 서비스와 Boradcast를 등록하는 것을 잊지 마세요!

매개변수 세부정보: set(int type, long startTime, PendingIntent pi)

1type:5개의 선택 값이 있습니다:
AlarmManager.ELAPSED_REALTIME:전화가 켜져 있을 때는 알람 시계를 사용할 수 없습니다. 이 상태에서 알람 시계는 상대 시간(시스템 시작 시작 기준)을 사용하고 상태 값은 3입니다.
AlarmManager.ELAPSED_REALTIME_WAKEUP알람 시계는 시스템을 깨우고 절전 모드에서 프롬프트 기능을 수행합니다. 이 상태에서는 알람 시계도 상대 시간을 사용합니다. 상태 값은
AlarmManager.RTC 이 상태에서는 알람 시계를 사용할 수 없습니다. , 현재 시스템 시간입니다.
AlarmManager.RTC_WAKEUP은 알람 시계가 절전 상태에서 사용 가능함을 의미하며 이 상태에서는 알람 시계가 절대값을 사용합니다. time이고 상태 값은 0입니다.
AlarmManager.POWER_OFF_WAKEUP은 전화기가 꺼져 있을 때 알람 시계도 정상적으로 프롬프트 기능을 수행할 수 있음을 의미하므로 5가지 상태 중 가장 일반적으로 사용됩니다. 이 상태에서는 알람 시계도 절대 시간을 사용하며 상태 값은 4입니다. 그러나 이 상태는 SDK 버전의 영향을 받는 것으로 보이며 일부 버전에서는 이를 지원하지 않습니다.

PS: 첫 번째 매개 변수는 두 번째 매개변수의 유형(REALTIME인 경우) 그렇다면 다음을 사용하십시오. SystemClock.elapsedRealtime() 메서드는 시스템이 시작된 이후 경과된 밀리초 수를 얻을 수 있습니다. RTC인 경우 System.currentTimeMillis()를 사용하여 1970.1.1 0에서 0까지의 시간을 가져옵니다. 현재 경과된 시간(밀리초)

②startTime: 알람시계의 첫 번째 실행 시간(밀리초) 시간은 사용자 정의할 수 있지만 일반적으로 현재 시간이 사용됩니다. 이 속성은 첫 번째 매개변수에 해당하는 알람시계의 경우 첫 번째 속성(유형)과 밀접한 관련이 있다는 점에 유의해야 합니다. 상대 시간(ELAPSED_REALTIMEELAPSED_REALTIME_WAKEUP)이 사용된 다음 이 속성을 사용합니다. (시스템 시작 시간을 기준으로 한) 상대 시간을 사용해야 합니다. 예를 들어 현재 시간은 다음과 같이 표현됩니다. SystemClock.elapsedRealtime(); 첫 번째 파라미터에 해당하는 알람 시계가 절대 시간(RTC, RTC_WAKEUP, POWER_OFF_WAKEUP)을 사용하는 경우 이 속성은 절대 시간을 사용해야 합니다. 예를 들어 현재 시간은 System.currentTimeMillis()로 표현됩니다.

3PendingIntent:방송 보내기, 알림 보내기 등 알람 시계의 실행 작업을 바인딩합니다. 보류 중인 의도 Intent의 캡슐화 클래스입니다.
서비스를 시작하여 알람 알림이 구현되면 주의해야 할 점은 PendingIntent 객체를 얻으려면 Pending.getService를 사용해야 합니다. (Context c, int i, Intentintent, int j) 방법
알람 알림이 방송을 통해 구현되는 경우, PendingIntent 객체를 얻으려면 PendingIntent.getBroadcast를 사용해야 합니다. (Context c, int i, Intentintent, int j) 메소드
Activity 메소드를 사용하여 알람 시계 프롬프트를 구현하는 경우 PendingIntent 객체를 획득합니다. PendingIntent.getActivity(Context c,int i,Intentintent,int j)를 사용해야 합니다. 방법.
이 세 가지 방법을 잘못 사용하면 오류는 보고되지 않지만 알람 프롬프트 효과는 표시되지 않습니다.

추가:

버전 4.4(API 19)부터 알람 작업의 트리거 시간이 부정확해지고 지연될 수 있습니다. 이는 시스템 때문입니다. 전력 소비 최적화를 위해 정확성이 필요한 경우 setExtra() 메서드를 호출할 수 있습니다~

핵심 코드:

public 클래스 LongRunningService 확장 서비스 {
@Override
public IBinder onBind(Intentintent) {
return null;
}

@Override
public int onStartCommand(Intentintent, int flags, int startId) {
                   >                                                                      그러나  스레드 특정 논리 연산을 수행합니다.
                                                                                                               사용 사용 사용 사용         사용 사용 사용 사용 's' 사용 사용 사용 사용 사용 사용 사용 out through out out out out out out out out out out out out out out out out out out to to's over's to 소유 사용 사용 존재                                           에 관하여            ();
}
    }).start();
        AlarmManager   관리자 = (AlarmManager) getSystemService(ALARM_SERVICE);
                    //                                                                              > endingIntent.getBroadcast(this, 0, i , 0);
      Manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, TriggerAtTime, pi); AlarmReceiver는 BroadcastReceiver {

@Override
Public Void ONRECEIVE(컨텍스트 컨텍스트, 인텐트 인텐트) {
인텐트 I = 새 인텐트(컨텍스트, LongrunningService. 수업) }


이 섹션 요약:

이 섹션에서는 Service, IntentService 및 Service에 대해 계속해서 자세히 알아봅니다. 실제 개발에서 흔히 볼 수 있는 두 가지 사례: 프론트엔드 서비스 구현과 서비스 배경 서비스의 실현! 다음 섹션에서는 서비스의 AIDL과 프로세스 간 통신을 계속해서 연구하겠습니다. 계속 지켜봐주세요~


참조: "The First Line of Code Android" - Guo Lin: 매우 훌륭한 Android 입문서입니다!