《Android开发艺术探索》读后笔记1

第一章

Activity的生命周期

  1. activity,活动,四大组件之一,直接译为界面更加合适.
  2. activity的生命周期有两种:一是典型情况下的生命周期,另一种是异常情况下的生命周期.
  3. 典型情况的生命周期,是指在有用户参与的情况下activity所经历的生命周期的变化;异常情况的生命周期是指activity由于系统内存不足被系统回收或者由于当前设备的Configuration(资源相关的系统配置)发生改变从而导致activity被销毁重建,两者的生命周期不同.
  4. 典型情况下的生命周期:onCreate()—onStart()—onResume()—onPause()—onStop()—onDestory().
    这里写图片描述
  5. onStart()与onResume()的区别:onStart()指activity已经可见,但是还没有出现在前台,没有获得焦点;而onResume()表示ativity已经可见并且出现在了前台,获取了焦点.除此之外,两者实际使用中没有明显区别.
  6. activity典型生命周期中的两个内圈,注意两个内圈都会不建议做耗时操作,因为会影响到新的activity的显示.
  7. 当用户打开新的activity或者切换到桌面的时候.onPause–onStop方法会被回调.但是有一种特殊情况:当新activity采用了透明的主题时,那么当前的activity不会回调onStop方法,因为界面不可见.
  8. 当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate方法,所以在onPause方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率.onPause()—onCreate()—onStart()—onResume()—onStop()(最后旧的activity会在新activity启动完成后onStop).
  9. 异常情况下activity的生命周期又可以分为两种情况:第一种是由于资源相关的系统配置发生改变导致activity被杀死并重新创建;第二种是由于系统资源不足导致低优先级的activity被杀死.
  10. 异常activity生命周期的第一种情况下,如果我们不做特殊处理,那么当系统资源配置发生改变后(如果横竖屏转换),activity就会被销毁并且重建,其生命周期如下图:
    这里写图片描述
    activity异常终止的时候,onSavedInstanceState会在activity的onStop方法之前被调用来保存当前activity的视图结构(状态),包括如文本框中用户输入的数据,ListView的滚动位置等.当activity被重新创建后,系统会调用onRestoreInstanceState方法,并且把activity销毁时onSavedInstanceState方法保存的Bundle对象作为参数传递给onRestoreInstanceState方法和onCreate方法.注意:只会在Activity被异常终止的情况下被调用,正常情况下系统不会回调这两个方法.
  11. 我们可以通过onCreate方法和onRestoreInstanceState方法来判断activity是否被重建,如果被重建,那么我们可以取出之前保存的数据并恢复.onRestoreInstanceState方法的调用时候在onStart之后.
  12. onCreate方法和onRestoreInstanceState方法都可以用来在activity异常终止后获取onSaveInsatnceState方法中存储数据,但是二者区别在于:onRestoreInstanceState一旦被调用,其参数bundle必定为非空,不需要在方法内做空值判断.而onCreate方法在获取数据时必须进行额外判断.官方推荐使用onRestoreInstanceState去恢复数据.
  13. 异常activity生命周期的第二种情况下,即资源内存不足导致低优先级的activity被杀死.activity按照优先级从高到低,可以分为三种情况:前台activity—可见但非前台activity—后台activity.
  14. View和Activity一样,每个View都有onSavedInstanceState和onRestoreInstanceState这两个方法,用于保存和恢复view的状态.
  15. 如果一个进程中没有四大组件在执行,那么这个进程很快被系统杀死,因此一些后台工作不适合脱离四大组件独自运行在后台中,最好的做法是将后台工作放到Service中从而保证进程有一定的优先级,这样就不会轻易被杀死.
  16. 如何在系统资源配置发生变化后,不重新创建activity,那么我们可以给activity指定configChanges属性.可以指定多个值,中间使用”|”连接起来.如:android:configChanges = “orientation|keyboardHidden”.
  17. configChanges的项目和含义:项目有很多,我们经常用到的只有:local(设备的本地位置发生了改变,一般指切换了系统语言),orientation(屏幕方向发生了改变),keyboardHidden(键盘的可访问性发生了改变,如用户调出了键盘)这三项,其他比较少用,要注意的是screenSize(屏幕尺寸发生变化,如设备旋转),smallestScreenSize(一般指切换到了外部的显示设备,表示实际的物理尺寸发生改变,与屏幕旋转无关),这两项的行为与编译选项有关,当编译的minSdkVersion和targetSdkVersion均低于13时,此选项不会导致activity重启,否则会发生重启,这时候需要设置screenSize和smallestScreenSize.设置后不会再调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,但是取而代之系统回去调用onConfigChangesd()方法,我们可以在这个方法中做一些特殊的逻辑处理.

