Android SDK开发包国内下载地址

不知道是因为最近kaihui还是怎么的,打开android sdk官方网站特别的慢,想下载最新版本的platform几乎变成不可能完成的任务,不知道为什么Google不像Apache那样在各国设立镜像站。为了预防今后再出现这样的情况,这次干脆把android开发所需要的各种包总结一下,顺便提供本地下载链接,省得以后找起来麻烦。

通过分析SDK Manager里要用到的repository文件,我下载了目前google提供的各类安卓开发包并上传到了网盘。由于网盘有CDN支持,即使不用迅雷,下载速度依然很快。如果你从官网下载很慢,不妨试试这些国内链接。

更新2017/12/20:新增了android studio 145 for windows版本。

更新2016/8/21:更新了platform-tools和tools的版本,并修复了部分失效链接。

更新2016/3/17:更新了android platform 6.0下载的版本(r01->r02),增加了level 23的document下载,增加了level22和23的source下载,增加了material design icons下载。注:部分百度网盘链接增加了提取码,原因和方法见说明

更新2015/12/18:修复了android studio、document、source和sys-img的失效链接,增加了mac和linux版本的android studio下载。

更新2015/9/21:增加了android 6.0的仿真器rom下载(x86),增加了1.3.2版android studio的下载。

更新2015/9/1:增加了android 6.0 (level 23)的sdk下载;增加support r22和support r23的下载。

更新2015/6/29:增加了android 5.1.1的platform下载。

更新2015/3/11:完善了sysimg部分,增加了Level 21的x86平台sysimg链接。

更新2015/1/5:增加了android 5.0的sample下载。

更新2014/12/19:增加了Support包下载,更新了doc资源版本和下载链接;更新了Android Studio版本到1.0.1,更新了SDK Tools到r24。

更新2014/12/2:更新了SDK Tools的版本,从22.6到23.0.2;增加了Android Studio下载。

更新2014/10/23:增加了部分Android-L资源下载。

更新2014/10/23:感谢CodeDream分享的另一个国内下载网址,内容比较全(没有adt-bundle)速度较快,推荐。

更新2014/8/15:更新了level14-17的samples包;增加了level18-20的samples包;增加了level20版本的源码包;增加了level18-20的platform包。

更新2014/8/7:增加了level18和level19的源码包,并更新了其他版本源码包的链接。

更新2014/7/1: 百度和微云的大部分链接都失效了,重新更新了adt-bundle和sdk的下载链接,部分文件版本有升级。

更新2014/03/11: 更新了adt-bundle和sdk的下载链接,sdk版本由22.3更新为22.6,增加了腾讯微云的下载链接作为备份。

更新2013/12/27: bd网盘病得不轻,分享链接以后几分钟再访问,就显示“啊哦,你来晚了”。甚至网盘里的文件都被删除,导致楼主硬盘里也没有这些文件了,回收站里只能恢复出空文件夹。楼主会再花几天时间重新下载,放到其他网盘里,见谅。“善待生命,远离bd网盘。”

更新2013/12/27: 最近bd网盘又犯病,导致很多链接都失效了,如果楼主更新链接不及时,请访问后面这个地址对照文件名挑选所需文件:http://pan.baidu.com/s/1i3iIsOP,口令eer5。

更新2013/11/13: 更新了adt-bundle的版本到20131030,更新了android sdk的版本到r22.3。

更新2013/8/22: 更新了adt-bundle的版本到20130729。

更新2013/6/5:更新了adt-bundle-17-windows-64bit.zip的网盘链接。

更新2013/3/4:有一小部分下载链接失效,经查是由于未知原因被百度网盘禁止分享了,请过一段时间再试。

更新2013/3: 有些包分操作系统版本,例如windows版、macos版和linux版,下载时注意文件名里的标识。

此帖长期更新,若有未及时更新的版本或失效链接,请留言通知主页君。

Android Studio

Google官方推荐新入门用户使用基于IntelliJ的Android Studio IDE进行开发,提供了更多的功能,原有基于Eclipse的ADT开发环境已降低更新频率,并将在2015年底左右停止更新。

文件名文件大小说明其他下载地址
android-studio-bundle-141.2288178-windows.exe1.1GWindows版,所需的全都包含了,推荐。提取码见说明官网
android-studio-ide-145.3276617-windows.zip340MWindows版,解压缩直接使用。 
android-studio-ide-141.2178183-windows.exe307MWindows版,仅IDE部分 
android-studio-ide-141.2288178-mac.dmg347MMac版。提取码见说明官网
android-studio-ide-141.2288178-linux.zip363MLinux版。提取码见说明官网

注:Android Studio有自动更新功能,即使是旧版本(v0.8.x)的,也可以先升级到v0.9.9,然后再升级到v1.x,升级包体积不大还是比较方便的。

可能是我电脑比较差(i5/8G/250G),感觉Android Studio运行起来比基于Eclipse的开发环境卡很多,加上操作方式和工程结构不一样,一下子很难适应。(注2015/9/21:1.3.2版感觉比1.0.1版快了很多,配合x86的仿真器实用性大大增强,推荐大家使用。)

ADT Bundle

多合一下载包,里面包含了:sdk + 特定版本platform + eclipse + adt + 兼容包,解压缩即可使用。唯一的缺点是体积比较大,如果你是从零开始配Android开发环境就下载吧。

文件名文件大小说明其他下载地址
adt-bundle-windows-x86-20131030.zip480.0M Android 4.2多合一开发包, Windows 32位。官网 微云
adt-bundle-windows-x86_64-20140321.zip510.0M Android 4.2(4.4?)多合一开发包, 注意所含eclipse是64位的。官网 微云
adt-bundle-mac-x86_64-20131030.zip448.0M Mac OS官网 微云
adt-bundle-linux-x86-20131030.zip473.0M Linux 32位官网 微云
adt-bundle-linux-x86_64-20131030.zip474.0M Linux 64位官网 微云

假设你把sdk安装到d:\android-sdk,则里面的目录结构应该是这样的:

d:\android-sdk\tools
这个目录里有ddms.bat等文件

d:\android-sdk\system-images\android-17\armeabi-v7a
这个目录里有userdata.img等文件(r14及以上才有这个目录)

d:\android-sdk\platforms\android-4.0.2
这个目录里有android.jar等文件

d:\android-sdk\platform-tools
这个目录里有adb.exe等文件

d:\android-sdk\extras
这个目录里有android等目录

