I've got a problem with my IntentService - my intention is to outsource the networking and processing from the activity (I previously used an AsyncTask which worked excellent, however I also want to extend it to a widget).
The thing is, I don't get any result from the service - it almost seems like it never gets called (or something is wrong in the code that's supposed to retreive the data)...
Can someone with more experience in using services than me take a look and spot the obvious (or hidden) errors I've made? Would be greatly appreciated!
public class StateCheckerService extends IntentService {
public StateCheckerService() {
super("StateCheckerService");
// TODO Auto-generated constructor stub
}
String pageContent;
public static final String API_URL = "http://omegav.no/api/dooropen.php", INTENT_ACTION="omegavdoor.FETCH_COMPLETE", EXTRA_STATUS="status", EXTRA_TIME="time", KEY_STATUS="";
SharedPreferences settings;
private int timeMins = 0, timeoutMillis = 5000, resultCode;
int status_code = 0;
public void onCreate() {
super.onCreate();
// Declares the SharedPreferences object to use
settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(null, resultCode, resultCode);
return START_FLAG_RETRY;
}
@Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
if (checkConnection()) {
// Start process of retrieving status
try {
getData();
} catch (IOException e) {
returnResult(11);
} finally {
returnResult(resultCode);
}
} else {
// Notify the user of missing connection
returnResult(0); // Error: connection unavailable
}
}
private boolean checkConnection() {
// Declare connection manager and NetworkInfo objects
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
// Check network connection
if (activeInfo != null && activeInfo.isConnected()) {
return true;
} else {
return false;
}
}
/** Function to get data from the remote server */
public void getData() throws IOException {
// Create URL object to connect to
URL url = new URL(API_URL);
// Open new HTTP connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// Set a connection timeout to prevent app lockup if it can't reach the server
urlConnection.setConnectTimeout(timeoutMillis);
// Attempt to connect and retrieve data
try { // Return exception if the stream is unreachable
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
// Process the contents of the stream
readStream(in);
}
finally {
// Disconnect after retrieving data
urlConnection.disconnect();
}
}
public void readStream(InputStream input) throws IOException {
// Reads the content of the page
try {
// String length varies with the time value - read some extra to avoid missing the end
pageContent = readIt(input, 40);
// Remove the extra white spaces at the end
} catch (UnsupportedEncodingException e) {
resultCode = 10;
}
if (pageContent == null) {
// Stop further processing (and cause the UI to report error)
resultCode = 13;
} else {
// Checks to see whether the "open" flag exists
if (pageContent.charAt(9) == '1') {
// Find out how long it's been open
int openTime = Integer.parseInt(pageContent.substring(20, pageContent.lastIndexOf("}")));
// Convert from seconds to minutes
timeMins = openTime / 60;
if (timeMins > 0) {
resultCode = 1; // Display how long it's been open
} else {
resultCode = 2; // If it just opened
}
} else {
// Find out how long it's been closed
int closedTime = Integer.parseInt(pageContent.substring(19, pageContent.lastIndexOf("}"))); // TODO: change 19 to 20 to support the API change
// Convert from seconds to minutes
timeMins = closedTime / 60;
if (timeMins > 0) {
resultCode = 3; // Display how long it's been open
} else {
resultCode = 4; // If it just closed
}
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
// Initialize reader object
Reader reader = null;
// Decode the input stream
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
private void returnResult(int resultCode) {
Intent resultIntent = new Intent();
resultIntent.setAction(INTENT_ACTION);
resultIntent.putExtra(EXTRA_STATUS, resultCode)
.putExtra(EXTRA_TIME, timeMins);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
public class StateChecker extends Activity {
String pageContent;
boolean doorIsOpen = false, notFirstRun = false, error = false;
private static final int transitionDuration = 250;
private ResponseReceiver receiver;
TransitionDrawable transition;
TextView text_doorState;
Button button_getState;
ProgressBar door_progress;
LinearLayout background;
int timeMins;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate the XML layout
setContentView(R.layout.state_checker);
// Declare the views to be adressed
text_doorState = (TextView) findViewById(R.id.tv_doorState);
button_getState = (Button) findViewById(R.id.button_refreshState);
door_progress = (ProgressBar) findViewById(R.id.doorState_progressBar);
background = (LinearLayout) findViewById(R.id.doorStateView);
IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new ResponseReceiver();
registerReceiver(receiver, filter);
}
/** Receiver class to listen to and handle result from checker service */
public class ResponseReceiver extends BroadcastReceiver {
public static final String ACTION_RESP = StateCheckerService.INTENT_ACTION;
@Override
public void onReceive(Context context, Intent intent) {
int result = intent.getIntExtra(StateCheckerService.EXTRA_STATUS, 0);
timeMins = intent.getIntExtra(StateCheckerService.EXTRA_TIME, 0);
Toast.makeText(context, "Result received", Toast.LENGTH_SHORT).show();
processResult(result);
}
}
public void onStart() {
super.onStart();
// Checks the door status on app launch
go();
}
public void onStop() {
super.onStop();
unregisterReceiver(receiver);
}
private void go() {
// Update text and progress bar to indicate it's working
text_doorState.setText(R.string.text_stateUpdating);
door_progress.setVisibility(View.VISIBLE);
// Fade the color back to grey if it is something else
if (notFirstRun) {
transition.reverseTransition(transitionDuration);
}
if (checkConnection()) {
Intent intent = new Intent(this, StateCheckerService.class);
startService(intent);
notify("service started");
} else {
// Notify the user of missing connection
notify(getString(R.string.error_connection_unavailable));
}
}
private boolean checkConnection() {
// Declare connection manager and NetworkInfo objects
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
// Check network connection
if (activeInfo != null && activeInfo.isConnected()) {
return true;
} else {
return false;
}
}
protected void processResult(Integer resultCode) {
switch (resultCode) {
case 1:
if (timeMins < 60) {
// Notify of open door and display minutes
notify(getString(R.string.text_stateOpenMins, timeMins));
} else {
// If it's been more than an hour, display the time in hours
int openTimeHours = timeMins / 60;
int remainder = timeMins - (openTimeHours * 60);
notify(getString(R.string.text_stateOpenHours, openTimeHours, remainder));
}
break;
case 2:
// Notify of open door (just opened)
notify(getString(R.string.text_stateOpenNow));
break;
case 3:
if (timeMins < 60) {
// Notify of closed door and display minutes
notify(getString(R.string.text_stateClosedMins, timeMins));
} else {
// If it's been more than an hour, display the time in hours
int closedTimeHours = timeMins / 60;
int remainder = timeMins - (closedTimeHours * 60);
notify(getString(R.string.text_stateClosedHours, closedTimeHours, remainder));
}
break;
case 4:
// Notify of closed door (just closed
notify(getString(R.string.text_stateClosedNow));
break;
case 10:
// Error message: unsupported stream format
notify(getString(R.string.error_stream_unsupported));
break;
case 11:
// Error message: connection failed
notify(getString(R.string.error_connection_failed));
break;
// Case 12 reserved
case 13:
// Error message: null data stream
notify(getString(R.string.error_stream_retrieve));
break;
}
if (resultCode >= 10) {
error = true;
} else {
error = false;
}
updateDisplay();
}
protected void updateDisplay() {
door_progress.setVisibility(View.GONE);
if (!error) {
/** Update the UI to reflect the door state */
if(doorIsOpen) {
// Update the text view to display an open door state
text_doorState.setText(getString(R.string.text_stateOpen));
// Change the background color
background.setBackgroundResource(R.drawable.trans_open);
transition = (TransitionDrawable) background.getBackground();
transition.startTransition(transitionDuration);
} else {
// Update the text view to display a closed door state
text_doorState.setText(getString(R.string.text_stateClosed));
// Change the background color
background.setBackgroundResource(R.drawable.trans_close);
transition = (TransitionDrawable) background.getBackground();
transition.startTransition(transitionDuration);
}
// Indicates that the app has gone through a successful execution
notFirstRun = true;
} else {
// If it failed to execute, display error message
text_doorState.setText(R.string.text_stateFailed);
// Revert to grey background
background.setBackgroundResource(R.drawable.background);
}
}
/** Helper class used to display toast notifications */
private void notify(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tovine.omegavdoor.widget"
android:versionCode="5"
android:versionName="1.0.1" >
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".Settings"
android:label="@string/title_activity_settings" >
</activity>
<activity
android:name=".StateChecker"
android:configChanges="orientation|screenSize"
android:title="@string/app_name"
android:windowSoftInputMode="stateAlwaysHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" />
<receiver
android:name=".WidgetStateProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="omegavdoor.FETCH_COMPLETE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/doorwidget" />
</receiver>
<service android:name="tovine.omegavdoor.widget.WidgetUpdateService">
<intent-filter>
<action android:name="omegavdoor.FETCH_COMPLETE" />
</intent-filter>
</service>
<service android:name="tovine.omegavdoor.widget.StateCheckerService"
android:process=":checker_process">
</service>
-->
<!-- <activity
android:name="Probability"
android:label="@string/title_activity_probability" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="StateChecker" />
</activity>
<activity
android:name=".LoadWebImg"
android:label="TestClass" /> -->
</application>
</manifest>
I can be wrong but it seems your returnResult() function doesn't send anything. Maybe you forgot to add sendBroadcst(resultIntent); call to it?