Android之旅1-Android四大组件之Activity篇

关于为什么要写属于自己的博客,我在前一篇文章中也提到了。主要是归纳自己这一个时期所学到的知识,帮助自己更好的理解,同时一旦忘了某一处,还可以有个地方快速的查到。我也打算在写这些博客期间,将官方文档回顾一遍。然后,还有自己的Java方面也会一起回顾一遍,毕竟自己的Java功底薄弱啊。虽然,我的英文水平很渣,但我相信我会坚持下去的。总之,写博客是一种好的习惯,希望自己可以一直坚持下去。

本篇作为Android的开篇,自然就会写最常见的组件Activity了。

一.Activity的基本使用
1. 创建Activity
2. 开启一个Activity
二.Activity间的数据传递过程
1. 向下一个Activity传递数据
2. 返回数据给上一个Activity
三.Activity的生命周期
1. Activity的生命周期的回调方法以及回调过程
2. 保存Activity的状态
四.Activity的启动模式

什么是Activity,你可以翻译成活动,但我总觉得这么翻译有些不妥,还是不能翻译出官方的味道。有的翻译成视图,这种感觉还会好一些。
那么还是让我们看一下官方文档关于Activity的描述:

  • Activity#####

An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map. Each activity is given a window in which to draw its user interface. The window typically fills the screen, but may be smaller than the screen and float on top of other windows.

一个Activity是一个应用组件,它提供了一块屏幕,方便与用户之间进行某些交互。像,打电话,拍照,发送邮件,或者是浏览地图。每一个Activity被给予一块窗口,去绘制它自己的用户界面。这个窗口可以填满整个屏幕,但是也可以是比屏幕更小或者悬浮在其他窗口之上。
上面的官方文档的介绍已经很清楚明了了,下面看一下如何使用Activity。

  • 创建Activity##

To create an activity, you must create a subclass of Activity(or an existing subclass of it). In your subclass, you need to implement callback methods that the system calls when the activity transitions between various states of its lifecycle, such as when the activity is being created, stopped, resumed, or destroyed.

首先要新建一个Activity。并且继承Activity,实现其中需要的回调方法,其中最重要的两个回调方法是onCreate()onPause()

onCreate()
You must implement this method. The system calls this when creating your activity. Within your implementation, you should initialize the essential components of your activity. Most importantly, this is where you must callsetContentView() to define the layout for the activity's user interface.

onCretate()是一个必须要实现的方法,系统在创建你的Activity时回调这个方法。在这里你可以初始化你的Activity所必需的组件。最重要的是,你需要调用setContentView()方法,来规范用户交互界面。

onPause()
The system calls this method as the first indication that the user is leaving your activity (though it does not always mean the activity is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).

onPause()是在用户首次离开你的Activity时调用的。所以在这个方法中,你应该保留用户的数据,给用户一个好的交互体验。

  • 实现UI界面###

完成用户界面的绘制,通常最常用的方式就是在一个xml文件中定义好Activity所需要的布局,然后在Activity的onCreate()方法中使用setContentView()方法就可以让一个布局与Activity关联起来。

  • 在清单文件中进行声明###

每一个Activity都应该在manifest文件中定义,也就是只有在这个清单文件中有过定义,系统才会识别出你是一个Activity。下面是具体需要在什么地方定义的结点。<activity android:name=".ExampleActivity"/>。这句话就声明ExampleActivity是一个Activity,前面的点.代表省略的包名。

<manifest ... >
    <application ... > 
         <activity android:name=".ExampleActivity" />  
         ...
    </application ... > 
         ...
</manifest >
  • 使用 intent filters####

使用下面的intent-filter声明一个当前的Activity是一个主活动,当启动应用时首先加载的页面就是当前这个Activity。

 <activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> 
      <intent-filter>  
            <action android:name="android.intent.action.MAIN" />   
            <category android:name="android.intent.category.LAUNCHER" />  
</intent-filter>
</activity>

总结一下如何使用Activity:

  1. 新建一个Activity并且继承Activity。
  2. 实现其中想实现的回调方法。共有七种回调方法,分别是:onCreate()、onRestart()、onStart() 、onResume()、onPause()、onStop()、onDestory()。
  3. 使用setContentView()方法将你的布局与Activity之间进行绑定。而布局一般都是以xml文件的形式进行定义的。这里也就涉及到了Android中的控件。
  4. 在manifest文件中进行注册,Android四大组件都必须在清单文件中进行注册,同时BroadcastReceiver也支持动态注册。
  • 开启一个Activity##

上面讲到了如何使用Activity,但是仅仅有一个Activity是不是也太单调了。而我们现在市面上的应用都是有好多个Activity所组成。所以这里就要用到Activity之间的跳转。而Activity之间的跳转就要用到上面提到过的Intent了。