当你下载下面列出的部件时,也请参考上面的目录结构示例将内容放在正确的位置。

Android SDK

SDK就是开发包,里面包含了两大类命令行工具:一类是SDK Tools,如android.bat、ddms.bat和emulator.exe等等,这些命令位于<sdk>\tools目录,但运行仿真器所需的rom并不包含在内;另一类是Platform Tools,包含了如adb.exe、aidl.exe、aapt.exe等等命令,它们位于<sdk>\platform-tools目录。

请区别清这几个名称:SDK、SDK Tools、Platform Tools、Platform Package。

文件名文件大小说明其他下载地址
installer_r24.3.4-windows.exe133M需要提取码,见说明官网
android-sdk_r24.3.4-windows.zip178M需要提取码,见说明官网
installer_r24.0.2-windows.exe87.1M 官网
android-sdk_r24.0.2-windows.zip133.0M 官网
installer_r23.0.2-windows.exe88.7M官方推荐下载这个安装包,是32位和64位通用的。官网 微云
android-sdk_r23.0.2-windows.zip134.0M文件名里虽然是"sdk",其实不含platform-tools,
叫"sdk tools"更合适。
官网 微云
android-sdk_r23.0.2-macosx.zip86.7M官网 微云
android-sdk_r23.0.2-linux.tgz134.0M官网 微云

SDK安装/解压缩后,只具有基本的功能,还无法开始进行开发。你还需要platform-tools和至少一个platform package才算完整。在eclipse里通过SDK Manager是可以在线安装的,但有时手动安装也许更快。

文件名文件大小说明其他下载地址
platform-tools_r24-linux.zip3M   
platform-tools_r24-macosx.zip3M   
platform-tools_r24-windows.zip3M   
platform-tools_r23.1.0-linux.zip3M   
platform-tools_r23.1.0-macosx.zip3M   
platform-tools_r23.1.0-windows.zip2M   
platform-tools_r14-linux.zip10.4M将platform-tools目录解压到sdk所在目录   官网
platform-tools_r14-macosx.zip10.8M官网
platform-tools_r14-windows.zip10.6M官网

假如你已经安装过android sdk,想单独升级tools,可以从下面的文件中选择所需要的压缩包下载,然后替换掉sdk目录下的对应子目录。

文件名文件大小说明其他下载地址
tools_r25.2.2-linux.zip261M   
tools_r25.2.2-macosx.zip187M   
tools_r25.2.2-windows.zip288M   
tools_r20.0.3-linux.zip78.8M和第一个表里的"sdk"相比,只少了avd manager和sdk manager。
建议仅在需要升级时下载,用tools目录替换原来的同名目录。
官网
tools_r20.0.3-macosx.zip55.5M官网
tools_r20.0.3-windows.zip86.0M官网

ADT

ADT(Android Development Tools)是一个Eclipse插件,如果没有这个插件,我们开发Android应用就得不停在命令行里敲各种命令,除非你是记事本开发狂人,否则还是老老实实享受ADT带来的方便吧。至于Eclipse本身可以去eclipse.org下载,这里就不再提供了。

文件名文件大小说明其他下载地址
ADT-21.0.0.zip12.9M 官网 微云
ADT-20.0.3.zip11.8M 官网 微云
ADT-12.0.0.zip5.4M如果你不习惯新版的ADT,这个版本也许适合你。官网 微云

ADT的版本号基本上是跟着SDK Tools走,即每出一个新版本的SDK Tools,就出一个同样版本的ADT。不过实际使用中,即使是老版本的ADT也能对新版SDK支持得不错。

Platforms

Android系统版本从1.0到写这篇帖子时的4.2,大大小小已经经历了10多个版本,每个版本发布时Google都会提供一个sdk platform package供开发者使用。一般这个压缩包是在Eclipse开发环境里用SDK Manager来下载的,但在国内选择这种方式会很痛苦,因为每个platform都有几十上百兆大,装一个就得几个小时,好像还不能断点续传(?)。

在国内,更方便的做法是先下载离线包,然后解压缩到android sdk的安装目录下,重启Eclipse后就会自动识别出来。

文件名文件大小说明其他下载地址
android-23_r02.zip67.2MAndroid 6.0 revision 02,提取码见说明官网
android-23_r01.zip67.1MAndroid 6.0 
android-22_r02.zip63.7MAndroid 5.1.1 
android-21_r01.zip63.9M Android 5.0东软
android-L_r04.zip66.2MAndroid L东软
android-20_r01.zip60.6M 官网
android-19_r03.zip60.8M 官网
android-18_r01.zip46.4M 官网
android-17_r01.zip 45.6MAndroid 4.2官网 微云
android-16_r03.zip45.7MAndroid 4.1, 4.1.1, JELLY_BEAN官网 微云
android-15_r03.zip42.3MAndroid 4.0.3, 4.0.4, ICE_CREAM_SANDWICH_MR1官网 
android-14_r03.zip43.7MAndroid 4.0, 4.0.1, 4.0.2, ICE_CREAM_SANDWICH官网 微云
android-3.2_r01-linux.zip103.0MAPI Level 13, HONEYCOMB_MR2
文件名虽然有linux但其实是平台无关的,下同,直至2.1。
官网 
android-3.1_r03-linux.zip101.0MAPI Level 12, HONEYCOMB_MR1官网 
android-3.0_r02-linux.zip99.6MAPI Level 11, HONEYCOMB官网 
android-2.3.3_r02-linux.zip81.5MAPI Level 10, GINGERBREAD_MR1官网 微云
android-2.3.1_r02-linux.zip75.0MAPI Level 9, GINGERBREAD官网 
android-2.2_r03-linux.zip71.1MAPI Level 8, FROYO官网 
android-2.1_r03-linux.zip66.8MAPI Level 7, ECLAIR_MR1官网 
android-2.0_r01-windows.zip72.7MAPI Level 5, ECLAIR官网 
android-2.0_r01-macosx.zip71.4M官网 
android-2.0_r01-linux.zip71.6M官网 
android-2.0.1_r01-windows.zip76.6MAPI Level 6, ECLAIR_0_1官网 
android-2.0.1_r01-macosx.zip75.3M官网 
android-2.0.1_r01-linux.zip75.5M官网 
android-1.6_r03-windows.zip61.6MAPI Level 4, DONUT官网 
android-1.6_r03-macosx.zip59.5M官网 
android-1.6_r03-linux.zip60.5M官网 
android-1.5_r04-windows.zip52.0MAPI Level 3, CUPCAKE官网 
android-1.5_r04-macosx.zip50.0M官网 
android-1.5_r04-linux.zip50.8M官网 
android-1.1_r1-windows.zip44.6MAPI Level 2   官网 
android-1.1_r1-macosx.zip43.4M官网 
android-1.1_r1-linux.zip43.3M官网 

