记录 Android 面试题, 有时间过来翻翻。

博主博客

目录

  • 六十一、ThreadLocal的原理及使用场景?
  • 六十二、如何计算View的嵌套层级?
  • 六十三、MVC、MVP、MVVM的区别及如何选择?
  • 六十四、SharedPreferences的apply()和commit()区别?
  • 六十五、Base64和MD5是加密算法吗?
  • 六十六、HttpClient和HttpURLConnection的区别?
  • 六十七、Activity A跳转B后返回,生命周期顺序?
  • 六十八、如何拦截短信?
  • 六十九、LocalBroadcast与全局广播的区别?
  • 七十、如何选择第三方库?

六十一、ThreadLocal的原理及使用场景?

(一)ThreadLocal核心原理

1. 基本概念

// ThreadLocal基本使用
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("线程私有数据");
String value = threadLocal.get(); // 获取当前线程存储的值
threadLocal.remove(); // 移除数据,防止内存泄漏
  • 本质:线程局部变量,为每个线程提供独立的变量副本
  • 目的:实现线程隔离,避免多线程环境下的数据竞争

2. 内部实现机制

(1)ThreadLocal数据结构
// Thread类内部结构(简化)
public class Thread {
    // 每个线程持有一个ThreadLocalMap
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
    // InheritableThreadLocal继承的Map
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

// ThreadLocalMap内部结构(简化)
static class ThreadLocalMap {
    // 使用弱引用Entry数组存储数据
    private Entry[] table;
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
        // 存储的实际值(强引用)
        Object value;
        
        Entry(ThreadLocal<?> k, Object v) {
            super(k);  // 对ThreadLocal的弱引用
            value = v; // 对value的强引用
        }
    }
}
(2)关键操作原理
// ThreadLocal.set()方法原理
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t); // 获取当前线程的ThreadLocalMap
    if (map != null) {
        // 以ThreadLocal实例为key,存储value
        map.set(this, value);
    } else {
        // 首次使用,创建ThreadLocalMap
        createMap(t, value);
    }
}

// ThreadLocal.get()方法原理
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 以ThreadLocal实例为key获取Entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果不存在,返回初始值
    return setInitialValue();
}

3. 内存模型与引用关系

线程A ──────持有───────▶ ThreadLocalMap实例A
   │                       │
   │                   包含Entry数组
   │                       │
   └─关联───ThreadLocal实例◀──弱引用─── Entry1
                           │        │
                           │        └─强引用─── Value1(线程A的数据)
                           │
                           ◀──弱引用─── Entry2
                                   │
                                   └─强引用─── Value2(线程A的另一个数据)

线程B ──────持有───────▶ ThreadLocalMap实例B
                           │
                           ◀──弱引用─── Entry3
                                   │
                                   └─强引用─── Value3(线程B的数据)

(二)主要使用场景

1. 线程安全的工具类封装

(1)SimpleDateFormat线程安全封装
// ❌ 错误用法:直接使用(非线程安全)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date()); // 多线程下可能出错

// ✅ 正确用法:使用ThreadLocal封装
public class DateUtil {
    private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    
    public static String format(Date date) {
        return dateFormatThreadLocal.get().format(date);
    }
    
    public static Date parse(String dateStr) throws ParseException {
        return dateFormatThreadLocal.get().parse(dateStr);
    }
    
    // 注意:用完及时清理,防止内存泄漏
    public static void remove() {
        dateFormatThreadLocal.remove();
    }
}

// 使用示例
try {
    String formatted = DateUtil.format(new Date());
    Date date = DateUtil.parse("2024-01-01 12:00:00");
} finally {
    DateUtil.remove(); // 确保清理
}
(2)其他非线程安全类的封装
// Random线程安全封装
public class ThreadLocalRandomUtil {
    private static final ThreadLocal<Random> randomThreadLocal =
        ThreadLocal.withInitial(Random::new);
    
    public static int nextInt(int bound) {
        return randomThreadLocal.get().nextInt(bound);
    }
}

// StringBuilder线程安全封装(适用于频繁拼接字符串的场景)
public class ThreadLocalStringBuilder {
    private static final ThreadLocal<StringBuilder> stringBuilderThreadLocal =
        ThreadLocal.withInitial(() -> new StringBuilder(1024));
    
    public static StringBuilder get() {
        StringBuilder sb = stringBuilderThreadLocal.get();
        sb.setLength(0); // 清空之前的内容
        return sb;
    }
}

2. 上下文信息传递

(1)Web应用中的用户上下文
// 用户上下文管理
public class UserContext {
    private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
    
    public static void setCurrentUser(User user) {
        currentUser.set(user);
    }
    
    public static User getCurrentUser() {
        return currentUser.get();
    }
    
    public static void clear() {
        currentUser.remove();
    }
}

// 在拦截器/过滤器中设置用户信息
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) {
        User user = extractUserFromRequest(request);
        UserContext.setCurrentUser(user);
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              Exception ex) {
        // 请求完成后清理ThreadLocal
        UserContext.clear();
    }
}

// 业务代码中获取用户信息(无需传递参数)
@Service
public class OrderService {
    public Order createOrder(OrderRequest request) {
        User currentUser = UserContext.getCurrentUser(); // 直接获取
        // 创建订单逻辑...
        return order;
    }
}
(2)分布式追踪ID传递
// 跟踪ID管理(用于日志追踪、调用链追踪)
public class TraceContext {
    private static final ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
    private static final ThreadLocal<String> spanIdHolder = new ThreadLocal<>();
    
    public static void setTraceId(String traceId) {
        traceIdHolder.set(traceId);
    }
    
    public static String getTraceId() {
        String traceId = traceIdHolder.get();
        return traceId != null ? traceId : generateTraceId();
    }
    
    public static void clear() {
        traceIdHolder.remove();
        spanIdHolder.remove();
    }
    
    private static String generateTraceId() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}

// MDC(Mapped Diagnostic Context)配合ThreadLocal使用
public class LoggingAspect {
    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 设置追踪ID
        String traceId = TraceContext.getTraceId();
        MDC.put("traceId", traceId);
        
        try {
            return joinPoint.proceed();
        } finally {
            // 清理
            MDC.clear();
            TraceContext.clear();
        }
    }
}

3. 数据库连接与事务管理

(1)MyBatis等ORM框架中的应用
// MyBatis中的SqlSession管理
public class SqlSessionManager {
    private static final ThreadLocal<SqlSession> sqlSessionThreadLocal = 
        new ThreadLocal<>();
    
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if (sqlSession == null) {
            sqlSession = openNewSqlSession();
            sqlSessionThreadLocal.set(sqlSession);
        }
        return sqlSession;
    }
    
    public static void closeSqlSession() {
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if (sqlSession != null) {
            sqlSession.close();
            sqlSessionThreadLocal.remove();
        }
    }
}

// 事务管理
public class TransactionManager {
    private static final ThreadLocal<Connection> connectionHolder = 
        new ThreadLocal<>();
    private static final ThreadLocal<Boolean> transactionActive = 
        ThreadLocal.withInitial(() -> false);
    
    public static void beginTransaction() {
        try {
            Connection connection = DataSourceUtils.getConnection();
            connection.setAutoCommit(false);
            connectionHolder.set(connection);
            transactionActive.set(true);
        } catch (SQLException e) {
            throw new RuntimeException("开启事务失败", e);
        }
    }
    
    public static void commit() {
        if (transactionActive.get()) {
            try {
                connectionHolder.get().commit();
            } catch (SQLException e) {
                throw new RuntimeException("提交事务失败", e);
            } finally {
                endTransaction();
            }
        }
    }
    
    private static void endTransaction() {
        try {
            Connection connection = connectionHolder.get();
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            // 记录日志
        } finally {
            connectionHolder.remove();
            transactionActive.remove();
        }
    }
}

4. Android开发中的使用场景

(1)Looper中的使用
// Android中Looper的实现
public final class Looper {
    // ThreadLocal存储每个线程的Looper实例
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("每个线程只能有一个Looper");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

// 在主线程初始化Looper
public static void prepareMainLooper() {
    prepare(false); // 主线程不允许退出
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("主线程Looper已经创建");
        }
        sMainLooper = myLooper();
    }
}
(2)Android中的其他应用
// 在Android中管理语言环境
object LocaleManager {
    private val localeThreadLocal = ThreadLocal<Locale>()
    
    fun setLocale(locale: Locale) {
        localeThreadLocal.set(locale)
    }
    
    fun getLocale(): Locale {
        return localeThreadLocal.get() ?: Locale.getDefault()
    }
}

// 在协程中使用ThreadLocal(需要特殊处理)
val userContext = ThreadLocal<String>()
fun launchWithContext() {
    val userName = userContext.get() ?: "Guest"
    
    // 协程中需要手动传递ThreadLocal值
    GlobalScope.launch(Dispatchers.Default + userContext.asContextElement()) {
        // 在这个协程中可以访问userContext.get()
        println("User: ${userContext.get()}")
    }
}

(三)内存泄漏问题与解决方案

1. 内存泄漏原因分析

(1)ThreadLocalMap的Entry设计
// Entry使用弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value; // 强引用
    
    Entry(ThreadLocal<?> k, Object v) {
        super(k);  // 弱引用:key(ThreadLocal实例)
        value = v; // 强引用:实际存储的值
    }
}

泄漏场景

  • 当ThreadLocal实例被回收(弱引用)后,Entry的key变为null
  • 但value仍然被Entry强引用,无法被回收
  • 如果线程长时间运行(如线程池),会导致value无法释放
(2)线程池中的泄漏
// 线程池场景下的内存泄漏
ExecutorService executor = Executors.newFixedThreadPool(4);

// 任务中使用ThreadLocal
Runnable task = () -> {
    ThreadLocal<byte[]> local = new ThreadLocal<>();
    local.set(new byte[1024 * 1024]); // 1MB数据
    
    // 任务执行完后,线程返回线程池
    // 如果没有调用remove(),1MB数据不会被释放
    // 下次任务复用线程时,会设置新的值,旧值仍然存在
};

executor.execute(task);

2. 解决方案与最佳实践

(1)始终使用try-finally清理
// 标准使用模式
ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();

try {
    Connection connection = getConnection();
    connectionHolder.set(connection);
    
    // 执行业务逻辑
    executeBusinessLogic();
} finally {
    // 确保清理
    Connection connection = connectionHolder.get();
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            // 记录日志
        }
    }
    connectionHolder.remove(); // 关键:清理ThreadLocal
}
(2)使用remove()方法清理
public class SafeThreadLocalUsage {
    private static final ThreadLocal<SimpleDateFormat> dateFormat =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
    public String formatDate(Date date) {
        try {
            return dateFormat.get().format(date);
        } finally {
            // 每次使用后清理
            dateFormat.remove();
        }
    }
}
(3)继承InheritableThreadLocal的注意事项
// InheritableThreadLocal会传递给子线程
public class InheritableThreadLocalDemo {
    private static final InheritableThreadLocal<String> inheritableThreadLocal =
        new InheritableThreadLocal<>();
    
    public static void main(String[] args) {
        inheritableThreadLocal.set("父线程数据");
        
        Thread childThread = new Thread(() -> {
            // 子线程可以获取父线程设置的值
            System.out.println("子线程获取: " + inheritableThreadLocal.get());
            
            // 子线程需要清理自己的数据
            inheritableThreadLocal.remove();
        });
        
        childThread.start();
        
        // 父线程也需要清理
        inheritableThreadLocal.remove();
    }
}
(4)使用FastThreadLocal(Netty优化版)
// Netty的FastThreadLocal优化了性能并减少了内存泄漏风险
import io.netty.util.concurrent.FastThreadLocal;

public class FastThreadLocalDemo {
    private static final FastThreadLocal<byte[]> fastThreadLocal = new FastThreadLocal<>();
    
    public void process() {
        try {
            fastThreadLocal.set(new byte[1024]);
            // 使用数据...
        } finally {
            // FastThreadLocal的remove()更高效
            fastThreadLocal.remove();
        }
    }
}

3. 检测与调试内存泄漏

(1)使用分析工具
# 生成堆转储文件
jmap -dump:live,format=b,file=heapdump.hprof <pid>

# 使用jhat分析堆转储
jhat heapdump.hprof
(2)代码检测模式
// 在开发阶段添加检测
public class DebugThreadLocal<T> extends ThreadLocal<T> {
    private final String name;
    private final Set<Thread> threads = Collections.synchronizedSet(new HashSet<>());
    
    public DebugThreadLocal(String name) {
        this.name = name;
    }
    
    @Override
    public void set(T value) {
        threads.add(Thread.currentThread());
        super.set(value);
    }
    
    @Override
    public void remove() {
        threads.remove(Thread.currentThread());
        super.remove();
    }
    
    // 检查是否有未清理的ThreadLocal
    public void checkLeak() {
        if (!threads.isEmpty()) {
            System.err.println("ThreadLocal '" + name + "' 可能泄漏,涉及线程: " + threads);
        }
    }
}

(四)现代替代方案

1. 协程中的局部变量

// Kotlin协程的协程局部变量
val userScope = CoroutineScope(Dispatchers.Default)
val threadLocalElement = ThreadLocal<String>().apply { set("初始值") }

// 将ThreadLocal转换为协程上下文元素
fun testCoroutineContext() {
    userScope.launch(threadLocalElement.asContextElement()) {
        // 在协程中可以访问ThreadLocal值
        println("协程中: ${threadLocalElement.get()}")
        
        // 修改值只影响当前协程
        threadLocalElement.set("修改后的值")
        
        // 启动子协程会继承上下文
        launch {
            println("子协程: ${threadLocalElement.get()}") // 输出"修改后的值"
        }
    }
}

2. Scoped Values(Java 20+预览特性)

// Java 20引入的Scoped Values(预览特性)
public class ScopedValueDemo {
    // 定义Scoped Value
    private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
    
    public void processRequest() {
        // 在作用域内绑定值
        ScopedValue.where(USER_ID, "user123")
                   .run(() -> {
                       // 在这个作用域内可以获取USER_ID
                       String userId = USER_ID.get();
                       System.out.println("处理用户: " + userId);
                       
                       // 调用其他方法,值自动传递
                       processOrder();
                   });
        // 作用域外无法访问USER_ID
    }
    
    private void processOrder() {
        // 不需要参数传递,可以直接获取
        String userId = USER_ID.get();
        System.out.println("为用户" + userId + "处理订单");
    }
}

3. 使用依赖注入框架管理上下文

// 使用Spring的RequestScope
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
    private String requestId = UUID.randomUUID().toString();
    
    public String getRequestId() {
        return requestId;
    }
}

// 在Controller中使用
@RestController
public class MyController {
    @Autowired
    private RequestScopedBean requestScopedBean;
    
    @GetMapping("/test")
    public String test() {
        // 每个请求有独立的实例
        return "Request ID: " + requestScopedBean.getRequestId();
    }
}

(五)性能优化与注意事项

1. ThreadLocal性能特点

// ThreadLocal的哈希冲突解决:线性探测法
private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    
    // 线性探测查找槽位
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        if (k == key) {
            e.value = value;
            return;
        }
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
    
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

性能特点

  • 读取快:直接访问当前线程的ThreadLocalMap
  • 写入中等:需要解决哈希冲突
  • 内存占用:每个线程额外维护一个ThreadLocalMap

2. 使用建议

// 1. 尽量复用ThreadLocal实例
private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

// 2. 避免创建大量ThreadLocal实例
public class ThreadLocalManager {
    // 集中管理所有ThreadLocal实例
    private static final ThreadLocal<Map<String, Object>> CONTEXT = 
        ThreadLocal.withInitial(HashMap::new);
    
    public static void put(String key, Object value) {
        CONTEXT.get().put(key, value);
    }
    
    public static Object get(String key) {
        return CONTEXT.get().get(key);
    }
    
    public static void clear() {
        CONTEXT.remove();
    }
}

// 3. 考虑使用缓存替代频繁创建的ThreadLocal
public class CachedThreadLocal<T> extends ThreadLocal<T> {
    private final Supplier<T> supplier;
    private final Map<Thread, T> cache = new ConcurrentHashMap<>();
    
    public CachedThreadLocal(Supplier<T> supplier) {
        this.supplier = supplier;
    }
    
    @Override
    public T get() {
        return cache.computeIfAbsent(Thread.currentThread(), t -> supplier.get());
    }
    
    @Override
    public void remove() {
        cache.remove(Thread.currentThread());
    }
}

(六)面试回答要点总结

1. 核心原理

  • 数据结构:每个Thread内部维护ThreadLocalMap,以ThreadLocal为key存储线程私有数据
  • 存储机制:Entry使用弱引用指向ThreadLocal,强引用指向value
  • 线程隔离:每个线程访问自己的副本,实现线程安全

2. 主要使用场景

  • 线程安全工具类:封装SimpleDateFormat、Random等非线程安全类
  • 上下文传递:用户信息、追踪ID、语言环境等
  • 连接管理:数据库连接、事务管理等资源隔离
  • 框架内部:Looper、Spring等框架内部使用

3. 内存泄漏问题

  • 根本原因:Entry的value是强引用,ThreadLocal被回收后value无法自动释放
  • 主要场景:线程池中线程复用,ThreadLocal未及时清理
  • 解决方案:始终在finally块中调用remove()方法

4. 现代替代方案

  • 协程局部变量:Kotlin协程中的CoroutineContext
  • Scoped Values:Java 20+的新特性
  • 依赖注入:Spring等框架的作用域Bean

5. 最佳实践

  • 声明为static final,避免重复创建
  • 使用try-finally确保remove()被调用
  • 避免在InheritableThreadLocal中存储大对象
  • 定期检查内存泄漏,使用分析工具监控

一句话总结:ThreadLocal通过为每个线程提供独立的变量副本,实现线程安全的数据隔离,适用于上下文传递和线程安全封装等场景,但需警惕线程池中的内存泄漏问题,务必在使用后及时清理。

六十二、如何计算View的嵌套层级?

(一)为什么需要计算View嵌套层级?

1. 性能影响分析

  • 布局测量时间:每增加一层嵌套,布局测量时间增加约1-3ms
  • 内存占用:每个View占用1-2KB内存,复杂布局可能导致内存浪费
  • 过度绘制风险:嵌套层级越深,过度绘制问题越严重
  • 滑动性能:列表、滚动视图中嵌套过深会导致卡顿

2. Android性能标准

// 性能基准参考值
object LayoutPerformance {
    // 理想嵌套层级
    const val OPTIMAL_DEPTH = 5  // 一般建议不超过5层
    
    // 不同层级的性能影响
    val performanceImpact = mapOf(
        1..3 to "优秀",      // 1-3层:性能优秀
        4..6 to "良好",      // 4-6层:性能良好
        7..10 to "警告",     // 7-10层:需要优化
        11..Int.MAX_VALUE to "严重" // 11层以上:性能问题严重
    )
}

(二)计算方法详解

1. 迭代计算法(推荐)

/**
 * 计算View的嵌套深度(迭代法)
 * 优点:避免递归栈溢出,性能更好
 * @param view 目标View
 * @return 从根ViewGroup到目标View的层级深度
 */
fun calculateViewDepthIterative(view: View): Int {
    var depth = 0
    var currentParent: ViewParent? = view.parent
    
    // 向上遍历直到根布局
    while (currentParent != null && currentParent is View) {
        depth++
        currentParent = currentParent.parent
    }
    
    return depth
}

// 扩展函数版本
fun View.getDepth(): Int {
    var depth = 0
    var parent = this.parent
    while (parent != null && parent is View) {
        depth++
        parent = parent.parent
    }
    return depth
}

2. 递归计算法

/**
 * 计算View的嵌套深度(递归法)
 * 注意:深度过大可能导致栈溢出
 * @param view 目标View
 * @return 嵌套深度
 */
fun calculateViewDepthRecursive(view: View, currentDepth: Int = 0): Int {
    val parent = view.parent
    return if (parent != null && parent is View) {
        calculateViewDepthRecursive(parent as View, currentDepth + 1)
    } else {
        currentDepth
    }
}

// 从根向下计算的递归版本
fun calculateDepthFromRoot(root: ViewGroup, target: View): Int {
    return calculateDepthInternal(root, target, 0)
}

private fun calculateDepthInternal(current: View, target: View, currentDepth: Int): Int {
    if (current == target) return currentDepth
    
    if (current is ViewGroup) {
        for (i in 0 until current.childCount) {
            val child = current.getChildAt(i)
            val foundDepth = calculateDepthInternal(child, target, currentDepth + 1)
            if (foundDepth != -1) return foundDepth
        }
    }
    
    return -1 // 未找到
}

3. 批量计算与统计

/**
 * 统计布局中所有View的深度信息
 */
class LayoutDepthAnalyzer {
    
    data class DepthInfo(
        val view: View,
        val depth: Int,
        val className: String,
        val viewId: String?
    )
    
    fun analyzeLayout(root: View): List<DepthInfo> {
        val result = mutableListOf<DepthInfo>()
        traverseViews(root, 0, result)
        return result
    }
    
