美文网首页
1、Activity

1、Activity

作者: Leo_JiangZhiHao | 来源:发表于2023-09-09 15:32 被阅读0次
Activity的显式启动和隐式启动
  • 显式启动:明确指定被启动对象的组件信息,包括包名和类名。显式启动是直接启动指定的activity,无需像隐式启动那样有个activity的过滤,效率很高,但是只适用同一应用之间的activity的跳转。
  • 隐式启动:不需要明确指定被启动对象的组件信息。隐式启动需要过滤,查找到与之匹配的activity,耗时很多。适用不同应用之间的跳转。
Activity任务栈是什么

任务栈即存放Activity任务的栈。当我们每打开一个Activity的时候它会就往Activity任务栈中压入一个Activity,当我们每销毁一个Activity的时候它会从Activity任务栈中弹出一个Activity,由于安卓系统自身的设计,我们只能在手机屏幕上获取当前一个Activity的焦点即栈顶元素,其余的Activity会在后台等待系统调用。
任务栈缺点:
1、每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差,需要点击多次返回才可以把程序退出了。
2、每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余,重复数据太多,会导致内存溢出的问题(OOM)。
为了解决任务栈的缺点,故引入了启动模式。

Activity的启动模式和使用场景
  • standard:标准模式。默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已经存在。用于普通Activity。
  • singleTop:栈顶复用模式。如果要启动的Activity已经处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,我们可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。用于通知栏启动的页面,否则多次点击通知栏会多次打开该Activity,返回会很奇怪。
  • singleTask:栈内复用模式。如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。不设置taskAffinity属性的话,默认为应用的包名。用于程序入口页面、WebView页面、扫一扫页面、电商中:购物界面,确认订单界面,付款界面。
  • singleInstance:单实例模式。该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。用于完全独立的页面,如闹钟的提示、锁屏页。
Activity的onNewIntent(Intent)方法什么时候会执行
  • 当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,此时会调用onNewIntent()方法。
  • 当ActivityA的LaunchMode为SingleInstance、SingleTask时,如果已经ActivityA已经在堆栈中,此时会调用onNewIntent()方法。
Activity常用的标记位Flags
  • FLAG_ACTIVITY_NEW_TASK:此标记位作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定相同android:launchMode="singleTask"
  • FLAG_ACTIVITY_SINGLE_TOP:此标记位作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定相同android:launchMode="singleTop"
  • FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,当它启动时,在同一个任务栈中位于它上面的Activity都要出栈。此标记位一般会和singleTask启动模式一起出现。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记位的Activity不会出现在历史Activity的列表中(这里点击手机除back和home键的另外一个键就会出现recent列表了),当某些情况我们不希望用户通过历史列表回到我们的Activity时这个标记比较有用。他等同于在XML中指定Activity的excludeFromRecents属性。android:excludeFromRecents="true"
Activity的生命周期
生命周期
onStart 和 onResume、onPause 和 onStop 的区别

onStart和onStop是从Activity是否可见这个角度来回调的。onResume和onPause是从Activity是否位于前台这个角度来回调的。

Activity A跳转Activity B,再按返回键,生命周期执行的顺序
  • A跳转B:A onPause → B onCreate → B onStart → B onResume → A onStop
  • B返回A:B onPause → A onRestart → A onStart → A onResume → B onStop → B onDestory
按home键再返回App、锁屏与解锁屏幕、跳转透明Activity界面再返回、启动一个 Theme 为 Dialog 的 Activity、弹出Dialog时Activity的生命周期
  • 按home键再返回App:onPause → onStop → onRestart → onStart → onResume
  • 锁屏与解锁屏幕:onPause → onStop → onRestart → onStart → onResume (需要指定屏幕的方向为android:screenOrientation="landscape",否则锁屏会从横屏到竖屏销毁一次,重建一次,解锁会从竖屏到横屏销毁一次,重建一次,共各两次)
  • 跳转透明Activity界面: A onPause → B onCreate → B onStart → B onResume (未调用A onStop)
  • 再返回:B onPause → A onResume → B onStop → onDestory (未调用A onRestart、onStart)
  • 启动一个 Theme 为 Dialog 的 Activity:同跳转透明Activity界面?
  • 弹出Dialog:什么都不会发生
横竖屏切换时Activity的生命周期

分两种情况:
1、不设置Activity的android:configChanges时,横竖屏切换会重新调用各个生命周期默认首先销毁当前activity,然后重新创建加载。
2、设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时, 横竖屏切换不会销毁Activity,不会执行各项生命周期,回调onConfigurationChanged方法。

系统回收Activity

安卓是通过回收activity的方式来回收内存的。activity处于onPause或者onStop状态时,假如系统资源不足(内存不足),会被系统回收释放。
1、当APP不在前台的时候,资源紧张,强杀APP进程并回收activity,这种情况不会调用生命周期的onDestroy方法。可以用“开发者选项”中的“限制后台进程数”来模拟这种情况。
2、当APP在前台,系统资源不足的时候,会回收APP处于pause或stop状态的Activity,这种情况不杀进程,但会调用onDestroy方法。可以用“开发者选项”中的“不保留活动”打开来模拟这种情况。

Activity的数据是怎么保存的,进程被杀后,保存的数据是怎么恢复的?

在onSaveInstanceState中保存数据,在onCreate或onRestoreInstanceState中恢复。

onSaveInstanceState()、onRestoreInstanceState()的调用时机

