Android O - Detect connectivity change in background -
first off: know connectivitymanager.connectivity_action
has been deprecated , know how use connectivitymanager.registernetworkcallback
. furthermore if read jobscheduler
, not entirely sure whether got right.
my problem want execute code when phone connected / disconnected to/from network. should happen when app in background well. starting android o have show notification if want run service in background want avoid.
tried getting info on when phone connects / disconnects using jobscheduler/jobservice
apis, gets executed time schedule it. me seems can't run code when event happens. there way achieve this? maybe have adjust code bit?
my jobservice:
@requiresapi(api = build.version_codes.lollipop) public class connectivitybackgroundserviceapi21 extends jobservice { @override public boolean onstartjob(jobparameters jobparameters) { logfactory.writemessage(this, log_tag, "job 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 logic consuming whether device connected network (and type) } logfactory.writemessage(this, log_tag, "job done. "); return false; } } @override public boolean onstopjob(jobparameters jobparameters) { logfactory.writemessage(this, log_tag, "job stopped"); return true; }
i start service so:
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 call - doesn't re-run if network changes
manifest (abstract):
<?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>
i guess there has easy solution not able find it.
second edit: i'm using firebase's jobdispatcher , works perfect across platforms (thanks @cutiko). basic structure:
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{ // logic.. } logfactory.writemessage(this, log_tag, "done 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 new connection } private enum connectiontype{ mobile,wifi,vpn,other; } }
i call (in boot receiver):
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();
edit: found hacky way. hacky say. works wouldn't use it:
- start foreground service, go foreground mode using startforeground(id, notification) , use
stopforeground
after that, user won't see notification android registers having been in foreground - start second service, using
startservice
- stop first service
- result: congratulations, have service running in background (the 1 started second).
- ontaskremoved get's called on second service when open app , cleared ram, not when first service terminates. if have repeating action handler , don't unregister in
ontaskremoved
continues run.
effectively starts foreground service, starts background service , terminates. second service outlives first one. i'm not sure whether intended behavior or not (maybe bug report should filed?) it's workaround (again, bad one!).
looks it's not possible notified when connection changes:
- with android 7.0
connectivity_action
receivers declared in manifest won't receive broadcasts. additionally receivers declared problematically receive broadcasts if receiver registered on main thread (so using service won't work). if still want receive updates in background can useconnectivitymanager.registernetworkcallback
- with android 8.0 same restrictions in place in addition can't launch services background unless it's foreground service.
all of allows these solutions:
- start foreground service , show notification
- this bothers users
- use jobservice , schedule run periodically
- depending on setting takes time until service get's called few seconds have passed since connection changes. in delays action should happen on connection change
this not possible:
connectivitymanager.registernetworkcallback(networkinfo, pendingintent)
cannot used because pendingintent executes it's action instantly if condition met; it's called once- trying start foreground service way goes foreground 1 ms , re-registers call results in comparable infinite recursion
since have vpnservice runs in foreground mode (and shows notification) implemented service check connectivity runs in foreground if vpnservice doesn't , vice-versa. shows notification, one.
in find 8.0 update extremely unsatisfying, seems either have use unreliable solutions (repeating jobservice) or interrupt users permanent notification. users should able whitelist apps (the fact alone there apps hide "xx running in background" notification should enough). off-topic
feel free expand solution or show me errors, these findings.
Comments
Post a Comment