Activity你应该知道的一切

Activity简介

Activity是一种展示型组件,用于向用户直接展示一个界面,并且可以接受用户的输入信息从而进行交互。Activity是最重要的一组组件,对用户来说,Activity是一个Android用户的全部,因为其他三大组件对于用户来说都是不可感知的。Activity的启动由Intent触发,其中Intent可以分为显示Intent和隐式Intent。Activity是具有生命周期的。一个Acticity组件可以有不同的启动模式,不同的启动模式具有不同的效果。Intent可以用于Activity之间进行数据的传递。Activity组件是可以停止的,在实际开发过程中可以通过Activity的finish方法来结束Activity的运行。Activity扮演的是一种前台界面的角色

Activity的生命周期

首先来看一张Activity的生命周期图:


activity.png

正常情况下的生命周期分析

(1)第一次启动一个Activity,回调方法:onCreate—>onStart—>onResume


a.gif

(2)当用户按住Home键的时候,回调如下:onPause->onStop


b.gif

(3)当用户再次返回到原Activity的时候,回调方法如下:
onRestart->onStart->onResume


c.gif

(4)当用户按back键回退时,回调方法如下:onPause->onStop->onDestroy


d.gif

异常情况下的生命周期分析

e.png

(1)系统配置发生改变导致Activity被杀死并重新创建

比如说,当横竖屏切换的时候,Activity就会被销毁并重新创建,当然我们也可以阻止系统重新创建Activity。当系统配置发生变化是,在onStop之前会调用onSaveInstanceState方法来保存当前Activity的状态。当Activity被重新创建后,系统会调用onRestoreInstanceState方法来恢复之前保存的状态,这个方法在onStart方法之后执行。

看代码:


package note.com.chapter_01;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import butterknife.ButterKnife;
import butterknife.InjectView;

/**
 * Created by zhoujian on 16/9/11.
 */
public class SecondActivity extends Activity {

    private static final String TAG = "SecondActivity";
    @InjectView(R.id.bt_back)
    Button mBtBack;
    private String mName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        ButterKnife.inject(this);
        Log.e(TAG, "onCreate()方法执行了");
        if (savedInstanceState != null) {
            mName = savedInstanceState.getString("name");
            Log.e(TAG, "onCreate=" + mName);
        }
        clickEvent();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("name", "周建");
        Log.e(TAG,"onSaveInstanceState方法执行了");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        //快捷键:option+command+f   快速提取变量
        String mName = savedInstanceState.getString("name");
        Log.e(TAG, "onRestoreInstanceState=" + mName);
    }

    private void clickEvent() {
        mBtBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e(TAG, "onRestart()方法执行了");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart()方法执行了");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG, "onResume()方法执行了");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG, "onPause()方法执行了");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop()方法执行了");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy()方法执行了");
    }
}

当横竖屏切换的时候,Activity会被杀死并重新创建,运行结果截图如下:


f.png

系统配置中内容很多,如何当某项内容发生改变后,我们不想系统重新创建Activity,可以给Activity指定configChanges属性,比如如果不想让屏幕旋转时重新创建,可以给onfigChanges属性添加orientation这个值。

android:configChanges="orientation"

android:configChanges的属性有很多,具体读者可以查阅相关文档。

 android:configChanges="orientation|mcc|mnc|locale|touchscreen|keyboard
             |keyboardHidden|navigation|screenLayout|fontScale|uiMode|screenSize
             |smallestScreenSize|layoutDirection">

(2)资源内存不足导致低优先级的Activity被杀死,导致Ativity被销毁并重新创建

优先级从高到低如下:

  • 正在和用户交互的Activity优先级最高。
  • 可见但非前台的Activity,比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户交互。
  • 已经被暂停的Activity,优先级最低。

Activity的启动模式

