Android面试 四大组件之Activity;

1.Activity生命周期

onCreate():是 activity 的创建时候调用,做一些初始化布局之类的操作
onStart():即将显示页面的时候调用。用户无法操作,也是初始化操作,
onresume():当前 Activity 处于栈顶,获取焦点可以和用户进行交互,处于运行状态
onPause():暂停状态。可能被其他的 activity
覆盖,仍然可见,但是失去焦点不能和用户进行交互
onstop():完全不可见的时候被调用。处于停止状态。内存不足这个 Activity
可能会被杀死,进行资源回收。
ondestroy():activity 被销毁的时候调用,进行资源释放。
onRestart():从不可见的时候变成可见。

成对出现:
onCreate 和 onDestroy:根据 activity 创建和销毁
onStart 和 onstop:根据 activity 是否可见,
onResume 和 onpause:根据 activity 是否显示在前台

(1)从A->B->A

A 到 B 
A(oncreate() -> onStart()->onResume()) ——> A(onpause()) ——> B(oncreate() -> onStart()->
onResume())——> A(onstop())

返回 B
B (onpause()) ——> A(onRestart() -> onStart()->onResume()) ——> B(onstop()->onDestroy())
总结:页面可见性发生改变时候,当前页面先调用 onpause();

(2)横竖屏切换时activity的生命周期

1. AndroidManifest没有设置configChange属性的时候
启动:
oncreate -> onstart -> onResume
切换横竖屏->onPause -> onSaveInstaceState ->onstop->onDestrory 
重新启动可见->oncreate -> onstart -> onReStoreInstanceState-> onResume   

总结:
没有设置configChanges属性,Android6.0,7.0,8.0系统手机表现都是一样的,当前的界面调用onSavelnstanceState走一遍流程,然后重启调用onRestorelnstanceState再走一遍完整流程,最终destory.

2.AndroidManifest设置了configChanges,android:configChanges="orientation”竖屏,

Android 6.0

启动:
onCreate --> onStart --> onResume
切换横屏:
onPause -->onSavelnstanceState -->onStop -->onDestroy 
-->onCreate-->onStart-->onRestorelnstanceState-->onResume 

Android 7.0

onConfigurationChanged-->onPause -->onSavelnstanceState -->onStop -->onDestroy 
-->onCreate-->onStart -->onRestorelnstanceState-->onResume 

Android 8.0

onConfigurationChanged

总结: 设置了configChanges属性为orientation之后
Android 6.0 同没有设置configChanges情况相同,完整的走完了两个生命周期,调用了onSavelnstanceState和onRestorelnstanceState方法;
Android 7.0 则会先回调onConfigurationChanged方法,剩下的流程跟Android 6.0 保持一致;
Android 8.0 系统更是简单只是回调了onConfigurationChanged方法,并没有走Activity的生命周期方法。

2.Activity 的四个启动模式

FLAG_ACTIVITY_SINGLE_TOP:对应 singleTop 启动模式。
FLAG_ACTIVITY_NEW_TASK:对应 singleTask 模式。
FLAG_ACTIVITY_CLEAR_TOP:同一任务栈的处于此Activity之上的都会出栈,和singleTask一起出现,触发OnNewIntent();

标准模式

就是不管 activity 存不存在都 new 一个 Activity 出来放在当前任务栈的栈顶,
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 跳转到 A,则任务栈中就是 ABCDA。
比较常见的场景是:社交应用中,点击查看用户 A 信息->查看用户 A 粉丝->在粉丝中挑选查看用户 B 信息->查看用户
A 粉丝… 这种情况下一般我们需要保留用户操作 Activity 栈的页面所有执行顺序。

Singletop

如果要启动的 Activity 处于栈顶,则调用 onNewIntent()
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 要跳转到它本身的 D ,调用 onNewIntent()
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 要跳转到 B,新建一个 B 压入栈顶,任务栈中就是 ABCDB

举例:三条推送点进去是一个 Activity,SingTop App
用户收到几条好友请求的推送消息,需要用户点击推送通知进入到请求者个人信息页,将信息页设置为SingleTop
模式就可以增强复用性。

SingTask

根据 TaskAffinity 寻找对应的任务栈。
如果任务栈不存在,那就新建任务栈,新建 activity
实例。
如果任务栈存在:任务栈中不存在该 Activity 实例,就新建一个 Activity
压入栈
任务栈中存在该实例,将该实例顶部的实例出栈,并将自己置于栈顶

比如 ABCD,要从 D 通过 Intent 跳转到 B(走 onNewIntent),则弹出 CD 销毁,变成 AB

举例,首页肯定在栈底。
使用举例:浏览器首页,用户从多个应用启动浏览器首页,主页面仅仅启动一次,其余都走onNewIntent,并且清空主页面上的其他页面。

onNewIntent 的调用时机:singleTop、singleTask、singleInstance 模式下都会调用 onNewIntent()。
目的:复用Activity;
onCreate() 和 onNewIntent() 不会被同时调用。

调用 onNewIntent()生命周期如下:onNewIntent()->onRestart()->onStart()->onResume()。

onNewIntent 方法, 如下所示:

protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent); 
    setIntent(intent); 
}

注意:没有在 onNewIntent()里面设置 setIntent()方法,将最新的 intent 设置给这个 activity 实例。那么在
onNewIntent()里面的 getIntent()得到的 intent 都是旧数据。

onNewIntent(Intent intent)方法就是提供给 singleTask 模式这种特定实现的有效保持 intent 上下文的方法;

单例模式

一个实例单独占一个任务栈,全局唯一性,如果使用时已经存在就将该任务栈调度到前台。

