首先Activity是android四大组件之一。作为用户与程序交互的一个载体。Activity创建了一个窗口,你可以通过setContentView这个方法将需要的UI放置在窗口。
从View层的角度来看,Activity承载了与用户交互的不同控件。
从控制层看,也就是内部逻辑,Activity需要保持各个界面的状态,背后会做很多持久化的操作。包括妥善管理生命周期的各个阶段。
Activity的生命周期
1)在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(active or running)
2)如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)。一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。
3)如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。
4)如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。
一个Activity完整的生命周期,从onCreate(Bundle)开始到onDestroy()结束。Activity在onCreate()设置所有的“全局”状态,在onDestory()释放所有的资源。
可见的生命周期,从onStart()开始到onStop()结束。
前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有Activity的最前面,和用户进行交互。
竖屏调整为横屏。生命周期的方法依次用:onPause->onStop->onDestroy->onCreate->onStart->onResume。这么一个过程。它会销毁掉原先的activity,重新创建。
必须重写的两个函数:onCreate,onPause
应用程序入口的Activity
Acitvity启动模式
Activty启动提供了四种启动模式。launchMode:
standard:每次启动新的活动窗口(new操作)
singleTop:如果在栈顶是目标活动,则直接打开.否则开启新的活动窗口(new).
ABCD-->D-->ABCD
ABCDB-->D-->ABCDBD
singleTask 和singleInstance基本上相同.差别在于若根活动设置为singleTask时,则由此所开启的活动也在同一任务中,即taskId相同。. 而若根活动设置为singleInstance时,由此开启的活动在新的任务中.即栈中只有一个活动,taskid不同(Lanucher)。.其余情况相同.
Here's the order of operations that occur when Activity A starts Acivity B:
- Activity A's onPause() method executes.
- Activity B's onCreate(), onStart(), and onResume() methods execute in sequence. (Activity B now has user focus.)
- Then, if Activity A is no longer visible on screen, its onStop() method executes.
Activity关闭的一种方式:调用finish()函数
Acitivity被系统回收前调用onsavedIntanceState()保存状态,在onCreate函数中会将保存的状态作为一个函数的参数回传
横竖屏切换的时候调用根据android:configChanges决定生命周期的回调函数的调用
Activity和Service
(1) startService
startService会经历 onCreate -> onStart stopService的时候直接onDestroy,通过startservice开启的服务.一旦服务开启, 这个服务和开启他的调用者之间就没有任何的关系了.调用者不可以访问 service里面的方法. 调用者如果被系统回收了或者调用了ondestroy方法, service还会继续存在
(2) bindService 的时候传入放置两个参数(1)Intent(2)ServiceConnection对象,对于service端回调用onBind函数返回Service的指针,Binder Driver负责建立Serice的指针和远端Service handle之间的映射关系(封装成binder对象),对于client端通过系统回调的onServiceConnected 获取Service 端的句柄,对service进行操作,进行同步的函数调用,具体的操作接口定义在Service的接口文件AIDL中,通过将相关的变量序列化,并通过Binder Driver传输相应的数据.(transact()和onTransact ) 多个线程中的client同时调用Service端的函数的时候==>在binder thread pool中有多个线程同时执行service端的方法,Service端需要进行同步处理(lock/synchronized/volatile).但是不正确的加锁会导致 有的线程获取不到锁,进而可能发生ANR
AIDL文件==>需要实现parcel接口,自动编译生成相应的.java 文件,stub表示service类,proxy表示client类
异步Binder IPC Call Messager
将Binder IPC 同步call 请求,通过向Service的主线程发送消息,转化成异步call.
ANR
在Android里,应用程序的响应性是由Activity Manager和WindowManager系统服务监视的。当它监测到以下情况中的一个时,Android就会针对特定的应用程序显示ANR:
(1) 在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)WMS 处理窗口事件
(2) BroadcastReceiver在10秒内没有执行完毕 AMS 监控
解决ANR异常概述:
考虑上面的ANR定义,让我们来研究一下为什么它会在Android应用程序里发生和如何最佳构建应用程序来避免ANR。
Android应用程序通常是运行在一个单独的线程(ActivityThread)里。这意味着你的应用程序所做的事情如果在主线程里占用了太长的时间的话,就会引发ANR对话框,因为你的应用程序并没有给自己机会来处理输入事件或者Intent广播。主线程会接受键盘事件,处理BR的onRecive方法,处理生命周期的各种回调函数和进行UI的更新操作
因此,运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和 onResume())里尽可能少的去做创建操作。潜在的耗时操作,例如网络或数据库操作,或者高耗时的计算如改变位图尺寸,应该在子线程里,方法是,主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程,然后主线程来进行UI的更新操作。
引起ANR异常的常见情况:
1、在主线程内进行网络操作,获取服务器上的资源
2、在主线程内进行一些缓慢的磁盘操作(数据库)ANR Log分析
http://www.cnblogs.com/wanqieddy/archive/2012/05/04/2482661.html
Intent和Intent filter
(1)基本概念
沟通Activity,Service,BroadcastReceiver之间的桥梁,IntentFilter 定义了一个组件可以接受的操作类型和可以处理的数据,Intent表示的是意图,就是你想处理的事情和对应的数据。 只有在Intent和对应模块的IntentFilter Match的时候,才会启动相应的模块进行处理
(2) Match比较:action/data/category
action : String类型 一个intentFilter可以定义多个action,intent对象的action必须至少match其中的一个
category: For an intent to pass the category test, every category in the Intent object must match a category in the filter . The filter can list additional categories, but it cannot omit any that are in the intent.)
注意:
In principle, therefore, an Intent object with no categories should always pass this test, regardless of what's in the filter . That's mostly true. However, with one exception, Android treats all implicit intents passed to startActivity() as if they contained at least one category: "android.intent.category.DEFAULT" (the CATEGORY_DEFAULT constant). Therefore, activities that are willing to receive implicit intents must include "android.intent.category.DEFAULT" in their intent filters.
data: specify a URI and a data type
URI
三部分组成:scheme://authority/path
When the URI in an Intent object is compared to a URI specification in a filter, it's compared only to the parts of the URI actually mentioned in the filter .(没有mention的部分不会被比较)
content://==> For matching the data in the Content Providers
Content URIs include the symbolic name of the entire provider (its authority ) and a name that points to a table (a path )
一个ContentProvier会定义其所对应的URI,具体见:
http://developer.android.com/guide/topics/providers/content-providers.html
MINI TYPE URI所对应的数据的类型
http://www.cnblogs.com/hnrainll/archive/2011/11/16/2250980.html
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。
ContentProvider与Android 数据库
ContentProvider实现跨进程的数据共享,客户端通过ContentResolver和URI 访问数据。
事务
在Android应用程序开发中,经常会遇到需要进行数据库操作的时候,有关数据库事务处理对Android应用程序的稳定性和效率提升非常重要。
首先Android数据库操作,特别是进行写操作的时候非常慢,将所有操作打包成一个事务能大大提高处理速度。
其次是保证数据的一致性,让一个事务中的所有操作都成功执行,或者失败,或者所有操作回滚。
实现Android数据库事务非常简单,只需要使用SQLiteDatabase类的三个方法即可。
◆beginTransaction();
◆setTransactionSuccessful();
◆endTransaction();
当调用endTransaction()时,所有从beginTransaction()开始的操作都会被提交。
一个简单的数据库事务操作如下所示:
点击(此处)折叠或打开
- mDatabase.beginTransaction();
- try{
- //在这里执行多个数据库操作
- //执行过程中可能会抛出异常
- mDatabase.setTransactionSuccessful();
- //在setTransactionSuccessful和endTransaction之间不进行任何数据库操作
- }catch(Exception e){
- //当数据库操作出现错误时,需要捕获异常,结束事务
- mDatabase.endTransaction();
- throw e;
- }
- //当所有操作执行完成后结束一个事务
- mDatabase.endTransaction();
- }
在 Android 系统中,所有 安装 到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系 ,如果一个 permission的protectionLevel为signature,那么就只有那些跟该permission所在的程序拥有同一个数字证书的应用程序才能取得该权限 。Android使用Java的数字证书相关的 机制 来给apk加盖数字证书,要理解android的数字证书,需要先了解以下数字证书的概念和java的数字证书机制。 Android系统要求每一个安装进系统的应用程序都是经过数字证书签名的,数字证书的私钥则保存在程序开发者的手中。Android将数字证书用来标识应用程序的作者和在应用程序之间建立信任关系 ,不是用来决定最终用户可以安装哪些应用程序。这个数字证书并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的。
(1)有利于程序升级, 当新版程序和旧版程序的数字证书相同时,Android系统才会认为这两个程序是同一个程序的不同版本 。如果新版程序和旧版程序的数字证书不相同,则Android系统认为他们是不同的程序,并产生冲突,会要求新程序更改包名。
(2)有利于程序的模块化设计和开发。Android系统允许拥有同一个数字签名的程序运行在一个进程中,Android程序会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发,而用户只需要在需要的时候下载适当的模块。
(3) 可以通过权限(permission)的方式在多个程序间共享数据和代码。Android提供了基于数字证书的权限赋予机制,应用程序可以和其他的程序共享概功能或者数据给那那些与自己拥有相同数字证书的程序。如果某个权限(permission)的protectionLevel是signature,则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。
Android Layout
Android对用五大布局对象,它们分别是FrameLayout(框架布局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局).
(1) FrameLayout 是最简单的一个布局对象。它被定制为你屏幕上的一个空白备用区域,之后你可以在其中填充一个单一对象 — 比如,一张你要发布的图片。所有的子元素将会固定在屏幕的左上角;你不能为FrameLayout中的一个子元素指定一个位置。后一个子元素将会直接在前一个子元素之上进行覆盖填充,把它们部份或全部挡住(除非后一个子元素是透明的)。
(2)LinearLayout 以你为它设置的垂直或水平的属性值,来排列所有的子元素。所有的子元素都被堆放在其它元素之后,因此一个垂直列表的每一行只会有一个元素(或者是一个组合元素(一个水平的LinearLayout)),而不管他们有多宽,而一个水平列表将会只有一个行高(高度为最高子元素的高度加上边框高度)。LinearLayout保持子元素之间的间隔以及互相对齐(相对一个元素的右对齐、中间对齐或者左对齐)。
LinearLayout还支持为单独的子元素指定weight 。好处就是允许子元素可以填充屏幕上的剩余空间。这也避免了在一个大屏幕中,一串小对象挤成一堆的情况,而是允许他们放大填充空白。子元素指定一个 weight 值,剩余的空间就会按这些子元素指定的weight 比例分配给这些子元素。默认的 weight 值为0。例如,如果有三个文本框,其中两个指定了weight 值为1,那么,这两个文本框将等比例地放大,并填满剩余的空间,而第三个文本框不会放大。
(3)RelativeLayout 允许子元素指定他们相对于其它元素或父元素的位置(通过ID 指定)。因此,你可以以右对齐,或上下,或置于屏幕中央的形式来排列两个元素。元素按顺序排列,因此如果第一个元素在屏幕的中央,那么相对于这个元素的其它元素将以屏幕中央的相对位置来排列。如果使用XML 来指定这个 layout ,在你定义它之前,被关联的元素必须定义。
(4)TableLayout 将子元素的位置分配到行或列中。一个TableLayout 由许多的TableRow 组成,每个TableRow 都会定义一个 row (事实上,你可以定义其它的子对象,这在下面会解释到)。TableLayout 容器不会显示row 、cloumns 或cell 的边框线。每个 row 拥有0个或多个的cell ;每个cell 拥有一个View 对象。表格由列和行组成许多的单元格。表格允许单元格为空。单元格不能跨列,这与HTML 中的不一样。
(5)AbsoluteLayout 可以让子元素指定准确的x/y坐标值,并显示在屏幕上。(0, 0)为左上角,当向下或向右移动时,坐标值将变大。AbsoluteLayout 没有页边框,允许元素之间互相重叠(尽管不推荐)。我们通常不推荐使用 AbsoluteLayout ,除非你有正当理由要使用它,因为它使界面代码太过刚性,以至于在不同的设备上可能不能很好地工作。
4种Layout:FrameLayout,LinearLayout,TableLayout,RelativeLayout。
放入Layout中进行排布的View的XML属性:
4种Layout中Item所共有的XML属性:
(1)layout_width
(2)layout_height
(3)layout_marginLeft
(4)layout_marginTop
(5)layout_marginRight
(6)layout_marginBottom
(7)layout_gravity
FrameLayout中的Item就具有这些属性。
对于LinearLayout还会有
(8)layout_weight
TableLayout的行TableRow是一个横向的(horizontal)的LinearLayout。
RelativeLayout有16个align相关的XML属性。
(9)layout_above
(10)layout_alignBaseline
(11)layout_alignBottom
(12)layout_alignLeft
(13)layout_alignParentBottom
(14)layout_alignParentLeft
(15)layout_alignParentRight
(16)layout_alignParentTop
(17)layout_alignRight
(18)layout_alignTop
(19)layout_below
(20)layout_centerHorizontal
(21)layout_centerInParent
(22)layout_centerVertical
(23)layout_toLeftOf
(24)layout_toRightOf
(1)和(2)用来确定放入Layout中的View的宽度和高度:它们的可能取值为fill_parent,wrap_content或者固定的像素值。
(3)(4)(5)(6)是放入Layout中的View期望它能够和Layout的边界或者其他View之间能够相距一段距离。
(7)用来确定View在Layout中的停靠位置。
(8)用于在LinearLayout中把所有子View排布之后的剩余空间按照它们的layout_weight分配给各个拥有这个属性的View。
(9)到(24)用来确定RelativeLayout中的View相对于Layout或者Layout中的其他View的位置。
android:layout_marginLeft指该控件距离边父控件的边距,android:paddingLeft指该控件内部内容,如文本距离该控件的边距。
如:
当按钮分别设置以上两个属性时,得到的效果是不一样的。
android:paddingLeft="30px":
按钮上设置的内容(例如图片)离按钮左边边界30个像素。
android:layout_marginLeft="30px"
整个按钮离左边设置的内容30个像素
这二个属性是相对的,假设B是A的子控件,设置B的margin和设置A的padding能达到相同的效果。
DPI
术语 | 说明 | 备注 |
Screen size(屏幕尺寸) | 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸 | 摩托罗拉milestone手机是3.7英寸 |
Aspect Ratio(宽高比率) | 指的是实际的物理尺寸宽高比率,分为long和nolong | Milestone是16:9,属于long |
Resolution(分辨率) | 和电脑的分辨率概念一样,指手机屏幕纵、横方向像素个数 通过settings->开发人员选项-->指针位置 查看 | Milestone是854*480 |
DPI(dot per inch) | 每英寸像素数,如120dpi,160dpi等,假设QVGA(320*240)分辨率的屏幕物理尺寸是(2英寸*1.5英寸),dpi=160 | 可以反映屏幕的清晰度,用于缩放UI的 |
Density(密度) | 屏幕里像素值浓度,resolution/Screen size可以反映出手机密度, |
|
Density-independent pixel (dip) layout文件中使用 | 指的是逻辑密度计算单位,dip和具体像素值的对应公式是dip/pixel=dpi值/160,也就是px = dp * (dpi / 160) |
|
View 和View Group当屏幕density=240时使用hdpi标签的资源;
当屏幕density=160时,使用mdpi标签的资源 ;
当屏幕density=120时,使用ldpi标签的资源
根据google的推荐,像素统一使用dip,字体统一使用sp
比个如吧,用画一条长度为240px的横线,在480宽的模拟器上看就是一半的屏宽,而在320宽的模拟器上看就是2/3的屏宽了。而dip,就是把屏幕的高分成480分,宽分成320分。比如你做一条160dip的横线,无论你在320还480的模拟器上,都是一半屏的长度。
(1)View 和View Group之间的关系
ViewGroup继承自View,ViewGroup中包含多个View,Layout继承自ViewGroup
两种获得View的方式
(1) 一种是Layout中声明,然后通过setContentView(int) 创建,通过findViewById(获得
(2)一种是通过Inflate
点击(此处)折叠或打开
- View view;
- LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflater.inflate(R.layout.mylayout, null);
- RelativeLayout item = (RelativeLayout) view.findViewById(R.id.item);
LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
点击(此处)折叠或打开
- container = (RelativeLayout) findViewById(R.id.lay_container);
- myview = new MyView(this);
- RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutPparams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
- myview.setLayoutParams(params);
- container.addView(myview);
通过ViewGroup.addView() 的方式将View 加入到ViewGroup,通过重写 onMeasure() 确定子控件的大小( 参考layout.xml 中的属性和父控件要求的measurespec ),最后通过 setMeasuredDimension(int, int) 进行设定,通过重写ViewGroup的onLayout() 确定子View在ViewGroup中的位置
自定义View 重写onDraw() 方法,在XML 中做如下声明:
点击(此处)折叠或打开
- <LinearLayout>
- <packageName.myView android:id="@+id/myView" />
- </LinearLayout>
- (MyView) myView = (MyView) findViewById(R.id.myView);
ListView Adapter data
在layout中使用
Android 性能优化
ListView/ConvertView/ViewHolder
异步线程加载数据,刷新UI
FPS
内存泄漏
生命周期短的引用指向周期长的引用Activity-->static 变量
cursor没有close
bitmap没有recyle
对象不用的时候,引用没有设置为null
listener没有unregister()
View切换和动画
ViewPager/ViewFlow/ViewSwitcher
Android 事件处理
HTTP/TCP/UDP
TraceView
Android Test Case Monkey
NDK JNI
android mk文件