我发布这个问题是因为我认为这种行为真的很奇怪。
我写了一个 AsyncTask
来从网络上下载一个文件。此 AsyncTask
在单独的类中声明,因此我可以从任何地方调用它。我注意到在 AsyncTask
执行期间,我的应用程序 react 非常缓慢,这很奇怪,因为根据定义,AsyncTask
的 doInBackground
方法应该在单独的线程中运行。我真的不明白是什么导致我的应用程序变慢。然后我试着用一个线程做同样的事情:PUFFF!好 10 倍!!
不应该是一样的吗??有人能解释一下为什么我的应用使用 Thread
比使用 AsyncTask
响应更快吗?
这是我对 Thread
和 AsyncTask
的实现。
通过 AsyncTask
的实现,一旦启动任务,UI 就会变得非常无响应
公共(public)类 DownloaderTask 扩展 AsyncTask {
private Context context;
private PowerManager.WakeLock mWakeLock;
String fileName;
public DownloaderTask(Context context) {
this.context = context;
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
}
@Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
String credentials = "user" + ":" + "password";
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
connection.setRequestMethod("GET");
connection.addRequestProperty("Authorization", "Basic " + base64EncodedCredentials);
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
fileName = url.toString().substring(url.toString().lastIndexOf("/") + 1);
output = new FileOutputStream(fileName + "_temp");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
// if (fileLength > 0) // only if total length is known
// publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
@Override
protected void onPostExecute(String result) {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("File downloaded").setContentText("The requested files have been downloaded").setAutoCancel(true);
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(fileName + "_temp");
file.renameTo(new File(fileName));
file = new File(fileName);
Uri uri = Uri.fromFile(file);
resultIntent.setData(uri);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, resultIntent, 0);
mBuilder.setContentIntent(contentIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
if (result != null)
Toast.makeText(context, "Download error: " + result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show();
}
使用这个线程的实现,如果完全响应的话,UI 也是这样做的
public class DownloaderTask extends Thread {
private Context context;
private PowerManager.WakeLock mWakeLock;
String fileName;
String url;
public DownloaderTask(Context context, String url) {
this.context = context;
this.url = url;
}
@Override
public synchronized void run() {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(this.url);
connection = (HttpURLConnection) url.openConnection();
String credentials = "user" + ":" + "password";
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
connection.setRequestMethod("GET");
connection.addRequestProperty("Authorization", "Basic " + base64EncodedCredentials);
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
postResult("Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage());
this.interrupt();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
fileName = url.toString().substring(url.toString().lastIndexOf("/") + 1);
output = new FileOutputStream(fileName + "_temp");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
total += count;
// publishing the progress....
// if (fileLength > 0) // only if total length is known
// publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
postResult(e.toString());
interrupt();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
postResult(null);
};
protected void postResult(String result) {
Intent downloadComplete = new Intent("OWNLOAD_COMPLETE");
context.sendBroadcast(downloadComplete);
NotificationCompat.Builder mBuilder;
// Creates an explicit intent for an Activity in your app
if (result != null) {
mBuilder= new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("Download failed").setContentText("The request download has failed").setAutoCancel(true);
new File( fileName + "_temp").delete();
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(null);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
} else {
mBuilder= new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("Download complete").setContentText("The request files have been downloaded").setAutoCancel(true);
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(fileName + "_temp");
file.renameTo(new File(fileName));
file = new File(fileName);
Uri uri = Uri.fromFile(file);
resultIntent.setData(uri);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, resultIntent, 0);
mBuilder.setContentIntent(contentIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
}
}
最佳答案
不确定,但我敢打赌问题是您可能明确调用了 AsyncTask.doInBackground()
而不是使用 AsyncTask.execute()
方法。
显式调用 doInBackground()
绕过整个 asyncTask 机制,并在调用它的同一线程上执行代码(在您的情况下可能是 UI 主线程)。
更多信息:http://developer.android.com/reference/android/os/AsyncTask.html
关于Android AsyncTask 和 Thread 不同的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27131373/