我的 Android 应用程序运行非常缓慢

My Android App is working very slowly

我的应用程序从加速度计读取数据并绘制实时图表。我在模拟器上 运行 它似乎工作正常但是当 运行 在真正的 android 设备上它 响应非常慢 ,设备是三星 GT-S6500 Android 2.3.6 (API 10).

我的应用程序中有一个启动和停止按钮,两者在模拟器上似乎都可以正常工作,但是当应用程序在上面提到的设备上 运行 启动按钮 运行 很好,但真实的时间图移动非常缓慢并且挂起停止按钮响应时间变得非常大。 有什么建议吗?

代码如下: MainActivity.java

package com.mycompany.falldetect;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;


import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;


import com.telerik.widget.chart.engine.databinding.DataPointBinding;
import com.telerik.widget.chart.visualization.cartesianChart.RadCartesianChartView;
import com.telerik.widget.chart.visualization.cartesianChart.axes.CategoricalAxis;
import com.telerik.widget.chart.visualization.cartesianChart.axes.LinearAxis;
import com.telerik.widget.chart.visualization.cartesianChart.series.categorical.LineSeries;

import static android.os.Environment.getExternalStorageDirectory;


public class MainActivity extends Activity implements SensorEventListener,
    OnClickListener {
private SensorManager SensorManager;
private Sensor mAccelerometer;
private FileWriter writer;
private Button btnStart, btnStop;
private boolean started = false;
private Queue<Data> seismicActivityBuffer;
private List<Data> allSeismicActivity;
private ViewGroup chartContainer;
private RadCartesianChartView chart;
private int bufferSize;
String f = "";
int m = 0;
double prev_xyz = 9.0;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    SensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

    mAccelerometer = SensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    this.seismicActivityBuffer = new LinkedList<Data>();
    this.allSeismicActivity = new ArrayList<Data>();
    this.bufferSize = 100;
    // Adding points to fill the screen at initial state.
    for (int i = -this.bufferSize/1000; i < 0; i++) {
        this.seismicActivityBuffer.add(new Data(i, 0, 0, 0));
    }

    this.chartContainer = (ViewGroup) findViewById(R.id.chart_container);

    this.chart = createChart(this.seismicActivityBuffer);
    this.chartContainer.addView(this.chart);

    btnStart = (Button) findViewById(R.id.btnStart);
    btnStop = (Button) findViewById(R.id.btnStop);

    btnStart.setOnClickListener(this);
    btnStop.setOnClickListener(this);

    btnStart.setEnabled(true);
    btnStop.setEnabled(false);


}

public void onStartClick(View view) {
    SensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}

public void onStopClick(View view) {
    SensorManager.unregisterListener(this);
}

protected void onResume() {
    super.onResume();
    this.SensorManager.registerListener(this, this.mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);

}

protected void onPause() {
    super.onPause();

    if (started) {
        SensorManager.unregisterListener(this);
    }


}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

@Override
public void onSensorChanged(SensorEvent event) {
    if (started) {
        double x = event.values[0];
        double y = event.values[1];
        double z = event.values[2];
        long timestamp = System.nanoTime();
        Data point = new Data(timestamp, x, y, z);


        //System.out.print("sesor: " + point.getTimestamp() + " " + point.getX() + " " + point.getY() + " " + point.getZ());

       // double xyz = Math.round(Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)));
        // String s=timestamp+" "+x+" "+y+" "+z+" "+xyz+" \n";
        // f+=s;

        if (this.seismicActivityBuffer.size() > this.bufferSize) {
            this.seismicActivityBuffer.remove();
        }

        this.seismicActivityBuffer.add(point);
        //this.allSeismicActivity.add(point);

        this.chartContainer.removeAllViews();
        this.chart = createChart(seismicActivityBuffer);
        this.chartContainer.addView(chart);
        /*if(xyz!=9 && xyz!=10){
            String s=timestamp+" "+x+" "+y+" "+z+" "+xyz+" \n";
            f+=s;
        }*/
        //System.out.println("xyz: "+xyz);


        /*if(xyz <= 5){
            m++;
            prev_xyz=xyz;

        }
        if(Math.abs(prev_xyz-xyz) >= 10 ){
            Toast.makeText(getBaseContext(),"FALL DETECTED!",Toast.LENGTH_LONG).show();
            f+="FALL DETECTED!\n";
            prev_xyz=9;
            fall=true;
            m=0;
        }*/


    }

}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btnStart:
            btnStart.setEnabled(false);
            btnStop.setEnabled(true);

            // save prev data if available
            started = true;

            this.chart = createChart(this.allSeismicActivity);
            this.chartContainer.addView(this.chart);

            break;
        case R.id.btnStop:
            stop();

            break;
        default:
            break;
    }

}