    private fun traverseViews(view: View, currentDepth: Int, result: MutableList<DepthInfo>) {
        // 记录当前View信息
        result.add(DepthInfo(
            view = view,
            depth = currentDepth,
            className = view::class.java.simpleName,
            viewId = view.resources?.getResourceEntryName(view.id)
        ))
        
        // 递归遍历子View
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                traverseViews(view.getChildAt(i), currentDepth + 1, result)
            }
        }
    }
    
    fun printDepthReport(root: View) {
        val depthInfoList = analyzeLayout(root)
        
        // 按深度分组
        val depthGroups = depthInfoList.groupBy { it.depth }
        
        println("===== 布局深度分析报告 =====")
        println("总View数量: ${depthInfoList.size}")
        println("最大深度: ${depthGroups.keys.maxOrNull() ?: 0}")
        
        depthGroups.entries.sortedBy { it.key }.forEach { (depth, views) ->
            println("深度 $depth: ${views.size} 个View")
            
            // 显示最深的前5个View
            if (depth >= 7) {
                views.take(5).forEach { info ->
                    val idStr = info.viewId ?: "无ID"
                    println("  - ${info.className} (id: $idStr)")
                }
            }
        }
    }
}

// 使用示例
fun analyzeCurrentLayout(activity: Activity) {
    val rootView = activity.window.decorView.findViewById<ViewGroup>(android.R.id.content)
    val analyzer = LayoutDepthAnalyzer()
    analyzer.printDepthReport(rootView.getChildAt(0))
}

(三)使用工具检测嵌套层级

1. Android Studio Layout Inspector

<!-- 在布局中添加调试标记 -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/root"
    tools:background="@color/debug_red"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <!-- 子View也可以添加调试背景 -->
    <LinearLayout
        android:id="@+id/level1"
        tools:background="@color/debug_green"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <!-- 更多嵌套 -->
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

使用步骤

  1. 连接设备或启动模拟器
  2. 选择菜单:View → Tool Windows → Layout Inspector
  3. 选择要检查的应用进程
  4. 在3D视图或层次结构面板中查看嵌套关系

2. 命令行工具检测

# 使用adb命令获取当前Activity的布局层次
adb shell uiautomator dump /sdcard/window_dump.xml
adb pull /sdcard/window_dump.xml .

# 分析布局文件中的嵌套
grep -o "<node" window_dump.xml | wc -l  # 统计节点数

3. 自定义性能监控工具

// 实时监控布局性能
class LayoutPerformanceMonitor : Application.ActivityLifecycleCallbacks {
    
    private val thresholdDepth = 8 // 警告阈值
    
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        activity.window.decorView.viewTreeObserver.addOnGlobalLayoutListener(
            object : ViewTreeObserver.OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    val root = activity.findViewById<View>(android.R.id.content)
                    val maxDepth = calculateMaxDepth(root)
                    
                    if (maxDepth > thresholdDepth) {
                        Log.w("LayoutPerformance", 
                            "${activity::class.simpleName}: 布局嵌套过深($maxDepth层)")
                        
                        // 生产环境可上报到监控平台
                        reportToAnalytics(activity, maxDepth)
                    }
                    
                    // 移除监听避免重复调用
                    root.viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
            }
        )
    }
    
    private fun calculateMaxDepth(view: View): Int {
        var maxDepth = 0
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                val childDepth = calculateMaxDepth(view.getChildAt(i))
                maxDepth = maxOf(maxDepth, childDepth)
            }
            maxDepth++ // 当前层
        }
        return maxDepth
    }
    
    private fun reportToAnalytics(activity: Activity, depth: Int) {
        // 上报到Firebase Analytics等
        val bundle = Bundle().apply {
            putString("activity", activity::class.java.simpleName)
            putInt("layout_depth", depth)
            putString("device_model", Build.MODEL)
        }
        // FirebaseAnalytics.getInstance(activity).logEvent("layout_depth_warning", bundle)
    }
    
    // 其他生命周期方法省略...
}

// 在Application中注册
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            registerActivityLifecycleCallbacks(LayoutPerformanceMonitor())
        }
    }
}

(四)现代Android开发中的优化方案

1. 使用ConstraintLayout减少嵌套

<!-- 传统嵌套方式:需要4层 -->
<LinearLayout>
    <LinearLayout>
        <RelativeLayout>
            <LinearLayout>
                <!-- 内容 -->
            </LinearLayout>
        </RelativeLayout>
    </LinearLayout>
</LinearLayout>

<!-- 使用ConstraintLayout:只需1层 -->
<androidx.constraintlayout.widget.ConstraintLayout>
    
    <TextView
        android:id="@+id/title"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
    
    <ImageView
        android:id="@+id/icon"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" />
    
    <Button
        android:id="@+id/action"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

2. 使用Merge标签优化include

<!-- parent_layout.xml -->
<LinearLayout>
    <include layout="@layout/child_layout" />
</LinearLayout>

<!-- ❌ 错误:child_layout.xml -->
<LinearLayout> <!-- 多余的嵌套 -->
    <TextView />
</LinearLayout>

<!-- ✅ 正确:child_layout.xml -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView />
</merge>

3. 使用ViewStub延迟加载

<!-- 复杂但不立即需要的布局使用ViewStub -->
<androidx.constraintlayout.widget.ConstraintLayout>
    
    <!-- 主要内容 -->
    <TextView />
    
    <!-- 延迟加载的部分 -->
    <ViewStub
        android:id="@+id/stub_complex_section"
        android:layout="@layout/complex_section"
        app:layout_constraintTop_toBottomOf="@id/main_content" />
    
</androidx.constraintlayout.widget.ConstraintLayout>
// Kotlin代码中按需加载
val viewStub = findViewById<ViewStub>(R.id.stub_complex_section)
viewStub.setOnInflateListener { stub, inflated ->
    // 布局加载完成后的回调
}
viewStub.inflate() // 需要时再加载

4. 使用Jetpack Compose(现代解决方案)

// Compose中嵌套深度不再是问题,但需要注意重组性能
@Composable
fun MyScreen() {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        // Compose自动优化布局,无需担心传统View的嵌套问题
        Header()
        
        LazyColumn {
            items(100) { index ->
                ItemRow(index)
            }
        }
        
        Footer()
    }
}

@Composable
fun ItemRow(index: Int) {
    // Compose使用单次测量,嵌套不影响性能
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Image(
            painter = rememberAsyncImagePainter(model = "https://example.com/image.jpg"),
            contentDescription = null
        )
        
        Column {
            Text("Item $index", style = MaterialTheme.typography.h6)
            Text("Description", style = MaterialTheme.typography.body2)
        }
    }
}

(五)性能优化实践

1. 布局优化检查清单

object LayoutOptimizationChecklist {
    
    fun checkLayoutPerformance(view: View): OptimizationReport {
        val report = OptimizationReport()
        
        // 检查嵌套深度
        val maxDepth = calculateMaxDepth(view)
        if (maxDepth > 8) {
            report.addIssue("嵌套过深", "当前最大深度: $maxDepth", "使用ConstraintLayout扁平化")
        }
        
        // 检查过度绘制
        if (hasOverdraw(view)) {
            report.addIssue("过度绘制", "可能存在不必要的背景", "移除不需要的背景色")
        }
        
        // 检查无用父容器
        val uselessContainers = findUselessContainers(view)
        if (uselessContainers.isNotEmpty()) {
            report.addIssue("无用容器", "发现${uselessContainers.size}个无用容器", "移除或使用merge标签")
        }
        
        return report
    }
    
    data class OptimizationReport(
        val issues: MutableList<LayoutIssue> = mutableListOf()
    ) {
        fun addIssue(title: String, description: String, suggestion: String) {
            issues.add(LayoutIssue(title, description, suggestion))
        }
    }
    
    data class LayoutIssue(
        val title: String,
        val description: String,
        val suggestion: String
    )
}

2. 使用Lint进行静态检查

<!-- 配置lint检查规则 -->
<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- 检查嵌套深度 -->
    <issue id="TooDeepLayout" severity="warning" />
    
    <!-- 检查无用的父布局 -->
    <issue id="UselessParent" severity="warning" />
    
    <!-- 检查可以合并的布局 -->
    <issue id="MergeRootFrame" severity="warning" />
</lint>

// 在Gradle中启用
android {
    lintOptions {
        warningsAsErrors true
        abortOnError true
    }
}

3. 自动化测试与监控

// UI测试中检查布局性能
@RunWith(AndroidJUnit4::class)
class LayoutPerformanceTest {
    
    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)
    
    @Test
    fun checkLayoutDepth() {
        activityRule.scenario.onActivity { activity ->
            val root = activity.findViewById<View>(android.R.id.content)
            val analyzer = LayoutDepthAnalyzer()
            val report = analyzer.analyzeLayout(root)
            
            val maxDepth = report.maxByOrNull { it.depth }?.depth ?: 0
            
            // 断言嵌套深度不超过阈值
            assertThat(maxDepth).isLessThan(10)
            
            // 记录性能数据
            recordLayoutMetrics(maxDepth, report.size)
        }
    }
    
    private fun recordLayoutMetrics(maxDepth: Int, viewCount: Int) {
        // 上报到测试报告
        println("布局统计: 最大深度=$maxDepth, View总数=$viewCount")
    }
}

(六)面试回答要点总结

1. 核心计算方法

  • 迭代法:通过view.parent向上遍历,直到根节点
  • 递归法:从根节点向下递归查找,注意栈溢出风险
  • 批量分析:遍历整棵树,统计所有View的深度信息

2. 关键性能指标

  • 安全范围:一般建议不超过5-8层嵌套
  • 警告阈值:超过10层需要重点优化
  • 测量时间:每层嵌套增加1-3ms测量时间

3. 检测工具

  • Android Studio Layout Inspector:可视化查看布局层次
  • 命令行工具:通过adb获取布局信息分析
  • 自定义监控:运行时检测并上报深度问题

4. 优化策略

  • 使用ConstraintLayout:替代多层LinearLayout/RelativeLayout
  • 使用Merge标签:消除多余的ViewGroup
  • 使用ViewStub:延迟加载复杂布局
  • 迁移到Compose:从根本上解决嵌套性能问题

5. 现代开发实践

  • Jetpack Compose:声明式UI,自动优化布局性能
  • MotionLayout:复杂动画布局优化
  • Lint静态检查:编码阶段发现嵌套问题

6. 一句话总结

计算View嵌套层级主要通过遍历View树实现,深度优化是Android性能优化的重要环节,现代开发中应优先使用ConstraintLayout、Compose等先进技术从源头减少不必要的嵌套。

六十三、MVC、MVP、MVVM的区别及如何选择?

(一)架构模式演进概述

1. 架构演进时间线

2008-2012     2013-2017     2018-至今
   MVC   →     MVP     →     MVVM + MVI
  (早期)      (过渡期)      (现代Android)

2. 核心目标对比

特性 MVC MVP MVVM
分离程度
测试便利性 困难 容易 容易
代码复杂度 中高
学习曲线 简单 中等 较陡
Google推荐 ❌ 已过时 △ 过渡方案 ✅ 官方推荐

(二)MVC模式详解

1. 传统MVC结构

┌─────────────────────────────────────────┐
│               Android MVC                │
├─────────────────────────────────────────┤
│  ┌─────────┐    ┌──────────┐  ┌──────┐  │
│  │  View   │ ←→ │Controller │←→│Model │  │
│  │ (XML)   │    │(Activity) │  │      │  │
│  └─────────┘    └──────────┘  └──────┘  │
└─────────────────────────────────────────┘
        ↓             ↓              ↓
    布局文件     业务逻辑控制       数据层

2. Android中的MVC实现

// Model层
data class User(val id: Int, val name: String, val email: String)

class UserRepository {
    fun getUser(id: Int): User {
        // 从网络或数据库获取数据
        return User(id, "张三", "[email protected]")
    }
}

// Controller层(Activity/Fragment)
class UserActivity : AppCompatActivity() {
    private val repository = UserRepository()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        
        // 处理用户输入
        findViewById<Button>(R.id.btn_load).setOnClickListener {
            loadUserData()
        }
        
        // 直接更新View
        val user = repository.getUser(1)
        findViewById<TextView>(R.id.tv_name).text = user.name
    }
    
    private fun loadUserData() {
        // 执行网络请求、数据处理等
        // 直接操作View
    }
}

3. MVC的问题与局限

// ❌ Activity/Fragment成为"上帝对象"
class ProblematicActivity : AppCompatActivity() {
    // 包含过多职责:
    // 1. 视图控制
    // 2. 业务逻辑
    // 3. 数据处理
    // 4. 生命周期管理
    // 5. 权限处理
    // 6. 导航控制
    // ... 导致代码臃肿,难以维护
}

(三)MVP模式详解

1. MVP结构设计

┌─────────────────────────────────────────┐
│               Android MVP                │
├─────────────────────────────────────────┤
│  ┌─────────┐    ┌───────────┐  ┌──────┐ │
│  │  View   │ ←→ │ Presenter │←→│Model │ │
│  │(Activity)│    │           │  │      │ │
│  └─────────┘    └───────────┘  └──────┘ │
└─────────────────────────────────────────┘

2. 合约(Contract)模式实现

// 1. 定义合约接口
interface UserContract {
    interface View {
        fun showLoading()
        fun hideLoading()
        fun showUser(user: User)
        fun showError(message: String)
    }
    
    interface Presenter {
        fun attachView(view: View)
        fun detachView()
        fun loadUser(userId: Int)
    }
}

// 2. Presenter实现
class UserPresenter(private val repository: UserRepository) : UserContract.Presenter {
    private var view: UserContract.View? = null
    
    override fun attachView(view: UserContract.View) {
        this.view = view
    }
    
    override fun detachView() {
        view = null
    }
    
    override fun loadUser(userId: Int) {
        view?.showLoading()
        
        // 模拟异步操作
        thread {
            Thread.sleep(1000)
            
            runOnUiThread {
                try {
                    val user = repository.getUser(userId)
                    view?.showUser(user)
                } catch (e: Exception) {
                    view?.showError("加载失败: ${e.message}")
                } finally {
                    view?.hideLoading()
                }
            }
        }
    }
}

// 3. View实现(Activity)
class UserActivity : AppCompatActivity(), UserContract.View {
    private lateinit var presenter: UserPresenter
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        
        val repository = UserRepository()
        presenter = UserPresenter(repository)
        presenter.attachView(this)
        
        findViewById<Button>(R.id.btn_load).setOnClickListener {
            presenter.loadUser(1)
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        presenter.detachView()
    }
    
    // 实现View接口
    override fun showLoading() {
        findViewById<ProgressBar>(R.id.progress_bar).visibility = View.VISIBLE
    }
    
    override fun hideLoading() {
        findViewById<ProgressBar>(R.id.progress_bar).visibility = View.GONE
    }
    
    override fun showUser(user: User) {
        findViewById<TextView>(R.id.tv_name).text = user.name
        findViewById<TextView>(R.id.tv_email).text = user.email
    }
    
    override fun showError(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

3. MVP的优缺点分析

// ✅ 优点
object MVPAdvantages {
    // 1. 关注点分离清晰
    // 2. 易于单元测试(Presenter不依赖Android框架)
    // 3. View变得简单,只负责UI更新
    // 4. 支持多View共享同一个Presenter
}

// ❌ 缺点
object MVPDisadvantages {
    // 1. 接口爆炸问题(每个功能都需要Contract接口)
    // 2. 需要手动管理View生命周期
    // 3. 存在内存泄漏风险(Presenter持有View引用)
    // 4. 样板代码多,开发效率较低
}

(四)MVVM模式详解

1. 现代MVVM结构(Android Jetpack)

┌─────────────────────────────────────────────────────┐
│                Android MVVM (Jetpack)                │
├─────────────────────────────────────────────────────┤
│  ┌──────────┐      ┌──────────────┐      ┌──────┐  │
│  │   View   │ ◄──► │ ViewModel    │ ◄──► │Model │  │
│  │(Activity)│      │              │      │      │  │
│  │ Fragment │      │  LiveData    │      │Repo  │  │
│  │  Compose │      │  StateFlow   │      │      │  │
│  └──────────┘      └──────────────┘      └──────┘  │
└─────────────────────────────────────────────────────┘
        DataBinding/ViewBinding       Room/Retrofit

2. 使用Jetpack组件的MVVM实现

// 1. 数据层 (Model/Repository)
data class User(val id: Int, val name: String, val email: String)

interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): User
}

class UserRepository @Inject constructor(
    private val api: UserApi,
    private val userDao: UserDao
) {
    suspend fun getUser(userId: Int): User {
        // 先检查本地数据库
        val cachedUser = userDao.getUser(userId)
        if (cachedUser != null) {
            return cachedUser
        }
        
        // 从网络获取
        val user = api.getUser(userId)
        
        // 缓存到数据库
        userDao.insertUser(user)
        
        return user
    }
}

// 2. ViewModel层
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {
    
    // UI状态管理
    sealed class UserState {
        object Loading : UserState()
        data class Success(val user: User) : UserState()
        data class Error(val message: String) : UserState()
    }
    
    // 使用StateFlow替代LiveData(推荐)
    private val _userState = MutableStateFlow<UserState>(UserState.Loading)
    val userState: StateFlow<UserState> = _userState.asStateFlow()
    
    // 事件处理
    private val _events = MutableSharedFlow<UserEvent>()
    val events: SharedFlow<UserEvent> = _events.asSharedFlow()
    
    init {
        loadUser(1)
    }
    
    fun loadUser(userId: Int) {
        viewModelScope.launch {
            _userState.value = UserState.Loading
            
            try {
                val user = repository.getUser(userId)
                _userState.value = UserState.Success(user)
            } catch (e: Exception) {
                _userState.value = UserState.Error("加载失败: ${e.message}")
                // 发送事件
                _events.emit(UserEvent.ShowErrorMessage(e.message ?: "未知错误"))
            }
        }
    }
    
    sealed class UserEvent {
        data class ShowErrorMessage(val message: String) : UserEvent()
    }
}

// 3. View层 (Activity/Fragment)
@AndroidEntryPoint
class UserActivity : AppCompatActivity() {
    private lateinit var binding: ActivityUserBinding
    private val viewModel: UserViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // ViewBinding
        binding = ActivityUserBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupObservers()
        setupClickListeners()
    }
    
    private fun setupObservers() {
        // 观察UI状态
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.userState.collect { state ->
                    when (state) {
                        is UserViewModel.UserState.Loading -> {
                            binding.progressBar.visibility = View.VISIBLE
                        }
                        is UserViewModel.UserState.Success -> {
                            binding.progressBar.visibility = View.GONE
                            binding.tvName.text = state.user.name
                            binding.tvEmail.text = state.user.email
                        }
                        is UserViewModel.UserState.Error -> {
                            binding.progressBar.visibility = View.GONE
                            Toast.makeText(this@UserActivity, state.message, Toast.LENGTH_SHORT).show()
                        }
                    }
                }
            }
        }
        
        // 观察事件
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.events.collect { event ->
                    when (event) {
                        is UserViewModel.UserEvent.ShowErrorMessage -> {
                            // 处理错误事件
                            showErrorDialog(event.message)
                        }
                    }
                }
            }
        }
    }
    
    private fun setupClickListeners() {
        binding.btnReload.setOnClickListener {
            viewModel.loadUser(1)
        }
    }
    
    private fun showErrorDialog(message: String) {
        AlertDialog.Builder(this)
            .setTitle("错误")
            .setMessage(message)
            .setPositiveButton("确定", null)
            .show()
    }
}

3. MVVM的变体:MVI(Model-View-Intent)

// MVI架构示例
class SearchViewModel : ViewModel() {
    
    // 状态(不可变)
    data class SearchState(
        val query: String = "",
        val results: List<String> = emptyList(),
        val isLoading: Boolean = false,
        val error: String? = null
    )
    
    // 意图(用户操作)
    sealed class SearchIntent {
        data class UpdateQuery(val query: String) : SearchIntent()
        object Search : SearchIntent()
        object Clear : SearchIntent()
    }
    
    private val _state = MutableStateFlow(SearchState())
    val state: StateFlow<SearchState> = _state.asStateFlow()
    
    fun processIntent(intent: SearchIntent) {
        when (intent) {
            is SearchIntent.UpdateQuery -> {
                _state.update { it.copy(query = intent.query) }
            }
            SearchIntent.Search -> {
                search()
            }
            SearchIntent.Clear -> {
                _state.update { SearchState() }
            }
        }
    }
    
    private fun search() {
        viewModelScope.launch {
            _state.update { it.copy(isLoading = true, error = null) }
            
            try {
                val results = repository.search(_state.value.query)
                _state.update { it.copy(results = results, isLoading = false) }
            } catch (e: Exception) {
                _state.update { it.copy(error = e.message, isLoading = false) }
            }
        }
    }
}

(五)现代Android架构组件

1. 官方推荐架构

