android中的线程池的使用

理解Android线程池与异步加载机制
本文深入解析Android中线程池的概念及应用,通过Handler+Runnable、Handler+Thread+Message、Handler+ExecutorService(线程池)+MessageQueue等模式,详细讲解如何实现异步加载图片,避免UI阻塞,提升用户体验。特别强调了引入本地缓存以优化重复图片加载效率,实现场景优化。
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解

Handler+Runnable模式

我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式。

这里为何不是新开线程的原因请参看这篇文章:Android Runnable 运行在那个线程 这里的代码其实是在UI 主线程中下载图片的,而不是新开线程。

我们运行下面代码时,会发现他其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。

package ghj1976.AndroidTest;

import java.io.IOException;
import java.net.URL;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView;

public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
                loadImage(",
                                R.id.imageView2);
                loadImage("http://cache.soso.com/30d/img/web/logo.gif, R.id.imageView3);
                loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                                R.id.imageView4);
                loadImage("http://images.cnblogs.com/logo_small.gif",
                                R.id.imageView5);
        }

        private Handler handler = new Handler();

        private void loadImage(final String url, final int id) {
                handler.post(new Runnable() {
                        public void run() {
                                Drawable drawable = null;
                                try {
                                        drawable = Drawable.createFromStream(
                                                        new URL(url).openStream(), "image.gif");
                                } catch (IOException e) {
                                        Log.d("test", e.getMessage());
                                }
                                if (drawable == null) {
                                        Log.d("test", "null drawable");
                                } else {
                                        Log.d("test", "not null drawable");
                                }
                                // 为了测试缓存而模拟的网络延时 
                                SystemClock.sleep(2000); 
                                ((ImageView) MainActivity.this.findViewById(id))
                                                .setImageDrawable(drawable);
                        }
                });
        }
}



Handler+Thread+Message模式

这种模式使用了线程,所以可以看到异步加载的效果。

核心代码:

package ghj1976.AndroidTest;

import java.io.IOException;
import java.net.URL;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView;

public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                loadImage2("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
                loadImage2("https://i-blog.csdnimg.cn/blog_migrate/d2ef47a0eecda820404c86dfa10195c7.gif",
                                R.id.imageView2);
                loadImage2("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
                loadImage2("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                                R.id.imageView4);
                loadImage2("http://images.cnblogs.com/logo_small.gif",
                                R.id.imageView5);
        }

        final Handler handler2 = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                        ((ImageView) MainActivity.this.findViewById(msg.arg1))
                                        .setImageDrawable((Drawable) msg.obj);
                }
        };

        // 采用handler+Thread模式实现多线程异步加载
        private void loadImage2(final String url, final int id) {
                Thread thread = new Thread() {
                        @Override
                        public void run() {
                                Drawable drawable = null;
                                try {
                                        drawable = Drawable.createFromStream(
                                                        new URL(url).openStream(), "image.png");
                                } catch (IOException e) {
                                        Log.d("test", e.getMessage());
                                }

                                // 模拟网络延时
                                SystemClock.sleep(2000);

                                Message message = handler2.obtainMessage();
                                message.arg1 = id;
                                message.obj = drawable;
                                handler2.sendMessage(message);
                        }
                };
                thread.start();
                thread = null;
        }

}


这时候我们可以看到实现了异步加载, 界面打开时,五个ImageView都是没有图的,然后在各自线程下载完后才把图自动更新上去

Handler+ExecutorService(线程池)+MessageQueue模式

能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。

这个例子是使用线程池。Android拥有与Java相同的ExecutorService实现,我们就来用它。

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

线程池的信息可以参看这篇文章:Java&Android的线程池-ExecutorService 下面的演示例子是创建一个可重用固定线程数的线程池

核心代码

package ghj1976.AndroidTest;

import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView;

public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
                loadImage3("https://i-blog.csdnimg.cn/blog_migrate/d2ef47a0eecda820404c86dfa10195c7.gif",
                                R.id.imageView2);
                loadImage3("http://cache.soso.com/30d/img/web/logo.gif",
                                R.id.imageView3);
                loadImage3("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                                R.id.imageView4);
                loadImage3("http://images.cnblogs.com/logo_small.gif",
                                R.id.imageView5);
        }

        private Handler handler = new Handler();

        private ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 引入线程池来管理多线程
        private void loadImage3(final String url, final int id) {
                executorService.submit(new Runnable() {
                        public void run() {
                                try {
                                        final Drawable drawable = Drawable.createFromStream(
                                                        new URL(url).openStream(), "image.png");
                                        // 模拟网络延时
                                        SystemClock.sleep(2000);
                                        handler.post(new Runnable() {
                                                public void run() {
                                                        ((ImageView) MainActivity.this.findViewById(id))
                                                                        .setImageDrawable(drawable);
                                                }
                                        });
                                } catch (Exception e) {
                                        throw new RuntimeException(e);
                                }
                        }
                });
        }
}

