分类目录归档:大话Android应用开发

开发环境搭建 第3节 Android SDK与Android Studio

第3节 Android SDK与Android Studio

3.1 Android SDK介绍

SDK是Software Development Kit的缩写。百度百科上的解释是:“一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合。”说明它是开发工具的集合。

Android SDK就是:为了方便我们这些第三方开发人员在Android平台开发一个应用程序,Google提供了一套工具给我们使用。简单的讲,这些工具包括:Android平台的应用程序编程接口—Application Programming Interface-简称API,这些API配套的说明文档,以及将代码编译成应用程序的编译工具,和各种能够方便我们写应用程序的其他工具。

SDK结构说明

3.2 Android Studio介绍

Android Studio是Google推荐的官方的集成开发环境。“集成开发环境”包含程序员写程序的编辑器、编译器、调试工具,极大提高程序员开发的效率。

其实程序开发并不一定要使用“集成开发环境”,使用一个最简单的记事本就可以编写代码。然后使用命令行工具,控制编译器对代码进行编译,就能得到我们最终希望的应用程序。

但是这样的开发方式对大多部分人来讲都很不方便。所以需要一个集成了这些功能的开发工具。这类开发工具除了集成简单的编辑编译功能,还会提供很多的有利于提高编程效率的工具,例如快速查找代码,快速定位,自动生成程序模版,查看调试信息等功能。

下图粗略的展示了Android Studio “集成开发环境”的效果图,从图中,我们可以看到文件浏览、代码编辑、调试信息显示和文字查找等开发经常要用到的功能被集成到了一起。

AS布局"

“集成开发环境”到全称是Intergraded Development Environment,简称IDE。针对不同的平台,Android Studio拥有Windows/Linux/MacOS三个不同的版本。

在Android Studio推出之前,Google提供的是Eclipse IDE,不过随着时间的推移,Eclipse的缺点(运行缓慢,占用内存率高)开始显现。后来Google终于推出了Android Studio作为Eclipse的代替IDE工具。

在今后的讲解中,我们将基于Android Studio,而曾经的Eclipse+ADT开发工具会很快的退出历史的舞台。

3.3 Android SDK与Android Studio的下载安装

针对不同的操作系统,Android SDK都有对应的版本。我们可以在developer.android.com/sdk/index.html找到他们的下载地址。

SDK下载页面

同样的,针对不同的操作系统,Android Studio也有对应的版本。

AS下载页面

国内的网络情况特殊,大家可能不能打开安卓开发的官网。这时大家可以到安豆网进行下载。

3.3.1 Windows系统

对于打算使用Windows系统进行Android应用开发的人来说,事情非常简单,Google提供了Android Studio和Android SDK的打包下载。

SDK下载地址

首先将下载好的Android SDK和Android Studio整体包,放到桌面上,双击安装包,

SDK下载到桌面

开始安装,

结构说明

点击下一步,

结构说明

接受许可协议,

结构说明

设置安装路径,

结构说明

开始安装,

结构说明
结构说明

安装结束,

结构说明

3.3.2 Mac OS系统

使用Mac OS系统的开发者,可以到官网分别下载Mac OS版本的Android SDK和Android Studio。

结构说明
结构说明

Android SDK下载后放到桌面,

结构说明

双击解压到当前文件夹,

结构说明

之后,将该文件夹,移动到开发者希望放置的位置。这里我们将它移动到了Application目录中。

Android Studio下载后放到桌面,

结构说明

双击启动,在弹出的对话框中,将Android Studio图标,拖动到Application图标上(安装到Application目录中),完成安装。

结构说明

3.4 Android SDK的配置

安装完成Android SDK和Android Studio以后,我们需要对两者作一个简单的配置。

3.4.1 配置Android SDK

对于Windows用户,进入到SDK所在目录(默认安装时,路径在C:\Users\用户名\AppData\Local\Android\sdk),双击SDK Manager.exe,启动Android SDK Manager。
对于Mac OS用户,进入到SDK所在目录(Application\android-sdk-macosx\tools\),双击android,启动Android SDK Manager。
可以看到如下界面。

结构说明

这时Android SDK Manager会访问google的服务器,获取最新Android SDK组件的版本并罗列出来,用户可以根据自己的需要,在前面的方框中打钩,选择想要下载或者更新的组件。
由于国内情况特殊,Android SDK Manager不能连接到Google官方的服务器,无法获取SDK更多信息。因此我们可以通过修改设置进行变通。
设置方式如下:
首先去修改网络访问设置:
对于Windows用户,选择Tools->Options,
对于MacOS用户,选择Android SDK Manager->Preferences
我们先尝试修改如下,将https访问强制修改成http访问

