为什么Future thread在应用程序的后台不起作用?

时间:2014-10-21 17:07:49

标签: java android future callable threadpoolexecutor

今天我发现了一个奇怪的问题。我想要的是在应用程序启动后检查服务器可用性(特别是SSL检查),如果服务器关闭则显示正确的消息。此过程应在后台运行,如果服务器出现问题,用户可以导航应用程序(应用程序可以脱机工作)。

我做的很简单。在主要活动中我有

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

    // Check Internet connection

    // Check Location sensor

    // Check server accessibility
    BackendCheck backendCheck = new BackendCheck(this);
    if (!backendCheck.execute())
    {
        displayErrorDialog();
        return;
    }
}

这是BackendCheck类:

    public class BackendCheck implements Callable<Boolean>
    {
        private static final String TAG = BackendCheck.class.getSimpleName();

        // Thread sleep time
        private static final int THREAD_SLEEP = 5000;

        // Number of attempts to call an API in order to get response
        private static final int MAX_ATTEMPT = 3;

        // Current attempt
        private int counter = 0;

        // The url that should be used in order to get server response
        private String mTestUrl;

        // App mContext
        private Context mContext;

        // Server status
        private boolean mServerStatus = false;


        public BackendCheck(Context context)
        {
            this(context, "");
        }

        public BackendCheck(Context context, String url)
        {
            this.mTestUrl = url;
            this.mContext = context;
        }

        public boolean execute()
        {
            // Check #mTestUrl and use Feature API if this variable is empty
            if (TextUtils.isEmpty(mTestUrl))
            {
                mTestUrl = PassengerConstants.URL_BASE + mContext.getResources()
                        .getString(R.string.uri_feature_payments);
            }

            // Get ExecutorService from Executors utility class, thread pool size is 10
            ExecutorService executor = Executors.newFixedThreadPool(10);

            do
            {
                // Increment counter
                counter++;

                // Submit Callable tasks to be executed by thread pool
                Future<Boolean> future = executor.submit(this);

                try
                {
                    // Break Do-While loop if server responded to request (there is no error)
                    if (!future.get())
                    {
                        mServerStatus = true;
                        break;
                    }
                }
                catch (InterruptedException e)
                {
                    Logger.error(TAG, e.getMessage());
                }
                catch (ExecutionException e)
                {
                    Logger.error(TAG, e.getMessage());
                }

            } while (counter < MAX_ATTEMPT);

            // Shut down the executor service now
            executor.shutdown();

            // Return server status
            return mServerStatus;
        }

        @Override
        public Boolean call() throws Exception
        {
            // Sleep thread for a few seconds
            Thread.sleep(THREAD_SLEEP);

            try
            {
                HttpClient client = new DefaultHttpClient();
                HttpGet get = new HttpGet(mTestUrl);
                Logger.debug(TAG, "Attempt (" + counter + "), try to check => " + mTestUrl);

                HttpResponse httpResponse = client.execute(get);
                int connectionStatusCode = httpResponse.getStatusLine().getStatusCode();


           Logger.debug(TAG,
                    "Connection code: " + connectionStatusCode + " for Attempt (" + counter
                            + ") of request: " + mTestUrl);

            if (isServerError(connectionStatusCode))
            {
                return true;
            }
        }
        catch (IllegalArgumentException e)
        {
            Logger.error(TAG, e.getMessage());
        }
        catch (Exception e)
        {
            Logger.error(TAG, e.getMessage());
        }

        return false;
    }

    /**
     * Server status checker.
     *
     * @param statusCode status code of HTTP request
     * @return True if connection code is 5xx, False otherwise.
     */
    private static boolean isServerError(int statusCode)
    {
        return (statusCode >= HttpURLConnection.HTTP_INTERNAL_ERROR);
    }
}

当我启动应用程序启动屏幕时,会发生什么。然后几秒钟之后mainActivity运行(第一个代码)然后 - 因为我的服务器停机(用于测试目的) - 我有黑屏15秒(因为我将MAX_ATTEMPT设置为3并且有5秒线程睡眠)然后我和# 39;能够看到mainActivity的UI和我的错误信息。

我希望Callable&lt;&gt;应该在后台工作,我在splashScreen之后看到mainActivity没有问题(黑屏)。

你怎么想?可能是什么问题?感谢。

2 个答案:

答案 0 :(得分:1)

您似乎正在主线程中执行BackendCheck callable。

扩展Callable的类通常通过ExecutorService执行,Runnable是一个单独的线程本身,因此它在后台执行。您可能需要查看Thread界面或FutureTask,如果您想要在后台执行单独的线程返回值。调用start方法将导致类在单独的线程中执行,如文档所示:

  

当使用实现接口Runnable的对象创建线程时,启动该线程会导致在该单独执行的线程中调用该对象的run方法。

如果您需要在执行结束时返回一些数据,我强烈推荐ExecutorService,但您可能也可以使用{{3}},但我对该课程的经验较少。希望这会有所帮助。

答案 1 :(得分:1)