Activity的启动模式

activity的launchMode是一种后进先出的栈结构,当任务栈中没有任何Activity的时候,系统就会回收这个任务栈.在默认情况下,我们启动activity会发现一个问题,多次启动同一个activity,系统会重复的创建多个实例.所以系统提供了启动模式来修改系统的默认行为.启动模式有以下四种:
1. standard 系统默认。每次启动会重新创建新的实例,谁启动了这个Activity,这个Activity就运行在启动它的栈里.但是有一种特殊情况,但我们使用ApplicationContext(非activity类型的Context)去启动standard模式的activity时,会报错:calling start activity from outside of activity context requires the FLAG ACTIVTY NEW_TASK flag.所以解决办法是为待启动的activity指定FLAG ACTIVTY NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这时候,待启动的activity是以singleTask模式启动的.
2. singleTop 栈顶复用模式。该Activity的onNewIntent方法会被回调,通过此方法我们可以取出当前请求的信息,onCreate和onStart并不会被调用,但如果新activity实例已经存在但不是位于栈顶,那么新activity依然会重新创建.
3. singleTask 栈内复用模式。只要该Activity在一个栈中存在,都不会重新创建,onNewIntent会被回调。如果不存在,系统会先寻找是否存在需要的栈,如果不存在该栈,就创建一个任务栈,然后把Activity实例放进栈中;如果存在该栈,再看是否存在实例,如果存在实例,系统就会把它调到栈顶并调用它的onNewIntent方法(同时singleTask默认具有clear top 效果,FLAG_ACTIVITY_CLEAR_TOP,会导致栈内所有在该实例上面的activity全部出栈),如果不存在,就会创建activity实例并压入栈中。
4. singleInstance 单实例模式。此为加强版的singleTask模式,除具备singTask所有的特性外,那就是具有此种模式的Activity只能单独存在于一个任务栈,而且由于栈内复用的特性,后续的请求均不会创建新的activity,除非这个独特的任务栈被系统销毁了.
5. 任务栈分为前台任务栈和后台任务栈,后台任务栈的activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台.
6. 在singleTask 启动模式中所提到的activity所需要的任务栈,那么什么是activity所需要的任务栈呢?我们要先了解标识Activity任务栈名称的属性:TaskAffinity(任务相关性),默认为应用包名。我们也可以指定,但是不能和应用包名一样,否则相当于没有指定.TaskAffinity主要和singleTask或者allowTaskReparenting属性配对使用,在其他情况下没有意义.
(1)当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的activity的目前的任务栈的名字,待启动的activity会运行在名字和TaskAffinity相同的任务栈中.
(2) 当TaskAffinity和allowTaskReparenting这个情况比较复杂.当一个应用A启动了应用B的某个activity后,如果这个activity的allowTaskReparenting属性为true,那么当应用B被启动的时候,此activity会从应用A的任务栈转移到B的任务栈中,也即应用A启动了应用B中的activityC,然后按home键回到桌面,这时候再单击B应用的桌面图标,这个时候启动的不是B应用的主activity,而是重新显示已经被A启动的activityC.
7. 设置启动模式既可以使用xml属性android:launchMode,也可以使用代码intent.addFlags(Intent.FLAG ACTIVTY NEW_TASK)。区别在于优先级和限定范围不同,代码形式优先级高;前者无法直接为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识,而后者无法为Activity指定singleInstance模式

Activity的Flags(标记位)

  1. FLAG_ACTIVITY_NEW_TASK : 这个标记位的作用是为Activity指定”singleTask”启动模式,其效果和在XML中指定该启动模式相同
  2. FLAG_ACTIVITY_SINGLE_TOP : 这个标记的使用是为Activitty指定”SingleTop”启动模式,其效果和在XML中指定该启动模式相同.
  3. FLAG_ACTIVTIY_CLEAR_TOP : 具有此标记的Acivity,当它启动时,在同一个任务栈中所有位于在上面的Activty都要出栈,这个模式会和singleTask启动模式一起出现,默认情况下singleTask启动模式具有此标记位的效果.在这种情况activity已经存在,系统会调用onNewintent,在这之上的都要出栈。
  4. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有这个标记和Activtiy不会出现在历史的Activity的列表中,等同于Activity的属性android:excludeFromRecents=”true”

