美文网首页android杂记Androidandriod学习
Activity你应该知道的一切

Activity你应该知道的一切

作者: 蓝枫zeke | 来源:发表于2016-12-18 10:33 被阅读787次

    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

    简书:http://www.jianshu.com/users/002601150b0b/latest_articles

    相关文章

      网友评论

      • CnPeng:我想问一下,在intent传递数据bean类的时候,如果让 bean 类实现了Parcelable 接口,且bean 类中有 bollean 类型的成员变量,那么在 writeToParcel ( ) 方法中,怎么将这个 bollean 类型的变量写出? 在 readXXX 的时候又该怎么读?
        蓝枫zeke:@CnPeng
        客气了,不用谢啊
        CnPeng:@蓝枫zeke 谢谢解答,现在明白怎么用了 :smile:
        蓝枫zeke:@CnPeng

        比如说添加boolean类型: isMale
        读取的时候:
        this.isMale = in.readByte() != 0;
        写出的时候:
        dest.writeByte(this.isMale ? (byte) 1 : (byte) 0);

      本文标题:Activity你应该知道的一切

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