Activity

什么是Activity

Activity是Android中包含了用户界面的应用组件。是应用和用户互动的入口。

为什么要使用Activity

用户与移动应用的互动并不是都从同一个位置开始的,例如正常启动一个邮件应用时会显示邮件列表,但从社交媒体应用启动邮件应用时,则可能会直接进入邮件的编辑页面。当一个应用调用另一个应用时,调用方会调用另一个应用的Activity,而不是整个应用。

如何使用Activity

新建Activity并声明就可以直接使用Activity。

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN">

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

activity声明中唯一的必要属性是android:name ,用于指定Activity的类名称。其他属性参考

Activity的生命周期

Activity的生命周期是什么

当用户浏览、退出和返回到应用中时,应用中的Activity实例会在其生命周期的不同状态间转换。Activity类会提供很多回调方法,这些回调方法会让Activity知晓某个状态已经改变:系统正在创建、停止或恢复某个Activity,或者再在销毁改Activity所在的进程。

生命周期的回调方法与活动的生存周期

  • onCreate():在Activity第一次被创建的时候调用。一般在此回调方法中完成一些初始化操作,比如加载布局、绑定事件等。

  • onStart():在Activity由不可见变为可见时调用。

  • onResume():在Activity准备好和用户进行交互的时候调用。此时Activity一定位于栈顶,处于运行状态。

  • onPause():在系统准备去启动或恢复另一个Activity时调用。通常在此方法中将一些消耗CPU的资源释放掉,保存关键的数据。此方法执行的速度要快,否则会影响新的栈顶Activity的使用。

  • onStop():在Activity完全不可见时调用。

  • onDestory():在Activity销毁之前调用,此时活动处于销毁状态。

  • onRestart():在Activity由停止状态变为运行状态之前调用。

分为三个生存期:

  1. 完整生存期:onCreate() → onDestory()

  2. 可见生存期:onStart() → onStop()

  3. 前台生存期:onResume() → onPause()

Activity的生存期

Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合。

Activity的活动状态

  • 运行状态

Activity位于返回栈的栈顶,此时处于运行状态。

  • 暂停状态

Activity不再位于栈顶,但仍然可见时,此时处于暂停状态。

  • 停止状态

Activity不再处于栈顶位置,并且完全不可见时,此时处于停止状态。

  • 销毁状态

Activity从返回栈中被移除,此时处于销毁状态。

Activity生命周期过程

新建Normal Activity 和DialogActivity(对话框Activity)。

MainActivity中由两个按钮分别启动Normal Activity和Dialog Activity。

当应用程序启动时,MainActivity执行onCreate() → onStrat() → onResume()

D/MainActivity: onCreate: 
D/MainActivity: onStart: 
D/MainActivity: onResume: 

从MainActivity启动NormalActivity时,MainActivity执行onPause() → onStop()

D/MainActivity: onPause: 
D/MainActivity: onStop: 

从NormalActivity返回MainActivity时,MainActivity执行onRestart() → onStart() → onResume()

D/MainActivity: onRestart: 
D/MainActivity: onStart: 
D/MainActivity: onResume: 

从MainActivity启动DialogActivity时,MainActivity执行onPause()

D/MainActivity: onPause: 

从DialogActivity返回MainActivity时,MainActivity执行onResume()

D/MainActivity: onResume: 

从MainActivity退出应用时,MainActivity执行onPause() → onStop() → onDestory()

D/MainActivity: onPause: 
D/MainActivity: onStop: 
D/MainActivity: onDestroy: 

Activity被回收

当Activity进入停止状态后,是有可能被系统回收的。如果Activity被系统回收了,那就不会执行onRestart()方法,而是会执行OnCreate()方法,这种情况下,Activity会被重新创建。

但是在Activity中保存的数据会因此丢失,Android给我们提供了onSaveInstanceState()回调方法,该方法可以保证Activity被回收之前一定会被调用,可以通过此方法来解决Activity被回收时临时数据丢失的问题。

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    String data = editText.getText().toString();
    outState.putString("data",data);

}

在onCreate回调中判断Bundle是否为空,不为空则取出里面的值。

if (savedInstanceState != null) {
    String data = savedInstanceState.getString("data");
    editText.setText(data);
}

活动的启动模式

四种启动模式:

  • standard

Activity默认的启动模式。在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。无论这个Activity在栈中是否存在,每次启动都会创建该活动的一个新的实例。

  • singleTop

在该模式下,当活动的启动模式指定为singleTop,在启动Activity时如果发现栈顶已经是该活动,则直接使用它,不再创建新的Activity实例。若创建的活动不在栈顶,则还是会创建新的Activity实例。

  • singleTask

在该模式下,每次启动该Activity时系统会首先检查在返回栈中是否存在该Activity的实例,若存在则直接使用,并把在该Activity之上的Activity全部出栈,如果没有发现则创建一个新的实例。

  • singleInstance

在该模式下,指定为singleInstance模式的Activity会启动一个新的返回栈来管理这个活动。

singleTask模式指定不同的taskAffinity也会启动新的返回栈

在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。

后面单独写一篇关于 Activity 启动模式的文章加深理解,在此简述

使用Intent

使用Intent可以进行活动的跳转。Intent是Android程序中各组件进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。一般用于启动活动、服务以及发送广播等。

显式Intent

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

隐式Intent

隐式Intent不明确指明要启动的Activity,而是指定action和category等信息,交由系统分析这个Intent,并找出合适的Activity去启动。

