首页 > 技术文章 > activity之相关

awkflf11 2018-06-18 21:02 原文

 

11.Activity几种情况生命周期?
12.ActA与 ActB互相跳转生命周期情况?
21.旋转屏幕Act执行的生命周期方法?
22.Android禁止屏幕旋转 & 旋转屏幕时保持Act内容?

---------

四.Activity相关?
1.Binder机制
binder机制是android端进程间通信的基石,采用aidl的ipc通信方式,利用它来定义两个进程相互通信的接口。
他是基于Service实现的一种线程间通信机制。它的本质是C/S架构的,需要一个服务器端,一个客户端。
AIDL通信方式里面有四个对象,一个是IInterface,专门用来负责接口的调度,Stub用来负责通信的响应和发送给service端的数据,Proxy负责两个进程通信的包装,算是间接调用Stub的包装类,service是服务端处理数据的关键类。用一张图来表示如下:

2.App启动流程
• 从Linux内核系统到init进程的分裂,以及后面会启动一个叫Zygote的进程开始,而Zygote会分裂出系统的核心服务进程SystemServer,也就是SystemServer里面包括了底层的ActivityManagerService、PackageManagerService、WindowManagerService等,这些核心服务都是通过Zygote.init启动的,ActivityManagerService就是我们后面通过binder的ipc通信机制来与客户端ActivityThread建立通信的。
• 当我们点击了应用之后,系统的Launcher应用会通过startActivity的方式启动应用,而Intent的获取会经过如下几部: (1) ActivityManagerService会通过PackageManager的resolveIntent()收集这个intent对象的指向信息。 (2)指向信息被存储在一个intent对象中。 (3)下面重要的一步是通过grantUriPermissionLocked()方法来验证用户是否有足够的权限去调用该intent对象指向的Activity。 (4)如果有权限, ActivityManagerService会检查并在新的task中启动目标activity. (5)现在, 是时候检查这个进程的ProcessRecord是否存在了。
• 所以如果ProcessRecord不是null,ActivityManagerService会创建新的进程来实例化该activity。
• ActivityManagerService调用startProcessLocked()方法来创建新的进程, 该方法会通过前面讲到的socket通道传递参数给Zygote进程. Zygote孵化自身, 并调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。
• 随后就是我们熟悉的ActivityThread.main方法通过Looper.prepare和Looper.loop方法开启消息循环
• 紧接着就是创建Application对象的过程,先是创建好ContextImpl对象,然后通过makeApplication方法将app进程与Application建立联系,这里的Application创建交给了Instrumentation的对象,其实后面activity的创建,生命周期的回调都是通过它来触发的。
• 创建完Application后,紧接着就是我们熟悉的Activity,activity的创建同样交给了Instrumentation对象,上面说过ActivityManagerService会将携带的Intent对象交给了Lanucher应用,Lanucher的startActivity经过一系列的操作,最终会走Instrumentation的execStartActivity方法,该方法里面会去请求ActivityManagerService服务,最终通过binder通信将信息传给了客户端的ApplicationThread,最终会触发ApplicationThread的scheduleLaunchActivity方法,该方法将消息发送给了ActivityThread的handler对象,最终交给了Instrumentation对象创建activity。后面也就触发一系列的生命周期方法。
3.Handle相关:
Handler 工作流程基本包括 Handler、Looper、Message、MessageQueue 四个部分。但我们在日常开发中,经常都只会用到 Handler 和 Message 两个类。Message 负责消息的搭载,里面有个target用于标记消息,obj用于存放内容,Handler 负责消息的分发和处理。
一般在开发中是怎么使用 Handler 的?
官方不允许在子线程中更新 UI,所以我们经常会把需要更新 UI 的消息直接发给处理器 Handler,通过重写 Handler 的handleMessage()方法进行 UI 的相关操作。
Handle使用中就没什么需要注意的吗?
有,Handler 如果设置为私有变量的话,Android Studio 会报警告,提示可能会造成内存泄漏,这种情况可以通过设置为静态内部类 + 弱引用,或者在onDestroy()方法中调用Handler.removeCallbacksAndMessages(null)即可避免
Handler 整体工作流程浅析分为以下四个步骤:
异步通信准备 => 消息入队 => 消息循环 => 消息处理
A:异步通信准备
I:假定是在主线程创建 Handler,则会直接在主线程中创建处理器对象Looper、消息队列对象MessageQueue和 Handler 对象。
需要注意的是,Looper和MessageQueue均是属于其创建线程的。
II:Looper对象的创建一般通过Looper.prepareMainLooper()和Looper.prepare()两个方法,而创建Looper对象的同时,将会自动创建MessageQueue。
III:创建好MessageQueue后,Looper将自动进入消息循环。此时,Handler自动绑定了主线程的Looper和MessageQueue。
B:消息入队
工作线程通过Handler发送消息Message到消息队列MessageQueue中,消息内容一般是 UI 操作。发送消息一般都是通过Handler.sendMessage(Message msg)和Handler.post(Runnabe r)两个方法来进行的。而入队一般是通过MessageQueue.enqueueeMessage(Message msg,long when)来处理。
C:消息循环
主要分为「消息出队」和「消息分发」两个步骤,Looper会通过循环取出消息队列MessageQueue里面的消息Message,并分发到创建该消息的处理者Handler。如果消息循环过程中,消息队列MessageQueue为空队列的话,则线程阻塞。
D:消息处理
Handler接收到Looper发来的消息,开始进行处理。
android消息机制?
消息机制指Handler、Looper、MessageQueue、Message之间如何工作的。
• handler是用来处理消息和接收消息的中间者,handler的创建会伴随着handler中产生looper和MessageQueue,handler依赖于looper,looper依赖于MessageQueue,所以在子线程中使用handler抛出异常是因为子线程中没有初始化looper对象,而主线程中looper是在ActivityThread中已经初始化过了,所以能直接在主线程中能拿到Handler。
• Looper是用来轮询消息,说白了就是通过loop方法实现死循环,有消息的时候,通过MessageQueue.next方法取出message,没有消息的时候,线程处于阻塞的状态。在有消息的时候获取到消息,将消息交给了handler,handler会根据消息中有没有callback,如果有callback会直接callback,否则通过handleMessage处理。
• MessageQueue是一个单链表结构来存储Message,每次通过next方法取出Message消息后,取完之后将message.next给当前的message,再将message.next=null,实际上就是移除当前的message。但是在looper里面每次在next取出message后,放到了message的sPool里面,缓存起来方便使用。
• Message就没什么好说的,主要存储平常经常用的obj和what信息,以及我们不用关心的target和callback等。
这里会问到,一个线程会有几个Looper,几个Handler,以及Looper会存在线程哪里?
一个线程一个Looper,可以有多个Handler,Looper会存在线程的ThreadLocal对象里,该对象是线程的缓存区。
ThreadLocal:它是和线程一一对应的,从Thread类可以看出来,ThreadLocal是作为Thread变量来使用。ThreadLocal只是ThreadLocalMap的一个包装类,实现了get和set方法,而ThreadLocalMap实际是一个由Entry内部类组成的数组,Entry是继承自弱应用,弱引用里面放的就是ThreadLocal当前对象,Entry的value存的是当前线程要存储的对象,value作为Entry的成员变量。 ThreadLocal经常会问到内存泄漏的问题,从上面分析可以发现ThreadLocalMap里面的Entry对象存储的ThreadLocal弱引用,而value直接作为Entry的强引用,因此在用到了ThreadLocal的地方,防止内存泄漏,手动调用remove方法。
4.IntentService
IntentService是google在原生的Service基础上通过创建子线程的Service。也就是说IntentService是专门为android开发者提供的能在service内部实现耗时操作的service。我们可以通过重写onHandleIntent方法实现耗时操作的回调处理,而且IntentService在耗时操作完成后,会主动销毁自己,IntentService可以通过多次启动来完成多个任务,而IntentService只会被创建一次,每次启动的时候只会触发onStart方法。内部是实现了Handler异步处理耗时操作的过程,一般多用在Service中需要处理耗时操作的功能。
IntentService:
IntentService是Service的子类,比普通的Service增加了额外的功能。IntentService会创建独立的worker线程来处理所有的Intent请求;会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程的问题;所有请求处理完成后,IntentService会自动停止,开发者无需手动调用stopSelf()方法停止Service;
提问:为什么IntentService中能实现耗时操作?
• 在onCreate中,通过HandlerThread来开启一条线程,而HandlerThread线程中会跟我们平常用的Handler不太一样,在run方法中创建了looper对象,所以HandlerThread能让IntentService在子线程中使用handler达到耗时操作。
5.HandlerThread
HandlerThread本身也是Thread,只是在Thread基础上封装上了Handler的载体,并且在run方法中创建了looper对象,这也是为什么在IntentService中能在HandlerThread中直接用handler的原因。而我们知道一个线程是可以有多个handler,所以用HandlerThread更加方便我们不用关心Handler的创建,一般用在多线程中直接处理任务。
6. Android中的四大组件相关?
Activity:
Activity是一个应用程序组件,提供一个屏幕(狭义的理解就是当前APP的界面),用户可以用来交互为了完成某项任务。(点击,登录,跳转页面)
Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件(设置布局文件)。
在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。
Activity四种启动模式?
Activity的启动模式指,可以根据实际开发需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activity等问题。
1)standard
standard为Activity的默认启动模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。(点back键会依照栈顺序依次退出)
2)singleTop
singleTop模式下,Activity可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
3)singleTask
singleTask表示只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
4)singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
BraodcastReceiver:(待补充)
使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
注册的方式分为两种:静态注册、动态注册
ContentProvider:(待补充)
外界可以通过ContentResolver接口来访问ContentProvider(内容提供者)中的数据。Uri 通用资源标志符(Universal Resource Identifier)Uri代表要操作的数据,Android中可用的每种资源 - 图像、视频片段等都可以用Uri来表示。ContentProvider共享数据是通过定义一个对外开放的统一的接口来实现的。然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。
简述System.exit(0) 、onDestory()、Activity.finish()三者的区别
1)System.exit(0) 是你正常结束程序,kill 掉当前进程,针对的是整个Application
2)onDestory()方法是Activity生命周期的最后一步,资源空间等就被回收了。当重新进入此Activity的时候,必须重新创建,执行onCreate()方法.
3)Activity.finish()当你调用此方法的时候,系统只是将最上面的Activity移出了栈,并没有及时的调用onDestory()方法,也就是占用的资源没有被及时释放。
Activity中的几种启动模式
一般会考察有哪几种启动模式,以及每种启动模式在什么场景下使用:
standard:这个是android默认的Activity启动模式,每启动一个Activity都会被实例化一个Activity,并且新创建的Activity在堆栈中会在栈顶。
singleTop:如果当前要启动的Activity就是在栈顶的位置,那么此时就会复用该Activity,并且不会重走onCreate方法,会直接它的onNewIntent方法,如果不在栈顶,就跟standard一样的。如果当前activity已经在前台显示着,突然来了一条推送消息,此时不想让接收推送的消息的activity再次创建,那么此时正好可以用该启动模式,如果之前activity栈中是A-->B-->C如果点击了推动的消息还是A-->B--C,不过此时C是不会再次创建的,而是调用C的onNewIntent。而如果现在activity中栈是A-->C-->B,再次打开推送的消息,此时跟正常的启动C就没啥区别了,当前栈中就是A-->C-->B-->C了。
singleTask:该种情况下就比singleTop厉害了,不管在不在栈顶,在Activity的堆栈中永远保持一个。这种启动模式相对于singleTop而言是更加直接,比如之前activity栈中有A-->B-->C---D,再次打开了B的时候,在B上面的activity都会从activity栈中被移除。下面的acitivity还是不用管,所以此时栈中是A-->B,一般项目中主页面用到该启动模式。
singleInstance:该种情况就用得比较少了,主要是指在该activity永远只在一个单独的栈中。一旦该模式的activity的实例已经存在于某个栈中,任何应用在激活该activity时都会重用该栈中的实例,解决了多个task共享一个activity。其余的基本和上面的singleTask保持一致。
上面的各种启动模式主要是通过配置清单文件,常见还有在代码中设置flag也能实现上面的功能:
FLAG_ACTIVITY_CLEAR_TOP:这种启动的话,只能单纯地清空栈上面的acivity,而自己会重新被创建一次,如果当前栈中有A-->B-->C这几种情况,重新打开B之后,此时栈会变成了A-->B,但是此时B会被重新创建,不会走B的onNewIntent方法。这就是单独使用FLAG_ACTIVITY_CLEAR_TOP的用处,能清空栈上面的activity,但是自己会重新创建。 如果在上面的基础上再加上FLAG_ACTIVITY_SINGLE_TOP此时就不重新创建B了,也就直接走B的onNewIntent。它两者结合着使用就相当于上面的singleTask模式。 如果只是单独的使用FLAG_ACTIVITY_SINGLE_TOP跟上面的singleTop就没啥区别了。
FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP=singleTask,此时要打开的activity不会被重建,只是走onNewIntent方法。
FLAG_ACTIVITY_SINGLE_TOP=singleTop
FLAG_ACTIVITY_NEW_TASK
• 在相同taskAffinity情况下:启动activity是没有任何作用的。
• 在不同taskAffinity情况下: 如果启动不同栈中的activity已经存在了某一个栈中的activity,那么此时是启动不了该activity的,因为栈中已经存在了该activity;如果栈中不存在该要启动的activity,那么会启动该acvitity,并且将该activity放入该栈中。
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP一起使用,并且要启动的activity的taskAffinity和当前activity的taskAffinity不一样才会和singleTask一样的效果,因为要启动的activity和原先的activity不在同一个taskAffinity中,所以能启动该activity,有点绕,写个简单的公式:
• FLAG_ACTIVITY_NEW_TASK如果启动同一个不同taskAffinity的activity才会有效果。
• FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP如果一起使用要开启的activity和现在的activity处于同一个taskAffinity,那么效果还是跟没加FLAG_ACTIVITY_NEW_TASK是一样的效果。
• FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP启动和现在的activity不是同一个taskAffinity才会和singleTask一样的效果。
FLAG_ACTIVITY_CLEAR_TASK
• 在相同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,启动activity是没有任何作用的。
• 在不同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,如果要启动的activity不存在栈中,那么启动该acitivity,并且将该activity放入该栈中,如果该activity已经存在于该栈中,那么会把当前栈中的activity先移除掉,然后再将该activity放入新的栈中。
FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_SINGLE_TOP用在当app正在运行点击push消息进到某个activity中的时候,如果当前处于该activity,此时会触发activity的onNewIntent。
FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP用在app没在运行中,启动主页的activity,然后在相应的activity中做相应的activity跳转。