onSaveInstanceState方法是只有在系统觉得该Activity存在被系统自动回收的可能性时才会调用。(用户主动销毁Activity不调用,例如按下back键)
总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(锁屏)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
onRestoreInstanceState()方法只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
所以在前4种情况下,当前activity的生命周期为:onPause → onSaveInstanceState → onStop
在第5种情况下,当前activity的生命周期为:onPause → onSaveInstanceState → onStop → onDestroy → onCreate → onStart → onRestoreInstanceState → onResume (11版本前可能先调用onSaveInstanceState)

onCreate和onRestoreInstance中恢复数据时的区别
  • 因为onSaveInstanceState不一定会被调用,所以onCreate里的Bundle参数可能为空,如果使用onCreate来恢复数据,一定要做非空判断。而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用。
  • onRestoreInstanceState是在onStart之后被调用的。有时候我们需要onCreate中做的一些初始化完成之后再恢复数据,用onRestoreInstanceState会比较方便。
Activity之间传递数据的方式
  • 通过intent传递数据。直接传递,intent.putExtra(key, value);通过bundle,intent.putExtras(bundle)。这两种都要求传递的对象必须可序列化(Parcelable、Serializable)
  • Application。将数据保存早全局Application中,随整个应用的存在而存在,这样很多地方都能访问。
  • 单例。自定义一个单例DataHolder,并且设置setData和getData方法。在启动activity之前:DataHolder.getInstance().setData(data)。新的activity中获取数据:String data = DataHolder.getInstance().getData()。
  • 静态类。自定义一个静态类DataHolder,并且设置setData和getData方法,而且考虑到极端的情况,有可能传递的对象的内存是极其大的,所以为了不造成内存泄漏,我们将要传递的对象构造成一个弱引用保存到该静态类。
  • 持久化数据。sqlite、SharedPreference、file等。优点:应用中所有地方都可以访问;即使应用被强杀也不是问题。缺点:操作麻烦;效率低下;io读写容易出错。
  • 剪切板传递数据。ClipboardManager是系统剪切板服务的接口,用来放入或取出全局剪切板中的文本。
Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案?

有限制,过大会出现crash异常。该异常是TransactionTooLargeException,即传递数据数据过大异常。并且有提到一句话“Parcel objects stored in the Binder transaction buffer”,这表明实际上底层parcel对象在不同activity直接传递过程中保存在一个叫做“Binder transaction buffer”的地方,既然是缓冲区,肯定有大小限制。可以考虑Application、单例、静态类、持久化方法。

scheme协议
  • scheme协议是什么:android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面。
  • 使用场景:目前①②⑤使用场景很广,有没有一种熟悉的感觉?
    ①通过小程序,利用Scheme协议打开原生app。
    ②H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面。
    ③APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面。
    ④APP根据URL跳转到另外一个APP指定页面。
    ⑤通过短信息中的url打开原生app。
  • 协议格式:
String urlStr="http://www.ycbjie.cn:80/yc?id=hello&name=cg";
//url =            protocol + authority(host + port) + path + query
//协议protocol=    http
//域名authority=   www.ycbjie.cn:80
//页面path=          /yc
//参数query=       id=hello&name=cg
//authority =      host + port
//主机host=        www.ycbjie.cn
//端口port=        80
  • 链接样式:[scheme]://[host]/[path]?[query]
  • 使用方法:

1、AndroidManifest中设置增加拦截器(intent-filter),设置scheme。

<activity
    android:name=".ui.main.ui.activity.SchemeFirstActivity"
    android:screenOrientation="portrait">
    <!--Android 接收外部跳转过滤器-->
    <!--要想在别的App上能成功调起App,必须添加intent过滤器-->
    <intent-filter>
        <!-- 协议部分配置 ,注意需要跟web配置相同-->
        <!--协议部分,随便设置 yc://ycbjie:8888/from?type=yangchong  -->
        <data android:scheme="yc"
            android:host="ycbjie"
            android:path="/from"
            android:port="8888"/>


        <!--下面这几行也必须得设置-->
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <action android:name="android.intent.action.VIEW" />
    </intent-filter>
</activity>

2、在Activity中的重写OnCreate,获取跳转参数。

public class SchemeFirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Uri uri = getIntent().getData();
        if (uri != null) {
            //获取指定参数值
            String type = uri.getQueryParameter("type");
            Log.e("UrlUtils","main: " + type);

            if (type.equals("yangchong")) {
                ActivityUtils.startActivity(GuideActivity.class);
            } else if (type.equals("main")) {
                ActivityUtils.startActivity(MainActivity.class);
            }
        }
        finish();
    }
}

3、调用方式
原生调用:

Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("yc://ycbjie:8888/from?type=yangchong"));
startActivity(intent);

网页调用:

<a href="yc://ycbjie:8888/from?type=yangchong">打开叮咚app</a>

4、如何判断一个Scheme是否有效

PackageManager packageManager = getPackageManager();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("yc://ycbjie:8888/from?type=yangchong"));
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isValid = !activities.isEmpty();
if (isValid) {
    startActivity(intent);
}

5、Scheme在短信息中注意要点
设置android:scheme="http"或者android:scheme="https"后,点击短信息或者h5页面,发现没有跳到指定的页面,反而打开的是网页链接。

相关文章

网友评论

      本文标题:1、Activity

      本文链接:https://www.haomeiwen.com/subject/zukjvdtx.html