private void stop() {
    btnStart.setEnabled(true);
    btnStop.setEnabled(false);
    started = false;
    SensorManager.unregisterListener(this);
    chartContainer.removeAllViews();
    //openChart();
    //createExternalStorageFile(f);
}

private RadCartesianChartView createChart(Iterable<Data> dataPoints) {
    RadCartesianChartView chart = new RadCartesianChartView(this);


    // create category binding with the X coordinate of the accelerometer point
    DataPointBinding categoryBinding = new DataPointBinding() {
        @Override
        public Object getValue(Object o) throws IllegalArgumentException {
            return ((Data) o).getTimestamp();
        }
    };


    // create value binding with the X coordinate of the accelerometer point
    DataPointBinding valueBinding_x = new DataPointBinding() {
        @Override
        public Object getValue(Object o) throws IllegalArgumentException {
            return ((Data) o).getX();
        }
    };
    DataPointBinding valueBinding_y = new DataPointBinding() {
        @Override
        public Object getValue(Object o) throws IllegalArgumentException {
            return ((Data) o).getY();
        }
    };
    DataPointBinding valueBinding_z = new DataPointBinding() {
        @Override
        public Object getValue(Object o) throws IllegalArgumentException {
            return ((Data) o).getZ();
        }
    };
    LineSeries series = new LineSeries();

    series.setCategoryBinding(categoryBinding);
    series.setValueBinding(valueBinding_x);
    chart.getSeries().add(series);
    series.setData(dataPoints);
    LineSeries series2 = new LineSeries();
    series2.setCategoryBinding(categoryBinding);
    series2.setValueBinding(valueBinding_y);
    chart.getSeries().add(series2);
    series2.setData(dataPoints);
    LineSeries series3 = new LineSeries();
    series3.setCategoryBinding(categoryBinding);
    series3.setValueBinding(valueBinding_z);
    chart.getSeries().add(series3);
    series3.setData(dataPoints);

    // feed the data to the chart


    // configure the vertical axis
    LinearAxis vAxis = new LinearAxis();
    // The maximum value of the accelerometer is 20 and the minimum -20, so give a bonus 10 to the vertical axis.
    vAxis.setMaximum(20);
    vAxis.setMinimum(-20);
    chart.setVerticalAxis(vAxis);

    // configure the horizontal axis
    CategoricalAxis hAxis = new CategoricalAxis();
    hAxis.setShowLabels(false);
    chart.setHorizontalAxis(hAxis);

    return chart;
}


void createExternalStorageFile(String a) {
    // Create a path where we will place our private file on external
    // storage.
    File file = new File(getExternalStorageDirectory(), "DemoFile.txt");

    try {
        // Very simple code to copy a picture from the application's
        // resource into the external file.  Note that this code does
        // no error checking, and assumes the picture is small (does not
        // try to copy it in chunks).  Note that if external storage is
        // not currently mounted this will silently fail.

        OutputStream os = new FileOutputStream(file);

        os.write(a.getBytes());
        os.close();
    } catch (IOException e) {
        // Unable to create file, likely because external storage is
        // not currently mounted.
        Log.w("ExternalStorage", "Error writing " + file, e);
    }
}


}

这里是logcat:

07-01 12:15:06.778 3542-3542/? I/art︰ 不延迟启用 -Xcheck:jni (已经开启)

07-01 12:15:07.284 3542-3554/com.mycompany.falldetect I/art:后台部分并发标记清除GC释放137(46KB)个AllocSpace对象,0(0B)个LOS对象, 43% 免费, 663KB/1175KB, 暂停 2.495ms 总计 156.875ms

07-01 12:15:07.302 3542-3554/com.mycompany.falldetect W/art:暂停所有线程耗时:17.061ms

07-01 12:15:08.583 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过120帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:08.860 3542-3554/com.mycompany.falldetect I/art:后台粘滞并发标记清除GC释放4193(196KB)个AllocSpace对象,0(0B)个LOS对象, 23% 免费, 896KB/1175KB, 暂停 1.315ms 总计 104.895ms

07-01 12:15:09.045 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过35帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:09.247 3542-3542/com.mycompany.falldetect D/gralloc_goldfish:检测到没有GPU模拟的模拟器。

07-01 12:15:09.358 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过30帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:09.426 3542-3554/com.mycompany.falldetect I/art:后台部分并发标记清除GC释放了291(16KB)个AllocSpace对象,0(0B)个LOS对象, 24% 免费, 3MB/5MB, 暂停 1.233ms 总计 153.762ms