一.Activity的生命周期和启动模式

1.1 Activity的生命周期全面分析

用户正常使用情况下的生命周期 & 由于Activity被系统回收或者设备配置改变导致Activity被销毁重建情况下的生命周期。

1.1.1 典型情况下的生命周期分析

Activity的生命周期和启动模式

 

 
  1. Activity第一次启动:onCreate->onStart->onResume。
  2. Activity切换到后台( 用户打开新的Activity或者切换到桌面) ,onPause->onStop(如果新Activity采用了透明主题,则当前Activity不会回调onstop)。
  3. Activity从后台到前台,重新可见,onRestart->onStart->onResume。
  4. 用户退出Activity,onPause->onStop->onDestroy。
  5. onStart开始到onStop之前,Activity可见。onResume到onPause之前,Activity可以接受用户交互。
  6. 在新Activity启动之前,栈顶的Activity需要先onPause后,新Activity才能启动。所以不能在onPause执行耗时操作。
  7. onstop中也不可以太耗时,资源回收和释放可以放在onDestroy中。

1.1.2 异常情况下的生命周期分析

1 系统配置变化导致Activity销毁重建

例如Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,Activity就会被销
毁并重新创建。
在异常情况下系统会在onStop之前调用onSaveInstanceState来保存状态。Activity重新创建后,会在onStart之后调用onRestoreInstanceState来恢复之前保存的数据。

