Android 四大组件——Activity

一、简介

Activity,安卓四大组件之一。

每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。

Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈里的Activity集合,这个栈被称作返回栈,栈(堆栈)是一种先进后出的数据结构,这里顺便提一下另一种常见的数据结构:队列,队列是一种先进先出的数据结构。

当启动一个新的Activity时,它会被放入返回栈中,并处于栈顶的位置。每当我们按下Back键或调用activity的finish()方法去销毁一个活动时,处于栈顶的Activity会出栈,这时前一个入栈的Activity就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity给用户。

二、知识点

1、生命周期

Activity生命周期是每一个Android开发者都必须掌握的,当我们深入理解活动的生命周期之后,就可以写出更加连贯流畅的程序,让我们的程序拥有更好的用户体验


image

简单来说,Activity的生命周期可分为6+1的组成的模式(6分为三对)

1.1、6个常见的生命周期方法:
onCreate() 当Activity创建时执行

onStart() 当Activity可见时执行(可见了,但是看不到,没获得焦点)

onResume() 当Activity获取焦点是执行

onPause() 当Activity失去焦点时执行

onStop() 当Activity不可见时执行

onDestroy() 当Activity销毁时执行

(获取焦点才算真正可见,因为获取了焦点用户才可以操作,所以可见但是看不到这句话不矛盾)

加1就是加上这个:

onRestart() 当Activity正在重新启动时执行。

就上面的6个常见的生命周期:

他们可以说是根据特征可以分成三组的:

onCreate() 对应 onDestroy() 创建和销毁

onStart() 对应 onStop() 可见和不可见

onResume() 对应 onPause() 获取焦点和失去焦点
注意事项:
  1. onPause()和onStop()被调用的前提是: 打开了一个新的Activity!而前者是旧Activity还可见的状态;后者是旧Activity已经不可见!
  2. 另外,亲测:AlertDialog和PopWindow是不会触发上述两个回调方法的~
1.2、生命周期方法逐个分析

最普通的正常的情况下我们点开一个新的Activity然后按下返回键关了这个Activity生命周期是这样子走的:

点开一个新的Activity(还没开启过): 先执行onCreate,紧接着执行onStart,接着这行onResume,然后就停在这个onResume,我们做一些操作嘛。 按下返回键:执行onPause,接着onStop,然后onDestroy,执行了onDestroy了Activity也就销毁了。

onCreate()
当Activity被创建时的调用的方法。只在创建的时候调用一次。 可以在这个方法里面做一些初始化的工作,比如setContentView加载布局文件,初始化一些变量的值等。这个是开发中最常见的方法。

onStart()
Activity已经可见了,但是还没办法进行交互,可以说我们还看不见。(不要纠结这句看不见,我们这里本文对于看见的理解是,我必须要能交互我才算看见,不然你说给我看见但是我不能操作屏幕有什么意思。)

onResume()
Activity已经获取焦点了,Activity处于可见的前台了。用户可以进行交互了在这个阶段。也可以理解为,必须获取焦点才能进行交互。
假设甲Activity是可见的可交互的,但是这时我们的乙Activity(一个新的非全屏的Activity或者一个透明的Activity)至于栈顶时,即在任务栈里我们的乙位甲的上方,此刻我们的甲Activity就会处于Paused状态了,也就是可见但是没能进行交互。

onPause()
Activity已经失去焦点了。用户没有办法在这个Activity进行操作了。一般来说当用户按下Home键或者按下Back键就会执行这个方法,这个方法执行后紧跟着都是执行onStop() 如果是按下back键:onPause() → onStop() → onDestroy() 如果是按下Home键:onPause() → onStop()
onPause方法不能不能进行回收工作,简单说就是这里进行回收工作很可能会拖慢影响其他Activty的显示,后面会涉及到。 回收和清理工作轻一点的科技交给onStop,重一些的交给onDestroy。

onStop()
Activity不可见了。 在这个方法我们可以进行一些比较简单的回收工作。

onDestroy()
Activity被销毁了,这个Activity死掉了,出栈了,拜拜了。 在这里我们可以做一些最终的回收和资源释放的工作。

onRestart()
Activity正在重新启动,也就是从不可见变为可见的一个过程。 当用户按下Home键然后有回到当前程序,就会执行这个方法,或者当用户从当前甲Activity打开一个新的Activity,然后又back键返回到甲Activity,又或者用户按下任务列表,然后选择了刚刚打开过的那个程序,那么这个方法也会执行。
2、Activity的四个状态

每个Activity在其生命周期中最多可能会有四种状态。

1.运行状态
  当一个Activity位于返回栈的栈顶时,这时Activity就处于运行状态,系统会将处于栈顶的Activity显示给用户。
2.暂停状态
   当一个Activity不再处于栈顶位置,但仍然可见,这时Activity就进入了暂停状态。初学者可能会有这样的疑问,既然Activity都已经不在栈顶了,怎么会还可见呢,这是因为并不是每一个Activity都会占满整个屏幕的,比如对话框形式的Activity只会占用屏幕中间的部分区域。
