Android中的Activity相关知识点整理

定义

Activity:是Android与用户进行交互的接口,它提供了一个界面供用户点击,滑动等操作,这就是Activity的意义


Activity生命周期:

1.activity的4种状态

  • running:表明activity正活跃,用户可以点击屏幕,应用做出相应;它是一个处于Activity栈顶的一个状态
  • paused:表明Activity失去焦点的时候,或者被一个非全屏的activity占据,或者被一个透明的activity放置在栈顶;这时候Activity失去了和用户交互的能力;不是说整个Activity被销毁,这时候它的状态信息和成员变量都在;如果处于内存不足的时候会被回收
  • stopped:一个Activity被另一个Activity完全覆盖的时候,这个被覆盖的activity就处于stopped状态;已经不可见了;这时候它的状态信息和成员变量都在;如果处于内存不足的时候会被回收
  • killed:表面activity已经被系统回收掉了,它所保存的信息和成员变量都不在了。

2.activity生命周期

这里写图片描述
Activity启动 –> onCreate() –> onstart() –> onResume()

onCreate():创建Activity时候回调第一个方法,可以做一些初始化的操作,比如布局的设置,view的初始化,对象的实例化,图片预加载等,activity还处于不可见状态,避免做耗时操作,以免出现黑屏的情况

onstart():表明activity正在启动,已经处于可见的状态了,但不是处于前台显示的状态,用户还不能与其交互,点击滑动等;就是一种看见但是无法触摸的一个状态

onResume():已经在前台显示了,用户可以与其进行交互,比如点击滑动等操作;

点击home键回到主界面 –> onPause() –> onStop()

onPause():表明整个activity处于停止状态,就是可见但是不能触摸,无法进行交互的状态;在这里也不要做耗时操作,因为回调这个方法完才会跳转到其它activity,而且android中指定如果onPause在500ms内没有执行完毕的话就会强制关闭Activity。

onStop():表示停止,被完全覆盖了,此时activity已经不可见了,但是activity还在内存中,没有被回收;但是内存如果紧张,有可能被回收

再次回到原Activity –> onRestart() –> onStart() –> onResume()

onRestart():表明activity正在重新启动,是不可见状态回到可见状态时候被调用

退出当前activity –> onPause() –> onStop() –> onDestory()

onDestory():表明当前activity正在被销毁,整个生命周期方法中的最后一个,可以做一些资源释放操作


Android进程优先级

前台 / 可见 / 服务 / 后台 / 空

  1. 前台进程:一般是前台正在交互的activity或者与前台activity绑定的service,这两种情况的进程就是可见进程
  2. 可见进程:比如一个activity处于可见但并不是处于前台,就是用户不能进行交互,这种就是可见进程
  3. 服务进程:在后台开启一个service服务这种是服务进程
  4. 后台进程:比如按home键,activity的前台进程就变成了后台进程,这时候进程不会被立马kill掉,会根据内存情况处理
  5. 空进程:在这五个进程里优先级最低,表示是一种没有活跃的组建,只是处于缓存的目的而保留,android系统可以随时杀掉它

Android任务栈

这里写图片描述

任务是一个activity的集合,它用栈的方式来管理activity;这个栈被称为返回栈(back stack),栈里的activity顺序是按打开顺序放置。

当用户在home界面点击应用图标时候,这个应用的任务就会被转移到前台,如果这个应用的任务是空的,说明最近这个应用没有被启动过,系统就会去创建一个新的任务,将该应用的主activity放入到返回栈中。

当一个activity启动了一个新activity的时候,新的activity会被放置到返回栈的栈顶并获取焦点;前一个activity仍然保留在任务栈,但处于停止状态。
当用户按下返回键的时候,处于栈顶的activity会被移除掉,前一个activity就会重新回到栈顶的位置。我们只能向栈顶添加activity或者将栈顶的activity移除掉。这说明返回栈是一个典型的后进先出的数据结构
如果一直按返回键,返回栈中的activity会一个一个的被移除,最终返回到主屏幕,这时候返回栈中activity全被清空,对应的任务也就不存在了。

当打开一个应用,对应的任务处于前台;这时候点击home键回到主屏幕,任务就被转移到后台;当任务处于后台状态的时候,返回栈中的activity都进入停止状态,但在返回栈中的顺序不会变,每个activity的信息和数据都在;当处于内存不足的情况下有可能会被销毁回收
这里写图片描述

如果你的应用程序有多个入口可以启动同一个activity,默认情况下每次启动都会创建一个该activity的新的实例,并不是将下面的activity移动到栈顶;带来的一个问题就是同一个activity在一个返回栈里存在多个实例;势必会引起一些问题。
这里写图片描述

Android系统管理任务和返回栈的方式就是把所有的activity都放入到一个相同的任务当中,通过一个后进先出的栈来管理;开发者可以通过修改activity启动模式来修改这一行为。


Activity启动模式

