我的 IntentService 有问题 - 我的目的是从活动中外包网络和处理(我之前使用了一个效果很好的 AsyncTask,但我也想将它扩展到一个小部件)。
问题是,我没有从服务中得到任何结果——它几乎似乎永远不会被调用(或者应该检索数据的代码中有问题)......
在使用服务方面比我更有经验的人可以看看并发现我犯的明显(或隐藏)错误吗?将不胜感激!
服务内容:
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();
}
}
编辑(AndroidManifest):
<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>