Activity整理总结(1)

作者: yuchu1900 | 来源:发表于2018-11-24 12:57 被阅读8次

1. Activity的生命周期

Activity说的通俗一点,一个Activity对应手机屏幕上的一个页面,就像一个网页一样。Activity什么时候能被看到?是不是一锁屏,Activity就要被销毁释放?要回答这些问题,就涉及到Activity的生命周期。

1. Activity任务栈

android系统用任务栈来管理Activity。当创建一个新的Activity后,此Activity将被加入到任务栈的栈顶,从而先前在栈顶的Activity就被新建Activity压到下一层,其他Activity依次往下移动一层;反之,当栈顶的Activity被销毁移出任务栈后,任务栈下一层的Activity就会依次上升一层。

2. Activity四种状态

1) 运行状态(Running):当Activity位于栈顶时,此时Activity对用户可见并且可交互;

2) 暂停状态(Paused):当Activity不在栈顶,但对用户仍然可见,此时Activity界面失去了焦点无法与用户进行交互,例如,栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕;

3) 停止状态(Stopped):此时Activity界面对用户完全不可见也不可交互;

4) 销毁状态(Destroyed):此时Activity界面被销毁,等待资源被回收

3. Activity生命周期

在每个不同的状态阶段,Android系统会自动对Activity内相应的方法进行回调,调用顺序看下图:

说明:

1. 从Activity创建到运行态,系统会依次回调如下方法:onCreate->onStart->onResume。

2. onCreate和onDestroy配对,onStart和onStop配对,onResume和onPause配对。

1)onCreate和onDestroy:在回调onCreate之后,系统会为Activity分配资源,在调用onDestroy之后系统才能回收这些资源。因此,界面初始化工作一般都放在onCreate里完成,而释放资源的工作应该放在onDestroy里。

2)onStart和onStop:当Activity执行onCreate后,界面对用户依然是不可见的,只有执行onStart回调方法后,Activity才对用户可见;直到系统回调onStop方法后,Activity对用户才不可见了

3)onResume和onPause:当Activity执行onResume回调方法时,Activity就可以响应用户交互;当回调onPause方法后,Activity就不再响应用户交互。

3. 其它状态回到运行态:

1) 当前如果处于onPause状态,那么要回到运行状态系统就只会回调:onResume。

2) 当前如果处于onStop状态,那么要回到运行状态系统就会依次回调:onRestart->onStart->onResume

3) 当前如果处于onDestory状态,那么要回到运行状态系统就会依次回调:onCreate->onStart->onResume

4. 打开新的Activity2的时候,先前在栈顶的Activity1,这二者回调方法执行顺序如下

Activity1: onPause

Activity2: onCreate

Activity2: onStart

Activity2: onResume

Activity1: onStop

2. Activity启动模式


我们可能希望跳转到原来创建过的某个Activity实例,但是这个Activity实例如果不在任务栈的栈顶,这时就要再创建一个新的Activity实例并将其压入栈顶,这样就可能产生大量重复的 Activity,有时我们不希望这样。这时就需要我们为 Activity 配置特定的启动模式,而不是使用默认的启动模式。设置启动模式的位置在AndroidManifest.xml 文件中 Activity的android:launchMode 属性

1. standard模式

这是默认模式,无需设置,每次激活Activity时都会创建Activity实例,并放入任务栈中。相当于入栈,按back键返回到前一个Activity相当于退栈。

2. singleTop模式

如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent()),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。可用来解决栈顶多个重复相同的Activity的问题

3. singleTask模式

如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。singleTask 模式可以用来退出整个应用。将主Activity设为SingTask模式,然后在要退出的Activity中转到主Activity,然后重写主Activity的onNewIntent函数,并在函数中加上一句finish。

4. singleInstance模式

在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。

3. Activity之间数据传递


3.1 通过Intent传递

这是一种比较规范的数据传递方式。首先,在要发送数据的Activity对象中创建一个Intent(意图)对象,然后可以将Integer、String、Float、Double等简单数据类型或可序列化对象(用户自己定义的序列化对象,记住自定义对象必须是序列化的)保存在Intent对象中,然后启动另一个,在另一个Activity中先获取Intent对象,再通过Intent对象来获得这些数据。示例代码如下:

1. 发送数据

1. 在当前要发送数据的Activity对象(即CurrentActivity)中,创建Intent对象

Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);//CurrentActivity.this代表发送者,OtherActivity.class代表接收者

2. 将要发送的数据放入Intent对象,有两种方式:

a. 直接将数据放入Intent对象,以key-value对方式存入。获取数据时,是用key来获取value。有多个数据可以全部用这种方式逐个存入Intent对象。

