线程池异步回掉的简单例子

来源:互联网 发布:tm域名有必要注册吗 编辑:程序博客网 时间:2024/06/18 16:23

本文是一个基于android activity请求网络数据情景,做的一个java的模拟网络异步请求的简单例子,包括了线程池和软引用的简单应用,如果有不对的地方,希望大家可以指正。
1.异步请求是为了执行耗时操作
2.线程池是为了并发多个请求
3.软引用是为了当回掉的类持有的对象被释放的时候,可以被垃圾回收及时处理(当然gc本身并不是即时回收的)

好,下面开始贴代码,然后大概说两句

IntelliJ IDEA 新建工程:AsyncDemo

新建接口:CallBack,代码如下

public interface CallBack {    void onFailed(String errorMsg);    void onSuccess(String threadName);}

新建抽象类:CallBackImpl,代码如下

public abstract class CallBackImpl implements CallBack {    @Override    public void onFailed(String errorMsg) {        // 错误统一处理        System.out.println(errorMsg);    }}

写一个抽象类来实现一个接口的好处是,你可以在抽象类里,做统一的处理,也可以做一个空实现,这样当继承这个抽象类时,就可以不必实现所有接口中定义的方法了,只关注想要的那个回掉的处理就好,而且如果有必须子类做处理的,可以在抽象类中不做实现,这样子类就必须实现这个方法,可以给记性不好的提供方便(O(∩_∩)O)。

新建类:RequestClass,代码如下:

public class RequestClass {    // 对回掉做一个软引用    private WeakReference<CallBack> weakRequest;    // 创建一个线程池,最多启动6个线程,超过6个线程的会排队等待    private ExecutorService service = Executors.newFixedThreadPool(6);    private RequestClass() {    }    // 登记式/静态内部类,一种单例模式的写法    // 参考:http://www.runoob.com/design-pattern/singleton-pattern.html    private static class Singleton {        private static final RequestClass INSTANCE = new RequestClass();    }    public static RequestClass getInstance() {        return Singleton.INSTANCE;    }    public void request(CallBack callBack, int index) {        weakRequest = new WeakReference<>(callBack);        service.execute(new Runnable() {            @Override            public void run() {                try {                    System.out.println("线程: " + index + " 开始....");                    Thread.sleep(3000);//模拟耗时                    System.out.println("线程: " + index + " 结束....");//                    callBack.onSuccess("线程: " + index);                    if (weakRequest.get() != null) {                        weakRequest.get().onSuccess("线程: " + index);                    }                } catch (InterruptedException e) {                    if (weakRequest.get() != null) {                        weakRequest.get().onFailed("错误信息");                    }                    e.printStackTrace();                }            }        });    }}

关于这里添加软应用的原因是这样的:
比如说在android的activity中做一个网络请求,我会在这个activity中写一个内部类,这个内部类的好处是,当网络访问成功后,可以在相应的回掉中处理数据的展示,而且可以直接操作activity这个类中的成员。
这样有一个不好的地方,就是这个回掉会持有这个activity的引用,也就是强引用,如果这个activity finish了而异步还没有完成,就会导致activity不会被及时销毁(假设说gc刚好要回收时),这就是那个老说的可能会导致内存泄露的问题。
但是如果这是个软引用的话,gc就会回收这个activity对象。这样即做到了会被及时回收,也做到了直接操作activity类内的成员的做法(当然,如果你不想用软引用,也可以把回掉抽取出去,然后以一个广播的形式通知activity做处理)
下面的main方法也模拟了这个效果。

新建类:DemoClass

public class DemoClass {    private Request request;    public DemoClass(int index) {        request = new Request();        //模拟一个请求        RequestClass.getInstance().request(request, index);    }    // 实现一个抽象类,处理需要的回掉    class Request extends CallBackImpl {        @Override        public void onSuccess(String threadName) {            System.out.println(threadName + "回掉成功......");        }    }}

可以把这个类当成一个activity,在页面启动后,添加了一个网络请求

新建测试类:MainClass,代码如下

public class MainClass {    public static void main(String[] args) {        // 可以将这个循环数增加,就会看到等待进入线程池的效果        for (int i = 0; i < 5; i++) {            DemoClass demoClass = new DemoClass(i);//            demoClass = null;            System.gc();        }        System.out.println("for 循环结束了...");    }}

当注释掉 demoClass = null 这句话时,运行结果如下:

线程: 0 开始....线程: 1 开始....线程: 2 开始....线程: 3 开始....线程: 4 开始....for 循环结束了...线程: 0 结束....线程: 1 结束....线程: 0回掉成功......线程: 1回掉成功......线程: 2 结束....线程: 2回掉成功......线程: 4 结束....线程: 4回掉成功......线程: 3 结束....线程: 3回掉成功......

当打开 demoClass = null 这句话时,运行结果如下:

线程: 0 开始....线程: 2 开始....线程: 4 开始....线程: 3 开始....for 循环结束了...线程: 1 开始....线程: 4 结束....线程: 1 结束....线程: 3 结束....线程: 0 结束....线程: 2 结束....

就没有了回掉成功的打印,因为显示的调用了一下System.gc();后,这个对象被回收了。但是如果做异步请求时,不加软应用的话,执行结果如下:
修改RequestClass中的代码如下:

public void request(CallBack callBack, int index) {//        weakRequest = new WeakReference<>(callBack);        service.execute(new Runnable() {            @Override            public void run() {                try {                    System.out.println("线程: " + index + " 开始....");                    Thread.sleep(3000);//模拟耗时                    System.out.println("线程: " + index + " 结束....");                    callBack.onSuccess("线程: " + index);//                    if (weakRequest.get() != null) {//                        weakRequest.get().onSuccess("线程: " + index);//                    }                } catch (InterruptedException e) {//                    if (weakRequest.get() != null) {//                        weakRequest.get().onFailed("错误信息");//                    }                    e.printStackTrace();                }            }        });    }

运行结果如下:

线程: 0 开始....线程: 2 开始....线程: 1 开始....线程: 4 开始....for 循环结束了...线程: 3 开始....线程: 0 结束....线程: 0回掉成功......线程: 4 结束....线程: 2 结束....线程: 2回掉成功......线程: 1 结束....线程: 1回掉成功......线程: 3 结束....线程: 3回掉成功......线程: 4回掉成功......

即使将demoClass置为null,并且调用了 System.gc(); 还是会回掉,回掉成功的方法。因为这是个强引用,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

关于java的四种引用类型可以参考:
https://www.cnblogs.com/mjorcen/p/3968018.html

另外,如果你在一个非UI线程中,想要在回掉中直接处理UI的话,可以这么干,就会切换到主线程,代码如下:

Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {    @Override    public void run() {        //切换到主线程    }});

好了,先说这么多。
资源下载地址:http://download.csdn.net/download/u013488064/10122082
没C币了,要了2个C币 ^_^

原创粉丝点击