Android 部分 Activity 篇

Android 部分 Activity 篇,第1张

Android 部分 Activity 篇 1.Activity 是什么? Activity 实际上只是一个与用户交互的接口而已。 2.Activity 生命周期 2.1 Activity 的 4 种状态    Active/Paused/Stopped/Killed 2.2 Activity 的生命周期分析 正常情况下的生命周期: Activity 启动–>onCreate()–>onStart()–>onResume() 点击 home 键回到桌面–>onPause()–>onStop() 再次回到原 Activity–>onRestart()–>onStart()–>onResume()

退出当前 Activity 时–>onPause()–>onStop()–>onDestroy()

2.3 一些特殊情况下的生命周期分析 2.3.1 Activity 的横竖屏切换 与横竖屏生命周期函数有关调用的属性是"android:configChanges",关于它的属性值设置 影响如下:  orientation:消除横竖屏的影响  keyboardHidden:消除键盘的影响  screenSize:消除屏幕大小的影响 当我们设置 Activity 的 android:configChanges 属性为 orientation 或者 orientation|keyboardHidden 或者不设置这个属性的时候,它的生命周期会走如下流 程: 1. 刚刚启动 Activity 的时候: 2. onCreate 3. onStart 4. onResume 5. 由竖屏切换到横屏: 6. onPause 7. onSaveInstanceState // 这里可以用来横竖屏切换的保存数据 8. onStop 9. onDestroy 10. onCreate 11. onStart 12. onRestoreInstanceState // 这里可以用来横竖屏切换的恢复数据 13. onResume 14. 横屏切换到竖屏: 15. onPause 16. onSaveInstanceState 17. onStop 18. onDestroy 19. onCreate 20. onStart 21. onRestoreInstanceState 22. onResume 当我们设置 Activity 的 android:configChanges 属性为 orientation|screenSize 或者 orientation|screenSize|keyboardHidden 1. 刚刚启动 Activity 的时候: 2. onCreate 3. onStart 4. onResume 5. 由竖屏切换到横屏: 6. 7. 什么也没有调用 8. 横屏切换到竖屏: 9. 什么也没有调用 而且需要注意一点的是设置了 orientation|screenSize 属性之后,在进行横竖屏切换的时 候调用的方法是 onConfigurationChanged (),而不会回调 Activity 的各个生命周期函 数; 当然在显示中我们可以屏蔽掉横竖屏的切换 *** 作,这样就不会出现切换的过程中 Activity 生命周期重新加载的情况了,具体做法是,在 Activity 中加入如下语句: 1. android:screenOrientation= "portrait" 始终以竖屏显示 2. android:screenOrientation= "landscape" 始终以横屏显示 如果不想设置整个软件屏蔽横竖屏切换,只想设置屏蔽某个 Activity 的横竖屏切换功能的 话,只需要下面 *** 作: 1. Activity. this .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRA IT); 以竖屏显示 2. Activity. this .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSC APE); 以横屏显示 最后提一点,当你横竖屏切换的时候,如果走了销毁 Activity 的流程,那么需要保存当前和 恢复当前 Activity 的状态的话,我们可以灵活运用 onSaveInstanceState()方法和 onRestoreInstanceState()方法。 2.3.2 什么时候 Activity 单独走 onPause()不走 onStop()? 2.3.3 什么时候导致 Activity 的 onDestory()不执行? 当用户后台强杀应用程序时,当前返回栈仅有一个 activity 实例时,这时候,强杀,是会 执行 onDestroy 方法的;当返回栈里面存在多个 Activity 实例时,栈里面的第一个没有销 毁的 activity 执行会 ondestroy 方法,其他的不会执行;比如说:从 mainactivity 跳转到 activity-A(或者继续从 activity-A 再跳转到 activity-B),这时候,从后台强杀,只会执 行 mainactivity 的 onDestroy 方法,activity-A(以及 activity-B)的 onDestroy 方法都 不会执行; 2.4 进程的优先级 前台>可见>服务>后台>空 前台: 与当前用户正在交互的 Activity 所在的进程。 可见: Activity 可见但是没有在前台所在的进程。 服务: Activity 在后台开启了 Service 服务所在的进程。 后台: Activity 完全处于后台所在的进程。 空: 没有任何 Activity 存在的进程,优先级也是最低的。 3.Android 任务栈 任务栈与 Activity 的启动模式密不可分,它是用来存储 Activity 实例的一种数据结构, Activity 的跳转以及回跳都与这个任务栈有关。详情请看下面的 Activity 的启动模式。 4.Activity 的启动模式 Activity 的启动模式,你在初学期间一定很熟悉了吧!不管你是否熟悉还是不熟悉,跟随 笔者的思路把 Activity 的启动模式整理一遍: 问题 1:Activity 为什么需要启动模式? 问题 2:Activity 的启动模式有哪些?特性如何 问题 3:如何给 Activity 选择合适的启动模式 问题 1:Activity 为什么需要启动模式? 我们都知道启动一个 Activity 后,这个 Activity 实例就会被放入任务栈中,当点击返回键 的时候,位于任务栈顶层的 Activity 就会被清理出去,当任务栈中不存在任何 Activity 实 例后,系统就回去回收这个任务栈,也就是程序退出了。这只是对任务栈的基本认识,深 入学习,笔者会在之后文章中提到。那么问题来了,既然每次启动一个 Activity 就会把对 应的要启动的 Activity 的实例放入任务栈中,假如这个 Activity 会被频繁启动,那岂不是 会生成很多这个 Activity 的实例吗?对内存而言这可不是什么好事,明明可以一个 Activity 实例就可以应付所有的启动需求,为什么要频繁生成新的 Activity 实例呢?杜绝 这种内存的浪费行为,所以 Activity 的启动模式就被创造出来去解决上面所描述的问题。 问题 2:Activity 的启动模式有哪些?特性如何 Activity 的启动模式有 4 种,分别是:standard,singleTop,singleTask 和 singleInstance。下面一一作介绍: 1.系统默认的启动模式:Standard 标准模式,这也是系统的默认模式。每次启动一个 Activity 都会重新创建一个新的实 例,不管这个实例是否存在。被创建的实例的生命周期符合典型情况下的 Activity 的生命 周期。在这种模式下,谁启动了这个 Activity,那么这个 Activity 就运行在启动它的那个 Activity 的任务栈中。比如 Activity A 启动了 Activity B(B 是标准模式),那么 B 就会进入 到 A 所在的任务栈中。有个注意的地方就是当我们用 ApplicationContext 去启动 standard 模式的 Activity 就会报错,这是因为 standard 模式的 Actiivty 默认会进入启动 它的 Activity 所属的任务栈中,但是由于非 Activity 类型的 Context(如 ApplicationContext)并没有所谓的任务栈,所以这就会出现错误。解决这个问题的方法就 是为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位,这样启动的时候就会 为它创建一个新的任务栈,这个时候启动 Activity 实际上以 singleTask 模式启动的,读者 可以自己仔细体会。 2.栈顶复用模式:SingleTop 在这种模式下,如果新的 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新 创建,同时它的 onNewIntent 方法被回调,通过此方法的参数我们可以取出当前请求的 信息。需要注意的是,这个 Activity 的 onCreate,onStart 不会被系统调用,因为它并没 有发生改变。如果新的 Activity 已经存在但不是位于栈顶,那么新的 Activity 仍然会重新 重建。举个例子,假设目前栈内的情况为 ABCD,其中 ABCD 为四个 Activity,A 位于栈低, D 位于栈顶,这个时候假设要再次启动 D,如果 D 的启动模式为 singleTop,那么栈内的情 况依然为 ABCD;如果 D 的启动模式为 standard,那么由于 D 被重新创建,导致栈内的情况 为 ABCDD。 3.栈内复用模式:SingTask 这是一种单例实例模式,在这种模式下,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,和 singleTop 一样,系统也会回调其 onNewIntent。具体 一点,当一个具有 singleTask 模式的 Activity 请求启动后,比如 Activity A,系统首先寻 找任务栈中是否已存在 Activity A 的实例,如果已经存在,那么系统就会把 A 调到栈顶并 调用它的 onNewIntent 方法,如果 Activity A 实例不存在,就创建 A 的实例并把 A 压入 栈中。举几个栗子:  比如目前任务栈 S1 的情况为 ABC,这个时候 Activity D 以 singleTask 模式请求启 动,其所需的任务栈为 S2,由于 S2 和 D 的实例均不存在,所以系统会先创建任务栈 S2,然后再创建 D 的实例并将其投入到 S2 任务栈中。  另外一种情况是,假设 D 所需的任务栈为 S1,其他情况如同上面的例子所示,那么由 于 S1 已经存在,所以系统会直接创建 D 的实例并将其投入到 S1。  如果 D 所需的任务栈为 S1,并且当前任务栈 S1 的情况为 ADBC,根据栈内复用的原 则,此时 D 不会重新创建,系统会把 D 切换到栈顶并调用其 onNewIntent 方法,同 时由于 singleTask 默认具有 clearTop 的效果,会导致栈内所有在 D 上面的 Activity 全部出栈,于是最终 S1 中的情况为 AD。 通过以上 3 个例子,你应该能比较清晰地理解 singleTask 的含义了。 4.单实例模式:SingleInstance 这是一种加强的 singleTask 模式,它除了具有 singleTask 模式所有的特性外,还加强了 一点,那就是具有此种模式的 Activity 只能单独位于一个任务栈中,换句话说,比如 Activity A 是 singleInstance 模式,当 A 启动后,系统会为它创建一个新的任务栈,然后 A 独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的 Activity, 除非这个独特的任务栈被系统销毁了。 对于 SingleInstance,面试时你有说明它的以下几个特点: (1)以 singleInstance 模式启动的 Activity 具有全局唯一性,即整个系统中只会存在一 个这样的实例。 (2)以 singleInstance 模式启动的 Activity 在整个系统中是单例的,如果在启动这样的 Activiyt 时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。 (3)以 singleInstance 模式启动的 Activity 具有独占性,即它会独自占用一个任务,被 他开启的任何 activity 都会运行在其他任务中。 (4)被 singleInstance 模式的 Activity 开启的其他 activity,能够在新的任务中启动, 但不一定开启新的任务,也可能在已有的一个任务中开启。 换句话说,其实 SingleInstance 就是我们刚才分析的 SingleTask 中,分享 Activity 为栈 底元素的情况。 总结 上面介绍了 4 种启动模式,这里需要指出一种情况,我们假设目前有 2 个任务栈,前台任 务栈的情况为 AB,而后台任务栈的情况为 CD,这里假设 CD 的启动模式均为 singleTask。现在请求启动 D,那么整个后台任务栈都会被切换到前台,这个时候整个后退 列表变成了 ABCD。当用户按 back 键的时候,列表中的 Activity 会一一出栈,如下图 1 所示: 注意: 前台任务栈:就是指和用户正在交互的应用程序所在的任务栈。 后台任务栈:就是指处于后台的应用程序所在的任务栈。

