高效循环避免多次垃圾回收调用Android



我有一个Android应用程序,允许用户记录数据(如加速度计、纬度、经度等)。这些字段共有9个,用户最多可以记录10分钟(每个字段3000条记录)。因此,总共可以收集27000个数据点。用户还可以拍摄图片和视频来上传SD卡。

当用户完成数据收集(或10分钟结束)时,数据将存储在字符串中,该字符串稍后作为.csv文件上传到SD卡。然而,问题是,由于大量的垃圾收集(似乎每秒大约5次收集!),永远需要才能将数据附加到字符串中。附加开始得很快,但随着数据的添加,速度似乎越来越慢。

这是导致滞后的循环:

           for( i = 0 ; i < len2 ; i++ ) {
                data += accelX[i] + ", " + accelY[i] + ", " + accelZ[i] + 
                ", " + accelT[i] + ", " + latitu[i] + ", " + 
                longit[i] + ", " + orient[i] + ", " + 
                magneX[i] + ", " + magneY[i] + ", " + magneZ[i] + 
                ", " + millis[i] + "n";
                partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
                dia.setProgress((int) partialProg);
            }

data只是一个字符串,没有任何内容被new调用,所以我不确定为什么GC被如此频繁地调用。我的问题是:这里的问题是什么,和/或我如何提高效率?

您正在创建许多对象。每次使用operator+时,实际上都会创建一个新对象,该对象是可扩展的(如果多次重复)

您可以使用StringBuilder并附加到它,然后在完成后创建String,从而提高它的效率。

例如:

sb.append(accelX[i]).append(',').append(accelY[i]).append(',').append(accelZ[i]);

[其中sb是StringBuilder的实例]

您可以使用StringBuilder来连接数据:

StringBuilder sb = new StringBuilder();
for( i = 0 ; i < len2 ; i++ ) {
    sb.append(accelX[i]).append(", ");
    sb.append(accelY[i]).append(", ");
    sb.append(accelZ[i]).append(", ");
    sb.append(accelT[i]).append(", ");
    sb.append(latitu[i]).append(", ");
    sb.append(longit[i]).append(", ");
    sb.append(orient[i]).append(", ");
    sb.append(magneX[i]).append(", ");
    sb.append(magneY[i]).append(", ");
    sb.append(magneZ[i]).append(", ");
    sb.append(millis[i]).append("n");
}

StringBuilder天生就可以更快地构建长字符串。

它还避免了将太多String对象分配到堆中;因为每个+=操作符都在创建一个新的String对象,而不是修改最后一个。这反过来又导致了大量的GC调用来清理所有冗余的String对象。另请参阅:http://chaoticjava.com/posts/stringbuilder-vs-string/

正如马塞洛所指出的;你可能还会发现,在低规格的Android设备上,处理内存中的大量数据可能会出现问题,此时你应该考虑每X次迭代将StringBuilder的内容附加到一个临时文件中,以保持低占用空间。在这个过程结束时,您可以将文件流式传输到计划的目的地,或者根据需要将文件的片段读取回内存。

您可以做的一个改进是,使用StringBuffer来构造数据,这样您就可以避免String构造和串联操作。例如:

StringBuffer buff = new StringBuffer();
buff.append(accelX[i]).append(...).apend(...)
字符串上的

+运算符实际上使用StringBuilder,因此每个循环至少执行两次分配(当调用.toString()将结果分配给data时,新的StringBuilderStringBuilder会创建一个字符串)。解决这个问题的最佳方法是在循环前面创建一个StringBuilder

StringBuilder buf = new StringBuilder();
String sep = ", ";
for( i = 0 ; i < len2 ; i++ ) {
    buf.append(accelX[i]).append(sep).append(accelY[i]).append(sep);
    buf.append(accelZ[i]).append(sep).append(accelT[i]).append(sep);
    buf.append(latitu[i]).append(sep).append(longit[i]).append(sep);
    buf.append(magneX[i]).append(sep).append(magneY[i]).append(sep);
    buf.append(magneZ[i]).append(sep).append(millis[i]).append('n');
    partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
    dia.setProgress((int) partialProg);
}

最新更新