更详细的Android版本对照表可以参考这里

System Images

从Level 14开始,每个platform package都被分为两部分了,一部分包含这个版本下开发所需的jar包,例如android-16_r03.zip;另一部分是system image(即仿真器rom文件),例如sysimg_armv7a-16_r03.zip。如果你要在电脑上使用仿真器,需要有对应版本的system image文件;而如果你只在真机上调试程序,则不需要下载仿真器rom。

下载后的sysimg文件直接解压缩到sdk/system-images目录下,例如sdk/system-images/android-17。

文件名文件大小说明其他下载地址
sysimg_x86_64-23_r03.zip322Mx86版 64位东软
sysimg_x86-23_r03.zip229Mx86版东软
sysimg_x86_64-21_r01271Mx86版官网
sysimg_armv7a-17_r01.zip111.0M仅仿真器rom官网 微云
sysimg_armv7a-16_r03.zip107.0M仅仿真器rom官网 微云
sysimg_armv7a-15_r02.zip91.7M仅仿真器rom官网 
sysimg_armv7a-14_r02.zip95.0M仅仿真器rom官网 

最近的版本除了arm仿真器,还有x86仿真器可供下载(由intel提供),在x86电脑上运行能够提速。

另:补充 凌寒00 网友提供的安装说明:“在sdk下建system-images文件夹,然后进入其中建立对应于api level的文件夹 类似于这样 android-17 ,然后把下载的映像文件解压后放到里面就可以了。
...路径示例 F:\adt-bundle-windows-x86_64-20140624\sdk\system-images\android-17\armeabi-v7a。”

Documents

供Android开发参考用的官方文档,文件比较大。我习惯有一个本地的文档,然后配合Everything使用,非常方便,文档里有不少内容源码里是没有的。

文件名文件大小说明其他下载地址
docs-23_r01.zip316M需要提取码,见说明 
docs-L_r01.zip198.0M  
docs-21_r01.zip258.0M  
docs-19_r02.zip172.0M  
docs-17_r01.zip163.0M 官网
docs-16_r03.zip156.0M 官网
docs-15_r02.zip126.0M 官网
docs-14_r01.zip104.0M 官网

Sources

我比较懒,从来没有用git下载过android源代码,如果你想用git下载,可以参考这篇文章里的操作方法。

官方目前提供了一些源代码压缩包,如下表所列,这些压缩包可以attach到eclipse里方便随时F3查看;最重要的是,有了源代码以后,Eclipse里代码辅助出来的参数名再也不会是"arg0"、"arg1"这样的了。

文件名文件大小说明其他下载地址
sources-23_r01.zip30.2M需要提取码,见说明 
sources-22_r01.zip27.5M需要提取码,见说明 
sources-21_r01.zip26.9M 东软
sources-20_r01.zip22.2M 官网
sources-19_r01.zip20.6M 官网 微云
sources-18_r01.zip19.2M 官网 微云
sources-17_r01.zip18.0M 官网 微云
sources-16_r02.zip17.0M 官网
sources-15_r02.zip15.7M 官网 
sources-14_r01.zip15.4M 官网 
sources-8-froyo.zip33.3M (非官方)官网
sources_2.1.zip30.0M (非官方)官网
sources_1.6.zip22.8M (非官方)官网 

Samples

Google提供了丰富的开发示例,如果时间允许多看看这些例子代码和运行效果,对提升手机应用的设计开发能力肯定大有帮助。

文件名文件大小说明其他下载地址
samples-23_r02.zip121M需要提取码,见说明 
samples-22_r06.zip118M需要提取码,见说明 
samples-21_r04.zip91.3M从官方下载后压缩得到。 
samples-L_r02.zip39.2M 东软
samples-20_r01.zip40.9M 官网
samples-19_r05.zip30.4M 官网
samples-18_r01.zip18.9M 官网
samples-17_r01.zip14.1M 官网
samples-16_r01.zip14.0M 官网
samples-15_r02.zip15.6M 官网 
samples-14_r02.zip15.5M 官网 
samples-3.2_r01-linux.zip11.6M 官网 
samples-3.1_r01-linux.zip11.5M 官网 
samples-3.0_r01-linux.zip11.4M 官网 
samples-2.3_r01-linux.zip8.1M 官网 
samples-2.3.3_r01-linux.zip8.1M 官网 
samples-2.2_r01-linux.zip7.6M 官网 
samples-2.1_r01-linux.zip7.3M 官网 

Supports

兼容包。要支持低版本android系统需要在项目里引入相应的兼容包,例如Android 2.1里没有Fragment的概念,那么要开发一个使用Fragment的应用,就要引入support-v4这个jar包,并使用android.support.v4.Fragment来(替代android.app.Fragment)进行开发。

文件名 文件大小说明其他下载地址 
support_r23.2.110.3M需要提取码,见说明 
support_r23.zip9.1M 东软
support_r22.2.zip8.5M 东软
 support_r19.1.zip 4.9M  
 support_r20.zip 5.25M  
 support_r21.0.3.zip 7.32MCardView,GridLayout都在这里面,Library源码形式 

补充解释一下,support-v4表示需要手机上的android版本至少是level 4(即1.6)才能运行;support-v13包含support-v4里的所有api,所以如果项目里包含了v13.jar就不用再包含v4.jar。

Material Design Icons

Google官方提供的Material Design图标库,每个图标有多个颜色和多个尺寸可供使用。

文件名文件大小说明其他下载地址
material-design-icons-master.zip57.5M需要提取码,见说明 

关于提取码的说明

从今年(2016年)开始,新增的网盘下载链接都添加了提取码,主要有两个原因:1)完全公开的百度网盘分享地址经常失效,博主要花大量时间更新失效链接,有提取码的链接则稳定很多;2)博主新做了一个网站,希望增加点人气(这谁丢的鸡蛋~)

总之,要获取提取码:点击打开 折扣君(rushy.cn)搜索“android”即可查看所有提取码 —— 这个网站是博主自己的,无广告无木马,请放心访问。

Update: 提取码整理如下表。

