Android第十四次面试总结

news2025/6/8 19:42:30

OkHttp中获取数据与操作数据

 一、数据获取核心机制

1. ​同步请求(阻塞式)​
// 1. 创建HTTP客户端(全局应复用实例)
OkHttpClient client = new OkHttpClient();

// 2. 构建请求对象(GET示例)
Request request = new Request.Builder()
    .url("https://api.example.com/data")  // 必填目标URL
    .header("User-Agent", "MyApp/1.0")    // 添加自定义请求头
    .get()                                // 明确指定GET方法
    .build();

// 3. 执行同步请求(阻塞当前线程)
try (Response response = client.newCall(request).execute()) {
    // 4. 验证响应状态码 (200-299范围表示成功)
    if (response.isSuccessful()) {
        // 5. 提取响应体数据(string()只能调用一次)
        String rawData = response.body().string();
        
        // 6. 数据处理逻辑
        processData(rawData);
    } else {
        // 处理服务器错误(如404, 500等)
        System.err.println("请求失败:" + response.code());
    }
} catch (IOException e) {
    // 7. 处理网络错误(超时、DNS解析失败等)
    e.printStackTrace();
}

关键点说明​:

  • execute():同步调用会阻塞当前线程
  • 使用场景​:后台任务(日志上报、文件下载)
  • 注意事项​:
    • 响应体.string()只能调用一次(后续调用返回空)
    • 必须使用try-with-resources确保资源释放
    • Android需在子线程执行
2. ​异步请求(非阻塞)​
// 构建请求(同同步示例)
Request request = ...; 

// 发起异步请求
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 1. 网络层失败处理(如无网络、超时)
        Log.e("Network", "请求失败: " + e.getMessage());
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        // 2. 注意:此回调在异步线程执行!
        try {
            if (response.isSuccessful()) {
                // 3. 获取原始响应数据
                String responseData = response.body().string();
                
                // 4. 切回主线程处理数据(UI操作必须主线程)
                runOnUiThread(() -> {
                    updateUI(responseData);  // 更新UI组件
                    saveToLocal(responseData); // 数据持久化
                });
            } else {
                // 5. 处理业务错误(如400 Bad Request)
                Log.w("API", "业务错误:" + response.code());
            }
        } finally {
            // 6. 确保关闭响应资源(防止内存泄漏)
            response.close();
        }
    }
});

// Android线程切换工具方法
private void runOnUiThread(Runnable action) {
    new Handler(Looper.getMainLooper()).post(action);
}

核心优势​:

  • 非阻塞调用:避免主线程卡顿
  • 自动线程切换:网络IO在工作线程执行
  • 生命周期安全:支持请求取消(call.cancel()

 二、数据操作深度解析

1. JSON数据解析

场景:解析用户数据接口响应

{
  "user": {
    "id": 123,
    "name": "张伟",
    "email": "zhangwei@example.com",
    "created_at": "2023-08-15T10:30:00Z"
  }
}

方案1:原生JSONObject解析(适合简单结构)​

String json = response.body().string();

try {
    JSONObject root = new JSONObject(json);
    JSONObject user = root.getJSONObject("user");
    
    int id = user.getInt("id");
    String name = user.getString("name");
    String email = user.getString("email");
    
    // 日期字符串转换
    String dateStr = user.getString("created_at");
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
    Date createDate = format.parse(dateStr);
    
    // 构建业务对象
    User userObj = new User(id, name, email, createDate);
} catch (JSONException | ParseException e) {
    // 处理格式错误或字段缺失
}

方案2:Gson自动映射(推荐复杂结构)​

// 实体类定义
public class User {
    private int id;
    private String name;
    private String email;
    
    @SerializedName("created_at") // 自定义字段映射
    private Date createDate;

    // Getters & Setters
}

// 解析操作
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Date.class, new DateDeserializer()) // 自定义日期解析
    .create();

// 直接映射JSON到Java对象
User user = gson.fromJson(json, User.class);

Gson日期转换器示例​:

class DateDeserializer implements JsonDeserializer<Date> {
    private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
        throws JsonParseException {
        try {
            return dateFormat.parse(json.getAsString());
        } catch (ParseException e) {
            throw new JsonParseException("日期格式错误", e);
        }
    }
}
2. XML数据解析

