Java OutOfMemoryError:无法分配JNI环境

Java OutOfMemoryError:无法分配JNI环境,java,android,multithreading,android-volley,handler,Java,Android,Multithreading,Android Volley,Handler,我正在制作一个跟踪应用程序,跟踪公交车的位置。我想让它实时,所以我必须每秒调用api。如果我设定3秒的间隔,那么它可以正常工作,但更改为1秒会使应用程序崩溃。我还在清单中添加了largeheap=true,但在这种情况下不起作用。我还为OutOfMemoryException添加了catch,但它仍然崩溃。我如何处理这个处理程序 请求 public void GetChildLocation(){ StringRequest stringRequest = new StringReque

我正在制作一个跟踪应用程序,跟踪公交车的位置。我想让它实时,所以我必须每秒调用api。如果我设定3秒的间隔,那么它可以正常工作,但更改为1秒会使应用程序崩溃。我还在清单中添加了
largeheap=true
,但在这种情况下不起作用。我还为OutOfMemoryException添加了catch,但它仍然崩溃。我如何处理这个处理程序

请求

public void GetChildLocation(){
    StringRequest stringRequest = new StringRequest(Request.Method.POST, LAST_COORDINATE_URL, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            try {
                JSONObject jsonObject = new JSONObject(response);
                JSONArray jsonArray = jsonObject.getJSONArray("data");

                JSONObject location = jsonArray.getJSONObject(0);

                LatLng newLatLng = new LatLng(Double.parseDouble(location.getString("latitude")),Double.parseDouble(location.getString("longitude")));
                if (latlng != null) {
                    double PI = 3.14159;
                    //Source
                    double lat1 = latlng.latitude* PI / 180;
                    double lng1 = latlng.longitude* PI / 180;
                    // destination
                    double lat2 = newLatLng.latitude* PI / 180;
                    double lng2 = newLatLng.longitude* PI / 180;
                    double dLon = (lng2 - lng1);
                    double y = Math.sin(dLon) * Math.cos(lat2);
                    double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
                    double brng = Math.toDegrees((Math.atan2(y, x)));
                    brng = (brng + 360) % 360;

                    mMap.animateCamera(CameraUpdateFactory.newLatLng(newLatLng));
                    animateMarker(mCurrentLocationMarker, newLatLng, (float) brng, false);
                    latlng = newLatLng;
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    GetChildLocation();
                }
            },1000);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    }){
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            Map<String,String> params = new HashMap<>();
            params.put("track_id",trackId);
            return params;
        }
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String,String> headers = super.getHeaders();
            if (headers == null || headers.equals(Collections.<String, String>emptyMap())){
                headers = new HashMap<String, String>();
            }
            MyApp.get().addSessionCookie(headers);
            return headers;
        }
    };
    if (getContext() != null) {
        try {
            RequestQueue queue = Volley.newRequestQueue(getContext());
            queue.add(stringRequest);
        }catch (OutOfMemoryError e){
            e.printStackTrace();
            RequestQueue queue1 = Volley.newRequestQueue(getContext());
            queue1.add(stringRequest);
        }
    }
}

首先,在正常情况下,您不应该捕获任何
OutOfMemoryError
或任何
Error
子类,请查看
Error
,这很好地解释了这一点:

错误是Throwable的一个子类,它表示一个合理的应用程序不应该试图捕获的严重问题。大多数此类错误都是异常情况。ThreadDeath错误虽然是“正常”情况,但也是错误的一个子类,因为大多数应用程序不应该尝试捕捉它

至于您的请求代码,每次启动请求时,您似乎都在分配大量内容,尤其是
Volley.newRequestQueue(getContext()),您可以看看官方网站,了解如何重用volley的请求队列以及如何实现基本缓存

您可以更进一步,也可以重用
StringRequest
本身,而无需每次创建新的值,同时重用
getParams
getHeader
的值,而无需每次分配新值

看起来可能是这样的,但同样,整个方法似乎并不正确,重新考虑应用程序的架构可能是个好主意

class YourClass {

    private static final int REQUEST_DELAY_MS = 1000;

    // Assuming you have initialize those somewhere
    private final Map params;
    private final Map headers;
    private final Handler handler;

    private final RequestQueue queue;

    private final StringRequest updateLocationRequest =  
        = new StringRequest(Request.Method.GET, "some url", new Response.Listener<String>() {
        @Override
        public void onResponse(final String response) {
            // handle your result here
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(final VolleyError error) {
            // handle error here
        }
    }) {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            return params;
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            return headers;
        }
    };

    // It's actually better to provide a RequestQueue here or use a singleton, 
    // but for sake of example doing it here
    public YourClass(@NonNull final Context context) {
        queue = Volley.newRequestQueue(context);

        // You put the respective params here
        headers = new HashMap<>();
        params = new HashMap<>();
    }

