多线程操作

 

对于多线程的操作:有两种方式:

1.继承Thread类。new Thread(){run()}.start();

2.实现Runnable类。new Thread(){new Runnable(){}}.start();

对于多线程的异步操作,处理方式有两种:

  1. Handler+Thread方式,即对Looper对象的管理
  2. AsyncTask
  3. 多线程的封装-线程池(线程池工厂或者自定义线程)

Handler+Thread方式:

对于Handler+Thread分为主线程与副线程的操作,只要我们对Looper对象操作即可,将消息message或者runnable进行send或者post即可。

主线程:runOnUiThread(runnable action)、view.post(runnable action)、主线程的Handler.post(runnable action)、Handler.sendMessage(message)

副线程:副线程的Handler.post(runnable action)

对于主线程的handler.sendMessage()不再阐述,在activity上声明一个Handler,默认handler构造器就是与主线程的Looper绑定,即在Handler的handleMessage()都是在主线程上的操作。

对于无论主线程还是副线程,针对handler.post(runnable action)的情况,Hanlder的封装如下:

/**
     * UI线程 handler
     **/
private static Handler mUiHandler;

/**
    *副线程handler
    **/
private static Handler SUB_THREAD1_HANDLER;

/**
     * 副线程1
     */
private static HandlerThread SUB_THREAD1;
/**
    * 锁
    **/
private static final Object mMainHandlerLock = new Object();

/**
     * 取得UI线程Handler
     *
     * @return
     */
    public static Handler getMainHandler() {
        if (mUiHandler == null) {
            synchronized (mMainHandlerLock) {
//                if (mUiHandler == null) {
                mUiHandler = new Handler(Looper.getMainLooper());
//                }
            }
        }
        return mUiHandler;
    }

/**
     * 获得副线程1的Handler.<br>
     * 副线程可以执行比较快但不能在ui线程执行的操作.<br>
     * 此线程禁止进行网络操作.如果需要进行网络操作.
     * 请使用NETWORK_EXECUTOR</b>
     *
     * @return handler
     */
    public static Handler getSubThread1Handler() {
        if (SUB_THREAD1_HANDLER == null) {
            synchronized (ThreadManager.class) {
                SUB_THREAD1 = new HandlerThread("SUB1");
                SUB_THREAD1.setPriority(Thread.MIN_PRIORITY);//降低线程优先级
                SUB_THREAD1.start();
                SUB_THREAD1_HANDLER = new Handler(SUB_THREAD1.getLooper());
            }
        }
        return SUB_THREAD1_HANDLER;
    }

 view.post()与handler.post()的区别,使用不当可能导致内存泄露:

相关:http://blog.csdn.net/a740169405/article/details/69668957

如果调用view.post()方法的线程对象被GC-root引用(慎用static修饰Thread),则会造成内存泄漏

因为:

  GC-root->Thread->ThreadLocal->ViewRootImpl->runnable

但是主线程除外,因为虽然主线程的runQueue无法被回收,但是每次执行完performTraversals(),都会将runQueue中的对象执行并清除。

经常我们将线程池对象定为static进行复用,因此需要慎用view.post().

  

 runOnUiThread(runnable action)与view.post(runnable action)的对比

共同点:第一种runOnUiThread,和第二种view.post()都是要求主线程执行,都是通过获取主线程的handler,通过handler.post(),把action封装成message传到Looper的消息循环中,当handler再次处理该message时,直接调用action的run方法。

区别:view.post(runnable action)是在view初始化完成后才执行post(runnnable),而runOnUiThread()是主线程立即执行,如果在onCreate()进行初始化activity时,需要对布局文件中的控件进行调整时,直接使用runOnUiThread代替view.post可能会造成view的信息获取都为空。

因为在activity创建时,生命周期onCreate(),布局文件初始化还没完成,在onCreate()方法上,仅仅是将控件的相关信息写入内存,还没映射到设备上,因此控件信息还没初始化,普遍上初始化完成要在onResume()方法之后。

handler原理

我们可以通过调用handler的post方法,把Runnable对象(一般是Runnable的子类)传过去;handler会在looper中调用这个Runnable的Run方法执行。

Runnable是一个接口,不是一个线程,一般线程会实现Runnable。所以如果我们使用匿名内部类是运行在UI主线程的,如果我们使用实现这个Runnable接口的线程类,则是运行在对应线程的。

View.post原理

View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

 拓展

如果我们要进行耗时的操作,比如下载操作,我们不能再UI线程;也不能直接在副线程直接进行下载,因为我们要考虑多线程直接的顾虑,我们引入线程池。

/**
     * AsyncTask的默认线程池Executor. 负责长时间的任务(网络访问) 默认3个线程
     */
    public static final Executor NETWORK_EXECUTOR;
    static {
        NETWORK_EXECUTOR = initNetworkExecutor();
    }
private static Executor initNetworkExecutor() {
        Executor result;
        // 3.0以上
        if (Build.VERSION.SDK_INT >= 11) {
            //result = AsyncTask.THREAD_POOL_EXECUTOR;
            result = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
        }
        // 3.0以下, 反射获取
        else {
            Executor tmp;
            try {
                Field field = AsyncTask.class.getDeclaredField("sExecutor");
                field.setAccessible(true);
                tmp = (Executor) field.get(null);
            } catch (Exception e) {
                tmp = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<Runnable>());
            }
            result = tmp;
        }

        if (result instanceof ThreadPoolExecutor) {
            //线程数改为CPU 核数 +1
            ((ThreadPoolExecutor) result).setCorePoolSize(FitmixUtil.getPhoneCpuCoreNum() + 1);
        }

        return result;
    }
/**
     * 在网络线程上执行异步操作. 该线程池负责网络请求等操作 长时间的执行(如网络请求使用此方法执行) 当然也可以执行其他 线程和AsyncTask公用
     *
     * @param run
     */
    public static void executeOnNetWorkThread(Runnable run) {
        try {
            NETWORK_EXECUTOR.execute(run);
        } catch (RejectedExecutionException e) {
        }
    }

 AsyncTask

引言:

我不太同意封装好就会影响性能的说法,在我实际的运用中,真正的缺点来自于AsyncTask的全局线程池只有5个工作线程,也就是说,一个APP如果运用AsyncTask技术来执行线程,那么同一时间最多只能有5个线程同时运行,其他线程将被阻塞(注:不运用AsyncTask执行的线程,也就是自己new出来的线程不受此限制),所以AsyncTask不要用于多线程取网络数据,因为很可能这样会产生阻塞,从而降低效率。

AsyncTask是封装好的线程池,比起Thread+Handler的方式,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了;

一个异步任务的执行一般包括以下几个步骤:

1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

原文地址:https://www.cnblogs.com/could-deng/p/6767622.html