场景:解析RSS订阅源

<rss>
  <channel>
    <item>
      <title>OkHttp 4.9发布</title>
      <link>https://example.com/news/123</link>
      <pubDate>Wed, 15 Aug 2023 08:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>

Pull解析实现​:

XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(xmlData));

List<NewsItem> newsList = new ArrayList<>();
NewsItem currentItem = null;

int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
    switch (eventType) {
        case XmlPullParser.START_TAG:
            String tagName = parser.getName();
            if ("item".equals(tagName)) {
                currentItem = new NewsItem();
            } else if (currentItem != null) {
                // 提取标签内文本
                if ("title".equals(tagName)) {
                    currentItem.setTitle(parser.nextText());
                } else if ("link".equals(tagName)) {
                    currentItem.setLink(parser.nextText());
                } else if ("pubDate".equals(tagName)) {
                    currentItem.setPublishDate(parseRssDate(parser.nextText()));
                }
            }
            break;
            
        case XmlPullParser.END_TAG:
            if ("item".equals(parser.getName())) {
                newsList.add(currentItem);
                currentItem = null;
            }
            break;
    }
    eventType = parser.next();
}
3. 数据加工处理

类型安全转换​:

// 带错误恢复的类型转换
public int safeParseInt(String value, int defaultValue) {
    try {
        return Integer.parseInt(value);
    } catch (NumberFormatException e) {
        logError("数字格式错误: " + value);
        return defaultValue;
    }
}

数据过滤(Java Stream API)​​:

List<User> users = getUsersFromResponse();

// 筛选VIP用户并提取邮箱列表
List<String> vipEmails = users.stream()
    .filter(u -> u.getLevel() >= 3)   // VIP等级条件
    .map(User::getEmail)              // 提取邮箱字段
    .filter(email -> email.contains("@")) // 邮箱有效性检查
    .collect(Collectors.toList());

响应缓存处理​:

// 创建带缓存的客户端
File cacheDir = new File(getCacheDir(), "okhttp_cache");
long cacheSize = 50 * 1024 * 1024; // 50MB

OkHttpClient client = new OkHttpClient.Builder()
    .cache(new Cache(cacheDir, cacheSize))
    .addNetworkInterceptor(new CacheControlInterceptor()) // 缓存控制
    .build();

// 自定义缓存策略拦截器
static class CacheControlInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response original = chain.proceed(chain.request());
        return original.newBuilder()
            .header("Cache-Control", "public, max-age=" + 3600) // 缓存1小时
            .build();
    }
}

Handler替代方案 

一、核心替代场景对比 

功能Handler 实现协程替代方案LiveData 替代方案
延时操作postDelayed()delay()无需直接替代
主线程切换post(Runnable)withContext(Dispatchers.Main)postValue() / setValue()
定时任务sendMessageDelayed()whileActive + delay()MutableLiveData + ViewModel
资源安全释放手动移除回调结构化并发自动取消自动生命周期感知
数据传递Message.obj协程返回值 / ChannelLiveData 观察模式

 二、Kotlin 协程替代方案详解

1. 主线程切换 (替代 post())

Handler 实现:​

handler.post(() -> {
    // 在主线程执行
    textView.setText("Updated");
});

协程替代方案:​

// 在任何协程上下文中
lifecycleScope.launch(Dispatchers.Default) {
    // 后台操作
    val result = fetchData()
    
    // 切换到主线程(替代Handler.post())
    withContext(Dispatchers.Main) {
        textView.text = result
        button.isEnabled = true
    }
}

优势:​

  • 顺序编程模型,避免回调嵌套
  • 自动处理线程池资源
  • 与生命周期自动绑定
2. 延时操作 (替代 postDelayed())

Handler 实现:​

handler.postDelayed(() -> {
    showNotification();
}, 3000);

协程替代方案:​

lifecycleScope.launch {
    // 非阻塞延迟 (不会占用线程资源)
    delay(3000)
    
    // 自动在主线程执行
    showNotification()
}

