Android面试被问过的问题

本文概述了Java的基础知识,如基本数据类型,以及Android开发中的多线程概念、UI组件的scaleType属性、不同dpi处理、性能优化技巧、自定义View方法、Android版本更新特性、第三方库使用和对象序列化过程。
摘要由CSDN通过智能技术生成

Java部分


基础知识篇

八大基本类型

名称大小(byte)
boolean1
char2
byte1
short2
int4
long8
float4
double4

String、StringBuffer和StringBuilder

String声明的是不可变的对象,而StringBuffer和StringBuilder是可变的。但前者是线程安全的,而后者不是,同时后者的效率要高于前者,因此在多线程环境下会使用前者,否则后者。

多线程篇

线程和进程的区别

单位特点
进程系统运行的基本单位运行过程中相互独立,但每个进程包含的线程之间可互相影响
线程独立运行的最小单位一个进程包含多个线程,所有线程共享该进程的所有资源

进程通信方式

  1. 管道
  2. 共享内存
  3. 信号量机制(wait、notify)
  4. 消息队列通信(Handler)

Android部分


UI篇

scaleType属性

属性名说明
center

居中,不做任何缩放;

图片过大时,显示不全;

图片过小时,完整显示,有空白

centerCrop

居中,以填满控件为目的;

保持宽高比缩放,直到图像宽高都 ≥ 控件宽高;

图片和控件宽高比例不等时,图片会被裁剪

centerInside

居中,以图片正常显示为目的;

图片过大时,按原图比例缩小,直到宽高之一等于控件宽高之一;

图片过小时,完整显示,有空白

fitCenter

居中;

保持宽高比缩放,直到宽高都 ≤ 控件宽高;

ImageView的默认缩放方式

fitEnd

居右侧或底部;

保持宽高比缩放,直到宽高都 ≤ 控件宽高

fitStart

居左侧或上侧;

保持宽高比缩放,直到宽高都 ≤ 控件宽高

fitXY按照控件大小拉伸,不保持原比例,填满控件
matrix不改变原图大小,从控件左上角开始绘制,超出裁剪

不同分辨率对应的dpi

首先需要明确,想要一套布局适用多个分辨率,可以将组件大小和字体大小的单位分别改为dp和sp。其中dp无关像素,sp是可伸缩像素

名称与px的换算关系对应分辨率对应dpi
ldpi0.75240x320120
mdpi1320x480160
hdpi1.5480x800240
xhdpi2720x1280320
xxhdpi31080x1920480

性能调优的布局优化

有以下方式:

  1. 使用RelativeLayout代替LinearLayout,可以减少因为后者引起的嵌套层级
  2. 使用抽象布局标签include、merge和ViewStub
  3. 使用最新的ConstraintLayout,它不需要使用任何嵌套

include、merge和ViewStub的具体区别:

include可以用来引入另一个布局文件:

<include layout="@layout/list_item"/>

merge通常和include配套使用:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <include layout="@layout/list_item"/>
    
</merge>

使用 <include> 时,默认情况下会在父布局中生成一个多余的视图层级。

这就是 <merge> 标签发挥作用的地方。当您使用 <merge> 标签包围 <include> 标签时,它告诉系统只合并引用布局中的视图层级,而不会生成多余的视图层级。这有助于减少视图层级,提高性能,并且可以使布局更具可读性。

 ViewStub用于延迟加载,它允许您在程序运行时动态地向布局中添加视图,而不需要将这些视图包含在初始布局中:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ViewStub
        android:id="@+id/stub"
        android:inflatedId="@+id/subTree"
        android:layout="@layout/my_sub_tree"
        android:layout_width="120dip"
        android:layout_height="40dip" />
    
    <!-- 可见或隐藏的其他视图 -->
    
</FrameLayout>

自定义View

开发方式包括:

  1. 组合控件
  2. 继承控件
  3. 自绘控件

Android版本篇

不同android版本之间的差异(此处仅列出11、12与他们前一版本之间的差异):

版本号(API)与前作的差异
11,API 30
  1. 需要apk签名方案v2或更高版本
  2. 读取应用列表权限
12,API 31-32
  1. 前台服务优化,原因是被过度使用
  2. android: exported:启动的activity需要设置这一属性为true
  3. 位置权限变更:允许app只访问大概的位置,在manifest文件中请求权限
  4. 新增附近设备权限

第三方库篇

使用诸如okhttpretrofit、EventBus等需要在gradle中implements的都是引用了第三方库。

