温故而知新-Android四大组件之Activity

2017-12-25  本文已影响0人  艾曼大山

Activity简介

Activity是Android一个非常重要的用户接口(四大组件之一),是可见的,主要是用户和应用程序之间进行交互的接口。在每个Activity中都可以放很多控件,所以也可以把Activity看作控件的容器。它负责了我们的界面显示,实际开发中我们通过setContentView(R.layout.id)设置界面显示的视图。在Android的项目结构设计中,Activity就相当于MVC设计模式中的View层。在Android的四大组件设计中,为了方便开发者进行开发使用,Android的开发者对四大组件通过生命周期进行管理,我们只需要继承Activity进行重写这些生命周期来管理和合理使用Activity。

首先要注册一个Activity清单配置
  <application android:allowbackup="false"   
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name"    
    android:roundicon="@mipmap/ic_launcher_round" 
    android:supportsrtl="true" 
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
       <intent-filter>
          <action android:name="android.intent.action.MAIN"/>
        <category  android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    </activity>
个别参数解释

1.allowBackup:是否允许备份应用的数据,当备份数据的时候,它的数据会被备份下来。如果设为false,那么绝对不会备份应用的数据,即使是备份整个系统,默认是true。

2.icon&roundIcon:现在Android新建项目后会自动设置两个图标,icon和roundicon,一个是普通图标,一个是圆形图标。

3.supportsRtl:声明你的application是否愿意支持从右到左(原来RTL就是right-to-left 的缩写...)的布局,如果设置为true,targetSdkVersion设置为17或更高,各种RTL的API将被激活,系统使用您的应用程序可以显示RTL布局。如果targetSdkVersion设置为16或更低的设置为false,RTL的API将被忽略或没有影响您的应用程序将具有相同的行为无论对用户现场的选择相关的布局方向(你的布局会从左至右),此属性的默认值是false。

4.activity :注册的Activity

5.intent-filter:过滤器intent-filter 设置action ="android.intent.action.MAIN"、category ="android.intent.category.LAUNCHER" ,意思就是设置MainActivity为app的入口函数。

下面我们来一起看看Android官方文档上提供的Activity的生命周期图:
Activity生命周期图
方法介绍
注意点
测试Activity的生命周期:
  1. 界面从“死亡”-->“运行"
    构造器-->onCreate()--->onStart()-->onResume()

  2. 界面从“运行”-->“死亡"
    onPause()-->onStop()-->onDestroy()

  3. 界面从“运行”-->“停止"
    onPause()-->onStop()

  4. 界面从“停止” -->“运行"
    onRestart()-->onStart()-->onResume()

  5. 界面从“运行”-->“暂停"
    onPause()

  6. 界面从“暂停” -->“运行"
    onResume()

在实际的开发中,我们根据activity的生命周期来实现我们的逻辑处理。通常在开发中,在onCreate()方法中进行一些变量的初始化工作,包括变量的初始化、控件的初始化、控件设置监听等。当一个activity由于失去焦点时再次重新获取焦点调用onResume方法。在onResume()方法中我们可以处理一些比如界面的更新操作。

onSaveInstanceState何时调用

在activity被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (传入的Bundle参数是由onSaveInstanceState封装好的)中恢复。这个方法在一个activity被杀死前调用,当该activity在将来某个时刻回来时可以恢复其先前状态。例如,如果activity B启用后位于activity A的前端,在某个时刻activity A因为系统回收资源的问题要被杀掉,A通过onSaveInstanceState将有机会保存其用户界面状态,使得将来用户返回到activity A时能通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢复界面的状态。

不要将这个方法和activity生命周期回调如onPause()或onStop()搞混淆了,onPause()在activtiy被放置到背景或者自行销毁时总会被调用,onStop()在activity被销毁时被调用。一个会调用onPause()和onStop(),但不触发onSaveInstanceState的例子是当用户从activity B返回到activity A时:没有必要调用B的onSaveInstanceState(Bundle),此时的B实例永远不会被恢复,因此系统会避免调用它。一个调用onPause()但不调用onSaveInstanceState的例子是当activity B启动并处在activity A的前端:如果在B的整个生命周期里A的用户界面状态都没有被破坏的话,系统是不会调用activity A的onSaveInstanceState(Bundle)的。

Activity优先级
  1. 前台Activity——正在和用户交互的Activity,优先级最高。

  2. 可见但非前台Activity——比如Activity中弹出的对话框,导致Activity可见但是位于后台无法和用户直接交互。

  3. 后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。

当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onResoreInstanceState来存储和恢复数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

Activity任务栈和四种启动模式
每个应用会有一个Activity任务栈,存放已启动的Activity
给Activity指定启动模式的两种方式

1、第一种是通过AndroidMenifest为Activity指定启动模式:launchMode

        <activity
            android:name=".activity.SecondActivity"
            android:configChanges="screenLayout"
            android:launchMode="singleTask"
            />

2、第二种是通过Intent中设置标志位来为Activity指定启动模式:addFlags

     Intent intent = new Intent();
        intent.setClass(this,SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);

这两种方式都可以为Activity指定启动模式,但是二者还是有区别的。首先,优先级上第二种方式比第一种要高,当两种同时存在时,以第二种方式为准;其次,上述两种方式在限定范围上有所不同,比如,第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity指定singleInstance模式。

TaskAffinity任务相关性

