面试题分享:Android 横竖屏切换时 Activity 的生命周期变化及资源相关系统

admin 2024-08-21 223 0

这是我最近收集的面试题集锦,献给年后找工作的同学们。这篇文章涉及的知识点比较广,可能比较零散,而且我省略了一些比较基础的知识(比如Android四大组件是什么之类的问题)。有些我附上了自己的理解,有些我附上了相关文章的详细链接。大家可以选择自己感兴趣的内容阅读,后续我会继续补充。

基础组件:横竖屏切换时Activity生命周期如何变化?

1.如果不配置android:ConfigChanges,系统会默认处理,重新构建Activity,这时候Activity生命周期又会走一遍。

android开发简历模板_简历模板软件开发_android简历模板

onSaveInstanceState() 和 onRestoreIntanceState()

当资源相关的系统配置发生改变或者资源不足时:比如屏幕旋转时,当前Activity会被销毁,在onStop之前会回调onSaveInstanceState,保存数据。Activity重新创建时,onStart之后会回调onRestoreInstanceState。Bundle数据会传递给onCreate(不一定带数据)和onRestoreInstanceState(肯定带数据)。当用户或者程序员主动销毁一个Activity时不会有回调,但是其他情况下会调用,用于保存界面信息。比如代码中调用finish()或者用户按返回时,都不会有回调。

2、如果设置了android:configChanges="orientation|keyboardHidden|screenSize">,Activity生命周期将不会重复,Activity也不会重建,只会回调onConfigurationChanged方法。

如何保证Service不被kill?简述Acitivty任务栈及使用方法

任务栈是后进先出的结构,栈顶的Activity具有焦点,当按下返回键时,栈中的Activity会逐个出栈,并调用其onDestory()方法,如果栈中没有Activity,系统会回收栈。每个APP默认只有一个栈,以APP包名命名。 1. Standard:默认模式:每次启动时都会创建一个新的Activity对象,并放入目标任务栈中

2、singleTop:判断当前任务栈顶是否存在相同的Activity对象,如果有则直接使用,如果没有则创建新的Activity对象并放入栈中。

3、singleTask:在任务栈中会判断是否有相同的Activity,如果有则清除该Activity上方的其他Activity对象的显示,如果没有则创建新的Activity并放到栈顶。

4、singleIntance:Activity会创建在一个新的任务栈中,且任务栈中只允许有一个Activity实例,其他调用该Activity的组件将直接使用任务栈中的Activity对象。

方法一:使用android:launchMode="standard|singleInstance|single Task|singleTop"来控制Acivity任务堆栈。

方法 2:Intent 标志:

Intent intent=new Intent();
intent.setClass(MainActivity.this, MainActivity2.class);
intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

有很多标志,例如:

Intent.FLAG_ACTIVITY_NEW_TASK 相当于 singleTask

Intent.FLAG_ACTIVITY_CLEAR_TOP 相当于 singleTop

相关文章:Android总结系列:Activity Intent Flags 和 Task 相关属性

与上下文相关的问题

Activity、Service、Application的Context不同,Activity继承自ContextThemeWraper,其他的都继承自ContextWrapper。

每个 Activity、Service 和 Application Context 都是 new ContextImpl 对象 getApplication() 来获取 Application 实例,但是这个方法只能在 Activity 和 Service 中调用。所以可能大多数情况下我们在 Activity 或 Service 中使用 Application,但如果我们想在一些其他场景中获取 Application 实例,比如 BroadcastReceiver 中,我们可以使用 getApplicationContext() 方法。

getApplicationContext()的作用域比getApplication()要广,任何Context的实例都可以通过调用getApplicationContext()方法来获取我们的Application对象。

上下文 (Context) 的数量等于活动 (Activity) 的数量 + 服务 (Service) 的数量 + 1,其中 1 表示应用程序 (Application)。

那么 Broadcast Receiver 和 Content Provider 呢?Broadcast Receiver 和 Content Provider 不是 Context 的子类。它们持有的 Context 是从其他地方传过来的,所以它们不计入 Context 总数中。

