Android非主线程更新UI

说明:
本文不是讲怎么通过Handler机制来进行UI的更新,而是绕过Android更新UI的线程判断机制,来进行UI更新的。
正文:
Android要求我们在主线程更新UI,如果在非主线程更新UI,就出出现如下异常:

 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                             at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6462)

进入ViewRootImpl.checkThead()源码:

  void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

Android UI更新的时候在这里进行的线程判断,如果不是主线程,就不能更新UI,即抛出异常CalledFromWrongThreadException所以我们只要在其他线程里把这个异常给捕获了,就可以在别的线程里更新UI了。代码如下:

 class Test implements Runnable {

        private TextView textView;

        public Test(TextView textView) {
            this.textView = textView;
        }

        @Override
        public void run() {
            try {
                textView.setText("new thread set textview");
            } catch (RuntimeException e) {
                e.printStackTrace();
            }  
        }
    }

当然这种方式违法了Android官方本意,也没有什么技术含量。我为什么会想到这种方式呢?还是因为看了何红辉的《Android源码设计模式解析与实践》一书中的一个Demo产生了疑问。
大致代码如下(加入了我的一部分调试代码):

public class ImageLoader {

    private static final String TAG = "ImageLoader";
    LruCache mImageCache;
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    public Thread mThread;

    public ImageLoader() {
        initImageCache();
    }

    private void initImageCache() {
        final int maxMermory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMermory / 4;
        mImageCache = new LruCache(cacheSize) {
            @Override
            protected int sizeOf(String url, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

    public void displayImage(final String url, final ImageView imageView, final TextView textView) {
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) {
                    Log.e(TAG, "bitmap is null!!!!");
                    return;
                }

                if (imageView.getTag().equals(url)) {
                    textView.setText("线程池中的数据");
//                    if (Looper.myLooper() == Looper.getMainLooper())
                    imageView.setImageBitmap(bitmap);
//                    if (mThread == Thread.currentThread()) {
//
//                    } else {
                        System.out.println("啦啦啦啦德玛西");
                        throw new IllegalArgumentException("你必须在主线程调用该方法");
//                    }

 
                }
                bitmap.recycle();
                mImageCache.put(url, bitmap);

            }
        });
    }

    public Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            final URLConnection conn = url.openConnection();
//            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

我发现一个问题:在线程池中竟然可以更新UI,而我又试了在普通的线程了确不行,并且在线程池中的run()方法里,竟然无法抛出异常。所以我又试了如下代码:

public class TestExecutor {

    public static void main(String[] args) {

        ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                throw new IllegalStateException("啦啦啦啦德玛西亚");
            }
        });
    }

}

这里边的异常也如法抛出。于是看了看源码,上网查看了线程池的使用,发现在线程池自动捕获了我们在run()方法里抛出的异常,所以程序能够正常执行。
具体请参考,Java线程池异常处理最佳实践
说到底还是涉及到了Java线程池的使用问题,希望通过这个例子加深大家对Java线程池的理解。

你可能感兴趣的:(Android非主线程更新UI)