activity的匹配规则

  1. 启动Activity分为两种,显示调用和隐式调用 ,如果两者都存在,以显示调用为准.
  2. IntentFilter中的过滤信息有action、category、data,为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别、category类别和data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。此外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intenf-filter即可成功启动对应的Activity。
<intent-filter>
    <action android:name="com.ryg.charpter_1.c" />
    <action android:name="com.ryg.charpter_1.d" />

    <category android:name="com.ryg.category.c" />
    <category android:name="com.ryg.category.d" />
    <--DEFAULT情况下可以不用匹配,activity默认启动时候会添加 -->
    <category android:name="android.intent.category.DEFAULT" />

    <data android:mimeType="text/plain" />
</intent-filter>

3.action匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action,只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,action匹配区分大小写。即上面的只要我们的Intent中的action值为”com.ryg.charpter_1.c”或者”com.ryg.charpter_1.d”都能匹配成功。如果没有指定action,则会匹配失败。
4. category匹配规则
Intent中如果有category那么所有的category都必须和过滤规则中的其中一个category相同,如果没有category的话那么就是默认的category,即android.intent.category.DEFAULT,所以为了Activity能够接收隐式调用,配置多个category的时候必须加上默认的category,因为系统在startActivity 或者startActivityForResult的时候会默认为intent加上android.intent.category.DEFAULT这个category,我们可以不匹配category也可以隐式启动activity.
5. 注意action和category的匹配规则不一样,action要求intent中必须有一个action而且必须能够和过滤规则中的某个action相同,而category要求Intent可以没有category,但是如果有category,不管几个都要和过滤规则中的任何一个category相同.
6. data匹配规则
data的匹配规则和action一样,Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data,data的结构很复杂,语法大致如下:

<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />

主要由mimeType和URI组成,其中mimeType代表媒体类型(有图片,文本,视频等不同的媒体格式),而URI的结构也复杂,大致如下:

<scheme>://<host>:<port>/[<path>]|[<pathPrefix>]|[pathPattern]

例如
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
scheme、host、port分别表示URI的模式、主机名和端口号,其中如果scheme或者host未指定那么URI就无效。
path、pathPattern、pathPrefix都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面包含了通配符(),”“表示0个或者多个任意字符,需要注意的是由于正则表达式的规范,如果想表示真实的字符串,那么”“要写成”\“;”\”要写成”\\”.
7. 如果过滤规则中的mimeType指定为image/或者text/等这种类型的话,那么即使过滤规则中没有指定URI,URI有默认的scheme是content和file!如果过滤规则中指定了scheme的话那就不是默认的scheme了。

//(1)URI有默认值
<intent-filter>
    <data android:mimeType="image/*"/>
    ...
</intent-filter>
//(2)URI默认值被覆盖
<intent-filter>
        <data android:mimeType="image/*" android:scheme="http" .../>
    ...
</intent-filter>

(1)匹配如下:

intent.setDataAndType(Uri.parse("file://abc"), "image/png");

如果要为Intent指定完整的data,必须要调用setDataAndType方法!不能先调用setData然后调用setType,因为这两个方法会彼此清除对方的值。
data的下面两种写法作用是一样的:

<intent-filter>
    <data android:scheme="file" android:host="www.github.com"/>
</intent-filter>

<intent-filter>
    <data android:scheme="file"/>
    <data android:host="www.github.com"/>
</intent-filter>
  1. Intent-filter匹配规则对于Service和BroadcastReceiver也是适用的,但是Service系统建议使用显式调用方式来启动服务.
  2. 如何判断是否有Activity能够匹配我们的隐式Intent,来防止出现无法找到activity的错误?
    (1)PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
    (2)PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity信息,而不是最佳匹配activity的信息.

方法原型:

public abstract List<ResolveInfo> queryIntentActivities(Intent intent,int flags);

public abstract ResolveInfo resolveActivity(Intent intent,int flags);

第二个参数需要注意,我们要使用MATCH_DEAFAULT_ONLY这个标记,这个标记位的含义是仅仅匹配那些在intent-filter中声明了

<category android:name="android.intent.category.DEFAULT">

这个category的Activity,
针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息,例如queryIntentServices、queryBroadcastReceivers等方法.
10. 有一类action和category比较重要,它们在一起用来标明这是一个入口Activity,并且会出现在系统的应用列表中。

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值