默认情况下,当我们多次启动同一个Activity的时候,系统会创建多个实例并把它们一一放入任务栈中。任务栈是一种“先进后出”的栈结构。
Android中的四种启动模式:standard、singleTop、singleTask、singleInstance

  • standard:标准模式,也是默认的启动模式
    每次启动一个Activity都会重新创建一个实例,不管这个实例是否存在。在这种模式下,谁启动了这个Activity,这个Activity就运行在启动它的那个Activity的任务栈中。
        <activity
            android:name=".MainActivity"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

比如说连续两次启动MainActivity,然后执行adb shell dumpsys activity命令查看任务栈情况


Snip20161218_11.png

可以看出此时只有一个任务栈,任务栈为当前包名,任务栈中有3个MainActivity(原本的一个MainActivity和启动两次)

  • singleTop:栈顶复用模式
    如果新的Activity的实例已经存在并且位于栈顶,那么此Activity不会被重新创建。
    如果新的Activity的实例已经存在但不是位于栈顶,那么此Activity仍然会重新创建。
        <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>

比如说连续两次启动MainActivity,然后执行adb shell dumpsys activity命令查看任务栈情况

因为MainActivity已经位于栈顶了,两次启动MainActivity的时候,不会重新创建,此时的任务栈中应该只有一个实例
看运行截图:


q.png

只有一个任务栈,任务栈中只有一个实例

  • singleTask:栈内复用模式
    当一个具有singleTask模式的Activity请求启动后,比如说Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在A所需的任务栈,就会重新创建一个任务栈,然后创建A的实例并把A放入任务栈中。如果存在A所需的任务栈,这是要看A是否在栈中有实例存在,如果存在,就把A调到栈顶并调用它的onNewIntent方法,如果不存在,就创建A的实例并把A压入栈中。
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

比如说连续两次启动MainActivity,然后执行adb shell dumpsys activity命令查看任务栈情况
因为MainActivity实例已经存在,系统就会把MainActivity实例调到栈顶,并两次调用它的onNewIntent方法
看运行截图:


q.png

两次调用onNewIntent方法:


x.png
  • singleInstance:单实例模式
    除具有singleTask模式的所有特征外,singleInstance模式的Activity只能单独位于一个任务栈中。
指定启动模式

有两种方式指定启动模式,第二种方式优先级高于第一种
第一种是在清单文件为Activity指定启动模式

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

第二种是通过给Intent设置标志位来为Activity指定启动模式

Intent mIntent =new Intent(MainActivity.this,MainActivity.class);                
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(mIntent);
Activity的Flags
  • FLAG_ACTIVITY_NEW_TASK
    作用是为Activity指定singleTask启动模式,和在清单文件中指定效果相同。
  • FLAG_ACTIVITY_SINGLE_TOP
    作用是为Activity指定singleTop启动模式,和在清单文件中指定效果相同。
  • FLAG_ACTIVITY_CLEAR_TOP
    具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有这个标记位的Activity不会出现在历史Activity的列表中,等同于指定android:excludeFromRecents="true"。

Activity的显示调用和隐式调用

显示调用:明确指定被启动对象的组件信息
隐式调用:不需要指定组件信息,隐式调用需要Intent能够匹配目标组件IntentFilter中所设置的过滤信息,如果不匹配则无法调用目标Activity

  <activity android:name=".SecondActivity">
            <intent-filter>

                <action android:name="com.zhoujian.define"/>
                <action android:name="com.zhoujian.start"/>

                <category android:name="com.zhoujian.text"/>
                <category android:name="com.zhoujian.cool"/>
                <category android:name="android.intent.category.DEFAULT"/>

                <data android:mimeType="text/plain"/>
                
            </intent-filter>
 </activity>
  • action的匹配规则
    要求Intent中的action存在且必须和过滤规则中的其中一个相同

  • category的匹配规则
    可以不设置,要设置的话,每一个都必须和过滤规则中的任一个相同

  • data的匹配规则
    和action的匹配规则类似,如果过滤规则中定义了data,那么Intent中必须要定义可匹配的data

下面给出匹配规则