这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈名字为应用的包名。我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。

下面可以做个小案例:应用包名为 "pjs.com.kaifa",MainActivity启动模式为standard,SecondActivity和ThirdActivity启动模式为singleTask并且设置了taskAffinity属性值为"pjs.com.task"(MainActivity没有设置taskAffinity)。意思为MainActivity默认在"pjs.com.kaifa"任务栈中,而SecondActivity和ThirdActivity启动后在"pjs.com.task"任务栈中。

  package="pjs.com.kaifa"

  <activity android:name=".activity.MainActivity"
            android:launchMode="standard"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activity.SecondActivity"
            android:configChanges="screenLayout"
            android:taskAffinity="pjs.com.task"
            android:launchMode="singleTask" />

        <activity android:name=".activity.ThirdActivity"
            android:configChanges="screenLayout"
            android:taskAffinity="pjs.com.task"
            android:launchMode="singleTask"
            ></activity>

我们运行项目在从MainActivity点击按钮(MainActivity中的按钮)进入SecondActivity中再点击(SecondActivity中的按钮)进入ThirdActivity中,日志中可以SecondActivity和SecondActivity实例在"pjs.com.task"任务栈中,MainActivity在"pjs.com.kaifa"任务栈中。(第三个任务栈是桌面任务栈)

 Running activities (most recent first):
      TaskRecord{1b1913f #154 A=pjs.com.task U=0 sz=2}
        Run #3: ActivityRecord{33200b08 u0 pjs.com.kaifa/.activity.ThirdActivity t154}
        Run #2: ActivityRecord{223dd797 u0 pjs.com.kaifa/.activity.SecondActivity t154}
      TaskRecord{28963a0c #153 A=pjs.com.kaifa U=0 sz=1}
        Run #1: ActivityRecord{242298cb u0 pjs.com.kaifa/.activity.MainActivity t153}
      TaskRecord{2890917a #49 A=com.android.incallui U=0 sz=1}
        Run #0: ActivityRecord{397b3f36 u0 com.android.incallui/.InCallActivity t49}

configChanges属性

我们知道当系统发生改变后,Activity会被重新创建,那么有么有办法不重新创建呢?答案是有的,可以给Activity指定configChanges属性。比如不想让Activity再屏幕旋转的时候重新创建,就可以给configChanges属性添加 orientation这个值,如果想指定多个值可以用“|”连接起来。

<manifest中配置>
<activity android:name=".activity.MainActivity"
           android:launchMode="standard"
           android:configChanges="orientation|screenSize"
           >
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>

<activity中重写三个方法>
@Override
   public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
       super.onSaveInstanceState(outState, outPersistentState);
       Log.e("TAG", "onSaveInstanceState");
   }

   @Override
   protected void onRestoreInstanceState(Bundle savedInstanceState) {
       super.onRestoreInstanceState(savedInstanceState);
       Log.e("TAG", "onRestoreInstanceState");
   }

   @Override
   public void onConfigurationChanged(Configuration newConfig) {
       super.onConfigurationChanged(newConfig);
       Log.e("TAG", "onConfigurationChanged, newConfig:"+newConfig.orientation);
   }
06-20 16:52:36.400 20760-20760/pjs.com.kaifa E/TAG: onConfigurationChanged, newConfig:2
06-20 16:52:38.730 20760-20760/pjs.com.kaifa E/TAG: onConfigurationChanged, newConfig:1
06-20 16:52:41.320 20760-20760/pjs.com.kaifa E/TAG: onConfigurationChanged, newConfig:2
06-20 16:52:42.510 20760-20760/pjs.com.kaifa E/TAG: onConfigurationChanged, newConfig:1

看下上面配置,在清单中配置 android:configChanges="orientation|screenSize",在activity中重写onSaveInstanceState、onRestoreInstanceState、onConfigurationChanged这三个方法。然后横竖屏来回切换并没有调用onSaveInstanceState和onRestoreInstanceState这俩方法,而是调用了onConfigurationChanged方法,这个时候我们就可以做一些自己的特殊处理了。

属性和含义.png
Intent Flag启动模式

intent 在跳转页面的时候可以设置intent.setFlags()

Activity的启动

1、显示启动
何为显示启动,就是在创建Intent的时指定要启动的activity的类。所以前提是我们已经知道Activity的类名称,这就运用在我们在开发app中进行activity的跳转。

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);

2、隐式启动
android系统给我们提供了许多服务,如打电话、发短信。但是在我们的应用中我们不知道具体的activity类名称,此时我们就需要用到隐式的activity启动方法。

//打电话
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368"));
startActivity(intent);

//发短信
Intent intent=new Intent();
intent.setAction("android.intent.action.SEND");
intent.setData(Uri.parse("mms:110"));
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);

//打开网页
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
有回调的跳转
/*
*ActivityA跳ActivityB
*/
Intent intnt = new Intent(this,ActivityB.class);
startActivityForResult(int reqCode, Intent intent): 带回调启动Activity

/*
*ActivityB销毁 返回ActivityA
/
 setResult(int resultCode, Intent data): 设置要返回的结果
 finish(): 结束当前Activity

/*
*ActivityA 接收之后ActivityB返回的参数
/
onActivityResult(int reqCode, int resultCode, Intent data): 回调方法
通过data获取返回的数据 进行操作
上一篇 下一篇

猜你喜欢

热点阅读