通过在目标Activity的<activity> 标签下配置的内容,可以指定当前Activity能够响应的action和categroy。

        <activity android:name=".SecondActivity" android:exported="true">
            <intent-filter>
                <action android:name="com.example.intenttest.ACTION_START">
                <category android:name="android.intent.category.DEFAULT">
            </category></action></intent-filter>
        </activity>

修改源Activity中按钮的点击事件。

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.intenttest.ACTION_START");
                startActivity(intent);
            }
        });

只有<action><category>中的内容同时匹配Intent中指定的action和category时,这个Activity才能相应该Intent。android.intent.category.DEFAULT是一种默认的category,在调用startActivity()时会自动将这个category添加到Intent中。

隐式Intent的更多用法

  • 启动浏览器
//启动网页
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);

http启动网页,tel表示拨打电话,geo表示显示地理位置。

从一个Activity启动另一个应用的Activity的三种方法

  • 通过自定义action启动
//                从一个Activity启动另一个应用的Activity的方法
//                第一种启动方法
                Intent intent = new Intent();
                intent.setAction("Activity.C");
                startActivity(intent);

在另一个应用的AndroidManifest文件中配置

<activity android:name=".CActivity">
            <intent-filter>
                <action android:name="Activity.C">
                <category android:name="android.intent.category.DEFAULT">
            </category></action></intent-filter>
        </activity>
  • 通过在Intent中指定包名和类名来查找
//                第二种启动方法
                ComponentName componentName = new ComponentName("com.example.fourcomponent","com.example.fourcomponent.CActivity");
                Intent intent = new Intent();
                intent.setComponent(componentName);
                startActivity(intent);
  • 第三种方法通过scheme启动
//                第三种启动方法
                Uri uri = Uri.parse("app://activity.c");
                Intent intent = new Intent("Activity.C",uri);
                startActivity(intent);

在另一个应用的AndroidManifest文件中配置

<activity android:name=".CActivity">
    <intent-filter>
        <action android:name="Activity.C">
        <action android:name="android.intent.action.VIEW">
        <category android:name="android.intent.category.DEFAULT">
        <data android:scheme="app" android:host="activity.c">
    </data></category></action></action></intent-filter>
</activity>

传递数据

  • 向下一个Activity传递数据

把数据暂存在Intent中,启动了另一个Activity后,再把数据取出。

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String data = "Hello SecondActivity";
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                intent.putExtra("extra_data",data);
                startActivity(intent);
            }
        });
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
        Log.d("SecondActivity", data);
    }
  • 返回数据到上一个Activity

startActivityForResult()方法也是用于启动活动,但这个方法期望在Activity销毁的时候能够返回一个结果给上一个活动。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                startActivityForResult(intent,1);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case 1:
                if (resultCode == RESULT_OK){
                    String returnData = data.getStringExtra("extra_data");
                    Log.d(TAG, "onActivityResult: " + returnData);
                }
                break;
            default:
        }
    }
}

SeconActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);

    findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent();
            intent.putExtra("extra_data","hello mainactivity");
            setResult(RESULT_OK,intent);
            finish();
        }
    });
}
D/MainActivity: onDestroy: 
D/MainActivity: onStart: 
D/MainActivity: onActivityResult: 
D/MainActivity: onResume: 
D/SecondActivity: onStop: 
D/SecondActivity: onDestroy: 

手动调用finish()方法的原因:onActivityResult()方法会在SecondActivity被销毁前执行。因此要在onBackPressed()回调中手动调用finish()方法。

Activity常用技巧

知晓当前是哪一个活动

新建一个普通的Java类,BaseActivity继承自AppCompatActivity,并重写onCreate()方法。

public class BaseActivity extends AppCompatActivity{

    private static final String TAG = "BaseActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
        ActivityControl.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityControl.removeActivity(this);
    }
}

随时随地退出程序

创建一个专门的集合类对所有的活动进行管理就可以了。

新建一个ActivityCollector类作为活动管理器。

public class ActivityControl {

    private static final String TAG = "ActivityControl";
    public static List<activity> activities = new ArrayList<>();

    public static void addActivity(Activity activity) {
        Log.d(TAG, "addActivity: ");
        activities.add(activity);
    }

    public static void removeActivity(Activity activity){
        Log.d(TAG, "removeActivity: ");
        activities.remove(activity);
    }

    public static void finishAll(){
        for (Activity activity : activities){
            if (!activity.isFinishing()){
                Log.d(TAG, "finishAll: " + activity.getLocalClassName());
                activity.finish();
            }
        }
    }
}

在BaseActivity中,使用addActivity()方法将Activity添加到List中,removeActivity()用于从List中移除活动,finishAll()用于将存储的活动全部销毁。

public class BaseActivity extends AppCompatActivity{

    private static final String TAG = "BaseActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
        ActivityControl.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityControl.removeActivity(this);
    }
}

最后把所有的Activity的父类更改为BaseActivity,在程序中的任何地方,只需要调用ActivityCollector.finishAll()就可以直接退出程序。

启动活动的最佳写法

在SecondActivity中添加一个actionStart()方法,在这个方法中完成Intent的构建,另外所有SecondActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后存储到Intent中,最后调用startActivity()方法启动SecondActivity。

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }

    public static void actionStart(Context context,String data1,String data2){
        Intent intent = new Intent(context,SecondActivity.class);
        intent.putExtra("param1",data1);
        intent.putExtra("param2",data2);
        context.startActivity(intent);
    }
}

在其他Activity,只要使用actionStart()方法就可以直接启动SecondActivity。

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SecondActivity.actionStart(MainActivity.this,"data1","data2");
            }
        });
```</activity></category></action></intent-filter></activity></activity></activity>