Activity

作者: 我家猫猫不睡觉 | 来源:发表于2020-08-21 11:26 被阅读0次

    文章参考 Android Activity 完全解析Android【安卓】Activity详解。仅方便个人记忆。

    一、正常情况下一个 Acitivity 会经历如下的生命周期:

      1. 生命周期介绍
        1)onCreate
        表示Activity正在被创建,做一些初始化动作,只在创建时调用一次。
        2)onStart
        表示Activity正在被启动,这时Activity已经可见,但不在前台,用户看不到也无法交互。
        3)onResume
        这里 Activity 已经可见了,可以和用户进行交互了,这时 Activity 处于栈顶,正在运行,这里要注意和 onStart 进行区分,可以理解为 onStart 和onResume 都表示 Activity 可见,但 onStart 的时候 Activity 还在后台,用户不可见,onResume 的时候在前台,用户可见
        4)onPause
        这个方法表示 Activity 正在停止,但还没有真正的停止,我们通常在这个方法中将一些消耗资源东西释放,可以存储一些关键的数据,但一定不能做耗时的操作,要不会意向下一个活动的使用
        5)onStop
        表示 Activity 即将停止,在 Activity 完全不可见得时候调用,可以做一些回收工作,也不能太耗时,这里要注意和 onPause 方法的区分,它和 onPause 方法的主要区别在于,如果新activity的方法是一个对话框式的活动,onPause 方法执行,onStop 方法不执行
        6)onDestroy
        表示 Activity 即将被销毁,对应于 onCreate 方法,这是 Acitivty 生命周期方法的最后一个回调方法,活动的状态将变为销毁状态
        7)onRestart
        这个方法在活动由停止状态变为运行状态时调用,这时候活动就是被重新启动了
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
     
        private static final String TAG = "MainActivity";
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.i(TAG, "MainActivity: onCreate");
        }
        @Override
        protected void onStart() {
            super.onStart();
            Log.i(TAG, "MainActivity: onStart");
        } 
        @Override
        protected void onResume() {
            super.onResume();
            Log.i(TAG, "MainActivity: onResume");
        }
        @Override
        protected void onPause() {
            super.onPause();
            Log.i(TAG, "MainActivity: onPause");
        }
        @Override
        protected void onStop() {
            super.onStop();
            Log.i(TAG, "MainActivity: onStop");
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.i(TAG, "MainActivity: onDestroy");
        }
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.i(TAG, "MainActivity: onRestart");
        }
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this,SecondActivity.class);
            startActivity(intent);
        }
    }
    
    这里我们创建了一个 MainActivity 并且重写了 Activity 生命周期的方法,这时候我们运行程序,在 Log 的控制台可以看到如下打印
    MainActivity: MainActivity: onCreate
    MainActivity: MainActivity: onStart
    MainActivity: MainActivity: onResume
    然后我们点击后退按钮,我们再来看一下控制台的打印内容
    MainActivity: MainActivity: onPause
    MainActivity: MainActivity: onStop
    MainActivity: MainActivity: onDestroy
    这时候一个 Activity 完整的生命周期就执行完毕了
    接下来我们不点击后退键,我们点击 Home 键来看一下生命周期的情况
    MainActivity: MainActivity: onCreate
    MainActivity: MainActivity: onStart
    MainActivity: MainActivity: onResume
    MainActivity: MainActivity: onPause
    MainActivity: MainActivity: onStop
    这里要注意 Activity 并没有去执行 onDestroy,这里 Activity 只是变成了不可见状态,并没有被销毁,我们再点击打开应用
    MainActivity: MainActivity: onRestart
    MainActivity: MainActivity: onStart
    MainActivity: MainActivity: onResume
    这里注意到应用并没有调用 onCreate 重新创建,而是只调用了 onRestart,onStart,onResume 方法
    

    这里我们就把在正常情况下的一个 Activity 的生命周期情况介绍完毕,总结如下:


    • Activity的状态
      每个activity在其生命周期中最多可能会有 4 种状态
      1)运行状态
      当一个活动位于返回栈的栈顶时,这时活动就处于运行状态,系统最不愿意回收的就是出于运行状态的活动,因为这样会带来非常差的用户体验
      2)暂停状态
      当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态,你可能会觉得,既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏膜的,比如对话框形式的活动只会占用屏膜一部分的区域,处于暂停状态的活动任然是存活的,系统也不愿意去回收这种活动(因为它还是可见的),只有内存极低的情况下,系统才会去考虑回收这种活动
      3)停止状态
      当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入停止状态,系统任然会为这种活动保存相应的状态和成员变量,但是当其他地方需要内存时,处于停止状态的活动有可能会被系统回收
      4)销毁状态
      当一个活动从返回栈中移除后就变成了销毁状态,系统会最倾向于回收处于这种状态的活动,从而保持手机的内存充足

    二、多个 Activity 交互的生命周期:

    这里我们新建一个 SecondActivity 类,我们在 MainActivity 中设置一个点击按钮,用来跳到 SecondActivity,并且和 MainActivity 一样复写生命周期的方法如下:

    /**
     * 生命周期测试SecondActivity
     */
     
    public class SecondActivity extends AppCompatActivity {
     
        private static final String TAG = "SecondActivity";
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            Log.i(TAG, "SecondActivity: onCreate");
        } 
        @Override
        protected void onStart() {
            super.onStart();
            Log.i(TAG, "SecondActivity: onStart");
        } 
        @Override
        protected void onResume() {
            super.onResume();
            Log.i(TAG, "SecondActivity: onResume");
        } 
        @Override
        protected void onPause() {
            super.onPause();
            Log.i(TAG, "SecondActivity: onPause");
        }
        @Override
        protected void onStop() {
            super.onStop();
            Log.i(TAG, "SecondActivity: onStop");
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.i(TAG, "SecondActivity: onDestroy");
        }
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.i(TAG, "SecondActivity: onRestart");
        }
    }
    
    

    现在我们来看分析生命周期的运行情况:首先打开 MainActivity,控制台打印生命周期运行如下:

    MainActivity: MainActivity: onCreate
    MainActivity: MainActivity: onStart
    MainActivity: MainActivity: onResume
    

    这时候我们再点击跳转按钮,控制台打印生命周期如下:

    MainActivity: MainActivity: onPause
    SecondActivity: SecondActivity: onCreate
    SecondActivity: SecondActivity: onStart
    SecondActivity: SecondActivity: onResume
    MainActivity: MainActivity: onStop
    

    这里我们思考如下内容
    1)为什么要先调用 onPause 方法暂停当前显示的 MainActivity
    2)为什么要等 SecondActivity 的 onCreate,onStart,onResume 方法执行完才执行 MainActivity的 onStop 方法,为什么不先执行当前 MainActivity的 onPause,onStop 方法呢?

    在 MainActivity 打开 SecondActivity 的时候,会先执行 MainActivity 的 onPause 方法,然后执行 SecondActivity 的 onCreate、onStart、onResume 方法,完了再执行 MainActivity 的 onStop 方法,这里面还是有一种思想的,首先执行 MainActivity 的 onPause 方法是要关闭当前 MainActivity 的一些状态信息,如音频,视频等的状态,以免对新打开的 Activity 造成影响,试想我们正在听歌或看电影,这时候打进来一电话,如果我们不把当前的 Activity 先onPause 掉,会使一种怎样的体验,一边听歌,一遍打电话,不敢想象这样的场景,那么为什么又是在 MainActivity 执行完 onPause 而没有 onStop 的情况下就先执行 SecondActivity 的 onCreate,onStart,onResume 方法然后再执行 onStop 方法呢,这是因为考虑到如果在打开 SecondActivity 开启时发生 crash 的情况,这样如果 MainActivity 执行了 onStop 方法则会变为不可见状态,就会呈现出黑屏状态,用户什么都看不到。

    三、横竖屏切换的时候 Acitivity 的生命周期:

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

    如果横竖屏切换的时候 Acitivity 的生命周期销毁掉然后会重新创建,所以我们展示的数据要在onSaveInstanceState 方法中去保存数据,在 MainActivity 中添加如下代码就可以降临时数据进行保存:

     @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            String data = "hello";
            outState.putString("key",data);
        }
    
    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (savedInstanceState != null) {
                String data = savedInstanceState.getString("key");
            }
            Log.i(TAG, "MainActivity: onCreate");
        }
    

    四、启动方式

    Activity的启动分为显式和隐式启动,原则上二者不该同时存在,同时存在以显式为主。显式需要指定被启动对象的组件信息(包名、类名),隐式不需要指定目标组件信息,只要Intent可以匹配上目标Activity的IntentFilter设置的过滤信息。

    • 1、使用显示Intent
    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
    startActivity(intent);
    
    private final int ACTIVITY_BACK = 1001;
    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
    startActivityForResult(intent,ACTIVITY_BACK);
    后面传递数据详解
    
    • 2、使用隐式Intent
      IntentFilter的过滤信息有action、category、data三种。一个Activity里可以有多组IntentFilter,每组IntentFilter里可以包含多个action、category、data,每组IntentFilter的任意三个及以上action、category、data形成一个匹配约束,只要Java代码里能够匹配一个匹配约束,即可启动目标Activity
     <activity android:name=".SecondActivity">
               <intent-filter>
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <category android:name="com.weihy"/>
                    <category android:name="android.intent.category.BROWSABLE"/>
                    <data android:scheme="http"/>
               </intent-filter>
               <intent-filter>
                    <action android:name="android.intent.action.SEND"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <data android:mimeType="image/*"/>
               </intent-filter>
     </activity>
    
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://www.baidu.com"));
    startActivity(intent);
    

    1)action匹配规则
    action是一个字符串,系统预定义了一些action,我们也可以在应用定义自己的action。Java代码里的Intent必须要有action,且这个字符串必须和xml中的action一致,区分大小写。
    2)category匹配规则
    category是一个字符串,系统预定义了一些category,我们也可以在应用定义自己的category。Java代码里category可以缺省,缺省时系统匹配xml中的android.intent.category.DEFAULT字符串,不缺省,匹配规则与action一致。
    3)data匹配规则
    如果xml里的IntentFilter定义了data,那么Java代码里的Intent必须要有data。
    data由两部分组成mimeType和URI,
    mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,
    URI表示统一资源标识符,可以定位本地和网络的资源

    //如下过滤规则
    <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="image/*"/>
    </intent-filter>
    

    这种规则指定了媒体类型为所有类型的图片,Intent中mimeType必须为“image/*”才能匹配,xml中的过滤规则虽然没有指定URI但是scheme有默认值为content或file,所以intent中URI的scheme必须为content或file

    //如下过滤规则
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*" android:scheme="file" android:host="asd"/>
    </intent-filter>
    
    intent.setDataAndType(Uri.parse("file://asd"),"image/*");
    

    像这种指定了完整的data的xml,Java代码不可以先用setData方法,再用setType方法指定URI和mimeType,因为这两个方法会将对方的值互相清空,应该用setDataAndType方法。

    使用隐式Intent不仅可以自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享称为可能。

    启动浏览器:
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri url = Uri.parse("https://www.baidu.com");
    intent.setData(url);
    startActivity(intent);
    
    启动相册:
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    startActivity(intent);
    
    启动电话界面:
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri url = Uri.parse("tel:123456");
    intent.setData(url);
    startActivity(intent);
    

    五、数据交互

    说到了Intent那就不能不谈Activity之间的数据交互
    1)简单的数据传递

    //传递
    Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
    intent.putExtra("name","name");
    intent.putExtra("age","age");
    startActivity(intent);
    //获取
    Intent intent = getIntent();
    String name =intent.getStringExtra("name");
    String age = intent.getStringExtra("age");
    

    2)传递 Bundle 对象

    Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
    //这里传递Bundle对象
    Bundle bundle = new Bundle();
    bundle.putString("name","姓名");
    bundle.putString("age","0");
    intent.putExtras(bundle);
    startActivity(intent);
    
    //获取方式同上
    Intent intent = getIntent();
    String name =intent.getStringExtra("name");
    String age = intent.getStringExtra("age");
    

    3)传递 JavaBean
    当我们传递的数据比较大的时候,这时候我们可以直接传递一个 JavaBean,这里注意要实现 Serializable 接口

    //Bean
    public class Person implements Serializable {
     
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    //传递
    Person person = new Person("姓名", 0);
    Intent intent = new Intent(MainActivity.this, ThirdActivity.class);
    intent.putExtras("person", person);
    startActivity(intent);
    //获取
    Intent intent = getIntent();
    Person person = (Person) intent.getSerializableExtra("person");
    

    4)返回数据给上个Activity

    private final int ACTIVITY_BACK = 1001;
    Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
    startActivityForResult(intent,ACTIVITY_BACK);
    
     //接收来自ThirdActivity的数据
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            switch (requestCode){
                case ACTIVITY_BACK:                //一个activity可能启动不同activity,用这个来区分
                    if(resultCode == RESULT_OK){   //这个字段区分栈顶activity是单纯的出栈,还是要向下传递消息
                        redBack = data.getIntExtra(RED_KEK,0);
                        greenBack = data.getIntExtra(GREEN_KEY,0);
                        blueBack = data.getIntExtra(BLUE_KEY,0);
                    }
                    break;
                default:
                    break;
            }
        }
    
    ThirdActivity的处理,这里我重写系统返回按键的onBackPressed方法,
    因为你把setResult放在finish方法或者onDestroy方法里的话,退出得太快,消息可能没办法传递下去。
    @Override
     public void onBackPressed() {
        getColorSendBack(selected);
        super.onBackPressed();
    }
      //返回数据给上个activity
    private void getColorSendBack(int mode){
        if(redBack == NO_COLOR){
            redBack = 0;
            blueBack = 0;
            greenBack = 0;
         }
         Intent intent = new Intent();
         intent.putExtra(RED_KEK,redBack);
         intent.putExtra(GREEN_KEY,greenBack);
         intent.putExtra(BLUE_KEY,blueBack);
         setResult(RESULT_OK,intent);  //此处告诉上个activity,不是单纯结束,有消息下来了
    }
    

    六、启动模式

    在说启动模式前,先看一下任务栈(返回栈)

    • Android使用任务(Task)来管理活动。一个任务就是一组存放在栈(也称返回栈 Back Stack)里的活动的集合。栈是一种先进后出的数据结构。
    • 在默认情况下,每当我们启动一个新的活动,它会在任务栈中入栈,并处于栈顶的位置。每当我们销毁一个活动(按Back键或调用finish()方法),处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。
    • 系统总是会显示处于栈顶的活动给用户。
    • 默认情况下所有Activity所需的任务栈的名字为应用的包名。任务栈有前台和后台之分,后台栈中的Activity处于暂停状态,用户可以通过切换,将后台任务栈再次调到前台。


    活动一共有四种启动模式:standard、singleTop、singleTask和singleInstance。
    在AndroidManifest.xml中给< activity>标签指定android:launchMode属性来选择启动模式,如下:

    <activity 
         android:name=".MainActivity"
         android:launchMode="singleTop">
         <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
    </activity>
    
    • standard:标准模式 (默认)
      每启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,并具有典型情况下的生命周期。谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。
      使用ApplicationContext去启动standard模式的Activity会报错,非Activity类型的Context没有所谓的任务栈,要给它设立一个FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它建立一个新的任务栈,而不是去找原Context的任务栈。
    • singleTop:栈顶复用模式
      可以说是standard模式的“子类”(就Activity复用的条件和入的栈而言,是一个一般到特殊的过程)。
      新的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建实例,因此它的onCreate、onStart方法不会被系统调用,而是会调用一个onNewIntent方法,通过这个方法,我们可以取得当前请求的信息。
      如果新的Activity不在栈顶,即使已经存在,仍然是会重新创建Activity实例。
    • singleTask:栈内复用模式
      A:新的Activity要求的任务栈S存在。
      检查栈S里是否存在新的Activity A,存在,因为clearTop效果,将A之上的Activity全部出栈,使其到达栈顶。
      B:新的Activity要求的任务栈S不存在。
      创建新的A的实例,并入栈S。
      此外,这种模式也是和singeTop模式类似,会复用Activity实例,所以当重复创建时,不会调用onCreate、onStart方法,而是会调用onNewIntent方法。
      可以说是singleTop模式的“子类”
    • singleInstance:单实例模式
      可以说是singleTask模式的“子类”。它除了具有 singleTask 的全部属性外,那就是这种模式的 Activity 只能单独的位于一个任务栈中
    • 给Activity指定启动模式的两种方法:
      在AndroidManifest.xml中指定
      android:launchMode=“singleTask”
      在Intent中设置标志位
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      优先级代码里比xml高,要以代码里为准。
      限定范围不同,xml里无法为Activity直接指定FLAG_ACTIVITY_CLEAR_TOP标识,代码里无法指定singleInstance模式。
    • Activity的Flags
      这里的标记位就是上文提到的使用Intent在代码里设置Activity启动方式的标记位。
      常用标记位如下:
    FLAG_ACTIVITY_NEW_TASK
    为Activity指定启动模式为“singleTask”。
    
    FLAG_ACTIVITY_SINGLE_TOP
    为Activity指定启动模式为“singleTop”
    
    FLAG_ACTIVITY_CLEAR_TOP  
    在“standard”模式下,使用这个标记位,如果默认任务栈里,已经有了目标ActivityA,那么A和A之上的Activity都要出栈,
    然后新建一个A压入栈顶。与“singleTask”配合使用时,使用这个标记位,目标A之上的Activity都要出栈,并且onNewIntent方法会被调用。
    
    FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有这个标志的Activity不会出现在历史Activity的列表中,
    当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。
    等同在xml中指定Activity的属性为:
    android:excludeFromRecents="true"
    

    七、Activity管理

    /**
     * Activity 管理类
     */
    public class ActivityManager {
     
        //Activity存储实体
        private static Stack<Activity> activityStack;
        private static ActivityManager instance;
     
        private ActivityManager() {}
     
        /**
         * 单一实例
         */
        public static ActivityManager getInstance() {
     
            if (instance == null) {
                synchronized (ActivityManager.class) {
                    if (instance == null) {
                        instance = new ActivityManager();
                    }
                }
            }
            return instance;
        }
     
     
        /**
         * 添加 Activity 到堆栈
         */
        public void addActivity(Activity activity) {
            if (activityStack == null) {
                activityStack = new Stack<Activity>();
            }
            activityStack.add(activity);
        }
     
        /**
         * 从堆栈移除 Activity
         */
        public void removeActivity(Activity activity) {
            if (activity != null) {
                activityStack.remove(activity);
            }
        }
     
        /**
         * 获取当前 Activity(堆栈中最后一个压入的)
         */
        public Activity currentActivity() {
            Activity activity = activityStack.lastElement();
            return activity;
        }
     
        /**
         * 结束当前 Activity(堆栈中最后一个压入的)
         */
        public void finishActivity() {
            Activity activity = activityStack.lastElement();
            finishActivity(activity);
            removeActivity(activity);
     
        }
     
        /**
         * 结束指定的 Activity
         */
        public void finishActivity(Activity activity) {
            if (activity != null) {
                activityStack.remove(activity);
                activity.finish();
            }
        }
     
        /**
         * 结束指定类名的 Activity
         */
        public void finishActivity(Class<?> cls) {
            for (Activity activity : activityStack) {
                if (activity.getClass().equals(cls)) {
                    finishActivity(activity);
                }
            }
        }
     
        /**
         * 结束所有 Activity
         */
        public synchronized void finishAllActivity() {
            for (int i = 0, size = activityStack.size(); i < size; i++) {
                if (null != activityStack.get(i)) {
                    activityStack.get(i).finish();
                }
            }
            activityStack.clear();
        }
    
        /**
         * 退出应用程序
         */
        public void AppExit(Context context) {
            try {
                finishAllActivity();
                ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                activityMgr.restartPackage(context.getPackageName());
                System.exit(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Activity

          本文链接:https://www.haomeiwen.com/subject/kenyjktx.html