文件名文件大小下载地址提取码
android-23_r02.zip67.1Mhttp://pan.baidu.com/s/1qXjPT84j403
docs-23_r01.zip316Mhttp://pan.baidu.com/s/1jGZmMBOw77c
support_r23.2.110.3Mhttp://pan.baidu.com/s/1kUoea9p86j7
samples-23_r02.zip121Mhttp://pan.baidu.com/s/1i4qjGjVkozs
samples-22_r06.zip118Mhttp://pan.baidu.com/s/1gerAa9lyw9w
material-design-icons-master.zip57.5Mhttp://pan.baidu.com/s/1eQPl4Dcq44e
sources-23_r01.zip30.2Mhttp://pan.baidu.com/s/1qX2gKyg3s42
sources-22_r01.zip27.5Mhttp://pan.baidu.com/s/1nusJx3Njujw
installer_r24.3.4-windows.exe133Mhttp://pan.baidu.com/s/1bszUQMj101
android-sdk_r24.3.4-windows.zip178Mhttp://pan.baidu.com/s/1c1cNbkWia5c
android-sdk_r24.3.4-linux.tgz294Mhttp://pan.baidu.com/s/1nutz9Dv4mn2
android-sdk_r24.3.4-macosx.zip93Mhttp://pan.baidu.com/s/1kTXON4b9uav
android-studio-ide-141.2288178-linux.zip363Mhttp://pan.baidu.com/s/1skg0suLe1f9
android-studio-ide-141.2288178-mac.dmg347Mhttp://pan.baidu.com/s/1hr1bfcoixql
android-studio-bundle-141.2288178-windows.exe1.1Ghttp://pan.baidu.com/s/1qXqBAEg2yso

以上内容随时补充更新,欢迎提醒和纠错,发在评论区域就可以了。

(博客迁移前链接::https://www.cnblogs.com/bjzhanghao/archive/2012/11/14/android-platform-sdk-download-mirror.html

[Android问答] 如何获得手机屏幕分辨率?

file

获得手机屏幕分辨率这个问题并不复杂,但是问的人实在很多,所以还是集中回答一下。

从Android 3.2(API Level 13)开始,在Activity里使用下面的方法来获取屏幕分辨率(单位是像素):

Display display = getWindowManager().getDefaultDisplay(); //Activity#getWindowManager()
Point size = new Point();
display.getSize(size); int width = size.x; int height = size.y;

如果代码不是写在Activity里,用下面的方法(通过WINDOW_SERVICE获取display对象):

WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW\_SERVICE);
Display display \= wm.getDefaultDisplay();
display.getSize(size); int width = size.x; int height = size.y;

如果Android版本小于3.2,那么因为Display对象还没有getSize()方法,应该用下面的方法获取屏幕分辨率:

Display display = getWindowManager().getDefaultDisplay(); int width = display.getWidth(); 
int height = display.getHeight();   

最后,附Android系统版本与API Level对照表(官方文档在这里,如果打不开,也可以在android源文件的android.os.Build里找到这些对应关系):

Platform Version API Level VERSION_CODE Notes
Android 5.0 21 LOLLIPOP (正式的名称)
  21 L (暂时使用的名称)
Android 4.4W 20 KITKAT_WATCH  
Android 4.4 19 KITKAT  
Android 4.3 18 JELLY_BEAN_MR2  
Android 4.2 17 JELLY_BEAN_MR1  
Android 4.1, 4.1.1 16 JELLY_BEAN Platform Highlights
Android 4.0.3, 4.0.4 15 ICE_CREAM_SANDWICH_MR1 Platform Highlights
Android 4.0, 4.0.1, 4.0.2 14 ICE_CREAM_SANDWICH
Android 3.2 13 HONEYCOMB_MR2
Android 3.1.x 12 HONEYCOMB_MR1 Platform Highlights
Android 3.0.x 11 HONEYCOMB Platform Highlights
Android 2.3.4, 2.3.3 10 GINGERBREAD_MR1 Platform Highlights
Android 2.3.2, 2.3.1, 2.3 9 GINGERBREAD
Android 2.2.x 8 FROYO Platform Highlights
Android 2.1.x 7 ECLAIR_MR1 Platform Highlights
Android 2.0.1 6 ECLAIR_0_1
Android 2.0 5 ECLAIR
Android 1.6 4 DONUT Platform Highlights
Android 1.5 3 CUPCAKE Platform Highlights
Android 1.1 2 BASE_1_1  
Android 1.0 1 BASE  

参考资料

Display | Android Developers

Android: How to get screen dimensions

What is API Level?

(博客迁移前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/12/2765835.html)

[Android问答] 开发环境问题集锦

file

工欲善其事,必先利其器。

和iOS开发相比,Android的开发环境的版本比较多,随之而来的问题也多。显然,我们不应该浪费宝贵的时间在解决开发环境带来的问题上,为此本文总结了常见的开发环境问题和解决方法,供大家查询和随时补充。

Debug certificate expired

Android SDK生成的用于调试的证书文件debug.keystore有效期是365天,当使用超过一年后控制台就会报这个错误。

Error generating final archive: Debug Certificate expired on 10/09/18 16:30

解决方法是手工删除debug.keystore文件,Windows系统下位于"C:\Documents and Settings\username\.android"目录,Linux和Mac系统下位于"~/.android/"目录。下次启动应用时,Eclipse会自动新建一个debug.keystore文件。最好"Project->Clean"一下项目以便触发编译器重新编译。

Failed to install apk on device: timeout

导致这个问题的确切原因不清楚,可能是由于不正确关闭adb连接。

Failed to install helloworld.apk on device 'emulator-5554': timeout

解决方法1:更换电脑usb口(不使用前置usb口)或重装手机驱动,将手机关机后再开机。

解决方法2:在Eclipse里选择Window->Preferences->Android->DDMS->ADB connection time out,将缺省的5000ms改为更大的值,例如20000ms

解决方法3:在命令行窗口里依次输入如下命令:

adb kill-server
adb start\-server

invalid command-line parameter

这是由于Eclipse开发环境无法找到所需的可执行文件造成的。

\[2011-07-10 07:10:22 - demo\] Android Launch! \[2011-07-10 07:10:24 - demo\] adb is running normally.
\[2011-07-10 07:10:24 - demo\] Performing com.demo.DemoActivity activity launch
\[2011-07-10 07:10:25 - demo\] Automatic Target Mode: launching new emulator with compatible AVD 'xxx' \[2011-07-10 07:10:25 - demo\] Launching a new emulator with Virtual Device 'xxx' \[2011-07-10 07:11:06 - Emulator\] invalid command-line parameter: Files\\Android\\android-sdk\\tools/emulator-arm.exe.