┌─────────────────────────────────────────────────────┐
│             现代Android应用架构(官方推荐)               │
├─────────────────────────────────────────────────────┤
│                   UI Layer (View)                    │
│           Activity / Fragment / Compose              │
│                ┌──────────┐                         │
│                │ ViewModel │◄────────────┐          │
│                └──────────┘              │          │
│                      │                   │          │
│                ┌──────────┐       ┌──────────┐     │
│                │   State  │       │   Event  │     │
│                └──────────┘       └──────────┘     │
│                      │                   │          │
├──────────────────────┼───────────────────┼──────────┤
│             Domain Layer (可选)                      │
│                Use Cases / Interactors              │
├──────────────────────┼──────────────────────────────┤
│                   Data Layer                         │
│      Repository ←─────┤   ├─────→ Data Sources      │
│                   Room  │   │  Network               │
│                         │   │                        │
└─────────────────────────┴───┴────────────────────────┘

2. Jetpack架构组件生态

// build.gradle.kts 依赖配置
dependencies {
    // ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
    
    // LiveData / StateFlow
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
    
    // Room (数据库)
    implementation("androidx.room:room-runtime:2.6.0")
    ksp("androidx.room:room-compiler:2.6.0")
    
    // Navigation (导航)
    implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
    implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
    
    // DataStore (替代SharedPreferences)
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    
    // Hilt (依赖注入)
    implementation("com.google.dagger:hilt-android:2.48")
    ksp("com.google.dagger:hilt-compiler:2.48")
    
    // Compose (声明式UI)
    implementation("androidx.compose.ui:ui:1.5.4")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
}

(六)架构选择指南

1. 项目规模与复杂度矩阵

object ArchitectureSelectionGuide {
    
    fun recommendArchitecture(project: ProjectSpecs): Architecture {
        return when {
            // 小型项目
            project.teamSize <= 2 && 
            project.expectedLifespan < 1.year && 
            project.complexity == ProjectComplexity.LOW -> 
                Architecture.MVC
            
            // 中型项目
            project.teamSize in 3..5 && 
            project.expectedLifespan in 1..2.years && 
            project.hasUnitTestRequirement -> 
                Architecture.MVP
            
            // 大型/长期项目
            project.teamSize > 5 && 
            project.expectedLifespan > 2.years && 
            project.needsHighTestCoverage -> 
                Architecture.MVVM
            
            // 现代新项目(无论大小)
            project.isGreenfield && 
            project.targetSdk >= 21 -> 
                Architecture.MVVM_WITH_JETPACK
            
            // 复杂UI交互项目
            project.hasComplexUIState && 
            project.needsPredictableStateManagement -> 
                Architecture.MVI
            
            else -> Architecture.MVVM_WITH_JETPACK
        }
    }
    
    enum class Architecture {
        MVC, MVP, MVVM, MVVM_WITH_JETPACK, MVI, MVI_WITH_COMPOSE
    }
    
    data class ProjectSpecs(
        val teamSize: Int,
        val expectedLifespan: Duration,
        val complexity: ProjectComplexity,
        val hasUnitTestRequirement: Boolean = false,
        val needsHighTestCoverage: Boolean = false,
        val isGreenfield: Boolean = true,
        val targetSdk: Int = 34,
        val hasComplexUIState: Boolean = false,
        val needsPredictableStateManagement: Boolean = false
    )
    
    enum class ProjectComplexity { LOW, MEDIUM, HIGH }
}

2. 具体场景推荐

(1)维护老项目
// 场景:维护Android 4.x时代的应用
object LegacyProjectStrategy {
    
    fun handleLegacyProject(currentArchitecture: String): MigrationPlan {
        return when (currentArchitecture) {
            "MVC" -> MigrationPlan(
                steps = listOf(
                    "1. 引入ViewBinding减少findViewById",
                    "2. 将业务逻辑抽离到Helper类",
                    "3. 逐步引入ViewModel处理简单状态",
                    "4. 最终目标:MVVM轻量级改造"
                ),
                estimatedTime = "3-6个月"
            )
            
            "MVP" -> MigrationPlan(
                steps = listOf(
                    "1. 保留Presenter层,添加ViewModel适配层",
                    "2. 逐步将逻辑迁移到ViewModel",
                    "3. 使用LiveData/StateFlow替代回调",
                    "4. 移除Presenter层,完成MVVM迁移"
                ),
                estimatedTime = "2-4个月"
            )
            
            else -> MigrationPlan(
                steps = listOf("维持现有架构,局部优化"),
                estimatedTime = "持续进行"
            )
        }
    }
}
(2)新项目技术选型
// 2024年新项目推荐技术栈
object ModernTechStack {
    val ARCHITECTURE = "MVVM + MVI混合模式"
    val UI_FRAMEWORK = "Jetpack Compose(优先)或 View系统 + DataBinding"
    val ASYNC = "Kotlin Coroutines + Flow"
    val DI = "Hilt"
    val NETWORK = "Retrofit + Kotlin Serialization"
    val DATABASE = "Room"
    val NAVIGATION = "Compose Navigation 或 Navigation Component"
    val STATE_MANAGEMENT = "ViewModel + StateFlow + SavedStateHandle"
    val TESTING = "JUnit5 + MockK + Turbine + Compose UI Testing"
}
(3)特定需求场景
// 根据需求选择架构
object ArchitectureByRequirement {
    
    fun selectForRequirement(requirement: Requirement): Architecture {
        return when (requirement) {
            Requirement.EASY_TESTING -> Architecture.MVVM
            Requirement.SIMPLE_UI -> Architecture.MVC
            Requirement.COMPLEX_BUSINESS_LOGIC -> Architecture.MVP
            Requirement.REACTIVE_UI -> Architecture.MVVM
            Requirement.STATE_PREDICTABILITY -> Architecture.MVI
            Requirement.RAPID_PROTOTYPING -> {
                if (canUseCompose()) Architecture.MVVM_WITH_COMPOSE
                else Architecture.MVC
            }
            Requirement.TEAM_SCALABILITY -> Architecture.MVVM
            Requirement.LONG_TERM_MAINTENANCE -> Architecture.MVVM_WITH_JETPACK
        }
    }
    
    enum class Requirement {
        EASY_TESTING, SIMPLE_UI, COMPLEX_BUSINESS_LOGIC,
        REACTIVE_UI, STATE_PREDICTABILITY, RAPID_PROTOTYPING,
        TEAM_SCALABILITY, LONG_TERM_MAINTENANCE
    }
}

(七)常见问题与解决方案

1. 架构选择误区

// ❌ 常见错误
object ArchitectureAntiPatterns {
    
    // 1. 过度设计
    fun overEngineeringExample() {
        // 为简单的展示页面引入完整MVI + 领域层 + 完整DI
        // 实际需求:显示静态列表
    }
    
    // 2. 架构不一致
    fun inconsistentArchitecture() {
        // 项目中混合使用MVC、MVP、MVVM,没有统一规范
    }
    
    // 3. 盲目追随潮流
    fun blindFollowing() {
        // 不考虑团队技能和项目需求,强制使用最新架构
    }
}

// ✅ 解决方案
object ArchitectureBestPractices {
    
    // 1. 渐进式演进
    const val PRINCIPLE_1 = "从简单开始,按需演进"
    
    // 2. 一致性优先
    const val PRINCIPLE_2 = "团队统一比架构先进更重要"
    
    // 3. 务实选择
    const val PRINCIPLE_3 = "选择适合团队和项目的,而非最潮的"
}

2. 性能注意事项

// MVVM DataBinding性能优化
object DataBindingPerformance {
    
    // 避免在xml中执行复杂逻辑
    // ❌ 错误示例
    const val BAD_BINDING = "@{viewModel.calculateComplexValue(user)}"
    
    // ✅ 正确做法
    const val GOOD_PRACTICE = """
        1. 在ViewModel中预计算数据
        2. 使用单向绑定避免不必要的更新
        3. 对于列表使用DiffUtil
        4. 避免在绑定表达式中创建新对象
    """
}

// ViewModel生命周期管理
class OptimizedViewModel : ViewModel() {
    
    // 使用协程的正确方式
    private val jobMap = mutableMapOf<Int, Job>()
    
    fun loadData(id: Int) {
        // 取消之前的相同请求
        jobMap[id]?.cancel()
        
        val newJob = viewModelScope.launch {
            // 执行请求
            val data = repository.getData(id)
            _data.value = data
        }
        
        jobMap[id] = newJob
        
        // 清理完成的job
        newJob.invokeOnCompletion {
            jobMap.remove(id)
        }
    }
}

(八)面试回答要点总结

1. 核心区别对比表

方面 MVC MVP MVVM
核心思想 分离显示、控制、数据 View-Presenter完全解耦 数据驱动,双向绑定
通信方式 View↔Controller↔Model View↔Presenter↔Model View↔ViewModel↔Model
测试重点 难以测试 Presenter易测试 ViewModel易测试
代码量 中(接口多) 中(绑定配置)
适用场景 简单应用、原型 中型应用、需要高测试覆盖率 大型应用、复杂UI交互

2. 现代Android开发推荐

  • 新项目首选:MVVM + Jetpack组件(ViewModel + LiveData/StateFlow)
  • UI框架:优先使用Jetpack Compose,其次View系统 + ViewBinding
  • 状态管理:单向数据流(StateFlow + 密封类)
  • 异步处理:Kotlin协程 + Flow
  • 架构演进:考虑MVI模式处理复杂UI状态

3. 选择考量因素

  • 团队规模与技能:小团队/新手团队可从MVC/MVP开始
  • 项目复杂度:简单功能用MVC,复杂业务用MVVM/MVI
  • 测试要求:高测试覆盖率项目适合MVP/MVVM
  • 维护周期:长期维护项目应选择MVVM + 官方组件
  • 性能要求:注意DataBinding的性能影响,合理使用

4. 迁移与演进建议

  • 老项目改造:渐进式迁移,先抽取ViewModel,再逐步重构
  • 技术债务:定期评估架构健康度,小步快跑式改进
  • 团队共识:建立编码规范,确保架构一致性

5. 一句话总结

在Android开发中,架构演进从MVC到MVP再到MVVM,现代开发应优先采用MVVM配合Jetpack组件,根据项目实际需求灵活选择,重在团队协作效率和代码可维护性,而非盲目追求最新架构。

六十四、SharedPreferences的apply()和commit()区别?

(一)核心区别对比

1. 基础特性对比

特性 commit() apply()
执行方式 同步阻塞 异步非阻塞
返回值 boolean(成功/失败) void
线程行为 阻塞调用线程直到写入完成 立即返回,后台写入
性能影响 可能引起ANR(主线程调用时) 性能更优,不会阻塞UI
数据一致性 强一致性,立即持久化 最终一致性,延迟持久化
错误处理 可捕获IOException 无法直接捕获写入错误

2. 使用代码示例对比

// commit() 使用示例
fun saveWithCommit(prefs: SharedPreferences, key: String, value: String): Boolean {
    return try {
        val editor = prefs.edit()
        editor.putString(key, value)
        val success = editor.commit() // 同步阻塞,返回结果
        if (!success) {
            Log.e("Prefs", "commit() 写入失败")
        }
        success
    } catch (e: Exception) {
        Log.e("Prefs", "commit() 异常: ${e.message}")
        false
    }
}

// apply() 使用示例
fun saveWithApply(prefs: SharedPreferences, key: String, value: String) {
    val editor = prefs.edit()
    editor.putString(key, value)
    editor.apply() // 异步非阻塞,无返回值
    
    // 注意:apply()立即返回,但写入可能尚未完成
    // 不能立即依赖写入结果进行后续操作
}

(二)底层实现原理

1. commit() 实现机制

// Android Framework 源码简化
public boolean commit() {
    // 1. 将修改提交到内存 Map
    MemoryCommitResult mcr = commitToMemory();
    
    // 2. 同步写入磁盘
    SharedPreferencesImpl.this.enqueueDiskWrite(
        mcr, null /* sync write on this thread */
    );
    
    try {
        // 3. 等待写入完成
        mcr.writtenToDiskLatch.await();
    } catch (InterruptedException e) {
        return false;
    }
    
    // 4. 通知监听器(在主线程)
    notifyListeners(mcr);
    
    // 5. 返回写入结果
    return mcr.writeToDiskResult;
}

2. apply() 实现机制

// Android Framework 源码简化
public void apply() {
    // 1. 将修改提交到内存 Map
    final MemoryCommitResult mcr = commitToMemory();
    
    // 2. 在主线程注册等待写入完成的Runnable
    final Runnable awaitCommit = new Runnable() {
        public void run() {
            try {
                mcr.writtenToDiskLatch.await();
            } catch (InterruptedException ignored) {}
        }
    };
    
    // 3. 将awaitCommit加入队列(QueuedWork)
    QueuedWork.addFinisher(awaitCommit);
    
    // 4. 异步写入磁盘(使用单线程Executor)
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
    
    // 5. 立即返回,不等待写入完成
}

3. 内存到磁盘的写入流程

commit()/apply() 写入流程:
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   内存修改       │ →  │  写入队列        │ →  │  磁盘持久化     │
│  (立即生效)      │    │  (MemoryCommit) │    │  (异步/同步)    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
        │                      │                      │
        │ commit(): 同步等待    │ apply(): 立即返回    │ commit(): 阻塞直到完成
        │ apply(): 不等待       │                     │ apply(): 后台执行

(三)性能影响与ANR风险

1. commit() 的ANR风险分析

// ❌ 危险:在主线程频繁调用commit()
class DangerousPrefsUsage {
    fun saveUserData(user: User) {
        val prefs = getSharedPreferences("user", Context.MODE_PRIVATE)
        
        // 每个commit()都会阻塞主线程
        prefs.edit().putString("name", user.name).commit()     // 阻塞1
        prefs.edit().putInt("age", user.age).commit()         // 阻塞2
        prefs.edit().putString("email", user.email).commit()  // 阻塞3
        // 如果磁盘I/O慢,这里可能触发ANR
    }
}

// ✅ 改进:批量操作,使用apply()
fun saveUserDataSafely(user: User) {
    val editor = getSharedPreferences("user", Context.MODE_PRIVATE).edit()
    
    // 批量设置,一次apply()
    editor.putString("name", user.name)
        .putInt("age", user.age)
        .putString("email", user.email)
        .apply() // 异步写入,无ANR风险
}

2. apply() 的性能优化机制

// apply()的优化:批量合并写入
class BatchWriteExample {
    fun testApplyOptimization() {
        val prefs = getSharedPreferences("test", Context.MODE_PRIVATE)
        
        // 连续多次apply()会被合并
        prefs.edit().putInt("count", 1).apply()
        prefs.edit().putInt("count", 2).apply()
        prefs.edit().putInt("count", 3).apply()
        
        // 实际只会写入一次:count = 3
        // 因为apply()使用单线程队列,后一个apply()会覆盖前一个
    }
}

// 注意:apply()不能保证写入顺序
fun testUnreliableOrder() {
    val prefs = getSharedPreferences("order", Context.MODE_PRIVATE)
    
    thread {
        prefs.edit().putString("from", "thread").apply()
    }
    
    // 主线程同时写入
    prefs.edit().putString("from", "main").apply()
    
    // 最终结果不确定,可能是"thread"或"main"
    // 因为两个apply()在不同线程,写入顺序不确定
}

(四)实际应用场景分析

1. 适合使用 commit() 的场景

// 场景1:需要确保写入成功的配置保存
object CriticalConfig {
    fun saveApiKey(apiKey: String): Boolean {
        return try {
            val prefs = App.context.getSharedPreferences("config", Context.MODE_PRIVATE)
            val success = prefs.edit().putString("api_key", apiKey).commit()
            
            if (!success) {
                // 写入失败,采取降级策略
                fallbackToSecureStorage(apiKey)
            }
            
            success
        } catch (e: Exception) {
            Log.e("Config", "保存API Key失败", e)
            false
        }
    }
    
    private fun fallbackToSecureStorage(apiKey: String) {
        // 使用Android KeyStore或其他安全存储
    }
}

// 场景2:批量事务性操作
fun saveTransactionData(data: Map<String, Any>): Boolean {
    val editor = getSharedPreferences("transactions", Context.MODE_PRIVATE).edit()
    
    data.forEach { (key, value) ->
        when (value) {
            is String -> editor.putString(key, value)
            is Int -> editor.putInt(key, value)
            is Boolean -> editor.putBoolean(key, value)
            // ... 其他类型
        }
    }
    
    // 事务性保存,需要知道是否成功
    return editor.commit()
}

2. 适合使用 apply() 的场景

// 场景1:UI相关设置(主题、语言等)
object UIPreferences {
    fun saveTheme(theme: String) {
        getSharedPreferences("ui", Context.MODE_PRIVATE).edit()
            .putString("theme", theme)
            .apply() // 异步保存,不影响用户体验
    }
    
    fun saveLanguage(locale: String) {
        getSharedPreferences("ui", Context.MODE_PRIVATE).edit()
            .putString("language", locale)
            .apply()
    }
}

// 场景2:用户行为统计
object AnalyticsTracker {
    private val prefs = getSharedPreferences("analytics", Context.MODE_PRIVATE)
    
    fun trackEvent(event: String) {
        val count = prefs.getInt(event, 0) + 1
        prefs.edit().putInt(event, count).apply() // 异步,不影响主线程
        
        // 定期批量上报
        if (count % 10 == 0) {
            uploadAnalytics()
        }
    }
}

// 场景3:缓存数据
object CacheManager {
    fun cacheData(key: String, data: String) {
        val editor = getSharedPreferences("cache", Context.MODE_PRIVATE).edit()
        
        // 设置过期时间
        val cacheData = mapOf(
            "data" to data,
            "timestamp" to System.currentTimeMillis()
        )
        
        editor.putString(key, Json.encodeToString(cacheData))
            .apply() // 缓存可以异步写入
    }
}

(五)现代Android开发的演进

1. SharedPreferences的问题

// SharedPreferences的局限性
object SharedPrefsLimitations {
    // 1. 没有类型安全
    fun getUnsafeValue(): String? {
        val prefs = getSharedPreferences("test", Context.MODE_PRIVATE)
        return prefs.getString("key", null) // 运行时类型检查
    }
    
    // 2. 没有错误处理机制(apply())
    fun saveWithoutErrorHandling() {
        prefs.edit().putString("key", "value").apply()
        // 如果写入失败,我们不知道
    }
    
    // 3. 主线程ANR风险(commit())
    fun riskyCommitOnMainThread() {
        prefs.edit().putString("key", "value").commit() // 可能ANR
    }
    
    // 4. 不支持Flow/RxJava
    // 无法监听变化(除了简单的OnSharedPreferenceChangeListener)
}

2. DataStore替代方案

// Preferences DataStore(类型安全、异步)
object DataStoreExample {
    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
        name = "settings"
    )
    
    // 定义Key(类型安全)
    object PreferencesKeys {
        val USER_NAME = stringPreferencesKey("user_name")
        val NOTIFICATIONS_ENABLED = booleanPreferencesKey("notifications_enabled")
        val THEME_COLOR = intPreferencesKey("theme_color")
    }
    
    // 写入数据(异步,返回Flow)
    suspend fun saveUserName(name: String) {
        context.dataStore.edit { preferences ->
            preferences[PreferencesKeys.USER_NAME] = name
        }
        // 自动应用,无需commit()/apply()
    }
    
    // 读取数据(响应式)
    val userNameFlow: Flow<String> = context.dataStore.data
        .map { preferences ->
            preferences[PreferencesKeys.USER_NAME] ?: ""
        }
        .catch { exception ->
            // 错误处理
            if (exception is IOException) {
                emit("")
            } else {
                throw exception
            }
        }
    
    // 事务性操作
    suspend fun updateUserProfile(name: String, notifications: Boolean) {
        context.dataStore.edit { preferences ->
            preferences[PreferencesKeys.USER_NAME] = name
            preferences[PreferencesKeys.NOTIFICATIONS_ENABLED] = notifications
        }
    }
}

3. Proto DataStore(复杂数据结构)

// settings.proto
syntax = "proto3";

option java_package = "com.example.app.datastore";
option java_multiple_files = true;

message UserSettings {
  string user_name = 1;
  int32 theme_color = 2;
  bool notifications_enabled = 3;
  int64 last_login = 4;
}
// Proto DataStore使用
object ProtoDataStoreExample {
    private val Context.userSettingsStore: DataStore<UserSettings> by
        dataStore(
            fileName = "user_settings.pb",
            serializer = UserSettingsSerializer
        )
    
    suspend fun saveComplexSettings(settings: UserSettings) {
        context.userSettingsStore.updateData { currentSettings ->
            currentSettings.toBuilder()
                .setUserName(settings.userName)
                .setThemeColor(settings.themeColor)
                .build()
        }
    }
    
    val settingsFlow: Flow<UserSettings> = context.userSettingsStore.data
        .catch { exception ->
            // 数据损坏时的处理
            if (exception is InvalidProtocolBufferException) {
                emit(UserSettings.getDefaultInstance())
            } else {
                throw exception
            }
        }
}

(六)最佳实践与迁移策略

1. 使用建议

// SharedPreferences使用规范
object SharedPrefsBestPractices {
    
    // 1. 创建单例,避免多次创建
    private lateinit var prefs: SharedPreferences
    
    fun init(context: Context) {
        prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
    }
    