高级延时场景 - 定时轮询:​

private var pollingJob: Job? = null

fun startPolling() {
    pollingJob = lifecycleScope.launch {
        while (isActive) { // 结构化并发感知取消
            fetchUpdates()
            delay(60_000) // 每分钟执行一次
        }
    }
}

fun stopPolling() {
    pollingJob?.cancel() // 取消定时任务(替代removeCallbacks())
}
3. 复杂任务管理 (替代多个 Runnable)

传统 Handler 问题:​

Handler handler = new Handler();

handler.post(task1);
handler.post(task2);
handler.postDelayed(task3, 1000);

协程结构化并发:​

lifecycleScope.launch {
    // 同时发起多个任务
    val deferred1 = async { loadUserProfile() }
    val deferred2 = async { loadUserOrders() }
    
    // 等待所有任务完成
    val (profile, orders) = awaitAll(deferred1, deferred2)
    
    // 处理结果(自动在主线程)
    updateUI(profile, orders)
    
    // 顺序执行多个任务
    withContext(Dispatchers.IO) {
        saveToLocal(profile)
        uploadAnalytics(orders)
    }
}

三、LiveData 替代方案详解

1. 主线程数据更新 (替代 Handler 的 UI 更新)

Handler 实现:​

// 后台线程
new Thread(() -> {
    String data = getData();
    handler.post(() -> textView.setText(data));
}).start();

LiveData 替代方案:​

// ViewModel中
class MyViewModel : ViewModel() {
    private val _uiData = MutableLiveData<String>()
    val uiData: LiveData<String> = _uiData
    
    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.fetchData()
            _uiData.postValue(result) // 自动切换到主线程
        }
    }
}

// Activity中
viewModel.uiData.observe(this) { data ->
    textView.text = data // 已在主线程
}
2. 生命周期感知 (替代手动回调移除)

传统 Handler 的问题:​

// 可能泄漏Activity
handler.postDelayed(() -> {
    if (getActivity() != null) {
        updateUI(); // 危险!可能访问已销毁的Activity
    }
}, 10000);

LiveData 解决方案:​

class SafeViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data
    
    init {
        viewModelScope.launch {
            while (true) {
                delay(10000)
                val newData = fetchPeriodicData()
                _data.postValue(newData)
            }
        }
    }
}

// Activity中 - 自动处理生命周期
viewModel.data.observe(this) { data ->
    // 只有Activity处于活跃状态时才会触发
    updateUI(data)
}
3. 事件总线替代方案 (单次事件处理)

传统 Handler 广播问题:​

// 多个Handler处理同一消息
handler.sendMessage(Message.obtain().apply {
    what = MSG_UPDATE
    obj = data
});

LiveData 事件总线:​

// 单次事件包装器
class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner) { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        }
    }

    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }
}

// ViewModel中使用
class EventViewModel : ViewModel() {
    private val _networkEvent = SingleLiveEvent<String>()
    val networkEvent: LiveData<String> = _networkEvent
    
    fun triggerEvent() {
        _networkEvent.value = "Event occurred at ${System.currentTimeMillis()}"
    }
}

MVVM 与 MVC 数据传输流向终极总结

 MVC 数据流向

单向环形闭环
用户操作 → View → Controller → Model → Controller → View更新

  • 核心特征​:Controller 作为中枢手动协调一切
  • 致命缺陷​:View 与 Model 隐性耦合,Controller 臃肿
  • Android 现状​:已被 Google 官方废弃

 MVVM 数据流向

双向自动通道
用户操作 → View → ViewModel ↔ Model
Model变更 → ViewModel → 自动 → View更新

  • 革命性突破​:
    • 数据绑定实现自动同步
    • ViewModel 完全解耦视图
    • 单向数据流确保可预测性
  • Android 未来​:
    Jetpack 官方架构(ViewModel + LiveData/Flow)

 本质区别

MVCMVVM
驱动力用户操作驱动数据变更驱动
更新方式手动命令式更新自动声明式更新
测试性需模拟视图独立测试业务逻辑
代码量冗余胶水代码多简洁易维护

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动力电池点焊机:驱动电池焊接高效与可靠的核心力量|比斯特自动化