官方文档中关于Intent的解释为:

You can start another activity by calling startActivity(), passing it an Intent that describes the activity you want to start. The intent specifies either the exact activity you want to start or describes the type of action you want to perform (and the system selects the appropriate activity for you, which can even be from a different application). An intent can also carry small amounts of data to be used by the activity that is started.

你可以启动一个其他的activity通过调用, 并传递一个Intent,它用于描述Activity。 intent指定了你想要启动的Activity,或者指定了你想展现的动作(系统帮你选择合适的Activity,它可能来自于其他的程序)。 intent也可以携带比较小量的数据,用于启动Activity。

从上面可以看出Intent分为两种类型,即显式Intent和隐式Intent。

显式Intent:在你自己的应用中,你经常会简单地启动一个已知的Activity, 通过创建一个明确的intent。这个intent指定了Activity的类名。如下为启动一个名为SignInActivity的Activity:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

隐式Intent:当你的应用中没有相应的功能时,你就可以通过隐式Intent来调用系统的或者是设备上其他的应用来完成相应的功能。类似的如:发送邮件,发送短信,拨打电话。如下为调用手机中具有浏览网页功能的应用访问一个网页:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
startActivity(intent);

这就是隐式Intent最有价值的地方,你可以创建一个Intent来描述你想要做什么,系统会为你从其他的应用中选择合适的Activity来处理。

  • Activity间进行数据传递###

向下一个Activity传递数据
Intent中提供了的一系列的putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,启动另外一个Activity后,再从Intent中取出数据就可以了。

Intent一系列的putExtra()方法

比如在FirstActivity中将一个字符串传递到SecondActivity中,可以如下操作:

1.在FirstActivity中将数据保存在Intent中。

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

2.在SecondActivity中取出Intent中的数据,getIntent()方法。

Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity", data);
  • 如果在上一个Activity传递过来的是字符串,在下一个Activity中取出时就应该用getStringExtra()。上一个Activity传递过来的是整型数据,在下一个Activity中取出时就应该用getIntExtra()。以此类推。

返回数据给上一个Activity
如何返回数据给上一个Activity呢?这里就用到了startActivityForResult(),这个方法与startActivity()一样,都可以启动另一个Activity。但不同的是通过startActivityForResult()来启动Activity,你便可以重写onActivityResult()方法来得到上一个Activity中你想得到的数据。具体看代码:

1.在FirstActivity中使用startActivityForResult()跳转到SecondActivity。这个方法接受两个参数,其中第二个参数为请求码,需要传入唯一值。

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);

2.在SecondActivity中仍然通过putExtra()方法,将数据存到Intent中。这里调用了一个setResult()方法,这个方法很重要,专门用来返回数据给上一个活动。其中的第一个参数为结果码。

Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();

3.在FirstActivity中重写onActivityResult()方法,将从SecondActivity中传递过的数据取出来。由于可能在一个Activity中调用startActivityForResult()去启动很多不同的Activity,因此需要检查请求码requestCode,判断数据的来源。

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {  
 super.onActivityResult(requestCode, resultCode, data);   
 switch (requestCode){    
    case 1:         
         if (requestCode==RESULT_OK){      
            String returnData = data.getStringExtra("data_return");                
            Log.d("FirstActivity", returnData);   
         }         
         break;     
    default:         
         break;  
  }
}

结束一个Activity##

下面来说说如何结束一个Activity。

You can shut down an activity by calling its finish() method. You can also shut down a separate activity that you previously started by calling finishActivity()

你可以调用finish()方法结束一个Activity,你也可以调用finishActivity()方法来结束之前开启的Activity。但是,一般情况下都不会这么做,因为Activity有其自己的生命周期。掌握这些生命周期的回调方法就可以轻松实现Activity从创建到销毁的整个过程。

Activity的生命周期##

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

1.onCreate()
当Activity被第一次创建时会调用此方法,你需要在此完成所需要的的初始化操作,加载布局或者绑定数据。

2.onStart()
这个方法在Activity可见之前,也就是活动即将可见,但在onCreate()方法后调用。

3.onResume()
这个方法在Activity准备好和用户进行交互时调用,此时的Activity会处于栈顶的位置,即Activity已经完全可见。

4.onPause()
这个方法在系统准备去启动或者恢复另一个Activity的时候调用。在这里应该保存一些关键的数据,停止动画,或者将一些消耗CPU的资源释放掉。这个方法执行的一定要快,因为另一个Activity的onResume()方法要在这一个Activity的onPause()执行完后才会执行。

5.onStop()
这个方法在完全不可见的时候调用。也就是当另一个Activity完全覆盖了当前的Activity就会调用此方法。当不是完全的覆盖时,例如是一个对话框式的Activity覆盖当前的Activity,则只会执行onPause()方法,而不会执行onStop()方法。