保存数据的流程: Activity被意外终止,调用onSaveIntanceState保存数据-> Activity委托Window,Window委托它上面的顶级容器一个ViewGroup( 可能是DecorView) 。然后顶层容器在通知所有子元素来保存数据。

这是一种委托思想,Android中类似的还有:View绘制过程、事件分发等。

系统只在Activity异常终止的时候才会调用 onSaveInstanceState 和onRestoreInstanceState 方法。其他情况不会触发。

2 资源内存不足导致低优先级的Activity被回收
三种Activity优先级:前台- 可见非前台 -后台,从高到低。
如果一个进程没有四大组件,那么将很快被系统杀死。因此,后台工作最好放入service中。

android:configChanges="orientation" 在manifest中指定 configChanges 在系统配置变化后不重新创建Activity,也不会执行 onSaveInstanceState 和onRestoreInstanceState 方法,而是调用 onConfigurationChnaged 方法。
附:系统配置变化项目

configChanges 一般常用三个选项:

  1. locale 系统语言变化
  2. keyborardHidden 键盘的可访问性发生了变化,比如用户调出了键盘
  3. orientation 屏幕方向变化

1.2 Activity的启动模式

1.2.1 Activity的LaunchMode

Android使用栈来管理Activity。

  1. standard
    每次启动都会重新创建一个实例,不管这个Activity在栈中是否已经存在。谁启动了这个Activity,那么Activity就运行在启动它的那个Activity所在的栈中。
    用Application去启动Activity时会报错,原因是非Activity的Context没有任务栈。解决办法是为待启动Activity制定FLAG_ACTIVITY_NEW_TASH标志位,这样就会为它创建一个新的任务栈。
  2. singleTop
    如果新Activity位于任务栈的栈顶,那么此Activity不会被重新创建,同时回调 onNewIntent 方法。onCreate和onStart方法不会被执行。
  3. singleTask
    这是一种单实例模式。如果不存在activity所需要的任务栈,则创建一个新任务栈和新Activity实例;如果存在所需要的任务栈,不存在实例,则新创建一个Activity实例;如果存在所需要的任务栈和实例,则不创建,调用onNewIntent方法。同时使该Activity实例之上的所有Activity出栈。
    参考:taskAffinity标识Activity所需要的任务栈
  4. singleIntance
    单实例模式。具有singleTask模式的所有特性,同时具有此模式的Activity只能独自位于一个任务栈中。

