最初に:ConnectivityManager.CONNECTIVITY_ACTION
が非推奨になったことと、connectivityManager.registerNetworkCallback
の使用方法を知っています。さらに、JobScheduler
について読んだとしても、それが正しいかどうかはわかりません。
私の問題は、電話がネットワークに接続/切断されたときにコードを実行したいということです。これは、アプリがバックグラウンドにあるときにも発生するはずです。 Android Oで始まるO回避したいことをバックグラウンドでサービスを実行したい場合、通知を表示する必要があります。JobScheduler/JobService
APIを使用して電話が接続/切断するときに情報を取得しようとしましたが、スケジュールした時間にのみ実行されます。私にとっては、このようなイベントが発生するとコードを実行できないようです。これを達成する方法はありますか?コードを少し調整する必要があるのでしょうか?
私のJobService:
@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;
}
次のようにサービスを開始します。
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>
これには簡単な解決策が必要だと思いますが、見つけられません。
2番目の編集:現在、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();
_
編集:私はハッキーな方法を見つけました。本当にハッキーと言って。それは動作しますが、私はそれを使用しません:
stopForeground
を使用すると、ユーザーには通知が表示されませんが、Androidとして登録します前景にいたstartService
を使用して、2番目のサービスを開始しますonTaskRemoved
で登録を解除しない場合、実行は継続されます。事実上、これはフォアグラウンドサービスを開始し、バックグラウンドサービスを開始してから終了します。2番目のサービスは最初のサービスよりも長持ちします。これが意図された動作であるかどうかはわかりません(バグレポートを提出する必要がありますか?)しかし、それは回避策です(これも悪いことです!
接続が変更されたときに通知を受け取ることができないようです:
CONNECTIVITY_ACTION
_受信機はマニフェストで宣言されません。さらに、プログラムで宣言された受信機は、受信機がメインスレッドに登録されている場合にのみ放送を受信します。まだ動作しません。バックグラウンドで更新を受信したい場合は、_connectivityManager.registerNetworkCallback
_を使用できます。これにより、次のソリューションが可能になります。
これは不可能です:
connectivityManager.registerNetworkCallback(NetworkInfo, PendingIntent)
は、条件が満たされた場合にPendingIntentがアクションを即座に実行するため使用できません。一度だけ呼び出されますフォアグラウンドモードで実行される(したがって通知を表示する)VPNServiceが既にあるので、VPNServiceが存在しない場合に接続を確認するサービスがフォアグラウンドで実行されるように実装しました。これは常に通知を表示しますが、通知は1つだけです。
全体として、8.0アップデートは非常に不満だと感じています。信頼性の低いソリューションを使用する(JobServiceを繰り返す)か、永続的な通知でユーザーを中断する必要があるようです。ユーザーはアプリをホワイトリストに登録できる必要があります(「XXはバックグラウンドで実行されています」という通知を非表示にするアプリがあるという事実だけでも十分です)。オフトピックのためにそんなに
ソリューションを拡張したり、エラーを表示したりしてください。ただし、これらは私の調査結果です。
ch4t4rジョブサービスにいくつかの変更を加えて、私のために働く必要があります
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) {
}
}
}
また、別の通常のサービスまたはboot_completeレシーバーからjobserviceを呼び出すことができます
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);
}
マニフェストで...
<service
Android:name=".Updater"
Android:permission="Android.permission.BIND_JOB_SERVICE"
Android:exported="true"/>