结构说明

然后看Android SDK Manager是否能正常访问Google服务器,如果可以,那就最好了。

3.4.2 Android SDK组件安装

Android SDK Manager列出了我们可以获取到的关于Android应用开发的所有工具,这些工具分了很多不同的子类:在status状态栏中标注了我们没有安装的工具。

整个SDK的工具很多,但不是每一个都要求我们必须安装。因为全部下载的话会非常的占用时间和消耗硬盘空间。
作为初学者,我们要安装的工具有:

Android SDK Tools;

Android SDK Platform-tools;

Android SDK Build-tools;

Android X.X SDK platform:X.X代表android SDK platform的版本,最新的版本一般都会兼容之前的版本,作为初学者,我们使用最新的。

Sources for Android SDK;

Extra中的Android support library、Android support Repository和Google Repository;

如果是安装Windows系统,还需要安装Extra中的Google USB Driver,为后面连接设备到电脑做准备。
其他工具大家可以根据自己的兴趣,选择性的下载,看看里面是什么内容。这些在后面的学习中,如果需要使用那些没有下载的工具,我们再来下载。

现在我们就选择性的安装最少的工具。
选择“Install packages”

结构说明

接受许可协议,点击“Install”开始安装。

结构说明

进度条显示当前的安装状态

结构说明

至此Android SDK已经安装成功。

3.4.3 Android SDK中API Level与Android 版本间的关系

从Android推出到现在(2015年),安卓已经发布了代号(用字母表示):C、D、E、F、G、H、I、J、K、L、M,不同代号说明这是一个大的系统升级;而每个代号都会对应一个到两三个不同的Android版本(数字表示):4,4,2 4.4.4 5.0 5.1,同一个代号之间的不同版本说明这是一个小的系统升级;每个版本都对应着不同的API Level(用数字表示)。

结构说明

3.5 Android Studio配置

Android Studio在第一次启动之前会连接到Google的服务器,检查SDK更新。但是受限于国内的网络环境,无法连接到Google服务,所以第一次启动会卡在更新界面很久。因此我们要让Android Studio跳过第一次启动的SDK更新检查。
打开文件idea.properties
Windows平台在C:\Program Files\Android\Android Studio\bin
MacOS平台在Application\Contents\bin
在文件最后添加如下一行

结构说明

之后启动Android Studio,选择Configure,

结构说明

选择Settings->Updates(Mac系统下如果没有Settings,可以先选择SDK Manager,再切换到Updagtes),

结构说明

将自动升级关闭掉,

结构说明

再选择“Android SDK”,可以看到下图的标注框,假如这里是空的,请点击设置Android SDK的位置。

结构说明

这里设置SDK的位置。

结构说明

设置完成,

结构说明

至此Android Studio的安装与配置工作就全部完成了。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

开发环境搭建 第2节 JDK

第2节 JDK

2.1 JDK介绍

Android Studio和Android SDK中很多模块都是用JAVA开发的,所以需要使用到JDK。JDK的全称是Java Development Kit,是其他两个组件运行的基础。JDK包含了Java程序运行的基础—JRE,以及开发JAVA程序的工具。它拥有一个JRE(Java Runtime Environment),这才是整个操作系统运行Java程序所需要的环境。

都说JAVA是跨平台的,它跨平台的特性就是依靠JRE实现的。JRE将操作系统与JAVA应用隔离开,JAVA应用运行在JRE当中。由此可以知道,针对不同的平台(Windows、LInux、MacOS),JDK都有对应的版本语。但是用JAVA开发的应用,只需要面对的是JRE,与JRE所在的平台就没有关系了,所以大家才会说JAVA是跨平台的。

JDK-JRE关系说明

现在有两种JDK,一个是Sun JDK(也就是Oracle JDK, Sun这家公司被Oracle收购了),另一个是Open JDK。安卓的“系统开发”需要在Linux系统下进行,它需要使用OpenJDK。目前我们只是进行“安卓应用”开发,使用SunJDK就可以了(应用开发也可以使用OpenJDK,不过这里我们为了安装的方便,选择使用SunJDK)。
Android Studio和Android SDK的运行离不开JDK。因此在安装他们之前,我们首先要安装JDK。

2.2 JDK下载

Oracle的官网 提供了官方Sun JDK的下载地址。

选择“JDK下载”,

之后,选择接受下载协议,

JDK下载许可