在新能源汽车与储能设备需求激增的背景下&#xff0c;动力电池的制造工艺直接影响产品性能与安全性。作为电芯与极耳连接的核心设备&#xff0c;点焊机如何平衡效率、精度与可靠性&#xff0c;成为电池企业关注的重点。 动力电池点焊机的核心功能是确保电芯与极耳的稳固连接。…

【MySQL】10.事务管理

1. 事务的引入 首先我们需要知道CURD操作不加控制会产生什么问题&#xff1a; 为了解决上面的问题&#xff0c;CURD需要满足如下条件&#xff1a; 2. 事务的概念 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&…

Bugku-CTF-Web安全最佳刷题路线

曾经的我也是CTF六项全能&#xff0c;Web安全&#xff0c;密码学&#xff0c;杂项&#xff0c;Pwn&#xff0c;逆向&#xff0c;安卓样样都会。明明感觉这样很酷&#xff0c;却为何还是沦为社畜。Bugku-CTF-Web安全最佳刷题路线&#xff0c;我已经整理好了&#xff0c;干就完了…

IT学习方法与资料分享

一、编程语言与核心技能&#xff1a;构建技术地基 1. 入门首选&#xff1a;Python 与 JavaScript Python&#xff1a;作为 AI 与数据科学的基石&#xff0c;可快速构建数据分析与自动化脚本开发能力。 JavaScript&#xff1a;Web 开发的核心语言&#xff0c;可系统掌握 React/V…

jenkins gerrit-trigger插件配置

插件gerrit-trigger下载好之后要在Manage Jenkins -->Gerrit Trigger-->New Server 中新增Gerrit Servers 配置好保存后点击“状态”查看是否正常

数论总结,(模版与题解)

数论 欧拉函数X质数&#xff08;线性筛与二进制枚举&#xff09;求解组合数欧拉降幂&#xff08;乘积幂次&#xff09;乘法逆元最小质因子之和模版 欧拉函数 欧拉函数的定义就是小于等于n的数里有f(n)个数与n互质&#xff0c;下面是求欧拉函数的模版。 package com.js.datas…

EasyRTC嵌入式音视频通信SDK助力物联网/视频物联网音视频打造全场景应用

一、方案概述​ 随着物联网技术的飞速发展&#xff0c;视频物联网在各行业的应用日益广泛。实时音视频通信技术作为视频物联网的核心支撑&#xff0c;其性能直接影响着系统的交互体验和信息传递效率。EasyRTC作为一款成熟的音视频框架&#xff0c;具备低延迟、高画质、跨平台等…

1-2 Linux-虚拟机(2025.6.7学习篇- win版本)

1、虚拟机 学习Linux系统&#xff0c;就需要有一个可用的Linux系统。 如何获得&#xff1f;将自己的电脑重装系统为Linux&#xff1f; NoNo。这不现实&#xff0c;因为Linux系统并不适合日常办公使用。 我们需要借助虚拟机来获得可用的Linux系统环境进行学习。 借助虚拟化技术&…

Deepseek基座:Deepseek-v2核心内容解析

DeepSeek原创文章1 DeepSeek-v3&#xff1a;基于MLA的高效kv缓存压缩与位置编码优化技术 2 Deepseek基座&#xff1a;DeepSeek LLM核心内容解析 3 Deepseek基座&#xff1a;Deepseek MOE核心内容解析 4 Deepseek基座&#xff1a;Deepseek-v2核心内容解析 5Deepseek基座&#xf…

2025主流智能体Agent终极指南:Manus、OpenManus、MetaGPT、AutoGPT与CrewAI深度横评

当你的手机助手突然提醒"明天会议要带投影仪转接头"&#xff0c;或是电商客服自动生成售后方案时&#xff0c;背后都是**智能体(Agent)**在悄悄打工。这个AI界的"瑞士军刀"具备三大核心特征&#xff1a; 自主决策能力&#xff1a;像老司机一样根据路况实时…

家政小程序开发——AI+IoT技术融合,打造“智慧家政”新物种

