android线程学习心得

有一篇关于android线程讲的非常好,大家可以参考下,其中有一句话讲的非常好,就拿来做开篇之句:

当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理,所以主线程通常又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

咱新手在第一次接触android线程的背景是这样的:

1:需求中要进行一次http交互

步骤大概是这样

1.1:点击提交按钮

1.2:http同步获取结果

1.3:将结果写入到TextView上。

这样写,执行时会报错,不能在UI线程上发起同步的http网络请求。(耗时可能会导致ANR,application not respond)

2:OK,既然不能在UI线程上发起http请求,那咱新开一个子线程。

于是步骤变成了这样:

1.1:点击提交按钮

1.2:开启一个子线程

1.3:http同步获取结果

1.4:将结果写入到TextView上。

一执行,BOOM,报错,大概意思是子线程里不能直接操作UI元素,为什么呢?请再看看本文开头的句子(线程安全)。

3:好吧,那就在子线程里通过Handler来操作UI的元素吧

最后,测试通过的代码大概是这样

1.1:点击提交按钮

1.2:开启一个子线程

1.3:http同步获取结果

1.4:将结果作为Message,传递给Handler

1.5:在Handler里将结果写入到TextView上。

tips:如果你用android studio 的code inspect功能,就会发现,它提示你,这样的做法可能会导致内存泄露,为什么呢?因为Handler里持有了UI里面的元素的引用,当UI结束掉自己时(此时handler还在耐心等待http访问结果),发现某个元素被Hnadler持有,就不能被GC回收了,这就会造成内存泄露。解决办法很简单,Handler改为static,消除内部匿名引用,同时,将对象的引用改为WeakReference<>即可。一篇详细解释原因的文章具体代码参考如下,来源于咱的通讯录APP

static class ImageDoneHandler extends Handler {
        WeakReference<ImageView> imageView;
        WeakReference<Bitmap> bitmap;
        WeakReference<String> url;
        ZImage.CacheType cacheType;

        ImageDoneHandler(Looper looper, ImageView _imageView, Bitmap _bitmap, String url, ZImage.CacheType cacheType) {
            super(looper);
            imageView = new WeakReference<>(_imageView);
            bitmap = new WeakReference<>(_bitmap);
            this.url = new WeakReference<>(url);
            this.cacheType = cacheType;
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what != MSG_IMAGE_LOAD_DONE)
                return;

            ImageView _imageView = imageView.get();
            Bitmap _bitmap = bitmap.get();
            String _url = url.get();
            if (_imageView == null || _bitmap == null)
                return;

            if (_url.equals(_imageView.getTag().toString())) {
                _imageView.setImageBitmap(_bitmap);

                if (cacheType == ZImage.CacheType.DiskMemory)
                    ZImage.getInstance().putToMemoryCache(_url, _bitmap);
            }
        }
    }

写到现在,咱还是不懂,为啥Handler里面就可以改UI里面的元素呢?

这时候就需要理解android异步消息处理的四大部分了( Message、 Handler、 MessageQueue 和Looper)。

咱才疏学浅,因此下面的知识来源于《第一行代码》书籍的节选片段,非常的精彩,值得反复阅读,大家深呼吸下,系好安全带,开始咯~

先来一张异步消息处理的整个流程图解,大家对照着图解看更直观(图片来源《第一行代码》)

1. Message

Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线
程之间交换数据。上一小节中我们使用到了 Message 的 what 字段,除此之外还可以使
用 arg1 和 arg2 字段来携带一些整型数据,使用 obj 字段携带一个 Object 对象。

2. Handler

Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消
息一般是使用 Handler 的 sendMessage()方法,而发出的消息经过一系列地辗转处理后,
最终会传递到 Handler 的 handleMessage()方法中。

3. MessageQueue

MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。
这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个 MessageQueue
对象。

4. Looper

Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop()方法后,就会
进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将它取
出,并传递到 Handler 的 handleMessage()方法中。每个线程中也只会有一个 Looper 对象。

了解了 Message、 Handler、 MessageQueue 以及 Looper 的基本概念后我们再来对异步消息处理的整个流程梳理一遍。

1:首先需要在主线程当中创建一个 Handler 对象,并重写handleMessage()方法。

2:然后当子线程中需要进行 UI 操作时,就创建一个 Message 对象,并通过 Handler 将这条消息发送出去。

3:之后这条消息会被添加到 MessageQueue 的队列中等待被处理,

4:而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handler的 handleMessage()方法中。

5:由于 Handler 是在主线程中创建的,所以此时 handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行 UI 操作了。

AsyncTask异步任务类

幸运的是,在一些大部分的场合,android为我们提供了一个AsyncTask异步任务抽象类,通过实现他可以非常方便的执行各种耗时操作,而不必担心UI线程被卡住,同时也避免了原生异步线程与UI线程交互繁琐的写法。

在继承时,我们可以指定三个泛型参数类型(都是引用类型哦,值类型的记得也要改成引用类型,比如int ->Integer),它们分别是:

1:参数Param ,传递给子线程执行的

2:进度提示Progress,如果需要实时在界面更新异步处理进度,就可以通过这个参数反馈

3:结果Result,在主线程里,我们就获取到了异步执行的结果。