网站会列出当前推荐的JDK版本供开发者下载。在选择JDK版本时,Android Studio或者Android SDK会在它们的发布文档中指出这些组件需要的JDK版本。不过一般来讲,使用JDK较新的版本就可以。

通过上面的图,我们可以看到JDK除了有版本的分类,还明确指出了对应的操作系统平台(Window LInux MacOS Solaris),以及这些系统的系统位数(32bit 64bit)。

操作系统 系统位数 下载包名字
Windows 64bit jdk-XXX-windows-x64.exe
Windows 32bit jdk-XXX-windows-i586.exe
MacOSX 64bit jdk-XXX-macos-x64.dmg
Ubuntu 64bit jdk-XXX-linux-x64.tar.gz
Ubuntu 32bit jdk-XXX-linux-i586.tar.gz

大家要根据自己使用的操作系统环境来进行选择下载,上面的表格列出了常用的系统所对应的下载文件名(其中XXX代表JDK的版本号,例如“8u65”)。

JDK对应平台下载地址

点击对应的下载链接就可以将安装包保存到各自的电脑当中。

2.3 JDK Windows 安装

Windows平台应该是新入门的开发者使用最多的平台。无论您使用的是Windows 7、Windows 8或者Windows 10,下面的内容都是适用的。这里我们使用Windows 10 64bit版本的操作系统作为讲解的示例。
这里我们将已经下载好的针对Windows平台的JDK安装包放在桌面上。

JDK保存到桌面

双击安装包,进入安装流程。

JDK安装1

点击下一步,

JDK安装2

接受默认安装目录或选择自己喜欢的安装目录,点击下一步,

JDK安装3

开始自动开始安装JRE,

JDK安装4

安装进行中,

JDK安装5

安装完成,

JDK安装6

至此,在Windows系统下的JDK安装过程结束。

最后我们来验证一下安装是否成功。Win+R启动“运行”,输入“cmd”命令,启动cmd命令行窗口,输入“java”。

JDK安装检查

假如命令行窗口输出了类似于上图的内容,说明JDK安装成功了。

2.4 JDK Mac安装

苹果的Mac OS也是很多开发者喜欢的操作系统,这里我们将介绍Mac OS下JDK的安装。

这里我们将已经下载好的针对MacOS平台的JDK安装包放在桌面上。

JDK下载完成

双击安装包,进入安装流程。

JDK安装1

双击图标,

JDK安装2

点击下一步,

JDK安装3

安装完成,

JDK安装4

至此,在Mac OS系统下的JDK安装过程结束。

最后我们来验证一下安装是否成功。启动terminal命令行窗口,输入“java”。

JDK安装完成检查

假如命令行窗口输出了类似于上图的内容,说明JDK安装成功了。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

开发环境搭建 第1节 概述

第1节 概述

这一章将为大家详细的介绍安卓应用开发平台的搭建过程:把一台普通的Windows系统或Mac系统的电脑,配置成为可以进行安卓开发的电脑。针对国内访问安卓官网吃力的问题,我们会介绍一些其他的方法,解决开发平台搭建的问题;接下来会使用搭建好的开发环境,创建一个最为简单的应用,用来验证我们搭建的是否成功;最后连接上真实的设备,将创建的应用运行到设备上。

要进行安卓开发,我们要做好两方面的准备,一方面是硬件的准备,一方面是软件的准备。

1.1 硬件准备

硬件方面需要做如下准备:
1. 一台用来开发的电脑,台式机、笔记本都可以。安卓开发对电脑对配置要求不高,只要你现有的系统能够流畅运行就可以,并没有特殊的限制;操作系统可以是Windows操作系统、Linux操作系统,也可以是Mac OSX操作系统。
2. 一部安卓系统的手机或者平板电脑。现在市场上主要是安卓4.0版本以上的系统,用这样的设备开发就很合适,如果有最新版本的系统,当然更好,你就可以看到新版本上更多的特定。不过对于新手来说,即使系统不是最新的,也丝毫不会影响大家的学习,开发本身就是一个承前启后的过程,在老版本上学到的东西在今后新版本上同样适用。虽然安卓开发平台中自带了设备模拟器(设备模拟器可以在电脑主机上模拟出一个安卓设备,例如手机或者平板电脑,即使用户没有真实的安卓设备也能进行开发),不过如果使用一台真实的设备会更好。这样不用长时间等待模拟器的慢速启动了,也不用受到不能使用某些硬件的限制。现在的安卓设备很便宜,也许各位的手机就是安卓系统,直接拿过来使用再方便不过了。
3. 一根手机与电脑的数据连接线,它将手机和电脑连接起来,完成调试工作。现在的连接线通常是micro USB数据线。实际上也可以通过WiFi连接的方式将手机和电脑连接起来,不过在无线连接的过程中可能会遇到这样或那样的问题,所以作为初学者,我们就使用真实的数据线来避免那些麻烦吧。

