安卓面试基础

https://github.com/JsonChao/Awesome-Android-Interview

1、良好的开发习惯 命名、组件、模式
2、独立思考的能力
3、主动并且善于沟通

设计模式(代理、单例、工厂、build)
数据结构
java语言基础(反射、注解、注入)
UI布局和控件、自定义UI控件
Http和Socket编程、性能优化、内存优化
安全加解密算法

基本知识点(activity、service创建、广播、intent)
深入知识点 (AIDL、Binder、多进程通信、事件分发、UI绘制流程、Handler)
基本知识点细节(Service启动的区别、Activity启动模式,标记、异步任务)
系统核心机制(跨进程交互、Handler消息循环)

dlna 投屏 soap协议 web service

Activity面试详解

1、Activity的生命周期

与用户交互的接口

状态:

running/paused(不完全遮挡失去交互、内存紧张)/stoped/killed

生命周期

onCreate、onStart(无法交互)、onResume、onPause、onStop、onDestroy

点击home键回到主界面 onPause->onStop
再次回到原Activity时,onRestart->onStart->onResume
退出当前Activity onPause->onStop->onDestroy

横竖屏切换时,onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume

进程优先级

前台进程、可见进程、服务进程、后台进程、空进程

2、Activity的启动模式

standard 每次都会重新创建,都走生命周期

singleTop
栈顶复用,如果栈顶是当前Activity,则不会重新创建,onNewIntent

singleTask
栈内复用,如果栈内存在当前Activity,则将栈内该Activity以上的Activity全部出栈,onNewIntent

singleInstance
单例模式,独占一个任务栈

3、Activity的任务栈

后进先出的原则,一个activity的集合,与启动模式相关
任务栈全部退出应用程序
一个Activity可以独享一个任务栈

4、scheme跳转协议