    // 2. 批量操作,减少I/O次数
    fun saveUserProfile(user: User) {
        prefs.edit().apply {
            putString("name", user.name)
            putInt("age", user.age)
            putString("email", user.email)
            putBoolean("verified", user.verified)
            // 一次apply()提交所有修改
        }.apply()
    }
    
    // 3. 关键数据使用commit(),非关键数据使用apply()
    fun saveCriticalData(key: String, value: String): Boolean {
        return try {
            prefs.edit().putString(key, value).commit()
        } catch (e: Exception) {
            Log.e("Prefs", "关键数据保存失败", e)
            false
        }
    }
    
    // 4. 封装读取操作,提供默认值
    fun getStringSafe(key: String, defaultValue: String = ""): String {
        return prefs.getString(key, defaultValue) ?: defaultValue
    }
    
    // 5. 进程间共享使用MODE_MULTI_PROCESS(已废弃,推荐ContentProvider)
    @Deprecated("使用ContentProvider或DataStore替代")
    fun getMultiProcessPrefs(context: Context): SharedPreferences {
        return context.getSharedPreferences(
            "multi_process",
            Context.MODE_MULTI_PROCESS // Android 7.0+已废弃
        )
    }
}

2. SharedPreferences → DataStore迁移

// 渐进式迁移策略
object MigrationStrategy {
    
    // 步骤1:封装SharedPreferences,准备迁移
    class LegacyPreferences(context: Context) {
        private val prefs = context.getSharedPreferences("legacy", MODE_PRIVATE)
        
        fun getLegacyValue(): String {
            return prefs.getString("old_key", "") ?: ""
        }
        
        fun setLegacyValue(value: String) {
            prefs.edit().putString("old_key", value).apply()
        }
    }
    
    // 步骤2:创建DataStore,支持SharedPreferences迁移
    private val Context.migratingDataStore: DataStore<Preferences> by preferencesDataStore(
        name = "migrated_settings",
        migrations = listOf(
            SharedPreferencesMigration(
                context,
                "legacy" // 要迁移的SharedPreferences名称
            ) { sharedPrefs: SharedPreferencesView, currentData: Preferences ->
                // 迁移逻辑
                val migratedData = currentData.toMutablePreferences()
                
                sharedPrefs.getAll().forEach { (key, value) ->
                    when (value) {
                        is String -> migratedData[stringPreferencesKey(key)] = value
                        is Int -> migratedData[intPreferencesKey(key)] = value
                        is Boolean -> migratedData[booleanPreferencesKey(key)] = value
                        is Float -> migratedData[floatPreferencesKey(key)] = value
                        is Long -> migratedData[longPreferencesKey(key)] = value
                    }
                }
                
                migratedData
            }
        )
    )
    
    // 步骤3:双写策略(过渡期)
    class TransitionalStorage(context: Context) {
        private val legacy = LegacyPreferences(context)
        private val dataStore = context.migratingDataStore
        
        suspend fun saveValue(key: String, value: String) {
            // 新数据写入DataStore
            dataStore.edit { prefs ->
                prefs[stringPreferencesKey(key)] = value
            }
            
            // 旧数据也更新(保持兼容)
            legacy.setLegacyValue(value)
        }
        
        suspend fun readValue(key: String): String {
            // 优先从DataStore读取
            return dataStore.data
                .map { it[stringPreferencesKey(key)] }
                .first() ?: legacy.getLegacyValue()
        }
    }
}

(七)常见问题与解决方案

1. apply() 不生效的问题

// 问题:apply()后立即读取可能读到旧值
object ApplyReadProblem {
    fun testApplyTiming() {
        val prefs = getSharedPreferences("test", MODE_PRIVATE)
        
        // 写入新值
        prefs.edit().putString("key", "new_value").apply()
        
        // 立即读取(可能读到旧值,因为apply()是异步的)
        val value = prefs.getString("key", "default")
        // value可能是"new_value",也可能是旧值
        
        // 解决方案1:使用commit()确保写入完成
        prefs.edit().putString("key", "new_value").commit()
        val reliableValue = prefs.getString("key", "default") // 一定是新值
        
        // 解决方案2:如果使用apply(),不要立即依赖新值
        // 使用回调或LiveData/Flow监听变化
    }
}

2. 多进程问题

// SharedPreferences多进程不可靠
object MultiProcessIssue {
    @Deprecated("MODE_MULTI_PROCESS在Android 7.0+已废弃")
    fun getUnreliableMultiProcessPrefs(): SharedPreferences {
        return getSharedPreferences("multi", Context.MODE_MULTI_PROCESS)
    }
    
    // 现代解决方案:使用ContentProvider或直接进程间通信
    class SecureMultiProcessStorage(context: Context) {
        private val contentResolver = context.contentResolver
        
        fun saveForMultiProcess(key: String, value: String) {
            val values = ContentValues().apply {
                put("key", key)
                put("value", value)
            }
            
            contentResolver.insert(
                Uri.parse("content://${AppAuthority.PROVIDER}/prefs"),
                values
            )
        }
    }
}

3. 性能监控

// 监控SharedPreferences性能
object PrefsPerformanceMonitor {
    
    fun monitorCommitTime() {
        val prefs = getSharedPreferences("monitored", MODE_PRIVATE)
        
        val startTime = System.currentTimeMillis()
        val success = prefs.edit().putString("test", "value").commit()
        val endTime = System.currentTimeMillis()
        
        val duration = endTime - startTime
        
        if (duration > 16) { // 超过一帧的时间(60fps)
            Log.w("Prefs", "commit()耗时${duration}ms,可能影响性能")
            
            // 上报到监控平台
            reportPerformanceIssue("commit_slow", duration)
        }
    }
    
    fun checkApplyQueue() {
        // 使用反射检查QueuedWork队列(仅调试用)
        if (BuildConfig.DEBUG) {
            try {
                val queuedWorkClass = Class.forName("android.app.QueuedWork")
                val method = queuedWorkClass.getDeclaredMethod("hasPendingWork")
                method.isAccessible = true
                val hasPendingWork = method.invoke(null) as Boolean
                
                if (hasPendingWork) {
                    Log.d("Prefs", "有未完成的apply()写入任务")
                }
            } catch (e: Exception) {
                // 忽略反射异常
            }
        }
    }
}

(八)面试回答要点总结

1. 核心区别

  • 同步vs异步:commit()同步阻塞,apply()异步非阻塞
  • 返回值:commit()返回boolean表示成功与否,apply()无返回值
  • 线程安全:两者都是线程安全的,但commit()可能阻塞UI线程
  • 数据一致性:commit()强一致性,apply()最终一致性

2. 使用选择原则

  • 使用commit()的场景
    • 需要立即知道写入结果
    • 关键配置保存,必须确保写入成功
    • 批量事务性操作需要原子性保证
  • 使用apply()的场景
    • UI相关设置(主题、语言等)
    • 用户行为统计、日志记录
    • 缓存数据等非关键数据
    • 大多数情况下优先使用apply()

3. 性能与ANR

  • ANR风险:在主线程频繁调用commit()可能触发ANR
  • 性能优化:apply()会将多次写入合并,减少I/O操作
  • 内存影响:两者都会先修改内存中的Map,区别在于磁盘写入时机

4. 现代替代方案

  • DataStore:官方推荐替代方案,支持协程、Flow、类型安全
  • 迁移策略:渐进式迁移,支持从SharedPreferences平滑过渡
  • 多进程存储:使用ContentProvider替代MODE_MULTI_PROCESS

5. 最佳实践

  • 批量操作,减少I/O次数
  • 关键数据用commit(),非关键数据用apply()
  • 封装SharedPreferences操作,便于迁移和维护
  • 监控性能,避免主线程阻塞

一句话总结:commit()保证数据立即持久化但可能阻塞线程,apply()提供更好的性能但只保证最终一致性,现代开发推荐逐步迁移到DataStore以获得更好的类型安全和异步支持。

六十五、Base64和MD5是加密算法吗?

(一)核心概念澄清

1. 安全算法分类体系

┌─────────────────────────────────────────────┐
│             安全算法三大类别                 │
├─────────────────────────────────────────────┤
│                                              │
│  1. 编码算法 (Encoding)                      │
│     ├─ Base64, Base32, URL Encoding          │
│     └─ 目的:数据转换,无安全性                │
│                                              │
│  2. 哈希算法 (Hashing)                       │
│     ├─ MD5, SHA-1, SHA-256, SHA-3            │
│     ├─ 目的:数据完整性验证、指纹生成           │
│     └─ 特性:单向不可逆                       │
│                                              │
│  3. 加密算法 (Encryption)                    │
│     ├─ 对称加密:AES, DES, ChaCha20          │
│     ├─ 非对称加密:RSA, ECC, DH               │
│     └─ 目的:数据保密性,可逆加解密            │
└─────────────────────────────────────────────┘

(二)Base64详解

1. Base64本质:编码算法

// Base64基本使用示例
fun base64Demo() {
    val original = "Hello Android!".toByteArray()
    
    // 编码(非加密!)
    val encoded = Base64.encodeToString(original, Base64.DEFAULT)
    println("Base64编码后: $encoded") // SGVsbG8gQW5kcm9pZCE=
    
    // 解码
    val decoded = Base64.decode(encoded, Base64.DEFAULT)
    println("Base64解码后: ${String(decoded)}") // Hello Android!
    
    // 验证:任何人都可以解码,无安全性
    val anyoneCanDecode = Base64.decode("SGVsbG8gQW5kcm9pZCE=", Base64.DEFAULT)
    println("任何人都能解码: ${String(anyoneCanDecode)}")
}

2. Base64工作原理

原始数据 (二进制):  01001000 01100101 01101100 01101100 01101111
分组 (6位一组):    010010 000110 010101 101100 011011 000110 1111xx
十进制:            18     6      21     44     27     6      60
Base64字符映射:    S      G      V      s      b      G      8
结果:             SGVsbG8=

3. Base64使用场景

// 场景1:图片转Base64(用于HTML/JSON传输)
fun imageToBase64(context: Context, @DrawableRes resId: Int): String {
    val inputStream = context.resources.openRawResource(resId)
    val bytes = inputStream.readBytes()
    return Base64.encodeToString(bytes, Base64.DEFAULT)
}

// 场景2:URL安全传输(使用Base64 URL安全模式)
fun encodeUrlSafe(data: String): String {
    return Base64.encodeToString(data.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING)
}

// 场景3:简单数据混淆(注意:不是加密!)
object SimpleDataObfuscation {
    // ❌ 错误:用Base64"加密"敏感数据
    fun unsafeEncodePassword(password: String): String {
        return Base64.encodeToString(password.toByteArray(), Base64.DEFAULT)
        // 攻击者可以轻松解码得到原始密码
    }
    
    // ✅ 正确:仅用于非敏感数据编码
    fun encodeApiKeyForHeader(apiKey: String): String {
        return "Basic " + Base64.encodeToString(apiKey.toByteArray(), Base64.NO_WRAP)
    }
}

(三)MD5详解

1. MD5本质:哈希算法(散列函数)

// MD5计算示例
import java.security.MessageDigest

fun calculateMD5(input: String): String {
    val md = MessageDigest.getInstance("MD5")
    val digest = md.digest(input.toByteArray())
    
    // 转换为16进制字符串
    return digest.joinToString("") { "%02x".format(it) }
}

fun md5Demo() {
    val data = "Hello World"
    val hash = calculateMD5(data)
    println("MD5哈希值: $hash") // b10a8db164e0754105b7a99be72e3fe5
    
    // 哈希特性验证
    println("相同输入 → 相同输出: ${calculateMD5(data) == hash}") // true
    println("微小变化 → 完全不同: ${calculateMD5("Hello World!")}") // ed076287532e86365e841e92bfc50d8c
    println("不可逆测试: 无法从哈希值还原原始数据")
}

2. MD5算法特性

// 哈希算法的核心特性
object HashAlgorithmProperties {
    
    // 1. 确定性:相同输入永远产生相同输出
    fun deterministic(input: String): Boolean {
        val hash1 = calculateMD5(input)
        val hash2 = calculateMD5(input)
        return hash1 == hash2
    }
    
    // 2. 快速计算:对任意长度数据,哈希计算相对快速
    fun fastComputation(data: ByteArray): Long {
        val start = System.nanoTime()
        MessageDigest.getInstance("MD5").digest(data)
        return System.nanoTime() - start
    }
    
    // 3. 雪崩效应:输入微小变化,输出完全不同
    fun avalancheEffect(): Boolean {
        val hash1 = calculateMD5("password")
        val hash2 = calculateMD5("password1")
        return hash1 != hash2 // 应该是true
    }
    
    // 4. 抗碰撞性(MD5在此失败)
    fun collisionResistance(): String {
        return """
            MD5的抗碰撞性已被攻破!
            2004年王小云教授团队找到MD5碰撞方法
            2008年可在一分钟内找到碰撞
        """.trimIndent()
    }
}

3. MD5已被破解的证明

// MD5碰撞示例(概念展示)
object MD5CollisionDemo {
    
    // 著名的MD5碰撞示例
    val collisionPair1 = """
        d131dd02c5e6eec4693d9a0698aff95c
        2fcab58712467eab4004583eb8fb7f89
        55ad340609f4b30283e488832571415a
        085125e8f7cdc99fd91dbdf280373c5b
        d8823e3156348f5bae6dacd436c919c6
        dd53e2b487da03fd02396306d248cda0
        e99f33420f577ee8ce54b67080a80d1e
        c69821bcb6a8839396f9652b6ff72a70
    """
    
    val collisionPair2 = """
        d131dd02c5e6eec4693d9a0698aff95c
        2fcab50712467eab4004583eb8fb7f89
        55ad340609f4b30283e4888325f1415a
        085125e8f7cdc99fd91dbd7280373c5b
        d8823e3156348f5bae6dacd436c919c6
        dd53e23487da03fd02396306d248cda0
        e99f33420f577ee8ce54b67080280d1e
        c69821bcb6a8839396f965ab6ff72a70
    """
    
    // 这两个不同的数据块会产生相同的MD5值
    fun demonstrateCollision() {
        println("碰撞数据1的MD5: ${calculateMD5(collisionPair1)}")
        println("碰撞数据2的MD5: ${calculateMD5(collisionPair2)}")
        println("两个不同的输入产生了相同的MD5哈希值!")
    }
}

(四)现代替代方案与最佳实践

1. 哈希算法演进路线

MD5 (1991) → SHA-1 (1995) → SHA-256 (2001) → SHA-3 (2015)
    ↓             ↓              ↓              ↓
 已破解       已破解        目前安全       最新标准

2. 密码存储专用算法

// 现代密码哈希最佳实践
object PasswordSecurity {
    
    // ❌ 绝对禁止的做法
    object BadPractices {
        // 1. 直接存储明文密码
        fun storePlainText(password: String): String = password
        
        // 2. 使用MD5等快速哈希
        fun storeWithMD5(password: String): String = calculateMD5(password)
        
        // 3. 不使用盐值(salt)
        fun unsaltedHash(password: String): String = sha256(password)
        
        // 4. 使用固定盐值
        fun fixedSaltHash(password: String): String {
            val salt = "static_salt"
            return sha256(salt + password)
        }
    }
    
    // ✅ 推荐做法:使用bcrypt, scrypt, argon2, PBKDF2
    object BestPractices {
        
        // 方法1:使用Android Jetpack Security库
        fun hashWithJetpackSecurity(password: String): String {
            // 需要添加依赖:implementation "androidx.security:security-crypto:1.1.0-alpha06"
            /*
            val masterKey = MasterKey.Builder(applicationContext)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build()
            
            val encryptedSharedPreferences = EncryptedSharedPreferences.create(
                applicationContext,
                "secret_shared_prefs",
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            )
            */
            return "使用Jetpack Security加密"
        }
        
        // 方法2:使用Bcrypt(通过第三方库)
        fun hashWithBcrypt(password: String): String {
            // 添加依赖:implementation "at.favre.lib:bcrypt:0.9.0"
            /*
            val bcryptHash = BCrypt.withDefaults().hashToString(12, password.toCharArray())
            return bcryptHash
            */
            return "bcrypt哈希值"
        }
        
        // 方法3:使用PBKDF2(Android内置支持)
        fun hashWithPBKDF2(password: String, salt: ByteArray): String {
            val iterations = 10000
            val keyLength = 256 // bits
            
            val spec = PBEKeySpec(
                password.toCharArray(),
                salt,
                iterations,
                keyLength
            )
            
            val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
            val key = factory.generateSecret(spec)
            val hash = key.encoded
            
            return Base64.encodeToString(hash, Base64.NO_WRAP)
        }
        
        // 方法4:完整的密码存储实现
        data class StoredPassword(
            val algorithm: String,  // e.g., "PBKDF2"
            val hash: String,       // Base64编码的哈希值
            val salt: String,       // Base64编码的盐值
            val iterations: Int,    // 迭代次数
            val timestamp: Long     // 创建时间
        )
        
        fun createPasswordHash(password: String): StoredPassword {
            // 生成随机盐值
            val salt = ByteArray(16).apply {
                SecureRandom().nextBytes(this)
            }
            
            val iterations = 310000 // OWASP 2021推荐值
            val hash = hashWithPBKDF2(password, salt)
            
            return StoredPassword(
                algorithm = "PBKDF2WithHmacSHA256",
                hash = hash,
                salt = Base64.encodeToString(salt, Base64.NO_WRAP),
                iterations = iterations,
                timestamp = System.currentTimeMillis()
            )
        }
        
        fun verifyPassword(password: String, stored: StoredPassword): Boolean {
            val salt = Base64.decode(stored.salt, Base64.NO_WRAP)
            val newHash = hashWithPBKDF2(password, salt)
            return newHash == stored.hash
        }
    }
}

3. 完整性校验方案

// 文件完整性校验(不使用MD5)
object IntegrityVerification {
    
    // 场景1:文件下载校验
    fun verifyFileIntegrity(file: File, expectedHash: String): Boolean {
        // ❌ 不要用MD5
        // val actualHash = calculateFileMD5(file)
        
        // ✅ 使用SHA-256或SHA-3
        val actualHash = calculateFileSHA256(file)
        return actualHash == expectedHash
    }
    
    private fun calculateFileSHA256(file: File): String {
        val digest = MessageDigest.getInstance("SHA-256")
        file.inputStream().use { input ->
            val buffer = ByteArray(8192)
            var bytesRead: Int
            while (input.read(buffer).also { bytesRead = it } != -1) {
                digest.update(buffer, 0, bytesRead)
            }
        }
        return digest.digest().joinToString("") { "%02x".format(it) }
    }
    
    // 场景2:API请求签名(HMAC)
    fun createApiSignature(
        apiKey: String,
        secret: String,
        timestamp: Long,
        body: String
    ): String {
        val message = "$apiKey:$timestamp:$body"
        val mac = Mac.getInstance("HmacSHA256")
        val secretSpec = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
        mac.init(secretSpec)
        val result = mac.doFinal(message.toByteArray())
        return Base64.encodeToString(result, Base64.NO_WRAP)
    }
}

(五)加密算法基础

1. 真正加密算法示例

// 对称加密示例(AES)
object RealEncryption {
    
    // AES加密
    fun encryptAES(data: String, key: SecretKey): String {
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        val iv = ByteArray(12).apply {
            SecureRandom().nextBytes(this)
        }
        
        val gcmSpec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec)
        
        val ciphertext = cipher.doFinal(data.toByteArray())
        
        // 组合IV和密文
        val result = ByteArray(iv.size + ciphertext.size)
        System.arraycopy(iv, 0, result, 0, iv.size)
        System.arraycopy(ciphertext, 0, result, iv.size, ciphertext.size)
        
        return Base64.encodeToString(result, Base64.NO_WRAP)
    }
    
    // AES解密
    fun decryptAES(encryptedData: String, key: SecretKey): String {
        val data = Base64.decode(encryptedData, Base64.NO_WRAP)
        
        // 提取IV(前12字节)
        val iv = data.copyOfRange(0, 12)
        val ciphertext = data.copyOfRange(12, data.size)
        
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        val gcmSpec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec)
        
        val plaintext = cipher.doFinal(ciphertext)
        return String(plaintext)
    }
    
    // 生成安全密钥
    fun generateAESKey(): SecretKey {
        val keyGenerator = KeyGenerator.getInstance("AES")
        keyGenerator.init(256) // 使用256位密钥
        return keyGenerator.generateKey()
    }
}

// 加密与Base64对比演示
fun encryptionVsBase64() {
    val secretMessage = "我的信用卡号是 1234-5678-9012-3456"
    
    println("=== Base64(编码)===")
    val base64Encoded = Base64.encodeToString(secretMessage.toByteArray(), Base64.DEFAULT)
    println("编码后: $base64Encoded")
    println("解码后: ${String(Base64.decode(base64Encoded, Base64.DEFAULT))}")
    println("任何人都可以解码,无安全性!")
    
    println("\n=== AES加密 ===")
    val key = RealEncryption.generateAESKey()
    val encrypted = RealEncryption.encryptAES(secretMessage, key)
    println("加密后: $encrypted")
    val decrypted = RealEncryption.decryptAES(encrypted, key)
    println("解密后: $decrypted")
    println("没有密钥无法解密,真正安全!")
}