    public void getChildLocation() {
        queue.add(updateLocationRequest);

        handler.postDelayed(getChildLocation(), REQUEST_DELAY_MS);
    }
}
classyourclass{
专用静态最终整数请求\延迟\毫秒=1000;
//假设您已经在某个地方初始化了这些
私有最终映射参数;
私有最终地图标题;
私人最终处理人;
私有最终请求队列;
私有最终StringRequest updateLocationRequest=
=新StringRequest(Request.Method.GET,“some url”,new Response.Listener()){
@凌驾
公共void onResponse(最终字符串响应){
//在这里处理你的结果
}
},new Response.ErrorListener(){
@凌驾
公共错误响应(最终截击错误){
//在这里处理错误
}
}) {
@凌驾
受保护的映射getParams()引发AuthFailureError{
返回参数;
}
@凌驾
公共映射getHeaders()引发AuthFailureError{
返回标题;
}
};
//实际上最好在这里提供RequestQueue或使用singleton,
//但为了举例说明,在这里这样做
公共YourClass(@NonNull final Context){
queue=Volley.newRequestQueue(上下文);
//你把各自的参数放在这里
headers=newhashmap();
params=新的HashMap();
}
public void getChildLocation(){
添加(updateLocationRequest);
postDelayed(getChildLocation(),REQUEST_DELAY_MS);
}
}

无论如何,真的有必要每秒钟(或每三秒)调用一次API来更新总线位置吗?我的意思是,总线在这三秒钟内会改变它的物理位置吗?

首先,在正常情况下,你不应该捕捉到
OutOfMemoryError
或任何
Error
子类,看看
Error
,这很好地解释了这一点:

错误是Throwable的一个子类,它表示一个合理的应用程序不应该试图捕获的严重问题。大多数此类错误都是异常情况。ThreadDeath错误虽然是“正常”情况,但也是错误的一个子类,因为大多数应用程序不应该尝试捕捉它

至于您的请求代码,每次启动请求时,您似乎都在分配大量内容,尤其是
Volley.newRequestQueue(getContext()),您可以看看官方网站,了解如何重用volley的请求队列以及如何实现基本缓存

您可以更进一步,也可以重用
StringRequest
本身,而无需每次创建新的值,同时重用
getParams
getHeader
的值,而无需每次分配新值

看起来可能是这样的,但同样,整个方法似乎并不正确,重新考虑应用程序的架构可能是个好主意

class YourClass {

    private static final int REQUEST_DELAY_MS = 1000;

    // Assuming you have initialize those somewhere
    private final Map params;
    private final Map headers;
    private final Handler handler;

    private final RequestQueue queue;

    private final StringRequest updateLocationRequest =  
        = new StringRequest(Request.Method.GET, "some url", new Response.Listener<String>() {
        @Override
        public void onResponse(final String response) {
            // handle your result here
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(final VolleyError error) {
            // handle error here
        }
    }) {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            return params;
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            return headers;
        }
    };

    // It's actually better to provide a RequestQueue here or use a singleton, 
    // but for sake of example doing it here
    public YourClass(@NonNull final Context context) {
        queue = Volley.newRequestQueue(context);

        // You put the respective params here
        headers = new HashMap<>();
        params = new HashMap<>();
    }

    public void getChildLocation() {
        queue.add(updateLocationRequest);

        handler.postDelayed(getChildLocation(), REQUEST_DELAY_MS);
    }
}
classyourclass{
专用静态最终整数请求\延迟\毫秒=1000;
//假设您已经在某个地方初始化了这些
私有最终映射参数;
私有最终地图标题;
私人最终处理人;
私有最终请求队列;
私有最终StringRequest updateLocationRequest=
=新StringRequest(Request.Method.GET,“some url”,new Response.Listener()){
@凌驾
公共void onResponse(最终字符串响应){
//在这里处理你的结果
}
},new Response.ErrorListener(){
@凌驾
公共错误响应(最终截击错误){
//在这里处理错误
}
}) {
@凌驾
受保护的映射getParams()引发AuthFailureError{
返回参数;
}
@凌驾
公共映射getHeaders()引发AuthFailureError{
返回标题;
}
};
//实际上最好在这里提供RequestQueue或使用singleton,
//但为了举例说明,在这里这样做
公共YourClass(@NonNull final Context){
queue=Volley.newRequestQueue(上下文);
//你把各自的参数放在这里
headers=newhashmap();
params=新的HashMap();
}
public void getChildLocation(){
添加(updateLocationRequest);
postDelayed(getChildLocation(),REQUEST_DELAY_MS);