AsyncTask is an abstract helper class for managing background operations that eventually
post back to the UI thread. It creates a simpler interface for asynchronous operations than
manually creating a Java Thread class.
Instead of creating threads for background processing and using messages and message
handlers for updating the UI, you can create a subclass of AsyncTask and implement the
appropriate event methods.The onPreExecute() method runs on the UI thread before
background processing begins.The doInBackground() method handles background processing,
whereas publishProgress() informs the UI thread periodically about the background
processing progress.When the background processing finishes, the
onPostExecute() method runs on the UI thread to give a final update.
The following code demonstrates an example implementation of AsyncTask to perform
the same functionality as the code for the Thread:
private class ImageLoader extends
AsyncTask<URL, String, String> {
@Override
protected String doInBackground(
URL... params) {
// just one param
try {
URL text = params[0];
// ... parsing code {
publishProgress(
"imgCount = " + curImageCount);
// ... end parsing code }
}
catch (Exception e ) {
Log.e("Net",
"Failed in parsing XML", e);
return "Finished with failure.";
}
return "Done...";
}
protected void onCancelled() {
Log.e("Net", "Async task Cancelled");
}
protected void onPostExecute(String result) {
mStatus.setText(result);
}
protected void onPreExecute() {
mStatus.setText("About to load URL");
}
protected void onProgressUpdate(
String... values) {
// just one value, please
mStatus.setText(values[0]);
}
}
When launched with the AsyncTask.execute() method, doInBackground() runs in a
background thread while the other methods run on the UI thread.There is no need to
manage a Handler or post a Runnable object to it.This simplifies coding and debugging.
Using Threads for Network Calls
The following code demonstrates how to launch a new thread that connects to a remote
server, retrieves and parses some XML, and posts a response back to the UI thread to
change a TextView:
import java.net.URL;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
// ...
new Thread() {
public void run() {
try {
URL text = new URL(
"http://api.flickr.com/services/feeds/photos_public.gne?
?id=26648248@N04&lang=en-us&format=atom");
XmlPullParserFactory parserCreator =
XmlPullParserFactory.newInstance();
XmlPullParser parser =
parserCreator.newPullParser();
parser.setInput(text.openStream(), null);
mHandler.post(new Runnable() {
public void run() {
status.setText("Parsing...");
}
});
int parserEvent = parser.getEventType();
while (parserEvent !=
XmlPullParser.END_DOCUMENT) {
// Parsing code here ...
parserEvent = parser.next();
}
mHandler.post(new Runnable() {
public void run() {
status.setText("Done...");
}
}
);
} catch (Exception e) {
Log.e("Net", "Error in network call", e);
}
}
}.start();
For this example, an anonymous Thread object will do.We create it and call its start()
method immediately.However, now that the code runs on a separate thread, the user interface
updates must be posted back to the main thread.This is done by using a Handler
object on the main thread and creating Runnable objects that execute to call setText()
on the TextView widget named status.
The rest of the code remains the same as in the previous examples. Executing both the
parsing code and the networking code on a separate thread allows the user interface to
continue to behave in a responsive fashion while the network and parsing operations are
done behind the scenes, resulting in a smooth and friendly user experience.This also allows
for handling of interim actions by the user, such as canceling the transfer.You can
accomplish this by implementing the Thread to listen for certain events and check for
certain flags.
Displaying Images from a Network Resource
Now that we have covered how you can use a separate thread to parse XML, let's take our
example a bit deeper and talk about working with non-primitive data types.
Continuing with the previous example of parsing for image locations from a flickr
feed, let's display some images from the feed.The following example reads the image data
and displays it on the screen, demonstrating another way you can use network resources:
import java.io.InputStream;
import java.net.URL;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import android.os.Handler;
// ...
final String imageSrc =
parser.getAttributeValue(null, "href");
final String currentTitle = new String(title);
imageThread.queueEvent(new Runnable() {
public void run() {
InputStream bmis;
try {
bmis = new URL(imageSrc).openStream();
final Drawable image = new BitmapDrawable(
BitmapFactory.decodeStream(bmis));
mHandler.post(new Runnable() {
public void run() {
imageSwitcher.setImageDrawable(image);
info.setText(currentTitle);
}
});
} catch (Exception e) {
Log.e("Net", "Failed to grab image", e);
}
}
});
You can find this block of code within the parser thread, as previously described.After the
image source and title of the image have been determined, a new Runnable object is
queued for execution on a separate image handling thread.The thread is merely a queue
that receives the anonymous Runnable object created here and executes it at least 10 seconds
after the last one, resulting in a slideshow of the images from the feed.
Retrieving Android Network Status
The Android SDK provides utilities for gathering information about the current state of
the network.This is useful to determine if a network connection is even available before
trying to use a network resource.The ConnectivityManager class provides a number of
methods to do this.The following code determines if the mobile (cellular) network is
available and connected. In addition, it determines the same for the Wi-Fi network:
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
// ...
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni =
cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiAvail = ni.isAvailable();
boolean isWifiConn = ni.isConnected();
ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileAvail = ni.isAvailable();
boolean isMobileConn = ni.isConnected();
status.setText("WiFi\nAvail = "+ isWifiAvail +
"\nConn = " + isWifiConn +
"\nMobile\nAvail = "+ isMobileAvail +
"\nConn = " + isMobileConn);
First, an instance of the ConnectivityManager object is retrieved with a call to the
getSystemService() method, available as part of your application Context.Then this instance
retrieves NetworkInfo objects for both TYPE_WIFI and TYPE_MOBILE (for the cellular
network).These objects are queried for their availability but can also be queried at a
more detailed status level to learn exactly what state of connection (or disconnection) the
network is in. Figure 12.2 shows the typical output for the emulator in which the mobile
network is simulated but Wi-Fi isn't available.
If the network is available, this does not necessarily mean the server that the network
resource is on is available.However, a call to the ConnectivityManager method
requestRouteToHost() can answer this question.This way, the application can give the
user better feedback when there are network problems.
For your application to read the status of the network, it needs explicit permission.The
following statement is required to be in its AndroidManifest.xml file:
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"/>
References and More Information
Java.net package:
http://developer.android.com/reference/java/net/package-summary.html
Android.net package:
http://developer.android.com/reference/android/net/package-summary.html
XML Pull Parsing:
http://www.xmlpull.org/
Android XML Pull Parser:
http://developer.android.com/reference/org/xmlpull/v1/XmlPullParser.html
0 件のコメント:
コメントを投稿