假设两个任务栈,前台任务栈为12,后台任务栈为XY。Y的启动模式是singleTask。现在请求Y,整个后台任务栈会被切换到前台。如图所示:

设置启动模式

  1. manifest中 设置下的 android:launchMode 属性。
  2. 启动Activity的 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 。
  3. 两种同时存在时,以第二种为准。第一种方式无法直接为Activity添加FLAG_ACTIVITY_CLEAR_TOP标识,第二种方式无法指定singleInstance模式。
  4. 可以通过命令行 adb shell dumpsys activity 命令查看栈中的Activity信息。

1.2.2 Activity的Flags

这些FLAG可以设定启动模式、可以影响Activity的运行状态。

  • FLAG_ACTIVITY_NEW_TASK
    为Activity指定“singleTask”启动模式。
  • FLAG_ACTIVITY_SINGLE_TOP
    为Activity指定“singleTop"启动模式。
  • FLAG_ACTIVITY_CLEAR_TOP
    具有此标记位的Activity启动时,同一个任务栈中位于它上面的Activity都要出栈,一般和FLAG_ACTIVITY_NEW_TASK配合使用。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果设置,新的Activity不会在最近启动的Activity的列表(就是安卓手机里显示最近打开的Activity那个系统级的UI)中保存。等同于在xml中指定android:exludeFromRecents="true"属性。