一种页面跳转协议,可以用于APP内跳转,也可以用于APP与H5页面跳转
通过Intent.setData(Uri.parse(“scheme://host:port/path?query”))实现

Fragment面试详解

1、Fragment生命周期

节省内存、可静态和动态加载、第5大组件
onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestroyView->onDestroy->onDetach

2、Fragment的通信

1、在fragment中调用activity中的方法 getActivity
2、通过接口回调,activity中调用fragment中的方法
3、通过setArguments
4、通过EventBus
5、通过Fragment.setTargetFragment
6、通过Fragment.getChildFragmentManager
7、通过Activity的FragmentManager
8、在fragment中调用fragment中的方法,getActivity,findFragmentById

3、Fragment的懒加载

1、通过setUserVisibleHint
2、通过onHiddenChanged

4、FragmentPagerAdapter和FragmentStatePagerAdapter的区别

viewPager+Fragment 滑动使用
FragmentPagerAdapter适用于页面较少的情况,不会销毁不显示的Fragment,不会回收内存

FragmentStatePagerAdapter使用于页面较多的情况,会销毁不显示的Fragment,真正释放内存

5、Fragment的replace和add的区别

replace会销毁重建,add不会,添加到BackStack中,可以返回
remove移除

Service面试详解

1、Service生命周期

概念:后台执行长时间运行而没有用户界面的应用组件。运行在主线程中,不能做长时间耗时操作

两种启动方式:startService、bindService
startService->onCreate->onStartCommand->onDestroy
bindService->onCreate->onBind->onUnbind->onDestroy

如何服务已经启动onCreate只执行一次,多次bindService,onBind只执行一次

startService步骤:
1、创建Service类继承Service
2、在AndroidManifest.xml中注册Service
3、通过startService启动Service
4、通过stopService停止Service

bindService步骤:activity和service绑定进行通信,进程间通信,多个activity可以绑定同一个service
1、创建Service类继承Service,创建一个实现IBiner接口的实例对象并提供公共方法给客户端调用,从onBind回调中返回此IBiner实例
2、在AndroidManifest.xml中注册Service
3、通过bindService绑定Service,在客户端中从onServiceConnected回调中获取IBinder实例,通过IBinder实例调用Service中的方法
4、通过unbindService解绑Service

2、Service和Thread的区别

Service运行在主线程,Thread运行在子线程
Service可以在后台运行(返回主界面时,播放音乐,数据统计等),Thread不能
Service可以和Activity进行通信,Thread不能
要做耗时操作可以在Service中开启子线程,如何在activity中开启子线程,很难做到通信控制

3、Service和IntentService的区别

IntentService是Service的子类,用于执行后台任务
IntentService内部封装了HandlerThread和Handler

Broadcast面试详解

1、BroadcastReceiver生命周期

一种传输信息的机制,广播内容是一个intent,这个intent可以传递数据,发送一个可以被很多接受者接受

自定义广播接收器,用于接收系统或app发送的广播消息
onReceive,通过Binder机制向AMS发送广播
Binder机制是C/S架构,用于进程间通信

registerReceiver->onReceive->unregisterReceiver

2、BroadcastReceiver注册方式

静态注册:在AndroidManifest.xml中注册,全局有效,app退出后依然有效
动态注册:在代码中注册,只在当前组件有效,app退出后失效

3、BroadcastReceiver使用场景

同一个app具有多个进程的不同组件之间的消息通信
不同app直接组件直接的消息通信
1、系统广播:如开机、网络变化、拍照、短信等
2、App内广播:如登录、注销、支付成功等
3、App间广播:如订单状态改变、支付成功等

4、本地广播和全局广播的区别

LocalBroadcastManager:发送和接收广播只在同一个app内,安全性高,效率高,内部主要使用Handler实现。

全局广播:发送和接收广播可以在不同的app之间,安全性低,效率低

5、有序广播和无序广播的区别

有序广播:发送的广播按照优先级从高到低接收,可以拦截广播,拦截后后面的接收者无法接收到
无序广播:发送的广播按照发送顺序接收,接收者无法拦截广播

WebView面试详解

  • 常见的一些坑
    1、API16及之前的本部存在远程代码执行漏洞,没有正确限制使用Webview.addJavascriptInterface接口,导致恶意代码执行,远程攻击者可以通过使用Java Reflection API,调用目标Android应用的任意Java对象方法,最终导致远程代码执行。
    2、webview写在其他容器中,内存泄漏
    3、webviewClient.onPageFinished方法在页面加载完成多次调用
    需要调用webViewChromeClient.onProgressChanged方法,判断页面加载进度,当页面加载进度为100时,才调用onPageFinished方法
    4、后台耗电问题,后台线程运行
    5、硬件加速问题,webview开启硬件加速,会导致页面渲染速度变慢,消耗更多CPU和GPU资源,消耗电量

  • 避免内存泄漏问题
    1、独立进程,简单暴力,不过可鞥涉及进程间通信
    2、动态添加webview,在不需要时,动态从ViewGroup中移除

Binder面试详解

Android Binder是Android操作系统用于进程间通信(IPC)的一种机制。Binder机制以其高效、灵活以及安全的特点成为Android系统核心组件之间通信的基础。

基本概念

  1. Binder驱动

    • Binder驱动是Linux内核的一部分,位于/dev/binder
    • 它负责管理Binder通信的底层细节,包括内存管理、权限检查、线程调度等。
  2. Binder实体(Server)

    • Binder实体通常是一个服务,它实现了具体的业务逻辑。
    • 该实体在用户态程序中,通过注册到Binder驱动来对外提供服务。
  3. Binder代理(Client)

    • Binder代理是用于与Binder实体通信的对象。
    • 它通常位于客户端进程中,通过Binder驱动与Binder实体通信。
  4. Binder引用

    • Binder引用是实体或代理的句柄,客户端通过这个句柄向Binder驱动发送请求。

工作流程

以下是Binder机制的工作流程:

  1. 服务注册

    • 服务进程通过Binder驱动注册服务(addService)。
    • Binder驱动为这个服务创建一个唯一标识的Binder引用。
  2. 服务发现

    • 客户端进程通过ServiceManager查找所需服务的Binder引用(getService)。
  3. 发送请求

    • 客户端通过Binder引用向Binder驱动发送请求。
    • 请求包含目标服务的引用、方法ID、参数等信息。
  4. 处理请求

    • Binder驱动将请求传递给目标服务进程。
    • 服务进程处理请求,并通过Binder驱动将结果返回给客户端。

关键组件

  1. ServiceManager

    • 是一个特殊的系统服务,负责管理所有的系统服务。
    • 提供服务注册和查询功能。
  2. Parcel

    • 用于在进程间传递数据的容器。
    • 支持序列化和反序列化各种数据类型。
  3. IBinder

    • Binder通信的核心接口。
    • 所有Binder实体和代理都实现这个接口。
  4. Binder线程池

    • 每个进程中都有一个Binder线程池,用于处理来自Binder驱动的请求。
    • 提高并发处理能力。

安全性

Binder机制在设计上考虑了安全性:

  1. 权限检查

    • Binder驱动会对请求进行权限检查,确保只有有权限的进程可以访问服务。
  2. UID/GID

    • 每个请求都带有UID(用户ID)和GID(组ID),服务进程可以根据这些信息进行访问控制。
  3. 身份验证

    • 通过SELinux策略进行额外的访问控制,进一步提高安全性。

性能

Binder机制通过以下方式提高性能:

  1. 共享内存

    • 使用共享内存减少数据拷贝,提高传输效率。
  2. 内核级优化

    • 利用内核态的高效性,减少上下文切换,提供高效的IPC通道。

示例代码

以下是一个简单的服务端和客户端的示例:

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
public class MyService extends Binder {
public void sayHello() {
System.out.println("Hello from the service!");
}
}

public class MyServiceManager {
public static void main(String[] args) {
MyService service = new MyService();
ServiceManager.addService("MyService", service);
}
}

客户端:

1
2
3
4
5
6
7
public class MyClient {
public static void main(String[] args) {
IBinder binder = ServiceManager.getService("MyService");
MyService service = MyService.Stub.asInterface(binder);
service.sayHello();
}
}

总结

Android Binder机制是一个高效、安全且灵活的IPC机制,通过Binder驱动、服务注册与发现、权限检查等一系列步骤,实现了进程间的高效通信。在Android系统中,Binder机制被广泛用于系统服务和应用程序之间的通信,是Android系统的核心组成部分之一。

AIDL详解

Android接口定义语言(AIDL)是一种用于定义Android应用程序中不同进程间接口的工具。它允许你创建能够在不同进程之间通信的接口,类似于Binder机制。以下是AIDL的详细讲解:

基本概念

  1. AIDL文件

    • AIDL文件定义了进程间通信接口。
    • 使用.aidl作为文件扩展名。
  2. 接口定义

    • 在AIDL文件中定义接口和方法。
    • 支持基本数据类型、String、List、Map等。
  3. Parcelable对象

    • 自定义数据类型需实现Parcelable接口,以便跨进程传输。

工作流程

  1. 定义AIDL接口

    • 创建一个.aidl文件,定义需要在进程间通信的方法。
  2. 编写AIDL实现

    • 在服务端实现定义的AIDL接口。
  3. 生成Binder代理

    • Android编译器根据AIDL文件生成StubProxy类。
  4. 注册服务

    • 在服务端注册AIDL接口,供客户端调用。
  5. 客户端连接

    • 客户端通过ServiceConnection获取服务,并调用AIDL方法。

示例步骤

1. 定义AIDL接口

创建一个AIDL文件,比如IMyService.aidl

1
2
3
4
5
6
package com.example.myapp;

interface IMyService {
void sayHello();
int add(int a, int b);
}

2. 实现AIDL接口

在服务端实现这个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyService extends Service {
private final IMyService.Stub binder = new IMyService.Stub() {
@Override
public void sayHello() {
System.out.println("Hello from the service!");
}

@Override
public int add(int a, int b) {
return a + b;
}
};

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

3. 注册服务

AndroidManifest.xml中注册服务:

1
2
3
4
5
<service android:name=".MyService" android:exported="true">
<intent-filter>
<action android:name="com.example.myapp.IMyService" />
</intent-filter>
</service>

4. 客户端连接服务

在客户端,绑定服务并调用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyActivity extends Activity {
private IMyService myService;

private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = IMyService.Stub.asInterface(service);
try {
myService.sayHello();
int result = myService.add(5, 3);
System.out.println("Result: " + result);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};

@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent("com.example.myapp.IMyService");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
}

注意事项

  1. 线程和同步

    • 默认情况下,AIDL方法在服务端的Binder线程池中执行,需要考虑线程安全。
  2. 数据类型限制

    • AIDL支持的类型有限制,复杂类型需要实现Parcelable
  3. 性能

    • 尽量减少跨进程调用,因为每次调用都涉及序列化和反序列化。

总结

AIDL是Android中用于实现进程间通信的关键工具。通过定义接口、实现方法及管理服务连接,可以在不同的应用程序组件之间实现高效、可管理的通信。AIDL特别适用于需要在不同应用程序之间共享复杂数据结构和进行频繁通信的场景。