2017-09-11 2 views
3

첫 번째 끄기 : ConnectivityManager.CONNECTIVITY_ACTION은 사용되지 않으며 connectivityManager.registerNetworkCallback을 사용하는 방법을 알고 있습니다. 또한 JobScheduler에 대해 읽는다면, 나는 그것이 옳은 것인지 잘 모르겠다.Android O - 백그라운드에서 연결 변경 감지

내 문제는 전화가 네트워크에 연결되거나 연결이 끊어진 경우 일부 코드를 실행하려고하는 것입니다. 앱이 백그라운드에서 작동 할 때 발생합니다. Android O부터 백그라운드에서 내가 원하지 않는 서비스를 실행하고 싶다면 알림을 표시해야합니다.
JobScheduler/JobService API를 사용하여 전화 연결/연결 끊기에 대한 정보를 얻으려고했지만 일정을 잡으면 실행됩니다. 나를 위해 그것은 같은 이벤트가 발생하면 코드를 실행할 수없는 것 같습니다. 이것을 달성 할 수있는 방법이 있습니까? 내 코드를 약간 조정해야 할까?

내 JobService :

JobScheduler jobScheduler = (JobScheduler)context.getSystemService(Context.JOB_SCHEDULER_SERVICE); 
ComponentName service = new ComponentName(context, ConnectivityBackgroundServiceAPI21.class); 
JobInfo.Builder builder = new JobInfo.Builder(1, service).setPersisted(true) 
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).setRequiresCharging(false); 
jobScheduler.schedule(builder.build()); 
      jobScheduler.schedule(builder.build()); //It runs when I call this - but doesn't re-run if the network changes 

매니페스트 (추상) : 나는에 대한 쉬운 해결책이 있어야한다 생각

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools"> 

    <uses-permission android:name="android.permission.INTERNET" /> 
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> 
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> 
    <uses-permission android:name="android.permission.VIBRATE" /> 

    <application> 
     <service 
      android:name=".services.ConnectivityBackgroundServiceAPI21" 
      android:exported="true" 
      android:permission="android.permission.BIND_JOB_SERVICE" /> 
    </application> 
</manifest> 

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 
public class ConnectivityBackgroundServiceAPI21 extends JobService { 

    @Override 
    public boolean onStartJob(JobParameters jobParameters) { 
     LogFactory.writeMessage(this, LOG_TAG, "Job was started"); 
     ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 
     NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); 
     if (activeNetwork == null) { 
      LogFactory.writeMessage(this, LOG_TAG, "No active network."); 
     }else{ 
      // Here is some logic consuming whether the device is connected to a network (and to which type) 
     } 
     LogFactory.writeMessage(this, LOG_TAG, "Job is done. "); 
     return false; 
    } 
} 
@Override 
public boolean onStopJob(JobParameters jobParameters) { 
    LogFactory.writeMessage(this, LOG_TAG, "Job was stopped"); 
    return true; 
} 

내가 같은 서비스가 시작 그러나 나는 그것을 발견 할 수 없다.

+0

서비스를 포 그라운드에서 실행하지 않으려는 경우 올바르지 않습니다. 연결 관리자를 사용하십시오. 연결이 바뀌면 구체적으로 무엇을하려합니까? – tyczj

+0

VPNService (또는 사용자가 구성 할 수있는 활동)를 시작하려고합니다. – Ch4t4r

+0

'NetworkCallback' 객체가 아닌'PendingIntent'와 함께'registerNetworkCallback()'을 사용하고 있습니까? –

답변

3

두 번째 편집 : 이제 firebase의 JobDispatcher를 사용하고 있으며 모든 플랫폼에서 완벽하게 작동합니다 (@cutiko에게 감사).

public class ConnectivityJob extends JobService{ 

    @Override 
    public boolean onStartJob(JobParameters job) { 
     LogFactory.writeMessage(this, LOG_TAG, "Job created"); 
     connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
      connectivityManager.registerNetworkCallback(new NetworkRequest.Builder().build(), networkCallback = new ConnectivityManager.NetworkCallback(){ 
       // -Snip- 
      }); 
     }else{ 
      registerReceiver(connectivityChange = new BroadcastReceiver() { 
       @Override 
       public void onReceive(Context context, Intent intent) { 
        handleConnectivityChange(!intent.hasExtra("noConnectivity"), intent.getIntExtra("networkType", -1)); 
       } 
      }, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); 
     } 

     NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); 
     if (activeNetwork == null) { 
      LogFactory.writeMessage(this, LOG_TAG, "No active network."); 
     }else{ 
      // Some logic.. 
     } 
     LogFactory.writeMessage(this, LOG_TAG, "Done with onStartJob"); 
     return true; 
    } 


    @Override 
    public boolean onStopJob(JobParameters job) { 
     if(networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)connectivityManager.unregisterNetworkCallback(networkCallback); 
     else if(connectivityChange != null)unregisterReceiver(connectivityChange); 
     return true; 
    } 

    private void handleConnectivityChange(NetworkInfo networkInfo){ 
     // Calls handleConnectivityChange(boolean connected, int type) 
    } 

    private void handleConnectivityChange(boolean connected, int type){ 
     // Calls handleConnectivityChange(boolean connected, ConnectionType connectionType) 
    } 

    private void handleConnectivityChange(boolean connected, ConnectionType connectionType){ 
     // Logic based on the new connection 
    } 

    private enum ConnectionType{ 
     MOBILE,WIFI,VPN,OTHER; 
    } 
} 