1.3 IntentFilter的匹配规则

Activity调用方式

  1. 显示调用 明确指定被启动对象的组件信息,包括包名和类名
  2. 隐式调用 不需要明确指定组件信息,需要Intent能够匹配目标组件中的IntentFilter中所设置的过滤信息。

匹配规则

  • IntentFilter中的过滤信息有action、category、data。
  • 只有一个Intent同时匹配action类别、category类别、data类别才能成功启动目标Activity。
  • 一个Activity可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。

** action**
action是一个字符串,匹配是指与action的字符串完全一样,区分大小写。
一个intent-filter可以有多个aciton,只要Intent中的action能够和任何一个action相同即可成功匹配。
Intent中如果没有指定action,那么匹配失败。

** category**
category是一个字符串。
Intent可以没有category,但是如果你一旦有category,不管有几个,每个都必须与intent-filter中的其中一个category相同。
系统在 startActivity 和 startActivityForResult 的时候,会默认为Intent加上 android.intent.category.DEFAULT 这个category,所以为了我们的activity能够接收隐式调用,就必须在intent-filter中加上 android.intent.category.DEFAULT 这个category。

** data**
data的匹配规则与action一样,如果intent-filter中定义了data,那么Intent中必须要定义可匹配的data。
intent-filter中data的语法:

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