(六)实际应用场景分析

1. 需要Base64的场景

// 适合使用Base64的场景
object Base64AppropriateUse {
    
    // 1. 图片嵌入HTML/CSS
    fun createDataUri(imageBytes: ByteArray): String {
        val base64Image = Base64.encodeToString(imageBytes, Base64.DEFAULT)
        return "data:image/jpeg;base64,$base64Image"
    }
    
    // 2. 二进制数据JSON传输
    data class ApiRequest(val data: String, val image: String) // image为Base64
    
    // 3. URL参数安全编码
    fun encodeUrlParameter(param: String): String {
        return Base64.encodeToString(param.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING)
    }
    
    // 4. 简单数据混淆(非加密)
    fun obfuscateNonCriticalData(data: String): String {
        // 注意:仅用于非敏感数据,如UI状态、配置标记等
        return Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)
    }
}

2. 需要哈希的场景(不用MD5)

// 适合使用哈希算法的场景
object HashAppropriateUse {
    
    // 1. 密码存储(使用专用算法)
    fun storePassword(password: String): PasswordSecurity.BestPractices.StoredPassword {
        return PasswordSecurity.BestPractices.createPasswordHash(password)
    }
    
    // 2. 文件完整性验证(使用SHA-256)
    fun calculateFileChecksum(file: File): String {
        return IntegrityVerification.calculateFileSHA256(file)
    }
    
    // 3. 去重/指纹识别
    object Deduplication {
        private val fileHashes = mutableSetOf<String>()
        
        fun isDuplicate(file: File): Boolean {
            val hash = calculateFileChecksum(file)
            return if (fileHashes.contains(hash)) {
                true
            } else {
                fileHashes.add(hash)
                false
            }
        }
    }
    
    // 4. 数字签名(使用HMAC)
    fun verifyMessageSignature(
        message: String,
        signature: String,
        secretKey: String
    ): Boolean {
        val mac = Mac.getInstance("HmacSHA256")
        val keySpec = SecretKeySpec(secretKey.toByteArray(), "HmacSHA256")
        mac.init(keySpec)
        val calculated = mac.doFinal(message.toByteArray())
        val calculatedBase64 = Base64.encodeToString(calculated, Base64.NO_WRAP)
        
        return calculatedBase64 == signature
    }
}

3. 需要真正加密的场景

// 适合使用加密算法的场景
object EncryptionAppropriateUse {
    
    // 1. 存储用户敏感数据
    fun encryptUserData(context: Context, data: String): String {
        // 使用Android Keystore系统
        val keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        
        if (!keyStore.containsAlias("app_encryption_key")) {
            // 生成新密钥
            val keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES,
                "AndroidKeyStore"
            )
            val keySpec = KeyGenParameterSpec.Builder(
                "app_encryption_key",
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setKeySize(256)
                .build()
            
            keyGenerator.init(keySpec)
            keyGenerator.generateKey()
        }
        
        val secretKey = keyStore.getKey("app_encryption_key", null) as SecretKey
        return RealEncryption.encryptAES(data, secretKey)
    }
    
    // 2. 安全通信(TLS/SSL)
    fun makeSecureApiCall(url: String, data: String) {
        // 使用OkHttp或Retrofit配置TLS
        val client = OkHttpClient.Builder()
            .sslSocketFactory(createSSLSocketFactory(), createTrustManager())
            .build()
        
        // ... 发起HTTPS请求
    }
    
    // 3. 安全数据共享
    fun shareEncryptedData(data: String, publicKey: PublicKey): String {
        val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
        cipher.init(Cipher.ENCRYPT_MODE, publicKey)
        val encrypted = cipher.doFinal(data.toByteArray())
        return Base64.encodeToString(encrypted, Base64.NO_WRAP)
    }
}

(七)安全算法选择指南

1. 决策流程图

开始
  ↓
需要保护数据保密性吗?
  ├─ 是 → 使用加密算法(AES/RSA)
  │      ├─ 本地存储 → Android Keystore + AES
  │      ├─ 网络传输 → TLS 1.3 + 证书固定
  │      └─ 数据共享 → RSA非对称加密
  │
  ├─ 否 → 需要验证数据完整性吗?
  │      ├─ 是 → 使用哈希算法
  │      │      ├─ 密码存储 → bcrypt/argon2/PBKDF2
  │      │      ├─ 文件校验 → SHA-256/SHA-3
  │      │      └─ 数字签名 → HMAC-SHA256
  │      │
  │      └─ 否 → 只需要数据转换?
  │             ├─ 是 → 使用编码算法(Base64)
  │             │      ├─ 二进制转文本 → Base64
  │             │      ├─ URL安全编码 → Base64 URL安全模式
  │             │      └─ 简单数据混淆 → Base64(仅非敏感数据)
  │             │
  │             └─ 否 → 重新评估需求
  ↓
结束

2. 安全算法速查表

object SecurityAlgorithmCheatsheet {
    
    // 编码算法(无安全性)
    val encodingAlgorithms = mapOf(
        "Base64" to "二进制↔文本转换,无密钥,可逆",
        "Base32" to "类似Base64,字母表不同",
        "URL Encoding" to "URL参数编码,%转义"
    )
    
    // 哈希算法(单向)
    val hashAlgorithms = mapOf(
        "MD5" to "❌已破解,仅用于非安全场景",
        "SHA-1" to "❌已破解,不推荐使用",
        "SHA-256" to "✅当前安全,广泛使用",
        "SHA-3" to "✅最新标准,未来证明",
        "Bcrypt" to "✅密码专用,抗GPU破解",
        "Argon2" to "✅密码哈希竞赛获胜者"
    )
    
    // 加密算法(可逆)
    val encryptionAlgorithms = mapOf(
        "对称加密" to mapOf(
            "AES-256-GCM" to "✅推荐,高效安全",
            "ChaCha20-Poly1305" to "✅移动设备优化",
            "DES/3DES" to "❌已过时,不安全"
        ),
        "非对称加密" to mapOf(
            "RSA-2048+" to "✅密钥交换,数字签名",
            "ECC (P-256)" to "✅更短密钥,相同安全",
            "DH/ECDH" to "✅密钥协商协议"
        )
    )
    
    // 根据场景推荐算法
    fun recommendAlgorithm(scenario: String): String {
        return when (scenario) {
            "密码存储" -> "Bcrypt (迭代次数≥12) 或 Argon2id"
            "文件校验" -> "SHA-256 或 SHA-3"
            "API签名" -> "HMAC-SHA256"
            "本地数据加密" -> "AES-256-GCM + Android Keystore"
            "网络传输" -> "TLS 1.3 + 证书固定"
            "数据编码" -> "Base64 (URL安全模式用于URL)"
            else -> "咨询安全专家"
        }
    }
}

(八)面试回答要点总结

1. 核心问题回答

  • Base64是加密算法吗?
    不是。Base64是编码算法,用于二进制到文本的转换,无安全性,可逆操作,目的是数据兼容性而非保密性。

  • MD5是加密算法吗?
    不是。MD5是哈希算法(散列函数),单向不可逆,用于数据完整性验证和指纹生成,但已被破解,不应用于安全场景。

2. 关键区别总结

特性 Base64 MD5 真正加密算法 (如AES)
目的 数据编码转换 数据完整性验证 数据保密性
可逆性 可逆(解码) 不可逆 可逆(解密)
密钥 有(加密/解密密钥)
安全性 无安全性 已不安全 设计目的就是安全
输出长度 可变(约4/3倍输入) 固定128位 取决于算法和模式
典型用途 图片转文本、URL编码 文件校验(已过时) 安全通信、数据加密

3. 现代实践建议

  • 密码存储:使用bcrypt、argon2、PBKDF2等专用密码哈希算法
  • 完整性校验:使用SHA-256或SHA-3替代MD5
  • 数据编码:Base64仅用于非敏感数据的格式转换
  • 真正加密:敏感数据使用AES-256-GCM等现代加密算法
  • 密钥管理:使用Android Keystore系统安全存储密钥

4. 常见误区纠正

  • ❌ “Base64加密了我的密码” → 错误!Base64只是编码,可以轻松解码
  • ❌ “MD5加密是安全的” → 错误!MD5已破解,可在短时间内找到碰撞
  • ❌ “使用Base64和MD5组合更安全” → 错误!安全不是套娃,使用错误算法不会增加安全性

5. 一句话总结

Base64是编码而非加密,MD5是哈希而非加密,两者都不提供真正的数据保密性。现代开发中应选择专门设计的加密算法来保护敏感数据,选择抗碰撞的哈希算法来确保数据完整性。

六十六、HttpClient和HttpURLConnection的区别?

(一)历史演进与现状

1. 网络库发展时间线

2005-2009          2010-2013           2014-至今
Apache HttpClient → HttpURLConnection → OkHttp/Retrofit
(早期Android)    (Android 2.3+优化) (现代网络库)
      ↓                  ↓                  ↓
API丰富但复杂       官方推荐但原始      成为行业标准
Android 6.0移除    Android 4.4+内置OkHttp   Retrofit基于OkHttp

(二)Apache HttpClient(已过时)

1. 基本特性

// Apache HttpClient使用示例(已废弃)
@Deprecated("Android 6.0+已移除,不应使用")
public class ApacheHttpClientExample {
    public static String fetchWithHttpClient(String url) throws Exception {
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(url);
        
        // 丰富的配置选项
        request.setConfig(RequestConfig.custom()
            .setConnectTimeout(5000)
            .setSocketTimeout(10000)
            .build());
        
        HttpResponse response = client.execute(request);
        HttpEntity entity = response.getEntity();
        
        return EntityUtils.toString(entity);
    }
}

2. 主要特点与问题

object ApacheHttpClientCharacteristics {
    
    // ✅ 曾经的优点(已过时)
    val advantages = listOf(
        "1. API功能丰富,支持高级特性",
        "2. 线程安全,连接池管理完善",
        "3. 支持身份认证、Cookie管理等",
        "4. 社区活跃,文档齐全(曾经)"
    )
    
    // ❌ 存在的问题
    val problems = listOf(
        "1. 包体积庞大(500+KB)",
        "2. API设计复杂,学习曲线陡峭",
        "3. 内存占用较高",
        "4. Android 6.0(API 23)已从SDK中移除",
        "5. 需要手动添加org.apache.http.legacy库才能使用",
        "6. Google官方已明确不推荐使用"
    )
    
    // 兼容性处理(如果需要使用)
    fun getLegacySupport(): String {
        return """
            如需在Android 6.0+使用HttpClient:
            
            在build.gradle中添加:
            android {
                useLibrary 'org.apache.http.legacy'
            }
            
            或在AndroidManifest.xml中声明:
            <uses-library
                android:name="org.apache.http.legacy"
                android:required="false" />
            
            但强烈建议迁移到现代网络库!
        """.trimIndent()
    }
}

(三)HttpURLConnection(官方基础库)

1. 基本使用

// HttpURLConnection基础示例
object HttpURLConnectionExample {
    
    fun get(urlString: String): String {
        var connection: HttpURLConnection? = null
        var inputStream: InputStream? = null
        
        try {
            val url = URL(urlString)
            connection = url.openConnection() as HttpURLConnection
            
            // 配置连接
            connection.requestMethod = "GET"
            connection.connectTimeout = 10000 // 10秒
            connection.readTimeout = 15000    // 15秒
            connection.setRequestProperty("User-Agent", "MyApp/1.0")
            
            // 启用GZIP压缩
            connection.setRequestProperty("Accept-Encoding", "gzip")
            
            // 连接
            connection.connect()
            
            // 处理响应
            val responseCode = connection.responseCode
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw IOException("HTTP错误代码: $responseCode")
            }
            
            // 读取响应(处理GZIP)
            inputStream = if ("gzip" == connection.contentEncoding) {
                GZIPInputStream(connection.inputStream)
            } else {
                connection.inputStream
            }
            
            return inputStream.bufferedReader().use { it.readText() }
            
        } finally {
            inputStream?.close()
            connection?.disconnect()
        }
    }
    
    fun post(urlString: String, body: String): String {
        val connection = URL(urlString).openConnection() as HttpURLConnection
        connection.requestMethod = "POST"
        connection.doOutput = true
        
        // 设置请求头
        connection.setRequestProperty("Content-Type", "application/json")
        connection.setRequestProperty("Content-Length", body.length.toString())
        
        // 写入请求体
        connection.outputStream.use { output ->
            output.write(body.toByteArray())
            output.flush()
        }
        
        // 读取响应
        return connection.inputStream.bufferedReader().use { it.readText() }
    }
}

2. Android版本演进带来的改进

object HttpURLConnectionEvolution {
    
    // Android 2.3+ 优化
    data class GingerbreadImprovements(
        val autoGzip: Boolean = true,          // 自动GZIP压缩
        val responseCache: Boolean = true,     // 响应缓存支持
        val httpsImprovements: Boolean = true  // HTTPS改进
    )
    
    // Android 4.0+ 进一步改进
    data class IceCreamSandwichImprovements(
        val transparentCompression: Boolean = true, // 透明压缩
        val requestBody: Boolean = true,           // 请求体改进
        val improvedTimeouts: Boolean = true       // 超时机制改进
    )
    
    // Android 4.4+ 重大改变:内部实现替换为OkHttp
    data class KitKatRevolution(
        val internalOkHttp: Boolean = true,        // 内部使用OkHttp实现
        val spdySupport: Boolean = true,           // SPDY协议支持
        val betterPerformance: Boolean = true      // 性能显著提升
    )
    
    fun getCurrentImplementation(): String {
        return when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> {
                """
                Android 4.4+ 中 HttpURLConnection 的实现:
                
                底层实现:OkHttp(Square公司提供)
                支持特性:
                - SPDY/HTTP2 协议支持
                - 连接池复用
                - 透明GZIP压缩
                - 响应缓存
                - 更优的重试机制
                
                注意:API保持兼容,但实现已更换
                """.trimIndent()
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH -> {
                "Android 4.0-4.3:改进的HttpURLConnection实现"
            }
            else -> {
                "Android 2.3-3.x:基础HttpURLConnection实现"
            }
        }
    }
}

3. 现代使用模式(结合协程)

// 使用协程封装HttpURLConnection
class HttpURLConnectionCoroutine {
    
    suspend fun fetchAsync(url: String): Result<String> = withContext(Dispatchers.IO) {
        try {
            val connection = URL(url).openConnection() as HttpURLConnection
            connection.connectTimeout = 10000
            connection.readTimeout = 15000
            
            val responseCode = connection.responseCode
            if (responseCode in 200..299) {
                val content = connection.inputStream.bufferedReader().use { it.readText() }
                Result.success(content)
            } else {
                Result.failure(IOException("HTTP $responseCode"))
            }
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    // 支持取消的版本
    suspend fun fetchWithCancellation(
        url: String,
        scope: CoroutineScope
    ): Deferred<Result<String>> = scope.async {
        fetchAsync(url)
    }
}

(四)OkHttp(现代标准)

1. OkHttp核心优势

// OkHttp基本使用
object OkHttpExample {
    
    private val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        })
        .build()
    
    suspend fun get(url: String): Result<String> = withContext(Dispatchers.IO) {
        try {
            val request = Request.Builder()
                .url(url)
                .header("User-Agent", "MyApp/1.0")
                .build()
            
            client.newCall(request).execute().use { response ->
                if (response.isSuccessful) {
                    val body = response.body?.string() ?: ""
                    Result.success(body)
                } else {
                    Result.failure(IOException("HTTP ${response.code}"))
                }
            }
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    // 异步请求
    fun getAsync(url: String, callback: (Result<String>) -> Unit) {
        val request = Request.Builder()
            .url(url)
            .build()
        
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                val body = response.body?.string() ?: ""
                callback(Result.success(body))
            }
            
            override fun onFailure(call: Call, e: IOException) {
                callback(Result.failure(e))
            }
        })
    }
}

2. OkHttp高级特性

// OkHttp高级配置
class AdvancedOkHttpConfig {
    
    fun createCustomClient(): OkHttpClient {
        return OkHttpClient.Builder()
            // 1. 连接池配置
            .connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
            
            // 2. 拦截器(强大特性)
            .addInterceptor(Interceptor { chain ->
                val original = chain.request()
                
                // 添加公共请求头
                val request = original.newBuilder()
                    .header("Authorization", "Bearer $token")
                    .header("Accept", "application/json")
                    .method(original.method, original.body)
                    .build()
                
                // 记录请求时间
                val startTime = System.nanoTime()
                val response = chain.proceed(request)
                val duration = (System.nanoTime() - startTime) / 1e6
                
                println("请求 ${request.url} 耗时 ${duration}ms")
                
                response
            })
            
            // 3. 网络拦截器(重定向、重试等)
            .addNetworkInterceptor(Interceptor { chain ->
                var request = chain.request()
                var response = chain.proceed(request)
                
                var tryCount = 0
                while (!response.isSuccessful && tryCount < 3) {
                    tryCount++
                    response.close()
                    response = chain.proceed(request)
                }
                
                response
            })
            
            // 4. 事件监听器
            .eventListener(object : EventListener() {
                override fun callStart(call: Call) {
                    println("开始请求: ${call.request().url}")
                }
                
                override fun callEnd(call: Call) {
                    println("请求结束: ${call.request().url}")
                }
            })
            
            // 5. 缓存配置
            .cache(Cache(File("/cache"), 10 * 1024 * 1024)) // 10MB
            
            // 6. Cookie管理
            .cookieJar(object : CookieJar {
                private val cookieStore = mutableMapOf<HttpUrl, List<Cookie>>()
                
                override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
                    cookieStore[url] = cookies
                }
                
                override fun loadForRequest(url: HttpUrl): List<Cookie> {
                    return cookieStore[url] ?: emptyList()
                }
            })
            
            .build()
    }
    
    // HTTP/2 和 WebSocket 支持
    fun advancedFeatures() {
        val client = OkHttpClient.Builder()
            .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
            .build()
        
        // WebSocket
        val request = Request.Builder()
            .url("wss://echo.websocket.org")
            .build()
        
        val webSocket = client.newWebSocket(request, object : WebSocketListener() {
            override fun onOpen(webSocket: WebSocket, response: Response) {
                webSocket.send("Hello!")
            }
            
            override fun onMessage(webSocket: WebSocket, text: String) {
                println("收到消息: $text")
            }
        })
    }
}

3. OkHttp在Android中的特殊优化

// Android专用的OkHttp配置
object AndroidOkHttpOptimization {
    
    fun createAndroidOptimizedClient(context: Context): OkHttpClient {
        return OkHttpClient.Builder()
            // 1. 使用Android的线程池
            .dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
            
            // 2. 添加Android网络状态感知拦截器
            .addInterceptor { chain ->
                if (!isNetworkAvailable(context)) {
                    throw IOException("网络不可用")
                }
                chain.proceed(chain.request())
            }
            
            // 3. 图片加载优化
            .addInterceptor { chain ->
                val request = chain.request()
                val isImageRequest = request.url.toString()
                    .contains(Regex(".(jpg|png|gif|webp)$", RegexOption.IGNORE_CASE))
                
                val newRequest = if (isImageRequest) {
                    request.newBuilder()
                        .header("Accept", "image/*")
                        .build()
                } else {
                    request
                }
                
                chain.proceed(newRequest)
            }
            
            // 4. 响应缓存(配合Android存储)
            .cache(Cache(
                File(context.cacheDir, "http_cache"),
                50L * 1024L * 1024L // 50MB
            ))
            
            .build()
    }
    
    private fun isNetworkAvailable(context: Context): Boolean {
        val connectivityManager = context.getSystemService(
            Context.CONNECTIVITY_SERVICE
        ) as ConnectivityManager
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val network = connectivityManager.activeNetwork
            val capabilities = connectivityManager.getNetworkCapabilities(network)
            return capabilities != null && (
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
            )
        } else {
            @Suppress("DEPRECATION")
            val networkInfo = connectivityManager.activeNetworkInfo
            return networkInfo != null && networkInfo.isConnected
        }
    }
}

(五)现代网络库生态

1. Retrofit + OkHttp组合

// Retrofit(类型安全的HTTP客户端)
interface GitHubService {
    @GET("users/{user}/repos")
    suspend fun listRepos(@Path("user") user: String): List<Repo>
    
    @POST("users/new")
    suspend fun createUser(@Body user: User): Response<User>
    
    @Multipart
    @POST("upload")
    suspend fun uploadFile(@Part file: MultipartBody.Part): UploadResponse
    
    @Streaming
    @GET
    suspend fun downloadFile(@Url url: String): Response<ResponseBody>
}

// Retrofit配置
object RetrofitSetup {
    private val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(HttpLoggingInterceptor())
        .build()
    
    private val retrofit = Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .build()
    
    val service: GitHubService = retrofit.create(GitHubService::class.java)
}

// 使用示例
class GitHubRepository {
    suspend fun getRepositories(username: String): List<Repo> {
        return try {
            RetrofitSetup.service.listRepos(username)
        } catch (e: Exception) {
            emptyList()
        }
    }
}

2. 其他现代网络库

