Fragment里getActivity()为空问题

问题

一个常见的场景:fragment在onViewCreated()里执行一个任务(AsyncTask)加载一些数据,任务没有完成时,用户切换到其他界面,这时如果在代码里调用了getActivity()方法,得到的将是空值。

这是一个在本地不容易发现的问题,但通过在线异常报告(例如bugly或友盟)容易发现。

解决方法

同时采取以下两项措施:

  1. 在任务的doInBackground()的一开始就获取context,而不是在每处都是用getActivity()获取;
  2. onStop()onDetach()方法里取消正在执行的任务以避免onPostExecute()被继续执行。

参考资料

getActivity() returns null in Fragment function

Swift开发iOS应用过程中的问题和解决记录

file

虚拟机里安装OSX+XCode开发环境

用真机的请直接跳过这个部分。

主要是在VitrualBox里安装mac系统和xcode,参考这篇教程,VirtualBox的版本是4.3.18 r96156,OSX版本是10.11 El Capitan,XCode版本是7.1 (7B91b)。

经过几天的初步使用,感觉用虚拟机开发iOS基本能够满足要求,i5/8g/ssd的配置目测能达到真机70%的开发效率。主要存在的问题是:1)在宽屏上无法满屏,两边会留有黑框,导致鼠标无法顺利停靠在两侧 2)键盘敲击频率过快(约5Hz)时字符乱序出现在编辑器里,例如快速输入“update”可能会得到“updaet” 3)虚拟机里的时钟有可能不准,开发过程中偶尔有问题与此相关,需要考虑在内。

调整分辨率

以管理员方式启动virtualbox和cmd命令行(重要!),执行下面的命令

cd "C:\Program Files\Oracle\VirtualBox"

VBoxManage setextradata "OSXElCapitan" VBoxInternal2/EfiGopMode 3 (注意,参数值 0=640×480, 1=800×600, 2=1024×768, 3=1280×1024, 4=1440×900, 5=1920×1200)

VBoxManage setextradata "OSXElCapitan" "CustomVideoMode1" "1360x768x32" (必须8的整数倍,我这里没有成功)

参考链接:How to Guide for Mavericks VM on Mavericks

通过Virtualbox的远程桌面连接到虚拟机

如果是NAT方式,目标地址是127.0.0.2,用guest里通过ifconfig查看到的10.0.2.xx是不行的。 远程桌面的方式比直接使用虚拟机卡不少,作为开发使用并不实用。

修饰键

苹果键盘与Windows键盘几个修饰键的对应关系如下(在设置里可以改,这里是缺省情况):

Command键(花键 ⌘):对应windows键

Control键(⌃):对应ctrl键

Shift键(⇧):对应shift键

Option键(⌥):对应alt键

两种键盘上这几个修饰键物理布局最大的区别,在于Command键与Option键位置相反,可以去“偏好设置->键盘”里改过来,这样切换两种键盘时就比较容易适应了。

OSX

常用快捷键

官方文档

跳到行首:“Command+左键”(Home键是跳到页首)

结束任务:Command+Option+Esc

XCode7快捷键

代码辅助:control+.

打开/关闭左侧Navigator边栏:Command+0(数字0)

打开/关闭右侧Utilities边栏:Option+Command+0(数字0)

快速打开文件(Open Quickly):Shift+Command+O(字母O)

转到定义(Go to Definition):Command+鼠标点击,或Option+Command+J

自动格式化代码:Control+I(需要先全选,否则只格式化当前行)

调整模拟器大小:Command+3(50%)

Swift2

一些教程

官网:swift.org

官方文档:The Swift Programming Language, 中文版

斯坦福免费视频公开课:Developing iOS 8 Apps with Swift中文翻译

国内视频教程:精通iOS移动开发(Xcode7&Swift2)(免费,内容比较初级)

cocoachina专题:从今天开始学习Swift(大量资源链接)

常量

多数项目里都需要一些常量,java里通常定义在interface里,用swift时可以定义在一个struct里,作为静态的存储成员(static let)。

类型转换

Double转Int: Int(myDouble)

保留小数点位数

方法1(3.1415926->3.14, 3->3.00):

let i = 3.1415926
let str = NSString(format:"%.2f",i)
print("\\(str)")  //will output 3.14

方法2(3.1415926->3.14, 3->3):

let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.DecimalStyle
nf.maximumFractionDigits = 2
print("\\(nf.stringFromNumber(3.1415926))") //will output 3.14