나는 (내 부팅 수신기)과 같이 호출 :

Job job = dispatcher.newJobBuilder().setService(ConnectivityJob.class) 
      .setTag("connectivity-job").setLifetime(Lifetime.FOREVER).setRetryStrategy(RetryStrategy.DEFAULT_LINEAR) 
      .setRecurring(true).setReplaceCurrent(true).setTrigger(Trigger.executionWindow(0, 0)).build(); 

편집 : 이것은 기본 구조이다 나는 해키 방법을 발견했다. 정말 해키. 그것은 작동하지만 난 그것을 사용하지 것이다 : 사용자가 알림을 볼 수 없습니다,

  • 가 전경 서비스를 시작 startForeground (ID, 통지)를 사용하여 포 그라운드 모드로 전환을 한 후 stopForeground를 사용하지만 안드로이드는 등록 전경에 있었던 것으로
  • 는 첫 번째 서비스를 startService
  • 정지를
  • 결과를 사용하여, 두 번째 서비스를 시작
  • : 축하합니다, 당신은 배경 (당신이 두 번째 시작 하나)에서 실행되는 서비스가있다.
  • onTaskRemoved get은 응용 프로그램을 열 때 RAM에서 지워지지만 첫 번째 서비스가 종료 될 때 두 번째 서비스에서 호출됩니다. 처리기와 같은 반복 동작이 있고 onTaskRemoved에서 등록을 취소하지 않으면 계속 실행됩니다.

효과적으로 백그라운드 서비스를 시작한 후 종료하는 포 그라운드 서비스를 시작합니다. 두 번째 서비스는 첫 번째 서비스보다 오래갑니다. 이것이 의도 된 행동인지 아닌지 (아마도 버그 보고서를 제출해야 할 것인가?) 확실하지는 않지만 해결 방법 (다시 나쁜 것!)입니다.CONNECTIVITY_ACTION 수신기는 매니페스트에 선언 된 안드로이드 7.0

  • 방송을 수신하지 않습니다 연결 변경 때 알림을받을 수 없습니다처럼


    보인다. 또한 수신기가 주 스레드에 등록되어있는 경우 브로드 캐스트를 수신하기 때문에 문제가 있다고 선언 된 수신기는 서비스를 사용할 수 없습니다. 여전히 백그라운드에서 업데이트를 받고 싶다면 connectivityManager.registerNetworkCallback

  • Android 8.0의 경우 동일한 제한이 적용되지만 전경 서비스가 아니면 배경에서 서비스를 시작할 수 없습니다.

      가 전경 서비스를 시작하고 보여 알림
      • 이 가장 가능성이
    • 사용 JobService 사용자
    • 에게 귀찮게를 실행되도록 예약
    • :

    이 모든

  • 는 이러한 솔루션을 수 있습니다 주기적으로
    • 설정에 따라 서비스가 호출 될 때까지 약간의 시간이 걸리므로 연결 변경 이후 몇 초가 지난 수 있습니다. 모든이의 모든

이 할 수없는 연결의 변화에서 발생한다 조치를 지연 :

  • PendingIntent가이 조건이 충족되는 즉시 경우 동작이의 실행 때문에 connectivityManager.registerNetworkCallback(NetworkInfo, PendingIntent) 사용할 수 없습니다; 그것은 단지
    • 1 ms 동안 전경로 이동이 방법을 전경 서비스를 시작하려고 한 번이라고 벌써부터 무한 재귀

에 비해 뭔가 호출 결과를 다시 등록 포어 그라운드 모드로 실행되는 VPNService가 있습니다. (따라서 알림을 표시합니다.) VPNService가 아닌 경우 연결을 확인하는 서비스가 포 그라운드에서 실행되고 그 반대의 경우도 구현되었습니다. 이는 항상 알림을 표시하지만 하나만 표시합니다.
모두 8.0 업데이트가 매우 불만족 스럽습니다. 신뢰할 수없는 솔루션 (JobService 반복)을 사용하거나 영구적 인 알림을 통해 사용자를 방해해야하는 것처럼 보입니다. 사용자는 앱을 허용 할 수 있어야합니다 ("XX가 백그라운드에서 실행 중임"이라는 표시를 숨기는 앱이 있다는 사실만으로도 충분 함). 주제를 벗어난 많은 사람들

자유롭게 해결책을 제시하거나 오류를 표시 할 수는 있지만 내 발견 사항입니다.

+0