如何在Service中创建Dialog对话框

1.我们获取到Dialog对象之后,需要设置它的类型,也就是:

对话框.获取窗口()。设置类型(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)

2.在Manifest中添加权限:

非UI线程可以更新View中的UI吗?

能

当访问UI时,ViewRootImpl会调用checkThread方法检查当前在哪个线程访问UI,如果不是UI线程,则会抛出异常。在执行onCreate方法时,ViewRootImpl还没有创建,所以无法检查当前线程。在onResume方法回调后,ViewRootImpl才会被创建。

void checkThread() {    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

非UI线程是可以刷新UI的,前提是它有自己的ViewRoot,即更新UI的线程和创建ViewRoot的线程是同一个,或者在执行checkThread()之前UI就更新完了。

相关文章:Android子线程真的不能更新UI吗?

如何解决ScrollView嵌套ListView和GridView冲突

重写ListView的onMeasure方法,自定义高度:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

主要考察对MeasureSpec的三种模式的理解及相关文章。

自定义View优化策略

为了加快视图速度,您需要减少频繁调用方法的不必要代码。从 onDraw 开始。特别注意不要在此处进行内存分配,因为这会导致 GC,从而导致延迟。在初始化或动画间隔期间进行内存分配。不要在动画运行时进行内存分配。

您还需要尽可能减少 onDraw 的调用次数。大多数情况下,onDraw 是由调用 invalidate() 引起的。因此请尽量减少调用 invalidate() 的次数。如果可能,尽量调用带有 4 个参数的 invalidate() 方法,而不是不带参数的 invalidate()。不带参数的 invalidate() 将强制重绘整个视图。

另一个非常耗时的操作是请求布局。每当执行 requestLayout() 时,Android UI 系统都会遍历整个 View 层次结构来计算每个视图的大小。如果发现有冲突的值,则需要重新计算多次。此外,需要尽可能保持 View 层次结构平坦,这对于提高效率非常有帮助。

如果您有一个复杂的 UI,则应考虑编写自定义 ViewGroup 来执行其布局操作。与内置视图不同,自定义视图可以让程序仅测量这一部分,从而避免遍历整个视图层次结构来计算大小。PieChart 示例展示了如何将 ViewGroup 继承为自定义视图的一部分。PieChart 有子视图,但它从不测量它们。相反,它根据自己的布局规则直接设置它们的大小。

线程处理程序、消息、循环器、消息队列

1.相关概念解释

主线程(UI 线程)

定义:当程序首次启动时,Android会同时启动一个主线程。 功能:主线程主要负责处理UI相关的事件。

信息

定义:Handler接收并处理的消息对象(Bean对象)

功能:存储及传输通讯过程中的相关信息

线程本地

定义:线程内部的数据存储类

功能:负责存储和获取本线程的Looper

消息队列

定义:使用单链表数据结构存储消息列表

作用:用于存储Handler发送的消息,按照先进先出的方式执行

处理程序

定义:Message的主处理器

功能:负责向消息队列发送消息&处理Looper分配的消息

循环播放器

定义:充当Message Queue和Handler之间的桥梁

功能:消息循环:从消息队列中循环取出Message 消息分发:将取出的Message投递给对应的Handler

2. 绘制自己的图表

android简历模板_android开发简历模板_简历模板软件开发

3.Handler发送消息的方式有哪几种?

1. 发送消息(Message msg) 2. 发布(Ruunable r)

4.Handler处理消息的方式有哪几种?

这是直接来自 dispatchMessage() 源代码:

public void dispatchMessage(Message msg) {    if (msg.callback != null) {        //1. post()方法的处理方法
        handleCallback(msg);
    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;
            }
        }        //2. sendMessage()方法的处理方法
        handleMessage(msg);
    }
}//1. post()方法的最终处理方法private static void handleCallback(Message message) {
    message.callback.run();
}//2. sendMessage()方法的最终处理方法public void handleMessage(Message msg) {
}