启动模式允许你去定义如何将一个activity的实例和当前任务进行关联,有两种方式来定义启动模式

  1. 使用manifest文件:在manifest文件中声明一个activity的时候指定launchMode属性
  2. 使用Intent flag参数:当使用Intent启动activity的时候,可以在Intent中加入flag来指定新启动的activity如何与当前任务进行关联

如果一个被启动的activity在manifest里定义了启动模式,然后在使用Intent启动的时候也设置了flag定义启动模式,那么Intent中的定义模式将会覆盖manifest中的定义

使用manifest定义启动模式

通过设置launchMode属性来定义,有四种可选参数:

  1. standard:这也是默认启动模式,即标准模式;如果在该文件中声明activity的时候不指定这个参数值,这个activity就模式使用这种模式;它的意思是每启动一个activity都会重新创建一个该activity的实例加入到当前任务中,会走完整的生命周期函数,就算任务中已经存在了这个activity的实例还是会创建。这样就会出现上面说的一个activity在返回栈中存在多个实例,这其实是非常消耗资源的。
  2. singleTop:即栈顶复用模式,这个中文翻译很有意思,给出了两个很重要的点,栈顶和复用;也就是说如果要启动的activity在返回栈内已经存在了一个实例并且还处于栈顶的位置,那么就不会在重新创建一个实例了,而是复用这个activity;复用体现在哪呢,就是调用这个activity的onNewIntent方法,而不会走onCreate-onStart-onResume这个创建逻辑了;如果这个activity不在栈顶,那么还是会重新创建的。
  3. singleTask:即栈内复用模式,也是一种单例模式;当启动的activity如果在栈内有实例,不管在不在栈顶都会复用这个实例,将其置于栈顶,调用这个activity的onNewIntent方法,并且将其上面的所有activity进行出栈处理,全部销毁。
    这里有个注意点:android系统会检测要启动的activity的affinity和当前任务的affinity是否相同,如果相同就会把这个activity放入到当前任务中,不同的话就会创建一个新的任务。而同一个程序中所有的activity的affinity默认都是相同的,不同的程序是不同的,这样启动别的应用的这种模式的activity会创建一个新的任务,启动自己应用的activity不会创建新的任务。
  4. singleInstance:即单例模式,即这个activity自己独享一个任务,所在的返回栈里面只有这个activity。
    比如给activity2设置singleTask模式,从activity1跳转到activity2,activity2跳转到activity3,再从在activity3跳转到activity2;这时候连续按两次返回键会返回到activity3再到activity1.
    再举一个例子,Android系统内置的浏览器程序声明自己浏览网页的Activity始终应该在一个独立的任务当中打开,也就是通过在元素中设置”singleTask”启动模式来实现的。这意味着,当你的程序准备去打开Android内置浏览器的时候,新打开的Activity并不会放入到你当前的任务中,而是会启动一个新的任务。而如果浏览器程序在后台已经存在一个任务了,则会把这个任务切换到前台。

使用Intent Flags 定义启动模式

使用方法就是在使用startActivity的时候构建Intent,对Intent加入一个flag来改变Activity与任务的关联模式

  1. FLAG_ACTIVITY_NEW_TASK:当Intent对象包含这个标记时,系统会寻找或创建一个新的task来放置目标Activity,寻找时依据目标Activity的taskAffinity属性进行匹配,如果找到一个task的taskAffinity与之相同,就将目标Activity压入此task中,如果查找无果,则创建一个新的task,并将该task的taskAffinity设置为目标Activity的taskActivity,将目标Activity放置于此task。注意,如果同一个应用中Activity的taskAffinity都使用默认值或都设置相同值时,应用内的Activity之间的跳转使用这个标记是没有意义的,因为当前应用task就是目标Activity最好的宿主
  2. FLAG_ACTIVITY_SINGLE_TOP:设置了这个flag,如果要启动的Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么就不会再次创建这个Activity的实例,而是直接调用它的onNewIntent()方法;否则会再创建这个activity;这种flag和在launchMode中指定”singleTop”模式所实现的效果是一样的。
  3. FLAG_ACTIVITY_CLEAR_TOP:设置了这个flag,如果要启动的Activity在当前任务的栈顶,就会销毁这个实例并重新创建这个activity;如果不在栈顶,还是销毁已存在的实例并清空这个实例上面的所有activity,最后重新创建这个activity。
  4. 如果将Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP两个结合用,如果要启动的Activity已经存在任务栈而且不在栈顶,那么会清空这个activity以上的所有activity,并回调这个activity的onNewIntent,不会重新创建这个activity;如果处于栈顶,就直接回调onNewIntent

taskAffinity参数

affinity可以用于指定一个Activity更加愿意依附于哪一个任务,在默认情况下,同一个应用程序中的所有Activity都具有相同的affinity,所以,这些Activity都更加倾向于运行在相同的任务当中。当然了,你也可以去改变每个Activity的affinity值,通过元素的taskAffinity属性就可以实现了。
taskAffinity属性接收一个字符串参数,你可以指定成任意的值(经我测试字符串中至少要包含一个.),但必须不能和应用程序的包名相同,因为系统会使用包名来作为默认的affinity值。
通常可以与allowTaskReparenting属性配合使用,设置为true时,Activity就拥有了一个转移所在任务的能力。具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。