如果不是请求的 D 而是请求的 C,那么情况就不一样了,如下图 2 所示:

 

5.Activity 组件之间的通信

1.Activity->Activity [1]Intent/Bundle 这种方式多用于 Activity 之间传递数据。示例代码如下:

 

1. //首先创建一个 Bundle 对象
2. Bundle bundle = new Bundle();
3. bundle.putString("data_string","数据");
4. bundle.putInt("data_int",10);
5. bundle.putChar("da_char",'a');
6.
7. //然后创建一个 Intent 对象
8. Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
9. intent.putExtras(bundle);
10. startActivity(intent);
[2]类静态变量 在 Activity 内部定义静态的变量,这种方式见于少量的数据通信,如果数据过多,还是使 用第一种方式。 [3]全局变量 创建一个类,里面定义一批静态变量,Activity 之间通信都可以访问这个类里面的静态变 量,这就是全局变量。 2.Activity->Service [1]绑定服务的方式,利用 ServiceConnection 这个接口。 首先我们需要在要绑定的服务中声明一个 Binder 类
1. public class MyService1 extends Service {
2.
3. public String data = "";
4.
5. public MyService1() {
6. }
7.
8. @Override
9. public IBinder onBind(Intent intent) {
10. // TODO: Return the communication channel to the service.
11. return new Binder();
12. }
13.
14.
15.
16. public class Binder extends android.os.Binder{
17.
18. public void sendData(String data){
19.
20. MyService1.this.data = data;
21.
22. }
23.
24. }
25.
26. }
然后我们让 Activity 实现 ServiceConnection 这个接口,并且在 onServiceConnected 方法中获取到 Service 提供给 Activity 的 Binder 实例对象,通过这个对象我们就可以与 Service 进行通信可以通过上述代码的 Binder 类中的 sendData()方法进行通信。
1. public class ServiceBindActivity extends AppCompatActivity implements Servic
eConnection,View.onClickListener {
2.
3. private Button bt0,bt1,bt2;
4.
5. public MyService1.Binder binder = null;
6.
7. @Override
8. protected void onCreate(Bundle savedInstanceState) {
9. super.onCreate(savedInstanceState);
10. setContentView(R.layout.activity_service_bind);
11.
12. bt0 = findViewById(R.id.bt0);
13. bt1 = findViewById(R.id.bt1);
14. bt2 = findViewById(R.id.bt2);
15.
16. bt0.setonClickListener(this);
17. bt1.setonClickListener(this);
18. bt2.setonClickListener(this);
19.
20.
21. }
22.
23. @Override
24. protected void onDestroy() {
25. super.onDestroy();
26. unbindService(this);
27. }
28.
29. //这个是服务绑定的时候调用
30. @Override
31. public void onServiceConnected(ComponentName componentName, IBinder iBin
der) {
32. binder = (MyService1.Binder) iBinder;
33. }
34.
35. //这个是服务解绑的时候调用
36. @Override
37. public void onServiceDisconnected(ComponentName componentName) {
38.
39. }
40.
41. @Override
42. public void onClick(View view) {
43.
44. switch (view.getId()){
45.
46. case R.id.bt0:
47.
48. //绑定服务
49. Intent intent = new Intent(ServiceBindActivity.this,MyServic
e1.class);
50. bindService(intent,this, Context.BIND_AUTO_CREATE);
51.
52. break;
53.
54. case R.id.bt1:
55.
56. //通过 binder 对象来和 Service 进行通信
57. if(binder != null)
58. binder.sendData("bt1");
59.
60. break;
61. case R.id.bt2:
62.
63. //通过 binder 对象来和 Service 进行通信
64. if(binder != null)
65. binder.sendData("bt2");
66.
67. break;
68.
69. }
70.
71. }
72. }
也不一定非要笔者这种写法,你也可以有自己的写法,但核心部分都一样。 [2]Intent 这种方式很简单,我们在启动和停止 Service 时所调用的方法都需要传入一个 Intent 实例 对象,通过这个传入的 Intent 对象,我们就可以与 Service 进行通信。示例代码如下: Activity 代码是这样的:
1. public class ServiceStartActivity extends AppCompatActivity implements View.
onClickListener {
2.
3. private Button bt0,bt1;
4.
5. private Intent intent ;
6.
7. @Override
8. protected void onCreate(Bundle savedInstanceState) {
9. super.onCreate(savedInstanceState);
10. setContentView(R.layout.activity_service_start);
11.
12. intent = new Intent(this, MyService2.class);
13.
14. bt0 = findViewById(R.id.bt0);
15. bt1 = findViewById(R.id.bt1);
16.
17. bt0.setonClickListener(this);
18. bt1.setonClickListener(this);
19.
20.
21. }
22.
23. @Override
24. public void onClick(View view) {
25.
26. switch (view.getId()){
27.
28. case R.id.bt0:
29.
30. //开启服务并且传递数据
31. intent.putExtra("data_stirng","string 数据");
32. startActivity(intent);
33.
34. break;
35.
36. case R.id.bt1:
37.
38. //结束服务
39. stopService(intent);
40.
41. break;
42.
43. }
44.
45. }
46. }
Service 中的代码是这样的:
1. public class MyService2 extends Service {
2.
3. public String data = "";
4.
5. public MyService2() {
6. }
7.
8. @Override
9. public IBinder onBind(Intent intent) {
10. // TODO: Return the communication channel to the service.
11. return null;
12. }
13.
14. @Override
15. public int onStartCommand(Intent intent, int flags, int startId) {
16. //得到 Activity 传递过来的数据
17. data = intent.getStringExtra("data_string");
18. return super.onStartCommand(intent, flags, startId);
19. }
20. }
这种通信方式的缺点显而易见,那就是只能传递少量的数据。 [3]CallBack + Handler,监听服务的进程变化 Service 中的代码:
1. public class MyService3 extends Service {
2.
3. //在 Service 中如果要进行耗时任务,可以通过 CallBack 接口提供的方法与 Activity
进行通信
4. public Callback callback;
5.
6. public MyService3() {
7. }
8.
9. @Override
10. public IBinder onBind(Intent intent) {
11. // TODO: Return the communication channel to the service.
12. return new Binder();
13. }
14.
15. public void setCallBack(CallBack callBack){
16. this.callback = callback;
17. }
18.
19. public Callback getCallback() {
20. return callback;
21. }
22.
23. public interface CallBack{
24. void onDataChange(String data);
25. }
26.
27. public class Binder extends android.os.Binder{
28.
29. public MyService3 getMyService3(){
30. return MyService3.this;
31. }
32.
33. }
34.
35. }
Activity 中的代码:
1. public class ServiceBind2Activity extends AppCompatActivity implements Servi
ceConnection{
2.
3.
4.
5. public MyService3.Binder binder = null;
6.
7. private Handler handler = new Handler(){
8.
9. @Override
10. public void handleMessage(Message msg) {
11. super.handleMessage(msg);
12.
13. Bundle bundle = msg.getData();
14. String data_string = bundle.getString("data_string");
15.
16. //接下来就是更新 ui
17.
18. }
19. };
20.
21. @Override
22. protected void onCreate(Bundle savedInstanceState) {
23. super.onCreate(savedInstanceState);
24. setContentView(R.layout.activity_service_bind2);
25. }
26.
27. @Override
28. public void onServiceConnected(ComponentName componentName, IBinder iBin
der) {
29.
30. binder = (MyService3.Binder) iBinder;
31. binder.getMyService3().setCallBack(new MyService3.CallBack() {
32.
33. //此方法提供给 MyService3 在子线程中调用
34. @Override
35. public void onDataChange(String data) {
36. Message message = new Message();
37. Bundle bundle = new Bundle();
38. bundle.putString("data_string","String 数据");
39. message.setData(bundle);
40. //通过 Handler 进行异步通信,不过耗时 *** 作放在 MyService3 中
41. handler.sendMessage(message);
42. }
43. });
44.
45. }
46.
47. @Override
48. public void onServiceDisconnected(ComponentName componentName) {
49.
50. }
51.
52.
53. }
可能第一次看到这段代码的你很懵逼吧,其实很简单,当 ServiceBind2Activity 去绑定服 务 MyService3 的时候,那么在 Activity 中的 onServiceConnected()方法被调用,此时位 于 MySerivce3 的 CallBack 接口引用被实例化,并且 onDataChange()方法被实现,可以 看到里面是一段 Handler 通信的代码,不错,这个方法是为 MyService3 做耗时 *** 作调用 的,笔者没有在 MyService3 中写耗时 *** 作的代码,不过说到这里你应该明白了这种通信 方式的好处了吧,也印证了标题:监听服务的进程变化。 3.Activity->Fragment [1]Bundle 在创建 Fragment 实例的时候,调用方法 setArguments 将一个 Bundle 对象传递给 Fragment,然后在 Fragment 中先去判断是否和当前 Activity 绑定上了,如果绑定上 了,就可以拿出这个 Bundle 中的数据啦。示例代码如下: 在 Activity 中代码是这样的:
1. //首先创建一个 Bundle 对象
2. Bundle bundle = new Bundle();
3. bundle.putString("data_string","数据");
4. bundle.putInt("data_int",10);
5. bundle.putChar("da_char",'a');
6.
7. Fragment fragment = new MyFragment1();
8. fragment.setArguments(bundle);
在 MyFragment1 中代码是这样的:
1. if(isAdded()){//这里判断是否 Fragment 和 Activity 进行了绑定
2.
3. Bundle bundle = getArguments();
4. String data_string = bundle.getString("data_string");
5. String data_int = bundle.getInt("data_int");
6. String data_char = bundle.getChar("data_char");
7.
8.
9. }
[2]直接进行方法调用 在 Activity 里通过 Fragment 的引用,可以直接调用 Framgent 中的定义的任何方法。示 例代码如下:
1. MyFragment1 myFragment1 = new MyFragment1();
2. myFragment.toString("传送的 string 数据");

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5709142.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存