Jsoup.parse takes 10x more time on 19+ android devices - android

For some reason using Jsoup.parse takes 10x more time on kitkat devices than on older devices, at first I thought it was related to ART runtime, but changing back to dalvik didn't help
Here is the code I'm using:
downloadedHtml = NetworkHelper.downloadString("https://en.m.wikipedia.org/wiki/Dusseldorf");
AppLog.i("Downloaded data, Jsoup is parsing the html");
hDoc = Jsoup.parse(downloadedHtml);
Element htmlElement = hDoc.select("html").first();
String langCode = htmlElement.attributes().get("lang");
ArticleInfo articleInfo = new ArticleInfo(getWikiLanguage(langCode), langCode, href);
article = new Article(articleInfo, href);
String title = hDoc.getElementById("section_0").text();
article.set_title(title);
Document documentNode = hDoc.ownerDocument();
Elements contents = documentNode.getElementsByClass("content");
if (contents == null || contents.isEmpty())
throw new IllegalArgumentException("content");
Element content = contents.first();
Elements imgElements = content.select("img");
Element htmlNode;
for (int i = 0; i < imgElements.size(); i++)
{
htmlNode = imgElements.get(i);
if (!htmlNode.hasAttr("src"))
continue;
String src = htmlNode.attr("src");
if (src.startsWith("//"))
htmlNode.attr("src", String.format("http:%s", src));
//else
//throw new UnsupportedOperationException();
}
//get section headings
Elements headlines = documentNode.getElementsByClass("mw-headline");
if (headlines != null)
{
Element headline;
for (int i = 0; i < headlines.size(); i++)
{
headline = headlines.get(i);
String headline_link = headline.id();
String headline_title = headline.text();
SectionHeadline sectionHeadline = new SectionHeadline(headline_title, headline_link);
article.get_sectionHeadlines().add(sectionHeadline);
}
}
article.set_html(content.outerHtml());
//get languages
//language list
Element languageSection = content.getElementById("mw-mf-language-section");
if (languageSection != null)
{
Elements languageLinks = languageSection.select("li");
Element languageLink;
for (int i = 0; i < languageLinks.size(); i++)
{
languageLink = languageLinks.get(i);
Element link = null;
Elements ls = languageLink.select("a");
if (ls == null || ls.size() == 0)
continue;
link = ls.first();
if (!link.hasAttr("href"))
continue;
String linkHref = link.attr("href");
if (linkHref != null && link.text() != null)
{
String languageCode = link.attr("lang");
if (linkHref.startsWith("//"))
linkHref = String.format("http:%s", linkHref);
ArticleInfo languageInfo = new ArticleInfo(getWikiLanguage(languageCode), languageCode, linkHref);
if (languageInfo.get_language() == "Unknown")
continue;
article.get_languages().add(languageInfo);
}
}
}
Any ideas what the problem may be?

The code in the question selects a portion of the document, saves it to a variable, selects a portion of that variable, saves it to a new variable, and so on. Another possible implementation is to use the selector syntax more heavily to select only the elements which are needed, and not save these intermediate steps in new objects.
The code below executed on my machine in 2 seconds. A similar excerpt from above executed in about 4 seconds. Subsequent timings were much closer, differing by approximately 50ms, so take that with a grain of salt.
I don't know if there is a performance issue in kitkat. You may find it helpful to add timers to your kitkat and dalvik versions to isolate if and where performance bottlenecks are present.
Here's my code:
long start = System.currentTimeMillis();
Document hDoc = Jsoup.
connect("https://en.m.wikipedia.org/wiki/Dusseldorf").
userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17").
get();
//select the first html element, then take the value of the lang attribute
String langCode = hDoc.select("html:eq(0)").attr("lang");
String title = hDoc.getElementById("section_0").text();
Document documentNode = hDoc.ownerDocument();
//select all the image elements having the attribute src which are
//descended from the first element with the content class
Elements imgElementsHavingSrcAttr = documentNode.select("*.content:eq(0) img[src]");
Element htmlNode;
//for each img element
for (Element img : imgElementsHavingSrcAttr)
{
htmlNode = img;
String src = img.attr("src");
if (src.startsWith("//"))
{
htmlNode.attr("src", String.format("http:%s", src));
}
}
System.out.println("Function took " + (System.currentTimeMillis()-start) + "ms");