Intent中的data有两部分组成:mimeType和URI。mimeType是指媒体类型,比如
image/jpeg、audio/mpeg4-generic和video/等,可以表示图片、文本、视频等不同的媒
体格式。

URI的结构:

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

实际例子

    content://com.example.project:200/folder/subfolder/etc
    http://www.baidu.com:80/search/info

scheme:URI的模式,比如http、file、content等,默认值是 file 。
host:URI的主机名
port:URI的端口号
path、pathPattern和pathPrefix:这三个参数描述路径信息。
path、pathPattern可以表示完整的路径信息,其中pathPattern可以包含通配符 * ,表示0个或者多个任意字符。
pathPrefix只表示路径的前缀信息。

过滤规则的uri为空时,有默认值content和file,因此intent设置uri的scheme部分必须为content或file。
Intent指定data时,必须调用 setDataAndType 方法, setData 和 setType 会清除另一方的值。
对于service和BroadcastReceiver也是同样的匹配规则,不过对于service最好使用显式调用。

隐式调用需注意

    • 当通过隐式调用启动Activity时,没找到对应的Activity系统就会抛出 android.content.ActivityNotFoundException 异常,所以需要判断是否有Activity能够匹配我们的隐式Intent。

    • 采用 PackageManager 的 resloveActivity 方法或Intent 的 resloveActivity 方法
      public abstract List<ResolveInfo> queryIntentActivityies(Intent intent,int flags);
      public abstract ResolveInfo resloveActivity(Intent intent,int flags);

      以上的第二个参数使用 MATCH_DEFAULT_ONLY ,这个标志位的含义是仅仅匹配那些在
      intent-filter中声明了 android.intent.category.DEFAULT 这个category的Activity。因为如果把不含这个category的Activity匹配出来了,由于不含DEFAULT这个category的Activity是无法接受隐式Intent的从而导致startActivity失败。

    • 下面的action和category用来表明这是一个入口Activity,并且会出现在系统的应用列表中,二者缺一不可。
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />

22.Android禁止屏幕旋转 & 旋转屏幕时保持Activity内容?

对android:configChanges属性,一般认为有以下几点:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