1.2 软件准备

软件方面需要做如下准备:
1. 安卓开发环境由“Android Studio”和“Android SDK”两大部分组成。而这两个组件的运行都要依赖JDK。所以搭建开发环境,就需要我们安装Android Studio、Android SDK和JDK三个部分。
2. 开发安卓的人一定要知道developer.android.com和source.android.com两个网站,尤其是前者,它是每个应用开发者都应该关注的网站。它们是google提供的安卓开发官方网站,有关安卓的一切最新信息和开发资料都可以在上面找到。不过由于国内的问题,这两个网站经常无法访问。不过没有关系,这里我们推荐国内的安豆网,里面收录了官方网站上所有有用的内容,更重要的是它还提供许多相关开发工具的下载。在后面的课程中,如果需要的资料无法通过安卓官网获取,可以先在这个网站找找。

开发者官网1
开发者官网2

/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

多线程开发 第5节 AsyncTask

更新时间 修改意见
2016-08-02 陈敏

第5节 AsyncTask

首先举个例子,获取手机上视频信息所需要的时间是个不能确定的事情。如果视频很少,也许几十毫秒就能完成,如果视频很多(比如几十个),也许就要花二十多秒。

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

代码的执行一旦连续占用这个线程超过一定的时间,系统就会弹出“程序无响应的”提示。

因此,我们可以考虑把获取视频信息的操作放到一个单独的线程thread中进行。

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

5.1 异步操作

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

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

获取视频信息是个异步操作,启动一个新线程-工作线程thread-查询视频信息,查询完成后,工作线程再将结果通知到主线程,让主线程将查询到的结果显示到界面上。界面的更新一定要在主线程中进行,不能在别的线程修改,否则系统会提示运行错误,这一点相当重要。因此我们一定要将查询的结果发送给主线程,让主线程处理界面的更新。

5.2 异步操作的方案

安卓系统提供的异步操作方案有:

  1. 创建工作线程thread和Handler,利用Handler在工作线程和主线程之间传递数据;
  2. 使用AsyncTask帮助类,AsyncTask中封装了工作线程,通过AsyncTask完成工作线程和主线程之间的数据传递;

第1种方案,已经在第3节里面介绍过了。

这里我们介绍第2种方案。

AsyncTask实际上它也是通过方案1实现的,只不过Android SDK对这套机制做了进一步封装,让使用者用起来更加方便。

AsyncTask适用于,

  1. 使用场景简单,只是单个任务的异步操作,没有多个线程之间的数据同步考虑;
  2. 使用方便,不用考虑太多的新线程创建的细节;

5.3 AsyncTask的使用

AsyncTask需要被继承成为一个新的子类来使用,在被继承时,要指定三种参数的类型-Param Progress Result,还需要实现doInBackground(Param...)函数,此外通常还要实现onProgressUpdate(Progress...) onPostExecute(Result) 两个回调函数。

  1. doInBackground(Param... params)函数:传入参数的Param类型就是AsyncTask中指定的Param类型。它运行在新创建的工作线程当中。

    使用MyTask时,要在主线程中使用excute()方法传入不定长参数,让Task运行起来,

    不定长参数会以数组的形式传递到doInBackground()函数当中,

  2. onProgressUpdate(Progress... progresses)函数:传入参数的Progress类型就是AsyncTask中指定的Progress类型。它运行在主线程中。

    doInBackground()中执行的是一个很耗时的工作,有时需要向主线程报告当前的运行状况,这就要使用到publishProgress()函数,publishProgress()也是使用的不定长参数,

    不定长参数会以数组的形式传递到onProgressUpdate()函数当中,

  3. onPreExecute()函数:当Task开始执行的时候,通过这里告诉主线程Task要开始工作了。它运行在主线程当中。

  4. onPostExecute(Result result)函数:传入参数的Result类型就是AsyncTask中指定的Result类型。它运行在主线程当中。

    doInBackground()函数返回的类型也是Result

    返回的结果作为参数传递给onPostExecute()函数,

  5. onCancel()函数会在调用者取消AsyncTask的工作的时候被触发。它运行在主线程当中。

    要取消AsyncTask的工作,首先要在主线程中调用cancel()方法,

    因为在doInBackground()中执行的是一个很耗时的工作,需要时不时的检查自己是否被取消执行了,

    最后,onCancelled()函数会被触发,这个函数会在主线程中被执行,

    一个AsyncTask只能被excute()一次,一旦运行之后,就不能重复的对它再次调用excute()了。