Intent mIntent = new Intent();
mIntent.setAction("com.zhoujian.define");
mIntent.addCategory("com.zhoujian.text");
mIntent.addCategory("com.zhoujian.cool");
mIntent.setDataAndType(Uri.parse("file://abc"),"text/plain");
startActivity(mIntent);

Intent在Activity间传递数据

  • Intent传递简单数据

在原Activity发送数据

Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name","周建");
bundle.putInt("age",25);
intent.putExtras(bundle);
startActivity(intent);

在目标Activity接受数据

Intent intent = getIntent();
bundle = intent.getExtras();
String mName = bundle.getString("name");
int mAge = bundle.getInt("age");
Log.d(TAG, "姓名:"+mName+",年龄:"+mAge);

  • Intent传递JavaBean
    实现Serializable接口
package note.com.chapter_01;

import java.io.Serializable;

/**
 * Created by zhoujian on 2016/12/21.
 */

public class Person implements Serializable
{
    private static final long serialVersionUID = 1L;
    private int age;
    private String name;

    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @Override
    public String toString()
    {
        return "Person{" +"age=" + age + ", name='" + name + '\'' + '}';
    }
}

在原Activity发送数据

Person person= new Person();
person.setName("周建");
person.setAge(25);
Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("person",person);
startActivity(intent);

在目标Activity接受数据


Intent intent = getIntent();
Person mPerson = (Person)intent.getSerializableExtra("person");
Log.d(TAG, mPerson.toString());

实现Parcelable接口

package note.com.chapter_01;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhoujian on 2016/12/21.
 */

public class User  implements Parcelable
{
    private int age;
    private String name;
    public User()
    {
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.age);
        dest.writeString(this.name);
    }
    protected User(Parcel in) {
        this.age = in.readInt();
        this.name = in.readString();
    }

    public static final Creator<User> CREATOR = new Creator<User>()
    {
        @Override
        public User createFromParcel(Parcel source)
        {
            return new User(source);
        }

        @Override
        public User[] newArray(int size)
        {
            return new User[size];
        }
    };

    @Override
    public String toString()
    {
        return "User{" + "age=" + age + ", name='" + name + '\'' + '}';
    }
}

在原Activity发送数据

User user = new User();
user.setAge(25);
user.setName("周建");

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

在目标Activity接受数据

Intent intent = getIntent();
User mUser = (User) intent.getParcelableExtra("user");
Log.d(TAG, mUser.toString());
  • Intent传递集合

在原Activity发送数据

ArrayList<Person> personArrayList = new ArrayList<Person>();

Person Aperson= new Person();
Aperson.setName("周建");
Aperson.setAge(25);
personArrayList.add(Aperson);

Person Bperson= new Person();
Bperson.setName("zhoujian");
Bperson.setAge(28);
personArrayList.add(Bperson);

Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("personArrayList",(Serializable)personArrayList);
startActivity(intent);

在目标Activity接受数据

Intent intent = getIntent();
ArrayList<Person> mList = (ArrayList<Person>) intent.getSerializableExtra("personArrayList");
Log.d(TAG, mList.toString());
  • onActivityResult

MainActivity.java

  private void clickEvent() {
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                //requestCode
                startActivityForResult(intent, INTENT_FLAG);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {


        if (resultCode == RESULT_OK)
        {
            switch (requestCode)
            {
                case INTENT_FLAG:
                    String result = data.getStringExtra("msg");
                    Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }

SecondActivity.java

  private void clickEvent() {
        mBtBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new  Intent();
                intent.putExtra("msg","我来自第二个界面");
                setResult(RESULT_OK,intent);
                finish();
            }
        });
    }

以上就是有关Activity的基础知识的总结

CSDN博客:http://blog.csdn.net/u014005316

Github:https://github.com/zeke123

掘金:https://gold.xitu.io/user/5740329671cfe4006c391e1d

简书://www.greatytc.com/users/002601150b0b/latest_articles

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容