但是,自从Android 3.2(API 13),
在设置Activity的android:configChanges="orientation|keyboardHidden"
后,还是一样会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。
所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,
如果你想阻止程序在运行时重新加载Activity,除了设置"orientation",你还必须设置"ScreenSize"。
解决方法:
AndroidManifest.xml中设置android:configChanges="orientation|screenSize“

(2)在manifest.xml文件设置android:configChanges="orientation|keyboardHidden|screenSize"切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。
需注意的是,在4.0以后的版本需要加上ScreenSize, 不然不会执行onConfigurationChanged方法.
执行顺序:
onCreate
onStart
onResume
onConfigurationChanged();

1.在应用中固定屏幕方向。
在AndroidManifest.xml的activity中加入:android:screenOrientation=”landscape”属性即可(landscape是横向,portrait是纵向)。


2.随屏幕旋转时,不重新调用onCreate。
当将手机屏幕旋转时,系统会被强制重置启动onCreate方法。
1)修改AndroidManifest.xml。
在activity属性中加入:android:configChanges=”orientation|keyboardHidden| screenSize”
android:configChanges,这个方法主要是负责列出清单,当清单上用户指定的设置改变时,Activity会自己处理这些变化。
orientation,屏幕界面旋转(可能是用户手动旋转的),
【注意:如果你的开发API等级等于或高于13,你还需要设置screenSize,因为screenSize会在屏幕旋转时改变】
keyboardHidden,键盘辅助功能改变;

2)在相对应的Activity中继承重写onConfigurationChanged方法,这个方法将会在我们的应用发生变化时,让我们能随心所谓地进行监听处理。
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) {
// Nothing need to be done here
} else {
// Nothing need to be done here
}
}
如果在两个if中写其他操作,比如setContentView(),那么当旋转屏幕时,就会重新调用新的view,并且需要重新设定view的内容,而当前的view内容没有保存。什么也不写的话,当前view中的内容会随着屏幕转动而转动,不影响操作,这是我想要的结果。


21.旋转屏幕Activity执行的生命周期方法?

很多时候会用到屏幕旋转时需要对一些数据进行保存,例如当横竖屏区切换时要保存先前屏幕的一些数据和状态,那么在旋转屏幕的时候都会调用activity生命周期的哪些方法呢,运行得出了如下结果:
(1)如果销毁当前的Activity,那么就要重写Activity的两个方法
onSaveInstanceState()保存数据 和 onRestoreInstanceState()还原数据
执行顺序:
onCreate();
onStart();
onResume()
onPause
onSaveInstanceState
onStop
onDestroy

onCreate
onStart
onRestoreInstanceState
onResume

在灭屏时调用的方法
onPause
onSaveInstanceState
onStop

屏幕重新唤醒时调用的方法:
onRestart
onStart
onResume

退出程序时调用的方法:
onPause
onStop
onDestroy 

12.两个Activity之间跳转时必然会执行的是哪几个方法?
一般情况下比如说有两个activity,分别叫A,B。
当在A 里面激活B 组件的时候, A会调用onPause()方法,然后B调用onCreate() ,onStart(), onResume()。
如果这个时候B覆盖了A的窗体, A会调用onStop()方法。
如果B是个透明的窗口,或者是对话框的样式, 就不会调用A的onStop()方法。
如果B已经存在于Activity栈中,B就不会调用onCreate()方法。

11.Activity的几种情况的生命周期?

 11.打开APP,进入ActivityA?

ActA --onCreate();
ActA --onStart();
ActA --onResume();

12.ActivityA跳转到ActivityB
ActA: onPause();
ActB: onCreate();
ActB: onStart();
ActB: onResume();
ActA: onStop();

13.ActivityB按back键返回?
ActB: onPause();
ActA: onRestart();
ActA: onStart();
ActA: onResume();
ActB: onStop();
ActB: onDestroy();


14.ActivityA按back键返回
ActA: onPause();
ActA: onStop();
ActA: onDestroy();

推荐阅读