综合上面的分析,自定义一个AsyncTask的方法如下,

使用一个AsyncTask的方法如下,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

多线程开发 第4节 HandlerThread

更新时间 修改意见
2016-08-02 陈敏

第4节 HandlerThread

3.1章节介绍了Thread、Looper与Handler的关系。为了让开发者能简单的使用具备LooperThread,而不需要开发者写代码组合它们,Android SDK提供了HandlerThread

HandlerThread的原理很简单,

  1. 创建一个线程A;
  2. 在A上创建一个Looper

这样这个新创建的线程就可以不停的接收和处理任务了。

4.1 HandlerThread的使用

  1. 创建并启动HandlerThread

  2. 获取可以访问HandlerThread对象任务队列的Handler;把Thread的Looper指定给Handler

  3. 使用HandlerHandlerThread对象布置任务;

  4. HandlerThread对象退出;

4.2 HandlerThread与Handler、Thread名称区分

HandlerThread与Handler、Thread名字相似,这里再做一下澄清。

  • Thread:线程,单纯的一个线程;
  • HandlerThread:具备任务队列的线程,它就是一个线程;
  • Handler:可以访问指定线程任务队列的一个工具;

/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

多线程开发 第3节 Handler

更新时间 修改意见
2016-08-02 陈敏

第3节 Handler

多个线程之间除了有“执行的同步”关系,还有“数据的共享”关系,以及“功能的委托”关系。

例如之前提到的视频信息显示到列表上,

  1. 委托数据查询功能:主线程启动一个工作线程thread-查询视频信息;
  2. 委托数据的界面更新功能:工作线程查询完成后,因为不能够修改界面元素,所以必须将结果通知到主线程,委托主线程将得到的结果显示到界面上。

为此,Android SDK提供了Handler帮助各个不同的线程之间传递数据或委托功能处理。

3.1 Thread、Looper与Handler的关系

一个线程创建并start以后,就会开始执行它Runnable中的run()方法。如果要这个线程一直运行而不退出的话,就要在里面设置一个循环,

Looper可以为Thread创建这样一个循环的环境,

Looper具有一个消息队列,可以存放任何线程(包括它自己)给自己布置的任务。这些任务被一条一条的放在队列当中,在loop()函数中被取出来-执行,然后又取出来-执行,周而复始,永不停止。

Handler是和Looper相关联的,通过Handler,任何线程可以把需要完成的任务放到Looper的消息队列里面。

Thread就好比一条产线,Looper中的消息队列就是这个流水线上的传送带,带子上分成了很多格,每一格放要处理的原料和处理这些原料的工人(原料和工人打包成了一个Message)。等轮到格子上的工人时,工人才能开始处理格子里放的原料。Handler就像是一个转运工具,提供给别的模块使用。这个工具可以把原料和工人放到产线的传送带上。

注意,一条产线只有一条传送带(Looper);但可以有多个为同一条产线提供转运服务的转运工具(Handler)。

所以使用这种产线的流程是,

  1. 创建一条产线A;
  2. 在这条产线A上创建一条传送带;
  3. 当别的模块B要向这条产线布置任务的时候,就要创建在产线A上工作的工人;
  4. B要告诉工人携带哪些原料,怎么处理这些原料;
  5. 等轮到产线A上对应的格子被处理的时候,上面的工人就开始操作了;

3.2 Handler的使用

Handler最常见的使用场景是:

  1. 为了进行耗时操作,主线程创建一个工作线程完成耗时操作;
  2. 工作线程开始耗时工作,时不时向主线程发送消息,告知当前完成的状态;
  3. 主线程根据工作线程的报告,更新界面元素,展示工作进度;

为了实现这样的功能,我们首先需要知道,

  1. 每个Activity都是运行在程序的主线程当中的;
  2. 只有主线程能修改界面元素,其他线程修改的话,应用会崩溃;
  3. 主线程在创建之后,系统已经为它设置了Looper,主线程已经有了消息处理的队列(生产流水线和流水线上的格子);

Handler处理这种场景时,我们有2种方式。

3.2.1 使用sendMessage()

  1. 创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的“工人”;
  2. 重写HandlerhandleMessage()函数;
  3. handleMessage()函数中,根据msg.what的不同,进行对应的处理;

