以下几种写法很常见:
1、直接 new Thread 进行请求,数据返回后使用 handler 回到主线程进行 UI 刷新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private void getData() { new Thread(new Runnable() { @Override public void run() { final String result = GetDataHelper.getData(); ShowImageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { // 处理结果,更新 UI } }); } }).start(); } |
2、或 直接 new AsyncTask。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private void getData() { new AsyncTask<String, String, String>(){ @Override protected String doInBackground(String... params) { String retult = GetDataHelper.getData(); return retult; } @Override protected void onPostExecute(String result) { // 处理结果,更新 UI } }.execute("params"); } |
3、使用封装好的网络请求库
1 2 3 4 5 6 7 8 9 10 11 12 13 | JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("api", null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d("TAG", response.toString()); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("TAG", error.getMessage(), error); } }); |
问题来了,就是面试被问烂了的匿名内部类隐式地持有其外部类的引用
,在内部类的生命比外部类的生命要长的情景下会引起内存泄露。
面试者都会说,使用静态内部类
可以解决这个问题,在这里就不再多说,具体解决可参考技术小黑屋的:
面试时大家都会说使用静态内部类代替非静态内部类和匿名内部类,但为什么以上几种会引起内存泄露的写法还是这么多人用、在项目中还是那么常见呢?
静态内部类繁琐?
不介意短暂的内存泄露?反正响应返回或超时后都会释放?
以上写法除了内存泄露问题,还存在缺乏线程管理、统一调度,耦合等问题。
还有没更优雅的做法?
API 请求中心化管理,提供静态方法给外部调用,不设置回调函数(回调引用一样会引起内存泄露),使用事件总线(EventBus、otto 或 自己写)进行响应的结果分发,观察者在生命周期注册与反注册事件监听。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class APIManager { private static RequestQueue mQueue; public void init(Context context){ mQueue = Volley.newRequestQueue(context); } public static void register(String username, String psw){ //do register } public static void login(String username, String psw){ //do login } public static void getData(){ String getDataUrl = "XXXXX"; JsonObjectRequest request = creatJsonObjectRequest(getDataUrl, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { GetDataEvent event = new GetDataEvent(0, response.toString()); EventBus.getDefault().post(event); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { GetDataEvent event = new GetDataEvent(-1, error.getMessage()); EventBus.getDefault().post(event); } }); mQueue.add(request); } private static JsonObjectRequest creatJsonObjectRequest(String url, JSONObject requestObject, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener){ return new JsonObjectRequest(url, requestObject, listener, errorListener); } } |
API 请求中心化管理,这里以 Volley 为例。若是自己封装的网络请求,也可以自定义线程池去管理线程。
提供静态方法getData()
给外部调用,不需传入回调函数,与外部脱离引用关系,解耦。请求响应后使用事件总线(这里以 EventBus 为例)分发响应结果事件EventBus.getDefault().post(event)
观察者,这里即当前 Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Override protected void onResume() { super.onResume(); // 注册监听 EventBus.getDefault().register(this); } @Override protected void onStop() { // 反注册监听 EventBus.getDefault().unregister(this); super.onStop(); } @Subscribe public void onMessageEvent(GetDataEvent event){ int errorCode = event.getErrorCode(); String message = event.getMessage(); } |
在生命周期注册与反注册事件监听,设置监听响应函数。
1 2 3 | private void getData() { APIManager.getData(); } |
需要请求可直接调用 API 管理中心提供的静态请求方法。
中心化管理 API,避免内存泄露,完美解耦
前两天实现了一个需求,在某一具体页面,发起网络请求后,无论用户是否还在发起请求页面,还是已经回退到之前的页面,只要请求响应,都需要在当前页面弹出一个 Dialog。
解耦发挥作用,只需要在全部页面(BaseActivity)注册监听这个请求的响应事件即可。