解决方法是在Eclipse里选择"Window->Preferences->Android"选项,检查"SDK Location"的路径是否包含了空格,如果是"c:\Program Files\android"这种,改为"c:\Progra~1\android",这样命令行就可以正常调用到了。

小提示:在命令行窗口里输入"dir /x"命令就可以列出8.3格式的文件名,看下面的结果:

2012-09-25  23:52    <DIR>          WATCHD~1 WatchData 2012-09-01  11:01    <DIR> Winamp 2012-07-24  22:22    <DIR>          WINDOW~4 Windows Live 2012-07-24  22:21    <DIR>          WI3957~1     Windows Live SkyDrive

INSTALL_FAILED_INSUFFICIENT_STORAGE

安装应用程序时遇到存储容量不足时会报这个错误:

Installation error: INSTALL\_FAILED\_INSUFFICIENT\_STORAGE
Please check logcat output for more details.
Launch canceled!

如果是在模拟器上运行应用,可以扩大AVD的内存容量。

如果是在真机上运行,可以在AndroidManifest.xml里修改安装偏好,让应用直接安装到SD卡上解决。

<manifest xmlns:android\="http://schemas.android.com/apk/res/android" package\="com.example" **android:installLocation\="preferExternal"** \> ... </manifest\>

Unable to open sync connection

虽然很多人遇到这个问题,但问题的根源并不确切,可能有多种原因造成adb报这个错误:

\[2010-10-12 09:36:48 - myapp\] Android Launch! \[2010-10-12 09:36:48 - myapp\] adb is running normally. \[2010-10-12 09:36:48 - myapp\] Performing com.mycompany.myapp.MyActivity activity launch \[2010-10-12 09:36:48 - myapp\] Automatic Target Mode: using device 'xxx' \[2010-10-12 09:36:48 - myapp\] Uploading myapp.apk onto device 'xxx' \[2010-10-12 09:36:48 - myapp\] Failed to upload myapp.apk on device 'xxx' \[2010-10-12 09:36:48 - myapp\] java.io.IOException: Unable to open sync connection! \[2010-10-12 09:36:48 - myapp\] Launch canceled!

解决方法1:拔掉手机连接线再重新连上;

解决方法2:在手机上关闭Debug选项再重新打开,这个选项在手机的"设置->应用程序->开发->USB调试"里。

Too many open files

这个问题与系统可同时打开文件数量设置有关,但一般不需要修改相关设置,用上一个问题(Unable to open sync connection)的方法即可解决。

第三方Jar包,NoClassDefFoundError

升级ADT版本以后容易出现这个问题:本来一切正常的Android项目,升级以后所有的第三方Jar包里的类都提示NoClassDefFoundError了。

原因可能出现在不同版本ADT使用的编译ant脚本的区别,可能的解决方法有两个:

方法1:在Eclipse里右键点击你的Android工程,选择"Properties->Java Build Path->Order and Export",在这里把所有第三方Jar包前面的复选框都勾上。

方法2:检查你的第三方Jar包文件是否放在工程目录下的"libs"目录,如果不是,改过来。

Failed to allocate memory: 8

一般是AVD的设置有问题,很可能是RAM值设得太高,降低些试试。早期adt版本里有个bug,就是RAM值里必须包含"MB",例如是"512MB"而不是"512",否则提示上述错误信息。

也有人提到过分辨率是原因之一,但我没有实际遇到过,如果只改小RAM没有解决问题,试着把分辨率也调低看看。

参考资料

“Debug certificate expired” error in Eclipse Android plugins

Android error: Failed to install *.apk on device *: timeout

The Android emulator is not starting, showing “invalid command-line parameter”

Solution: Android INSTALL_FAILED_INSUFFICIENT_STORAGE error

Android adb “Unable to open sync connection!”

com.android.ddmlib.SyncException: Too many open files

NoClassDefFoundError - Eclipse and Android

Android emulator failed to allocate memory 8