任何想要布置任务的线程只需要利用HandlersendMessage()函数,就能把任务放到主线程的消息队列中,

这样主线程的消息队列中就有了这个新任务。等到这个消息被处理的时候,主线就可以根据参数修改界面元素了。

除了使用

也可以使用

这里使用的Message就携带了“工人”和“原料”,Message通过Handler对象获取,

发送Message可以直接发送,也可以延时发送,

3.2.2 使用post()

  1. 创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的工人

  2. 在工作线程中使用Handlerpost()方法,里面的Runnable就是在Handler所服务线程中运行;

还可以使用

3.3 Handler任务的移除

大多数情况下,当Activity退出以后,需要将它布置给主线程的任务给移除掉。如果不移除,可能会遇到大麻烦。可以想象一下,

  1. 工作线程通过Handler给主线程布置了一个任务-根据一个参数修改界面显示,此时这个任务已经放到了主线程的任务队列里面;
  2. 用户突然退出了这个Activity,Activity被销毁了,但是主线程是不会退出的(Activity只是主线程上长的一个果子,果子被摘了,但是不会影响树的存在);
  3. 主线程的任务队列依次执行到了工作线程布置的任务,任务要求更新Activity上的界面元素,但是这个Activity已经被销毁了;

这时,程序的行为表现有可能和你希望的不同,出现一些与期望不符的现象,严重时,可能会造成程序崩溃。所以当一个Activity退出销毁的时候,一定要把相关的任务移除。这样做还有助于避免内存泄露。

3.3.1 使用removeMessages()

通过它移除任务队列中所有特定Message,

3.3.2 使用removeCallbacks()

通过它移除任务队列中所有特定Runnable,

这里的mHandlerRunnable就是之前post()方式使用的mHandlerRunnable,所以在使用post()方式的时候,我们要把Runnable的引用保存起来,以便以后的移除操作。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

多线程开发 第2节 线程Thread

更新时间 修改意见
2016-08-02 陈敏

第2节 线程Thread

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

2.1 Thread的简单使用

创建一个新的线程,

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

  2. Runnable为参数,创建一个Thread,调用Threadstart()方法后,新线程就运行起来了,并执行Runnable中的run()函数;

  3. run()函数执行完毕后,新线程就退出;

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

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

  2. 如果主线程要取消这个线程的工作,修改这个标志位就好了,

2.2 Thread的创建

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

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

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

2.3 Thread的停止

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

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

2.3.1 正常退出

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

2.3.2 使用标志位

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

  2. 如果主线程要取消这个线程的工作,修改这个标志位就好了,

2.3.3 使用interrupt()方法

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

不过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()方法,等待子线程的结束,

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

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

2.4.2 wait()方法

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

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

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

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

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


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

多线程开发 第1节 概述

更新时间 修改意见
2016-08-02 陈敏

第1节 概述

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

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

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

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

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

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

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

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


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

动画效果 第2节 渐变动画

第2节 渐变动画

渐变动画有四种最常见到的效果:透明、平移、缩放和旋转。这四种效果都有共同点,

  1. 动画对象作为整体一点点变化;
  2. 这是一个状态的迁移过程,需要时间的配合,动画的时间长短可以指定,动画变化的速度也是可以指定的;
  3. 动画执行后,这个动画的对象,并不是真正的变成了动画展示的那样,它其实还保持着以前的样子,你看到的不过是个它的影子罢了,动画结束,影子就消失了。

动画效果可以通过资源文件定义,也可以通过代码来定义。

2.1 透明动画

透明动画的效果是,动画对象的透明度从一个程度变成另一个程度。例如从透明变成不透明这种淡入效果。

2.1.1 xml定义透明动画

通过xml定义透明动画,

  1. res下新建一个anim目录,然后在这个目录下创建一个xml文件
  2. 定义动画效果,

透明动画的各个属性代表了如下的含义:

  1. 标签:这是一个透明效果的动画;
  2. fromAlpha:动画开始时,动画对象的透明度值,取值在0-1之间,0是完全透明,1是完全不透明,01之间的值就是各种半透效果;
  3. toAlpha:动画结束时,动画对象的透明度值,取值在0-1之间;
  4. duration:动画持续的时间,单位是毫秒;
  5. interpolator:这是插值器,用来指定动画变化的速率,例如是先快后慢,还是先慢后快,或者是均匀的变化。安卓系统为我们提供了很多现成的插值器,我们可以直接使用,例如上面的线性插值器@android:anim/linear_interpolator

