不断检查Web服务中的新数据会导致java.lang.OutOfMemoryError

时间:2016-02-13 18:59:19

标签: android android-volley thread-sleep

我正在我的应用中进行长时间轮询,每500毫秒检查一次新数据,然后在有新的更改时更新我的​​textview。它做得很好,但几分钟后,我的应用程序崩溃并给了我这个错误:

java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
                                                                                 at java.lang.Thread.nativeCreate(Native Method)
                                                                                 at java.lang.Thread.start(Thread.java:1063)
                                                                                 at com.android.volley.RequestQueue.start(RequestQueue.java:142)
                                                                                 at com.android.volley.toolbox.Volley.newRequestQueue(Volley.java:66)
                                                                                 at com.android.volley.toolbox.Volley.newRequestQueue(Volley.java:78)
                                                                                 at com.example.rendell.longpolling.MainActivity.sendRequest(MainActivity.java:96)
                                                                                 at com.example.rendell.longpolling.MainActivity$1$1.run(MainActivity.java:59)
                                                                                 at android.os.Handler.handleCallback(Handler.java:739)
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                 at android.os.Looper.loop(Looper.java:135)
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5253)
                                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                                 at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:695)

MainActivity.java

public class MainActivity extends AppCompatActivity {

TextView text;
Date noteTS;

public static final String JSON_URL = "http://192.168.0.100/androidphp/data.php";

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

    text = (TextView) findViewById(R.id.text);

    (new Thread(new Runnable()
    {

        @Override
        public void run()
        {
            while (!Thread.interrupted())
                try
                {
                    Thread.sleep(500);
                    runOnUiThread(new Runnable() // start actions in UI thread
                    {

                        @Override
                        public void run()
                        {
                        // this action have to be in UI thread
                            /*noteTS = Calendar.getInstance().getTime();

                            String time = "hh:mm"; // 12:00
                            text.setText(DateFormat.format(time, noteTS));*/
                            sendRequest();
                        }
                    });
                }
                catch (InterruptedException e)
                {
                    // ooops
                }
        }
    })).start(); // the while thread will start in BG thread
}

public void sendRequest(){

    //While the app fetched data we are displaying a progress dialog
    //final ProgressDialog loading = ProgressDialog.show(this,"Fetching Data","Please wait...",false,false);

    StringRequest stringRequest = new StringRequest(JSON_URL,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                    //text.setText(response);

                    //loading.dismiss();
                    try{
                        showJSON(response);
                    }catch(Exception e) {}
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    //Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
                }
            });

    RequestQueue requestQueue = Volley.newRequestQueue(this);
    requestQueue.add(stringRequest);
}

private void showJSON(String json){
    ParseJson pj = new ParseJson(json);
    pj.parseJSON();
    text.setText(ParseJson.playing[0]);
    }
}

ParseJson.java

public class ParseJson {
public static String[] playing;

public static final String JSON_ARRAY = "result";
public static final String RESULT_ID = "playing";

private JSONArray users = null;

private String json;

public ParseJson(String json){
    this.json = json;
}

public void parseJSON(){
    JSONObject jsonObject=null;
    try {
        jsonObject = new JSONObject(json);
        users = jsonObject.getJSONArray(JSON_ARRAY);

        playing = new String[users.length()];

        for(int i=0;i<users.length();i++){
            JSONObject jo = users.getJSONObject(i);
            playing[i] = jo.getString(RESULT_ID);
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    }
}

2 个答案:

答案 0 :(得分:2)

我不确定您的代码有什么问题,但我可以给您一些提示

首先,阅读关于如何为volley制作单例类的android文档,每次发送请求时都要创建一个新的RequestQueue,我为整个应用程序使用一个队列,也许在某些情况下你创建更多但我现在无法想到,很可能这是你的主要问题,因为错误发生在newQueue方法

你不必制作单例类,但至少尝试将队列作为实例变量并每次添加它。

第二个有更简单的方法每隔x秒执行一次任务,我听说(不确定)在android中使用thread.sleep不推荐

以下是我通常执行此类任务的方式:

Timer timer = new Timer();
    final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // do something on UI
        }
    };
    TimerTask task = new TimerTask () {
        @Override
        public void run () {
            //send volley request here
        }
    };
    timer.schedule(task, 0, 60000); // 60000 is time in ms

答案 1 :(得分:0)

考虑将Rxjava与Retrolambda一起用于此

Observable.interval(500, TimeUnit.MILLISECONDS, Schedulers.io()).map(tick -> sendRequest()).doOnError(err -> //handle error).observeOn(AndroidSchedulers.mainThread()).subscribe(//handle subscription);

更新

对于java7和6,请使用此

.map(new Func1<Long, String>() {
                @Override
                public String call(Long tick) {
                    return sendRequest();
                }
            })

sendRequest方法

private String sendRequest(){ URL url;
    HttpURLConnection urlConnection = null;
    JSONArray response = new JSONArray();

    try {
        url = new URL(params[0]);
        urlConnection = (HttpURLConnection) url.openConnection();
        int responseCode = urlConnection.getResponseCode();

        if(responseCode == HttpStatus.SC_OK){
            String responseString = readStream(urlConnection.getInputStream());
            Log.v("CatalogClient", responseString);
            response = new JSONArray(responseString);
        }else{
            Log.v("CatalogClient", "Response code:"+ responseCode);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if(urlConnection != null)
            urlConnection.disconnect();
    }

    return response;}

和读取流方法

private String readStream(InputStream in) {
      BufferedReader reader = null;
      StringBuffer response = new StringBuffer();
      try {
        reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while ((line = reader.readLine()) != null) {
          response.append(line);
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        if (reader != null) {
          try {
            reader.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
      return response.toString();
    } 

现在在.subscribe()中覆盖Action1并读取字符串响应。