这里我们象第一步一样使用了 handler.post(new Runnable() {  更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { 来确保下载是在线程池的线程中。

Handler+ExecutorService(线程池)+MessageQueue+缓存模式

下面比起前一个做了几个改造:

  • 把整个代码封装在一个类中
  • 为了避免出现同时多次下载同一幅图的问题,使用了本地缓存

封装的类:

package ghj1976.AndroidTest;

import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;

public class AsyncImageLoader3 {
        // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
        public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
        
        private ExecutorService executorService = Executors.newFixedThreadPool(5); // 固定五个线程来执行任务
        private final Handler handler = new Handler();

        /**
         * 
         * @param imageUrl
         *            图像url地址
         * @param callback
         *            回调接口
         * @return 返回内存中缓存的图像,第一次加载返回null
         */
        public Drawable loadDrawable(final String imageUrl,
                        final ImageCallback callback) {
                // 如果缓存过就从缓存中取出数据
                if (imageCache.containsKey(imageUrl)) {
                        SoftReference<Drawable> softReference = imageCache.get(imageUrl);
                        if (softReference.get() != null) {
                                return softReference.get();
                        }
                }
                // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
                executorService.submit(new Runnable() {
                        public void run() {
                                try {
                                        final Drawable drawable = loadImageFromUrl(imageUrl); 
                                                
                                        imageCache.put(imageUrl, new SoftReference<Drawable>(
                                                        drawable));

                                        handler.post(new Runnable() {
                                                public void run() {
                                                        callback.imageLoaded(drawable);
                                                }
                                        });
                                } catch (Exception e) {
                                        throw new RuntimeException(e);
                                }
                        }
                });
                return null;
        }

        // 从网络上取数据方法
        protected Drawable loadImageFromUrl(String imageUrl) {
                try {
                        // 测试时,模拟网络延时,实际时这行代码不能有
                        SystemClock.sleep(2000);

                        return Drawable.createFromStream(new URL(imageUrl).openStream(),
                                        "image.png");

                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
        }

        // 对外界开放的回调接口
        public interface ImageCallback {
                // 注意 此方法是用来设置目标对象的图像资源
                public void imageLoaded(Drawable imageDrawable);
        }
}


说明:

final参数是指当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。参看:Java关键字final、static使用总结 
这里使用SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的,更详细的可以参看:内存优化的两个类:SoftReference 和 WeakReference

前段调用:

package ghj1976.AndroidTest;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;

import android.widget.ImageView;

public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
                loadImage4("https://i-blog.csdnimg.cn/blog_migrate/d2ef47a0eecda820404c86dfa10195c7.gif",
                                R.id.imageView2);
                loadImage4("http://cache.soso.com/30d/img/web/logo.gif",
                                R.id.imageView3);
                loadImage4("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                                R.id.imageView4);
                loadImage4("http://images.cnblogs.com/logo_small.gif",
                                R.id.imageView5);
        }

        private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();

        // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
        private void loadImage4(final String url, final int id) {
                // 如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行
                Drawable cacheImage = asyncImageLoader3.loadDrawable(url,
                                new AsyncImageLoader3.ImageCallback() {
                                        // 请参见实现:如果第一次加载url时下面方法会执行
                                        public void imageLoaded(Drawable imageDrawable) {
                                                ((ImageView) findViewById(id))
                                                                .setImageDrawable(imageDrawable);
                                        }
                                });
                if (cacheImage != null) {
                        ((ImageView) findViewById(id)).setImageDrawable(cacheImage);
                }
        }

}
<think>好的,我现在需要帮助用户了解Android线程池的实现机制,特别是Executor Framework。首先,用户的问题是关于Android线程池的实现方式,提到了Executor Framework,所以我应该重点围绕这个部分展开。 首先,我需要回忆一下Android线程池的相关知识。记得Android中的线程池主要是通过java.util.concurrent包中的Executor Framework来实现的,这个框架提供了一系列的线程池创建和管理工具。可能涉及到ThreadPoolExecutor这个核心类,以及Executors工厂类提供的几种预定义线程池,比如FixedThreadPool、CachedThreadPool、SingleThreadExecutor等。 接下来,用户给的参考引用中有四个来源,其中引用[1]和[2]提到了使用ExecutorService、AsyncTask和Runnable来实现线程池。而引用[3]和[4]是关于SpringBoot和SpringCloud的,可能和Android无关,但需要确认是否有相关的内容可以借鉴,不过可能用户是误贴了这些内容,或者需要排除干扰。因此,我应该主要关注引用[1]和[2]的内容。 用户的问题还要求回答结构清晰,分步骤说明,并且生成相关问题。同时需要遵循系统级指令中的格式要求,比如数学公式的格式,引用标识的正确使用等。 首先,我应该解释Executor Framework在Android中的实现机制。可能需要分点说明,比如核心组件、线程池类型、任务提交方式、线程池参数等。然后结合代码示例,比如如何使用ExecutorService来创建线程池,执行任务。同时,用户提供的引用中有AsyncTask,虽然现在可能已经被弃用,但可能需要提到,并说明推荐的方式。 然后,需要确保代码示例正确,并且符合用户提供的引用中的方式一,即主类文件使用ExecutorService、AsyncTask、Runnable。但AsyncTask在Android中已经过时,可能需要指出,并建议使用ExecutorService配合Handler或者LiveData来更新UI。 另外,需要检查LaTeX语法是否正确,但用户的问题中可能不涉及数学公式,不过如果有任何公式,比如参数说明中的核心数、队列容量等,可能需要用行内公式格式。 在回答过程中,需要自然地添加引用标识,比如在提到ExecutorService的时候,引用用户提供的引用[1]和[2],但需要确保引用的内容确实相关。例如,引用[1]提到的是Android线程池的经典使用方式,包括ExecutorService、AsyncTask、Runnable,这正好符合用户的问题。 可能需要注意,用户提供的引用[3]和[4]是关于SpringBoot和SpringCloud的,可能与当前问题无关,可以忽略,或者在生成相关问题的时候,如果涉及到其他框架的线程池,可以生成相关问题,但需要明确区分Android和Spring的上下文。 现在,我需要组织回答的结构: 1. 概述Executor Framework在Android中的作用。 2. 核心组件:ThreadPoolExecutor和Executors工厂类。 3. 四种预定义线程池类型及其适用场景。 4. 自定义线程池的参数配置。 5. 结合代码示例说明如何使用ExecutorService提交任务。 6. 提到AsyncTask的过时,推荐使用Executor配合其他机制更新UI。 7. 引用相关站内引用,例如方式一的实现。 同时,生成相关问题,比如线程池参数如何配置、不同线程池的适用场景、AsyncTask的替代方案等。 需要确保回答符合中文,使用正确的术语,避免技术错误,比如不混淆ThreadPoolExecutor和Executors的关系,正确解释各参数的意义。 最后,检查是否符合系统级指令中的格式要求,例如代码块使用正确的语法,引用标识正确放置,没有使用Markdown格式,段落清晰,分点说明等。</think>### Android线程池实现机制(Executor Framework) Android通过Java的`java.util.concurrent`包提供**Executor框架**实现线程池管理,其核心组件是`ThreadPoolExecutor`和`Executors`工厂类。以下为关键实现机制: --- #### 1. **核心组件** - **`ThreadPoolExecutor`** 直接控制线程池参数,包括: - **核心线程数**($corePoolSize$):常驻线程数量 - **最大线程数**($maxPoolSize$):线程池可扩展的最大上限 - **任务队列**($workQueue$):存储待执行任务(如`LinkedBlockingQueue`) - **线程存活时间**($keepAliveTime$):非核心线程空闲时的存活时长[^1] - **`Executors`工厂类** 提供预定义线程池类型,简化创建过程: ```java ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4); // 固定大小线程池 ``` --- #### 2. **Android中的线程池类型** | 类型 | 特点 | 适用场景 | |-----------------------|----------------------------------------------------------------------|------------------------| | **FixedThreadPool** | 固定核心线程数,无超时机制 | CPU密集型任务 | | **CachedThreadPool** | 线程数动态扩展,空闲线程自动回收 | 短期异步任务 | | **SingleThreadPool** | 单一线程顺序执行任务 | 需顺序执行的任务队列 | | **ScheduledThreadPool** | 支持定时或周期性任务 | 定时/延迟任务 | --- #### 3. **任务提交方式** - **`Runnable`接口**:无返回值任务 ```java executorService.execute(new Runnable() { @Override public void run() { // 后台逻辑 } }); ``` - **`Callable`接口**:支持返回结果 ```java Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "Result"; } }); ``` --- #### 4. **自定义线程池示例** 通过`ThreadPoolExecutor`配置参数: ```java int corePoolSize = 2; int maxPoolSize = 5; long keepAliveTime = 30L; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10); ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue ); executor.execute(task); // 提交任务 ``` --- #### 5. **与UI线程的交互** - **`AsyncTask`(已过时)**:早期用于后台任务与UI更新,但易引发内存泄漏[^2]。 - **推荐方式**:使用`ExecutorService`配合`Handler`或`LiveData`更新UI: ```java executor.execute(() -> { // 后台计算 String result = processData(); new Handler(Looper.getMainLooper()).post(() -> { textView.setText(result); // 更新UI }); }); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值