时间

NSDate:相当于java.util.Date,获得系统当前时间直接用NSDate()

NSTimeInterval:等同于double,NSDate.timeIntervalSince1970可以得到自1970年的秒数(注意不是java里的毫秒数)

NSDateFormatter:用来帮助在String与NSDate类型之间做转换 参考

官方文档页

嵌套struct

注意一个地方,例如struct A里定义了struct B,实例化A时,必须先执行a.property1 = value1以后再执行b.a=a,否则会发现b.a.property1=nil。

UIView

所有控件的基类。

frame与bound的区别:frame是view的边界,bound是view可以绘制的区域(bound是可以超出frame范围的)。参考链接1 参考链接2

UITableView

基本使用:可以用ViewController里放TableView,也可以直接用TableViewController,参考How to make a simple tableview with iOS 8 and Swift

使用TableViewController时显示Activity Indicator View(菊花)有问题,不太完美的解决方案见:UITableView Activity Indicator the Apple way,或使用第三方实现,例如下面会提到的SVProgressHUD

处理cell点击事件,实现tableView:didSelectRowAtIndexPath方法即可,注意看清楚别实现成tableView:didDeselectRowAtIndexPath。 参考链接

点击cell跳转(假设从A跳到B):在storyboard里,直接从A的cell到B拖拽创建一个show类型的segue并指定此segue的identity,在A代码里实现forSegue方法将要传的参数赋值给B即可。

UICollectionView

相当于Android里的GridView,可以显示多列数据,只是没有cell的默认实现,也就是必须使用自定义cell。

要让每个cell的宽度恰好等于collection view的一半,可通过实现下面的方法实现(参考链接):

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
  return CGSize(width: CGFloat(self.view.frame.size.width / 2), height: self.view.frame.size.height / 3) 
}

AutoLayout

假设有一个长宽都是150的image view,里面要加载一个网络图片(尺寸未知),要求网络图片能够完整显示在image view里且最长边与image view的边长相等,如何实现呢?参考以下链接:

被这个问题困扰了一段时间,以上几个链接提供的方法都没成功。后来发现,在使用autolayout的情况下,控件的width和height应该通过constraint指定(见下图),配合contentMode=scalefit即可解决。

file

顺便提一句,在storyboard里看到的控件位置并不一定是运行时的位置,因为autolayout情况下只考虑constraint,运行时的位置在storyboard里以橘黄色虚线表示(见下图)。

file

Storyboard还提供了多设备预览功能,使用方法是“打开 Main.storyboard ,然后选择 View\Assistant Editor\Show Assistant Editor ,这时编辑区会分隔为两部分。再点击顶部导航栏中的 Automatic ,在弹出菜单中选择 Preview ,最后选择 Main.storyboard (Preview) ... 点击预览界面左下角的 + 按钮,会弹出当前storyboard文件支持的各种尺寸的设备”(来源

关于"Relative to margins"选项,在xcode7里这个选项是默认勾选的,每个view默认有宽度为8的margin,可以通过“Editor->Canvas->Show Layout Rectangles”菜单项打开,margin位置显示为蓝色细线。举例来说,当勾选了“Relative to margins”选项后,如果一个子view的leading space to 父view是0,运行时会看到子view距离父view的边界还有8的空白区域(效果相当于android里给父view设置了8的padding)。这个缺省margin值可以通过下面的方法设置:

self.rootView.layoutMargins = UIEdgeInsetsMake(0, 50, 0, 0);

参考链接:

从此爱上iOS Autolayout 

Layout Margin Comes With iOS8

第三方依赖包

依赖包管理/Carthage

先安装Homebrew(osx的包管理工具),然后通过Homebrew安装Carthage(比cocoapods灵活,去中心,只支持ios8和以上版本)。

在工程目录下新建“Cartfile”文件,并填写要依赖的包,然后执行carthage update命令,此时如果提示“unable to find utility "xcodebuild", not a developer tool”,可使用"sudo xcode-select --switch"命令解决,这样就下载了依赖包。为了让xcode能用到carthage编译的framework,先从Finder把刚才生成的.framework文件拖到工程的General -> Linked Frameworks and Libraries区域(在Navigator里点击工程根目录即可看到)里,然后在Build Phases里添加一个新内容是“/usr/local/bin/carthage copy-frameworks”的Run Script,并在Input Files里增加所需的编译后的依赖包如“$(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework”。 参考Carthage的Getting Start 

经测试还需要在工程的Build Setting -> Framework Search Path里添加“$(SRCROOT)/Carthage/Build/iOS”,否则build会失败。参考资料

依赖包管理/CocoaPods

有些第三方依赖只支持cocoapods,所以也要装一个cocoapods。cocoapods的资料比较多,例如这篇,也比较容易安装使用。一个需要注意的问题是,执行pod setup命令后提示Setting up CocoaPods master repo要等很久,大约1小时。

在swift文件里使用object-c开发的库,需要借助一个bridging文件,通常名称是“项目名-Bridging-Header.h”。可以让xcode生成这个文件,方法是在工程里创建一个空的Objective-C文件,最后一步时xcode会提示是否创建bridging文件,点YES然后再删掉这个空文件即可。在bridging文件里使用类似#import <AFNetworking/AFNetworking.h>的方式包含依赖包。

如果pod install执行很慢,可能是pod更新spec时被墙,可以加--no-repo-update参数。参考链接

pod install --verbose --no-repo-update

还可以改用国内镜像,例如:

pod repo remove master 
pod repo add master https://git.oschina.net/6david9/Specs.git 
pod reap list

无法安装Realm:ios-charts依赖Realm,安装时很慢(有时等待数小时无果,有时提示SSL Handshake错误),其实真正原因是Realm/core所在服务器被墙。可以通过1)使用VPN解决,或2)手工下载安装core(参考Download core 0.96.0 failed),或3)使用国内镜像(如果已经下载core失败过需要清除缓存,参考Core occasionally cannot be downloaded from China)。

