[Android實例] Handler+ExecutorService(線程池)+MessageQueue模式+緩存模式


android線程池的理解,晚上在家無事 預習了一下android異步加載的例子,也學習到了一個很重要的東東 那就是線程池+緩存  下面看他們的理解

[size=1.8em]Handler+Runnable模式

我們先看一個並不是異步線程加載的例子,使用 Handler+Runnable模式。

這里為何不是新開線程的原因請參看這篇文章:Android Runnable 運行在那個線程 這里的代碼其實是在UI 主線程中下載圖片的,而不是新開線程。

我們運行下面代碼時,會發現他其實是阻塞了整個界面的顯示,需要所有圖片都加載完成后,才能顯示界面。

?
代碼片段,雙擊復制
01
02
03
04
05
06
07
08
09
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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(<div style= "width: 100px; height: 100px;background: url(static/image/common/loading.gif) no-repeat center center;" ></div><img id= "\"aimg_LOPt3\"" onclick= "\"zoom(this," this .src,= "" 0 ,= "" 0 )\ "=" " class=" \"zoom\" " file=" \"http: //www.chinatelecom.com.cn/images/logo_new.gif\"" onmouseover="\"img_onmouseoverfunc(this)\"" lazyloadthumb="\"1\"" border="\"0\"" alt="\"\"" src="\"http://www.chinatelecom.com.cn/images/logo_new.gif\"" lazyloaded="true" style="height: 1px; width: 1px;">",
                                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模式

這種模式使用了線程,所以可以看到異步加載的效果。

核心代碼:

?
代碼片段,雙擊復制
01
02
03
04
05
06
07
08
09
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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( "http://www.chinatelecom.com.cn/images/logo_new.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 下面的演示例子是創建一個可重用固定線程數的線程池

核心代碼

?
代碼片段,雙擊復制
01
02
03
04
05
06
07
08
09
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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( "http://www.chinatelecom.com.cn/images/logo_new.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+緩存模式

下面比起前一個做了幾個改造:

  • 把整個代碼封裝在一個類中
  • 為了避免出現同時多次下載同一幅圖的問題,使用了本地緩存

封裝的類:

?
代碼片段,雙擊復制
01
02
03
04
05
06
07
08
09
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
          *            回調接口
          * <a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"" target="\"_blank\"">@return</a> 返回內存中緩存的圖像,第一次加載返回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

前段調用:

?
代碼片段,雙擊復制
01
02
03
04
05
06
07
08
09
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
39
40
41
42
43
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( "http://www.chinatelecom.com.cn/images/logo_new.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);
                }
        }
 
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM