在 Android 应用中加载 PHP 生成的数据时可能存在的竞争条件


Possible race condition when loading data generated by PHP in an Android app

我正在编写一个处理销售数据的应用程序。 数据将输出到我的应用可以访问和读取的专用网页。 但是,我的应用并不总是接收到它应该接收的所有数据。 (如果我在桌面浏览器中打开网页,数据始终是完整的) 我怀疑这可能是由于竞争条件,即应用程序在网页完全加载之前尝试读取网页上的数据。 下面是从网页读取数据的代码:

     try {
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(120000 /* milliseconds, or 2 minutes */);
            conn.setConnectTimeout(120000 /* milliseconds, or 2 minutes */);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            // Starts the query
            conn.connect();
            int response = conn.getResponseCode();
            Log.d(DEBUG_TAG, "The response is: " + response);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            is = conn.getInputStream();
            // Convert the InputStream into a string
            String contentAsString = readIt(is, len);
            return contentAsString;
            // Makes sure that the InputStream is closed after the app is
            // finished using it.
        } finally {
            if (is != null) {
                is.close();
            } 
        }

如您所见,我添加了一个 Thread.sleep() 来给页面加载时间,这有所帮助,但尚未完全解决问题。

我该怎么做才能确保应用等到网页完全加载后再尝试从中读取数据?

编辑:这是我的readit函数:

public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");        
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    }
编辑

2:我已经编辑了 readIt 以循环,直到在缓冲区中找到某个字符序列。 这有效,但如果数据加载速度不够快,那么应用程序就会崩溃,因为Android认为存在无限循环。 以下是编辑后的代码:

    public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        boolean xyz = false;
        Reader reader = null;
        char[] buffer = null;
        while (xyz == false){
            reader = new InputStreamReader(stream, "UTF-8");        
            buffer = new char[len];
            reader.read(buffer);
            String test = new String(buffer);
            System.out.println(test);
            if (test.contains("@@@")){
                xyz = true;
            }
        }
        return new String(buffer);
    }

好吧,这是我发现有效的方法。 问题是,即使流未完全加载,读取器也会从流中读取。 它还将从流中读取 1000000 个字符,即使没有那么多要读取的字符。 这导致读者读取任何可用的字符,然后用 UTF-8 未知符号 ( ) 填充 1000000 个字符的其余部分。 您在此处看到的代码在流上循环,每次读取自上次循环以来加载的任何内容,并丢弃字符。 它确实要求你正在阅读的任何内容都不包含任何非 UTF-8 字符,因为它会在它找到的第一个字符串上拆分字符串,但如果你用 UTF-8 读取,无论如何都应该确保这一点。 它还要求您在数据末尾放置一个不常见的字符序列以标记结尾。

// This converts the data stream into a String that can be manipulated.
    public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        boolean isDone = false;     // Boolean to keep track of whether the data is loaded or not.
        Reader reader = new InputStreamReader(stream, "UTF-8");     // Input stream reader
        char[] buffer = null;       // Character array to hold the data from the stream
        String readDump = null;     // String to hold data converted from the character array, which will most likely contain junk characters.
        String splitDump [] = null;     // String array that holds the valid data and junk from readDump after they've been separated.
        String dataHolder = "";     // Output string that holds the valid data
        // While the final characters have not been read (@@@)
        while (isDone == false){        
            buffer = new char[len];
            reader.read(buffer);        // Read data from the stream
            readDump = new String(buffer);      // Turn it into a string
            splitDump = readDump.split("[''x00''x08''x0B''x0C''x0E-''x1F]", 2);     //Split the string into valid data and junk.
            dataHolder += splitDump[0];     // Add the valid data to the output string
            System.out.println(dataHolder);     //Debug
            if (dataHolder.contains("@@@")){
                // If the output string has the final characters in it, then we are done.
                isDone = true;
            }
        }
        return dataHolder;
    }