首页 > 学院 > 开发设计 > 正文

安卓-Activity生命周期

2019-11-08 00:38:16
字体:
来源:转载
供稿:网友

一、简述

android是使用任务来管理活动的,一个任务就是一组存放在栈中的活动的集合。

1.活动有四个状态:1)运行状态:活动位于返回栈的栈顶2)暂停状态:活动不处于栈顶位置,但是可见3)停止状态:活动不处于栈顶位置,且完全不可见4)销毁状态:当一个活动从返回栈中移除

2.活动生存周期Activity中定义了7各回调方法onCreate():活动第一次创建时调用,完成初始化操作:加载布局、绑定事件onStart():活动由不可见变为可见时调用onResume():活动准备好和用户交互时调用。处于返回栈的栈顶,并且处于运行状态onPause():系统准备去启动或者恢复另一个活动时调用。比如:上面弹出各对话框onStop():活动在完全不可见的时候调用onDestroy():活动被销毁之前调用onRestart():活动由停止状态变为运行状态

生命周期又可分为3种生存期完整生存期:onCreate()到onDestroy()之间可见生存期:onStart()到onStop()之间前台生存期:onResume()和onPause()之间

总结下状态的变化:

一个Activity从创建到显示经历:onCreate()  onStart() onResume()一个Activity上弹出一个全屏的Activity,经历:onPause  onStop,上面的销毁,onRestart onStart  onResume一个Activity上弹出一个对话框,下面的Activity经历:onPause  ,此时点击返回键,下面的Activity执行onResume,再点击返回键,Activity执行onPause  onStop  onDestroy

下面也引用网上的一张图:

上面这张图是各个文章中引用最多的,详细说明了各个状态。

二、测试各种情况

1)测试一个Activity正常启动时

测试结果:

02-23 18:55:55.524 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onCreate: 02-23 18:55:55.744 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onStart: 02-23 18:55:55.744 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onResume:

可以看到Activity在启动的过程中经历了:onCreate->onStart->onResume

2)从后台切换到前台

测试结果:

02-23 19:21:51.954 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onRestart: 02-23 19:21:51.954 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onStart:

02-23 19:21:51.964 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onResume:

02-23 19:21:52.004 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged:

可以看到Activity经历了:onRestart->onStart->onResume->onWindowFocusChanged

3)全屏的Activity跳转且不透明

测试结果:

02-23 19:38:31.854 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onPause: 02-23 19:38:31.874 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged: 02-23 19:38:31.904 20215-20215/com.mobile.cdtx.blog D/SecondDemoActivity: onCreate: 02-23 19:38:31.994 20215-20215/com.mobile.cdtx.blog D/SecondDemoActivity: onStart: 02-23 19:38:31.994 20215-20215/com.mobile.cdtx.blog D/SecondDemoActivity: onResume: 02-23 19:38:32.084 20215-20215/com.mobile.cdtx.blog D/SecondDemoActivity: onWindowFocusChanged: 02-23 19:38:32.474 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onStop:

其中ActivityLifeActivity是下面的界面,SecondDemoActivity是跳转后的界面

ActivityLifeActivity经历了:onPause->onWindowFocusChanged->onStop

SecondDemoActivity经历了:onCreate->onStart->onResume->onWindowFocusChanged

4)全屏的Activity返回到下面的Activity

测试结果:

02-23 19:31:59.854 8605-8605/com.mobile.cdtx.blog D/SecondDemoActivity: onPause:02-23 19:31:59.864 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onRestart: 02-23 19:31:59.864 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onStart: 02-23 19:31:59.864 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onResume: 02-23 19:31:59.894 8605-8605/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged: 02-23 19:31:59.964 8605-8605/com.mobile.cdtx.blog D/SecondDemoActivity: onWindowFocusChanged: 02-23 19:32:00.284 8605-8605/com.mobile.cdtx.blog D/SecondDemoActivity: onStop: 02-23 19:32:00.284 8605-8605/com.mobile.cdtx.blog D/SecondDemoActivity: onDestroy:

其中,SecondDemoActivity是上面的界面,ActivityLifeActivity是下面的界面

SecondDemoActivity经历了:onPause->onWindowFocusChanged->onStop->onDestroy

ActivityLifeActivity经历了:onRestart->onStart->onResume->onWindowFocusChanged

5)从前台切换到后台

测试结果:

02-23 19:43:37.854 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onPause: 02-23 19:43:37.874 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged: 02-23 19:43:38.354 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onStop:

ActivityLifeActivity从前台切换到后台经历了:onPause->onWindowFocusChanged->onStop

6)Activity上弹出Toast

测试结果:

未打印任何日志,这说明窗口焦点和Activity未受任何影响

7)Activity上弹出对话框

测试结果:

02-23 19:49:10.394 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged:

可以看到Activity只经历了:onWindowFocusChanged

8)Activity上弹出的对话框消失

测试结果:

02-23 19:50:34.684 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged:

可以看到Activity只经历了:onWindowFocusChanged

9)弹出半透明对话框

测试结果:

