多线程开发(一)-安卓系统的线程

第1节 线程概述

安卓应用只有一个主线程-各个组件都是在这个线程中运行。作为组件的之一的Activity就是在这个线程中更新应用界面的,例如,用户点击界面上的一个按钮,按钮得到响应,整个过程就是在这个主线程里。所以这个主线程绝对不可以做耗时的操作。假如在按钮中做了耗时的操作,那么当它进行耗时操作的时候,你去点击界面上的其它按钮是不会有反应的,就好像程序冻在了那里。

我们的代码一旦连续占用这个线程超过一定的时间,系统就会弹出“程序无响应的”提示,这个提示叫做ANR-Applicatin No Response。

这就好比你在正在做一件事情A,突然另一件事情B来打扰你,你不得不停下手头的工作来完成,做完了才能继续之前的工作;这时如果有另外一个人(另一个线程)来帮助你,把事情B全部包揽了,那你就不用分心了。当另一个人把事情B做完后,告诉你一声就可以了。

启动一个新的线程,分担耗时工作的方法是一种异步操作:我让你帮我做一件事情,布置任务后,我就去做其他的事情了,等你做完了再告诉我结果;

与它对应的是同步操作:我让你帮我做一件事情,布置任务后,我啥也不做,就等着你做完了告诉我结果;

例如,一个视频播放应用,获取视频信息所需要的时间是个不能确定的事情。如果视频很少,也许几十毫秒就能完成,如果视频很多(比如几十个),也许就要花二十多秒。

因此,我们可以考虑把获取视频信息的操作放到一个单独的线程thread中进行。启动一个新线程-工作线程thread-查询视频信息,查询完成后,工作线程再将结果通知到主线程,让主线程将查询到结果的结果显示到界面上。因为界面的更新一定要在主线程中进行,不能在别的线程修改,否则系统后提示运行错误。因此我们一定要将查询的结果发送给主线程,让主线程处理界面的更新。

这里就涉及到了线程的创建,工作分配,以及它们之间的配合-信息的传递。

第2节 线程Thread

一个Activity运行在一个主线程上,它不能进行耗时巨大的操作,也不能执行耗时不确定的操作。因此需要在新的线程中进行这些耗时的操作,这种进行耗时操作的线程称作工作线程。

2.1 Thread的简单使用

创建一个新的线程,

  1. 创建一个Runnable,重写它的run()函数,这个函数里用来进行耗时的操作;

    
        @Override
        public void run() {
            //耗时的操作
            while(xxx)
            {
    
            }
        }
    };
  2. Runnable为参数,创建一个Thread,调用Threadstart()方法后,新线程就运行起来了,并执行Runnable中的run()函数;
    Thread thread = new Thread(runnable);
    thread.start();
  3. run()函数执行完毕后,新线程就退出;

在线程执行耗时操作的过程中,有时要取消这个线程的操作,

  1. 最好的办法是在run()函数中增加一个标志位,工作线程会随时检查这个标志位是否被设置上,如果设置上了,就让run()函数,立即返回,

    //设置标志位,为退出线程使用
    boolean needStop = false;
    ......
    Runnable runnable = new Runnable() {
    
        @Override
        public void run() {
            //耗时的操作
            while(!needStop)
            {
    
            }
        }
    };
  2. 如果主线程要取消这个线程的工作,修改这个标志位就好了,
    needStop = true;

2.2 Thread的创建

Thread可以拥有不同的优先级,从低到高有10级。操作系统根据线程的优先级来进行资源调度,优先为优先级高的线程分配CPU资源。在默认情况下,新创建的线程使用默认的优先级NORM_PRIORITY。设置优先级的方式很简单,

Thread thread = new Thread(runnable);
thread.setPriority(Thread.NORM_PRIORITY);

可以为线程取名字,当我们用Android Monitor工具调试应用的时候,就能看到创建线程时它的名字,方便我们观察、调试程序,

Thread thread = new Thread(runnable, "新线程的名字");

有时,一个应用会同时启动多个Thread,在创建它们的时候可以为它们设置ThreadGroup参数,将它们分成一组,便于对这些线程进行统一的管理。比如,中断这个组里所有线程的运行;

ThreadGroup group = new ThreadGroup("线程组");
Thread thread1 = new Thread(runnable, group);
thread1.start();
Thread thread2 = new Thread(runnable, group);
thread2.start();
Thread thread3 = new Thread(runnable, group);
thread3.start();

//中断3个线程的执行
group.interrupt();

