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

本文是一个基于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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值