5.Message、Handler、MessageQueen、Looper之间什么关系?

首先是MessageQueen,MessageQueen是一个消息队列,可以存储Handler发送的消息,它提供了入队和出队的方法,用于管理消息队列。入队和出队的原理都是利用单链表的数据结构进行插入和删除,即enqueueMessage()方法和next()方法。这里说的Message其实就是Bean对象,里面的属性就是用来记录Message的各种信息的。

然后,就有了这个Looper。Looper是一个可以循环的从MessageQueen中取出Message的循环器。它提供了初始化Looper和循环取出Message的方法,分别是prepare()方法和loop()方法。在prepare()方法中,Looper会关联一个MessageQueen,而Looper会存放在一个ThreadLocal中。在loop()方法中,通过ThreadLocal取出Looper,使用MessageQueen的next()方法取出Message。然后判断Message是否为空,如果是,则阻塞Looper,如果不是,则通过dispatchMessage()方法将Message分发给Handler,Handler执行handlerMessage()方法。由于handlerMessage()方法是空方法,所以这也是为什么Handler中需要重写handlerMessage()方法的原因。这里要注意的是,一个线程中只能有一个Looper。这里说的ThreadLocal其实就是不同线程中用来存储对应线程的Looper的对象。

最后就是Handler了,它是Looper和MessageQueen之间的桥梁。Handler提供了一系列发送Message的方法,最终通过MessageQueen的enqueueMessage()方法将Message存入MessageQueen中。我们通常可以在主线程中直接使用Handler,因为在应用程序启动的时候,在入口的main方法中已经默认帮我们创建好了Looper。

相关文章:blog.csdn.net/qq_30379689...

6.为什么在子线程中创建Handler会抛出异常?

Handler的工作依赖于Looper,而Looper(和消息队列)是属于某一个线程的(ThreadLocal是线程内部的数据存储类,通过它可以将数据存储在指定的线程中,其他线程无法获取),其他线程无法访问。所以Handler是间接和线程绑定的。所以要使用Handler需要保证Handler创建的线程中有Looper对象,并启动循环。因为默认子线程中没有Looper,所以会报错。正确的使用方式是:


  private final class WorkThread extends Thread {        private Handler mHandler;        public Handler getHandler() {            return mHandler;
        }          public void quit() {
            mHandler.getLooper().quit();
        }        @Override
        public void run() {            super.run();            //创建该线程对应的Looper,
            // 内部实现
            // 1。new Looper()
            // 2。将1步中的lopper 放在ThreadLocal里,ThreadLocal是保存数据的,主要应用场景是:线程间数据互不影响的情况
            // 3。在1步中的Looper的构造函数中new MessageQueue();
            //其实就是创建了该线程对用的Looper,Looper里创建MessageQueue来实现消息机制
            //对消息机制不懂得同学可以查阅资料,网上很多也讲的很不错。
            Looper.prepare();
            mHandler = new Handler() {                @Override
                public void handleMessage(Message msg) {                    super.handleMessage(msg);
                    Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
                }
            };            //开启消息的死循环处理即:dispatchMessage
            Looper.loop();            //注意这3个的顺序不能颠倒
            Log.d("WorkThread", "end");
        }
    }

处理线程

1.HandlerThread的作用

当系统有多个耗时任务需要执行时,每个任务都会启动一个新的线程去执行耗时任务,这样就会导致系统多次创建和销毁线程,从而影响性能。为了解决这个问题,Google 提供了 HandlerThread,在线程中创建一个 Looper 循环器,让 Looper 轮询消息队列。当有耗时任务进入队列时,不需要再启动新的线程,在原有线程中就可以执行耗时任务,否则线程会阻塞。

2.HandlerThread的优缺点

相关文章:Android多线程IntentService完全详解

意图服务

相关文章:Android多线程IntentService完全详解

异步任务

1.什么是AsyncTask

