zoukankan      html  css  js  c++  java
  • Activity

    一:Android的启动模式

      activity

    供用户操作的界面

    描述:

    1)表示用户交互的一个界面(活动),每一个activity对应一个界面

    2)是所有View的容器:button,textview,imageview;我们在界面上看到的都是一个个的view

    3)有个ActivityManager的管理服务类,用于维护与管理Activity的启动与销毁;

    Activity启动时,会把Activity的引用放入任务栈中

    4)一个应用程序可以被别的应用程序的activity开启

           此时,是将此应用程序的引用加入到了开启的那个activity的任务栈中了

    5) activity是运行在自己的程序进程里面的

           在一个应用程序中,可以申请单独的进程,然此应用程序中的一个组件在新的进程中运行

    6)可以在activity里面添加permission标签,调用者必须加入这个权限

           与钱打交道的界面,都不允许被其他应用程序随意打开

    如果觉得那个activity比较重要,可以在清单文件中配置,防止别人随意打开,需要配置一个权限

    1、创建Activity

    1)定义类继承自Activity类;

    2)在清单文件中Application节点中声明<activity>节点;

           <activity

                android:name="com.itheima.activity.MainActivity"

                android:label="@string/app_name" >

                <!-- 程序的入口,LAUNCHER表示桌面快捷方式,进入的是此Activity -->

                <intent-filter>

                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />  <!—启动时,默认匹配 --

                </intent-filter>

            </activity>

     

    2、启动Activity

    通过意图(Intent)来启动一个Activity;

    1)  显示启动:

    显示启动一般用于自己调用自己的情况(在当前应用找),这样的启动方式比较快速,创建Intent后指定包名和类名;

           Intent intent = new Intent(this, OtherActivity.class);

           startActivity(intent);             // 启动新的Activity

           或者:

           Intent intent = new Intent();

           intent.setClassName("com.itheima.activity", "com.itheima.activity.OtherActivity"); // 包名、全类名

           startActivity(intent);             // 启动新的Activity

    2)隐式启动:

    一般用于调用别人的Activity,创建Intent后指定动作和数据以及类型;

           // 电话

           Intent intent = new Intent();

           intent.setAction(Intent.ACTION_CALL);                                        // 设置动作

           intent.setData(Uri.parse("tel://123456"));                                 // 设置数据

           // 网页

           intent.setAction(Intent.ACTION_VIEW);

           intent.setData(Uri.parse("http://192.168.1.45:8080/androidWeb"));

           // 音频/视频,设置type

           intent.setAction(Intent.ACTION_VIEW);

           intent.setDataAndType(Uri.parse("file:///mnt/sdcard/daqin.mp3"), "audio/*");  // 设置数据和数据类型,将启动音频播放器(vedio)

    3)为隐式启动配置意图过滤器:

      n  显式意图是指在创建意图时指定了组件,而隐式意图则不指定组件,通过动作、类型、数据匹配对应的组件;

      n  在清单文件中定义<activity>时需要定义<intent-filter>才能被隐式意图启动;

      n  <intent-filter>中至少配置一个<action>和一个<category>,否则无法被启动;

      n  Intent对象中设置的action、category、data在<intent-filter>必须全部包含Activity才能启动;

      n  <intent-filter>中的<action>、<category>、<data>都可以配置多个,Intent对象中不用全部匹配,每样匹配一个即可启动;

      n  如果一个意图可以匹配多个Activity,Android系统会提示选择;

             <!-- 注册 Activity, lable 表示Activity的标题 -->

            <activity

                android:name="com.itheima.activity.OtherActivity"

                android:label="OtherActivity" >

                <!-- 配置隐式意图,匹配http -->

                <intent-filter>

                    <action android:name="android.intent.action.VIEW" />           <!—必须,表示动作为View -->

                    <data android:scheme="http" />                                             <!—http开头-->                             

                    <category android:name="android.intent.category.DEFAULT" /> <!-- 必须,表示启动时,默认匹配 -->

                </intent-filter>

                <!-- 匹配tel -->

                 <intent-filter>

                    <action android:name="android.intent.action.CALL" />                  

                    <data android:scheme="tel" />                                                                                

                    <category android:name="android.intent.category.DEFAULT" /> <!-- 必须,表示启动 -->

                </intent-filter>

                <!-- 匹配 音频、视频 -->

                <intent-filter>

                    <action android:name="android.intent.action.VIEW" />                   

                    <data android:scheme="file" android:mimeType="audio/*" />   <!—文件协议l类型 -->                                                                         

                    <data android:scheme="file" android:mimeType="video/*" />                

                    <category android:name="android.intent.category.DEFAULT" /> <!-- 必须,表示启动 -->                                                       

                </intent-filter>

         </activity>

     

    3、启动时传递数据

    可通过意图Intent对象实现Activity之间的数据传递;

    使用Intent.putExtra()方法装入一些数据, 被启动的Activity可在 onCreate方法中getIntent()获取;

    可传输的数据类型: a.基本数据类型(数组),  b. String(数组),  c. Bundle(Map),  d. Serializable(Bean), e.Parcelable(放在内存一个共享空间里);

    基本类型:

           Intent intent = new Intent(this, OtherActivity.class);

           intent.putExtra("name", "张飞");         // 携带数据

           intent.putExtra("age", 12);

           startActivity(intent);

    一捆数据:

           Intent intent = new Intent(this, OtherActivity.class);

           Bundle b1 = new Bundle();

           b1.putString("name", "赵云");

           b1.putInt("age", 25);

           Bundle b2 = new Bundle();

           b2.putString("name", "关羽");

           b2.putInt("age", 44);

           intent.putExtra("b1", b1);

           intent.putExtra("b2", b2);

    序列化对象(须实现序列化接口):

           Intent intent = new Intent(this, OtherActivity.class);

           Person p = new Person("张辽", 44);

           intent.putExtra("p", p);

    接收数据:

           在OtherActivity 的onCreate()方法,通过 getIntent().get 相关的数据的方法来获取数据;

     

    4、关闭时返回数据

    基本流程:

    l 使用startActivityForResult(Intent intent, int requestCode) 方法打开Activity;

    l 重写onActivityResult(int requestCode, int resultCode, Intent data) 方法;

    l 新Activity中调用setResult(int resultCode, Intent data) 设置返回数据之后,关闭Activity就会调用上面的onActivityResult方法;

    注意:新的Activity的启动模式不能设置成 singleTask(如果已创建,会使用以前创建的)与singleInstance(单例,单独的任务栈),

             不能被摧毁(执行不到finish方法),父Activity中的 onActivityResult方法将不会执行;

    finish():表示关闭当前Activity,会调用onDestroy方法;

    Activity_A:

           public void openActivityB(View v) {

                  Intent intent = new Intent(this, Activity_B.class);

                  Person p = new Person("张辽", 44);

                  intent.putExtra("p", p);

                  startActivityForResult(intent, 100);                                         // 此方法,启动新的Activity,等待返回结果, 结果一旦返回,自动执行onActivityResult()方法

           }

           protected void onActivityResult(int requestCode, int resultCode, Intent data) {

                  if(data == null) {                                                           // 没有数据,不执行

                         return;

                  }

                  System.out.println(requestCode + ", " + resultCode);         // code 可用来区分,哪里返回的数据

                  String name = data.getStringExtra("name");

                  int age = data.getIntExtra("age", -1);

           }

    Activity_B:

           public void close(View v) {

                  // == 关闭当前Activity时,设置返回的数据 ==

                  Intent intent = new Intent();

                  intent.putExtra("name", "典韦");

                  intent.putExtra("age", 55);

                  setResult(200, intent);   

                  finish();                        // 关闭,类似于点击了后退

           }

     

    5、生命周期

    1)Acitivity三种状态

    1. 运行:activity在最前端运行;
    2. 停止:activity不可见,完全被覆盖;
    3. 暂停:activity可见,但前端还有其他activity<>,注意:在当前Activitiiy弹出的对话框是Activity的一部分,弹出时,不会执行onPause方法;

    2)生命周期相关的方法(都是系统自动调用,都以 on 开头):

    1. onCreate:      创建时调用,或者程序在暂停、停止状态下被杀死之后重新打开时也会调用;
    2. onStart:                   onCreate之后或者从停止状态恢复时调用;                                                            
    3. onResume:   onStart之后或者从暂停状态恢复时调用,从停止状态恢复时由于调用onStart,也会调用onResume(界面获得焦点);
    4. onPause:       进入暂停、停止状态,或者销毁时会调用(界面失去焦点);
    5. onStop:          进入停止状态,或者销毁时会调用;
    6. onDestroy:    销毁时调用;
    7. onRestart:    从停止状态恢复时调用;

    3)生命周期图解:

     

           应用启动时,执行onCreate onStart onResume,退出时执行:onPause onStop onDestroy;

     

    6、横竖屏切换与信息的保存恢复

    切换横竖屏时,会自动查找layout-port 、layout-land中的布局文件,默认情况下,

    切换时,将执行摧毁onPause onStop onDestroy,再重置加载新的布局onCreate onStart onResume;

    切换时如果要保存数据, 可以重写: onSaveInstanceState();

    恢复数据时, 重写: onRestoreInstanceState();

    è固定横屏或竖屏:                                  android:screenOrientation="landscape"

    è横竖屏切换, 不摧毁界面(程序继续执行) android:configChanges="orientation|keyboardHidden|screenSize"

    保存信息状态的相关方法:

    1. onSaveInstanceState:    

    在Activity被动的摧毁或停止的时候调用(如横竖屏切换,来电),用于保存运行数据,可以将数据存在在Bundle中;

    1. onRestoreInstanceState:

    该方法在Activity被重新绘制的时候调用,例如改变屏幕方向,onSavedInstanceState可为onSaveInstanceState保存的数据

    7、启动模式

    1)任务栈的概念

    问:一个手机里面有多少个任务栈?

    答:一般情况下,有多少个应用正在运行,就对应开启多少个任务栈;  

           一般情况下,每开启一个应用程序就会创建一个与之对应的任务栈;

           二般情况下,如launchMode为 singleInstance,就创建自己单独的任务栈;

    2)任务栈的作用:

    它是存放Activity的引用的,Activity不同的启动模式,对应不同的任务栈的存放;

    可通过getTaskId()来获取任务栈的ID,如果前面的任务栈已经清空,新开的任务栈ID+1,是自动增长的;

    3)启动模式:

    在AndroidManifest.xml中的<activity>标签中可以配置android:launchMode属性,用来控制Actvity的启动模式;

    在Android系统中我们创建的Acitivity是以栈的形式呈现的:

    ①、standard:默认的,每次调用startActivity()启动时都会创建一个新的Activity放在栈顶;

    ②、singleTop:启动Activity时,指定Activity不在任务栈栈顶就创建,如在栈顶,则不会创建,会调用onNewInstance(),复用已经存在的实例

    ③、singleTask:在任务栈里面只允许一个实例,如果启动的Activity不存在就创建,如果存在直接跳转到指定的Activity所在位置,

                         如:栈内有ABCD,D想创建A, 即A上的BCD相应的Activity将移除;

    ④、singleInstance:(单例)开启一个新的任务栈来存放这个Activity的实例在整个手机操作系统里面只有一个该任务栈的实例存在,此模式开启的Activity是运行在自己单独的任务栈中的

     

    4)应用程序、进程、任务栈的区别

    ①、应用程序:

    四大组件的集合

    在清单文件中都放在application节点下

    对于终端用户而言,会将其理解为activity

    ②、进程:

    操作系统分配的独立的内存空间,一般情况下,一个应用程序会对应一个进程,特殊情况下,会有多个进程

    一个应用程序会对应一个或多个进程

    ③、任务栈:task stack(back stack)后退栈

           记录用户的操作步骤,维护用户的操作体验,

           专门针对于activity而言的,只用于activity

           一般使用standard,其他情况用别的

     

    5)启动模式的演示

    1、创建两个activity,布局中设置两个按钮,分别开启两个activity

    第一、standard启动模式的:开启几个就会在任务栈中存在几个任务

    01和02都是存在于一个任务栈中的

      

    第二、在清单文件中将02的启动模式改为singletop,

    此时02处于栈顶,就只会创建一个02的任务,再开启02,也不会创建新的

     

    第三、将02的启动模式改为singletask

           如果02上面有其他任务栈,就会将其他的清除掉,利用这个已经创建的02

           当开启02的时候,即先将01清除,然后利用下面的02

     

    第四、将02的启动模式改为singleinstance

           可以通过打印任务栈的id(调用getTaskId()方法)得知,两个activity不在同一个任务栈中

    若先开启三个01,在开启02,任务栈如图:

     

    再开启01,任务栈的示意图如下:

     

    此时按返回键,会先一层一层清空01,最后再清空02

    空进程:任务栈清空,意味着程序退出了,但进程留着,这个就是空进程,容易被系统回收;

    8、内存管理

           Android系统在运行多个进程时,如果系统资源不足,会强制结束一些进程,优先选择哪个进程来结束是有优先级的。

      会按照以下顺序杀死:

        ①、空:  进程中没有任何组件;

        ②、后台:进程中只有停止状态的Activity;

        ③、服务:进程中有正在运行的服务;

        ④、可见:进程中有一个暂停状态的Activity;

        ⑤、前台:进程中正在运行一个Activity;

      Activity在退出的时候进程不会销毁, 会保留一个空进程方便以后启动. 但在内存不足时进程会被销毁;

      Activity中不要在Activity做耗时的操作, 因为Activity切换到后台之后(Activity停止了), 内存不足时, 也容易被销毁;

     9.Content的startActivity方法需添加FLAG_ACTIVITY_NEW_TASK flag

      BootBroadcastReceiver继承自Android.content.BroadcastReceiver

    1. public void onReceive(Context context, Intent intent) {  
    2.     ……  
    3.     Intent startTaobao = new Intent(context, Start.class);  
    4.     startTaobao.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
    5.     context.startActivity(startTaobao);  
    6.     ……  

    10.Activity的启动与生命周期的监控(分析原码)

      应用程序被开启后,是需要开启并创建Activity,加载相应的view,从而展示出应用程序

      1、Activity是通过startActivity开启起来的,startActivity是由有Context调用的,其具体的实现类是ContextImpl

        在ContextImpl中的startActivity方法中,会调用ActivityThread的相关方法【mMainThread.getInstrumentation().execStartActivity()】;可以追溯到Instrumentation这个类,其中的execStartActivity()的方    法中实现了startActivity的调用:ActivityManagerNative.getDefault().startActivity,由此可以看出是底层进行处理。

      2、ActivityMonitor监控Activity

        当Activity实例创建的时候,就会给Activity配置一个监视器ActivityMonitor,监控Activity的声明周期:

        在Instrumentation的execStartActivity()的方法中,上来先判断ActivityMonitor是否为null:在第一次开启Activity的时候,ActivityMonitor还是null的,就会调用                        ActivityManagerNative.getDefault().startActivity(…..),是在操作native底层的信息,从而执行startActivity,再去开启一个Activity。

        简单来说,就是通过调用JNI,调用startActivity方法,开启Activity;创建好了之后,随即也创建好了Activity的监视器ActivityMonitor

      3、在ActivityMonitor中就有Activity各种生命周期的监控

        ①、在newActivity方法中:

          可以通过拿到Activity的字节码,创建一个Activity,并将这个Activity返回

               还会调用attach方法,传入ActivityThread的线程

        ②、在各种生命周期的方法中,调用activity的各自的生命周期的方法

      总结:

           1、通过PackageManagerService将所有用到的资源加载进内存中

           2、在Launcher中,将view等控件加载到ViewGroup中,点击每个item会有相应的操作

           3、在公开的文档中是找不到具体调用startActivity的类的,而是由系统完成调用的,实现了Activity的启动

        实际就是通过Context的实现类ContextImpl进行调用的,一步步转到底层(ActivityManagerNative)实现调用

           4、另一个重要的类就是ActivityMonitor,监控Activity生命周期的;在其newActivity方法中创建了Activity,并调用了attach方法;

        也就是说当一个Activity被创建的时候,就会绑定一个ActivityMonitor,用来监控Activity的生命周期

  • 相关阅读:
    将vue文件script代码抽取到单独的js文件
    git pull 提示错误:Your local changes to the following files would be overwritten by merge
    vue和uniapp 配置项目基础路径
    XAMPP Access forbidden! Access to the requested directory is only available from the local network.
    postman与newman集成
    postman生成代码段
    Curl命令
    POST方法的Content-type类型
    Selenium Grid 并行的Web测试
    pytorch转ONNX以及TnesorRT的坑
  • 原文地址:https://www.cnblogs.com/dubo-/p/6676079.html
Copyright © 2011-2022 走看看