07-01 12:15:09.739 3542-3549/com.mycompany.falldetect W/art:暂停所有线程耗时:19.010ms

07-01 12:15:13.154 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过33帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:13.516 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过36帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:13.901 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过37帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:20.126 3542-3549/com.mycompany.falldetect W/art:暂停所有线程耗时:51.394ms

07-01 12:15:20.140 3542-3542/com.mycompany.falldetect I/Choreographer﹕跳过38帧!应用程序可能在其主线程上做了太多工作。

07-01 12:15:20.585 3542-3549/com.mycompany.falldetect W/art:暂停所有线程耗时:17.665ms

07-01 12:15:20.656 3542-3554/com.mycompany.falldetect I/art:后台粘滞并发标记清除GC释放8627(235KB)个AllocSpace对象,0(0B)个LOS对象, 0% 免费, 7MB/7MB, 暂停 1.020ms 总计 124.402ms

07-01 12:15:22.087 3542-3549/com.mycompany.falldetect W/art:暂停所有线程耗时:27.697ms

07-01 12:15:22.122 3542-3554/com.mycompany.falldetect W/art:暂停所有线程耗时:29.438ms

07-01 12:15:23.703 3542-3554/com.mycompany.falldetect W/art:暂停所有线程耗时:30.292ms

07-01 12:15:24.505 3542-3554/com.mycompany.falldetect I/art:后台部分并发标记清除GC释放1195(43KB)个AllocSpace对象,1(3MB)个LOS对象, 25% 免费, 3MB/5MB, 暂停 1.126ms 总计 116.397ms

07-01 12:15:25.263 3542-3554/com.mycompany.falldetect W/art:暂停所有线程耗时:38.968ms

07-01 12:18:45.820 3542-3554/com.mycompany.falldetect I/art:后台部分并发标记清除GC释放1244(45KB)个AllocSpace对象,1(3MB)个LOS对象, 25% 免费, 3MB/5MB, 暂停 31.610ms 总计 38.187ms

好的,所以你在图表上做了很多更新:

public void onSensorChanged(SensorEvent event) {

那是很多代码,在很短的时间内达到 运行,我最好的猜测是 phone 根本不够快,无法完成所有这些小部件更新,在下一个传感器数据触发另一个事件之前。

加速度计是一种高度敏感的设备,因此会产生很多噪音。这意味着这个事件将被触发大约每秒 100 次(或更多)

因此,这意味着您的事件处理程序必须快于 1/100 秒,否则您将建立一个事件队列,该队列将永远持续增长,并随着队列增加..

解决方法很简单:

让事件处理程序,简单地将数据推送到数据数组/列表/向量中..

让代码的另一部分(定时事件或主循环)从数组/列表/向量中提取数据,并以低得多的频率更新图形,即每秒 2 次(如果在循环,在循环中添加一个小的睡眠,以允许调度程序完成它的工作)。

这将允许事件处理程序在触发下一个传感器事件之前完成。

编辑:更多帮助(我 不是 java 程序员,所以 这可能不是甚至是 运行可用代码,但你应该明白我要传达的意思):

@Override
public void onSensorChanged(SensorEvent event) {
    if (!started) {
        return;
    }

    double x = event.values[0];
    double y = event.values[1];
    double z = event.values[2];
    long timestamp = System.nanoTime();
    Data point = new Data(timestamp, x, y, z);

    //add to list, that can be accessed by another loop
    if (this.seismicActivityBuffer.size() > this.bufferSize) {
        this.seismicActivityBuffer.remove();
    }
    this.seismicActivityBuffer.add(point);

    //this takes too long to do in this eventhandler (says Henrik)
    //so another loop must update the chart, using the seismicActivityBuffer
    //
    //this.chartContainer.removeAllViews();
    //this.chart = createChart(seismicActivityBuffer);
    //
    //this.chartContainer.addView(chart);

}

public void onStartClick(View view) {
    SensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    this.doMainLoop = 1;
}

public void onStopClick(View view) {
    SensorManager.unregisterListener(this);
    this.doMainLoop = 0;
}

private void stop() {
    //all the stuff you do now + this:
    this.doMainLoop = -1;
}

private void mainChartLoop()
{
    while(this.doMainLoop > -1)
    {
      if(this.doMainLoop > 0)
      {
         this.chartContainer.removeAllViews();
         this.chart = createChart(this.seismicActivityBuffer); 
         this.chartContainer.addView(chart);
      }
    }
}

public void onCreate(Bundle savedInstanceState) {
    // all your stuff + this:
    this.mainChartLoop();
}