用户按下后取消正在进行的连接

Posted

技术标签:

【中文标题】用户按下后取消正在进行的连接【英文标题】:Cancel an ongoing connection when user presses back 【发布时间】:2011-11-15 12:23:03 【问题描述】:

我一直在搜索这个,但我没有找到我的具体问题。我知道AskyncTask 可以使用.cancel(true) 取消,但这只有在我有一个可以检查值isCanceled() 的循环时才会发生。

但我的问题是.. 当用户按下返回时,如何取消 AsyncTask (卡在 httpclient.execute() 中)?如果用户导航离开该 Activity 并转到另一个我不希望 AsyncTask 运行数量不受控制,因为这可能会导致内存问题,用户可以来回导航并创建未确定数量的任务。这就是为什么我要关闭它们。有人知道方法吗?我发布了我用来连接的代码:

public class Test extends Activity 

@Override
protected void onStart() 
    super.onStart();

    new ConnectionTask().execute("https://www.mywebserver.com/webservice.php?param1=test");


private class ConnectionTask extends AsyncTask<String, Void, String>

    @Override
    protected String doInBackground(String... params) 
        try 

            HttpClient httpclient = DefaultHttpClient(params,clientConnectionManager);
            HttpPost httpPost = new HttpPost(params[0]);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();

            if(httpEntity != null)
                return EntityUtils.toString(httpEntity);
         catch (Exception e) 
            e.printStackTrace();
        
        return null;        
    

  


你知道我应该在 onStop() 中添加什么来取消正在进行的 httpClient.execute() 函数吗?有时几乎永远卡住。

非常感谢您的帮助,非常感谢您。

更新

如果我关闭ConnectionManager,我必须再次为 https 握手,对吗?在我创建httpClient 时查看此代码,我将其用于 https:

HttpClient httpclient = DefaultHttpClient(params,clientConnectionManager);

感谢大家的快速回复以及此处提供的各种解决方案。我将尝试使用超时(不必等待太多)+cancel() 函数来避免处理onPostExecute。我会告诉结果是否如预期的那样!非常感谢大家!

【问题讨论】:

您是否尝试取消正在进行的线程? 当您尝试取消 AsyncTask 时,它只是将 isCancelled() 设置为 true,我无法销毁 AsyncTask(我知道),.cancel(true) 不起作用。 试试这个 httpclient.getConnectionManager().shutdown(); 我写了http,但实际上是https。这段代码是简化版,因为我想保留connectionmanager和httpParams以避免进一步握手,有没有办法避免关闭connectionManager? 【参考方案1】:

你可以覆盖

onPause() Activity 的方法并在那里编写停止连接代码。如果选项特定于后退按钮,那么您可以覆盖

onBackButtonPressed() 方法停止执行。

【讨论】:

你知道如何停止连接吗?从 2 个不同的线程访问 httpClient 是否安全?我试过了,但没有这种取消或中止连接的方法......你有什么想法吗?【参考方案2】:

AsyncTask 类中有一个cancel() 方法。维护一个成员到 asynctask 并在onDestroy(). 中取消它,然后将该成员设置为 null。

更新

使用ClientConnectionManager 关闭连接。

http://developer.android.com/reference/org/apache/http/conn/ClientConnectionManager.html

更新 2

检查此链接以设置连接超时。

How to set HttpResponse timeout for Android in Java

【讨论】:

cancel 方法只是将一个变量设置为 true,实际上并没有关闭 AsyncTask,它被认为是要求高的循环,然后在每次迭代中检查值 isCancelled()。事实并非如此,这样的循环不存在,因此我无法检查 isCancelled() cancel() 将尝试取消任务的执行。如果您希望任务在取消时尽快完成,那么您需要检查isCancelled,如果为真则返回。你误会了。 developer.android.com/reference/android/os/… 但是看这个:“可以通过调用cancel(boolean)随时取消任务。调用此方法将导致后续调用isCancelled()返回true。调用此方法后,onCancelled( Object), 而不是 onPostExecute(Object) 将在 doInBackground(Object[]) 返回后被调用" 我们仍然需要 doInBackground 来返回,这就是问题所在,它永远不会返回,因为它卡在 httpClient.execute() 中,我我错了吗? :S 你在你的上下文中是对的。我更新了我的答案。您可以设置连接超时。【参考方案3】:

onPause()onBackButtonPressed() 中,就您的任务致电cancel()。在doInBackground() 之后

HttpResponse httpResponse = httpClient.execute(httpPost);

检查isCanceled(),如果为真则立即返回。

当然,您仍然有运行多个任务的风险,但是由于此操作是 UI 驱动的(即由用户交互启动的任务),因此最多应该有几个同时运行,前提是超时关于 HttpClient 是合理的。