AsyncTask 是一个轻量级的异步任务类,可以在线程池中执行后台任务,然后将执行进度和最终结果传递给主线程并在主线程中更新 UI。AsyncTask 可以更方便地在主线程中执行后台任务并访问 UI,但是对于特别耗时的后台任务,AsyncTask 并不适合。对于特别耗时的任务,建议使用线程池。

2.如何使用AsyncTask

三个参数params:表示后台任务执行时的参数类型,该参数会传递给AysncTask的doInBackground()方法Progress:表示后台任务执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数result:表示后台任务返回结果的参数类型,该参数会作为onPostExecute()方法的参数五个方法onPreExecute():异步任务启动前的回调,在主线程中执行doInBackground():执行异步任务,在线程池中执行onProgressUpdate():doInBackground中调用publishProgress时的回调,在主线程中执行onPostExecute():异步任务执行完后的回调,在主线程中执行onCancelled():异步任务取消时的回调

3. AsyncTask导致的内存泄漏

原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放。 解决方案:AsyncTask持有外部Activity的弱引用。 将AsyncTask改为静态内部类Activity。 在onDestory()中调用AsyncTask.cancel()

4. 结果丢失

屏幕旋转或者Activity在后台被系统杀死都会导致Activity被重新创建,之前运行的AsyncTask会持有前一个Activity的引用,这个引用已经失效,此时调用onPostExecute()更新界面就不再起作用了。

5. AsyncTask 并行还是串行

AsyncTask在Android2.3之前默认使用并行执行,Android2.3之后默认使用串行执行,Android2.3之后如果需要使用并行执行,可以调用AsyncTask的executeOnExecutor();

6. AsyncTask 内部的线程池

私有静态易失性执行器sDefaultExecutor = SERIAL_EXECUTOR;

sDefaultExecutor是SerialExecutor的一个实例,是一个静态变量,也就是说一个进程内的所有AsyncTask对象都共用同一个SerialExecutor对象。

相关文章:Android多线程-AsyncTask工作原理深入分析

各种各样的

一般来说第一轮都是基础面试,需要扎实的基础。

最常用的Android基础知识

Java 基础

了解一些常见事物的原理,比如:handler,thread等。

项目中的技术要点

第二轮你需要了解更深入的东西

Android事件分发机制原理

Android绘图机制原理

WindowManager相关知识

进程间传输方式

Java内存管理机制

一些常用的list和map原理,以及子类之间的区别

进入第三轮基本没问题,但要注意以下问题

这一轮一般是问老板或者部门主管,问的问题一般有深度有广度,问到薪资问题要说一个合适的,小公司可以随便说,大公司一定要谨慎。不确定的时候可以告诉对方,要求他们给你一个合理的薪资,一般是在原有薪资基础上增加,听猎头说一般涨幅在15%-30%,超级NB可以要求30%以上,如果觉得自己还不错,很厉害,建议最多20%,对于普通人来说,定在15%左右最靠谱。公司内部一般有一套机制,根据公司情况来定。

我们的面试原则是拿到合理的薪资,拿到Offer。

个人发展情况。这个问题比较难回答,如果跟公司的方向不匹配,很有可能就进不了公司了。建议先试探性地问一下公司缺什么,自己能不能给公司提供相应的东西。当然,有自己追求的小伙伴可以大胆问。我的方向是架构师,哈哈哈哈,比较极端,别学我。我觉得选择是双向的,所以我知道自己需要什么。

你最擅长什么,UI 还是其他?这个问题就比较难回答了,你说你擅长 UI,是不是意味着你其他能力就不擅长?虽然不知道面试官的意图,但是我能感觉到这个问题不是那么容易回答的。我会回答什么都能做,什么业务需要我都承担。可能回答得不是很好,但是总之要符合公司的定位,这样就不会出错了。

文章摘录:一位拥有五年百度、阿里巴巴、聚美优品、映客面试经验的Android开发者

推荐的简历模板:

简历模板软件开发_android开发简历模板_android简历模板

简历模板软件开发_android开发简历模板_android简历模板