• Android四大组件全然解析(一)---Activity


    本文參考androidandroidframeworksasecorejavaandroidappActivity.java文件里的类凝视。以及android/frameworks/base/docs/html/guide/components/activities.jd文件


    One Activity简单介绍:

    Activity是一个单独的、能够和用户交互的东西。差点儿全部的activities都要与用户交互,所以activity承担着创建window的重任,你能够通过setContentView的方法往window里填充view。通过一个主题属性android.R.attr#windowIsFloating来设置activities是全屏显示full-screen还是悬浮窗isfloat(比方dialog,或者是一个悬浮的view),当然这里所说的全屏显示不包括状态栏。

    <!-- 全屏显示  -->
     <item name="windowIsFloating">false</item>
    

    在相应的主题中有dialog主题样式的

    <!-- 悬浮窗显示 -->
     <style name="Theme.Material.BaseDialog">
    ''''''
     <item name="windowIsFloating">true</item>
     ''''''
     </style>

    在继承Activity时须要实现两个方法

    • onCreate: 在该方法中初始化activity。

      更重要的是,通常须要在该方法中调用setContentView方法来载入layout文件。而且用findViewById来检索须要和你进行交互的layout文件里的控件。

    • onPause :在用户离开该activity时调用该方法处理。用户做出的不论什么改动都应该在该方法中提交commit(通常保存提交的数据使用android.content.ContentProvider)。

    为了能够使用 android.content.Context#startActivity Context.startActivity()打开一个activity。我们须要将activity在相应包下的androidmanifest文件里使用activity节点进行声明

    <application android:icon="@drawable/icon"
    ''''''
    <!-- 声明activity -->
    <activity android:name=".MainMenuActivity"
    ''''''
    </activity>
    ''''''
    </application>

    Two 与activity相关的主题有

    • #Fragments
    • #ActivityLifecycle
    • #ConfigurationChanges
    • #StartingActivities
    • #SavingPersistentState
    • #Permissions
    • #ProcessLifecycle

    一,Fragments:

    fragment简单介绍

    Fragment開始于Android3.0。是应用程序组件碎片的意思。能够被放在activity内部。通过FragmentManager来管理与fragment的交互。

    fragmentmanager的对象能够通过两种方式获取到

    • Activity#getFragmentManager() Activity.getFragmentManager()
    • Fragment#getFragmentManager() Fragment.getFragmentManager()

    fragment类能够用来获取各种各样的结果,在它内部,它代表一个普通的操作或者是交互接口。

    fragment与包括他的activity紧密相连,fragment依赖于activity存在。尽管fragment有它自己的生命周期,但fragment的生命周期与activity的生命周期息息相关。fragment生命周期图例如以下:
    这里写图片描写叙述

    fragment依附于activity存在,当activity被stopped之后,activity中的fragment不能够started。

    当activity销毁后。位于activity中的fragment也随之销毁。

    全部Fragment的子类必须包括一个无參的构造方法。当有须要时尤其是在状态恢复期。framework层会常常又一次初始化fragment,framework会去找无參构造器去初始化fragment。假设fragment中无參构造器不可用的话就会在状态恢复时抛出 runtime exception 。

    fragment相关主题有:

    • #OlderPlatforms旧版本号
    • #Lifecycle生命周期
    • #Layout布局
    • #BackStack

    OlderPlatforms:

    fragment 是在Android3.0才加入进来的,所以在Android3.0之前假设想要使用fragment能够使用android.support.v4.app.FragmentActivity具体能够參考
    fragments for all

    Lifecycle

    • #onAttach 当fragment与activity绑定时调用
    • #onCreate 当fragment初始化创建时调用
    • #onCreateView创建并返回与fragment相关的view视图
    • #onActivityCreated 通知fragment它所绑定的activity已经oncreate
    • #onViewStateRestored 通知fragment所保存的全部view的状态已经被恢复
    • #onStart fragment对用户可见
    • #onResume fragment对用户既可见又获取焦点
    • #onPause fragment不再能和用户交互(activity被paused或者fragment之间的操作)
    • #onStop fragment对用户不可见(activity被stopped或者fragment之间的操作)
    • #onDestroyView 清除与fragment相关的全部views相关资源
    • #onDestroy 清除fragment状态
    • #onDetach 当fragment与activity解绑时调用
      fragment生命周期

    layout

    fragment能够作为应用程序布局的一部分,借助fragment,activity能够更好的 模块化。为更大的屏幕创建更复杂的用户交互,帮助应用实现小屏和大屏之间的尺寸的切换。比如,在一个activity上能够编写一个有item列表的fragment。然后再组合一个fragment去显示每一个item的具体信息。

     <fragment
                class="com.example.android.wifidirect.DeviceListFragment"
                android:id="@+id/frag_list"
                android:layout_width="match_parent"
                android:layout_height="@dimen/phone_list_height">
                <!-- Preview: layout=@layout/row_devices -->
            </fragment>

    fragment相关能够參考:
    fragment嵌套
    fragment与activity

    二,ActivityLifeCycle

    在系统中,activity被一个称为activity栈activity stack的东西在管理。当新创建一个activity时,就被被放在栈顶,而且成为正在执行的activity—-先前的activity会被保留在activity的下方。当位于栈的activity退出后,位于该activity下方的activity就会执行到前台,activity栈遵循栈的原则:后进先出。

    一个activity基本上有四种状态

    1. active/running:当activity位于屏幕前台时(此时位于栈顶)我们称他为处于active或者是running的状态。
    2. paused:当activity失去焦点但仍对用户可见时。称之为paused的状态。

      在成这样的原因可能是有一个透明的activity或者是size较小的activity覆盖在该activity之上。处于paused的activity也处于存活状态(该activity保留了全部的状态和成员信息。并仍旧和wiindowmanager绑定),可是当内存不足时,处于paused状态的activity非常有可能被系统杀死

    3. stopped:当一个activity全然的被还有一个activity覆盖时就处于stopped状态。此时activity对用户不可见且没有焦点,可是仍旧保存有全部状态和成员信息。

      当其它地方须要内存时,系统会将处于stopped状态的activity杀死。

    4. 假设用户处于paused或者stopped状态。系统非常可能通过finish该activity或者是kill进程的方式将该activity从内存中移除。当再次载入给用户时,必须又一次開始而且恢复他先前的状态。

    接下来看一张activity生命周期的流程图:(来自源代码)
    这里写图片描写叙述

    写的非常是清楚啊
    在这个流程图中能够看到有三个关键的循环

    1. entire lifetime:activity的完整周期:从activity第一次调用onCreate開始到最后调用ondestroy为止。

      activity会在oncreate方法中创建全部的全局global状态,而且在onDestroy()方法中释放全部剩余的资源。

      比如。假设activity在后台开启一个网络下载进程,activity会在onCreate方法中开启该线程,并在onDestroy方法中停止该线程。

    2. visible lifetime可见周期:发生在activity调用onStart和onStop之间。在可见期间activity对用户可见,但有可能不在前台无法与用户交互。

      在这两个方法之间,你能够保存activity须要展现给用户的资源。

      比如。能够在onStart中注冊一个BroadcastReceiver来监控影响UI的改变,并在onstop方法中注销该广播。onStart和onstop方法能够被多次调用。来让activity可见或者隐藏 。

    3. foreground lifetime前台时期:activity调用onResume開始。到调用onPause为止。

      在这期间activity位于全部activity之上而且能够和用户进行交互。

      一个activity能够频繁的在resumed状态和paused状态之间切换—比如当设备休眠时activity处于paused,所以activity的onResume和onPause方法中应该是一些轻量级的代码

    public class MyActivity extends Activity{
    
     protected void onStart() {
    
     //注冊广播
     mContext.registerReceiver(mReceiver, mIntentFilter);
     }
    
     protect void onStop(){
    
     //注销广播
      mContext.unregisterReceiver(mReceiver);
     }
     }

    activity的完整生命时期包括下面全部activity的方法。你能够实现这些方法来完毕工作。

    全部activity会实现onCreate方法来完毕初始化操作;好多activity也会实现onPause方法来提交数据的改变并准备停止用户交互。在覆写方法时应该调用父类的方法

    public class Activity extends ApplicationContext{
    
       protected void onCreate(Bundle savedInstanceState)
       protected void onStart();
       protected void onRestart();
       protected void onResume();
       protected void onPause();
       protected void onDestroy();
    }

    activity生命周期方法之间的切换例如以下表所看到的

    Method Description killable Next
    onCreate() 当activity第一次创建时调用。

    在onCreate方法中须要做一些静态初始化的操作:创建views,绑定列表数据等等。假设onCreate传入的bundle參数不为null的话,能够从bundle中获取到activity先前的状态

    No onStart
    onRestart() 当activity已经被stopped,但又又一次载入时调用 No onStart
    onStart() 当activity对用户可见visible时调用 No 假设activity成为前台activity,则接下来会调用onResume方法。

    假设activity被隐藏hidden则接下来会调用onStop方法

    onResume() 当activity能够開始于用户交互即activity获取到焦点时会调用该方法。随着用户的输入,activity会处在栈顶。 No onPause
    onPause() 当系统想要让一个先前的activity获取焦点时调用。

    该方法通常常使用来提交一些未保存的数据,停止动画以及其它一些消耗cpu内存的事情。

    方法的实现体必须高速。由于下一个activity仅仅有在onPause方法执行返回之后才会resumed所以会一直处于堵塞状态

    当activity又一次返回到前台to the front时会调用onResume,当activity对用户不可见invisible时会调用onStop
    onStop() 当activity不再对用户可见时会调用该方法,由于其它activity已经resumed而且覆盖了该activity。当一个新的activity開始时会被放在该activity的前面时会调用onStop。或者该activity被销毁会调用onStop yes 假设activity又一次载入出来与用户交互,则会调用onRestart方法。假设activity被销毁则会调用onDestroy方法
    onDestroy() 当activity被销毁时会调用该方法。Activity被销毁有两种情况,一种是用户调用了activity的finish方法结束了activity,一种是系统为节省空间销毁了activity。能够调用isFinishing方法来区分是哪一种情况 yes Nothing

    表格中killable这一列值得注意一下:

    1. 对于被标记成能够被killable的方法来说,当activity执行完这些方法返回时,持有该activity的进程《在不论什么时候》都可能被系统杀死,不再执行该activity中的不论什么一行代码。也由于如此。你应该在onPause方法中去保存数据(比如,用户的编辑)。

      另外。当将activity执行到后台状态时能够调用onSaveInstanceState(Bundle)方法来将activity的动态数据保存到一个bundle对象中,假设activity须要又一次create的话,能够在onCreate中获取到Bundle数据。
      注:在Android3.0以前保存数据应该在onPause中进行,由于onSaveInstanceState不是activity的生命周期的一部分。在进程生命周期相关中不会被调用。从Android3.0開始发生了改变。应用仅仅有在onStop方法返回后才干被killable。这也就导致了在activity被杀死之前执行完onPause之后可能会调用onSaveInsatanceState(Bundle),而且能够让应用一直等待去保存数据直到执行了onStop方法。

    2. 对于那些没有标记为能够被killable的方法表示,在方法開始调用直至方法返回这一段时间系统都不会去杀死activity的进程。因而一个activity是在调用onPause之后调用onResume之前才处于能够被kill的状态。

    三, Configuration

    假设一个设备的配置Resources.Configuration发生改变,显示给用户的交互界面也应该随之更新来配合格局发生的改变(横竖屏)。

    由于activity是与用户交互的主要机制,它包括用来处理设备配置改变的一些函数。
    除非你有其它的指定。否则当设备的configuration发生改变时(比如,屏幕方向,语言。输入设备等等)会引起你当前activity的destroyed。经历activity的正常的生命周期过程onPause–>onStop–>onDestroy。

    假设activity已经载入到前台in the foreground或者对用户可见visible了,该activity实例一旦调用了onDestroy方法。就会去创建一个新的activity的实例,该activity实例能够获取到通过onSaveInstanceState所保存的先前的状态savedInstanceState.
    应用中不论什么资源。包括layout文件都会基于configuration值的改变而发生变化。

    因而。处理configuration改变最安全的方法就是去检索全部资源文件,包括layouts,drawable。strings。由于activity必须知道怎样如保存他们的状态以及怎样依据保存的状态又一次创建他们,所以,快捷的方法就是提供一个新的配置来restart一个activity。
    在某些情况下。你可能想在configuration发生改变是不去重新启动activity,这须要借助配置文件里的属性android.R.attr#configChanges android:configChanges来完毕。

    <!-- 当屏幕方向或者键盘方向发生改变时不去调用activity的oncreate-->
    android:configChanges="keyboardHidden|orientation"

    当对该属性进行了配置后,就代表你能够进行监控。当所规定的配置发生变化时就会去调用onConfigurationChanged方法。而不是重新启动activity。

    四, StartingActivities

    开启一个activity而且获取到结果
    android.app.Activity#startActivity方法用来打开一个activity,打开之后activity会被放在栈顶。

    在调用startActivity方法时须要用intent指明要打开的activity。
    有时你可能想在activity结束时获取到一个返回结果。

    比方,你可能开启一个activity来让用户从联系人列表中选择一个联系人,当activity结束时把选择的结果返回回来,那就能够用android.app.Activity#startActivityForResult(Intent, int)打开activity,结果能够在activity的onActivityResult方法中获取
    在一个存在的activity中调用setResult(int)方法把结果返回给它的父activity。在返回结果时必须提供一个结果码,能够是RESULT_CANCELED、RESULT_OK或者是其它自己定义的代号。另外,也能够选择返回一个带有额外数据的intent返回回去。

    借助结果码。全部的信息都能够在parent的onActivityResult获取到。假设子activity发生了崩溃,父activity接受到的结果码就是RESULT_CANCELED

     public class MyActivity extends Activity {
          ...
    
          static final int PICK_CONTACT_REQUEST = 0;
    
          public boolean onKeyDown(int keyCode, KeyEvent event) {
              if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
                  // When the user center presses, let them pick a contact.
                  startActivityForResult(
                      new Intent(Intent.ACTION_PICK,
                      new Uri("content://contacts")),
                      PICK_CONTACT_REQUEST);
                 return true;
              }
              return false;
          }
    
          protected void onActivityResult(int requestCode, int resultCode,
                  Intent data) {
              if (requestCode == PICK_CONTACT_REQUEST) {
                  if (resultCode == RESULT_OK) {
                      // A contact was picked.  Here we will just display it
                      // to the user.
                      startActivity(new Intent(Intent.ACTION_VIEW, data));
                  }
              }
          }
      }

    五,SavingPersistentState

    保存永久状态:
    activity一般会保存两种持久的状态

    • 共享文档 :数据(借助content provider保存在数据库中的数据)
    • 内部状态:比方用户的偏好设置
      对于第一类共享数据,activity应该使用“edit in place”编辑到位用户模型。

      也就是说,用户编辑之后能够马上保存,不须要其它的步骤。在使用这个模型时必须遵循下面两条规则

      1. 创建一个新文档时。马上创建他所依赖的数据库条目或者是文件。

        比如,假设用户选择写一个新的email。与该email相关的新的条目也必须要创建起来。来保证假设用户去了其它不论什么的activity这个email不会在草稿中消失。

      2. 当activity调用onPause方法时,他应该将用户针对数据所作出的改动进行commit提交。这个操作能够保证用户的改动能够被其它将要执行的activity所知道。

        你也有可能想要在activity生命周期的关键时刻去主动提交改动的数据:比如,在新打开一个activity之前,在activity被finish之前。当用户切换输入字段等等
        这个模型的设计是用来防止当用户在activity之间进行切换时数据的丢失,而且同意system在activity被paused之后的不论什么时间能够安全的把activity杀死(由于其它地方须要系统资源,该activity被杀死)。这也就是说着用户按下“BACK”键并不意味”cancel”—它意味着保存他当前的内容并离开。

        在一个activity中取消编辑必须通过其它的机制来提供。比如。一个明白的“revert(还原)”或者是“undo(撤销)”选项。

    Activity也提供了一个API来管理与activity相关的内部状态。比如,能够用来记录用户的偏好设置,并对用户的日历接卖弄进行一个初始化。或者是在使用浏览器时为用户显示一个默认的主页。


    Activity的持久的状态通过getPreferences方法管理。同意检索或者是改动与activity相关的一组name/value键值对。为了让preferences能够应用程序多个组件(activities,receivers,services,providers)之间共享,你能够使用已有的方法
    Context#getSharedPreferences Context.getSharedPreferences()来检索相应某个特殊name的preferences对象。(跨进程时不能通过preferences分享数据—仅仅能通过contentProvider)

    下面是日历中的一个代码片段

    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();
          }
      }

    六,Permissions

    在Androidmanifest中注冊activity时能够给activity写明一个权限,这样其它应用在打开该activity时就须要拥有这个权限。


    当打开一个activity时,你能够设置在intent上设置标志位Intent.FLAG_GRANT_READ_URI_PERMISSION或者 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。
    这将授予activity对intent中特定uri的訪问权限。訪问权限会被保留到activity结束(他将保持到主机进程被杀死而且其它临时性损坏)。Android2.0来说。假设activity已经被created而且一个新的intent也被发送给onNewIntent(intent),不论什么新授予的uri权限都会被加入到所现有的uri中。

    七,ProcessLifecycle

    Android系统试图将应用进程尽可能的保留更长时间,可是当内存较少时终于须要杀死旧的进程。正如在activity的生命周期ActivityLifecycle中所描写叙述的那样,关于哪一个进程应该被移除由与用户交互状态决定。

    通常来说,依据执行在进程中的activity的状态能够看出进程有四种状态,依照重要性的顺序排列。系统在又一次排序杀死重要进程之前会优先杀死最不重要的进程。

    1. foreground activity前台activity:(activity位于屏幕上方。与用户进行交互)是最重要的。假设占用的内存超过了用户的可用内存,该进程会在最后被杀死。通常。从这方面来说设备已经到达一个内存分页状态,所以为了保证用户输入的流畅必须要杀死一些进程
    2. visible activityactivity(对用户可见。可是没在前台即不能和用户交互,比如activity上方弹出一个dialog)也是非常重要的,仅仅有在系统要求保持前台activity执行时才会杀死该进程
    3. background activity(后台进程。activity对用户不可见。而且已经被paused的activity)不再是危险的activity,所以系统能够非常安全的杀死它的进程回收内存执行foreground 进程和visible 进程。假设它的进程须要被杀死,当用户再次切换回该activity时(再次显示到屏幕上),会调用oncreate方法而且从savedInstanceState中取出先前onSaveInstanceState保存的数据,以保证它在又一次打开时获取到用户最后离开时的状态
    4. empty process(空进程)是一个没有不论什么activity或者其它应用组件的进程。当内存不足时会非常快被杀死。

      由于这个原因,不论什么你在activity之外的后台操作都必须在service和broadcastreceiver的上下文中执行,以保证系统能够保持你的进程执行。
      有时候一个activity可能须要去做一个与activity生命周期无关的长时间执行的一个操作。

      举个样例,相机应用同意你从网络下载图片。下载须要花非常长事件。当进行下载操作时应用同意用户离开相机应用。

      为了实现这个目的,你的activity就应该开一个service来处理下载操作。这个操作能够让系统优先考虑你的进程(考虑到它比其它不可见的应用程序更重要),与原始的activity处于何种状态无关


    Three 任务栈:Tasks and Back Stack

    包括两部分内容
    1. ActivityState:activity状态的保存
    2. ManagingTasks:管理任务栈。

    对于任务栈的管理又分为四个方面。

    这四个方
    面各自是

    • #TaskLaunchModes:任务启动模式
    • #Affinities:
    • #Clearing:清除后台栈
    • #Starting:開始任务

    任务栈相关说明

    一个应用程序通常包括多个activities。每一个activity都应该环绕一个特殊的action来设计,用户能够执行这个动作也能够打开其它activities。比如,email应用程序可能有一个应用程序用来显示新的message列表。当用户选则列表中的当中一条时。程序会打开还有一个activity用来查看message。

    activity也能够打开设备中其它应用程序中的activities。比如,假设你想发送一个email,你能够定义一个“intent”来执行发送的动作,并让携带着一些数据,这些数据有可能是一个email地址和一条message。


    在其它应用程序中,处理这样的intent的activity会打开。

    在这样的情况下,intent是发送email,所以负责发送email的应用程序中相关的activity就会打开(假设有多个activity都支持这样的intent,系统就会让用户进行选择)。

    当email被发送之后。你的activities就会又一次获取焦点。就像是发送email的activity属于你的应用程序一样。尽管activity来自于不同的应用程序,Android通过保持activities位于同一个任务栈来实现无缝切换的用户体验
    任务栈是一个装载activities的容器。依照activities的打开顺序存入栈中。

    一, activity中加入fragment

    任务栈中也能够加入fragment。比方一个activity有两个页面,一个是显示list列表的fragmentA,一个是显示item的具体信息的fragmentB。假设此时想要显示还有一个item的具体信息fragmentC,即第二个页面的fragment由B切换到了C。假设你想实现的效果为当用户按下BACK键时fragmentC消失并又一次返回fragmentB,那么你能够做例如以下操作:在fragment进行切换时,调用commit提交之前调用addToBackStack方法。

    二,开启任务栈

    主屏幕是大部分应用的入口。所以非常多任务栈都是从主屏幕开启的。用户点击launcher界面上的快捷方式就能够开启一个任务栈。假设该任务栈不存在就会创建而且把程序中的“main”activity作为第一个activity。假设该任务栈存在则会将后台的任务栈载入到前台来并回复任务栈被放置在后台时所保存的状态。
    假设在当前activityA开启还有一个activityB。那么此时activityB就会被压栈并处于栈顶,activityA就会被stopped(此时系统会保存activityA的状态)而且处于activityB的下方。

    当用户点击BACK键返回时就会将activityB弹出栈并将activityB销毁,然后又一次载入activityA至resume而且恢复activityA被stopped之前的状态。栈中的activity的顺序由压入栈的顺序决定,不会被又一次排序。

    当一个activity被载入时就会位于栈顶,当一个activity被销毁时就会被弹出。任务栈遵循“后进先出”的规则。


    图一所看到的
    这里写图片描写叙述
    图一说明了当activity被载入时会位于栈顶,当按下BACK键时activity会被弹出栈而且被销毁。

    假设一直按BACK键,那么栈中全部的activity都会被弹出并销毁直至显示出来主屏幕界面或者是启动该任务栈之前的状态,当任务栈中没有activity存在时任务栈就会消失。

    在开启任务栈中须要规定一个入口,通过activity节点下的一些属性来规定:

     <activity android:name="AndouKun" android:label="@string/app_name" 
    
                      android:launchMode="singleTask">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>

    三。多任务栈

    图二
    这里写图片描写叙述
    图二显示任务栈B位于前台与用户进行交互。任务栈A位于后台等待被resume

    图三
    这里写图片描写叙述
    图三中能够看出一个栈中有多个activity的实例。

    一个任务栈是一个总体,当用户开启一个新的任务或者是回到主屏幕须要当前任务栈回到后台时,任务栈总体处于后台状态,任务栈中的activity处于stopped的状态。可是任务栈仍旧是完善的仅仅是被还有一个任务栈替代失去焦点而已,系统会帮其保存数据。

    如图二所看到的,假设任务栈A此时处于前台且栈中有三个activity a –> b—>c a处于栈顶,假设用户按下了home键而且开启了一个新的任务栈B,当再次按下home键时任务栈B进入后台。然后用户再次开启任务栈A。此时会去载入离开任务栈A之前的状态。


    多任务栈能够被系统保存。可是假设同一时候执行多个任务栈的话在内存不足时后台任务栈非常easy被系统杀死。此时activity的状态就会丢失。

    所以要及时保存activity的状态。比方在activity被stopped时调用onSaveInstanceState方法来保存状态。

    四,managetask 管理任务栈

    由于栈中的activity从来不会又一次排序,当用户载入一个已经在栈中存在的activity时会选择去又一次初始化创建而不是说使用栈中存在的,这就造成一个问题。当用户点击back键返回时能够发现一个界面可能会呈现多次,造成了一种非常不好的用户体验。

    为了解决问题,这就须要对activity和任务栈进行一个管理。
    先来总结一下默认状态下activity和task

    • 当activityA中打开activityB时,activityA被stopped且状态被保存(比方滑动位置,输入文本等数据信息),activityB被resumed。假设此时按下BACK键。activityB就会被销毁。activityA就会又一次resume获取焦点而且恢复所保存的数据
    • 当用户按下HOME键回到主屏幕时。activity就会被stopped而且此时任务栈会进入后台模式,可是系统仍旧保存任务栈中每一个activity的状态。

      假设用户再次打开任务栈,任务栈又会又一次进入前台而且载入处于栈顶的activity

    • 假设用户按下了BACK键。当前的activity会被销毁。就会去载入在栈中位于该activity下方的activity。
    • 即便是来自于其它栈,activity也能够被多次初始化。

    ActivityState activity的状态

    经过以上能够看出,当activity被stopped的时候系统默认情况下会保存activity的状态,可是当activity被stopped的时候非常easy被系统杀死。所以应该主动去保存activity的状态。

    ManagingTasks 管理任务栈

    默认情况下Android任务栈的管理遵循后进先出的原则。无需在意activity在任务栈中怎么存在以及activity是怎样和任务栈联系的。我们也能够改变这样的默认的方式。比方

    • 当启动activity时放在一个新的任务栈中而不是在当前任务栈中
    • 重用任务栈中的某个activity实例而不是又一次创建
    • 当用户离开任务栈时清除全部activity仅仅保留根activity(典型的使用方法:按两次back键结束应用)

    改动任务栈默认行为

    改变默认的任务栈中activity的启动方式有两种方法

    • 在Androidmanifest文件的activity节点下定义一些任务栈相关属性
    • 在代码中给在start一个activity时给intent加入flag标志位
      任务栈对activity的默认处理一般不用改动。

    第一种方式:activity节点下的属性来改动
    主要有下面几种属性

    • taskAffinity
    • launchMode
    • allowTaskReparenting
    • clearTaskOnLaunch
    • alwaysRetainTaskState
    • finishOnTaskLaunch

    另外一种方式:为intent设置标志位
    主要有下面几种标志位

    • android.content.Intent#FLAG_ACTIVITY_NEW_TASK
    • android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP
    • android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP

    TaskLaunchModes:任务启动模式

    任务启动模式定义了怎样去初始化一个与当前任务栈相关的activity。能够通过两种方式来定义不同的 启动模式

    • 在Androidmanifest文件里定义
    • 在intent文件里定义

    假设activityA中启动activityB,activityB能够在Androidmanifest文件里定义怎样去启动,activityA也能够在intent中定义activityB怎样启动。

    intent中的參数高于配置文件里定义的參数。

    有些參数对Androidmanifest能够用,有些intent能够用,有些两者都可用。

    ManifestForTasks:在Androidmanifest中规定启动模式

    launchMode属性:定义了activity怎样进入任务栈,总共同拥有四种模式

    1. standard:activity的默认启动模式。

      activity能够被多次初始化。每一个实例能够属于不同的任务栈,任务栈中能够有多个该activity的实例

    2. singleTop:假设activity实例存在于当前栈的顶端则重用该实例。会调用onNewIntent()而不是去create该activity的实例。

      activity能够被多次初始化,每一个实例能够属于不同的栈。一个栈能够有过个实例(可是仅仅是activity不在栈顶时才会去又一次创建activity的实例)。
      比如,假设任务栈中有activityA—>B—>C—>D,且A处于栈底D处于栈顶。有一个intent须要启动D,假设D的启动模式是默认的standard的话那么就会在栈中创建D的实例,即此时任务栈中情况为A—>B—>C—>D—>D。假设D的启动模式是singleTop的话则。在启动D时会调用onNewIntent()方法重用任务栈中的实例。任务栈中的 情况是A—>B—>C—>D。

      但假设是启动B,就算设置了B的启动模式为singleTop,仍旧会又一次创建一个B的实例。即任务栈情况是A–>B–>C–>D–>B。
      假设是直接创建的activity实例,则在按下BACK按键时会返回先前activity的状态,但假设是通过intent去重用一个已经存在的activity,则在按下返回键时不会返回重用之前的状态,仅仅能返回重用之后的状态。

    3. singleTask:系统在一个新的任务栈中初始化一个activity并把该activity当做根activity。可是,假设activity的实例已经存在于还有一个栈中(不同于当前栈),系统就会调用 onNewIntent() 重用该实例。而不是去又一次创建。尽管activity是在一个新的任务栈中开启的可是按下back键仍旧会返回先前的activity(此时按下back键是把眼下处于前台的任务栈即承载该activity的任务栈销毁。显示开启该任务栈之前的任务栈)。
    4. singleInstance:相似singleTask。可是在存储该activity的任务栈中不会去初始化其它activity的实例。所以是单例任务栈。

    举个样例,浏览器的web界面就应该是singleTask启动模式,别的应用能够声明一个intent来打开浏览器页面,此时浏览器页面的activity被放在还有一个任务栈中(假设activity存在则将所属任务栈置为前台。否则为activity开启新的任务栈)而不是当前任务栈。


    不管打开activity时是放置在当前任务栈还是又一次去开启一个任务栈。按下BACK键总是返回先前的activity。可是假设在开启一个activity时使用singleTask模式,假设后台任务栈中存在该activity的实例,则整个后台任务栈就会被带到前台来。


    例如以下图四所看到的:
    这里写图片描写叙述

    有两个任务栈,前台任务栈A(activity1->activity2),后台任务栈B(activityX –>activityY),y的启动模式为singleTask,当在activity2中启动y时会将任务栈B总体移到任务栈A上,此时按下BACK键会显示activityX。

    IntentFlagsForTasks 利用intent的flag来定义activity的启动模式

    当调用startActivity方法开启一个activity时能够通过设置intent的flag来改动activity的默认启动模式。

    1. android.content.Intent#FLAG_ACTIVITY_NEW_TASK:在一个新的任务栈中开启activity,假设包括你正在打开的activity的任务栈已经存在,则将任务栈移动至前台并恢复最后的状态,此时activity收到新的intent调用onNewIntent();(等同于singleTask
    2. android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP:假设activity的实例处于当前任务栈的栈顶,则重用该实例,调用activity的onNewIntent()(等同于singleTop)
    3. android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP:假设activity已经执行在当前栈,那么就会将当前栈中全部位于该activity之上的activity的实例销毁,而且重用该实例,调用activity的onNewIntent(activity节点下没有与该功能相应的启动模式)。该标志位常常和FLAG_ACTIVITY_NEW_TASK联合使用,

      Affinities 处理相关的任务栈

      affinity表明activity想要属于的任务栈,也就是说在开启该activity时会放在哪个任务栈中。默认情况下一个应用中全部的activity有一个affinity。所以默认情况下全部activity启动会放在同一个任务栈中,可是也能够通过改动taskAffinity属性(Androidmanifest的activity节点下)来改动activity想要放置的任务栈。已达到同一应用不同activity放置在不同的任务栈中或者是不同应用的activity共享同一任务栈中的目的。

    taskAffinity属性须要一个字符串值。为一个应用的包名(应用所特有的,系统能够通过该包名来识别不同的应用程序)。

    affinity在两种情况下会发生作用:

    1. 当启动一个activity时的intent中包括FLAG_ACTIVITY_NEW_TASK时。

      默认情况下activity会被载入到调用startActivity的activity所在的任务栈中也就是当前栈,被调用这放入到同样的栈中。可是假设startActivity时intent中包括标志FLAG_ACTIVITY_NEW_TASK时系统就会去寻找一个不同的任务栈来载入该activity。通常情况下都会须要一个新的任务栈,可是事实上并不全是。

      假设存在一个后台栈与要启动的activity所指定的affinity一致则在该后台栈中启动activity。否则就要去创建新的任务栈。一些应用常常会在外部的任务栈中去开启activity比方通知栏notificationmanager。
      2.当activity的allowTaskReparenting被设置为true时,在这样的情况下当任务栈执行到前台时。同意activity从启动它的任务栈移动到他想要属于的任务栈。
      比如。一个天气预报选择城市界面A作为一个应用程序的一部分,与该应用程序的其它activity有同样的affinity。当在你的任务栈中启动该activityA时,会在你的任务栈中加入该activityA的实例,可是当天气预报的 应用程序所属的任务栈又一次回到前台时activityA就会被又一次分配到天气预报应用的任务栈中去。

    Clearing:清除任务栈

    假设用户离开任务栈非常长时间,系统会将任务栈中除了根activity之外的全部activity都清除掉,当系统再次开启该任务栈时仅仅能恢复根activity的状态。系统之所以这么做是由于认为在过了一段时间后用户非常可能想要放弃以前未保存的改动。
    有一些属性能够影响系统对任务栈的处理。

    • alwaysRetainTaskState :假设任务栈中根activity的该属性为true。就算过了非常长时间,任务栈中的 全部activity也不会被销毁。
    • clearTaskOnLaunch:假设任务栈中根activity的该属性为true,一旦任务栈处于后台就会被清除。

      也就是说与alwaysRetainTaskState 属性值恰好相反,一旦离开任务栈再次返回回来就是初始状态。

    • finishOnTaskLaunch:与clearTaskOnLaunch属性相似,但该属性是针对activity而言的而不是整个任务栈。一旦用户离开了该任务栈再次返回时该activity已经被销毁。

    假设认为还不错,欢迎点赞,关注我的微信公众号
    fanfan程序媛

  • 相关阅读:
    批量插入以及数据存在重复就进行更新操作
    插件-过滤器
    NamedParameterJdbcTemplate
    菜鸟python---文件 + 操作
    菜鸟python---文件操作
    菜鸟python---以后会遇到的坑
    菜鸟python---二次编码
    菜鸟python---基础数据类型补充
    菜鸟python---深浅拷贝
    菜鸟python---集合
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7294989.html
Copyright © 2020-2023  润新知