Android AsyncTask 和 Thread 不同的行为

标签 android multithreading android-asynctask difference

我发布这个问题是因为我认为这种行为真的很奇怪。

我写了一个 AsyncTask 来从网络上下载一个文件。此 AsyncTask 在单独的类中声明,因此我可以从任何地方调用它。我注意到在 AsyncTask 执行期间,我的应用程序 react 非常缓慢,这很奇怪,因为根据定义,AsyncTaskdoInBackground 方法应该在单独的线程中运行。我真的不明白是什么导致我的应用程序变慢。然后我试着用一个线程做同样的事情:PUFFF!好 10 倍!!

不应该是一样的吗??有人能解释一下为什么我的应用使用 Thread 比使用 AsyncTask 响应更快吗?

这是我对 ThreadAsyncTask 的实现。

通过 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/

相关文章:

android - 为海豚安卓浏览器写一个插件

java - synchronized block 是如何实现同步的

Android - 在 UI Fragment 中保留对象

android - 调用 OnPrepareOptionsMenu 会产生空指针异常

php - 我无法使用 json 在本地主机服务器中发布数据和图像

C++11 多线程在神经网络中的性能非常低

ios - 如果核心数据计数/获取请求中的实体名称错误,如何避免崩溃?

java - 将数组 [] 从 AsyncTask 返回到 Main Activity

android - 您应该为每个不同的调用创建新的异步任务还是使用相同的

Java Android、倒数计时器和 ProgressBar(先读)