(1)Ktor Client(Kotlin多平台)
// Ktor Client示例
object KtorExample {
    private val client = HttpClient(CIO) {
        install(JsonFeature) {
            serializer = KotlinxSerializer()
        }
        install(HttpTimeout) {
            requestTimeoutMillis = 15000L
        }
    }
    
    suspend fun fetchData(): String {
        return client.get("https://api.example.com/data")
    }
}
(2)Volley(Google出品,适用于简单场景)
// Volley示例(适合小规模、频繁的请求)
object VolleyExample {
    fun requestWithVolley(context: Context, url: String) {
        val queue = Volley.newRequestQueue(context)
        
        val stringRequest = StringRequest(
            Request.Method.GET, url,
            { response ->
                // 处理响应
            },
            { error ->
                // 处理错误
            }
        )
        
        queue.add(stringRequest)
    }
}

(六)综合对比与选择指南

1. 详细特性对比表

特性 Apache HttpClient HttpURLConnection OkHttp Retrofit
当前状态 ❌ 已废弃 ✅ 内置(底层为OkHttp) ✅ 行业标准 ✅ 推荐(基于OkHttp)
API设计 复杂、重量级 简单、原始 现代、友好 类型安全、声明式
性能 中等 良好(Android 4.4+优) 优秀 优秀(依赖OkHttp)
HTTP/2支持 有限 Android 4.4+支持 完整支持 完整支持
连接池 支持 Android 4.4+支持 优秀支持 优秀支持
拦截器 有限 不支持 强大支持 强大支持
异步处理 有限 需自行实现 优秀支持 协程/RxJava支持
缓存机制 支持 支持 高级支持 支持
WebSocket 不支持 不支持 支持 支持
学习成本
维护性 优秀 优秀
适用场景 已废弃项目 简单HTTP请求 现代网络需求 API密集型应用

2. 选择决策树

开始网络库选择
    │
    ├─ 需求:简单HTTP请求,无外部依赖
    │       ↓
    │    使用 HttpURLConnection(Android 4.4+)
    │
    ├─ 需求:现代网络功能,良好性能
    │       ↓
    │    使用 OkHttp
    │       ├─ 需要高级功能:拦截器、缓存、HTTP/2
    │       └─ 项目已使用Square生态
    │
    ├─ 需求:REST API调用,类型安全
    │       ↓
    │    使用 Retrofit + OkHttp
    │       ├─ 大量API接口需要管理
    │       ├─ 需要自动序列化/反序列化
    │       └─ 已使用协程或RxJava
    │
    ├─ 需求:Kotlin多平台项目
    │       ↓
    │    使用 Ktor Client
    │
    └─ 需求:维护遗留代码
            ↓
        保持原有HttpClient(尽快迁移)

3. 迁移指南

// 从HttpURLConnection迁移到OkHttp
object MigrationGuide {
    
    // 步骤1:添加依赖
    const val GRADLE_DEPENDENCY = """
        dependencies {
            implementation("com.squareup.okhttp3:okhttp:4.12.0")
            // 可选:日志拦截器
            implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
        }
    """
    
    // 步骤2:迁移代码模式
    fun migrateFromHttpURLConnection(httpURLConnectionCode: String): String {
        return when {
            httpURLConnectionCode.contains("HttpURLConnection") -> {
                """
                // 原HttpURLConnection代码迁移为OkHttp:
                
                // 1. 创建OkHttpClient单例
                private val client = OkHttpClient()
                
                // 2. 替换请求逻辑
                val request = Request.Builder()
                    .url("https://api.example.com")
                    .build()
                
                client.newCall(request).execute().use { response ->
                    // 处理响应
                }
                """.trimIndent()
            }
            else -> "无需迁移或使用其他模式"
        }
    }
    
    // 步骤3:渐进式迁移策略
    fun progressiveMigrationStrategy(): List<String> {
        return listOf(
            "1. 新功能直接使用OkHttp/Retrofit",
            "2. 修改现有功能时,顺便迁移到新网络库",
            "3. 创建网络层抽象,便于替换实现",
            "4. 编写测试确保迁移不影响功能",
            "5. 最终移除旧网络库依赖"
        )
    }
}

(七)性能优化与最佳实践

1. 连接池优化

object ConnectionPoolOptimization {
    
    fun createOptimizedClient(): OkHttpClient {
        return OkHttpClient.Builder()
            // 优化连接池(根据服务器限制调整)
            .connectionPool(
                ConnectionPool(
                    maxIdleConnections = 5,     // 最大空闲连接数
                    keepAliveDuration = 5,      // 保持活动时间(分钟)
                    timeUnit = TimeUnit.MINUTES
                )
            )
            
            // DNS优化(使用HTTP DNS)
            .dns(Dns.SYSTEM) // 或自定义Dns实现
            
            // 连接超时重试
            .retryOnConnectionFailure(true)
            
            // 协议优化
            .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
            
            .build()
    }
    
    // 监控连接池状态
    fun monitorPool(client: OkHttpClient) {
        val pool = client.connectionPool
        println("空闲连接数: ${pool.idleConnectionCount()}")
        println("总连接数: ${pool.connectionCount()}")
    }
}

2. 缓存策略

object CacheStrategy {
    
    fun createCachingClient(context: Context): OkHttpClient {
        return OkHttpClient.Builder()
            .cache(Cache(
                directory = File(context.cacheDir, "okhttp_cache"),
                maxSize = 50L * 1024L * 1024L // 50MB
            ))
            
            // 添加缓存控制拦截器
            .addInterceptor { chain ->
                val request = chain.request()
                val response = chain.proceed(request)
                
                // 根据服务器缓存头设置缓存策略
                val cacheControl = CacheControl.Builder()
                    .maxAge(60, TimeUnit.SECONDS) // 客户端缓存60秒
                    .build()
                
                response.newBuilder()
                    .header("Cache-Control", cacheControl.toString())
                    .build()
            }
            
            .addNetworkInterceptor { chain ->
                val request = chain.request()
                val response = chain.proceed(request)
                
                // 离线时使用缓存
                if (!isNetworkAvailable()) {
                    val maxStale = 60 * 60 * 24 * 7 // 离线时缓存一周
                    val offlineCacheControl = CacheControl.Builder()
                        .maxStale(maxStale, TimeUnit.SECONDS)
                        .build()
                    
                    return@addNetworkInterceptor response.newBuilder()
                        .header("Cache-Control", offlineCacheControl.toString())
                        .build()
                }
                
                response
            }
            
            .build()
    }
}

3. 安全配置

object SecurityConfiguration {
    
    fun createSecureClient(): OkHttpClient {
        return OkHttpClient.Builder()
            // 证书固定(防止中间人攻击)
            .certificatePinner(
                CertificatePinner.Builder()
                    .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAA=")
                    .build()
            )
            
            // 连接规格(TLS配置)
            .connectionSpecs(listOf(
                ConnectionSpec.MODERN_TLS,
                ConnectionSpec.COMPATIBLE_TLS,
                ConnectionSpec.CLEARTEXT
            ))
            
            // 主机名验证
            .hostnameVerifier { hostname, session ->
                HostnameVerifier.BASIC.verify(hostname, session) &&
                hostname.endsWith(".example.com")
            }
            
            // 添加安全拦截器
            .addInterceptor { chain ->
                val request = chain.request()
                
                // 确保使用HTTPS
                if (!request.isHttps && BuildConfig.DEBUG.not()) {
                    throw SecurityException("生产环境必须使用HTTPS")
                }
                
                // 添加安全头
                val secureRequest = request.newBuilder()
                    .header("X-Content-Type-Options", "nosniff")
                    .header("X-Frame-Options", "DENY")
                    .header("X-XSS-Protection", "1; mode=block")
                    .build()
                
                chain.proceed(secureRequest)
            }
            
            .build()
    }
}

(八)面试回答要点总结

1. 核心区别总结

  • Apache HttpClient
    • 已过时,Android 6.0+已移除
    • API丰富但复杂,包体积大
    • 仅用于维护遗留代码
  • HttpURLConnection
    • 官方基础库,轻量简单
    • Android 2.3+优化,Android 4.4+底层实现为OkHttp
    • 适合简单HTTP请求
  • OkHttp
    • 现代网络库标准,性能优秀
    • 支持HTTP/2、连接池、拦截器等高级特性
    • HttpURLConnection在Android 4.4+的底层实现
  • Retrofit
    • 基于OkHttp的类型安全HTTP客户端
    • 声明式API,适合RESTful接口
    • 支持协程、RxJava等

2. 现代开发推荐

  • 新项目:直接使用Retrofit + OkHttp组合
  • 简单需求:使用OkHttp直接请求
  • 维护老项目:逐步迁移到OkHttp
  • Kotlin多平台:考虑Ktor Client

3. 性能优化要点

  • 合理配置连接池参数
  • 使用HTTP/2减少连接建立开销
  • 实现适当的缓存策略
  • 添加拦截器监控和优化网络请求
  • 注意内存泄漏,正确管理生命周期

4. 安全注意事项

  • 生产环境强制使用HTTPS
  • 实现证书固定防止中间人攻击
  • 添加安全相关的HTTP头部
  • 合理处理网络错误和超时

5. 一句话总结

在Android网络编程演进中,已从Apache HttpClient过渡到官方优化的HttpURLConnection,最终确立了OkHttp作为现代标准,Retrofit在此基础上提供了更优雅的类型安全API,新项目应直接采用Retrofit+OkHttp组合。

六十七、Activity A跳转B后返回,生命周期顺序?

(一)标准场景生命周期顺序

1. A启动B(标准流程)

// 生命周期调用顺序:
Activity A.onPause()      → 
Activity B.onCreate()     → 
Activity B.onStart()      → 
Activity B.onResume()     → 
Activity A.onStop()

2. B返回A(标准流程)

// 生命周期调用顺序:
Activity B.onPause()      → 
Activity A.onRestart()    → 
Activity A.onStart()      → 
Activity A.onResume()     → 
Activity B.onStop()       → 
Activity B.onDestroy()

(二)详细生命周期流程图

sequenceDiagram
    participant A as Activity A
    participant B as Activity B
    participant System as Android系统

    Note over A,B: 场景:A启动B
    
    A->>System: onPause() - A失去焦点
    System->>B: onCreate() - B创建
    System->>B: onStart() - B即将可见
    System->>B: onResume() - B获得焦点
    System->>A: onStop() - A完全不可见
    
    Note over A,B: 场景:B返回A
    
    B->>System: onPause() - B失去焦点
    System->>A: onRestart() - A重新启动
    System->>A: onStart() - A即将可见
    System->>A: onResume() - A获得焦点
    System->>B: onStop() - B完全不可见
    System->>B: onDestroy() - B销毁

(三)实际代码验证示例

// Activity A
class ActivityA : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_a)
        Log.d("Lifecycle", "A: onCreate")
        
        findViewById<Button>(R.id.btn_go_to_b).setOnClickListener {
            startActivity(Intent(this, ActivityB::class.java))
        }
    }
    
    override fun onStart() {
        super.onStart()
        Log.d("Lifecycle", "A: onStart")
    }
    
    override fun onResume() {
        super.onResume()
        Log.d("Lifecycle", "A: onResume")
    }
    
    override fun onPause() {
        super.onPause()
        Log.d("Lifecycle", "A: onPause")
    }
    
    override fun onStop() {
        super.onStop()
        Log.d("Lifecycle", "A: onStop")
    }
    
    override fun onRestart() {
        super.onRestart()
        Log.d("Lifecycle", "A: onRestart")
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("Lifecycle", "A: onDestroy")
    }
}

// Activity B
class ActivityB : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_b)
        Log.d("Lifecycle", "B: onCreate")
        
        findViewById<Button>(R.id.btn_back_to_a).setOnClickListener {
            finish() // 返回Activity A
        }
    }
    
    override fun onStart() {
        super.onStart()
        Log.d("Lifecycle", "B: onStart")
    }
    
    override fun onResume() {
        super.onResume()
        Log.d("Lifecycle", "B: onResume")
    }
    
    override fun onPause() {
        super.onPause()
        Log.d("Lifecycle", "B: onPause")
    }
    
    override fun onStop() {
        super.onStop()
        Log.d("Lifecycle", "B: onStop")
    }
    
    override fun onDestroy() {
        super.onDestroy()
        Log.d("Lifecycle", "B: onDestroy")
    }
}

// 日志输出示例:
/*
A: onCreate
A: onStart
A: onResume

// 点击按钮从A跳转到B
A: onPause
B: onCreate
B: onStart
B: onResume
A: onStop

// 点击返回按钮从B返回A
B: onPause
A: onRestart
A: onStart
A: onResume
B: onStop
B: onDestroy
*/

(四)特殊场景下的生命周期变化

1. 透明Activity或对话框样式

<!-- B是透明或对话框样式的Activity -->
<style name="Theme.DialogActivity" parent="Theme.AppCompat.Light.Dialog">
    <item name="android:windowIsTranslucent">true</item>
</style>
// 当B是透明或对话框Activity时:
// A启动B:A不会调用onStop()
A.onPause() → B.onCreate() → B.onStart() → B.onResume()
// A仍然部分可见,所以不调用onStop()

// B返回A:
B.onPause() → A.onResume() → B.onStop() → B.onDestroy()
// A直接调用onResume(),不调用onRestart()和onStart()

2. 使用startActivityForResult()的返回流程

// 使用Activity Result API(现代方式)
class ActivityA : AppCompatActivity() {
    private val resultLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == RESULT_OK) {
            // 处理返回结果
        }
    }
    
    fun startBForResult() {
        resultLauncher.launch(Intent(this, ActivityB::class.java))
    }
}

// 生命周期顺序与标准流程相同,只是在返回时额外处理结果回调

3. 配置更改导致重建

// 如果屏幕旋转发生在跳转过程中:
// A启动B,然后旋转屏幕
A.onPause() → B.onCreate() → B.onStart() → B.onResume() → A.onStop()
// 旋转屏幕
A.onSaveInstanceState() → A.onDestroy() → A.onCreate() → A.onStart() → A.onRestoreInstanceState()
B.onSaveInstanceState() → B.onDestroy() → B.onCreate() → B.onStart() → B.onResume() → B.onRestoreInstanceState()

4. Android 10+的后台启动限制

// Android 10+从后台启动Activity受限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    // 检查是否可以从后台启动Activity
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    if (activityManager.isBackgroundRestricted) {
        // 无法从后台启动,需要用户交互或显示通知
        Log.w("Lifecycle", "后台启动受限")
    }
}

(五)状态保存与恢复

1. 状态保存时机

// 在跳转过程中,系统可能调用onSaveInstanceState()
// A启动B时:
A.onPause() → A.onSaveInstanceState() → B.onCreate() → ...

// B返回A时,如果系统可能回收B:
B.onPause() → B.onSaveInstanceState() → A.onRestart() → ...

2. 现代状态管理

// 使用ViewModel + SavedStateHandle管理状态
class ActivityBViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    
    // 自动保存和恢复状态
    val userInput: MutableLiveData<String> = 
        savedStateHandle.getLiveData("userInput", "")
    
    fun saveInput(input: String) {
        savedStateHandle["userInput"] = input
    }
}

(六)实际开发中的注意事项

1. 避免在onPause()中执行耗时操作

// ❌ 错误做法
override fun onPause() {
    super.onPause()
    saveDataToDatabase() // 耗时操作,会延迟下一个Activity的启动
    uploadLogs()         // 网络请求,可能导致ANR
}

// ✅ 正确做法
override fun onPause() {
    super.onPause()
    // 只执行必要且快速的操作
    releaseExclusiveResources() // 释放相机等独占资源
}

override fun onStop() {
    super.onStop()
    // 在onStop()中执行较耗时的保存操作
    saveDataToDatabase()
    
    // 或使用WorkManager在后台执行
    scheduleBackgroundWork()
}

2. 正确处理返回键

// 现代返回处理(Android 13+)
class ActivityB : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 使用OnBackPressedDispatcher处理返回
        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (shouldInterceptBackPress) {
                    // 自定义处理逻辑
                    showExitConfirmation()
                } else {
                    // 默认行为
                    isEnabled = false
                    onBackPressed()
                }
            }
        })
    }
}

3. 内存优化建议

// 在onStop()或onDestroy()中释放资源
override fun onStop() {
    super.onStop()
    
    // 释放不必要的大对象引用
    largeBitmap?.recycle()
    largeBitmap = null
    
    // 取消网络请求
    networkRequest?.cancel()
    
    // 注销广播接收器
    unregisterReceiver(receiver)
}

override fun onDestroy() {
    super.onDestroy()
    
    // 清理静态引用,避免内存泄漏
    MySingleton.clearContextReference(this)
    
    // 停止后台服务(如果需要)
    stopService(serviceIntent)
}

(七)调试与监控工具

1. 使用Lifecycle日志

// 添加Lifecycle观察者监控生命周期
class LifecycleLogger : LifecycleObserver {
    
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreate() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_CREATE")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_START")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_RESUME")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_PAUSE")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_STOP")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        Log.d("LifecycleDebug", "${javaClass.simpleName}: ON_DESTROY")
    }
}

// 在Activity中使用
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(LifecycleLogger())
    }
}

2. Android Studio Profiler监控

# 使用ADB命令监控Activity堆栈
adb shell dumpsys activity activities

# 查看当前活动的Activity
adb shell dumpsys activity | grep -E "mResumedActivity|mFocusedActivity"

(八)面试回答要点总结

1. 标准生命周期顺序总结

  • A启动B
    1. A.onPause() - A失去焦点
    2. B.onCreate() - B创建
    3. B.onStart() - B即将可见
    4. B.onResume() - B获得焦点
    5. A.onStop() - A完全不可见
  • B返回A
    1. B.onPause() - B失去焦点
    2. A.onRestart() - A重新启动(如果A已onStop())
    3. A.onStart() - A即将可见
    4. A.onResume() - A获得焦点
    5. B.onStop() - B完全不可见
    6. B.onDestroy() - B销毁

2. 关键注意事项

  • 透明Activity:如果B是透明或对话框样式,A不会调用onStop()
  • 状态保存:系统可能在onStop()之前调用onSaveInstanceState()
  • 配置更改:屏幕旋转等配置更改会导致Activity重建
  • 内存管理:在onStop()或onDestroy()中释放资源,避免内存泄漏
  • 耗时操作:避免在onPause()中执行耗时操作,以免延迟下一个Activity启动

3. 现代开发最佳实践

  • 使用ViewModel管理UI相关数据,避免在生命周期方法中处理复杂逻辑
  • 使用Lifecycle-aware组件自动管理资源释放
  • 采用Activity Result API替代startActivityForResult()
  • 使用OnBackPressedDispatcher处理返回逻辑

4. 常见问题解答

  • Q: 为什么A的onStop()在B的onResume()之后调用?
    A: 这是为了确保用户界面平滑过渡,先让新Activity完全显示,再停止旧Activity。

  • Q: 什么情况下不会调用onDestroy()?
    A: 当进程被系统强制终止时,onDestroy()可能不会被调用。

  • Q: 如何保证数据在跳转过程中不丢失?
    A: 使用ViewModel + SavedStateHandle或onSaveInstanceState()保存临时状态。

5. 一句话总结

Activity跳转遵循"先暂停旧Activity,再启动新Activity"的原则,返回时按相反顺序恢复,理解这一流程有助于合理管理资源和优化用户体验。

六十八、如何拦截短信?

(一)Android短信拦截机制演进

1. 短信接收广播的历史变化

<!-- Android 4.4之前:所有应用都可以拦截 -->
<receiver android:name=".SmsReceiver">
    <intent-filter android:priority="999">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

<!-- Android 4.4之后:需要默认短信应用权限 -->
<receiver android:name=".SmsReceiver"
    android:exported="true"
    android:permission="android.permission.BROADCAST_SMS">
    <intent-filter android:priority="999">
        <action android:name="android.provider.Telephony.SMS_DELIVER" />
    </intent-filter>
</receiver>

(二)现代Android短信拦截方案

1. 方案一:成为默认短信应用(Android 4.4+ 唯一可行方案)

// 1. 检查当前是否为默认短信应用
fun isDefaultSmsApp(context: Context): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        val defaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(context)
        defaultSmsPackage == context.packageName
    } else {
        true // Android 4.4以下所有应用都可以拦截
    }
}

// 2. 请求用户设置为默认短信应用
fun requestDefaultSmsAppPermission(activity: Activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT).apply {
            putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, activity.packageName)
        }
        activity.startActivity(intent)
    }
}

// 3. 广播接收器实现(需要高优先级和默认应用权限)
class SmsReceiver : BroadcastReceiver() {
    