Related

Compare a set of numbers in string

I am trying to make somekind of version checker for my application.
The idea is to compare the numbers from 2 strings and if 1 set of numbers is bigger then the other a new version has been found.
oldString = 360 some - File v1.52.876 [build 2546]
newString = 360 some - File v1.53.421 [build 2687]
What I need is to compare the set numbers after the 'v' in both strings as there can also be numbers (360) in front of the file, as shown in above example.
Below method checks an arraylist (loadTrackedItems) which contains the files to be checked agains the newly received item (checkItemTrack).
But I am having trouble getting the correct numbers.
Is there a better way to do this?, could somebody be so kind and help a bit.
Thank you in advance.
public static boolean newTrackedVersion(String checkItemTrack) {
final List<String> tracking = new ArrayList<String>(loadTrackedItems);
boolean supported = false;
for (final String u : tracking) {
if (checkItemTrack.contains(u)) {
supported = true;
// get the index of the last 'v' character
int trackindex = checkItemTrack.lastIndexOf("v");
String newItem = checkItemTrack.replaceAll("[a-zA-Z]", "").replace("\\s+", "")
.replaceAll("[-\\[\\]^/,'*:.!><~##$%+=?|\"\\\\()]+", "");
String inList = u.replaceAll("[a-zA-Z]", "").replace("\\s+", "")
.replaceAll("[-\\[\\]^/,'*:.!><~##$%+=?|\"\\\\()]+", "");
long newTrack = Long.parseLong(newItem.trim());
long inTrackList = Long.parseLong(inList.trim());
if (newTrack > inTrackList) {
//Toast.makeText(context,"New version found: " + checkItemTrack, Toast.LENGTH_LONG).show();
Log.w("NEW VERSION ", checkItemTrack);
Log.w("OLD VERSION ", u);
}
break;
}
}
return supported;
}
if you receive only two strings to compare this solution will work try it.
String oldString = "360 some - File v1.52.876 [build 2546]";
String newString = "360 some - File v1.53.421 [build 2687]";
String oldTemp = oldString.substring(oldString.indexOf('v'), oldString.indexOf('[')).trim();
String newTemp = newString.substring(newString.indexOf('v'), newString.indexOf('[')).trim();
int res = newTemp.compareTo(oldTemp);
if(res == 1){
//newString is higher
}else if(res == 0){
//both are same
}else if(res == -1){
//oldString is higher
}

Realm - Do not update if property equals