6.onDestroy()
这个方法在Activity被销毁之前调用,之后活动状态变为销毁的状态。是Activity会接受的最后一个回调方法。它的回调是因为调用了finish()方法,或者是系统为了节省空间销毁了它。这两种场景可以用onFinishing()方法判断出来。

7.onRestart()
这个方法在Activity被重新启动之前会调用。也就是在停止状态变为运行状态之前会调用。

其中onPause(),onStop(),onDestroy()这三个回调方法之后,是可以被系统杀死的,也就是回收资源。因为onPause()方法这三个中最先执行,onPause()方法是有保证在进程被杀死之前调用的。在系统内存紧急的情况下,onStop(),onDestroy()方法可能不会执行。因此,就需要你在onPause()方法中保存那些重要的数据,如:用户的输入。但在这个方法中不能做过多的操作,因为另一个Activity的onResume()方法要在这一个Activity的onPause()执行完后才会执行,如果在这里做了大量的操作,是会带来极差的用户体验的,因为用户的等待时间变长了。

以上的七个方法除去onRestart()之外,是两两相对的,从而将Activity分为三种生存期。

1.entire lifetime
即完整生存期,就是Activity在onCreate()和onDestroy()方法之间所经历的过程。一般情况下,一个Activity要在onCreate()方法中完成各种出的初始化的操作,而在onDestroy()方法中完成资源的释放。

2.visible lifetime
即可见生存期,就是Activity在onStart()和onStop()方法之间所经历的过程。在这一时期,Activity对于用户是可见的,并且可以与之交互。在这两个方法之间,你可以在Activity中维持你想向用户展示的资源。比如:你可已在onStart()方法中注册一个广播,来监听UI的变化,并且在onStop()方法中进行取消注册。

3. foreground lifetime
即前台生存期,就是Activity在onResume()和onPause()方法之间所经历的过程。在这一过程中,Activity是位于屏幕上可见的,并且可以获得输入的焦点,也就是Activity处于运行的状态。此时,Activity是可以与用户进行交互的,我们平时接触的最多的也就是这一状态下的Activity了。同时,处于前台的Activity可能经常性的改变状态,在这两个方法中维护数据一定要是轻量级的,以避免用户的等待。

以下是官方文档给出的一张生命周期图,可以帮助我们更好的理解Activity的生命周期。

Activity的生命周期图

Activity完整的生命周期回调方法

public class ExampleActivity extends Activity { 
    @Override    
    public void onCreate(Bundle savedInstanceState) {     
        super.onCreate(savedInstanceState);  
       // The activity is being created.   
 }  
     @Override   
     protected void onStart(){       
         super.onStart();     
        // The activity is about to become visible.  
  }    
     @Override   
     protected void onResume(){  
         super.onResume();    
        // The activity has become visible (it is now "resumed").   
 }    
     @Override 
     protected void onPause(){       
         super.onPause();       
        // Another activity is taking focus (this activity is about to be "paused"). 
   }   
     @Override  
     protected void onStop() {   
         super.onStop();     
        // The activity is no longer visible (it is now "stopped")   
 }  
     @Override    
     protected void onDestroy() {   
        super.onDestroy();     
       // The activity is about to be destroyed.   
 }
}

保存Activity的状态###

如何在Activity的状态改变后保存数据呢?

下面官方文档给出了图告诉我们如何进行Activity状态改变后如何进行保存数据的操作。

Activity的状态保存

在一个Activity执行onPause()和onStop()方法后,Activity的状态仍然会保留。所以此时,再回到应用的前台,所有的状态就会恢复。但是,系统一旦回收内存,就有可能造成Activity被销毁了。此时,回到前台,就会重新执行onCreate()方法或者onRestart()方法,所以其中的数据也就都没有了。所以要想要这种情况之下进行重要数据的保存,就要用到onSaveInstanceState()这一回调方法了。但是,这一方法并不保证会在Activity销毁前一定执行,官方文档给出的解释是:用户可能通过Back键显式的离开你的Activity。所以,要调用onSaveInstanceState()就要在onStop()或者onPause()之前调用。

同时,官方文档还提到了一点。即使你没有实现这一方法,Android中几乎所有的组件都有默认还原数据的功能。但前提是你给了这个组件一个独一无二的id,即android:id。如果你不想使用这个功能,可以显式的关闭它,通过 设置android:saveEnabled属性为 "false",或者调用setSaveEnabled()方法。