    override fun onReceive(context: Context, intent: Intent) {
        // 检查是否是默认短信应用(Android 4.4+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (!isDefaultSmsApp(context)) {
                return // 不是默认应用,无法拦截
            }
        }
        
        when (intent.action) {
            Telephony.Sms.Intents.SMS_DELIVER_ACTION -> {
                // Android 4.4+ 的广播
                handleIncomingSms(intent, true)
            }
            Telephony.Sms.Intents.SMS_RECEIVED_ACTION -> {
                // Android 4.4- 的广播
                handleIncomingSms(intent, false)
            }
        }
    }
    
    private fun handleIncomingSms(intent: Intent, canAbort: Boolean) {
        val smsMessages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
        if (smsMessages.isNullOrEmpty()) return
        
        for (sms in smsMessages) {
            val sender = sms.displayOriginatingAddress
            val body = sms.messageBody
            val timestamp = sms.timestampMillis
            
            Log.d("SmsReceiver", "收到短信: $sender - $body")
            
            // 判断是否需要拦截
            if (shouldIntercept(sender, body)) {
                if (canAbort) {
                    abortBroadcast() // 阻止短信传递给其他应用
                    saveSmsToDatabase(sender, body, timestamp)
                    
                    // 发送通知告知用户已拦截
                    sendInterceptNotification(context, sender, body)
                }
                // Android 4.4以下,非默认应用也可以拦截
                else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                    abortBroadcast()
                    saveSmsToDatabase(sender, body, timestamp)
                }
            }
        }
    }
    
    private fun shouldIntercept(sender: String, body: String): Boolean {
        // 实现拦截逻辑,例如:
        // 1. 黑名单拦截
        // 2. 关键词过滤
        // 3. 正则表达式匹配
        val blacklist = listOf("1069", "10086", "95588")
        val keywords = listOf("诈骗", "中奖", "贷款", "赌场")
        
        return blacklist.any { sender.contains(it) } ||
               keywords.any { body.contains(it) }
    }
}

2. 方案二:短信内容提供器(SMS Content Provider)读取

// 读取已接收的短信(需要READ_SMS权限)
object SmsReader {
    
    fun readSms(context: Context): List<SmsMessage> {
        val messages = mutableListOf<SmsMessage>()
        
        val uri = Uri.parse("content://sms/inbox")
        val projection = arrayOf(
            "_id", "address", "body", "date", "type"
        )
        
        val cursor = context.contentResolver.query(
            uri,
            projection,
            null,
            null,
            "date DESC LIMIT 20"
        )
        
        cursor?.use {
            val idIndex = it.getColumnIndex("_id")
            val addressIndex = it.getColumnIndex("address")
            val bodyIndex = it.getColumnIndex("body")
            val dateIndex = it.getColumnIndex("date")
            
            while (it.moveToNext()) {
                val message = SmsMessage(
                    id = it.getLong(idIndex),
                    sender = it.getString(addressIndex),
                    content = it.getString(bodyIndex),
                    timestamp = it.getLong(dateIndex)
                )
                messages.add(message)
            }
        }
        
        return messages
    }
    
    data class SmsMessage(
        val id: Long,
        val sender: String,
        val content: String,
        val timestamp: Long
    )
}

3. 方案三:使用SMS Retriever API(官方推荐,用于验证码)

// Google推荐的短信验证码自动读取方案
class SmsRetrieverHelper {
    
    // 1. 启动短信监听(监听5分钟)
    fun startSmsRetriever(context: Context) {
        val client = SmsRetriever.getClient(context)
        
        val task = client.startSmsRetriever()
        task.addOnSuccessListener {
            Log.d("SmsRetriever", "短信监听已启动")
            // 监听5分钟,用于接收包含应用哈希的短信
        }
        task.addOnFailureListener { e ->
            Log.e("SmsRetriever", "启动失败", e)
        }
    }
    
    // 2. 广播接收器接收短信
    class SmsRetrieverReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
                val extras = intent.extras
                val status = extras?.get(SmsRetriever.EXTRA_STATUS) as Status?
                
                when (status?.statusCode) {
                    CommonStatusCodes.SUCCESS -> {
                        // 获取短信内容
                        val message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
                        Log.d("SmsRetriever", "收到短信: $message")
                        
                        // 提取验证码
                        val otp = extractOtp(message)
                        otp?.let { sendOtpToApp(context, it) }
                    }
                    CommonStatusCodes.TIMEOUT -> {
                        Log.d("SmsRetriever", "监听超时")
                    }
                }
            }
        }
        
        private fun extractOtp(message: String?): String? {
            return message?.let {
                // 提取6位数字验证码
                val pattern = Regex("\\d{6}")
                pattern.find(it)?.value
            }
        }
    }
}

(三)权限声明与动态请求

1. AndroidManifest.xml权限配置

<!-- 必须声明的权限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />

<!-- Android 4.4+ 默认短信应用需要声明 -->
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.BROADCAST_SMS" />

<!-- 广播接收器声明 -->
<receiver
    android:name=".SmsReceiver"
    android:exported="true"
    android:permission="android.permission.BROADCAST_SMS">
    <intent-filter android:priority="2147483647"> <!-- 最高优先级 -->
        <!-- Android 4.4+ -->
        <action android:name="android.provider.Telephony.SMS_DELIVER" />
        <!-- Android 4.4以下 -->
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

<!-- SMS Retriever广播接收器 -->
<receiver
    android:name=".SmsRetrieverReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
    </intent-filter>
</receiver>

2. 动态权限请求(Android 6.0+)

// 权限请求管理
class SmsPermissionManager(private val activity: Activity) {
    
    companion object {
        private const val REQUEST_SMS_PERMISSION = 100
        private val REQUIRED_PERMISSIONS = arrayOf(
            Manifest.permission.RECEIVE_SMS,
            Manifest.permission.READ_SMS
        )
    }
    
    fun checkAndRequestPermissions(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val deniedPermissions = REQUIRED_PERMISSIONS.filter {
                activity.checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED
            }
            
            if (deniedPermissions.isNotEmpty()) {
                activity.requestPermissions(
                    deniedPermissions.toTypedArray(),
                    REQUEST_SMS_PERMISSION
                )
                false
            } else {
                true
            }
        } else {
            true // Android 6.0以下不需要动态请求
        }
    }
    
    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ): Boolean {
        if (requestCode == REQUEST_SMS_PERMISSION) {
            val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
            
            if (!allGranted) {
                // 有权限被拒绝,向用户解释为什么需要这些权限
                showPermissionRationale()
            }
            
            return allGranted
        }
        return false
    }
    
    private fun showPermissionRationale() {
        AlertDialog.Builder(activity)
            .setTitle("需要短信权限")
            .setMessage("拦截垃圾短信需要读取短信的权限")
            .setPositiveButton("去设置") { _, _ ->
                openAppSettings()
            }
            .setNegativeButton("取消", null)
            .show()
    }
    
    private fun openAppSettings() {
        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
            data = Uri.fromParts("package", activity.packageName, null)
        }
        activity.startActivity(intent)
    }
}

(四)Android版本兼容性处理

1. 不同Android版本的策略

object SmsInterceptionStrategy {
    
    fun getCurrentStrategy(): String {
        return when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> {
                """
                Android 4.4+ 策略:
                1. 必须成为默认短信应用才能拦截
                2. 使用 SMS_DELIVER 广播
                3. 需要 BROADCAST_SMS 权限
                4. 可以调用 abortBroadcast() 阻止短信传递
                """.trimIndent()
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH -> {
                """
                Android 4.0-4.3 策略:
                1. 任何应用都可以注册短信广播接收器
                2. 使用 SMS_RECEIVED 广播
                3. 可以调用 abortBroadcast() 拦截短信
                4. 没有默认应用限制
                """.trimIndent()
            }
            else -> {
                """
                Android 4.0以下策略:
                1. 权限要求较宽松
                2. 可以使用短信广播
                3. 但现代应用已不需要支持
                """.trimIndent()
            }
        }
    }
    
    // 获取正确的广播Action
    fun getSmsAction(): String {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Telephony.Sms.Intents.SMS_DELIVER_ACTION
        } else {
            Telephony.Sms.Intents.SMS_RECEIVED_ACTION
        }
    }
}

2. 处理Android 8.0+的广播限制

// Android 8.0+需要动态注册广播接收器
class SmsInterceptorService : Service() {
    
    private lateinit var smsReceiver: SmsReceiver
    
    override fun onCreate() {
        super.onCreate()
        
        // 动态注册广播接收器(Android 8.0+推荐)
        smsReceiver = SmsReceiver()
        val filter = IntentFilter().apply {
            priority = IntentFilter.SYSTEM_HIGH_PRIORITY
            addAction(SmsInterceptionStrategy.getSmsAction())
        }
        
        registerReceiver(smsReceiver, filter)
        
        // 启动前台服务(Android 8.0+要求)
        startForegroundService()
    }
    
    private fun startForegroundService() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "sms_interceptor",
                "短信拦截",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "用于拦截垃圾短信"
            }
            
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
            
            val notification = Notification.Builder(this, "sms_interceptor")
                .setContentTitle("短信拦截服务运行中")
                .setContentText("正在保护您免受垃圾短信骚扰")
                .setSmallIcon(R.drawable.ic_notification)
                .build()
            
            startForeground(1, notification)
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(smsReceiver)
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

(五)实际应用场景与实现

1. 完整的短信拦截器实现

// 主拦截逻辑
class AdvancedSmsInterceptor {
    
    // 拦截规则配置
    data class InterceptRule(
        val ruleType: RuleType,
        val pattern: String,
        val isRegex: Boolean = false
    ) {
        enum class RuleType {
            SENDER, CONTENT, BOTH
        }
    }
    
    private val rules = mutableListOf<InterceptRule>()
    
    fun addRule(rule: InterceptRule) {
        rules.add(rule)
    }
    
    fun shouldIntercept(sender: String?, content: String?): Boolean {
        if (sender == null && content == null) return false
        
        return rules.any { rule ->
            when (rule.ruleType) {
                InterceptRule.RuleType.SENDER -> {
                    sender?.let { matchesRule(it, rule) } ?: false
                }
                InterceptRule.RuleType.CONTENT -> {
                    content?.let { matchesRule(it, rule) } ?: false
                }
                InterceptRule.RuleType.BOTH -> {
                    (sender?.let { matchesRule(it, rule) } ?: false) &&
                    (content?.let { matchesRule(it, rule) } ?: false)
                }
            }
        }
    }
    
    private fun matchesRule(text: String, rule: InterceptRule): Boolean {
        return if (rule.isRegex) {
            Regex(rule.pattern).containsMatchIn(text)
        } else {
            text.contains(rule.pattern, ignoreCase = true)
        }
    }
    
    // 处理拦截的短信
    fun processInterceptedSms(
        context: Context,
        sender: String,
        content: String,
        timestamp: Long
    ) {
        // 1. 保存到数据库
        saveToDatabase(context, sender, content, timestamp)
        
        // 2. 发送通知
        sendNotification(context, sender, content)
        
        // 3. 可选:转发到指定号码(如客服)
        if (shouldForwardToService()) {
            forwardToServiceNumber(context, sender, content)
        }
    }
    
    private fun saveToDatabase(
        context: Context,
        sender: String,
        content: String,
        timestamp: Long
    ) {
        // 使用Room数据库保存拦截记录
        val record = InterceptedSms(
            sender = sender,
            content = content,
            timestamp = timestamp,
            ruleMatched = getMatchedRule(sender, content)
        )
        
        // database.interceptedSmsDao().insert(record)
    }
    
    private fun sendNotification(context: Context, sender: String, content: String) {
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
                as NotificationManager
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "intercepted_sms",
                "拦截的短信",
                NotificationManager.IMPORTANCE_HIGH
            ).apply {
                description = "被拦截的垃圾短信通知"
            }
            notificationManager.createNotificationChannel(channel)
        }
        
        val notification = NotificationCompat.Builder(context, "intercepted_sms")
            .setContentTitle("拦截到垃圾短信")
            .setContentText("来自: $sender")
            .setStyle(NotificationCompat.BigTextStyle().bigText(content))
            .setSmallIcon(R.drawable.ic_shield)
            .setAutoCancel(true)
            .build()
        
        notificationManager.notify(NotificationIdGenerator.getNextId(), notification)
    }
}

2. 用户界面:短信拦截规则管理

// 规则管理Activity
class SmsRuleActivity : AppCompatActivity() {
    
    private lateinit var ruleAdapter: RuleAdapter
    private val rules = mutableListOf<AdvancedSmsInterceptor.InterceptRule>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sms_rules)
        
        setupRecyclerView()
        loadRules()
        
        // 添加规则按钮
        findViewById<Button>(R.id.btn_add_rule).setOnClickListener {
            showAddRuleDialog()
        }
    }
    
    private fun showAddRuleDialog() {
        val dialog = AlertDialog.Builder(this)
            .setTitle("添加拦截规则")
            .setView(R.layout.dialog_add_rule)
            .setPositiveButton("添加") { dialog, _ ->
                val ruleType = when {
                    checkSenderRadio.isChecked -> 
                        AdvancedSmsInterceptor.InterceptRule.RuleType.SENDER
                    checkContentRadio.isChecked -> 
                        AdvancedSmsInterceptor.InterceptRule.RuleType.CONTENT
                    else -> 
                        AdvancedSmsInterceptor.InterceptRule.RuleType.BOTH
                }
                
                val pattern = editPattern.text.toString()
                val isRegex = checkRegex.isChecked
                
                if (pattern.isNotBlank()) {
                    val rule = AdvancedSmsInterceptor.InterceptRule(
                        ruleType = ruleType,
                        pattern = pattern,
                        isRegex = isRegex
                    )
                    
                    rules.add(rule)
                    ruleAdapter.notifyDataSetChanged()
                    saveRule(rule)
                }
                
                dialog.dismiss()
            }
            .setNegativeButton("取消", null)
            .create()
        
        dialog.show()
    }
}

(六)安全与隐私考虑

1. 隐私政策要求

// 必须向用户明确说明短信权限的使用
object PrivacyPolicy {
    
    fun showSmsPermissionExplanation(context: Context, callback: (accepted: Boolean) -> Unit) {
        AlertDialog.Builder(context)
            .setTitle("短信权限说明")
            .setMessage("""
                我们需要短信权限来:
                
                1. 拦截垃圾短信和诈骗短信
                2. 防止恶意短信干扰您的正常使用
                3. 保护您的隐私和安全
                
                我们承诺:
                - 不会上传您的短信内容到服务器
                - 不会将短信用于除拦截外的其他目的
                - 所有短信数据仅存储在您的设备上
                
                您可以随时在设置中关闭此功能。
            """.trimIndent())
            .setPositiveButton("同意并继续") { _, _ ->
                callback(true)
            }
            .setNegativeButton("拒绝") { _, _ ->
                callback(false)
            }
            .setCancelable(false)
            .show()
    }
}

2. 数据安全处理

// 敏感数据加密存储
object SecureSmsStorage {
    
    private val masterKey: MasterKey by lazy {
        MasterKey.Builder(context)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build()
    }
    
    private val sharedPreferences by lazy {
        EncryptedSharedPreferences.create(
            context,
            "intercepted_sms",
            masterKey,
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        )
    }
    
    fun saveSmsSecurely(sms: InterceptedSms) {
        val encryptedContent = encrypt(sms.content)
        
        sharedPreferences.edit()
            .putString("sms_${sms.timestamp}", encryptedContent)
            .apply()
    }
    
    private fun encrypt(text: String): String {
        // 使用AndroidX Security Crypto进行加密
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        cipher.init(Cipher.ENCRYPT_MODE, masterKey)
        
        val encryptedBytes = cipher.doFinal(text.toByteArray())
        return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
    }
}

(七)现代替代方案与最佳实践

1. 使用系统提供的智能短信分类(Android 9.0+)

// Android 9.0+ 系统提供了短信分类功能
object SystemSmsClassifier {
    
    fun categorizeMessage(context: Context, message: String): String? {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // 使用系统的短信分类器
            val classifier = context.getSystemService(TelephonyManager::class.java)
                ?.createForSubscriptionId(subscriptionId)
            
            // 注意:需要成为默认短信应用才能使用
            val classificationResult = classifier?.classify(message)
            classificationResult?.firstOrNull()
        } else {
            null
        }
    }
}

2. 云端短信分析(隐私友好方式)

// 使用哈希值进行云端垃圾短信检测
object CloudSmsAnalysis {
    
    suspend fun analyzeSmsHash(senderHash: String, contentHash: String): Boolean {
        return try {
            // 不发送原始短信内容,只发送哈希值
            val response = apiClient.post<AnalysisResponse>(
                "/sms/analyze",
                body = AnalysisRequest(
                    senderHash = senderHash,
                    contentHash = contentHash,
                    timestamp = System.currentTimeMillis()
                )
            )
            
            response.isSpam
        } catch (e: Exception) {
            false // 网络错误时默认不拦截
        }
    }
    
    private fun generateHash(text: String): String {
        // 使用SHA-256生成哈希值,保护隐私
        val digest = MessageDigest.getInstance("SHA-256")
        val hash = digest.digest(text.toByteArray())
        return Base64.encodeToString(hash, Base64.NO_WRAP)
    }
}

(八)面试回答要点总结

1. 核心实现方案

  • Android 4.4以下:注册高优先级SMS_RECEIVED广播接收器,调用abortBroadcast()拦截
  • Android 4.4+:必须成为默认短信应用,使用SMS_DELIVER广播,需要BROADCAST_SMS权限
  • 现代方案:优先使用SMS Retriever API(仅限验证码),避免敏感权限

2. 权限要求

  • RECEIVE_SMS:接收短信权限(必须)
  • READ_SMS:读取短信权限(可选)
  • BROADCAST_SMS:广播短信权限(Android 4.4+默认应用需要)
  • Android 6.0+:需要动态请求权限

3. 版本兼容性关键点

  • Android 4.4:引入默认短信应用限制
  • Android 8.0:建议动态注册广播接收器
  • Android 10:后台启动限制加强
  • Android 11:权限管理更严格,需要详细说明用途

4. 隐私与安全最佳实践

  • 明确告知用户权限使用目的
  • 敏感数据本地加密存储
  • 避免上传原始短信内容
  • 提供便捷的关闭和清理功能
  • 遵循Google Play政策关于短信权限的使用

5. 现代替代方案

  • 验证码读取:使用SMS Retriever API,无需敏感权限
  • 垃圾短信过滤:利用系统分类器(Android 9.0+)
  • 云端分析:使用哈希值进行隐私友好的分析

6. 一句话总结

短信拦截在Android 4.4+变得严格,必须成为默认短信应用并请求相应权限,开发时应优先考虑用户隐私,使用最小必要权限,并探索现代替代方案如SMS Retriever API。

六十九、LocalBroadcast与全局广播的区别?

(一)核心概念对比

1. 全局广播(Global Broadcast)

// 全局广播发送示例
fun sendGlobalBroadcast(context: Context) {
    val intent = Intent("com.example.ACTION_GLOBAL")
    intent.putExtra("data", "全局广播数据")
    
    // 发送到系统,所有应用都可能接收到
    context.sendBroadcast(intent)
    
    // 带权限的发送
    context.sendBroadcast(intent, "com.example.PERMISSION")
    
    // 有序广播
    context.sendOrderedBroadcast(intent, null)
}

// 全局广播接收器
class GlobalReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        val data = intent.getStringExtra("data")
        Log.d("GlobalReceiver", "收到全局广播: $action, 数据: $data")
    }
}

2. 本地广播(Local Broadcast)

// LocalBroadcastManager使用示例(已过时)
@Deprecated("LocalBroadcastManager已过时,仅作演示")
fun sendLocalBroadcast(context: Context) {
    val intent = Intent("com.example.ACTION_LOCAL")
    intent.putExtra("data", "本地广播数据")
    
    // 通过LocalBroadcastManager发送,仅应用内可见
    LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
}

// 本地广播接收器注册
@Deprecated("LocalBroadcastManager已过时,仅作演示")
fun registerLocalReceiver(context: Context, receiver: BroadcastReceiver) {
    val filter = IntentFilter("com.example.ACTION_LOCAL")
    LocalBroadcastManager.getInstance(context)
        .registerReceiver(receiver, filter)
}

(二)详细特性对比表

特性 全局广播(Global Broadcast) 本地广播(Local Broadcast)
作用范围 跨进程,系统级 应用内,进程内
安全性 需权限控制,可能被恶意应用接收 仅应用内,安全
性能 通过系统IPC,开销较大 基于Handler,性能高
注册方式 静态(AndroidManifest)或动态注册 只能动态注册
生命周期 需要手动管理,可能内存泄漏 生命周期自动关联(已过时)
系统开销 需要Binder通信,系统广播队列 纯应用内通信,无系统开销
有序广播 支持有序广播(sendOrderedBroadcast) 不支持有序广播
粘性广播 Android 5.0+已废弃 不支持
现代状态 仍在使用,但有严格限制 已过时(LocalBroadcastManager)

(三)底层实现原理

1. 全局广播实现机制

// 全局广播系统架构(简化)
object GlobalBroadcastSystem {
    /*
    全局广播流程:
    发送方App → Binder IPC → ActivityManagerService (AMS) → 
    AMS查找匹配接收器 → 通过Binder IPC → 接收方App进程 →
    接收方App的Receiver执行
    
    关键组件:
    1. ActivityManagerService:中央调度器
    2. BroadcastQueue:广播队列
    3. Binder:进程间通信
    4. IntentResolver:Intent匹配解析
    */
}