(博客迁移前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/11/2765341.html

[Android问答] ListView如何加载远程图片?(附代码)

 ListView在Android应用里扮演非常重要的角色,但很多开发者在使用ListView时都遇到过不少麻烦。一个常见的问题是:列表中要显示一系列记录,每条记录带有一张缩略图(产品照片、用户头像等等),而这个缩略图是通过一个远程URL地址来标识的。这样的应用场景该如何实现呢?

file

为了避免下载图片带来的延迟,所有远程图片都应该使用异步方式加载,即使用单独的线程下载图片,待图片下载完毕后显示在ImageView里。Android里可以像普通Java一样启动新线程,但当这个线程要更新界面时,必须使用Handler来请求,否则会为应用程序带来潜在危害。

RemoteImageHelper

为了将复杂的逻辑分离,我们单独写一个名为RemoteImageHelper的类来处理“异步下载图片并更新到界面”这个问题,这个类能够实现以下功能:

  • 图片开始下载前,ImageView里显示一个表示“正在加载”的占位图;
  • 图片在后台下载,下载完成后显示在ImageView里;
  • 若图片下载失败,ImageView显示一个表示下载失败的占位图;

下面让我们来看一下实现代码:

首先需要有一个方法下载远程图片,这里我们不用把图片下载到手机上,直接返回一个InputStream类型的结果即可。如果运行时这个方法报错,请检查是否在AndroidManifest.xml里添加了android.permission.INTERNET权限。

private InputStream download(String urlString) throws MalformedURLException, IOException {
    InputStream inputStream \= (InputStream) new URL(urlString).getContent(); return inputStream;
}

然后是最主要的异步加载图片方法,“正在下载”和“下载失败”的图片可根据需要自己替换。代码如下所示:

private final Map<String, Drawable> cache = new HashMap<String, Drawable>(); public void loadImage(final ImageView imageView, final String urlString, boolean useCache) { if (useCache && cache.containsKey(urlString)) {
        imageView.setImageDrawable(cache.get(urlString));
    } //Show a "Loading" image here
 imageView.setImageResource(R.drawable.image\_indicator);

    Log.d(this.getClass().getSimpleName(), "Image url:" + urlString); final Handler handler = new Handler() {
        @Override public void handleMessage(Message message) {
            imageView.setImageDrawable((Drawable) message.obj);
        }
    };

    Runnable runnable \= new Runnable() { public void run() {
            Drawable drawable \= null; try {
                InputStream is \= download(urlString);
                drawable \= Drawable.createFromStream(is, "src"); if (drawable != null) {
                    cache.put(urlString, drawable);
                }
            } catch (Exception e) {
                Log.e(this.getClass().getSimpleName(), "Image download failed", e); //Show a "download fail" image 
                drawable = imageView.getResources().getDrawable(R.drawable.image\_fail);
            } //Notify UI thread to show this image using Handler
            Message msg = handler.obtainMessage(1, drawable);
            handler.sendMessage(msg);
        }
    }; new Thread(runnable).start();
}

关于缓存:在这个例子里我们使用一个内存中的HashMap作为图片缓存,它实现简单但当应用退出后缓存就会被清除。在实际项目里,你可以考虑实现一个基于文件的缓存机制,即将下载的图片保存到SD卡上,注意要定期清除长期不用的图片以节约存储空间。

使用RemoteImageHelper

如何使用这个类呢?下面是一个例子。请注意,为了达到更好的演示效果,代码里在调用loadImage()方法时第三个参数用false禁止了图片缓存功能,在实际项目中,你很可能需要改为true来避免重复下载图片以便提高性能。

List<MyRecord> exampleRecords;
**LazyImageHelper lazyImageHelper** **\= new LazyImageHelper();** class MyAdapter extends ArrayAdapter<MyRecord> { public MyAdapter(Context context) { super(context, R.layout.record\_row, R.id.lblLabel, exampleRecords);
    }

    @Override public View getView(int position, View convertView, ViewGroup parent) {
        View view \= super.getView(position, convertView, parent);
        MyRecord record \= getItem(position);

        TextView lblLabel \= (TextView) view.findViewById(R.id.lblLabel);
        ImageView imageView \= (ImageView) view.findViewById(R.id.img);

        lblLabel.setText(record.getLabel()); //For demo purpose, cache is DISABLED here.
        **lazyImageHelper.loadImage(imageView, record.getImageUrl(), false);** //To enable cache, simply use following code: //lazyImageHelper.loadImage(imageView, record.getImageUrl(), true);

        return view;
    }
}

以上代码中的MyRecord是一个简单的POJO类,表示一个业务对象,它具有id、label和imageUrl三个属性。你可以在完整的工程代码中找到它。

代码下载

上述示例工程编译后的APK文件点击这里下载,可运行在Android 2.1或以上版本。

上述示例工程的源代码点击这里下载。

参考资料

Handler

android的消息处理机制

How do I do a lazy load of images in ListView

Issue 13959:Make listviews more programmer friendly

(博客搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/11/load-images-in-android-listview.html)

[Android问答] 如何实现“退出应用”功能?

file

刚从桌面应用开发转做手机开发的同学常常被这个问题困扰——用户按下Home键后,应用不是“完全退出”而是“运行在后台”,它仍然占用着系统资源,这么多后台运行的应用必然导致系统变慢,是不是应该在我的应用里给用户提供一个“退出菜单”或“退出按钮”呢?

我在Android开发文档里暂时没有找到关于这个问题的解释,但经过在网上调查很多资料以后,我认为答案是比较明显的:不应提供“退出应用”功能

虽然文档里没有明确说明,但假如这是常用功能,应该有简便的方法实现,而实际上要靠代码“退出”一个应用并非易事。以下总结了能够模拟退出效果的两个方案:

方案1:打开系统主屏来模拟应用退出的效果,这和用户按Home键没有什么区别。

Intent intent = new Intent(Intent.ACTION\_MAIN);
intent.addCategory(Intent.CATEGORY\_HOME);
intent.setFlags(Intent.FLAG\_ACTIVITY\_NEW\_TASK);
startActivity(intent);

方案2:直接杀掉当前应用进程。这个方法太暴力了,我找到一段iOS开发文档,上面强烈不建议使用杀进程的方式来退出应用,原因也适用于Android系统:这样退出的效果容易让用户以为应用崩溃了。

int pid=android.os.Process.myPid();
android.os.Process.killProcess(pid);

此外,有人建议调用System.exit(0)退出应用,实际测试发现这个方法常常只能关闭当前Activity,或是根本不起作用。

由此可以看出,Android系统的设计里本来就没有“退出应用”的机制,当用户按下Home键或在应用首页里按下Back键后,应用被置于后台,而何时要彻底杀掉应用进程则由系统决定。Android和iOS都已抛弃了“退出应用”这个概念,对手机用户来讲,他只需要知道“启动应用”——概念越少越简单。

参考资料:

Quitting an application - is that frowned upon?
android - exit application code
How to close/exit an application in android?
Proper way to exit iPhone application?
How do I programmatically quit my iOS application?

[Android问答] 旋转屏幕导致Activity重建怎么办?

file

Android开发文档上专门有一小节解释这个问题。简单来说,Activity是负责与用户交互的最主要机制,任何“设置”(Configuration)的改变都可能对Activity的界面造成影响,这时系统会销毁并重建Activity以便反映新的Configuration。

“屏幕方向”(orientation)是一个Configuration,通过查看Configuration类的javadoc可以看到其他Configuration还有哪些:如fontScalekeyboardHiddenlocale等等。

当屏幕旋转时,这个Configuration就发生了改变,因此当前显示的Activity需要被重建,Activity对象会被终止,它的onPause()onStop()onDestroy()方法依次触发,然后一个新的Activity对象被创建,onCreate()方法被触发。假设屏幕旋转前,用户正在手机上填写一个注册表单,如果处理不当,用户会发现旋转后的表单变成空白的了,严重影响使用体验。

要解决这个问题有三种方法:

方法1:禁止旋转屏幕

毫无疑问,这是最懒的办法了,相当于回避了本文提出的问题,方法如下看看就好:

<activity android:name=".MyActivity"
          android:screenOrientation="portrait"
          android:label="@string/app_name">

方法2:旋转后恢复现场

既然Activity会被销毁,那么我们就可以使用前文介绍过的“持久化/恢复现场”方法来解决。即在onPause()里将用户当前已经输入的内容保存到数据库或Preference,在onCreate()方法里读取并填充到表单中,这也是官方推荐的方法。

需要补充一点,如果Activity重建需要耗费大量资源或需要访问网络导致时间很长,可以实现onRetainNonConfigurationInstance()方法将所需数据先保存到一个对象里,像下面这样:

@Override
public Object onRetainNonConfigurationInstance() {
    final MyDataObject data = collectMyLoadedData();
    return data;
}

重建时,在onCreate()方法里通过getLastNonConfigurationInstance()方法获得之前保存的数据,如下所示:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
    if (data == null) {//表示不是由于Configuration改变触发的onCreate()
        data = loadMyData();
    }
    ...
}

