【Android春招每日一练】(十四) 剑指4题+Android进阶

概览

剑指offer:0~n-1中缺失的数字、二叉搜索树的第k大节点、二叉树的深度、平衡二叉树
Android基础:Context详解、Android MVP模式、Binder机制及AIDL使用

剑指offer

1.53 0~n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:
输入: [0,1,3]
输出: 2
//二分查找类
//判断mid索引和nums[mid]值是否相等
class Solution {
    public int missingNumber(int[] nums) {
        int left = 0,right = nums.length - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(mid == nums[mid]){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return left;
    }
}
1.54 二叉搜索树的第k大节点

给定一棵二叉搜索树,请找出其中第 k 大的节点的值。

示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 4
//中序遍历为递增序列,改成中序得倒序遍历为递减序列
class Solution {
    int k,res;
    public int kthLargest(TreeNode root, int k) {
        this.k = k;
        dfs(root);
        return res;
    }

    void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.right);			//先递归右子树
        if(k == 0) return;
        if(--k == 0) res = root.val;
        dfs(root.left);
    }
}
1.55 二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7],

	3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。
//dfs
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
    }
}
1.56 平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]
	3
   / \
  9  20
    /  \
   15   7
返回 true 。
//后序遍历
class Solution {
    public boolean isBalanced(TreeNode root) {
        return recur(root) != -1;
    }

    int recur(TreeNode root){
        if(root == null) return 0;
        int left = recur(root.left);
        if(left == -1) return -1;
        int right = recur(root.right);
        if(right == -1) return -1;
        return Math.abs(left - right) < 2 ? Math.max(left,right) + 1 : -1;
    }
}

Android基础

Context详解

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QM4YJlZM-1643280056583)(D:\Typora\img\image-20220126142229564.png)]

Context的两个子类分工明确,其中ContextImplContext的具体实现类,ContextWrapperContext的包装类。Activity,

Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。

Context数量=Activity数量+Service数量 +1

作用域

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HSE5MfKE-1643280056588)(D:\Typora\img\image-20220126142359310.png)]

总结:凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。

getApplication()和getApplicationContext() 区别

通过代码,打得出两者的内存地址都是相同的,说明它们是同一个对象。其实这个结果也很好理解,因为前面已经说过了, Application本身就是一个Context,所以这里获取getApplicationContext()得到的结果就是Application本身的实例。那么问题来了,既然这两个方法得到的结果都是相同的,那么Android为什么要提供两个功能重复的方法呢?实际上这两个方法在作用域上有比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在ActivityService中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了。

正确使用Context

一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:

  1. 当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。

  2. 不要让生命周期长于Activity的对象持有到Activity的引用。

  3. 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

Android MVP模式

概述

MVP,全称 Model-View-Presenter,即模型**-视图-**层现器。

提到MVP,就必须要先介绍一下它的前辈MVC,因为MVP正是基于MVC的基础发展而来的。两个之间的关系也是源远流长。

MVC,全称Model-View-Controller,即模型**-视图-**控制器。 具体如下:

View:对应于布局文件

Model:业务逻辑和实体模型

Controllor:对应于Activity

但是View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller,使得Activity变得臃肿。

而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的:

View 对应于Activity,负责View的绘制以及与用户交互

Model 依然是业务逻辑和实体模型

Presenter 负责完成View于Model间的交互

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZDKrgME-1643280056590)(D:\Typora\img\image-20220126151215148.png)]

总结:MVP模式减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理,模块职责划分明显,层次清晰。与之对应的好处就是,耦合度更低,更方便的进行测试。

MVC和MVP的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIqJNJql-1643280056591)(D:\Typora\img\image-20220126151303289.png)]

MVC中是允许ModelView进行交互的,而MVP中很明显,ModelView之间的交互由Presenter完成。还有一点就是PresenterView之间的交互是通过接口的。 而且MVCV对应的是布局文件,MVPV对应的是Activity

MVP模式的整个核心流程:

View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有View层的Interface的引用以及Model层的引用,而View层持有Presenter层引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的引用,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载情况,最后Presenter层再调用View层的接口将加载后的数据展示给用户。

TODO:尝试写一个MVP模型的小demo

Binder机制及AIDL使用

IPC原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58GQipoO-1643280056593)(D:\Typora\img\image-20220126175430186.png)]

每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间。当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享 的内核内存空间来完成底层通信工作的。Client端与Server端进程往往采用ioctl等方法与内核空间的驱动进行交互。

Binder原理

Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及Binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pScklSON-1643280056594)(D:\Typora\img\image-20220126175521836.png)]

Client进程:使用服务的进程。

Server进程:提供服务的进程。

ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

Binder运行机制

图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

**注册服务(addService):**Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。

**获取服务(getService):**Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。

**使用服务:**Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:Client是客户端,Server是服务端。

图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信 (Interprocess Communication)方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自 定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。

AIDL的使用

AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成可以在Android设备上两个进程之间进行进程间通信(Interprocess Communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数,来完成进程间通信。

服务端进程:

1:创建AIDL文件如xxx.aidl

2:在接口中自定义方法,如果有bean则实例化改bean实体类

3:构建Service远程服务

4:最后在清单文件里 注册服务,其中属性要加上export:true 让外部程序可以访问。

客户端进程:

1:将服务端的AIDL文件拷贝到main文件夹下,(如果有bean也要对应拷贝)。(包名要求与服务端的一致)

2:重建一下项目检查一下java文件是否构建成功

3:连接绑定服务,获取aidl接口实例。

4:按需调用远程接口。

TODO:尝试做一个小demo

总结

1.后面是些Andriod较为进阶的知识,需要理解和掌握,最好能自己尝试写一个小demo加深印象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leisure-ZL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值