2. LocalBroadcastManager实现原理(已过时)

// LocalBroadcastManager内部实现(简化)
@Deprecated("内部实现分析,仅用于理解")
class LocalBroadcastManager internal constructor(context: Context) {
    
    // 核心数据结构
    private val receivers = HashMap<BroadcastReceiver, ArrayList<IntentFilter>>()
    private val actions = HashMap<String, ArrayList<ReceiverRecord>>()
    
    // 使用Handler在主线程处理
    private val handler = Handler(context.mainLooper)
    
    // 发送广播的核心方法
    fun sendBroadcast(intent: Intent): Boolean {
        synchronized(this) {
            // 1. 匹配接收器
            val entries = ArrayList<ReceiverRecord>()
            val action = intent.action
            
            // 查找匹配的接收器
            actions[action]?.let { entries.addAll(it) }
            
            // 2. 添加到待执行队列
            val receivers = ArrayList<ReceiverRecord>()
            for (entry in entries) {
                if (entry.filter.match(
                    intent.action,
                    intent.type,
                    intent.scheme,
                    intent.data,
                    intent.categories,
                    "LocalBroadcast"
                )) {
                    receivers.add(entry)
                }
            }
            
            if (receivers.isEmpty()) return false
            
            // 3. 通过Handler在主线程执行
            handler.post {
                for (receiver in receivers) {
                    receiver.receiver.onReceive(context, intent)
                }
            }
            
            return true
        }
    }
}

(四)现代替代方案

1. LiveData / StateFlow(官方推荐)

// 使用ViewModel + LiveData替代广播
class EventViewModel : ViewModel() {
    
    // 单次事件(替代广播)
    private val _singleEvent = MutableLiveData<Event<String>>()
    val singleEvent: LiveData<Event<String>> = _singleEvent
    
    // 状态数据(替代数据传递)
    private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    fun sendEvent(message: String) {
        _singleEvent.value = Event(message)
    }
    
    fun updateState(newState: UiState) {
        _uiState.value = newState
    }
    
    // 封装单次事件,防止重复消费
    class Event<out T>(private val content: T) {
        var hasBeenHandled = false
            private set
        
        fun getContentIfNotHandled(): T? {
            return if (hasBeenHandled) {
                null
            } else {
                hasBeenHandled = true
                content
            }
        }
    }
}

// 在Activity/Fragment中观察
class MainActivity : AppCompatActivity() {
    private val viewModel: EventViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 观察单次事件
        viewModel.singleEvent.observe(this) { event ->
            event.getContentIfNotHandled()?.let { message ->
                showToast(message)
            }
        }
        
        // 观察状态
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    updateUI(state)
                }
            }
        }
    }
}

2. Kotlin Flow / Channel(协程通信)

// 使用Flow/Channel进行应用内通信
object EventBusFlow {
    
    // 事件密封类
    sealed class AppEvent {
        data class UserLoggedIn(val userId: String) : AppEvent()
        data class DataUpdated(val data: List<String>) : AppEvent()
        object NetworkStateChanged : AppEvent()
    }
    
    // 使用SharedFlow广播事件
    private val _events = MutableSharedFlow<AppEvent>(
        replay = 0,
        extraBufferCapacity = 64
    )
    val events: SharedFlow<AppEvent> = _events.asSharedFlow()
    
    // 发送事件
    suspend fun sendEvent(event: AppEvent) {
        _events.emit(event)
    }
    
    // 发送事件(非挂起版本)
    fun sendEventNonBlocking(event: AppEvent) {
        viewModelScope.launch {
            _events.emit(event)
        }
    }
}

// 使用Channel进行一对一通信
class CommunicationChannel {
    
    // 请求-响应模式
    private val requestChannel = Channel<String>()
    private val responseChannel = Channel<String>()
    
    suspend fun sendRequest(request: String): String {
        requestChannel.send(request)
        return responseChannel.receive()
    }
    
    suspend fun processRequests() {
        for (request in requestChannel) {
            val response = handleRequest(request)
            responseChannel.send(response)
        }
    }
}

3. 基于观察者模式的EventBus

// 轻量级EventBus实现
object ModernEventBus {
    
    // 使用ConcurrentHashMap保证线程安全
    private val subscriptions = ConcurrentHashMap<Class<*>, MutableList<(Any) -> Unit>>()
    
    // 订阅事件
    fun <T : Any> subscribe(eventType: Class<T>, subscriber: (T) -> Unit) {
        val list = subscriptions.getOrPut(eventType) { mutableListOf() }
        list.add(subscriber as (Any) -> Unit)
    }
    
    // 取消订阅
    fun <T : Any> unsubscribe(eventType: Class<T>, subscriber: (T) -> Unit) {
        subscriptions[eventType]?.remove(subscriber as (Any) -> Unit)
    }
    
    // 发布事件
    fun publish(event: Any) {
        val eventType = event::class.java
        subscriptions[eventType]?.forEach { subscriber ->
            try {
                subscriber(event)
            } catch (e: Exception) {
                Log.e("EventBus", "事件处理异常", e)
            }
        }
    }
    
    // 使用示例
    data class UserEvent(val userId: String, val action: String)
    
    fun usageExample() {
        // 订阅
        ModernEventBus.subscribe(UserEvent::class.java) { event ->
            println("用户事件: ${event.userId}, ${event.action}")
        }
        
        // 发布
        ModernEventBus.publish(UserEvent("123", "login"))
    }
}

(五)实际应用场景

1. 需要全局广播的场景

// 适合使用全局广播的场景
object GlobalBroadcastUseCases {
    
    // 场景1:系统事件监听
    fun listenSystemEvents(context: Context) {
        val filter = IntentFilter().apply {
            addAction(Intent.ACTION_BOOT_COMPLETED)
            addAction(Intent.ACTION_POWER_CONNECTED)
            addAction(Intent.ACTION_TIME_CHANGED)
        }
        
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                when (intent.action) {
                    Intent.ACTION_BOOT_COMPLETED -> {
                        // 开机启动
                        startBackgroundService()
                    }
                    Intent.ACTION_POWER_CONNECTED -> {
                        // 电源连接
                        scheduleBackup()
                    }
                }
            }
        }
        
        context.registerReceiver(receiver, filter)
    }
    
    // 场景2:跨应用通信(需要权限控制)
    fun sendToOtherApp(context: Context) {
        val intent = Intent("com.other.app.ACTION_REFRESH")
        intent.setPackage("com.other.app") // 指定目标应用
        
        // 带权限发送,确保只有授权的应用能接收
        context.sendBroadcast(
            intent,
            "com.example.PERMISSION_PRIVATE"
        )
    }
    
    // 场景3:有序广播处理
    fun sendOrderedBroadcastExample(context: Context) {
        val intent = Intent("com.example.ORDERED_ACTION")
        
        context.sendOrderedBroadcast(
            intent,
            "com.example.PERMISSION",
            object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    // 最终接收者
                    Log.d("Ordered", "所有接收者处理完成")
                }
            },
            null, // scheduler
            Activity.RESULT_OK,
            null, // initialData
            null  // initialExtras
        )
    }
}

2. 应用内通信的现代方案

// 现代应用内通信架构
class AppCommunicationArchitecture {
    
    // 方案1:单一数据源 + 状态管理
    data class AppState(
        val user: User? = null,
        val theme: Theme = Theme.LIGHT,
        val notifications: List<Notification> = emptyList(),
        val networkState: NetworkState = NetworkState.CONNECTED
    )
    
    // 使用StateFlow管理全局状态
    class AppStateManager {
        private val _state = MutableStateFlow(AppState())
        val state: StateFlow<AppState> = _state.asStateFlow()
        
        fun updateUser(user: User) {
            _state.update { it.copy(user = user) }
        }
        
        fun updateTheme(theme: Theme) {
            _state.update { it.copy(theme = theme) }
        }
    }
    
    // 方案2:事件总线 + 生命周期感知
    class LifecycleAwareEventBus {
        
        private val eventFlow = MutableSharedFlow<AppEvent>()
        
        // 自动取消订阅的扩展函数
        fun <T> SharedFlow<T>.observeWithLifecycle(
            lifecycleOwner: LifecycleOwner,
            minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
            action: (T) -> Unit
        ) {
            lifecycleOwner.lifecycleScope.launch {
                lifecycleOwner.repeatOnLifecycle(minActiveState) {
                    collect { action(it) }
                }
            }
        }
        
        // 发送事件
        suspend fun send(event: AppEvent) {
            eventFlow.emit(event)
        }
        
        // 观察事件(自动取消)
        fun observeEvents(
            lifecycleOwner: LifecycleOwner,
            onEvent: (AppEvent) -> Unit
        ) {
            eventFlow.observeWithLifecycle(lifecycleOwner) { onEvent(it) }
        }
    }
    
    // 方案3:使用Navigation传递数据
    fun navigateWithData() {
        val action = HomeFragmentDirections.actionHomeToDetail(
            itemId = "123",
            itemName = "示例项目"
        )
        findNavController().navigate(action)
    }
}

(六)性能与安全考量

1. 广播的性能问题

object BroadcastPerformance {
    
    // 全局广播的性能开销
    fun measureBroadcastCost(context: Context) {
        // ❌ 避免频繁发送全局广播
        for (i in 1..100) {
            context.sendBroadcast(Intent("ACTION_UPDATE_$i"))
            // 每次广播都涉及Binder IPC和系统调度,开销大
        }
        
        // ✅ 优化方案:批量更新或使用其他机制
        viewModel.data.observe(this) { data ->
            // 使用LiveData,只有数据变化时更新
            updateUI(data)
        }
    }
    
    // 内存泄漏风险
    class LeakyActivity : AppCompatActivity() {
        private val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                updateUI()
            }
        }
        
        override fun onStart() {
            super.onStart()
            // 注册广播
            registerReceiver(receiver, IntentFilter("SOME_ACTION"))
        }
        
        // ❌ 忘记注销会导致内存泄漏
        // override fun onStop() {
        //     super.onStop()
        //     unregisterReceiver(receiver) // 必须注销
        // }
    }
}

2. 安全最佳实践

object BroadcastSecurity {
    
    // 1. 权限保护
    fun sendSecureBroadcast(context: Context) {
        val intent = Intent("com.example.SECURE_ACTION")
        
        // 发送时指定权限
        context.sendBroadcast(intent, "com.example.PRIVATE_PERMISSION")
        
        // 接收方需要声明相同权限
        /*
        <uses-permission android:name="com.example.PRIVATE_PERMISSION" />
        
        <receiver 
            android:name=".SecureReceiver"
            android:permission="com.example.PRIVATE_PERMISSION">
            <intent-filter>
                <action android:name="com.example.SECURE_ACTION" />
            </intent-filter>
        </receiver>
        */
    }
    
    // 2. 限制广播接收器导出
    fun configureSecureReceiver() {
        /*
        AndroidManifest.xml配置:
        
        <receiver
            android:name=".InternalReceiver"
            android:exported="false"> <!-- 不导出,仅应用内可用 -->
            <intent-filter>
                <action android:name="com.example.INTERNAL_ACTION" />
            </intent-filter>
        </receiver>
        */
    }
    
    // 3. 数据验证
    class ValidatingReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            // 验证发送者
            val callingPackage = callingPackage
            if (callingPackage != "trusted.package") {
                return // 拒绝不可信来源
            }
            
            // 验证数据
            val data = intent.getStringExtra("data")
            if (data.isNullOrEmpty() || data.length > 1000) {
                return // 数据格式校验
            }
            
            // 处理逻辑
            processData(data)
        }
    }
}

(七)兼容性与迁移策略

1. LocalBroadcastManager迁移指南

// 从LocalBroadcastManager迁移到现代方案
object MigrationFromLocalBroadcast {
    
    // 案例1:简单的数据通知
    @Deprecated("旧方式:使用LocalBroadcastManager")
    object OldNotificationSystem {
        fun notifyDataChanged(context: Context) {
            LocalBroadcastManager.getInstance(context)
                .sendBroadcast(Intent("DATA_CHANGED"))
        }
    }
    
    // ✅ 新方式:使用LiveData/StateFlow
    object NewNotificationSystem {
        private val _dataChanged = MutableLiveData<Unit>()
        val dataChanged: LiveData<Unit> = _dataChanged
        
        fun notifyDataChanged() {
            _dataChanged.value = Unit
        }
    }
    
    // 案例2:带数据的通知
    @Deprecated("旧方式")
    object OldDataBus {
        fun sendUserUpdated(context: Context, user: User) {
            val intent = Intent("USER_UPDATED").apply {
                putExtra("user", user)
            }
            LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
        }
    }
    
    // ✅ 新方式:使用SharedFlow
    object NewEventBus {
        private val _userEvents = MutableSharedFlow<User>()
        val userEvents: SharedFlow<User> = _userEvents.asSharedFlow()
        
        suspend fun sendUserUpdated(user: User) {
            _userEvents.emit(user)
        }
    }
    
    // 迁移步骤
    fun migrationSteps(): List<String> {
        return listOf(
            "1. 分析现有LocalBroadcastManager的使用场景",
            "2. 根据场景选择合适的替代方案:",
            "   - 简单状态通知 → LiveData",
            "   - 复杂事件流 → SharedFlow/StateFlow",
            "   - 组件间通信 → ViewModel + LiveData",
            "   - UI事件 → 回调接口或Navigation",
            "3. 逐步替换,保持向后兼容",
            "4. 移除LocalBroadcastManager依赖"
        )
    }
}

2. 依赖库替代方案

// build.gradle.kts 依赖配置
dependencies {
    // ❌ 不再需要
    // implementation "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0"
    
    // ✅ 现代替代方案依赖
    
    // LiveData
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
    
    // Kotlin Coroutines + Flow
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
    
    // 事件总线库(可选)
    implementation("com.github.kirich1409:eventbus:1.0.0")
    
    // RxJava(如果需要响应式编程)
    implementation("io.reactivex.rxjava3:rxjava:3.1.7")
    implementation("io.reactivex.rxjava3:rxandroid:3.0.2")
}

(八)面试回答要点总结

1. 核心区别总结

  • 作用范围
    • 全局广播:跨进程,系统级通信
    • 本地广播:应用内,进程内通信(已过时)
  • 实现机制
    • 全局广播:通过AMS + Binder IPC
    • 本地广播:通过Handler + 应用内注册表(LocalBroadcastManager)
  • 性能
    • 全局广播:开销大,涉及系统调用
    • 本地广播:性能好,纯应用内操作
  • 安全性
    • 全局广播:需要权限控制,可能泄露数据
    • 本地广播:安全,仅应用内可见

2. 现代替代方案

  • LiveData:状态管理,生命周期感知,适合UI状态更新
  • StateFlow/SharedFlow:响应式数据流,支持复杂事件流
  • ViewModel:跨配置更改的数据持有者,组件间通信
  • 回调接口:直接的组件间通信
  • Navigation组件:Fragment间安全传递数据

3. 使用场景指南

  • 使用全局广播
    • 监听系统事件(开机、网络变化等)
    • 跨应用通信(需严格控制权限)
    • 发送有序广播需要特定处理顺序时
  • 避免使用本地广播
    • 新项目不应使用LocalBroadcastManager
    • 老项目应逐步迁移到现代方案
  • 现代应用内通信
    • UI状态更新 → ViewModel + LiveData/StateFlow
    • 事件通知 → SharedFlow/EventBus模式
    • 数据传递 → Navigation arguments或回调

4. 性能与安全最佳实践

  • 避免频繁发送全局广播
  • 及时注销广播接收器,避免内存泄漏
  • 使用权限保护敏感广播
  • 验证广播数据的来源和内容
  • 优先使用应用内通信机制替代全局广播

5. 一句话总结

全局广播用于跨进程系统级通信但开销大,本地广播(LocalBroadcastManager)曾是应用内高效通信方案但已过时,现代Android开发应使用LiveData、StateFlow等响应式组件实现更安全、高效、易维护的应用内通信。

七十、如何选择第三方库?

在Android开发中,合理选择第三方库是项目成功的关键。除了你提到的六个核心标准,还需要特别关注开源协议的法律风险。下图系统梳理了从初步筛选到最终决策的全流程,帮你建立一个清晰的评估框架:

flowchart TD
    A[启动第三方库技术选型] --> B{明确需求与约束}
    
    B --> C[技术可行性预研]
    C --> D[多维标准深入评估]
    
    subgraph D [多维标准深入评估]
        D1[📈 活跃度与维护<br>社区、提交、Issue响应]
        D2[⚙️ 稳定性与采用度<br>版本、大厂背书]
        D3[⚡ 性能与包体积<br>启动耗时、方法数]
        D4[🔗 兼容性<br>Min SDK, Jetpack/Compose]
        D5[📚 文档与上手成本<br>API文档、示例项目]
        D6[⚠️ 法律风险<br>开源协议传染性]
    end
    
    D --> E{综合评估与决策}
    
    E -- 通过 --> F[✅ 引入并制定集成计划]
    E -- 不通过 --> G[🔄 寻找替代方案或自研]
    
    F --> H[📦 完成集成]
    G --> H

🔍 如何深入评估每个维度

在遵循上述流程时,你可以参考以下更具体的评估方法:

  1. 活跃度与维护性

    • 观察提交频率:在GitHub/GitLab上,健康的项目应有定期(如每月)的提交记录,而非长期停滞。
    • 分析Issue和PR:查看近期打开的Issue是否得到维护者响应,合并PR是否活跃。大量未处理的旧Issue可能是危险信号。
    • 检查分支与版本:是否有清晰的发布版本(Releases/Tags)和稳定的主分支,这体现了工程管理的规范性。
  2. 稳定性与采用度

    • 查看版本号:遵循语义化版本控制的项目更可靠。1.0.02.x通常比0.x.x版本更稳定。
    • 考察生产环境使用:除了知名公司是否采用,可以搜索技术论坛、社区,了解该库在真实项目中的长期反馈和遇到的“坑”。
  3. 性能与包体积影响

    • 警惕启动耗时:库是否在Application初始化或主页面加载时执行大量耗时操作?可以尝试在小项目中集成测试启动时间。
    • 计算方法数:使用Android Studio的 APK Analyzerdexcount-gradle-plugin 等工具,精确评估库引入的方法数,这对预防 64K方法数限制至关重要。
    • 评估内存占用:对于图片加载、网络缓存等库,需关注其内存管理策略。
  4. 兼容性

    • 明确最低API要求:确保与项目最低支持版本匹配。
    • 检查AndroidX/Jetpack兼容:现代库应基于AndroidX,并与Jetpack组件(如 LifecycleViewModel)良好协同。
    • 评估未来升级:库是否跟上了最新的系统特性(如深色模式、隐私沙盒)和开发趋势(如Jetpack Compose)?
  5. 文档与上手成本

    • 完整性:好的文档应包括详细的API说明、配置指南和最佳实践。
    • 实用性:提供完整的示例项目(Sample App)能极大降低集成门槛。
    • 社区支持:活跃的社区(如Stack Overflow上的问答、专门的Slack/Discord频道)意味着当你遇到问题时,能更快地找到解决方案。
  6. 法律风险(最易忽视的关键点)
    这是评估中至关重要但常被忽略的一环,主要涉及开源许可证的合规性

    • 核心风险:一些许可证(如 GPL、LGPL)具有“传染性”。如果你的应用使用了此类许可证的库,并与之紧密链接(静态链接或修改后使用),可能被要求将整个应用的源代码开源
    • 如何选择:商业项目应优先选择对商业应用友好的宽松许可证,例如:
许可证类型 主要要求 商业友好度
MIT 在软件副本中包含原许可证和版权声明 非常高
Apache 2.0 需说明修改,并包含原许可证、专利声明等 非常高
BSD 与MIT类似,要求相对简单 非常高
LGPL 动态链接使用通常无传染性;静态链接或修改库代码可能需开源 中等
GPL 只要使用,衍生作品通常必须开源
  • 行动建议:集成前务必仔细阅读库的开源许可证全文。对于GPL/LGPL等协议,如果不确定,建议咨询法务或寻求更宽松协议的替代库。

💡 其他重要考量因素

  • 备选方案与可替换性:评估是否有同等优秀的替代库?避免被一个库“锁死”。设计时,应针对第三方库的功能抽象出接口,将具体实现隔离,这样未来替换库的成本会大大降低。
  • 集成与维护成本:评估库的API设计是否与你的项目架构匹配(如是否支持协程、RxJava)。复杂的配置和非常规的API会增加团队的学习和维护成本。

📝 决策与总结

在综合评估后,你可以问自己几个最终问题:

  1. 这个库解决的痛点是否明确且必要?
  2. 其带来的好处是否远超潜在的性能、维护和法律风险?
  3. 团队是否有能力应对它可能带来的问题?

总的来说,选择第三方库是一个权衡的艺术。没有完美的库,只有最适合当前项目阶段、团队能力和长期目标的库。建立并遵循一个包含技术、社区、法律等多个维度的系统化评估 checklist,能有效规避风险,提升项目工程质量。

如果你正在为某个特定功能(比如网络请求、图片加载或依赖注入)选型,我可以为你提供一些主流库的对比分析。

参考文献