好的,我刚刚解决了我的问题。 &#39; njzk2&#39;是正确的。问题是future.get()正在运行或阻塞主线程。我通过做一些修改来解决这个问题。

  1. 首先,我从一个新线程调用我的execute()方法。因此整个处理将在另一个线程中完成。
  2. 我添加了新的start()方法以便运行它。
  3. BackendCheck课程中添加一个听众,并在我的活动中实施。
  4. 由于我想在服务器关闭时显示一个对话框而我在另一个线程中,然后runOnUiThread(runnable)用于在主线程中显示对话框。
  5. 这是我的完整代码供您参考。 在我的活动中:

    @Override
        protected void onStart()
        {
            super.onStart();
    
            // Check Location sensor
    
            // Check server accessibility
            BackendCheck backendCheck = new BackendCheck(this);
            backendCheck.setServerListener(new BackendCheck.BackendCheckListener()
            {
                @Override
                public void onServerIsDown()
                {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        public void run() {
                            displayErrorDialog();
                        }
                    });
                }
            });
            backendCheck.start();
        }
    

    我的BackendCheck课程:

    public class BackendCheck implements Callable<Boolean>
    {
        public interface BackendCheckListener
        {
            public void onServerIsDown();
        }
    
        private static final String TAG = BackendCheck.class.getSimpleName();
    
        // Thread sleep time
        private static final int THREAD_SLEEP = 5000;
    
        // Number of attempts to call an API in order to get response
        private static final int MAX_ATTEMPT = 3;
    
        // Current attempt
        private int counter = 0;
    
        // The url that should be used in order to get server response
        private String mTestUrl;
    
        // App mContext
        private Context mContext;
    
        // Server status
        private boolean mIsServerWorking = false;
    
        // Server listener
        private BackendCheckListener mListener;
    
    
        public BackendCheck(Context context)
        {
            this(context, "");
        }
    
        public BackendCheck(Context context, String url)
        {
            this.mTestUrl = url;
            this.mContext = context;
        }
    
        public void setServerListener (BackendCheckListener listener)
        {
            this.mListener = listener;
        }
    
        public void start()
        {
            Thread thread = new Thread()
            {
                @Override
                public void run() {
                    boolean isServerWorking = execute();
                    if(!isServerWorking)
                    {
                        mListener.onServerIsDown();
                    }
                }
            };
    
            thread.start();
        }
    
        private boolean execute()
        {
            // Check #mTestUrl and use Feature API if this variable is empty
            if (TextUtils.isEmpty(mTestUrl))
            {
                mTestUrl = PassengerConstants.URL_BASE + mContext.getResources()
                        .getString(R.string.uri_feature_payments);
            }
    
            // Get ExecutorService from Executors utility class
            ExecutorService executor = Executors.newFixedThreadPool(1);
    
            do
            {
                // Increment counter
                counter++;
    
                // Submit Callable tasks to be executed by thread pool
                Future<Boolean> future = executor.submit(this);
    
                try
                {
                    // Skip sleeping in first attempt
                   if(counter > 1)
                    {
                        // Sleep thread for a few seconds
                        Thread.sleep(THREAD_SLEEP);
                    }
    
                    // Break Do-While loop if server responded to request (there is no error)
                    if (!future.get())
                    {
                        mIsServerWorking = true;
                        break;
                    }
                }
                catch (InterruptedException e)
                {
                    Logger.error(TAG, e.getMessage());
                }
                catch (ExecutionException e)
                {
                    Logger.error(TAG, e.getMessage());
                }
    
            } while (counter < MAX_ATTEMPT);
    
            // Try to shut down the executor service now
            try
            {
                executor.shutdown();
                executor.awaitTermination(THREAD_SLEEP, TimeUnit.MILLISECONDS);
            }
            catch (InterruptedException e)
            {
                Logger.error(TAG, e.getMessage());
            }
    
            // Return server status
            return mIsServerWorking;
        }
    
        @Override
        public Boolean call() throws Exception
        {
            try
            {
                HttpClient client = new DefaultHttpClient();
                HttpGet get = new HttpGet(mTestUrl);
                Logger.debug(TAG, "Attempt (" + counter + "), try to check => " + mTestUrl);
    
                HttpResponse httpResponse = client.execute(get);
                int connectionStatusCode = httpResponse.getStatusLine().getStatusCode();
                Logger.debug(TAG,
                        "Connection code: " + connectionStatusCode + " for Attempt (" + counter
                                + ") of request: " + mTestUrl);
    
                if (isServerError(connectionStatusCode))
                {
                    return true;
                }
            }
            catch (IllegalArgumentException e)
            {
                Logger.error(TAG, e.getMessage());
            }
            catch (Exception e)
            {
                Logger.error(TAG, e.getMessage());
            }
    
            return false;
        }
    
        /**
         * Server status checker.
         *
         * @param statusCode status code of HTTP request
         * @return True if connection code is 5xx, False otherwise.
         */
        private static boolean isServerError(int statusCode)
        {
            return (statusCode >= HttpURLConnection.HTTP_INTERNAL_ERROR);
        }
    }