Android应用程序跳过帧,一段时间后崩溃

时间:2016-01-20 22:10:55

标签: java android multithreading background-process ui-thread

我之前发过关于我的应用程序跳过框架的帖子,因为我可能没有正在进行后台活动并正确更新用户界面,这里有一个指向问题的链接: Doing background stuff on a different UI

我使用过AsyncTask,Handlers,Timers,Threads ......我仍然得到应用程序正在跳帧的相同错误。

以下是我的完整代码:

public class MainActivity extends Activity {
    public static final String DEBUG_TAG = "MainActvity";
    public int mInterval = 5000; // 10 seconds by default, can be changed here, it should be aligned
                                 // with how many secs in takes to get latency packets
    private NetworkInformation dsNetwork;

    private TextView wifiTextView;
    private TextView ipTextView;
    private TextView macTextView;
    private TextView ssidTextView;
    private TextView linkSpeedTextView;
    private TextView frequencyTextView;
    private TextView signalLevelTextView;
    private TextView dlBandwidthTextView;
    private TextView upBandwidthTextView;
    private TextView latencyTextView;

    private Handler handler;
    //private Runnable runnableCode;
    //private Timer timerAsync;
    //private TimerTask timerTaskAsync;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        wifiTextView = (TextView)findViewById(R.id.wifi_textView);
        ipTextView = (TextView)findViewById(R.id.ip_val_textView);
        macTextView = (TextView)findViewById(R.id.mac_val_textView);
        ssidTextView = (TextView)findViewById(R.id.ssid_val_textView);
        linkSpeedTextView = (TextView)findViewById(R.id.linkSpeed_val_textView);
        frequencyTextView = (TextView)findViewById(R.id.frequency_val_textView);
        signalLevelTextView = (TextView)findViewById(R.id.signalLevel_val_textView);
        dlBandwidthTextView = (TextView)findViewById(R.id.dlBandwidth_val_textView);
        upBandwidthTextView = (TextView)findViewById(R.id.upBandwidth_val_textView);
        latencyTextView = (TextView)findViewById(R.id.latency_val_textView);

        handler = new Handler();
        startRepeatingTask();
    }


    Runnable mStatusChecker = new Runnable() {
        @Override
        public void run() {
            updateUI(dsNetwork); //this function can change value of mInterval.
            handler.postDelayed(mStatusChecker, mInterval);
        }
    };

    void startRepeatingTask() {
        dsNetwork = new NetworkInformation(getApplicationContext());
        mStatusChecker.run();
    }

    void stopRepeatingTask() {
        handler.removeCallbacks(mStatusChecker);
    }



    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }



    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }



    private void updateUI(NetworkInformation dsNetwork){
        // Handles updating the textviews in the UI
        //Log.d(DEBUG_TAG, "updateUI(NetworkInformation)");

        if (dsNetwork.isConnected()){
            wifiTextView.setText(R.string.wifi_is_on);
            wifiTextView.setTextColor(Color.GREEN);

            ipTextView.setText(dsNetwork.getIpAddress());
            macTextView.setText(dsNetwork.getMacAddress());
            ssidTextView.setText(dsNetwork.getSsid());
            linkSpeedTextView.setText(String.valueOf(dsNetwork.getLinkSpeed()) + " Mbps");
            frequencyTextView.setText(String.valueOf(dsNetwork.getFrequency()) + " MHz");
            signalLevelTextView.setText(String.valueOf(dsNetwork.getSignalLevel()) + "%");
            dlBandwidthTextView.setText(String.valueOf(dsNetwork.getDlBandwidth()/1000) + " Mbps");
            upBandwidthTextView.setText(String.valueOf(dsNetwork.getDlBandwidth()/1000) + " Mbps");
            latencyTextView.setText(String.valueOf(dsNetwork.getLatency("10.10.0.62")) + " ms");
        }
        else {
            wifiTextView.setText(R.string.wifi_is_off);
            wifiTextView.setTextColor(Color.RED);

            ipTextView.setText("N/A");
            macTextView.setText("N/A");
            ssidTextView.setText("N/A");
            linkSpeedTextView.setText("N/A");
            frequencyTextView.setText("N/A");
            signalLevelTextView.setText("N/A");
            dlBandwidthTextView.setText("N/A");
            upBandwidthTextView.setText("N/A");
            latencyTextView.setText("N/A");
        }

    }

}

以下是处理获取信息的网络类:

public class NetworkInformation {


    public static int SIGNAL_LEVEL_LIMIT = 101;
    public Context mContext;
    public static String DEBUG_TAG = "NetworkInformation";


    private boolean isConnected;
    private String networkType; //Wifi, Mobile Data, Ethernet...
    private String ssid;
    private String ipAddress;
    private String macAddress;
    private int frequency;      // in MHz
    private int linkSpeed;      // in Mbps
    private int signalLevel;    // Measured from 1 to SIGNAL_LEVEL_LIMIT
    private int dlBandwidth;    // downstream bandwidth in Kbps
    private int upBandwidth;    // upstream bandwidth in Kbps


    public boolean isConnected() {
        return isConnected;
    }

    public String getNetworkType() {
        return networkType;
    }