HTTP请求/远程图片

据说SwiftHTTP是个坑,暂时绕开,使用系统提供的NSURLSession简单封装一下。使用方法参考链接

iOS9里默认要求所有http请求都使用https,如果一定要用http,在工程的info.plist里修改“App Transport Security Settings -> Allow Arbitrary Loads”为true。

AFNetworking,功能强大且被广泛使用的Http库,还带有异步加载/缓存远程图片的功能;对应Swift版本的名称是Alamofire

JSON

暂时使用SwiftyJSON,感觉还好,使用举例:

let json1 = JSON(\["param1":1\])

下拉刷新/上拉翻页

下拉刷新:iOS8起内置了UIRefreshControl可以方便的实现下拉刷新功能 用法

上拉翻页/加载更多:暂时没有发现原生的简便方法实现,第三方库倒是比较多,例如SVPullToRefreshMJRefresh

进度条

由于TableViewController显示Activity Indicator有问题,目前在用SVProgressHUD作为替代方案。

折线图/饼图

ios-chart,项目主页的Usage里说了一堆步骤其实都是针对手动安装的,如果用cocoapods直接编辑Podfile添加pod 'Charts'再执行pod install即可,bridge都不用改,在自己的.swift文件里import Charts就可以使用了。不过我遇到一个编译问题,ChartPlatform.swift这个文件编译不过去,自己在文件结尾加了个空行解决。使用方面,一个比较好的教程见这个链接

经过试用,发现ios-charts画时序图(timeseries)比较麻烦,要自己做一些计算(参考)。

替代方案:Core Plot(侧重科学绘图),ios-linechart(使用简单),JBChartView(来自Jawbone),BEMSimpleLineGraph(使用简单)。