淡入效果就是刚开始没有显示,逐渐显示出来,所以它的fromAlpha就要设置成0toAlpha设置成1;

淡出效果就是刚开始显示,逐渐隐藏起来,所以它的fromAlpha就要设置成1toAlpha设置成0;

另外,Android SDK也提供了自定义插值器的方法,我们在以后介绍。

2.1.2 java代码定义透明动画

通过java代码定义透明动画,

  1. 创建一个AlphaAnimation,填入起始alpha值和结束的alpha值;
  2. 设定持续时间;
  3. 设定插值器;

2.2 平移动画

平移动画的效果是,动画对象沿着水平方向或者垂直方向移动(或者两个方向同时移动)到另一位置。

2.2.1 xml定义平移动画

通过xml定义透明动画,

  1. res下新建一个anim目录,然后在这个目录下创建一个xml文件
  2. 定义动画效果,

平移动画的各个属性代表了如下的含义:

  1. 标签:这是一个平移效果的动画;
  2. fromXDelta:动画开始时,动画对象相对于它真实位置在x方向上的坐标;
  3. fromYDelta:动画开始时,动画对象相对于它真实位置在y方向上的坐标;
  4. toXDelta:动画结束时,动画对象相对于它真实位置在y方向上的坐标;
  5. toYDelta:动画结束时,动画对象相对于它真实位置在y方向上的坐标;
  6. duration:动画持续的时间,单位是毫秒;
  7. interpolator:这是插值器,用来指定动画变化的速率;

起始坐标有3种表达方式:

  1. 动画对象的绝对位置:用数值表示。
  2. 动画对象相对自身的位置:用表示。例如给fromXDelta设定-100%,表示动画开始时,起始的左边边界相对于自身真实位置,在负的自身宽度的地方;

  3. 动画对象相对于它父布局的位置:用%p表示,这里的p就是parent的缩写。

让对象从左往右平移进入的效果,就是让它的fromXDelta设置成-100%toXDelta设置成0%fromYDelta设置成0%toYDelta设置成0%

让对象从下往上平移进入的效果,就是让它的fromXDelta设置成0%toXDelta设置成0%fromYDelta设置成100%(y坐标是从上到下为正方向);toYDelta设置成0%

2.2.2 java代码定义平移动画

通过java代码定义平移动画,

  1. 创建一个TranslateAnimation,填入x y方向的起始坐标和结束坐标;设置坐标时,可以设定坐标的类型,Animation.RELATIVE_TO_SELF表示使用相对自身的位置坐标,Animation.RELATIVE_TO_PARENT表示使用相对父布局的位置坐标,Animation.ABSOLUTE表示使用绝对位置坐标;
  2. 设定持续时间;
  3. 设定插值器;

2.3 缩放动画

伸缩动画的效果是,动画对象以某个位置为中心进行放大或者缩小。

2.3.1 xml定义伸缩动画

通过xml定义伸缩动画,

  1. res下新建一个anim目录,然后在这个目录下创建一个xml文件
  2. 定义动画效果,

伸缩动画的各个属性代表了如下的含义:

  1. 标签:这是一个伸缩效果的动画;
  2. fromXScale:动画开始时,动画对象在x方向上相对于它真实大小的缩放比例;
  3. fromYScale:动画开始时,动画对象在x方向上相对于它真实大小的缩放比例;
  4. toXScale:动画结束时,动画对象在y方向上相对于它真实大小的缩放比例;
  5. toYScale:动画结束时,动画对象在y方向上相对于它真实大小的缩放比例;
  6. duration:动画持续的时间,单位是毫秒;
  7. interpolator:这是插值器,用来指定动画变化的速率;
  8. pivotX:动画进行缩放时的中心点x坐标;
  9. pivotY:动画进行缩放时的中心点y坐标;

这里的pivotXpivotY可以设置绝对坐标,也可以设置相对自身的相对坐标,以及相对于父布局的相对坐标%p

2.3.2 java代码定义伸缩动画

通过java代码定义伸缩动画,

  1. 创建一个ScaleAnimation,填入x y方向的起始伸缩比例和结束伸缩比例;设置中心坐标时,可以设定坐标的类型,Animation.RELATIVE_TO_SELF表示使用相对自身的位置坐标,Animation.RELATIVE_TO_PARENT表示使用相对父布局的位置坐标,Animation.ABSOLUTE表示使用绝对位置坐标;
  2. 设定持续时间;
  3. 设定插值器;