2.3 Thread的停止

Thread的停止就是指这个线程的退出。线程的退出原因无外乎两种,

  1. 工作线程的工作完成了;
  2. 工作线程虽然正在进行耗时工作,但是被取消了,要提前结束;

2.3.1 正常退出

Runnable中的run()函数执行完并返回后,当前的Thread就退出了。

2.3.2 使用标志位

  1. run()函数中增加一个标志位,工作线程会随时检查这个标志位是否被设置上,如果设置上了,就让run()函数,立即返回,

    //设置标志位,为退出线程使用
    boolean needStop = false;
    ......
    Runnable runnable = new Runnable() {
    
        @Override
        public void run() {
            //耗时的操作
            while(!needStop)
            {
    
            }
        }
    };
  2. 如果主线程要取消这个线程的工作,修改这个标志位就好了,
    needStop = true;

2.3.3 使用interrupt()方法

interrupt()是线程提供的一个标准的线程退出方法,如果当前的工作线程正被Object.waitThread.joinThread.sleep阻塞,那么使用thread.interrupt()之后,正在运行的线程会抛出一个InterruptedException异常,

Runnable runnable = new Runnable() {

    @Override
    public void run() {
        ...

        while(!needStop)
        {

            try {
                ......
                Sleep(1000); 

            } catch ( InterruptedException e ) {
                needStop = true
            }

        }
    }
};

不过interrupt()方法并不是万能的,不是所有阻塞情况下都能够让线程立即退出。

例如当该线程正在用ServerSocketaccept()方法等待连接的时候,即使调用了这个工作线程的interrupt()方法,该线程还是不会抛出异常的。

它只对Object.waitThread.joinThread.sleep这几种阻塞有效果。对于网络读取数据时代阻塞状态解除是没有效果的。

*对于网络读取数据时造成的阻塞,我们会在以后相应的章节详细介绍解决方法。

2.4 线程之间的同步

线程间的同步就是指线程A执行到一个地方的时候,停了下来,等待线程B的执行结果;等线程B执行出结果后,线程A才能继续执行。

2.4.1 join()方法

最常见的就是主线程的执行,依赖于工作线程的退出。

主线程启动了工作线程以后,有时候需要等到工作线程结束以后再进行接下来的操作。

例如一个Activity,在onCreate()的时候创建了一个工作线程Thread B;后来在用户的要求下,Activity退出,要被销毁了;销毁Activity时,主线程要等到Thread B执行完了才能继续接着进行剩下的清理工作,那么Activity可以在它的onDestroy()函数中可以使用join()方法,等待子线程的结束,

private Thread mTreadB;

@Override
protected void onCreate() {
    super.onCreate();

    mThreadB = new Thread(runnable);
    mThreadB.start();
}

@Override
protected void onDestroy() {
    super.onDestroy();

    mThreadB.join();
    //进行剩下的清理操作
}

join()方法,会一直处于阻塞状态,直到线程B退出。

onDestroy()中使用join()有天生的缺点:不能在主线程中进行耗时不可控的操作(例如这里等待工作线程执行完毕),万一工作线程的退出花费了很长的时间,那就有问题了。这里的场景只是用来举一个例子而已。

2.4.2 wait()方法

利用Objectwait()方法实现线程间的同步,需要线程之间共享一个“锁”-Object对象。

final Object lock = new Object();

当主线程A执行到一个阶段的时候,如果要等待线程B,就把锁置于等待状态,

lock.wait()

此时,线程A就出于阻塞的状态,不能往下执行了。

线程B继续它的运行,当它执行到一个阶段的时候,将锁设置成放行状态,

lock.notify();

此时,线程A的阻塞状态解除,可以继续往下执行了。

时间: 2024-08-03 19:25:56

多线程开发(一)-安卓系统的线程的相关文章

itop4412开发版-安卓系统卸载默认apk使用文档

itop4412开发版的安卓系统默认不是最高权限,可以看见后面最后一个是$符号,如下图 1,所以 想我们需要进入 root 权限,可以看见后面最后一个是#符号,如下图所示.在这个变换中只需 要在超级终端中输入 su 这个命令即可. 接下来的事情我们就是要删除我们要删除的软件了,我们默认的 app 都是 system/app 这个文件中,下面我介绍删除的步骤. 1.进入 system/app 这个文件夹中,找到我们要删除的软件名字,我就删除 Music.apk 为例,进入目录找到该 apk,如下图