更新

一旦确定需要取消任务,您也可以关闭连接管理器。 see docs

这应该关闭套接字并立即从execute() 返回。连接管理器在您创建 DefaultHttpClient 时设置。

【讨论】:

我该如何设置超时,因为它现在没有发生,我可以等待超过一两分钟并且没有得到响应。 其实你应该只是关闭连接管理器。我添加了更新。 对不起,我只是写了一个更简单的代码来避免问题中的大量代码,我使用 https 并且想维护连接管理器,所以关闭它会让我再次握手。我想我将不得不使用超时。【参考方案4】:

我的理解是httpClient.execute()是阻塞的,所以没有运行代码来检查isCancelled()的值。而且您不希望关闭连接管理器。

这可能有点老套,但如果您在线程上调用Thread.interrupt()httpClient.execute() 被阻塞,会发生什么情况?

快速测试可以验证这一点,只需在ConnectionTask 定义中添加Thread 类型的私有实例变量,将其设置为doBackground() 顶部的Thread.currentThread(),并添加一个调用@987654329 的公共方法@就可以了。

如果你很幸运,这将导致httpClient.execute() 立即退出,并抛出异常。您可以在方法调用结束和 AsyncTask 自然结束之前抓住它并做任何您需要做的事情。

【讨论】:

好主意!我会尝试一下,然后返回结果! 它没有带来任何结果...我试着按照你说的做,`AsyncTask private Thread thisThread; @Override protected String doInBackground(String... params) thisThread = Thread.currentThread(); [...] public void cancelConnection() thisThread.interrupt(); ` 但是没有这样的中断......从AsyncTask改为Thread + Handler会有帮助吗? 哦,所以你是从另一个线程调用cancelConnection()httpClient.execute() 仍然只是坐在那里?我担心,这就是为什么我说'如果你很幸运'......从Android API文档中它并没有说execute()抛出InterruptedException,所以它没有承诺它会。由于它不尊重这一点,所以我无法阻止它坐在那里直到超时,抱歉。不过值得一试。不,我认为 Handler 不会产生任何影响,execute() 仍然会挂起 Handler 的线程,直到超时。 非常感谢您的帮助。我一直在尝试没有运气。我将不得不为 userSeven7s sais 实现 .execute() 的超时。再次感谢您;)【参考方案5】:

根据HttpClient docs,使用HttpUriRequest#abort()中止请求

1.4。中止请求

在某些情况下,由于目标服务器上的高负载或客户端发出的并发请求过多,HTTP 请求执行无法在预期的时间范围内完成。在这种情况下,可能需要提前终止请求并解除阻塞在 I/O 操作中阻塞的执行线程。 HttpClient 正在执行的 HTTP 请求可以通过调用 HttpUriRequest#abort() 方法在执行的任何阶段中止。此方法是线程安全的,可以从任何线程调用。当一个 HTTP 请求被中止时,它的执行线程——即使当前在一个 I/O 操作中被阻塞——通过抛出一个 InterruptedIOException 来保证解除阻塞

【讨论】:

正是我想要的。谢谢。【参考方案6】:

这个问题有一个快速的解决方案,我曾经在我的案例中解决过。这可能不是正确的方法,因为当您按下后应用程序会立即响应,但在后台网络操作将继续(如果您设置,则直到超时)而不阻塞应用程序:-

在新服务中执行所有网络操作,例如 NSERV。使 NSERV 扩展 Thread 类并在 run 方法中执行所有网络操作。为了使您的代码更清晰,更好地使启动 NSERV 的活动/服务也扩展 Thread 类并从它们的 run 方法启动 NSERV。

然后使用静态字段或单例从 NSERV 访问活动/服务中的变量。

前:

public class NSERV  extends Service implements Runnable
public int onStartCommand (Intent intent, int flags, int startId)
 
        Thread t = new Thread (this, "abc");
        t.start();          
        return super.onStartCommand(intent, flags, startId);            


public void run() 

//NETWORK OPERATION...   
onDestroy();


public void onDestroy() 
       
        stopSelf();
        super.onDestroy();


【讨论】:

以上是关于用户按下后取消正在进行的连接的主要内容,如果未能解决你的问题,请参考以下文章

登录按钮按下后,如何获取Tkinter entry的值并传递给SQL查询?

按下后更改Touchable / Pressable Item的背景颜色

课程设计数字秒表设计 求高手解答。

了解用户在应用内购买期间何时按下了取消按钮

如何修改UIButton按下后默认的蓝色效果

Kivy - 按下后更改按钮的颜色?