问题
I'll get straight onto some facts/figures I discovered pls help me if you've faced/solved a similar problem.
I send back data every 5 minutes to a server unless the user manually toggles it Off with the help of a wakeful broadcast receiver
through a intent service
. Plus I get wifi locks before doing (I've tried this already without locks; doesnt work either)
Now on devices sending back data from regular data networks (2G/3G/4G) this works fine but on those connected to a wifi network somehow gets me a getActiveNetworkInfo()
as null
(Even directly hitting the web URL gives a hostname not found error
) this surely happens on HTC One (with v.4.4.2 installed) others devices do work fine.
we all know about the previous official connectivity manager have had issues in returning too much true / false condition even if network was not/available. I highly doubt if they've cropped up again or is just some custom OEM jing-bang
the alarm gets fired >> fires up the
wakeful broadcast receiver >> get wifi locks >> sleep the thread for 3 secs inside the
onReceive` after getting locks in order to wait for wifi to resume connectivity >> do my service work afterwards.am weighing the possibility of forcing use of mobile data connection instead of wifi. (by using
startUsingNetworkFeature()
) Does anyone have a solution for the same?
回答1:
Hope I'm not too late, I had a problem just like yours. My app needed to send data (from internal storage file) to server at certain intervals (30m/1h/2h/4h). For getActiveNetworkInfo()
to give proper result, you need to wake the wifi (because after 5-15 min of phone sleep wifi turns off). That gave me a lot of trouble, but here's how my solution works:
First, I have a WakefulBroadcastReceiver
that gets called when I need to wake the phone:
/** Receiver for keeping the device awake */
public class WakeUpReceiver extends WakefulBroadcastReceiver {
// Constant to distinguish request
public static final int WAKE_TYPE_UPLOAD = 2;
// AlarmManager to provide access to the system alarm services.
private static AlarmManager alarm;
// Pending intent that is triggered when the alarm fires.
private static PendingIntent pIntent;
/** BroadcastReceiver onReceive() method */
@Override
public void onReceive(Context context, Intent intent) {
// Start appropriate service type
int wakeType = intent.getExtras().getInt("wakeType");
switch (wakeType) {
case WAKE_TYPE_UPLOAD:
Intent newUpload = new Intent(context, UploadService.class);
startWakefulService(context, newUpload);
break;
default:
break;
}
}
/** Sets alarms */
@SuppressLint("NewApi")
public static void setAlarm(Context context, int wakeType, Calendar startTime) {
// Set alarm to start at given time
alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, WakeUpReceiver.class);
intent.putExtra("wakeType", wakeType);
pIntent = PendingIntent.getBroadcast(context, wakeType, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// For android 4.4+ the method is different
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.KITKAT) {
alarm.setExact(AlarmManager.RTC_WAKEUP,
startTime.getTimeInMillis() + 5000, pIntent);
} else {
alarm.set(AlarmManager.RTC_WAKEUP,
startTime.getTimeInMillis() + 5000, pIntent);
}
// The + 5000 is for adding symbolic 5 seconds to alarm start
}
}
Note the public static void setAlarm(Context, int, Calendar)
function, I use that function to set the alarms (see Main application).
Next, the service itself isn't an IntentService
, just Service
:
/** Service for uploading data to server */
public class UploadService extends Service {
private static final String serverURI = "http://your.server.com/file_on_server.php";
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
WifiLock wfl;
private Intent currentIntent;
private SharedPreferences sharedPrefs;
private int updateInterval;
private boolean continueService;
/** Service onCreate() method */
@Override
public void onCreate() {
super.onCreate();
// Initialize wifi lock
wfl = null;
// Initialize current Intent
currentIntent = null;
// Create separate HandlerThread
HandlerThread thread = new HandlerThread("SystemService",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// Get shared variables values if set
sharedPrefs = getSharedPreferences("serviceVars", MODE_PRIVATE);
updateInterval = sharedPrefs.getInt("sendInterval", 60); // in your case 5
continueService = sharedPrefs.getBoolean("bgServiceState", false);
}
/** Service onStartCommand() method */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If continuing, set new alarm
if (continueService) {
Calendar nextStart = Calendar.getInstance();
nextStart.set(Calendar.SECOND, 0);
// Set alarm to fire after the interval time
nextStart.add(Calendar.MINUTE, updateInterval);
WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD,
nextStart);
}
// Get current Intent and save it
currentIntent = intent;
// Acquire a wifi lock to ensure the upload process works
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wfl = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "WifiLock");
if (!wfl.isHeld()) {
wfl.acquire();
}
// For each start request, send a message to start a job and give
// start ID so we know which request we're stopping when we finish
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If service gets killed, it will restart
return START_STICKY;
}
/** Handler that receives messages from the thread */
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
/** Executes all service operations */
@Override
public void handleMessage(Message msg) {
// First wait for 5 seconds
synchronized (this) {
try {
wait(5 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
// Start checking if internet is enabled
boolean hasInternet = false;
long endTime = System.currentTimeMillis() + 55 * 1000;
// Check every second (max 55) if connected
while (System.currentTimeMillis() < endTime) {
if (hasInternet(UploadService.this)) {
hasInternet = true;
break;
}
synchronized (this) {
try {
wait(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Connected to internet or 60 (5 + 55) seconds passed
if (hasInternet) {
// Connect to server
connectToServer(serverURI, fileName);
} else {
// Can't connect, send message or something
}
// Stop service
stopSelf(msg.arg1);
}
}
/** Checks if phone is connected to Internet */
private boolean hasInternet(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = null;
if (cm != null) {
ni = cm.getActiveNetworkInfo();
}
return ni != null && ni.getState() == NetworkInfo.State.CONNECTED;
}
/** Service onDestroy() method */
@Override
public void onDestroy() {
// Release wifi lock
if (wfl != null) {
if (wfl.isHeld()) {
wfl.release();
}
}
// Release wake lock provided by BroadcastReceiver.
WakeUpReceiver.completeWakefulIntent(currentIntent);
super.onDestroy();
}
/** Performs server communication */
private void connectToServer(String serverUri, String dataFileName) {
// this function does my network stuff
}
/** Service onBind() method - Not used */
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
I also have a BOOT
BroadcastReceiver
, so if alarm is set and then phone is rebooted it will run service again:
/** Receiver for (re)starting alarms on device reboot operations */
public class BootReceiver extends BroadcastReceiver {
WakeUpReceiver alarm = new WakeUpReceiver();
/** BroadcastReceiver onReceive() method */
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
WakeUpReceiver.setAlarm(context, WakeUpReceiver.WAKE_TYPE_UPLOAD,
Calendar.getInstance());
}
}
}
Last, but not least in the Main Activity
, I start the service by starting receivers:
// Start upload service now
Calendar timeNow = Calendar.getInstance();
WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD, timeNow);
// Enable BootReceiver to (re)start alarm on device restart
getPackageManager().setComponentEnabledSetting(
new ComponentName(this, BootReceiver.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
And when I stop the service, I stop the BOOT
receiver too:
// Disable BootReceiver to (re)start alarm on device restart
getPackageManager().setComponentEnabledSetting(
new ComponentName(this, BootReceiver.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Additionally, don't forget to add proper permissions and components to manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<receiver
android:name=".BootReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".WakeUpReceiver" />
<service
android:name=".UploadService"
android:enabled="true"
android:label="ServerUpload"
android:launchMode="singleInstance" />
I think I covered it all, I'll be glad if this helps anyone solve their problems.
来源:https://stackoverflow.com/questions/24750860/unable-to-access-internet-via-wifi-from-a-background-service