2.4 旋转动画

旋转动画的效果是,动画对象以某个位置为中心进行旋转。

2.4.1 xml定义旋转动画

通过xml定义旋转动画,

  1. res下新建一个anim目录,然后在这个目录下创建一个xml文件
  2. 定义动画效果,

伸缩动画的各个属性代表了如下的含义:

  1. 标签:这是一个旋转效果的动画;
  2. fromDegrees:动画开始时,动画对象相对于真实位置旋转的角度;
  3. toDegrees:动画开始时,动画对象相对于真实位置旋转的角度;
  4. duration:动画持续的时间,单位是毫秒;
  5. interpolator:这是插值器,用来指定动画变化的速率;
  6. pivotX:动画进行缩放时的中心点x坐标;
  7. pivotY:动画进行缩放时的中心点y坐标;

这里的pivotXpivotY可以设置绝对坐标,也可以设置相对自身的相对坐标,以及相对于父布局的相对坐标%p

2.4.2 java代码定义旋转动画

通过java代码定义旋转动画,

  1. 创建一个RotateAnimation,填入起始角度和结束角度;设置中心坐标时,可以设定坐标的类型,Animation.RELATIVE_TO_SELF表示使用相对自身的位置坐标,Animation.RELATIVE_TO_PARENT表示使用相对父布局的位置坐标,Animation.ABSOLUTE表示使用绝对位置坐标;
  2. 设定持续时间;
  3. 设定插值器;

2.5 使用动画

2.5.1 使用xml定义的动画

  1. 使用AnimationUtils.loadAnimation()装载动画;
  2. 对要使用动画的对象使用startAnimation()函数,启动动画;

2.5.2 使用代码定义的动画

使用代码定义的动画就和使用xml一样,对要使用动画的对象使用startAnimation()函数,启动动画;

2.6 多个动画效果叠加

这几种动画不仅可以单独使用,也可以混合着使用,例如你希望一个对象一边淡入一边做平移。

2.6.1 xml定义多个动画

对同一个对象同时使用多个动画效果,只要在定义动画的xml文件中,添加多个希望的动画就行了。

2.6.2 java代码定义多个动画

在java代码中可以,

  1. 创建多个动画;
  2. 创建AnimationSet;
  3. 将创建的多个动画加入到AnimatonSet当中;

2.7 渐变动画的监听

有的时候,需要知道一个动画执行的状态,就需要给动画添加一个监听器。通过监听器获得动画状态变化的通知。

2.8 渐变动画的其它共同属性

在前面我们已经介绍了android:duration android:interpolator等共同的属性。现在我们再介绍几个其它的重要属性。

2.8.1 android:fillAfter

假设一个按钮被使用透明效果的动画,从不透明变到全透明。动画完成后,这个按钮会被还原到它的真实状态(按钮并没有消失或透明,仍然显示在以前的位置)。

如果给这个动画设置了android:fillAfter属性为true,那么在执行完动画后,按钮还是保持动画最后一帧的效果。

这两个属性要设置在标签当中才能有效果。

也就是说,如果是用代码创建动画的话,得把这个动画放到AnimationSet之中再来设置属性。

2.8.2 android:repeatModeandroid:repeatCount

可以通过设置android:repeatCount的数值,让动画重复播放;

  1. 该值为大于0:动画重复该数值表示的次数;
  2. 该值设置为INFINITE:动画将会无限循环进行下去;

android:repeatCount被设置大于0以后,还可以配合使用android:repeatMode

  1. 该值设置为RESTART:每一次动画执行完成后,回到最初状态重新执行下一次动画;
  2. 该值设置为REVERSE:每一次动画执行完成后,动画倒着执行;

动画效果 第1节 概述

第1节 概述

在安卓系统中,为了界面或者其中的组件在切换、改变的时候显得自然生动、具有流动性的美感,就给它们添加了动画的效果。

例如图片切换的时候,前一张图片淡出,后一张图片淡入。

动画分了三类: frame动画(逐帧动画)、 property动画(属性动画)、 tween动画(渐变动画)。

  1. 逐帧动画有点像播放电影,它把很多图片串起来,按照顺序一张一张显示,通过播放形成动画效果;

  2. 属性动画是对控件某个属性使用的动画,例如一个按钮的宽度要从窄设置到宽,而我们希望它的宽度调整的时候,能看到它从窄到宽变化的过程,这时就需要使用属性动画。

  3. 渐变动画是对控件整体使用的动画,有四种最常见到的效果:透明、平移、缩放和旋转。