02-23 19:52:23.124 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onPause: 02-23 19:52:23.144 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged: 

可以看到ActivityLifeActivity经历了:onPause->onWindowFocusChanged

10)弹出的半透明对话框消失

02-23 19:54:18.944 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onResume: 02-23 19:54:18.964 20215-20215/com.mobile.cdtx.blog D/ActivityLifeActivity: onWindowFocusChanged:

ActivityLifeActivity经历了:onResume->onWindowFocusChanged

11)改变屏幕方向

然后在“Settings->Display”中,将“Auto-rotate Screen”一项选中,表明可以自动根据方向旋转屏幕,中文设置中显示的是:显示->自动旋转屏幕

测试结果:

02-23 20:03:12.884 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onPause: 02-23 20:03:12.884 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onStop: 02-23 20:03:12.884 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onDestroy: 02-23 20:03:12.924 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onCreate: 02-23 20:03:13.044 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onStart: 02-23 20:03:13.044 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onRestoreInstanceState: 02-23 20:03:13.044 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onResume: 02-23 20:03:13.124 9747-9747/com.mobile.cdtx.blog D/SecondDemoActivity: onWindowFocusChanged:

可以看到Activity经历了:onPause->onStop->onDestroy->onCreate->onStart->onRestoreInstanceState->onResume->onWindowFocusChanged

其中onRestoreInstanceState用于恢复保存的数据

上面的数据说明经历销毁到重建的过程,怎么样才可以避免这种情况呢?

1.AndroidManifest.xml中声明:<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>

2.在Activity的注册中设置:android:configChanges="orientation|screenSize“属性

3.在Activity中重写下面的方法:

@Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)        {            Log.d(TAG, "onConfigurationChanged:横屏");// 获取横屏        } else if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)        {// 获取竖屏            Log.d(TAG, "onConfigurationChanged:竖屏");// 获取横屏        }    }测试结果:

02-23 20:27:29.034 9379-9379/com.mobile.cdtx.blog D/SecondDemoActivity: onConfigurationChanged:横屏

可以看到Activity并没有经历销毁到重建的过程。

有些文章上说android:configChanges="orientation“这个属性这样设置,但是在新高版本的API中不行仍然会经历从销毁到重建的过程。自从Android 3.2(API 13),screen size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,如果你想阻止程序在运行时重新加载Activity,除了设置"orientation",还必须设置"ScreenSize"。

如果不想让屏幕发生旋转怎么办呢?

指定属性:

android:screenOrientation="portrait"或android:screenOrientation="landscape"

如果<activity>配置了android:screenOrientation属性,则会使android:configChanges="orientation"失效

测试代码中也增加了onSaveInstanceState方法的测试,但是发现Activity即将被销毁的时候并没有执行这个方法,这是为什么呢?

当Activity只执行onPause方法时(Activity a打开一个透明Activity b)这时候如果App设置的targetVersion大于android3.0则不会执行onSaveInstanceState方法。

以上测试结果已经可以说明常见的调用情况了,大家看着测试数据多体会多测试。

三、测试代码AndroidManifest.xml中用到的关键代码

<uses-permission android:name="android.permission.CALL_PHONE"/><uses-permission android:name="android.permission.CHANGE_CONFIGURATIONS"/>....<activity android:name=".main.activity.ActivityLifeActivity"></activity><!--如果指定了android:screenOrientation="landscape"这个属性,则屏幕不会发生旋转--><activity android:name=".main.activity.SecondDemoActivity" android:configChanges="orientation|screenSize" ></activity>主测试界面布局文件activity_life.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:id="@+id/activity_life"    android:layout_width="match_parent"    android:layout_height="match_parent">    <Button        android:id="@+id/id_btn_jump"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="模拟界面跳转"/>    <Button        android:id="@+id/id_btn_toast"        android:textAllCaps="false"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="弹出Toast提示"/>    <Button        android:id="@+id/id_btn_dialog"        android:textAllCaps="false"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="弹出对话框提示"/>    <Button        android:id="@+id/id_btn_home"        android:textAllCaps="false"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="模拟跳到主屏幕界面"/>    <Button        android:id="@+id/id_btn_call"        android:textAllCaps="false"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="模拟打电话"/></LinearLayout>主界面代码:

import android.Manifest;import android.content.Intent;import android.content.pm.PackageManager;import android.content.res.Configuration;import android.net.Uri;import android.os.Bundle;import android.os.PersistableBundle;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;import com.mobile.cdtx.blog.R;//测试Activity的生命周期public class ActivityLifeActivity extends AppCompatActivity implements View.OnClickListener {    PRivate static final String TAG = "ActivityLifeActivity";    private Button btnJump, btnToast, btnDialog, btnHome, btnCall;    Intent intent;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "onCreate: ");        setContentView(R.layout.activity_life);        initView();    }    @Override    protected void onStart() {        super.onStart();        Log.d(TAG, "onStart: ");    }    @Override    protected void onResume() {        super.onResume();        Log.d(TAG, "onResume: ");    }    @Override    protected void onPause() {        super.onPause();        Log.d(TAG, "onPause: ");    }    @Override    protected void onStop() {        super.onStop();        Log.d(TAG, "onStop: ");    }    @Override    protected void onDestroy() {        super.onDestroy();        Log.d(TAG, "onDestroy: ");    }    @Override    protected void onRestart() {        super.onRestart();        Log.d(TAG, "onRestart: ");    }    @Override    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {        super.onSaveInstanceState(outState, outPersistentState);        Log.d(TAG, "onSaveInstanceState: ");    }    @Override    protected void onRestoreInstanceState(Bundle savedInstanceState) {        super.onRestoreInstanceState(savedInstanceState);        Log.d(TAG, "onRestoreInstanceState: ");    }    @Override    public void onWindowFocusChanged(boolean hasFocus) {        super.onWindowFocusChanged(hasFocus);        Log.d(TAG, "onWindowFocusChanged: ");    }    //当指定了android:configChanges="orientation"后,方向改变时onConfigurationChanged被调用    @Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        Log.d(TAG, "onConfigurationChanged:");    }    //控件初始化    private void initView() {        btnJump = (Button) findViewById(R.id.id_btn_jump);        btnJump.setOnClickListener(this);        btnToast = (Button) findViewById(R.id.id_btn_toast);        btnToast.setOnClickListener(this);        btnDialog = (Button) findViewById(R.id.id_btn_dialog);        btnDialog.setOnClickListener(this);        btnHome = (Button) findViewById(R.id.id_btn_home);        btnHome.setOnClickListener(this);        btnCall = (Button) findViewById(R.id.id_btn_call);        btnCall.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.id_btn_jump:                startActivity(new Intent(ActivityLifeActivity.this, SecondDemoActivity.class));                break;            case R.id.id_btn_toast:                Toast.makeText(this, "测试Toast", Toast.LENGTH_SHORT).show();                break;            case R.id.id_btn_dialog:                new AlertDialog.Builder(this)                        .setTitle("标题")                        .setMessage("简单消息框")                        .setPositiveButton("确定", null)                        .show();                break;            case R.id.id_btn_home:                Intent MyIntent = new Intent(Intent.ACTION_MAIN);                MyIntent.addCategory(Intent.CATEGORY_HOME);                startActivity(MyIntent);                break;            case R.id.id_btn_call:                intent = new Intent(Intent.ACTION_CALL);                Uri data = Uri.parse("tel:1234567890");                intent.setData(data);                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {                    ActivityCompat.requestPermissions(ActivityLifeActivity.this, new String[] {Manifest.permission.CALL_PHONE},1);                    return;                }else{                    startActivity(intent);                }                break;            default:                break;        }    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        if (requestCode == 1) {            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {                intent = new Intent(Intent.ACTION_CALL);                Uri data = Uri.parse("tel:1234567890");                intent.setData(data);                startActivity(intent);            } else {                Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show();            }            return;        }        super.onRequestPermissionsResult(requestCode, permissions, grantResults);    }}运行效果图:

模拟界面跳转的界面的布局文件:

activity_second_demo.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/activity_second_demo"    android:layout_width="match_parent"    android:layout_height="match_parent"></RelativeLayout>布局很简单什么控件都没,主要是在代码中打印关键方法。

模拟界面跳转的界面的代码:

import android.content.res.Configuration;import android.os.Bundle;import android.os.PersistableBundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import com.mobile.cdtx.blog.R;public class SecondDemoActivity extends AppCompatActivity {    private static final String TAG = "SecondDemoActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "onCreate: ");        setContentView(R.layout.activity_second_demo);    }    @Override    protected void onStart() {        super.onStart();        Log.d(TAG, "onStart: ");    }    @Override    protected void onResume() {        super.onResume();        Log.d(TAG, "onResume: ");    }    @Override    protected void onPause() {        super.onPause();        Log.d(TAG, "onPause: ");    }    @Override    protected void onStop() {        super.onStop();        Log.d(TAG, "onStop: ");    }    @Override    protected void onDestroy() {        super.onDestroy();        Log.d(TAG, "onDestroy: ");    }    @Override    protected void onRestart() {        super.onRestart();        Log.d(TAG, "onRestart: ");    }    @Override    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {        super.onSaveInstanceState(outState, outPersistentState);        Log.d(TAG, "onSaveInstanceState: ");    }    @Override    protected void onRestoreInstanceState(Bundle savedInstanceState) {        super.onRestoreInstanceState(savedInstanceState);        Log.d(TAG, "onRestoreInstanceState: ");    }    @Override    public void onWindowFocusChanged(boolean hasFocus) {        super.onWindowFocusChanged(hasFocus);        Log.d(TAG, "onWindowFocusChanged: ");    }    //当指定了android:configChanges="orientation"后,方向改变时onConfigurationChanged被调用    @Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)        {            Log.d(TAG, "onConfigurationChanged:横屏");// 获取横屏        } else if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)        {// 获取竖屏            Log.d(TAG, "onConfigurationChanged:竖屏");// 获取横屏        }    }}


上一篇:API 前后端分离重构

下一篇:Paint大全

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表