关于Activity你应该知道的一切
2016-11-27 21:22
507 查看
Activity
Context上下文
可以理解为语文中的上下文,也就是语境从Android系统的角度来理解,Context就是当前应用所处的环境,
从程序的角度上来理解,Context是一个抽象类,Activity、Service、Application都是Context的子类
在Android中,可以通过上下文访问跟当前应用相关的资源
获取系统资源(getsystemservice 获取系统的服务 getwallpaper 获取壁纸)
获取应用的私有的资源(getAssets 获取assets目录下的内容)
获取res目录的资源(getResource())
startActivity+startService
获取内部文件(夹)路径
创建View
XXXActivity.this和getApplicationContext的区别?
一个是当前Activity的实例,一个是当前应用的实例,两个的生命周期不同,使用时要注意使用的场景,如果使用不当就会造成一些问题。
Context的应用场景
凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有(生命周期),防止内存泄漏
1.什么是Activity
Activity是Android四大组件之一,用于展示界面并和用户交互。Activity之间通过Intent进行通信。Intent即意图,用于描述一个页面的信息,同时也是一个数据的载体。
2. Activity之间的跳转
Activity之间的跳转分为2种:显式跳转:直接通过字节码 .class打开对应activity,在打开自己应用activity的时候 一般使用显示意图
如果是打开其他应用的activity,intent创建之后要通过intent.setClassName(“包名”,”全类名”); 这个方法打开其它应用的activity(一般不用)
Intent intent = new Intent(this, SecondActivity.class); startActivity(intent);
隐式跳转:通过在清单文件中activity节点下配置一个intent-filter,可以让自己的应用或其他应用通过匹配意图过滤器来打开当前的activity
需要注意:想通过隐式意图打开activity,意图过滤器必须要写,category节点配置为DEFAULT 虽然代码中不用匹配category
如果 data节点中既有scheme节点也有mimetype 需要使用setDataAndType来匹配这两个信息
//创建一个Intent对象 Intent intent = new Intent(); //设置Action intent.setAction("android.intent.action.VIEW"); //设置category intent.addCategory("android.intent.category.BROWSABLE"); //设置参数 intent.setData(Uri.parse("http://www.gxs.com")); //启动Activity startActivity(intent); 清单文件中 * action 指定动作(自定义) * data 指定数据 * category 类别 (机顶盒,车载电脑) * 通常我们需要去开发一个和系统应用一样功能的软件时,就可以拿取他的action和data和category
一般打开自己应用的activity使用显示意图,打开别人应用的activity使用隐式意图
当打开系统应用Activity时,需要寻找系统应用的action/Category/Data
打开相应的系统应用,观察logcat中activitymanager打印的信息
I/ActivityManager: START u0 {cmp=com.android.mms/.ui.ComposeMessageActivity} from pid 1081 I/ActivityManager: Displayed com.android.mms/.ui.ComposeMessageActiy
根据ComposeMessageActivity在系统上层源码里对应的清单文件中找到对应的结点(Activity-intentfilter)
如果需要传递数据的话,可以去代码中找到传输数据的格式
通过隐式意图开启一个Activity,可以先进行一下判断
如果不判断,当没有Activity匹配的时候,会出现错误PackageManager.resolveActivity
Intent.resolveActivity
当返回值为空时,就是没有。另外注意第二个参数为MATCH_ DEFAULT_ ONLY
intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可
IntentFilter中的过滤信息有:action,category,data分析各属性的匹配规则:
action的匹配规则
隐式启动必须要有action
action的匹配规则要求intent中的action和IntentFilter中的action完全相同
category的匹配规则
intent中可以没有category,但若是有,不管有几个,只要能够和过滤规则中任何一个category相同即可
data的匹配规则
这个和action类似,如果过滤规则中定义了data,那么intent必须也要定义能够匹配的data
data的结构:(data由mimeType和URI两部分组成)
MIME是描述消息内容类型的因特网标准。 MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。 类型/子类型 * mimeType指媒体类型 * image/jpg/video....可以表示图片,文本,视频等不同的媒体格式 * URI包含的数据就比较多了,下面是URI的结构 <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>] eg: http://www.baidu.com:80/search/info content://com.jike.project:200/folder/subfolder/etc 下面介绍一下每个数据的含义: scheme:URI的模式,比如http,file,content等 Host:URI的主机名,比如www.baidu.com port:URI中的端口号,比如80 path,pathPrefix,pathPattern:这三个参数表示路径信息 //data的语法 <data android:host="string" android:mimeType="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:port="string" android:scheme="string" />
若清单文件中的Activity声明为:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
则此Activity将作为程序的入口,有几个作为入口的Activity,apk文件安装的时候就会生成几个图标。
3. 使用Intent传递数据
Intent可传递的数据类型有: 基本数据类型及其数组 String String数组 String集合 Integer集合 序列化(serilizeble parcelable) 数组和集合,也可以通过Bundler来传递数据 把数据封装到一个对象中注意:Intent传递的数据过多可能会造成跳转速度极慢甚至黑屏一会,不要用Intent传递过多的数据,会影响到应用程序的使用。
意图设计目的
高内敛,低耦合。
使得程序组件之间彼此独立,又可以互相调用。
数据传递有两种情况:
1.从A界面打开B界面,把A界面的数据传递给B界面
示例如下:
在清单文件中设置SecondActivity的intent-filter参数
<activity android:name="com.gxs.activitySkip.SecondActivity"> <!-- 配置意图过滤器 --> <intent-filter > <!-- 在意图过滤器中设置action和category,当有匹配的action和category的时候启动该Activity --> <action android:name="com.gxs.activitySkip.SecondActivity"/> <category android:name="android.intent.category.DEFAULT"/> <!-- 设置数据协议 --> <data android:scheme="money"/> <!-- 配置数据的mimeType:必须为xxx/xxx的格式,否则会报异常 --> <data android:mimeType="data/mymime"/> </intent-filter> </activity>
在FirstActivity布局中添加一个按钮,给该按钮绑定事件,点击该按钮实现跳转到SecondActivity界面
// 发送数据给SecondActivity public void sendData2Second(View view) { // 创建一个Intent对象 Intent intent = new Intent(); // 设置Action intent.setAction("com.gxs.activitySkip.SecondActivity"); // android.intent.category.DEFAULT为Android系统默认,省略也可以 intent.addCategory("android.intent.category.DEFAULT"); ** //当有setData和setType需要使用这个,这两个会互相清除对方的值** intent.setDataAndType(Uri.parse("money:转账100元。"), "data/mymime"); //给Intent设置数据 intent.putExtra("name", "张三"); ArrayList<String> list = new ArrayList<String>(); e62d for(int i=0;i<10;i++){ list.add("list"+(i+1)); } //给Intent设置字符串类型的集合 intent.putStringArrayListExtra("list", list); //声明一个Bundle对象 Bundle bundle = new Bundle(); //在Bundle对象中绑定数据 bundle.putString("pwd", "123456"); //给Intent设置Bundle对象 intent.putExtras(bundle); // 启动Activity startActivity(intent); }
编写SecondActivity类,使其接收数据
public class SecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); //从当前Context对象中获取Intent Intent intent = getIntent(); //获取数据 String data = intent.getData().toString(); System.out.println(data); //从Intent中获取Bundle对象 Bundle extras = intent.getExtras(); //从Bundle中获取key为name的数据 String name = (String) extras.get("name"); System.out.println("name="+name); //从Bundle对象中获取key为pwd的数据 String pwd = (String) extras.get("pwd"); System.out.println(pwd); //从Bundle中获取ley为list的数据 List<String> list = (List<String>) extras.get("list"); System.out.println(list); }
2.从A界面打开B界面,B界面关闭的时候,返回一个数据给A界面
跳转时使用startActivityForResult方法开启新页面
startActivityForResult(intent, 请求码);
在被开启的新的页面里, 调用setResult方法设置回传的数据
//获取当前Intent Intent intent = getIntent(); //获取Intent中绑定的数据 Bundle extras = intent.getExtras(); int data = (Integer) extras.get("rp"); //创建一个新的Intent Intent data = new Intent(); data.putExtra("phone", phone); //设置一个结果数据,数据会返回给调用者 setResult(结果码, data); //关闭掉当前的activity,才会返回数据 finish();
当页面被关闭后,回传数据给前一个页面的onActivityResult方法,在此方法中对数据进行处理即可,如果有多个页面会返回数据 可以通过requestcode 或者 resultcode区分 不同数据
//请求码 结果码 传递的数据 protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //从当前Intent中获取数据(需要进行非空判断) Bundle extras = data.getExtras(); String result = (String) extras.get("result"); }
注意:如果请求码设为-1,则使用startActivityForResult(intent, 请求码),没有结果返回。
4. 生命周期
生命周期方法* oncreate() activity被创建的时候调用的方法 > 作用:ui界面的初始化 setContentView() * onStart() 当界面变成用户可见的时候调用的方法(可见不可交互) > 作用:更新ui的操作,eg:播放视频 * onResume()//resume继续 ,当界面获取到焦点的时候(可见可交互) > 作用:界面获取到了焦点,按钮可以相应点击事件 * onPause()//pause暂停,当界面失去焦点的时候调用的方法(部分可见不可交互) > 作用:界面失去了焦点,按钮不可以被点击了 > 不能在onPause中做重量级操作 * onStop() activity的界面用户不可见(完全不可见) > 界面不可见,eg:暂停视频播放 * onDestroy() activity被销毁的时候调用的方法 > 作用:界面退出之前的扫尾操作,eg:线程的销毁,退出前数据的保存。 * onRestart()//activity重新启动 >作用:activity被最小化了,并没有销毁,如果下次再去打开这个activity就会调用此方法
完整生命周期
onCreate-->onStart-->onResume-->onPause-->onStop-->onDestory
可视生命周期
onStart-->onResume-->onPause-->onStop
前台生命周期
onResume-->onPause
见图——activity生命周期
正常情况下的生命周期分析:
onStart和onStop是针对Activity**是否可见来说的,OnResume和OnPause是针对Activity是否位于前台**来说的。
当前是ActivityA,这时打开ActivityB,A的OnPause先于B的OnResume执行。
不能在onPause中做重量级操作,因为必须onPause执行完成之后新Activity才能Resume
异常情况下的生命周期分析:
相关的系统配置发生改变(屏幕旋转)或者内存不足而导致Activity被杀死并重新创建
由于Activity是在异常情况下终止,系统会调用onSaveInstanceState(Bundle outState)来保存当前Activity的状态(这个方法在Onstop之前调用,并且这个方法只会在Activity异常终止的情况下调用,正常不会调用),
当Activity被重新创建,系统会调用onRestoreInstanceState(Bundle savedInstanceState)方法,并把onSaveInstanceState保存的Bundle对象传递给onRestoreInstanceState和onCreate方法。
因此我们可以通过onRestoreInstanceState判断Activity是否重建,如果重建了可通过onRestoreInstanceState中的Bundle来恢复数据(在OnStart之后调用)
在activity重建过程中,系统会保存当前Activity的视图结构,并自动恢复,比如文本框中的数据,listview滚动的位置,并自动恢复
和Activity一样,每个View都有这两个方法,通过查看源码可知系统为每个View保存恢复哪些数据。
保存恢复View层次结构,系统工作流程: activity被异常终止--activity调用onSaveInstanceState保存数据--然后Activity会委托Window去保存数据--接着window委托它里面的顶级容器DectorView去保存数据 --DectorView通知子View保存数据。 这是一种委托思想,上层委托下层。类似于View的绘制,事件的分发机制。
内存不足导致低优先级的Activity被杀死
其数据存储和恢复过程与1过程一致。
activity优先级
前台activity——正在和用户交互,优先级最高
可见但非前台activity——比如activity中弹出一个对话框,优先级其次
后台activity——执行了OnStop
横竖屏切换的生命周期
默认情况下 ,横竖屏切换, 会销毁当前的activity,重新创建一个新的activity不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
需求:让屏幕旋转不再重新调用生命周期
横竖屏写死
android:screenOrientation="landscape" android:screenOrientation="portrait" //screen 屏幕 orientation 方向 landscape 风景(躺) portrait 肖像
让系统的环境 不在去敏感横竖屏的切换。
android:configChanges="orientation|screenSize|keyboardHidden"
在Activity结点声明
5. 任务栈
任务栈创建的目的:管理activity程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
只有在任务栈栈顶的activity才可以跟用户进行交互。
任务栈可以移动到后台, 并保留了每一个activity的状态. 并有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
当把任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
一般一个应用程序对应一个任务栈,手机操作系统里面有多个任务栈
任务栈的id是应用程序第一次开启的时候分配出来的,是一个自增长的int类型的数据。如果应用程序退出,id被回收。
一个Task中的activity可以来自不同的APP,一个APP的activity也可以在不同的栈中。
任务栈的缺点:
每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差, 需要点击多次返回才可以把程序退出了。
每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式
activity启动模式,修改任务栈
启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例共用一个task。Activity一共有以下四种launchMode:
standard 标准启动模式
默认的启动模式,每次启动activity都会创建新的实例。
singletop 栈顶复用模式
当栈顶有将要开启的Activity时,会复用这个Activity,同时这个activity的onNewIntent方法会被回调。这个activity的onCreate,OnStart方法不会被调用,因为它没有发生改变。若是栈顶没有,那么就会重新创建
应用场景:适用于接收到消息后显示的界面。例如:QQ接收到消息后会弹出activity,但如果一次来10条消息,总不能一次弹出10个activity。
singetask 栈内复用模式,在当前任务栈里面只能有一个实例存在
当一个启动模式为singleTask的activityA请求启动后,系统会先寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例并将A放到栈中。如果存在A所需的任务栈,这时要看栈中有是否有要开启的activityA,如果有则直接复用并删除A上面的activity,将A移动到栈顶,同singletop一样,也会回调这个activity的onNewIntent方法。如果没有实例,则创建A实例并压入栈中
现在有两个任务栈,前台任务栈中有BA,后台任务栈中有DC,假设DC启动启动模式都为singleTask。现在请求启动D,那么整个后台任务栈都会被切到前台,这时候前台任务栈为DCBA,当按back键时,前台栈中activity会一一出栈;如果请求启动C,那么情况就不一样了,会把D删除,C切换到前台。(具体看图——singleTask启动模式特例.pptx)
所以这种启动模式通常可以用来退出整个应用程序。。将主activity设为singleTask,然后在要退出的activity中转到主Activity,从而将主Activity上的其他activity全部清除,然后在主Activity中的onNewIntent()中加上finish(),将最后一个activity结束。
singleInstance 单实例模式,可以看作加强版singleTask模式
activity会开启一个新的任务栈,并且这个任务栈里面只有一个实例存在。
这种启动模式和浏览器的工作原理类似。当多个程序访问浏览器时,如果浏览器没有打开,则打开浏览器,否则会在当前打开的浏览器中访问。举个例子来说,当应用A的任务栈创建了ActivityA实例,并且其启动模式为sinleInstance,如果应用B也要激活ActivityA,则不需要创建,两个应用共享即可。
如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
关于singleInstance这种启动模式还有一点需要特殊说明:
如果在一个singleInstance的activityA中通过startActivityForResult()去启动另一个activityB,那么在A中拿不到数据。因为android不允许task间互相传递数据。
onNewIntent()
protected void onNewIntent(Intent intent)通过这个方法可以取出当前请求的信息
* 第一次创建Activity A时,执行的逻辑顺序是:
* onCreate() >onStart()>onResume()
* 而如果使用singleTask模式第二次启动Activity A,且A处于任务栈的顶端,则执行的逻辑顺序是:
* onNewIntent() >onRestart> onStart>onResume()。
注意,getIntent()仍返回原来的意图。你可以使用setIntent来设置新的意图。
如何给activity设置模式
在清单文件中设置
<activity android:name=".B" android:launchMode="singleTask"******* -------------------------------------------------------------- android:taskAffinity="kk" //任务栈名称,默认为包名 //taskAffinity属性主要和singleTask启动模式配对使用或者allowTaskPeparenting属性配对使用 * 当一个应用A启动了应用B的某个activity,如果这个activity的allowTaskPeparenting设置为true的话,那么当应用B被启动后,此activity会直接从应用A的任务栈转移到应用B的任务栈中。 />
在代码中通过Intent设置
Intent intent =new Intent(this,MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);********* startActivity(intent); Intent Flag设置启动模式 * Intent.FLAG_ ACTIVITY_ NEW_TASK * 开启一个新的task来存放这个activity * 通常用于在广播,服务中开启一个新的activity。由于他们中并不存在activity栈,所以需要这样。 * intent.FLAG_ ACTIVITY_ NO_HISTORY * 使用这种模式启动的activity,当该activity启动其他activity的时候,这个activity就消失了。eg:A-B,B中以这种模式启动C,C在启动D,则当前栈为ABD * FLAG_ ACTIVITY_ SINGLE_ TOP * ==singleTop * FLAG_ ACTIVITY_ clear_ Top * 清除位于它上面的Activity
二者区别:
1. 第二种优先级大于第一种
2. 当两个同时设置时,以第二种为准,
任务栈_清单文件中相关参数
TaskAffinity 标识了一个Activity需要的任务栈。默认情况下,所有Activity的任务栈的名字是包名 这个属性主要和singleTask/allowTaskReparenting配合使用 1. 当TaskAffinity和singleTask配合使用时,TaskAffinity为当前任务栈的名字,这个Activity会运行在名字和TaskAffinity相同的任务栈中 2. 当TaskAffinity与allowTaskReparenting配合使用的时候。当应用A启动应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true,那么当B被启动后,此Activity会直接从A的任务栈转移到B的任务栈中(栈顶)
Android中允许将四大组件放到单独的进程中运行,只有四大组件可以放到别的进程中
补充
android:allowBackup它表示是否允许应用程序参与备份。如果将该属性设置为false,则即使备份整个系统,也不会执行这个应用程序的备份操作,而整个系统备份能导致所有应用程序数据通过ADB来保存。该属性必须是一个布尔值,或为true,或为false,其默认值为true。
相关文章推荐
- android activity 应该知道的一切
- 关于Android内存优化你应该知道的一切
- 关于Android内存优化你应该知道的一切
- 关于View,你应该知道的一切
- 关于Java序列化你应该知道的一切
- 关于Intent你应该知道的一切
- Activity你应该知道的一切
- 关于Android内存优化你应该知道的一切
- 关于Java序列化你应该知道的一切
- android activity 应该知道的一切(完整篇)
- Java编程--关于JNI你应该知道的一切
- Java编程--关于JNI你应该知道的一切
- 关于渐进式(PWA) Web 应用,你应该知道的一切
- 你应该知道的10件关于Java 6的事情
- 你应该知道的 - 关于 Java 2D 中图形上下文基类 Graphics
- 你应该知道的10件关于Java 6的事情
- 你应该知道的10件关于Java 6的事情
- 你应该知道的10件关于Java 6的事情-Java基础-Java-编程开发
- 你应该知道的10件关于Java 6的事情
- 关于J2ME,应该知道的事