Android的活动介绍

Android的活动介绍

1. 活动是什么

活动(Activity)是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?

2. Intent 在活动中的使用

Intent 大致可分为两种:显示 Intent隐式 Intent

使用显示 Intent

Intent 有多个构造函数的重载,其中一个是Intent(Context packageContext,Class<?>cls)

  • 第一个参数Context要求提供一个启动活动的上下文
  • 第二个参数Class则是指定想要启动的目标活动

代码如下所示:

// 显示Intent
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);

使用隐式 Intent

隐式 Intent并不明确指出想要启动哪一个活动,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并找出合适的活动去启动。

用法:打开AndroidManifest.xml,添加如下代码:

<activity android:name=".inquiry_activity.SecondActivity">   
      <intent-filter>       
            <!-- 指定活动能响应的action和category -->        
            <action android:name="com.yezijie.ACTION_START"/>       
            <category android:name="android.intent.category.DEFAULT"/>   
      </intent-filter>
</activity>

此时在activity中的代码修改为:

// 隐式Intent
Intent intent = new Intent("com.yezijie.ACTION_START");
startActivity(intent);

只有和中的内容同时能够匹配上Intent中指定的action和category时,活动才能响应该Intent。上例中android.intent.category.DEFAULT是一种默认的category,若Intent中增加一个category:

// 隐式Intent
Intent intent = new Intent("com.yezijie.ACTION_START");
intent.addCategory("com.yezijie.MY_CATEGORY");//增加一个category
startActivity(intent);

此时在 标签中应再添加一个category声明:

<activity android:name=".inquiry_activity.SecondActivity">   
      <intent-filter>       
            <!-- 指定活动能响应的action和category -->        
            <action android:name="com.yezijie.ACTION_START"/>       
            <category android:name="android.intent.category.DEFAULT"/>   
            <!-- 新添加的category -->
            <category android:name="android.intent.category.MY_CATEGORY"/>   
      </intent-filter>
</activity>

更多隐式 Intent 的用法

使用隐式Intent,还可以启动其他程序的活动,比如调用系统的浏览器来打开某个网页:

// 隐式Intent:打开网页
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

比如调用系统的拨号界面:

// 隐式Intent:打开系统拨号界面
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);

在标签中再配置一个标签,用于更精确地指定当前活动能够响应什么类
型的数据。标签中主要可以配置以下内容。
android:scheme。用于指定数据的协议部分,如上例中的http部分、geo表示显示地理位置、tel表示拨打电话。
android:host。用于指定数据的主机名部分,如上例中的www.baidu.com部分。
android:port。用于指定数据的端口部分,一般紧随在主机名之后。
android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。

只有标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能够响应该Intent。不过一般在标签中都不会指定过多的内容,如上面浏览器示例中,其实只需要指定android:schemehttp,就可以响应所有的http协议的Intent了。

向下一个活动传递数据

Intent中提供了一系列 putExtra() 方法的重载,可把要传递的数据暂存在Intent中,启动了另一个活动后再从Intent中取出。如FirstActivity中传递一个字符串到SecondActivity中,FirstActivity中的代码为:

String data = "hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);

SecondActivity中的代码如下:

Intent intent = getIntent();
// String:getStringExtra(); int:getIntExtra(); 布尔型:getBooleanExtra() ...以此类推
String data = intent.getStringExtra("extra_data");
Log.d(TAG, data);

返回数据给上一个活动

Activity 中有个startActivityForResult() 方法用于启动活动,此方法在活动销毁时能返回一个结果给上个活动。startActivityForResult()方法接收两个参数:Intent请求码(用作回调时判断数据来源)。
修改FirstActivity中的代码如下:

Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);//请求码只要是唯一值就行了,这里传入1

接着在SecondActivity中添加返回数据:

Intent intent = new Intent();
intent.putExtra("data_return","hello FirstActivity");
setResult(RESULT_OK,intent);
finish();//销毁当前活动

SecondActivity被销毁后回调上一个活动的onActivityResult()方法,因此还需要在FirstActivity中重写此方法来得到返回数据:

/** 
 * 处理得到的返回数据 
 * @param requestCode  启动活动时传入的请求码
 * @param resultCode  返回数据时传入的处理结果
 * @param data  携带返回数据的Intent 
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {    
       switch (requestCode){       
            case 1:           
                 if (resultCode == RESULT_OK){                
                     String returnedData = data.getStringExtra("data_return");           
                     Log.d(TAG, returnedData);           
                 }            
                 break;                
            default:           
                 break;   
       }
}

若在SecondActivity是通过按下Back键回到FirstActivity,可以在SecondActivity中重写onBackPressed()方法:

@Override
public void onBackPressed() {    
     Intent intent = new Intent();
     intent.putExtra("data_return","hello FirstActivity");
     setResult(RESULT_OK,intent);
     finish();
}

3. 活动的生命周期

返回栈

Android是使用任务(Task)来管理活动的,一个任务就是一组存放在里的活动的集合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back 键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

返回栈工作示意图

活动状态

每个活动在其生命周期中最多可能会有4种状态

  • 运行状态
    当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。

  • 暂停状态
    当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一 个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。

  • 停止状态
    当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。

  • 销毁状态

    当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。

活动的生存期

Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节。

  • onCreate()。每个活动中都重写了这个方法,它会在活动第一次被创建的时候调用。在这个方法中完成活动的初始化操作,比如说加载布局、绑定事件等。

  • onStart()。这个方法在活动由不可见变为可见的时候调用。

  • onResume()。这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。

  • onPause()。这个方法在系统准备去启动或者恢复另一个活动的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。

  • onStop()。这个方法在活动完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。

  • onDestroy()。这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。

  • onRestart()。这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。

以上7个方法中除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为3种生存期

  • 完整生存期。活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期。
  • 可见生存期。活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。
  • 前台生存期。活动在onResume()方法和onPause()方法之间所经历的,就是前台生存期。

活动的生命周期

活动被回收

Activity 中提供了一个 onSaveInstanceState()回调方法,这 个方法会保证一定在活动被回收之前调用onSaveInstanceState()方法会携带一个 Bundle 类型的参数,Bundle 提供了一系列的方法用于保存数据,如用 putString()方法保存字符串,用 putInt()方法保存整型数据, 以此类推。

每个保存方法需要传入两个参数,

  • 第一个参数是,用于后面从 Bundle 中取值,

  • 第二个参数是真正要保存的内容

在Activity 中添加如下代码就可以将临时数据进行保存:

@Override
protected void onSaveInstanceState(Bundle outState) {    
    super.onSaveInstanceState(outState);
    String tempData = "Something you just typed";
    outState.putString("data_key", tempData);
}

Activity 被回收后的恢复操作,修改其onCreate()方法,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) { 
        // 若不为空,取出相应的数据
        String tempData = savedInstanceState.getString("data_key"); 
        Log.d(TAG, tempData);
    }
}
注:

Intent还可以结合Bundle一起用于传递数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标活动之后先从Intent中取出Bundle,再从Bundle中一一取出数据。

活动的启动模式

启动模式一共有4种,分别是 standardsingleTopsingleTasksingleInstance,可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式。

standard

standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。在 standard 模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用 standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
standard模式示意图

singleTop

当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。不过当Activity并未处于栈顶位置时,这时再启动Activity,还是会创建新的实例的。

singleTop模式示意图

singleTask

当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。

singleTask模式示意图

singleInstance

singleInstance模式应该算是4种启动模式中最特殊也最复杂的一个了,不同于以上3种启动模式,指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的 taskAffinity,也会启动一个新的返回栈)。假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一 个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。

singleInstance模式示意图

知晓当前是在哪一个活动

新建一个BaseActivity类,然后让BaseActivity继承自AppCompatActivity,并重写onCreate()方法,如下所示:

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity", getClass().getSimpleName());
    }
}

getClass().getSimpleName()获取了当前实例的类名。接下来我们需要让BaseActivity成为Activity项目中所有活动的父类。

随时随地退出程序

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

public class ActivityCollector {

    public static List<Activity> activities = new ArrayList<>();
    
    public static void addActivity(Activity activity) {
    	activities.add(activity);
    }
    
    public static void removeActivity(Activity activity) {
    	activities.remove(activity);
    }
    
    public static void finishAll() {
        for (Activity activity : activities) {
            if (!activity.isFinishing()) {
            	activity.finish();
            }
        }
        activities.clear();
    }
}

通过一个List来暂存活动,然后提供了一个addActivity()方法用于向List中添加一个活动,提供了一个removeActivity()方法用于从List中移除活动,最后提供了一个finishAll()方法用于将List中存储的活动全部销毁掉。

接下来修改BaseActivity中的代码

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity", getClass().getSimpleName());
        ActivityCollector.addActivity(this);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

不管你想在什么地方退出程序,只需要调用ActivityCollector.finishAll()方法就可以了。

当然你还可以在销毁所有活动的代码后面再加上杀掉当前进程的代码,以保证程序完全退出,杀掉进程的代码如下所示:

android.os.Process.killProcess(android.os.Process.myPid());

其中,killProcess()方法用于杀掉一个进程,它接收一个进程id参数,可以通过myPid()方法来获得当前程序的进程id。需要注意的是,killProcess()方法只能用于杀掉当前程序的进程,不能使用这个方法去杀掉其他程序。

启动活动的最佳写法

动活动的方法:通过 Intent 构建出当前的“意图”,然后调用 startActivity()startActivityForResult()方法将活动启动起来,如果有数据需要从一个活 动传递到另一个活动,也可以借助 Intent 来完成。假设 SecondActivity 中需要用到两个非常重要的字符串参数,在启动 SecondActivity 的时候必须要传递过来,那么我们很容易会写出如下代码:

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("param1", "data1");
intent.putExtra("param2", "data2"); 
startActivity(intent);

但此时 SecondActivity 并不是由你开发的,但现在你负责的部分需要有启动SecondActivity 这个功能,而你却不清楚启动这个活动需要传递哪些数据。这时无非就有两种办法,一个是你自己去阅读 SecondActivity 中的代码,二是询问负责编写 SecondActivity的同事。你会不会觉得很麻烦呢?其实只需要换一种写法,就可以轻松解决掉上面的窘境。

修改 SecondActivity 中的代码,如下所示:

public class SecondActivity extends BaseActivity {
    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);
    }
……
}

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

这样写的好处在哪里呢?最重要的一点就是一目了然,SecondActivity 所需要的数据全部都在方法参数中体现出来了,这样即使不用阅读 SecondActivity 中的代码,或者询问负责编写 SecondActivity 的同事,你也可以非常清晰地知道启动 SecondActivity 需要传递哪些数据。

另外,这样写还简化了启动活动的代码,现在只需要一行代码就可以启动 SecondActivity, 如下所示:

SecondActivity.actionStart(FirstActivity.this, "data1", "data2");


您的关注和点赞是我分享的动力,如有帮助请勿吝啬!ヽ( ̄▽ ̄)ノ



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值