intent.putExtra("age", 5); //前面的"age"是个key,5代表存入一个整数的value

intent.putExtra("price",1.5f); //再存入一个Float类型的数据

b. 先创建Bundle对象,将数据放入Bundle对象,然后将Bundle对象再放入Intent对象

Bundle bundle =newBundle();// 创建Bundle

bundle.putBoolean("male", true);//放入一个布尔型数据

bundle.putString("name","Jack"); //再放入一个字符串数据

intent.putExtras(bundle); //将Bundle对象存入Intent对象中

3. 启动新Activity(就是要接收数据的Activity)

startActivity(intent);

2. 获取数据

在新开启的Activity对象(对应上面的OtherActivity)中,获取Intent对象,然后通过Intent获取数据,也有两种方式:

a. 直接通过Intent对象来获取

Intent intent =getIntent(); // 获取上一个Activity传递过来的Intent对象,getIntent方法是Activity自带的

int age = intent.getIntExtra("age",0); //"age"这个key要与发送的一致才能接收到数据,后面的0表示如果读取失败时,就使用这个默认值,保证获取到的数据不为空。

boolean sex = intent.getBooleanExtra("male",true); //后面的true表示如果读取失败时的默认值

b. 先获取Bundle对象,在通过Bundle对象获取数据

Intent intent = getIntent();

Bundle bundle = intent.getExtras();

String name = bundle.getString("name");

float price = bundle.getFloat("price", 0f);

注:不论前面用何种方式发送数据,接受数据时都可以用上面这两种方式的任意一种。

3. 传递自定义对象

首先,自定义对象必须是可序列化的,就是自定义对象必须是实现Serializable或者Parcelable(序列化细节,在此不展开了)。如下定义一个序列化对象User:

public class User implements Serializable{

    private String name;

    public User(String name){

        this.name = name;

    }

    public String getName() {

        return name;

    }

    publicvoid setName(String name) {

        this.name = name;

    }

}

发送对象时,可以这样:

User user = new User("Tom");

Intent intent = new Intent(SendActivity.this, ReceiveActivity.class);

Bundle bundle =  new Bundle();

bundle.putSerializable("user", user);

intent.putExtras(bundle);

startActivity(intent);

接收时,可以这样获取User对象:

Intent intent = getIntent();

User user= (User)intent.getSerializableExtra("user");

4. 返回Activity的数据传递

上面的方法都是说从Activity1跳到新建的Activity2怎么传递数据,如果现在要关闭Activity2并返回到Activity1,这时要带数据回Activity1怎么办呢?android给Activity提供了一套方法来实现这个目的。

1. 从Activity1跳转到Activity2:

Intent intent =new Intent();

intent = intent.setClass(Activity1.this, Activity2.class);

Bundle bundle =new Bundle();

bundle.putString("name", "GG");

intent.putExtras(bundle);

startActivityForResult(intent,1249); //只有这里不同。这里的1249表示请求码,可以随意设置一个整数,如果有多个跳转到不同Activity的startActivityForResult方法被调用时,为了区分,每个跳转都要设置不同的请求码。

2. 从Activity2返回数据到Aactivity1:

Intent intent =new Intent(Activity2.this, Aactivity1.class);

Bundle bundle =new Bundle();

bundle.putInt("resultData","Activity2返回的数据");

intent.putExtras(bundle);

this.setResult(RESULT_OK, intent); //RESULT_OK是返回代表处理结果的状态码

this.finish(); //关闭Activity2

3. 在Activity1中重写Activity的onActivityResault方法,并在方法里接收数据:

@override

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

    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode == 1249){//先判断请求码是否匹配

        switch(resultCode) {//根据状态码,处理返回结果

            case RESULT_OK:                         

                Bundle bundle =data.getExtras();//获得数据

                String result = bundle.getInt("resultData"); // 这个key要与Activity2中设置的一致

                break;

            ......

            default:break;                   

        }   

    }  else if(requestCode==xxxx){ //xxxx表示别的请求码

            ......

    }

}

3.2 通过全局对象传递数据

如果某些数据需要经常使用,要长时间驻留内存(例如用户的基本信息,比如用户id、用户名、头像等,在程序运行时要经常用到),一般这种数据定义为全局对象,将该对象定义在Application类中。因为Application代表App本身,在App初始化时就会被创建,并且在程序运行过程中一直都在,可以保证数据对象不会被轻易释放。首先需要自定义一个类,该类继承Android的Application类,例如命名为MyApplication类;然后在MyApplication中定义数据对象,在编写完自定义的MyApplication类后,还需要打开AndroidManifest配置文件,在<application>标签中指定全局类名name为MyApplication,这样App启动时系统会使用我们自定义的MyApplication类来创建Application对象。

