jsoup在kitkat上的解析速度比kitkat之前的任何东西都慢得多。我不确定它是否是art运行时,但在对一个解析方法运行了一个速度测试后,发现它大约慢了5倍,我不知道为什么。
这部分代码是在异步任务的doinbackground中运行的。

    JsoupParser parser = new JsoupParser();
    parser.setPath(String.valueOf(application.getCacheDir()));

    Collection<Section> allSections = eguide.getSectionMap().values();
    for (Section section : allSections) {
         parser.createNewAssetList();
         parser.setContent(section.color, section.name, section.text, section.slug);
         if (!TextUtils.isEmpty(section.text)) {
            section.text = parser.setWebViewStringContent();
            section.assets = parser.getAssets();
            for (Asset asset : section.assets)
                asset.heading = section.heading;
         }
    }

我很早以前就写过这个,可能效率不高,但是它设置了解析器,加载了一个section对象列表,对于每个对象,它将html提取表和图像解析为一个不同对象的列表,这些对象返回到原始section对象。
这是我的解析器类。
public class JsoupParser{

private List<Asset> assets;
private int assetCount;
private String slug,name,color,path;
private Document doc;

public JsoupParser() {
    assetCount = 0;
    assets = new ArrayList<Asset>();
}

public void setPath(String path) {
    this.path = path;
}

public void setContent(String color, String name, String text, String slug){
    this.color = color;
    this.name = name;
    this.slug = slug;
    doc = Jsoup.parse(text);
}

public void createNewAssetList(){
    assetCount = 0;
    assets = new ArrayList<Asset>();
}

public String setWebViewStringContent() {

    addScriptsAndDivTags();

    //parse images
    Elements images  = doc.select("img[src]");
    parseImages(images);

    //parse tables
    Elements tableTags = doc.select("table");
    parseTables(tableTags);

    return doc.toString();
}

private void addScriptsAndDivTags() {

    Element bodyReference = doc.select("body").first(); //grab head and body ref's
    Element headReference = doc.select("head").first();

    Element new_body = doc.createElement("body");
    //wrap content in extra div and add accodrion tag
    bodyReference.tagName("div");
    bodyReference.attr("id", "accordion");
    new_body.appendChild(bodyReference);
    headReference.after(new_body);
}

private void parseTables(Elements tableTags) {
    if (tableTags != null) {
        int count = 1;
        for (Element table : tableTags) {
            Asset item = new Asset();
            item.setContent(table.toString());
            item.setColor(color);
            item.id = (int) Math.ceil(Math.random() * 10000);
            item.isAsset=1;
            item.keywords = table.attr("keywords");
            String linkHref = table.attr("table_name");
            item.slug = "t_" + slug + " " + count ;
            if(!TextUtils.isEmpty(linkHref)){
               item.name = linkHref;
            }
            else{
               item.name ="Table-" + (assetCount + 1) + " in " + name;
            }
            // replace tables
            String inline = table.attr("inline");
            String button = ("<p>Dummy Button</p>");

            if(!TextUtils.isEmpty(inline)&& inline.contentEquals("false") || TextUtils.isEmpty(inline) )
            {
              table.replaceWith(new DataNode(button, ""));
            }
            else{
                Element div = doc.createElement("div");
                div.attr("class","inlineTableWrapper");
                div.attr("onclick", "window.location ='table://"+item.slug+"';");
                table.replaceWith(div);
                div.appendChild(table);
            }
            assets.add(item);
            assetCount++;
            count++;
        }
    }
}

private void parseImages(Elements images) {
    for (Element image : images) {
        Asset item = new Asset();

        String slug = image.attr("src");
        //remove first forward slash from slug to account for img:// protocol in image linking
        if(slug.charAt(0)=='/')
            slug = slug.substring(1,slug.length());
        image.attr("src", path +"/images/" + slug.substring(slug.lastIndexOf("/")+1, slug.length()));
        image.attr("style", "px; border:1px solid #000000;");
        String image_name = image.attr("image_name");
        if(!TextUtils.isEmpty(image_name)){
           item.name = image_name;
        }
        else{
           item.name ="Image " + (assetCount + 1) + " in " + name;
        }

        // replace tables
        String inline = image.attr("inline");

        String button = ("<p>Dummy Button</p>");
        item.setContent(image.toString()+"<br/><br/><br/><br/>");
        if(!TextUtils.isEmpty(inline)&& inline.contentEquals("false"))
        {
            image.replaceWith(new DataNode(button, ""));
        }
        else{
           image.attr("onclick", "window.location ='img://"+slug+"';");
        }

        item.keywords = image.attr("keywords");
        item.setColor(color);
        item.id = (int) Math.ceil(Math.random() * 10000);
        item.slug = slug;
        item.isAsset =2;
        assets.add(item);
        assetCount++;
    }
}

public String getName() {
    return name;
}

public List<Asset> getAssets() {
    return assets;
}
}

同样,它可能不是很有效率,但我到目前为止还无法找出为什么它需要这样一个表现在风筝。任何信息都将非常感谢。
谢谢!

最佳答案

更新日期2015年4月7日,jsoup的作者将我的建议合并到主主干中,此时检查ascii或utf编码,并跳过慢速(在android 4.4和5上)canencode()调用,所以只需更新jsoup源代码树并重新构建,或者拉入他的最新jar。
之前的评论和对问题的解释:我发现了问题所在,至少在我的应用程序中-jsoup的entities.java模块有一个escape()函数-用于所有文本节点的by element.outerhtml()调用。除此之外,它还测试每个文本节点的每个字符是否可以使用当前编码器进行编码:

 if (encoder.canEncode(c))
    accum.append(c);
 else...

canencode()调用在android kitkat和棒棒糖上非常慢。由于我的html输出只有utf-8格式,而且unicode几乎可以对任何字符进行编码,所以不需要进行此检查。我在escape()函数的开头进行了测试,从而更改了它:
boolean encIsUnicode = encoder.charset().name().toUpperCase().startsWith("UTF-");

然后,当需要测试时:
if (encIsUnicode || encoder.canEncode(c))
    accum.append(c);
else ...

现在我的应用程序工作起来就像风筝和棒棒糖上的一个符咒-以前花了10秒,现在不到1秒。我向jsoup主存储库发出了一个pull请求,并进行了一些较小的优化。不确定jsoup作者是否会合并它。如果你愿意,请检查我的叉子:
https://github.com/gregko/jsoup
如果您使用一些您事先知道的其他编码,您可以添加自己的测试(例如,查看字符是ascii还是其他什么),以避免昂贵的canencode(c)调用。
格雷戈

07-24 09:48
查看更多