美文网首页
Android非主线程更新UI

Android非主线程更新UI

作者: Adooooo | 来源:发表于2017-05-16 18:13 被阅读239次

说明:
本文不是讲怎么通过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<String, Bitmap> 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<String, Bitmap>(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系列:彻底了解Handler

    一、Handler 1.1 Android为什么==非ui线程==不能==更新ui== UI线程的机制 为什么UI...

  • 关于Android在非UI线程更新UI的问题。

    为了解决在Android非UI线程更新UI这个问题,Android提供了一些方法,从其他线程访问UI线程。 Act...

  • 异步消息处理

    异步消息处理 android耗时操作只能放在非主线程,message从子线程进入主线程,从不能更新UI到更新UI,...

  • Android 异步处理

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面 概述:每个Android应用...

  • 非UI线程不能更新View源码探索

    非UI线程不能更新View,相信对每个Android开发者来说都不陌生,那大家有没有想过为什么非UI线程就不能更新...

  • Android handler使用方法

    android中系统不允许在非主线程更新UI。当我们在非主线程做了耗时操作后,需要去更新UI的时候,我们就需要使用...

  • Android 进阶之 Handler 消息机制

    众所周知,在 Android 中,非 UI 线程中是不能更新 UI 的,如果在子线程中做 UI 相关操作,可能会出...

  • 消息处理机制

    本文主要包含:1、推荐主线程更新UI的原因。(Android单线程模型)2、消息处理机制的原理。3、非UI线程真的...

  • 子线程不能更新UI?

    看这里看这里:Android中子线程真的不能更新UI吗 只能在UI线程访问UI原因 Android的UI访问是没有...

  • Android 的Handler消息机制

    Handler作用 Android 规定只有在主线程(UI线程)更新UI,否则会抛出异常,并且Android又建议...

网友评论

      本文标题:Android非主线程更新UI

      本文链接:https://www.haomeiwen.com/subject/jxyvdttx.html