序列化篇

先将一个对象序列化成可存储或传输的状态,传递给另一个activity或者其他地方后,再将其反序列化为一个新的对象。

多媒体

桌面的音视频小组件是如何实现“播放”、“上一曲”、“下一曲”这种操作的?

使用MediaSession和MediaController可以实现。

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.session.MediaControllerCompat;
import android.media.session.MediaSessionCompat;
import android.media.session.PlaybackStateCompat;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;

public class MyMusicService extends Service {

    private static final String TAG = "MyMusicService";
    private MediaPlayer mediaPlayer;
    private MediaSessionCompat mediaSession;
    private final IBinder musicBinder = new MusicServiceBinder();

    public class MusicServiceBinder extends Binder {
        MyMusicService getService() {
            return MyMusicService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();

        // 初始化 MediaSession。
        mediaSession = new MediaSessionCompat(this, TAG);

        // 启用来自 MediaButtons 和 TransportControls 的回调。
        mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

        // 不让 MediaButtons 在应用不可见时重新启动播放器。
        mediaSession.setMediaButtonReceiver(null);

        // 设置初始的 PlaybackState 包含 ACTION_PLAY,以便媒体按钮可以启动播放器。
        PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
                .setActions(
                        PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PAUSE |
                                PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                                PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                                PlaybackStateCompat.ACTION_STOP);
        mediaSession.setPlaybackState(stateBuilder.build());

        // MySessionCallback 包含来自媒体控制器的回调方法。
        mediaSession.setCallback(new MySessionCallback());

        // 由于服务正在运行,启动 MediaSession。
        mediaSession.setActive(true);

        // 创建内部的 MediaPlayer 并将其附加到此会话。
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnCompletionListener(mp -> {
            // 当这首歌播放完毕时,跳转到下一首。
            mediaSession.setPlaybackState(
                    stateBuilder.setState(PlaybackStateCompat.STATE_STOPPED, 0, 1).build());
        });

        // 为了本示例的目的,自动开始播放。
        // 对于真实的应用程序,您应该让播放器处于停止状态,
        // 直到用户请求播放。
        startPlaying();
    }

    // ...

    private void startPlaying() {
        // 播放一首歌。
        // 为了简单起见,我们只为持续时间设置一个随机的位置。
        mediaPlayer.reset();
        try {
            mediaPlayer.setDataSource("http://sample-song-url.com");
            mediaPlayer.prepare();
            mediaPlayer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 更新状态为 PLAYING。
        PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1f)
                .setActions(
                        PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PAUSE |
                                PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                                PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                                PlaybackStateCompat.ACTION_STOP);
        mediaSession.setPlaybackState(stateBuilder.build());
    }

    // ...

    @Override
    public IBinder onBind(Intent intent) {
        return musicBinder;
    }

    // ...

    private class MySessionCallback extends MediaSessionCompat.Callback {
        @Override
        public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
            // 将媒体按钮事件转发给 MediaController。
            return MediaControllerCompat.getMediaController(MyMusicService.this)
                    .dispatchMediaButtonEvent(mediaButtonIntent);
        }

        @Override
        public void onPlay() {
            startPlaying();
        }

        @Override
        public void onPause() {
            mediaPlayer.pause();
            PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
                    .setState(PlaybackStateCompat.STATE_PAUSED, mediaPlayer.getCurrentPosition(), 1f)
                    .setActions(
                            PlaybackStateCompat.ACTION_PLAY |
                                    PlaybackStateCompat.ACTION_PAUSE |
                                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                                    PlaybackStateCompat.ACTION_STOP);
            mediaSession.setPlaybackState(stateBuilder.build());
        }

        @Override
        public void onSkipToNext() {
            // 跳转到下一首。
            startPlaying(); // 为了简单起见,只是重新开始当前的歌曲。
        }

        @Override
        public void onSkipToPrevious() {
            // 跳转到上一首。
            startPlaying(); // 为了简单起见,只是重新开始当前的歌曲。
        }

        @Override
        public void onStop() {
            // 停止播放。
            stopPlaying();
        }
    }

    private void stopPlaying() {
        // 停止播放。
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;

        // 更新状态为 STOPPED。
        PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_STOPPED, 0, 1f)
                .setActions(
                        PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PAUSE |
                                PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                                PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                                PlaybackStateCompat.ACTION_STOP);
        mediaSession.setPlaybackState(stateBuilder.build());
    }

    // ...
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值