任务栈是 APP 管理 Activity 的一种容器 ,一般一个应用程序一个任务栈,任务栈管理该应用的 activity 进出栈

taskAffinity 属性能给 Activity 指定 task,但必须使用 FLAG_ACTIVITY_NEW_TASK 标记
默认的 taskAffinity 常用于独立栈:

用例:比如闹钟的提醒页面,点击之后进入闹钟详情,再返回原app不影响原来的app

3.Activity/Dialog/PopupWindow/Toast与WindowState:

* Activity/Dialog/PopupWindow/Toast在WMS都有对应的WindowState,
* 只是Activity/Dialog/PopupWindow的WindowState属于同一个AppWindowToken,也就是Activity的token,
* 而Toast的WindowState属于自己独有的WindowToken。

4. Android 程序中 Context 分成两种。一种是 Activity Context,另一种是 Application Context。

凡是跟 UI 相关的,都应该使⽤Activity 做为 Context 来处理

通过 Application Context 来启动 Activity 的话。就需要 FLAG_ACTIVITY_NEW_TASK 属性,不管这个 Activity
是属于其他程序还是自己这个程序的。

使用:

Intent intent = new Intent(new Intent(this, TestActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); MyApplication.getContext().startActivity(intent);

如果我们用ApplicationContext 去启动一个 LaunchMode 为 standard 的 Activity 的时候会报错

android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity
context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

因为⾮Activity类型的 Context 并没有所谓的任务栈,所以待启动的 Activity 就找不到栈了。

为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity 是以 singleTask 模式启动的。

在 Application 和 Service 中去 layout.inflate 也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。

对于 startActivity 操作

①当为 Activity Context 则可直接使用

②当为其他 Context, 则必须带上 FLAG_ACTIVITY_NEW_TASK flags 才能使用,因为⾮ Activity context 启动Activity 没有 Activity 栈,则无法启动,因此需要加开启新的栈;

getApplication和getApplicationContext区别?

1.对于Activity/Service来说,getApplication()和getApplicationContext()的返回值完全相同; 除非厂商修改过接口;

2.BroadcastReceiver在onReceive的过程,能使用getBaseContext.getApplicationContext获取所在Application,而无法使用getApplication;

3.ContentProvider能使用getContext().getApplicationContext获取所在应用程序。绝大多数情况下没有问题,但是有可能会出现空指针的问题,
情况如下:
当同一个进程有多个apk的情况下,对于第二个apk是由provider方式拉起的,前面介绍过provider创建过程并不会初始化所在应用程序,此时执行返回的结果便是空。所以对于这种情况要做好判空。

5.activit相关问题

(1)onDestory()一定会执行吗?

正常情况下的返回 onDestory 一定会执行的, 后台强杀可能会发生:

当前仅有一个 Activity,这时候强杀,会执行,
当前很多 activity 实例,从 A 到 B 到 C,后台强杀只会 A 的 onDestroy,BC 都不会执行了。

(2)onStop()一定会执行吗?

如果要启动的是个透明的窗口,或者是对话框的样式,就不会执行。onstop 用于停止更新UI。

(3)怎么写一个Activity 的统一管理类:

⑴定义一个 ActivityManager 实现 Application.ActivityLifecycleCallbacks;

⑵ List<WeakReference> mActivityStack;

public class ActivityManager implements Application.ActivityLifecycleCallbacks {
    public static final String TAG = "ActivityManager";
    private static ActivityManager instance = new ActivityManager();

    public static ActivityManager getActivityManager() {
        return instance;
    }

    // 当前所有的activity
    private static final List<WeakReference<Activity>> mActivityStack = new ArrayList<>();

    // 获取所有的activity
    public List<WeakReference<Activity>> getActivityStack() {
        return mActivityStack;
    }

    public Activity getCurrentActivity() {
        return mCurrentActivity.get();
    }

    /**
     * 当前的activity,弱引用持有防止内存泄漏
     */
    @SuppressLint("StaticFieldLeak")
    private  WeakReference<Activity> mCurrentActivity;


    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        mCurrentActivity = new WeakReference<>(activity);

    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {

    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {

    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {

    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {

    }

    private void addActivity(@NonNull Activity activity) {
        for (WeakReference<Activity> activityWeakReference : mActivityStack) {
            if (activityWeakReference.get() == activity) {
                return;
            }
            mActivityStack.add(new WeakReference<>(activity));
        }
    }

    private void removeActivity(@NonNull Activity activity) {
        Iterator<WeakReference<Activity>> iterator = mActivityStack.listIterator();
        while (iterator.hasNext()) {
            WeakReference<Activity> activityWeakReference = iterator.next();
            if (activityWeakReference.get() == activity || activityWeakReference == null) {
                iterator.remove();
            }
        }
    }
}

6.Intent 可传递的数据类型有 3 种

1.java 的 8 种基本数据类型(boolean byte char short int long float double)、String 以及他们的数组形式;
2.Bundle 类,Bundle 是一个以键值对的形式存储可传输数据的容器;
3.实现了 Serializable 和 Parcelable 接口的对象,这些对象实现了序列化。

Intent 传输数据的大小有限制吗?如何解决?
答:Intent 中的 Bundle 是使用 Binder 机制进行数据传送的,数据会写到内核空间,
Binder 的缓冲区是有大小限制的,有些 ROM 是 1M, 有些 ROM 是 2M;

解决办法:
1.尽量减少传输数据量。
2.Intent 通过绑定一个 Bundle 来传输,这个可以超过 1M,不过也不能过大。
3.通过内存共享,使用静态变量或者使用 EventBus 等类似的通信工具。
4.通过文件共享。
  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值