Android Jsoup Parser在kitkat上非常慢

时间:2014-11-13 16:41:15

标签: android performance parsing jsoup android-4.4-kitkat

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提取表和图像到一个不同对象的列表,这些对象返回到原节对象..

这是我的解析器类。

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;
}
}

再次它可能效率不高但我到目前为止还无法找出为什么它会对kitkat产生如此大的影响。任何信息都会非常值得赞赏。 谢谢!

2 个答案:

答案 0 :(得分:3)

更新2015年4月7日 jsoup的作者将我的建议纳入主干,此时检查ASCII或UTF编码并跳过慢速(在Android 4.4和5上)canEncode( )调用,所以只需更新你的jsoup源代码树并重新构建,或者拉出他最新的jar。

此问题的早期评论和解释:我发现问题是什么,至少在我的应用中 - jsoup的Entities.java模块有一个escape()函数 - 例如,使用通过Element.outerHtml()调用所有文本节点。除其他外,它测试每个文本节点的每个字符是否可以使用当前编码器进行编码:

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

在Android KitKat和Lollipop上,canEncode()调用非常慢。由于我的HTML输出仅为UTF-8,并且Unicode几乎可以编码任何字符,因此不需要进行此检查。我通过在escape()函数的开头测试来改变它:

boolean encIsUnicode = encoder.charset().name().toUpperCase().startsWith("UTF-");

然后,当需要测试时:

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

现在我的应用程序就像KitKat和Lollipop上的魅力一样 - 之前花了10秒钟,现在只需不到1秒。我通过这个更改和一些较小的优化向主jsoup存储库发出了一个pull请求。不确定jsoup作者是否会合并它。如果您愿意,请查看我的前叉:

https://github.com/gregko/jsoup

如果你使用事先知道的其他编码,你可以添加自己的测试(例如,查看字符是ASCII还是其他),以避免代价高昂的canEncode(c)调用。

格雷格

答案 1 :(得分:0)

你确实使用了很多字符串连接(这可能是大量数据的杀手)

item.name ="Table-" + (assetCount + 1) + " in " + name;

根据这篇文章:Is it always a bad idea to use + to concatenate strings - 你应该避免在循环中进行仲裁 - 你的代码就是这种情况..怎么样:

item.name = String.format(“%s中的表格%s”,assetCount + 1,名称);