清空返回栈

如果用户将任务切换到后台之后过了很长一段时间,系统会将这个任务中除了最底层的那个Activity之外的其它所有Activity全部清除掉。当用户重新回到这个任务的时候,最底层的那个Activity将得到恢复。这个是系统默认的行为,因为既然过了这么长的一段时间,用户很有可能早就忘记了当时正在做什么,那么重新回到这个任务的时候,基本上应该是要去做点新的事情了。

当然,既然说是默认的行为,那就说明我们肯定是有办法来改变的,在元素中设置以下几种属性就可以改变系统这一默认行为:

  1. alwaysRetainTaskState:如果将最底层的那个Activity的这个属性设置为true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。
  2. clearTaskOnLaunch:如果将最底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将最底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。
  3. finishOnTaskLaunch:这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。

scheme跳转协议

它是一种页面内跳转协议,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面,主要用于支持以下几种场景

  1. 通过scheme协议,服务器可以定制化告诉app跳转哪个页面
  2. 通过scheme协议,可以通过通知栏消息定制化跳转页面
  3. 通过scheme协议,可以由h5页面跳转app原生页面

协议的完整格式如下

mango://appwork/man?id=8897&name=angel

  1. mango:这个代表scheme协议格式,通过格式去筛选需要调起的activity;可以通过uri.getScheme()获取;比如是http://开头的,就会调起浏览器程序
  2. appwork:代表Scheme协议名或者说域名;比如https://www.baidu.com,可以通过uri.getAuthority()或者uri.getHost()获取www.baidu.com
  3. /man:表示指定页面(路径),可以通过uri.getPath()获取
  4. id=8897&name=angel:就是具体的参数名对应具体值,所有的参数名可以通过uri.getQueryParameterNames()获取到一个Set集合,可以通过uri.getQueryParameter(“id”)获取具体参数值

在使用scheme协议跳转到具体页面时需要先检查下目的页面是否存在

public void valldCheck(Uri uri){
        PackageManager packageManager = getPackageManager();
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
        boolean isValid = !activities.isEmpty();
        if (isValid) {
            startActivity(intent);
        }
    }

在使用的时候需要在manifest里给目的activity配置scheme信息

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data
                    android:scheme="mango"
                    android:host="appwork"
                    android:path="/man"
                    />
            </intent-filter>

 </activity>

使用方式

场景一
先获取服务器给我们的uri,然后跳转到本应用的activity或者其它应用的activity,可以使用如下方式;这个协议需要双方约定好,其实大公司用的比较多,因为像阿里腾讯同时有好几个app装在用户的手机上,需要相互调用

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("mango://appwork/man?id=8897&name=angel"));
startActivity(intent);

场景二
而在H5页面的时候可以通过WebViewClient的shouldOverrideUrlLoading方法去解析scheme协议,然后进行相应的跳转,

mWebView.setWebViewClient(new WebViewClient() {
                  @Override
                  public boolean shouldOverrideUrlLoading(WebView view, String url) {

                      Log.e(TAG,"shouldOverrideUrlLoading url="+url);
                      // 根据协议的参数,判断是否是所需要的url
                      // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                      //假定传入进来的 url = "mango://appwork/man?id=8897&name=angel"(同时也是约定好的需要拦截的)
                      Uri uri = Uri.parse(url);
                      // 如果url的协议 = 预先约定的 mango 协议
                      // 就解析往下解析参数
                      if ( uri.getScheme().equals("mango")) {

                          // 如果 authority  = 预先约定协议里的 appwork,即代表都符合约定的协议
                          // 所以拦截url,下面JS开始跳转到native指定页面
                          if (uri.getAuthority().equals("appwork")) {

                              Intent intent = new Intent(Intent.ACTION_VIEW,uri);
                              startActivity(intent);
                          }
                          return true;
                      }
                      return super.shouldOverrideUrlLoading(view, url);
                  }
              }
        );
    }

scheme使用注意:

  1. 当别人使用scheme协议调用我们的activity的时候,如果使用FLAG_ACTIVITY_NEW_TASK启动activity,由上面使用Intent Flags 定义启动模式可知,这时候系统可能为这个activity新建一个任务,导致与被调用的应用的其它activity不处于同一个任务,这时候需要在manifest里给activity设置taskAffinity值,通常是应用包名,强制处于同一个任务中。
  2. 当应用A启动应用B的activity时候,可能应用B所在的任务还是处于后台,这样需要在应用B被启动的activity里做处理,当收到启动请求后强制把当前任务移到前台
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);  
activityManager.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME);

猜你喜欢

转载自blog.csdn.net/qq_30993595/article/details/81072473
今日推荐