方法3:手工处理旋转

一般情况下Configuration的改变会导致Activity被销毁重建,但也有办法让指定的Configuration改变时不重建Activity,方法是在AndroidManifest.xml里通过android:configChanges属性指定需要忽略的Configuration名字,例如下面这样:

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

这样设置以后,当屏幕旋转时Activity对象不会被销毁——作为替代,Activity的onConfigurationChanged()方法被触发,在这里开发者可以获取到当前的屏幕方向以便做必要的更新。既然这种情况下的Activity不会被销毁,旋转后Activity里正显示的信息(例如文本框中的文字)也就不会丢失了。

假如你的应用里,横屏和竖屏使用同一个layout资源文件,onConfigurationChanged()里甚至可以什么都不做。但如果横屏与竖屏使用不同的layout资源文件,例如横屏用res/layout-land/main.xml,竖屏用res/layout-port/main.xml,则必须在onConfigurationChanged()里重新调用setContentView()方法以便新的layout能够生效,这时虽然Activity对象没有销毁,但界面上的各种控件都被销毁重建了,你需要写额外的代码来恢复界面信息。

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "横屏模式", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "竖屏模式", Toast.LENGTH_SHORT).show();
    }
}

官方的Android开发文档不建议使用这种方式处理Configuration改变:

Note: Using this attribute should be avoided and used only as a last-resort. Please read Handling Runtime Changes for more information about how to properly handle a restart due to a configuration change.

最佳实践

考虑到旋转屏幕并不是使Activity被销毁重建的唯一因素,仍然推荐前文介绍过的方法:在onPause()里持久化Activity状态,在onCreate()里恢复现场,可以做到一举多得;虽然Google不推荐设置android:configChanges属性的方式,但如果你的Activity横向纵向共用同一个layout文件,方法3无疑是最省事的。

参考资料:

Configuration Changes
Handling Runtime Changes
Activity restart on rotation Android
How to handle screen orientation change when progress dialog and background thread active?

(搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/09/2761897.html)

[Android问答] 如何应对Activity进程被杀?

我们要了解Android手机开发与桌面开发有一个主要不同之处:通常在一部Android手机里同时运行着多个应用(app),每个app对应一个系统进程,当系统需要更多的资源(如内存)而空闲资源不足时,Android系统就会选择杀掉一些“低优先级”的进程以便释放所需资源。

Android系统是如何确定进程优先级的高低的呢?

  • 如果一个app正在与用户交互,那么它所在的进程具有最高优先级;
  • 其次,如果一个app是可见的,例如被一个对话框部分遮挡,它所在进程具有第二高的优先级;
  • 再次,如果app当前是不可见的,也就是被切换到了后台,则它所在进程具有第三高的优先级;这里要补充一点,如果这个后台app启动了一个service,则它比一般的后台app优先级高一些。
  • 最后,如果一个进程里没有包含任何app,这个进程的优先级是最低的。

当系统资源严重不足时,任何一个进程都有可能被杀掉,而当用户想回到一个已经不存在于内存中的Activity时,系统只得新建一个这样的Activity对象并调用它的onCreate()方法进行恢复。所以有时出现这种状况:一个app大部分时间运行很好,偶尔在切换Activity时出现空指针异常导致强制关闭,这多半是在onCreate()方法里使用了已经被重置为空的对象(例如intent里的变量)造成的。即使不出现异常,也会造成表单数据丢失等严重影响使用体验的问题。

要解决这类问题,切不可抱“现在手机内存大,进程一般不会被杀掉”这种侥幸心理,而应该以“应用随时都会被杀掉”的态度来谨慎处理,下面介绍Google建议的方式。

解决方法

由于Activity随时可能需要重建,所以我们要做的事情就是在适当的位置将Activity所需数据进行持久化(从ram复制到rom或sd卡),并在onCreate()方法里利用这些数据恢复现场。

Activity有两类数据需要进行持久化处理:“文档类型数据”和“内部状态类型数据”,前者例如用户正在编辑的表单,后者如用户偏好。

一、为了持久化文档类型的数据,Google建议使用”即时生效”(edit in place)的编辑策略,具体的方式如下:

  • 用户新建文档时,在SQLite数据库(根据需要也可以使用preference)里也立即新建一条记录。(与此相对的方式是:为用户提供一个“保存”按钮,只有当用户按下按钮时才将文档保存到数据库。)
  • 当用户离开当前Activity时,onPause()方法会被触发,在这个方法里将当前正在编辑的文档持久化到数据库。这样一来,如果用户是从这个Activity切换到另一个相关Activity,仍然可以看到刚刚保存的内容。

这种方式可以最大限度避免数据丢失,只要onPause()方法被触发执行,即使Activity所在进程被系统kill掉也不会造成数据丢失。唯一要注意的是,界面上最好能提供一个“取消”按钮或菜单,以便让用户可以选择不保存对文档的更改。

二、为了持久化内部状态类型的数据,可以在onPause()里使用Activity#getPreferences(int)方法,这个方法返回SharedPreferences类型的对象,利用它可以记录用户对这个Activity的偏好信息。例如一个日历应用,用户可以选择显示为周视图或月视图,这样的信息作为内部状态记录到SharedPreferences对象以后,下次再打开这个Activity时就可以按用户上次的选择来显示日历了。

以下代码来自官方文档,演示了如何持久化并恢复一个Activity的当前显示模式:

 public class CalendarActivity extends Activity {
     ...

     static final int DAY_VIEW_MODE = 0;
     static final int WEEK_VIEW_MODE = 1;

     private SharedPreferences mPrefs;
     private int mCurViewMode;

     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         //取回之前持久化的日历显示模式
         SharedPreferences mPrefs = getSharedPreferences();
         mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
     }

     protected void onPause() {
         super.onPause();

         //持久化日历显示模式 
         SharedPreferences.Editor ed = mPrefs.edit();
         ed.putInt("view_mode", mCurViewMode);
         ed.commit();
     }
 }

是有点麻烦,但为了能让app稳定运行也值了。