    public String getSsid() {
        return ssid;
    }

    public int getFrequency() {
        return frequency;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public String getMacAddress() {
        return macAddress;
    }

    public int getLinkSpeed() {
        return linkSpeed;
    }

    public int getSignalLevel() {
        return signalLevel;
    }

    public int getDlBandwidth() {
        return dlBandwidth;
    }

    public int getUpBandwidth() {
        return upBandwidth;
    }




    // Constructor: gets application contex, sets whether or not there is connection, and sets the
    // connection-related variables
    public NetworkInformation(Context mContext) {

        this.mContext = mContext;
        setConnectionStatus();
        setNetworkType();

        if (this.networkType == "None") {
            // TODO: handle the case when there's no connection
        }
        if (this.networkType == "Wifi") {
            setWifiInfo();
        }
        if(this.networkType == "Mobile Data") {
            //TODO: setMobileDataInfo()
        }
    }



    /*
    Returns the latency to a given server in mili-seconds by issuing a ping command.
    system will issue 5 ICMP Echo Request packet each having size of 56 bytes
    every second, and returns the avg latency of them.
    Returns 0 when there is no connection
     */
    public double getLatency(String ipAddress){
        String pingCommand = "/system/bin/ping -c 4 " + ipAddress;
        String inputLine = "";
        double avgRtt = 0;

        try {
            // execute the command on the environment interface
            Process process = Runtime.getRuntime().exec(pingCommand);
            // gets the input stream to get the output of the executed command
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            inputLine = bufferedReader.readLine();
            while ((inputLine != null)) {
                if (inputLine.length() > 0 && inputLine.contains("avg")) {  // when we get to the last line of executed ping command
                    break;
                }
                inputLine = bufferedReader.readLine();
            }
        }
        catch (IOException e){
            Log.v(DEBUG_TAG, "getLatency: EXCEPTION");
            e.printStackTrace();
        }

        // Extracting the average round trip time from the inputLine string
        String afterEqual = inputLine.substring(inputLine.indexOf("="), inputLine.length()).trim();
        String afterFirstSlash = afterEqual.substring(afterEqual.indexOf('/') + 1, afterEqual.length()).trim();
        String strAvgRtt = afterFirstSlash.substring(0, afterFirstSlash.indexOf('/'));
        avgRtt = Double.valueOf(strAvgRtt);

        return avgRtt;
    }






    /*
    The following are private utility functions
     */


    // Checks if the device is connected to the Internet, and sets the class variables
    private void setConnectionStatus() {
        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

        // if there is internet connection, set the variabel
        if ((activeNetwork != null) && (activeNetwork.isConnectedOrConnecting()))
            this.isConnected = true;
        else
            this.isConnected = false;
    }



    // Returns the type of network: Internet, Mobile Data, WiMax...
    private void setNetworkType() {
        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

        String networkType = "None";

        switch (activeNetwork.getType()) {
            case ConnectivityManager.TYPE_WIFI:
                networkType = "Wifi";
                break;
            case ConnectivityManager.TYPE_MOBILE:
                networkType = "Mobile Data";
                break;
            case ConnectivityManager.TYPE_ETHERNET:
                networkType = "Ethernet";
                break;
            case ConnectivityManager.TYPE_WIMAX:
                networkType = "WiMAX";
                break;
            case ConnectivityManager.TYPE_VPN:
                networkType = "VPN";
                break;
        }

        this.networkType = networkType;
    }



    // Gets the wifi information, and updates the class variables
    private void setWifiInfo() {

        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        Network[] network = cm.getAllNetworks();
        NetworkCapabilities netCapab = cm.getNetworkCapabilities(network[0]);

        WifiManager wifiManager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();

        if (wifiInfo != null) {
            this.ssid = wifiInfo.getSSID();
            this.ipAddress = Formatter.formatIpAddress(wifiInfo.getIpAddress());
            this.macAddress = wifiInfo.getMacAddress();
            this.linkSpeed = wifiInfo.getLinkSpeed();
            this.frequency = wifiInfo.getFrequency();
            this.dlBandwidth = netCapab.getLinkDownstreamBandwidthKbps();
            this.upBandwidth = netCapab.getLinkUpstreamBandwidthKbps();
            this.signalLevel = wifiManager.calculateSignalLevel(wifiManager.getConnectionInfo().getRssi(), SIGNAL_LEVEL_LIMIT);
        }
    }
}

当应用程序首先运行时,它运行正常,但它变得越来越慢,最后在10分钟后崩溃...... 我一直在logcat中得到这个错误,它不断出现: I / Choreographer:跳过183帧!应用程序可能在其主线程上做了太多工作。

哦,我以这种方式做了Handler:Repeat a task with a time delay? 而这样: Update TextView Every Second

但都没有效果。有人救了我...

1 个答案:

答案 0 :(得分:1)

方法getLatency(...)是一个长时间运行的操作(启动外部进程并执行一些I / O)。不应在更新UI的主线程中调用此类内容。编舞者试图保持60fps,因此3秒的被阻止的主线程可能导致180帧丢帧。

启动一些后台任务,获取所需的全部数据,然后在主线程中更新UI。