onSaveInstanceState()这一方法在Activity被销毁之前调用,这个方法携带一个Bundle类型的参数,并且是以键值对方式保存数据的,如: putString()和[putInt()。

下面看一下具体的实现:

1.在MainActivity中调用onSaveInstanceState(),进行数据的保存。

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

2.如何进行数据的恢复呢?其实onCreate()方法带有一个Bundle类型的参数,这个方法一般为null,但是Activity被系统回收之前有通过onSaveInstanceState()保存数据的话,这个参数就会带有之前保存的所有数据。当然,你也可以通过onRestoreInstanceState()方法来取回数据,它同样也携带了一个Bundle类型的参数。

@Override
protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {  
        String data =  savedInstanceState.getString("data");  
        Log.d("MainActivity", data);}
}

官方文档还给出一点检验Activity的还原能力的方法,就是旋转手机屏幕的方向,观察保存的数据是否能够还原。其中说到这种方法很重要,因为用户会经常性的做此动作。反正我是不会经常性的旋转屏幕,它既然提到了,就了解一下吧。

Activity之间的协同###

在官方文档的最后,提到了如何协调各个Activity之间的关系。其实也就是遵循Activity的生命周期。它还给出一个例子:在Activity A 中开启Activity B的顺序是什么?

1.先执行Activity A的onPause()方法。
2.然后Activity B 的onCreate(),onStart(),onResume()方法会接连的执行,Acitivity B此时已经获取了焦点。
3.如果此时Activity A不可见了,它的onStop()方法会得到执行。

Activity的启动模式##

在Android启动模式一共有四种,分别是standrd,singleTop,singleTask,singleInstance。可以在AndroidManifest.xml中给<activity>标签指定android:launchMode属性来选择相应的启动模式。你也可以通过Intent携带一个Flag来指定你通过startActivity()方法来启动的另一个Activity的启动模式。当然,另一个Activity也可以在AndroidManifest.xml中指定自己的启动模式。那你就会问了一个Activity出现了两种启动模式,那这个Activity执行谁的命令啊!官方文档给出的答复是,自己在AndroidManifest.xml中定义的失效,上一个Activity从Intent中携带的Flag是什么模式,这一个Activity就执行什么模式。

1.standrd
这是Activity的默认启动模式,Android使用返回栈来管理Activity。在这种模式下,系统不会在意这个Activity是否在返回栈存在,每次启动都会创建该Activity的一个实例。假如在Activity A 中启动Activity A,就会一直创建实例。在栈中就是 A-A-A,一直是叠加的。所以现在想要退出Activity,就需要按三次Back键。

2.singleTop
这种启动模式,在启动Activity时会判断栈顶是否已经存在了该Activity,如果存在了该Activity就不会重复的创建实例出来,而是直接复用栈顶的Activity。还是上面的例子,在栈中的样子是A,对,仅创建一次。所以,只需要按一次Back键就可以退出Activity。如果现在栈中是这样的A-B,Activity B在栈顶,我在Activity B中启动Activity A。此时栈中的情景是A-B-A。又创建了一个A的实例,因为A不在栈顶。

3.singleTask
这种启动模式,在启动Activity时会判断栈中方是否已经存在了该Activity,如果栈中存在,就会直接使用该实例,并将此Activity之上的所有Activity都出栈。也就是说此时栈中只能有这一个实例。如果栈中没有实例,就会新建一个Activity。

4.singleInstance
这种启动模式是这四种当中最为特殊和复杂的一种了。声明为singleInstance模式的Activity会单独存在在一个返回栈中,不管是哪一个应用程序来访问此Activity都是共用的同一个返回栈,也就解决了共享Activity实例的问题。

写在最后##

关于Activity相关的知识讲到这里也就算是基本完成了,这其中也仍有许多的不足,也有很多的知识没能吃透。但好在,我是看着官方文档写出的这篇文章,还是有一点进步的。其中仍然还有些知识没写到这里,其中的部分知识的理解上也存在着偏差,这些就要留在以后来解决。以后,如果我对某个知识点有新的认识,仍然会更新出来。本篇关于Activity的总结到此也就结束了。

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

推荐阅读更多精彩内容

  • Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等...
    岳小川阅读 480评论 0 3
  • 转载注明出处://www.greatytc.com/p/c2c2ee4eb48a 1. 简介 本篇不针对于...
    王三的猫阿德阅读 2,098评论 2 5
  • Activity https://developer.android.com/guide/components/a...
    XLsn0w阅读 698评论 0 4
  • 我是一个幸福的人,拥有数不清的小幸福,它们伴着我成长,让我的时时刻刻面带微笑,也使我的人生变得很有意义。 ...
    Dawn慧阅读 332评论 0 0
  • 从武汉出发来到上海,准备开启新的征程。一路上,模模糊糊,在思考,在恍惚,在探寻。前面的路,充满希望,也充满期...
    陳毓沣阅读 117评论 0 0