h5页面开发面试题,攒了一个月的Android面试题及详细解答(1),2024大厂Android社招面试题

这种就比较全面了。调用方法并且获取返回值。

2) JS调用Android端代码

主要有两种方法:

通过WebView的addJavascriptInterface()进行对象映射

public class AndroidtoJs extends Object {

// 定义JS需要调用的方法

// 被JS调用的方法必须加入@JavascriptInterface注解

@JavascriptInterface

public void hello(String msg) {

System.out.println(“JS调用了Android的hello方法”);

}

}

mWebView.addJavascriptInterface(new AndroidtoJs(), “test”);

//js中:

function callAndroid(){

// 由于对象映射,所以调用test对象等于调用Android映射的对象

test.hello(“js调用了android中的hello方法”);

}

这种方法虽然很好用,但是要注意的是4.2以后,对于被调用的函数以@JavascriptInterface进行注解,否则容易出发漏洞,因为js方可以通过反射调用一些本地命令,很危险。

通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url

这种方法是通过shouldOverrideUrlLoading回调去拦截url,然后进行解析,如果是之前约定好的协议,就调用相应的方法。

// 复写WebViewClient类的shouldOverrideUrlLoading方法

mWebView.setWebViewClient(new WebViewClient() {

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

Uri uri = Uri.parse(url);

// 如果url的协议 = 预先约定的 js 协议

if ( uri.getScheme().equals(“js”)) {

// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议

if (uri.getAuthority().equals(“webview”)) {

System.out.println(“js调用了Android的方法”);

// 可以在协议上带有参数并传递到Android上

HashMap<String, String> params = new HashMap<>();

Set collection = uri.getQueryParameterNames();

}

return true;

}

return super.shouldOverrideUrlLoading(view, url);

}

}

);

如何避免WebView内存泄露

WebView的内存泄露主要是因为在页面销毁后,WebView的资源无法马上释放所导致的。现在主流的是两种方法:

1)不在xml布局中添加webview标签,采用在代码中new出来的方式,并在页面销毁的时候去释放webview资源

//addview

private WeakReference webActivityReference = new WeakReference(this);

mWebView = new BridgeWebView(webActivityReference .get());

webview_container.addView(mWebView);

//销毁

ViewParent parent = mWebView.getParent();

if (parent != null) {

((ViewGroup) parent).removeView(mWebView);

}

mWebView.stopLoading();

mWebView.getSettings().setJavaScriptEnabled(false);

mWebView.clearHistory();

mWebView.clearView();

mWebView.removeAllViews();

mWebView.destroy();

mWebView=null;

2)另起一个进程加载webview,页面销毁后干掉这个进程。但是这个方法的麻烦之处就在于进程间通信。

使用方法很简单,xml文件中写出进程名即可,销毁的时候调用System.exit(0)

<activity android:name=“.WebActivity”

android:process=“:remoteweb”/>

System.exit(0)

webView还有哪些可以优化的地方

  • 提前初始化或者使用全局WebView。首次初始化WebView会比第二次初始化慢很多。初始化后,即使WebView已释放,但一些多WebView共用的全局服务/资源对想仍未释放,而第二次初始化不需要生成,因此初始化变快。

  • DNS采用和客户端API相同的域名,DNS解析也是耗时比较多的部分,所以用客户端API相同的域名因为其DNS会被缓存,所以打开webView的时候就不会再耗时在DNS上了

  • 对于JS的优化,尽量不要用偏重的框架,比如React。其次是高性能要求页面还是需要后端渲染。最后就是app中的网页框架要统一,这样就可以对js进行缓存和复用。

这里有美团团队的总结方案,如下:

  • WebView初始化慢,可以在初始化同时先请求数据,让后端和网络不要闲着。

  • 后端处理慢,可以让服务器分trunk输出,在后端计算的同时前端也加载网络静态资源。

  • 脚本执行慢,就让脚本在最后运行,不阻塞页面解析。

  • 同时,合理的预加载、预缓存可以让加载速度的瓶颈更小。

  • WebView初始化慢,就随时初始化好一个WebView待用。

  • DNS和链接慢,想办法复用客户端使用的域名和链接。

  • 脚本执行慢,可以把框架代码拆分出来,在请求页面之前就执行好。

Activity、View、Window 之间的关系。

每个 Activity 包含了一个 Window对象,这个对象是由 PhoneWindow做的实现。而 PhoneWindow 将 DecorView作为了一个应用窗口的根 View,这个 DecorView 又把屏幕划分为了两个区域:一个是 TitleView,一个是ContentView,而我们平时在 Xml 文件中写的布局正好是展示在 ContentView 中的。

说说Android的事件分发机制完整流程,也就是从点击屏幕开始,事件会怎么传递。

我觉得事件分发机制流程可以分为三部分,分别是从外传里,从里传外,消费之后。

1)首先,从最外面一层传到最里面一层:

如果当前是viewgroup层级,就会判断 onInterceptTouchEvent是否为true,如果为true,则代表事件要消费在这一层级,不再往下传递。接着便执行当前 viewgroup 的onTouchEvent方法。如果onInterceptTouchEvent为false,则代表事件继续传递到下一层级的 dispatchTouchEvent方法,接着一样的代码逻辑,一直到最里面一层的view。

伪代码解释:

public boolean dispatchTouchEvent(MotionEvent event) {

boolean isConsume = false;

if (isViewGroup) {

if (onInterceptTouchEvent(event)) {

isConsume = onTouchEvent(event);

} else {

isConsume = child.dispatchTouchEvent(event);

}

} else {

//isView

isConsume = onTouchEvent(event);

}

return isConsume;

}

2)到最里层的view之后,view本身还是可以选择消费或者传到外面。

到最里面一层就会直接执行onTouchEvent方法,这时候,view有没有权利拒绝消费事件呢?按道理view作为最底层的,应该是没有发言权才对。但是呢,秉着公平公正原则,view也是可以拒绝的,可以在onTouchEvent方法返回false,表示他不想消费这个事件。那么它的父容器的onTouchEvent又会被调用,如果父容器的onTouchEvent又返回false,则又交给上一级。一直到最上层,也就是Activity的onTouchEvent被调用。

伪代码解释:

public void handleTouchEvent(MotionEvent event) {

if (!onTouchEvent(event)) {

getParent.onTouchEvent(event);

}

}

3)消费之后

当某一层viewGroup的onInterceptTouchEvent为true,则代表当前层级要消费事件。如果它的onTouchListener被设置了的话,则onTouch会被调用,如果onTouch的返回值返回true,则onTouchEvent不会被调用。如果返回false或者没有设置onTouchListener,则会继续调用onTouchEvent。而onClick方法则是设置了onClickListener则会被正常调用。

伪代码解释:

public void consumeEvent(MotionEvent event) {

if (setOnTouchListener) {

int tag = onTouch();

if (!tag) {

onTouchEvent(event);

}

} else {

onTouchEvent(event);

}

if (setOnClickListener) {

onClick();

}

}

解决滑动冲突的办法。

解决滑动冲突的根本就是要在适当的位置进行拦截,那么就有两种解决办法:

  • 外部拦截:从父view端处理,根据情况决定事件是否分发到子view

  • 内部拦截:从子view端处理,根据情况决定是否阻止父view进行拦截,其中的关键就是requestDisallowInterceptTouchEvent方法。

1)外部拦截法,其实就是在onInterceptTouchEvnet方法里面进行判断,是否拦截,见代码:

//外部拦截法:父view.java

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

boolean intercepted = false;

//父view拦截条件

boolean parentCanIntercept;

switch (ev.getActionMasked()) {

case MotionEvent.ACTION_DOWN:

intercepted = false;

break;

case MotionEvent.ACTION_MOVE:

if (parentCanIntercept) {

intercepted = true;

} else {

intercepted = false;

}

break;

case MotionEvent.ACTION_UP:

intercepted = false;

break;

}

return intercepted;

}

还是比较简单的,直接判断拦截条件,然后返回true就代表拦截,false就不拦截,传到子view。注意的是ACTION_DOWN状态不要拦截,如果拦截,那么后续事件就直接交给父view处理了,也就没有拦截不拦截的问题了。

2)内部拦截法,就是通过requestDisallowInterceptTouchEvent方法让父view不要拦截。

//父view.java

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {

return false;

} else {

return true;

}

}

//子view.java

@Override

public boolean dispatchTouchEvent(MotionEvent event) {

//父view拦截条件

boolean parentCanIntercept;

switch (event.getActionMasked()) {

case MotionEvent.ACTION_DOWN:

getParent().requestDisallowInterceptTouchEvent(true);

break;

case MotionEvent.ACTION_MOVE:

if (parentCanIntercept) {

getParent().requestDisallowInterceptTouchEvent(false);

}

break;

case MotionEvent.ACTION_UP:

break;

}

return super.dispatchTouchEvent(event);

}

requestDisallowInterceptTouchEvent(true)的意思是阻止父view拦截事件,也就是传入true之后,父view就不会再调用onInterceptTouchEvent。反之,传入false就代表父view可以拦截,也就是会走到父view的onInterceptTouchEvent方法。所以需要父view拦截的时候,就传入flase,需要父view不拦截的时候就传入true。

Fragment生命周期,当hide,show,replace时候生命周期变化

1)生命周期:

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。

  • onCreate():Fragment被创建时调用。

  • onCreateView():创建Fragment的布局。

  • onActivityCreated():当Activity完成onCreate()时调用。

  • onStart():当Fragment可见时调用。

  • onResume():当Fragment可见且可交互时调用。

  • onPause():当Fragment不可交互但可见时调用。

  • onStop():当Fragment不可见时调用。

  • onDestroyView():当Fragment的UI从视图结构中移除时调用。

  • onDestroy():销毁Fragment时调用。

  • onDetach():当Fragment和Activity解除关联时调用。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力)

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。

加油,共勉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

辛苦,我们应得的。

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。

加油,共勉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值