注册

Android 面试题及答案

‍‍8、跟activity和Task 有关的 Intent启动方式有哪些?其含义?‍‍


‍‍核心的Intent Flag有:‍‍ 


‍‍FLAG_ACTIVITY_NEW_TASK‍‍ 


‍‍FLAG_ACTIVITY_CLEAR_TOP ‍‍


‍‍FLAG_ACTIVITY_RESET_TASK_IF_NEEDED ‍‍


‍‍FLAG_ACTIVITY_SINGLE_TOP‍‍


‍‍FLAG_ACTIVITY_NEW_TASK‍‍ 


  ‍‍如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的 Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。‍‍ 


 ‍‍ 这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。 


 使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。 


  这个标志不能用于调用方对已经启动的Activity请求结果。


FLAG_ACTIVITY_CLEAR_TOP 


  如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。 


  例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。 


  上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为 “multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。 


  这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。 


FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 


    如果设置这个标志,这个activity不管是从一个新的栈启动还是从已有栈推到栈顶,它都将以the front door of the task的方式启动。这就讲导致任何与应用相关的栈都讲重置到正常状态(不管是正在讲activity移入还是移除),如果需要,或者直接重置该栈为初始状态。


FLAG_ACTIVITY_SINGLE_TOP 


  如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的


FLAG_ACTIVITY_BROUGHT_TO_FRONT 


  这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。 


FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 


  如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。 


  这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个 Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。 


FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 


  如果设置,新的Activity不会在最近启动的Activity的列表中保存。 


FLAG_ACTIVITY_FORWARD_RESULT 


  如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的 Activity。 


FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 


  这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。 


FLAG_ACTIVITY_MULTIPLE_TASK 


  不要使用这个标志,除非你自己实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,可以禁用把已存的Task送入前台的行为。当设置时,新的Task总是会启动来处理Intent,而不管这是是否已经有一个Task可以处理相同的事情。 


  由于默认的系统不包含图形Task管理功能,因此,你不应该使用这个标志,除非你提供给用户一种方式可以返回到已经启动的Task。 


  如果FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。 


FLAG_ACTIVITY_NO_ANIMATION 


  如果在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用 Acitivity迁移动画。这并不意味着动画将永不运行——如果另一个Activity在启动显示之前,没有指定这个标志,那么,动画将被应用。这个标志可以很好的用于执行一连串的操作,而动画被看作是更高一级的事件的驱动。 


FLAG_ACTIVITY_NO_HISTORY 


  如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。 


FLAG_ACTIVITY_NO_USER_ACTION 


  如果设置,作为新启动的Activity进入前台时,这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。 


  典型的,一个Activity可以依赖这个回调指明显式的用户动作引起的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。 


  如果一个Activity通过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。 


FLAG_ACTIVITY_PREVIOUS_IS_TOP 


  If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately. 


FLAG_ACTIVITY_REORDER_TO_FRONT 


  如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。 


  例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略。 


9、 请描述下Activity的生命周期。


答:activity的生命周期方法有:onCreate()、onStart()、onReStart()、onResume()、onPause()、onStop()、onDestory();


可见生命周期:从onStart()直到系统调用onStop()


前台生命周期:从onResume()直到系统调用onPause()


10、 activity在屏幕旋转时的生命周期


答:不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法


11、 如何启用Service,如何停用Service。


服务的开发比较简单,如下:


第一步:继承Service类


public class SMSService extends Service {}

第二步:在AndroidManifest.xml文件中的节点里对服务进行配置:


服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。


如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。


如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。


服务常用生命周期回调方法如下: 


onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。


onDestroy()该方法在服务被终止时调用。


与采用Context.startService()方法启动服务有关的生命周期方法


onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。


与采用Context.bindService()方法启动服务有关的生命周期方法


onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。


onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用


12、 注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。


答:首先写一个类要继承BroadcastReceiver


第一种:在清单文件中声明,添加




   
复制代码

‍‍第二种使用代码进行注册如:‍‍


IntentFilter filter =  new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
IncomingSMSReceiver receiver = new IncomgSMSReceiver();
registerReceiver(receiver.filter);复制代码

两种注册类型的区别是:


1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。


2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。


13、 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。


答:简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理.


Message Queue(消息队列):用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列


Handler:可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息


Looper:是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的


Handler:Handler接受到消息后调用handleMessage进行处理


Message:消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理


在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:


1. Message 


    Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。


2. Handler 


    Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。


3. Message Queue 


    Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。


    每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。


4. Looper 


    Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:


    1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。


    2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:


    1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;


    2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;


    3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。


    由此可见,我们实现的handleMessage方法是优先级最低的!


    3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!


    在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行! 


    1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper; 


2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。


14、 简要解释一下activity、 intent 、intent filter、service、Broadcase、BroadcaseReceiver


答:一个activity呈现了一个用户可以操作的可视化用户界面;一个service不包含可见的用户界面,而是在后台运行,可以与一个activity绑定,通过绑定暴露出来接口并与其进行通信;一个broadcast receiver是一个接收广播消息并做出回应的component,broadcast receiver没有界面;一个intent是一个Intent对象,它保存了消息的内容。对于activity和service来说,它指定了请求的操作名称和待操作数据的URI,Intent对象可以显式的指定一个目标component。如果这样的话,android会找到这个component(基于manifest文件中的声明)并激活它。但如果一个目标不是显式指定的,android必须找到响应intent的最佳component。它是通过将Intent对象和目标的intent filter相比较来完成这一工作的;一个component的intent filter告诉android该component能处理的intent。intent filter也是在manifest文件中声明的。


作者:优弧同学
链接:https://juejin.cn/post/6844903441039360007
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册