月薪20+的Android面试都问这些问题(含答案)

简介: 金三银四跳槽季即将过完,相信大家肯定急需一套Android面试宝典,今天给大家准备了我珍藏已久的Android高阶面试宝典,供大家学习 !【文末有干货】 一、面试题: 1.自定义Handler时如何避免内存泄漏2.onNewIntent的调用时机3.RecyclerView相比ListView有哪些优势4.谈一谈Proguard混淆技术5.ANR出现的场景及解决方案 二、详细解析: 1.自定义Handler时如何避免内存泄漏 一般非静态内部类持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。

金三银四跳槽季即将过完,相信大家肯定急需一套Android面试宝典,今天给大家准备了我珍藏已久的Android高阶面试宝典,供大家学习 !【文末有干货】

webp

一、面试题:

1.自定义Handler时如何避免内存泄漏
2.onNewIntent的调用时机
3.RecyclerView相比ListView有哪些优势
4.谈一谈Proguard混淆技术
5.ANR出现的场景及解决方案

二、详细解析:

1.自定义Handler时如何避免内存泄漏

一般非静态内部类持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。为了避免这个问题,我们可以自定义的Handler声明为静态内部类形式,然后通过弱引用的方式,让Handler持有外部类的引用,从而可避免内存泄漏问题。

以下是代码实现

private WeakReference < MainActivity > activityWeakReference;

private MyHandler myHandler;

static class MyHandler extends Handler {

    private MainActivity activity;

    MyHandler(WeakReference < MainActivity > ref) {

        this.activity = ref.get;

    }

    @Override

    public void handleMessage(Message msg) {

        super.handleMessage(msg);

        switch (msg.what) {

        case 1:

            //需要做判空操作
            if (activity != ) {

                activity.mTextView.setText("new Value");

            }

            break;

        default:

            Log.i(TAG, "handleMessage: default ");

            break;

        }

        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            //在onCreate中初始化
            activityWeakReference = new WeakReference < MainActivity > (this);

            myHandler = new MyHandler(activityWeakReference);

            myHandler.sendEmptyMessage(1);

            mTextView = (TextView) findViewById(R.id.tv_test);

        }

2.onNewIntent的调用时机

在Android应用程序开发的时候,从一个Activity启动另一个Activity并传递一些数据到新的Activity上非常简单,但是当您需要让后台运行的Activity回到前台并传递一些数据可能就会存在一点点小问题。

首先,在默认情况下,当您通过Intent启到一个Activity的时候,就算已经存在一个相同的正在运行的Activity,系统都会创建一个新的Activity实例并显示出来。为了不让Activity实例化多次,我们需要通过在AndroidManifest.xml配置activity的加载方式(launchMode)以实现单任务模式,如下所示:

<activity android:label="@string/app_name"

android:launchmode="singleTask"android:name="Activity1">

</activity>

launchMode为singleTask的时候,通过Intent启到一个Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法

前提:ActivityA已经启动过,处于当前应用的Activity堆栈中;当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent方法

当ActivityA的LaunchMode为SingleInstance,SingleTask时,如果已经ActivityA已经在堆栈中,那么此时会调用onNewIntent方法

当ActivityA的LaunchMode为Standard时,由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法,仍然调用的是onCreate方法

以下是代码实例

  • 设置MainActivity的启动模式为SingleTask(栈内复用)
<activity

android:name=".MainActivity"

android:launchMode="singleTask">

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

  • MainActivity中重写onNewIntent方法
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mButton;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mButton = (Button) findViewById(R.id.forward_btn);

        mButton.setOnClickListener(new View.OnClickListener {

            @Override

            public void onClick(View view) {

                startActivity(new Intent(MainActivity.this, Main2Activity.class));

            }

        });

        @Override

        protected void onNewIntent(Intent intent) {

            Toast.makeText(this, "onnewIntent", Toast.LENGTH_SHORT).show;

            Log.i(TAG, "onNewIntent: i done....");

        }

  • Main2Actvity执行点击跳转,MainActivity被复用,执行onNewIntent方法
public class Main2Activity extends AppCompatActivity {

    private Button mButton;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main2);

        mButton = (Button) findViewById(R.id.btn);

        mButton.setOnClickListener(new View.OnClickListener {

            @Override

            public void onClick(View view) {

                startActivity(new Intent(Main2Activity.this, MainActivity.class));

                finish;

            }

        });

3.RecyclerView相比ListView有哪些优势

首先需要解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)

其次RecyclerView提供了添加、删除item的动画 效果,而且可以自定义

RecyclerView相比ListView优势在于可以轻松实现:

  • ListView的功能
  • GridView的功能
  • 横向ListView的功能
  • 横向ScrollView的功能
  • 瀑布流效果
  • 便于添加Item增加和移除动画

不过一个挺郁闷的地方就是,系统没有提供ClickListener和LongClickListener。

不过我们也可以自己去添加,只是会多了些代码而已。

实现的方式比较多,你可以通过mRecyclerView.addOnItemTouchListener去监听然后去判断手势,

当然你也可以通过adapter中自己去提供回调

webp

4.谈一谈Proguard混淆技术

Proguard技术有如下功能:

  • 压缩 --检查并移除代码中无用的类
  • 优化--对字节码的优化,移除无用的字节码
  • 混淆--混淆定义的名称,避免反编译
  • 预监测--在java平台对处理后的代码再次进行检测

代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。

那么为什么要使用代码混淆呢?