public class User{

    private String name;

    public String getName(){

        return name; 

    }

    public void setName(String name){

        this.name= name; 

    }

}

public class MyApplication extends Application{

    private User user;

    public String getUser(){

        return this.user;

    }

    public void setUser(String name){

        this.user = new User();

        this.user.setName(name);

    }

}

public class Activity1 extends Activity{

    private MyApplication application;

    public void onCreate(Bundle savedInstanceState){

        ......

        application = (MyApplication) getApplication();   

        application.setUser("Mary"); 

    }

}

public class Activity2 extends Activity{

    private MyApplication application;

    private User user;

    protected void onCreate(Bundle savedInstanceState){

        ......

        application = (MyApplication) getApplication();     

        user= application.getUser();     

    }

}

这个在Activity1中初始化过的User对象,在任何可以获取Application对象的地方都可以再次获得这个User对象。

有些人说可以在Activity中使用静态变量来传递数据,个人建议尽量不要在此使用静态变量,这种操作很可能导致数据不一致,静态变量会打破对象的封装性。


5. Activity现场保存状态


一般情况下,调用Activity的onPause()和onStop()方法后,Activity实例其实仍然存在于内存中,Activity实例的所有信息和状态数据不会被销毁,当Activity重新回到运行态后,实例的状态数据都会得到保留。但是当系统内存不足时, 调用onPause()和onStop()方法后的Activity可能会在某个时刻被系统销毁,此时内存中没有该Activity的实例了,当用户重新打开这个Activity,就要新建一个实例,而这个实例就没有先前实例的状态数据。为了解决这个问题,我们可以在Activity被杀掉之前保存实例的当前状态,可以通过覆写Activity的onSaveInstanceState()方法来实现这个目的。

如果开发者没有覆写onSaveInstanceState()方法,此方法的默认实现会自动保存activity中的某些状态数据,比如activity中各种UI控件的状态。android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法,因此当activity被摧毁和重建时,这些UI控件会自动保存和恢复状态数据。比如EditText控件会自动保存和恢复输入的数据,而CheckBox控件会自动保存和恢复选中状态。开发者只需要为这些控件指定一个唯一的ID(通过设置android:id属性即可),剩余的事情就可以自动完成了。如果没有为控件指定ID,则这个控件就不会进行自动的数据保存和恢复操作。所以复写onSaveInstanceState方法,多用来保存Activity中的一些成员变量值。

如果我们复写了onSaveInstanceState方法,调用过程大致如下:

1. onSaveInstanceState(Bundle outState)方法有一个Bundle类型的参数outState,可以将Activity的状态数据存储到这个Bundle对象中,onSaveInstanceState方法会在系统销毁Activity前被系统回调,并且在回调onPause()或onStop()方法之前调用;

2. 当该activity在将来某个时刻被重建时,系统在回调onCreate(Bundle savedInstanceState)或者onRestoreInstanceState(Bundle savedInstanceState)方法时,我们就可以用传进来的Bundle参数savedInstanceState来恢复Activity的状态。

public class MyActivity extends Activity{

    private String restoreData;

    //恢复数据

    @Override public void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);

        if(savedInstanceState !=null) {             

            restoreData= savedInstanceState.getString("restoreData");//获取要恢复的数据

      }

        //恢复数据

        @Override

        public void onRestoreInstanceState(Bundle saveInstanceState){

            super.onRestoreInstanceState( saveInstanceState);         

            restoreData= saveInstanceState.getString("restoreData");

        }

        // 保存状态数据

        @Override

        protected void onSaveInstanceState(Bundle outState){

            super.onSaveInstanceState(outState);         

            outState.putString("restoreData", restoreData);

        }

        ……

}

注意:

1.  onSaveInstanceState()方法只适合保存Activity与用户交互的瞬态数据,比如UI控件的状态,成员变量的值等。若要向数据库中插入记录等这种保存持久化数据的操作,应该放在onPause()中做。

2. onSaveInstanceState()不是Activity生命周期的方法,不是什么情况下都会被调用的。系统调用onSaveInstanceState遵循一个重要原则:即当系统“未经你许可”时销毁了你的Activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。onSaveInstanceState方法在下面几种情况下会系统被调用:

1) 当用户按下HOME键时;

2) 长按HOME键,选择运行其他的程序时;

3) 按下电源按键(关闭屏幕显示)时;

4) 从Activity A中启动一个新的Activity时;

5) 屏幕方向切换时,例如从竖屏切换到横屏时。

相关文章

网友评论

    本文标题:Activity整理总结(1)

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