基于用户历史订单&#xff08;如“每周一次保洁”&#xff09;、设备状态&#xff08;如智能门锁记录的清洁频率&#xff09;&#xff0c;自动生成服务计划。 结合天气数据&#xff08;如“雨天推荐玻璃清洁”&#xff09;&#xff0c;动态推送服务套餐。 IoT设备联动&#x…

Keil开发STM32生成hex文件/bin文件

生成hex文件生成bin文件 STM32工程的hex文件和bin文件都可以通过Keil直接配置生成 生成hex文件 工程中点击魔术棒&#xff0c;在 Output 中勾选 Create HEX File 选项&#xff0c;OK保存工程配置 编译工程通过后可以看到编译输出窗口有创建hex文件的提示 默认可以在Output文…

PDF 转 Markdown

本地可部署的模型 Marker Marker 快速准确地将文档转换为 markdown、JSON 和 HTML。 转换所有语言的 PDF、图像、PPTX、DOCX、XLSX、HTML、EPUB 文件在给定 JSON 架构 &#xff08;beta&#xff09; 的情况下进行结构化提取设置表格、表单、方程式、内联数学、链接、引用和代…

北大开源音频编辑模型PlayDiffusion,可实现音频局部编辑,比传统 AR 模型的效率高出 50 倍!

北大开源了一个音频编辑模型PlayDiffusion&#xff0c;可以实现类似图片修复(inpaint)的局部编辑功能 - 只需修改音频中的特定片段&#xff0c;而无需重新生成整段音频。此外&#xff0c;它还是一个高性能的 TTS 系统&#xff0c;比传统 AR 模型的效率高出 50 倍。 自回归 Tra…

蒲公英盒子连接问题debug

1、 现象描述 2、问题解决 上图为整体架构图&#xff0c;其中左边一套硬件设备是放在机房&#xff0c;右边是放在办公室。左边的局域网连接了可以访问外网的路由器&#xff0c;利用蒲公英作为旁路路由将局域网暴露在外网环境下。 我需要通过蒲公英作为旁路路由来进行远程访问&…

Unity | AmplifyShaderEditor插件基础(第五集:简易膨胀shader)

一、&#x1f44b;&#x1f3fb;前言 大家好&#xff0c;我是菌菌巧乐兹~本节内容主要讲一下&#xff0c;如何用shader来膨胀~ 效果预览&#xff1a; 二、&#x1f4a8;膨胀的基本原理 之前的移动是所有顶点朝着一个方向走&#xff0c;所以是移动 如果所有顶点照着自己的方…

WINUI——Magewell视频捕捉开发手记

背景 因需要融合视频&#xff0c;并加载患者CT中提取出的气管镜与病变&#xff0c;以便能实时查看气管镜是否在正确位置。 开发环境 硬件&#xff1a;Magewell的USB Capture HDMI Gen 2 IDE&#xff1a;VS2022 FrameWork: .Net6 WINUI Package: MVVMToolKit NLog Ma…

TDengine 开发指南——无模式写入

简介 在物联网应用中&#xff0c;为了实现自动化管理、业务分析和设备监控等多种功能&#xff0c;通常需要采集大量的数据项。然而&#xff0c;由于应用逻辑的版本升级和设备自身的硬件调整等原因&#xff0c;数据采集项可能会频繁发生变化。为了应对这种挑战&#xff0c;TDen…

第34次CCF-CSP认证真题解析(目标300分做法)

第34次CCF-CSP认证 矩阵重塑&#xff08;其一&#xff09;AC代码及解析矩阵重塑&#xff08;其二&#xff09;AC代码及解析货物调度AC代码及解析 矩阵重塑&#xff08;其一&#xff09; 输入输出及样例&#xff1a; AC代码及解析 1.线性化原矩阵 &#xff1a;由于cin的特性我们…

video-audio-extractor:视频转换为音频

软件介绍 前几天在网上看见有人分享了一个源码&#xff0c;大概就是py调用的ffmpeg来制作的。 这一次我带来源码版&#xff08;需要py环境才可以运行&#xff09;&#xff0c;开箱即用版本&#xff08;直接即可运行&#xff09; 软件特点 软件功能 视频提取音频&#xff1a…