(一)Android开发之安卓系统的前世今生

1 什么是Android Android中文名称"安卓",英文含义为"机器人",Android是谷歌旗下的一款基于linux平台的开源操作系统.主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发.该平台由操作系统.中间件.用户界面和应用软件组成.?   底层Linux内核只提供基本功能,其他的应用软件则由各公司自行开发,大部分程序以Java语言编写. 2 Android的发展历程(来源百度百科) 2003年10月,Andy Rubi

友善之臂tiny4412-1306开发板安卓系统烧写

折腾了很久,终于烧写成功.不废话,咱们说说流程吧. 首先,我们需要有一个基于tiny4412的kernel,从友善之臂官网获取. 然后解压: 1.tar -xvf  linux-3.5 .... 然后cp  tiny4412-android_deconfig  .config 接下来make zImage 在 arch/arm/boot/生成对应的zImage ,这个也就是我们的kernrl. 当然在此之前,我们需要安装交叉编译工具arm-linux-gcc.没有的话就装上这个环境,arm-li

安卓系统移植和开发第一章心得体会

进入大三第二学期,我们学习了安卓系统的移植和驱动开发,这是我们继上学期安卓嵌入式开发对安卓系统的进一步学习.第一章我们学习了安卓系统的编译和移植实例,首先我们要下载课程所需要的软件环境,现有的环境是一套能正常运行Linux2.6.21的EZ6410硬件系统和移植目标在EZ 6410系统上运行的安卓2.3系统,在移植过程中主要涉及的步骤有,安装Linux安卓的内核,安装交叉工具链,移植内核支持的Ez6410 平台,安装安卓SDK,获得安装安卓系统的根文件系统.设置系统环境,完成安卓正常启动,嵌入式

Java多线程开发系列之一:走进多线程

对编程语言的基础知识:分支.选择.循环.面向对象等基本概念后,我们需要对java高级编程有一定的学习,这里不可避免的要接触到多线程开发. 由于多线程开发整体的系统比较大,我会写一个系列的文章总结介绍 多线程开发的概念.使用.线程状态.同步.线程池.希望与大家共勉. 在第一部分,也就是本节我们先介绍下 什么是多线程程序.线程和进程又是什么,以及为什么要搞多线程. (一)什么是多线程程序 多线程听上去是非常专业的概念,其实非常简单,我们在日常生活中,经常的接触到多线程. 比如 (1)在工厂,工人努力

iOS多线程开发--NSThread NSOperation GCD

多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势必影响用户体验.早在单核处理器时期 就有多线程,这个时候多线程更多的用于解决线程阻塞造成的用户等待(通常是操作完UI后用户不再干涉,其他线程在等待队列中,CPU一旦空闲就继续执行, 不影响用户其他UI操作),其处理能力并没有明显的变化.如今无论是移动操作系统还是PC.服务器都是多核处理器,于是“并行

Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread

事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一.事件派发线程的前世今生 事件(Event)派发(Dispatch)线程(Thread)简写为EDT,也就是各个首字母的简写.在一些书或者博客里边也将其译为事件分发线程.事件调度线程.巴拉巴拉,总之,知道这些名字就行.笔者认为这里翻译成派发更准确点. 熟悉Swing和awt编程的小伙伴对事件派发线程

Java多线程开发系列之四:玩转多线程(线程的控制2)

在上节的线程控制(详情点击这里)中,我们讲解了线程的等待join().守护线程.本节我们将会把剩下的线程控制内容一并讲完,主要内容有线程的睡眠.让步.优先级.挂起和恢复.停止等. 废话不多说,我们直接进入正题:  3.线程睡眠  sleep() 所有介绍多线程开发的学习案例中,基本都有用到这个方法,这个方法的意思就是睡眠(是真的,请相信我...).好吧,如果你觉得不够具体,可以认为是让当前线程暂停一下,当前线程随之进入阻塞状态,当睡眠时间结束后,当前线程重新进入就绪状态,开始新一轮的抢占计划!

安卓系统下的多线程断点下载实现

近期研究多线程下载,写了个demo.整理下来,或许会对别人有帮助. 多线程下载的话一般开启两到三个线程吧.假设线程太多的话时间会浪费在线程的切换上,倒是浪费了大把的时间.线程多了也不是一件好事. 原理的话看我的还有一篇博文,其实是将代码移植到了安卓系统上.java实现的多线程下载demo public class MainActivity extends Activity { protected static final int DOWNLOAD_ERROR = 1; private stati