I am using realm for Android. I have the following code and it works but I was wondering if it is the best way to go about updating objects and if it would cause any performance issues.
Currently, I do not want to update an existing object if the status is set to processing.
List<WorkOrderObject> woList = new ArrayList<>();
for (int i = 0; i < openWorkOrders.size(); i++) {
if (!visnetawrap.isUserLoggedIn) {
return;
}
WorkOrderObject wo = visnetawrap.gsonClient.fromJson(openWorkOrders.get(i).toString(), WorkOrderObject.class);
WorkOrderObject currWO = realmThread.where(WorkOrderObject.class).equalTo("id", wo.getOrderRawId()).findFirst();
if (currWO != null) {
if (currWO.getOrderStatus().equals("Processing")) {
continue;
}
}
issueDateTime = AppUtils.formatTimestampToDateTime(wo.getOrderIssueDate());
issueDateString = issueDateTime.toLocalDateTime().toString("MM/dd/yyyy", Locale.US);
dueDateTime = AppUtils.formatTimestampToDateTime(wo.getOrderDueDate());
dueDateString = dueDateTime.toLocalDateTime().toString("MM/dd/yyyy", Locale.US);
if (!issueDateString.equals("") && !issueDateString.equals("00/00/0000") && issueDateTime.getYear() >= now.getYear() && !dueDateString.equals("") && !dueDateString.equals("00/00/0000") && dueDateTime.getYear() >= now.getYear()) {
//Log.d("dueDate", dueDateString);
woList.add(wo);
}
}
realmThread.beginTransaction();
realmThread.copyToRealmOrUpdate(woList);
realmThread.commitTransaction();
I think basically it is the same.
Since you are worried about performance here are ways you can improve.
private static String PROCESSING = "Processing";
private static String DATE_FORMAT = "MM/dd/yyyy";
private static String EMPTY_DATE = "00/00/0000";
public void betterMethod() {
List<WorkOrderObject> woList = new ArrayList<>(openWorkOrders.size());
//I think this code doesnot need to be inside loop.
if (!visnetawrap.isUserLoggedIn) {
return;
}
for (int i = 0, j = openWorkOrders.size(); i < j; i++) {
//Since you are using gson there are ways to convert JsonArray to list directly which is a better way than this
WorkOrderObject wo = visnetawrap.gsonClient.fromJson(openWorkOrders.get(i).toString(), WorkOrderObject.class);
WorkOrderObject currWO = realmThread.where(WorkOrderObject.class).equalTo("id", wo.getOrderRawId()).findFirst();
if (currWO != null && currWO.getOrderStatus().equals(PROCESSING)) { //Its cleanar way
continue;
}
issueDateTime = AppUtils.formatTimestampToDateTime(wo.getOrderIssueDate());
issueDateString = issueDateTime.toLocalDateTime().toString(DATE_FORMAT, Locale.US);
dueDateTime = AppUtils.formatTimestampToDateTime(wo.getOrderDueDate());
dueDateString = dueDateTime.toLocalDateTime().toString(DATE_FORMAT, Locale.US);
//I assume you have stripped out code where it needs string
//You can use TextUtils.isEmpty() or issueDateString.isEmpty() ,
// issueDateString.equals("") does is creates new String which is empty and compares issueDateString with it while above methods just check the
//length of string
if (!TextUtils.isEmpty(issueDateString) && !issueDateString.equals(EMPTY_DATE) && issueDateTime.getYear() >= now.getYear() && !TextUtils.isEmpty(dueDateString) && !dueDateString.equals(EMPTY_DATE) && dueDateTime.getYear() >= now.getYear()) {
//Log.d("dueDate", dueDateString);
woList.add(wo);
}
}
if (!woList.isEmpty()) {
realmThread.beginTransaction();
realmThread.copyToRealmOrUpdate(woList);
realmThread.commitTransaction();
}
}
For loop can be very large so conditional statement like currWO.getOrderStatus().equals("Processing") will create an new string and compares. It's better to initialize the string before and pass as above.
Converting JsonArray to List
Why instantiating arrays like new ArrayList<>(openWorkOrders.size()) and using for loop with list like for (int i = 0, j = openWorkOrders.size(); i < j; i++) {}
Streamlining Android Apps: Eliminating Code Overhead by Jake Wharton