3.停止状态
    当一个Activity不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。
4.销毁状态
    当一个Activity从返回栈中移除后就变成了销毁状态。
3、Activity的创建-关闭过程
1.自定义Activity类名,继承Activity类或者它的子类;
2.重写onCreate()方法,在该方法中调用setContentView()设置要显示的视图文件(R.layout.activity);
3.在AndroidManifest.xml文件中对Activity进行配置;
4.启动Activity调用StartActivity(Intent);
5.关闭Activity调用finish()方法直接关闭当前Activity;
4、Activity的四种启动模式
1 .standard 默认的启动模式,标准模式
结论:每开启一个Activity,就会在栈顶添加一个Activity实例。多次间隔或者直接启动一个甲Activity会添加多个甲的示例,可重复添加。(间隔 ABA, 直接 ACC或者AAA)

这里我们需要明白一个事情,Service和ApplicationContext是没办法直接开启一个新的Activity,因为只有Activity类型的Context的Activity才能开启,但还是有解决办法的,那就是让我们要开的那个新的Activity设置为FLAG_ACTIVITY_NEW_TASK标识。

2 .singletop 单一顶部模式 (顶部不会重复)

结论:如果开启的Activity已经存在一个实例在任务栈的顶部(仅限于顶部),再去开启这个Activity,任务栈不会创建新的Activity的实例了,而是复用已经存在的这个Activity,onNewIntent方法被调用;之前打开过,但不是位于栈顶,那么还是会产生新的实例入栈,不会回调onNewIntent方法。

当我们把一个Activity设置为singleTop,当我们点击打开这个Activity的时候,我们打开B页面,会出现几种情况:

说明:当前A和C都是Standard,B是singleTop

之前没打开过: 此时任务栈里面只有A,A所在的任务栈是S1,这个时候打开singleTop的B,B入栈,入的是S1这个栈,谁打开它进入谁的栈,此时S1的情况是BA,B为栈顶。

之前打开过,但是位于栈顶: 那么复用这个栈,不会有新的实例压入栈中。同时 onNewIntent 方法会被回调,我们可以利用这个方法获得页面传过来的消息或者其他操作。

之前打开过,但是不是位于栈顶: 那么还是会产生新的实例入栈。

3 .singleTask 单一任务

(整个任务栈只有一个对应自身的实例) 结论:如果开启的甲Activity已经存在一个实例在任务栈S1,再去开启这个Activity,位于栈顶则直接复用,回调onNewIntent方法;位于里面,也是复用,回调onNewIntent方法,复用的同时的是直接把自己上方的全部Activity都干掉。

当我们把一个Activity设置为singleTask模式之后,当我们点击开启这个Activity,会出现3种情况:

说明:打开B,A和C是Standard,B是singleTask

之前没开启过:A开启B的时候,B进入A的任务栈。为了顶部

之前开启过情况1:如果现在任务栈情况是BA,B位于栈顶,此时点击B,那么不会创建新的实例,任务栈还是BA,回调onNewIntent方法。

之前开启过情况2:假如现在任务栈情况是CBA,C为了栈顶,那么这时打开B,因为B是singleTask,这时不会创建新的实例,但是肯定会把B置为栈顶(B在回到栈顶的时候不是跳过去的,而是把自己上面的其他Activity全部干掉,这样就只剩下自己和自己下面的Activity了),那么这时任务栈里面的情况就剩下 BA,回调onNewIntent方法.

4 .singleInstance单一实例(单例),任务栈里面自已自己一个人

结论:当启动一个启动模式为singleInstance的Activity时(之前没启动过),这时系统将开辟出另外一个任务栈,用于存放这个Activity,而且这个新的任务栈只能存放自身这唯一一个Activity。singleInstance页面作为前台任务打开自己打开自己,则复用,任务栈顺序无变化;singleInstance页面作为后台任务栈,则切换成为前台任务栈,无新实例产生,复用。

复用就会调用onNewIntent方法。

默认的Activity都是standard模式的,那如果我们要把一个Activity指定为 singleTask 模式呢?

有两种启动方法:一种方法是manifest指定,另外一种方式是代码指定。

manifest指定

//比如我们要指定为singleTask模式 manifest里面的Activity有个 launchMode属性来制定启动模式:
<activity android:name=".SecondActivity"
        android:launchMode="singleTask"/>

代码指定intent.addFlag

//比如我们要指定为singleTop模式
Intent intent  = new Intent();
intent.setClass(FirstActivity.this,SecondActivity.class);
// 通过Intent的addFlag指定
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
5、启动一个Activity的几种方式

在Android中我们可以通过下面两种方式来启动一个新的Activity,注意这里是怎么启动,而非 启动模式!!分为显示启动和隐式启动!

  1. 显式启动:
通过包名来启动,写法如下:
    ①最常见的:
    startActivity(new Intent(当前Act.this,要启动的Act.class));
    ②通过Intent的ComponentName:
    ComponentName cn = new ComponentName("当前Act的全限定类名","启动Act的全限定类名") ;
    Intent intent = new Intent() ;
    intent.setComponent(cn) ;
    startActivity(intent) ;
    ③初始化Intent时指定包名:
    Intent intent = new Intent("android.intent.action.MAIN");
    intent.setClassName("当前Act的全限定类名","启动Act的全限定类名");
    startActivity(intent);

2.隐式启动:

 // Intent-filter的Action,Category或data来实现
    <intent-filter>
        <action android:name="actionName" />
        <category android:name="categoryName" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
   //java代码启动
        Intent intent = new Intent();
        intent.setAction("actionName");
        intent.addCategory("categoryName")
        startActivity(intent);
  1. 另外还有一个直接通过包名启动apk的:
Intent intent = getPackageManager().getLaunchIntentForPackage
    ("apk第一个启动的Activity的全限定类名") ;
 if  (intent != null) 
     startActivity(intent) ;
6、系统给我们提供的常见的Activity

好的,最后给大家附上一些系统给我们提供的一些常见的Activtiy吧!

//1.拨打电话
// 给移动客服10086拨打电话
Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);

//2.发送短信
// 给10086发送内容为“Hello”的短信
Uri uri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "Hello");
startActivity(intent);

//3.发送彩信(相当于发送带附件的短信)
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "Hello");
Uri uri = Uri.parse("content://media/external/images/media/23");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");
startActivity(intent);

//4.打开浏览器:
// 打开Google主页
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent  = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//5.发送电子邮件:(阉割了Google服务的没戏!!!!)
// 给someone@domain.com发邮件
Uri uri = Uri.parse("mailto:someone@domain.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 给someone@domain.com发邮件发送内容为“Hello”的邮件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("text/plain");
startActivity(intent);
// 给多人发邮件
Intent intent=new Intent(Intent.ACTION_SEND);
String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人
String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送
String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_BCC, bccs);
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("message/rfc822");
startActivity(intent);

//6.显示地图:
// 打开Google地图中国北京位置(北纬39.9,东经116.3)
Uri uri = Uri.parse("geo:39.9,116.3");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//7.路径规划
// 路径规划:从北京某地(北纬39.9,东经116.3)到上海某地(北纬31.2,东经121.4)
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//8.多媒体播放:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/foo.mp3");
intent.setDataAndType(uri, "audio/mp3");
startActivity(intent);

//获取SD卡下所有音频文件,然后播放第一首=-= 
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//9.打开摄像头拍照:
// 打开拍照程序
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
startActivityForResult(intent, 0);
// 取出照片数据
Bundle extras = intent.getExtras(); 
Bitmap bitmap = (Bitmap) extras.get("data");

//另一种:
//调用系统相机应用程序,并存储拍下来的照片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
time = Calendar.getInstance().getTimeInMillis();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
.getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg")));
startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);

//10.获取并剪切图片
// 获取并剪切图片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true"); // 开启剪切
intent.putExtra("aspectX", 1); // 剪切的宽高比为1:2
intent.putExtra("aspectY", 2);
intent.putExtra("outputX", 20); // 保存图片的宽和高
intent.putExtra("outputY", 40); 
intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路径
intent.putExtra("outputFormat", "JPEG");// 返回格式
startActivityForResult(intent, 0);
// 剪切特定图片
Intent intent = new Intent("com.android.camera.action.CROP"); 
intent.setClassName("com.android.camera", "com.android.camera.CropImage"); 
intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp"))); 
intent.putExtra("outputX", 1); // 剪切的宽高比为1:2
intent.putExtra("outputY", 2);
intent.putExtra("aspectX", 20); // 保存图片的宽和高
intent.putExtra("aspectY", 40);
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true); 
intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp")); 
startActivityForResult(intent, 0);

//11.打开Google Market 
// 打开Google Market直接进入该程序的详细页面
Uri uri = Uri.parse("market://details?id=" + "com.demo.app");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//12.进入手机设置界面:
// 进入无线网络设置界面(其它可以举一反三)  
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);  
startActivityForResult(intent, 0);

//13.安装apk:
Uri installUri = Uri.fromParts("package", "xxx", null);   
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);

//14.卸载apk:
Uri uri = Uri.fromParts("package", strPackageName, null);      
Intent it = new Intent(Intent.ACTION_DELETE, uri);      
startActivity(it); 

//15.发送附件:
Intent it = new Intent(Intent.ACTION_SEND);      
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");      
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");      
sendIntent.setType("audio/mp3");      
startActivity(Intent.createChooser(it, "Choose Email Client"));

//16.进入联系人页面:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);

//17.查看指定联系人:
Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id联系人ID
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);

参考:(http://www.runoob.com/w3cnote/android-tutorial-activity.html
写在最后: 本系列的文章旨在学习过程中的总结,如果对你也有帮助,荣幸之至。

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

推荐阅读更多精彩内容