您的位置:首页 > 移动开发 > Android开发

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

2017-11-16 22:08 225 查看
本文是一个基于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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 线程池 异步