(本文原文链接:https://www.cnblogs.com/bjzhanghao/p/5211586.html

Android 5.0/5.1开发问题专贴

注:非5.0特定的开发问题,可以在这个帖子里查:Android开发问题汇总

1、官方提供的例子android-support-v7-appcompat编译时提示android:actionModeShareDrawable属性不存在

官方例子里这个工程的target是19,需要改为21才能正确编译,否则提示

error: Error: No resource found that matches the given name: attr 'android:actionModeShareDrawable'.

。具体方法是修改project.properties文件,将target=19改为target=21,然后clean此工程或重启eclipse即可。参考链接

若仍然无法编译,可能是appcompat的版本不是最新,请通过sdk manager将support包全部升级到最新版(见下图,图片来自这里。国内用户如果无法升级,可修改hosts文件将dl-ssl.google.com静态解析到可访问的ip地址,ip地址可参考这个帖子,在超级ping里获取到)。

file

2、parseSDKContent failed问题

升级sdk到5.0以后,原来的Eclipse经常弹出parseSDKContent错误对话框,甚至有时会提示AndroidManifest file missing。

file

解决方法是删除.android目录(参考链接,在windows里这个目录是C:\Documents and Settings\YOUR_USER_NAME\.android或C:\Users\YOUR_USER_NAME\.android),不过这样会同时删除掉debug.keystore文件。也有人说不需要删除整个.android目录,更新DDMS就可以了(是指更新ADT?),但我没有试过。

3、谷歌开源的Material Design图标

file

可以从GitHub上下载,链接在此。另外,materialdesignicons.comandroidicons.com这两个网站也提供了一些不错的material design图标下载。

其他开源图标库:阿里巴巴提供的iconfont.cn,图标社区NounProject

在线LowPoly生成器:Trianglify

4、PreferenceActivity不显示actionbar

参考这个帖子,目前support包不支持PreferenceActivity(没有PreferenceActionbarActivity这样的类),所以解决方法要么是改用PreferenceFragment,要么使用第三方的补丁包(Fragment的方案),另一个补丁包(Activity的方案,但有缺陷——getPreferencesXmlId()只接受一个preference.xml文件)。

5、Dialog Theme的actionbar背景颜色显示不全

如下图所示,当使用Theme.AppCompat.Light.Dialog时发现actionbar背景色只显示出一半。根据这个帖子的讨论,可能是AppCompat的目前版本还没有做好。

file

6、使用appcompat里RecyclerView和CardView时的问题

GitHub上的这个开源项目可以帮助解决一些问题,例如添加divider、点击事件等等,但还远远不够。

CardView的多状态背景色问题,暂时没有解决,参考链接

下拉刷新:可使用android原生的SwipeRefreshLayout解决;

上拉翻页:方案1)仍使用SwipeRefreshLayout; 方案2)自己监听事件实现Endless效果;

HeaderView:RecyclerView没有像ListView那样提供addHeaderView()方法,要实现类似效果,有两种方法:1)将第一个item作为header,使用android-parallax-recyclerview这个库; 2)让第一个item完全透明,下层显示一个同高的view作为header,使用ASOV这个库。

7、实现Material Design(简称MD)的方方面面

MD是一系列UI特性的组合,阿里巴巴团队的这个帖子介绍得清晰易懂,但要在我们的应用里逐一实现这些特性就不那么容易了,特别是要兼容Android 4.x甚至2.x的时候。Google官方AppCompat v21在这方面只提供了有限的支持,例如ActionBar和侧滑菜单,而像FAB(Fixed Action Button)等等则没有包含在内。

其实在GitHub上已经有不少第三方的实现,值得一提的是,这个名为MaterialDesignCenter的项目把大量相关项目汇总在了一起供开发者参考,值得一看。以下列出我认为值得使用的第三方实现:

FAB: FloatingActionButton

对话框: material-dialogs

各类UI控件: MaterialDesignLibrary

另外几个汇集了android上各类交互效果的项目Interactive-animationawesome-android-uiandroid-open-project(300+项目,不仅限于MD),同样值得参考。

8、使用SlidingTabLayout替代Actionbar的tab导航

v7包的Actionbar对象里,与navigation有关的方法(例如setNavigationMode)都不建议使用了,应使用googleio2014提供的SlidingTabLayout实现类似功能。

参考链接1参考链接2

9、Android 5.1将AlarmManager的setInterval()最小值限制为1分钟

这个比较坑,毕竟有一些应用依赖AlarmManager机制进行更新,当设备升级到android 5.1后就会出现问题。由于开发文档里并没有提到,所以具体情况见googlecode上的讨论。讨论链接需翻墙,google主要回复如下:

“If you are trying to run more often than every 5 seconds, alarms are the wrong way to go about it. Waking up the device that often is extremely bad for battery life. If you have live UI that needs to be updated continually, use a wakelock and then schedule your activity on a handler. This is actually *more* battery efficient than setting an alarm every second.”

替代方法是在Service里使用ScheduledExecutorService实现定时任务,与AlarmManager的区别见 参考链接

10、使用Android Support Design开发包实现Material Design

Google在2015 I/O大会推出了Android Support Design开发包,方便开发者实现多种常用的MD效果。以下几个有用链接:

INTRODUCTION TO COORDINATOR LAYOUT ON ANDROID

(译)掌握 Coordinator Layout 

高逼格UI-ASD(Android Support Design)

NestedScrollView

CoordinatorLayout与滚动的处理

Android Design Support Library使用详解

(搬家前链接:https://www.cnblogs.com/bjzhanghao/p/4194164.html

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