해킹 된 솔루션과 관련하여 백그라운드에서 서비스가 더 길게 (24 시간) 실행되었는지 테스트 했습니까? 내가 mainThread에 의해 시작된 작업 스레드를 실행하는 또 다른 해킹 솔루션을 시도했다. 이 스레드는 주기적으로 네트워크 모니터링을 수행합니다 (20 초 간격). 그러나 더 오랜 기간 동안 테스트를 한 결과, 시스템 자원 요구 사항에 따라 시스템이 전체 프로세스를 종료했거나 때로는 이와 같이 처리되는 것으로 나타났습니다. –

+0

오랜 기간 동안 실행되는지 테스트하지 않았지만 시스템의 리소스가 부족한 경우 전경 서비스가 아닌 모든 서비스가 항상 종료됩니다. 경우에 따라 onTerminate() 호출을 무시할 수 있습니다.이 호출은 일반적으로 참조를 해제하고 계속 진행되는 작업을 중지하므로 서비스가 영원히 계속 실행될 수 있습니다. 나는 당신이 그것을 시험 할 것을 조언 할 것입니다. 저에게 가장 중요한 사실은 배경에있을 때 제가 서비스를 시작할 수 있다는 것입니다. – Ch4t4r

+0

부팅 수신자를 호출 한 직후에 작업 발송자 트리거를 설정합니다. 그렇다면 직업 파견인은 무엇이 필요합니까? 부팅 수신기에서 서비스를 호출 할 수 있습니다. –

0

내가 나를

public class JobServicio extends JobService { 
String LOG_TAG ="EPA"; 
ConnectivityManager.NetworkCallback networkCallback; 
BroadcastReceiver connectivityChange; 
ConnectivityManager connectivityManager; 
@Override 
public boolean onStartJob(JobParameters job) { 
    Log.i(LOG_TAG, "Job created"); 
    connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     connectivityManager.registerNetworkCallback(new NetworkRequest.Builder().build(), networkCallback = new ConnectivityManager.NetworkCallback(){ 
      // -Snip- 
     }); 
    }else{ 
     registerReceiver(connectivityChange = new BroadcastReceiver() { //this is not necesary if you declare the receiver in manifest and you using android <=6.0.1 
      @Override 
      public void onReceive(Context context, Intent intent) { 
       Toast.makeText(context, "recepcion", Toast.LENGTH_SHORT).show(); 
       handleConnectivityChange(!intent.hasExtra("noConnectivity"), intent.getIntExtra("networkType", -1)); 
      } 
     }, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); 
    } 


    Log.i(LOG_TAG, "Done with onStartJob"); 
    return true; 
} 
@Override 
public boolean onStopJob(JobParameters job) { 
    if(networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)connectivityManager.unregisterNetworkCallback(networkCallback); 
    else if(connectivityChange != null)unregisterReceiver(connectivityChange); 
    return true; 
} 
private void handleConnectivityChange(NetworkInfo networkInfo){ 
    // Calls handleConnectivityChange(boolean connected, int type) 
} 
private void handleConnectivityChange(boolean connected, int type){ 
    // Calls handleConnectivityChange(boolean connected, ConnectionType connectionType) 
    Toast.makeText(this, "erga", Toast.LENGTH_SHORT).show(); 
} 
private void handleConnectivityChange(boolean connected, ConnectionType connectionType){ 
    // Logic based on the new connection 
} 
private enum ConnectionType{ 
    MOBILE,WIFI,VPN,OTHER; 
} 
    ConnectivityManager.NetworkCallback x = new ConnectivityManager.NetworkCallback() { //this networkcallback work wonderfull 

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 
    @Override 
public void onAvailable(Network network) { 
     Log.d(TAG, "requestNetwork onAvailable()"); 
     if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 
    //do something 
     } 
     else { 
     //This method was deprecated in API level 23 
     ConnectivityManager.setProcessDefaultNetwork(network); 

    } 
    } 
     @Override 
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { 
    Log.d(TAG, ""+network+"|"+networkCapabilities); 
} 

@Override 
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 
    Log.d(TAG, "requestNetwork onLinkPropertiesChanged()"); 
} 

@Override 
public void onLosing(Network network, int maxMsToLive) { 
    Log.d(TAG, "requestNetwork onLosing()"); 
} 

@Override 
public void onLost(Network network) { 
} 
    } 
    } 

에 대한 Ch4t4r의 jobservice 작업에 일부 수정을 추가해야 한 당신은 다른 일반 서비스 나 매니페스트 boot_complete 수신기

 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ 
      Job job = dispatcher.newJobBuilder().setService(Updater.class) 
        .setTag("connectivity-job").setLifetime(Lifetime.FOREVER).setRetryStrategy(RetryStrategy.DEFAULT_LINEAR) 
        .setRecurring(true).setReplaceCurrent(true).setTrigger(Trigger.executionWindow(0, 0)).build(); 
      dispatcher.mustSchedule(job); 
     } 

에서 jobservice를 호출 할 수 있습니다 .. .

<service 
     android:name=".Updater" 
     android:permission="android.permission.BIND_JOB_SERVICE" 
     android:exported="true"/> 
관련 문제