来个例子吧

    /**
     * http请求用户是否存在,穿入http的url地址,返回布尔类型是否存在
     */
    class QueryUserExistTask extends AsyncTask<String,Void,Boolean>
    {

        /**
         * 在异步请求处理之前
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * 异步处理,这里同样不能交互UI元素哦
         * @param params
         * @return
         */
        @Override
        protected Boolean doInBackground(String... params) {
            return null;
        }

        /**
         * 异步处理完了,切回到主线程,返回处理结果
         * @param aBoolean
         */
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
        }
    }

调用例子:

  new QueryUserExistTask().execute("http://192.168.1.1/u/kimmy");

总结:

1:保持主线程流畅度很重要,耗费大量资源的工作尽量放到子线程完成。

2:大部分情况下AsyncTask都能胜任异步的重任。

3:高并发的异步任务、或者异步任务之间彼此需要调度的情况,需要自己编写线程池来处理

时间: 2024-10-03 12:27:36

android线程学习心得的相关文章

第9章 Android硬件抽象层 学习心得

第9章 Android硬件抽象层 心得体会 这一章主要概括的介绍了安卓硬件抽象层的主要内容,对安卓的HAL做了一个总体的介绍.通过对第9章的学习,使我对HAL有了一个感性的认识. 首先我了解了Android的硬件抽象层的定义,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节.也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间

Android学习心得(16) --- Dex文件结构实例解析(2)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下通过一个实例来分析dex文件结构和组成. 参考Leb128数据类型 Android学习心得(5) --- dex数据类型LEB128 参考实例分析学习理解dex文件结构Android学习心得(15) --- Dex文件结构解析(1) 参考baksmali工具使用Android学习心得(4) --- MAC下smali文件编写与运行 1.编译 我们通过一个例子来分析dex文件的构成 创建一个Hello.java文

Android Window PhoneWindow Activity学习心得--第三弹

Android Window  PhoneWindow Activity学习心得--第三弹 前面 我们完成了从Activity到PhoneWindow的整体跨度 正如我们所知道的与Activity组件关联的一个应用程序窗口视图对象关联一个ViewRoot对象,而将 一个Activity组件的应用程序窗口视图对象与一个ViewRoot对象关联是通过该Activity组件所使用的 窗口管理器(WindowManager)来执行的. 在我们初始化DecorView完成之后,我们需要关联应用程序窗口视图

Android学习心得(13) --- Android代码混淆(1)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下最新的ADT环境下如何进行Android混淆 在新版本的ADT创建项目时,混码的文件不再是proguard.cfg, 而是project.properties和proguard-project.txt. 我们把之前配置都配置好了,proguard已经在包含在sdk中了. 环境配置参考Android学习心得(1) --- MAC下Android环境的搭建 新建一个项目的时候,会自动生成project.proper

多线程及线程池学习心得

一.线程的应用与特点 多线程是程序员不可或缺的技术能力,多线程技术在各个方面都有应用,特别在性能优化上更是起到至关重要的作用.但是,如果多线程写得不好,往往会适得其反,特别是高并发时会造成阻塞.超时等现象.多线程具有以下特点:1.独立性,拥有自己独立的资源,拥有自己私有的地址空间:2.动态性,进程具有自己的生命周期和各种不同的状态:3.并发性,多个进程可以在单个处理器上并发执行,不会相互影响,并行是指同一时刻有多条指令在多个处理器上同时执行.线程是进程的组成部分,一个进程可以拥有多个线程,一个线

Android学习心得(14) --- Android代码混淆(2)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下Android混淆如何自定义proguard参数来实现 环境配置参考 Android学习心得(1) --- MAC下Android环境的搭建 签名参考Android学习心得(12) --- APK签名keystore 代码混淆Android学习心得(13) --- Android代码混淆(1) dex2jar&jd-guiAndroid学习心得(8) --- MAC下dex2jar和jd-gui使用 1.介绍p

Android学习心得(11) --- MAC下Android反编译(3)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 在前一章我们学习了反编译. 这一章再次学习一下两个demo. 两个demo下载地址 KeygenMe#1.apk crackme0a.apk 链接: http://pan.baidu.com/s/1kTs11Tt 密码: davk KeygenMe#1.apk 1.首先在模拟器安装测试 由于模拟器检测不到wifi mac地址,所以会报错. 我们首先需要将检测代码跳过 先通过dex2jar和jd-gui查看源代码进行分析 查看了m

Android 学习心得 快速排序

快速排序(Quicksort) 是对冒泡排序的一种改进,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列. 设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序. 值得注意的是,快速排序不是一种稳定

Android 数据库ORM框架GreenDao学习心得及使用总结&lt;一&gt;

Android 数据库ORM框架GreenDao学习心得及使用总结<一> 转: http://www.it165.net/pro/html/201401/9026.html 最近在对开发项目的性能进行优化.由于项目里涉及了大量的缓存处理和数据库运用,需要对数据库进行频繁的读写.查询等操作.因此首先想到了对整个项目的数据库框架进行优化. 原先使用android本身内置的sqllite,也就是用的最基本的SQLiteOpenHelper方法,这种方法对自己来说比较方便易懂.但是在使用过程中感觉很繁