Comparing two strings in android [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Comparing two identical strings with == returns false
I am having real hard time with solving this code. This might look silly but I am not able to figure out what is happening. I am parsing a JSONArray(No big deal!) I am traversing the array with a for loop. I want to break the loop when the user input and the value matches. Here is my code
String regi = null;
JSONObject studentObject = null;
try {
JSONArray returned = test.getInternetData();
int i;
for (i = 0; i < returned.length(); i++) {
studentObject = returned.getJSONObject(i);
regi = studentObject.getString("REGISTRATION_NO");
if (regi == reg) {
name = studentObject.getString("STUDENT_NAME");
break;
}
}
course = studentObject.getString("COURSE_CODE");
Log.d("Details", name + course + regi + i);//Out put: nullGraduate081018394:name - null
//course: Graduate (same for all), regi: last registration number,
//i: giving totalnumber of objects
As per my knowledge the loop should stop when it finds a match. The COURSE_CODE will be corresponding to the student. Am I missing something?
Please note: The function getInternetData() is returning the whole JSON Array. The loop is completely traversing every object.
Strings cannot be compared with == in Java. You have to use string1.equals(string2).
Use regi.equals(reg) or regi.contentEquals(reg) instead of == and you will be fine :-)
use regi.contentEquals(reg) or !regi.contentEquals(reg) for comparison
you should use regi.contentEquals(reg)
try using this
JSONArray returned = test.getInternetData();
int i;
for (i = 0; i < returned.length(); i++) {
// added the below line
studentObject = new JsonObject();
studentObject = returned.getJSONObject(i);
regi = studentObject.getString("REGISTRATION_NO");
if (regi.equals(reg)) {
name = studentObject.getString("STUDENT_NAME");
break;
}
}
instead of just
JSONArray returned = test.getInternetData();
int i;
for (i = 0; i < returned.length(); i++) {
studentObject = returned.getJSONObject(i);
regi = studentObject.getString("REGISTRATION_NO");
if (regi == reg) {
name = studentObject.getString("STUDENT_NAME");
break;
}
}

Android 4.0 ice cream sandwich Parser Errors

I have made a application which reads information from an Api.
Link : http://api.amp.active.com/camping/campground/details?contractCode=CO&parkId=50032&api_key=2chxq68efd4azrpygt5hh2qu
Following is my code :
NodeList list = element.getElementsByTagName("detailDescription");
Log.i("ZealDeveloper","I M In detail "+list.getLength());
if(list != null && list.getLength() > 0){
for(int i = 0; i < list.getLength(); i++){
entry = (Element) list.item(i);
description = entry.getAttribute("description");
drivingDirection = entry.getAttribute("drivingDirection");
latitude=entry.getAttribute("latitude");
longitude=entry.getAttribute("longitude");
}
}
NodeList list1 = element.getElementsByTagName("amenity");
Log.i("ZealDeveloper","I M In 2 "+list1.getLength());
if(list1 != null && list1.getLength() > 0){
for(int i = 0; i < list1.getLength(); i++){
entry = (Element) list1.item(i);
nameAmenity = entry.getAttribute("name");
listAmenity.add(nameAmenity);
}
arrAmenity = listAmenity.toArray(new String[listAmenity.size()]);
StringBuilder builder = new StringBuilder();
for ( int i = 0; i < arrAmenity.length; i++ ){
builder.append(arrAmenity[i]+"\n");
}
txtAmenity.setText(builder);
}
#
I am Getting list.getLength() as 0(was getting 1 in pervious versions of android) , so the parser this condition. For amenity tag i m getting the desired list size.
The only reason I can think of is that "detailDescription" is already root of the document so probably element is the tag you are seeking for. It doesn't have any children with the name "detailDescription" so getElementsByTagName("detailDescription") returns empty list. So change first half of your code as follows:
Log.i("ZealDeveloper","I M In detail " + element.getTagName());
if(element.getTagName().equalsIgnoreCase("detailDescription")) {
description = element.getAttribute("description");
drivingDirection = element.getAttribute("drivingDirection");
latitude = element.getAttribute("latitude");
longitude = element.getAttribute("longitude");
}
/* rest of your code...*/
I had the same issue. I switch from DOM to XPath. More here

Comparing string array with string and posting my result to main.xlm

I am having trouble with understanding how to compare strings in Java for Android. I have written code to do this in JavaScript and Palm but am new to Java and am a little confused. Case in point, I am trying to modify the example on the Android Developers site for SpinnerActivity (http://developer.android.com/resources/samples/Spinner/src/com/android/example/spinner/SpinnerActivity.html). In my application I am looking at pipe sizes in the spinner not planets. When the user picks a pipe size I want to reference an array of pipe sizes and be able to pick other parameters associated with that pipe size like the outside diameter (OD) of the pipe. I have modified the above sample code and added and array for the pipe sizes and the OD sizes. I then try to compare what the user picked in the pipe sizes spinner with my pipe sizes array and use the number of the array that matches to pick the associated OD. There is something wrong with the way I am trying to make this comparision. I set both of these values as stings but they never seem to find one another.
HelloSpinner1.java section I have changed is:
public void onItemSelected(AdapterView<?> parent, View v, int pos, long row) {
HelloSpinner1.this.mPos = pos;
HelloSpinner1.this.mSelection = parent.getItemAtPosition(pos).toString();
/*
* Set the value of the text field in the UI
*/
TextView resultText = (TextView)findViewById(R.id.SpinnerResult);
resultText.setText(HelloSpinner1.this.mSelection);
String[] OD; // array of pipe ODs
OD = new String[30]; // allocates memory for 30 floating point numbers
OD[0] = "0.405";
OD[1] = "0.540";
OD[2] = "0.675";
OD[3] = "0.840";
OD[4] = "1.050";
OD[5] = "1.315";
OD[6] = "1.660";
OD[7] = "1.9";
OD[8] = "2.375";
OD[9] = "2.875";
OD[10] = "3.5";
OD[11] = "4";
OD[12] = "4.5";
OD[13] = "5.563";
OD[14] = "6.625";
OD[15] = "8.625";
OD[16] = "10.750";
OD[17] = "12.75";
OD[18] = "14";
OD[19] = "16";
OD[20] = "18";
OD[21] = "20";
OD[22] = "22";
OD[23] = "24";
OD[24] = "26";
OD[25] = "28";
OD[26] = "30";
OD[27] = "32";
OD[28] = "34";
OD[29] = "36";
String [] Size;
Size = new String [30];
Size[0] = "1/8";
Size[1] = "1/4";
Size[2] = "3/8";
Size[3] = "1/2";
Size[4] = "3/4";
Size[5] = "1";
Size[6] = "1-1/4";
Size[7] = "1-1/2";
Size[8] = "2";
Size[9] = "2-1/2";
Size[10] = "3";
Size[11] = "3-1/2";
Size[12] = "4";
Size[13] = "5";
Size[14] = "6";
Size[15] = "8";
Size[16] = "10";
Size[17] = "12";
Size[18] = "14";
Size[19] = "16";
Size[20] = "18";
Size[21] = "20";
Size[22] = "22";
Size[23] = "24";
Size[24] = "26";
Size[25] = "28";
Size[26] = "30";
Size[27] = "32";
Size[28] = "34";
Size[29] = "36";
String ODSize;
for (int i = 0; i <= 29; i++){
if (Size.equals("HelloSpinner1.this.mSelection")) {
ODSize = OD[i];
break;
}
}
}
The associated strings.xml rorm the android site with slight modifications is:
Pipe and Tube
1/8
1/4
3/8
3/4
1
1-1/4
1-1/2
2
2-1/2
3
3-1/2
4
5
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
Select a Pipe Size
Just a quick note, but I see two things here:
1) You have "HelloSpinner1.this.mSelection" in quotes. Inside .equals, that is comparing your variable Size directly to that string ... not the value stored in that object. For example, you're asking: "1/8" ?= "HelloSpinner1.this.mSelection" ... not, "1/8" ?= "1/4" in this case. That might be most of your problem here.
2) You could just use the position of the spinner inside your Listener method. That gives you the position of the selection on the spinner. If you aren't modifying those values, you would already know the index into your array. If you were modifying them, /then/ you could do a string comparison (or concurrently modify your array to keep them up to date).
You might also want to check what you're comparing against once you eliminate the quotes problem. A very simple way to do that would be to declare a String for each value, then run it in the debugger or log the output.
Lastly, as personal preference, I don't like to store long arrays which aren't going to change in the xml file. Just code that up as an array which doesn't have to be interpreted later. That'll give you the array access directly, and speed up execution some.

Categories

Resources