有些同学要问了,为什么是在onPause()里持久化而不是在onSaveInstanceState()里?官方文档有下面一段话简要解释了原因,即前者比后者更可靠,因为onSaveInstanceState()不属于Activity生命周期的一部分。——既然如此,我想不出onSaveInstanceState()还有什么其他用途了,大家干脆忘了它吧,还有onRestoreInstanceState()。

Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.

注1:关于上面这段话,网上存有一些争议,我个人还是比较倾向在onPause()里做持久化——既可靠又好记,唯一的缺点是调用次数稍多。

注2:从Android 3.0(HoneyComb)版本开始,Activity进程在被系统杀掉之前,将保证onStop()方法先执行完成,因此如果我们开发的应用只运行在3.0以上,可以把持久化工作放在onStop()里以减少持久化的次数。

最佳实践

不要抱侥幸心理,你的Activity随时可能被销毁。

解决方法:在onPause()里持久化Activity数据,在onCreate()里恢复现场

参考资料:

ActivitySaving Activity state in Android

(搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/08/2759948.html

[Android问答] 如何理解Activity生命周期?

Android官方文档里对Activity的生命周期有比较详尽的描述,但由于资源回收机制带来不确定性,我们的程序运行结果常常与预期的不符,而调试这类问题又十分消耗时间和精力。解决的根本办法还是要理解透Activity的生命周期及相关内容,这篇帖子着重介绍Activity生命周期本身,之后会用一两篇帖子来介绍如何处理异常的状态变化。

下图是官方文档里的Activity生命周期图,其中彩色标出的四个框是Activity的四种状态,当Activity的状态改变时会触发一个或多个onXXX()方法。

file

onCreate()

当Acitivity第一次被创建时触发,一般在这里要做的事情包括创建视图(setContentView())、向视图填充必要的数据等等。

onRestart()

这个我比较少用到,按文档上的介绍,如果Activity之前被stop过,那么下一次onStart()方法之前会先触发这个方法。

onStart()

只要Activity从不可见变成可见,就会触发到这个方法,但被AlertDialog遮挡/显示的情况不算在内。

onResume()

当Activity来到最上层的时候,也就是开始与用户直接交互时,触发这个方法。例如本来Activity被一个AlertDialog遮挡,当这个AlertDialog消失时,onResume()方法就被触发。

onPause()

和onResume()的触发条件刚好相反,如果Activity本来在最上层,当它要让出最上层的位置时会触发这个方法。onPause()和onResume()是被触发最频繁的两个方法,所以在这里不应该执行过于消耗资源的方法。

onStop()

当有其他Activity覆盖了当前Activity时,不论另一个Activity是新开始的还是从下层移至最上层的,当前Activity的onStop()方法都会被触发。

onDestroy()

Activity生命周期的终点。有两种情况会导致它被触发:1)执行了Activity#finish()方法;2)Android系统由于资源不足等原因决定杀掉Activity所在进程。通过isFinishing()方法可以判断出是哪种情况。在这个方法里,我们一般要做的事情是释放Activity占有的资源,例如后台正在进行的下载线程等等。

最后,举个实际例子来说明,假设你有一个“首页Activity”和一个“编辑页Activity”。

  • 当用户点击首页里的“开始编辑”按钮时,首页的onPause()->onStart()onStop()依次触发,编辑页的onCreate()->onStart()->onResume()依次触发;(感谢James.H.Fu指出的错误)
  • 当用户在编辑页按下“返回”按钮时,编辑页的onPause()->onStop()依次触发,之后首页的onStart() -> onResume()依次触发;
  • 这时用户在首页按下“返回”按钮,首页的onPause()->onStop()->onDestroy()依次触发。

参考资料:

Activity Lifecycle
Simplest Android Activity Lifecycle
Activity lifecycle explained in details

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/07/2758276.html

[Android问答] px、dp和sp,这些单位有什么区别?

相信每个Android新手都会遇到这个问题,希望这篇帖子能让你不再纠结。

px:

即像素,1px代表屏幕上一个物理的像素点;

px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide,下同)。

file

偶尔用到px的情况,是需要画1像素表格线或阴影线的时候,用其他单位如dp会显得模糊。

dp:

这个是最常用但也最难理解的尺寸单位。它与“像素密度”密切相关,所以首先我们解释一下什么是像素密度。假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算出在这部手机的屏幕上,每英寸包含的像素点的数量为240/1.5=160dpi(横向)或320/2=160dpi(纵向),160dpi就是这部手机的像素密度,像素密度的单位dpi是Dots Per Inch的缩写,即每英寸像素数量。横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。

不同的手机/平板可能具有不同的像素密度,例如同为4寸手机,有480x320分辨率的也有800x480分辨率的,前者的像素密度就比较低。Android系统定义了四种像素密度:低(120dpi)、中(160dpi)、高(240dpi)和超高(320dpi),它们对应的dp到px的系数分别为0.75、1、1.5和2,这个系数乘以dp长度就是像素数。例如界面上有一个长度为“80dp”的图片,那么它在240dpi的手机上实际显示为80x1.5=120px,在320dpi的手机上实际显示为80x2=160px。如果你拿这两部手机放在一起对比,会发现这个图片的物理尺寸“差不多”,这就是使用dp作为单位的效果,见下图。

file

更新20140701: 是不是所有android手机的屏幕宽度用dp衡量都是固定值(例如320dp)呢?答案是否定的,如果写一个程序画宽度等于320dp的横线,在不同手机上运行,会发现在有些手机上横线比手机屏幕短,有些则比屏幕长,在平板上与手机上相比差别则更加明显。

dip:

与dp完全相同,只是名字不同而已。在早期的Android版本里多使用dip,后来为了与sp统一就建议使用dp这个名字了。

sp:

与缩放无关的抽象像素(Scale-independent Pixel)。sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是“大”或“超大”时,1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸以后的效果——窗口大小不变,只有文字大小改变。

还有几个比较少用到的尺寸单位:

mm:

即毫米;

in:

即英寸,1英寸=2.54厘米(约);

pt:

1pt=1/72英寸=0.035厘米;

最佳实践:

文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。例如textSize="16sp"、layout_width="60dp";偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时:

<View layout_width="match_parent" layout_height="1px"/>

最后,推荐一张Android UI设计参考图:Android Design Cheat Sheet

参考资料:

Difference of px, dp, dip and sp in Android?
Supporting Multiple Screens

DisplayMetrics

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2012/11/06/2757300.html