因为Java是一种跨平台的解释性开发语言,而java的源代码会被编译成字节码文件,存储在.class文件中,由于跨平台的需要,java的字节码中包含了很多源代码信息,诸如变量名、方法名等等。并且通过这些名称来访问变量和方法,这些变量很多是无意义的,但是又很容易反编译成java源代码,为了防止这种现象,我们就需要通过proguard来对java的字节码进行混淆,混淆就是对发布的程序进行重新组织和处理,使得处理后的代码与处理前的代码有相同的功能,和不同的代码展示,即使被反编译也很难读懂代码的含义,哪些混淆过的代码仍能按照之前的逻辑执行得到一样的结果。

但是,某些java类是不能被混淆的,比如实现了序列化的java类是不能被混淆的,否则反序列化时会出问题。

下面这类代码混淆的时候要注意保留,不能混淆。

  • Android系统组件,系统组件有固定的方法被系统调用。
  • 被Android Resource 文件引用到的。名字已经固定,也不能混淆,比如自定义的View 。
  • Android Parcelable ,需要使用android 序列化的。

其他Anroid 官方建议 不混淆的,如

  • android.app.backup.BackupAgentHelper
  • android.preference.Preference
  • com.android.vending.licensing.ILicensingService
  • Java序列化方法,系统序列化需要固定的方法。
  • 枚举 ,系统需要处理枚举的固定方法。
  • 本地方法,不能修改本地方法名
  • annotations 注释
  • 数据库驱动
  • 有些resource 文件

5.ANR出现的场景及解决方案

在Android中,应用的响应性被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当用户触发了输入事件(如键盘输入,点击按钮等),如果应用5秒内没有响应用户的输入事件,那么,Android会认为该应用无响应,便弹出ANR对话框。而弹出ANR异常,也主要是为了提升用户体验。

解决方案是对于耗时的操作,比如访问网络、访问数据库等操作,需要开辟子线程,在子线程处理耗时的操作,主线程主要实现UI的操作

webp

这些是小编根据上面的高级工程师技术大纲整理的一套系统全面而且非常深入的Android进阶资料

webp
webp

命运永远会眷顾那些努力付出的人。如果你还没达到你的目标,那是因为你努力的程度还不够!

希望看到最后的朋友们,通过一年到两年的努力,都能实现追求的目标。

相关文章
|
2天前
|
Android开发 Kotlin
Android经典面试题之Kotlin的==和===有什么区别?
本文介绍了 Kotlin 中 `==` 和 `===` 操作符的区别:`==` 用于比较值是否相等,而 `===` 用于检查对象身份。对于基本类型,两者行为相似;对于对象引用,`==` 比较值相等性,`===` 检查引用是否指向同一实例。此外,还列举了其他常用比较操作符及其应用场景。
154 93
|
5天前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
37 20
Android经典面试题之图片Bitmap怎么做优化
|
1月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关,并且消息处理机制在整个Android知识体系中也是尤其重要,在太多的源码分析的文章讲得比较繁琐,很多人对整个消息处理机制依然是懵懵懂懂,这篇文章通过一些问答的模式结合Android主线程(UI线程)的工作原理来讲解,源码注释很全,还有结合流程图,如果你对Android 消息处理机制还不是很理解,我相信只要你静下心来耐心的看,肯定会有不少的收获的。
100 3
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
2月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
**Kotlin中的`by lazy`和`lateinit`都是延迟初始化技术。`by lazy`用于只读属性,线程安全,首次访问时初始化;`lateinit`用于可变属性,需手动初始化,非线程安全。`by lazy`支持线程安全模式选择,而`lateinit`适用于构造函数后初始化。选择依赖于属性特性和使用场景。**
96 5
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
|
1月前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟悉整套的分发机制有助于更好的分析各种点击滑动失效问题,更好去扩展控件的事件功能和开发自定义控件,同时事件分发机制也是Android面试必问考点之一,如果你能把下面的一些事件分发图当场画出来肯定加分不少。废话不多说,总结一句:事件分发机制很重要。
99 9
|
1月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
69 2
|
2月前
|
监控 Android开发 开发者
Android经典面试题之实战经验分享:如何简单实现App的前后台监听判断
本文介绍在Android中判断应用前后台状态的两种方法:`ActivityLifecycleCallbacks`和`ProcessLifecycleOwner`。前者提供精细控制,适用于需针对每个Activity处理的场景;后者简化前后台检测,适用于多数应用。两者各有优劣:`ActivityLifecycleCallbacks`更精确但复杂度高;`ProcessLifecycleOwner`更简便但可能在极端场景下略有差异。根据应用需求选择合适方法。
27 2
|
2月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
40 8
|
2月前
|
Android开发 开发者
Android经典面试题之SurfaceView和TextureView有什么区别?
分享了`SurfaceView`和`TextureView`在Android中的角色。`SurfaceView`适于视频/游戏,独立窗口低延迟,但变换受限;`TextureView`支持复杂变换,视图层级中渲染,适合动画/视频特效,但性能略低。两者在性能、变换、使用和层级上有差异,开发者需按需选择。
44 1
|
2月前
|
SQL Java Unix
Android经典面试题之Java中获取时间戳的方式有哪些?有什么区别?
在Java中获取时间戳有多种方式,包括`System.currentTimeMillis()`(毫秒级,适用于日志和计时)、`System.nanoTime()`(纳秒级,高精度计时)、`Instant.now().toEpochMilli()`(毫秒级,ISO-8601标准)和`Instant.now().getEpochSecond()`(秒级)。`Timestamp.valueOf(LocalDateTime.now()).getTime()`适用于数据库操作。选择方法取决于精度、用途和时间起点的需求。
39 3