GVKun编程网logo

android笔记选(android 笔记)

19

本文将分享android笔记选的详细内容,并且还将对android笔记进行详尽解释,此外,我们还将为大家带来关于2021最新Android笔试题总结美团Android岗职能要求、AndroidStud

本文将分享android笔记选的详细内容,并且还将对android 笔记进行详尽解释,此外,我们还将为大家带来关于2021最新Android笔试题总结美团Android岗职能要求、Android Studio 3,阿里+头条+腾讯等大厂Android笔试题目分享、android从入门到精通!阿里内部Android笔记火爆IT圈,挥泪整理面经、Android多线程断点续传下载原理及实现,2021阿里Android笔试总结的相关知识,希望对你有所帮助。

本文目录一览:

android笔记选(android 笔记)

android笔记选(android 笔记)

    first  首先学会布局


注入解放双手,用好一些插件比如butterKnife能省去很多findViewById的麻烦;

用好属性动画,看过很多App,引导页带动画的给人第一印象就很好,毕竟这是一个看“脸”的时代;

虽然App设计风格一直都是IOS,还是要支持MaterialDesign,毕竟很 cool,现在市面上这样风格的还是比较少;

熟悉android-support-v7-21新组件包,比如RecycleView、DrawerLayout、CardView等等都已经有很高使用率;

布局复杂导致的性能问题不容小觑,多看一些相关知识,精简布局;

2.图片方面:

最喜欢Picasso,尤其是管道式的调用,编程之美啊;

而且Picasso还可以支持高斯模糊,太方便了;

3.网络方面:

现在的趋势基本上都是okHttp + retrofit,还好之前实习的时候师兄用的就是这个,感觉还是很方便的;

retrofit的风格好像是传说中的REST,下阶段需要看看源码;

图片上传是个问题;

4.第三方:    

现在的App基本都要有这些功能:.分享、支付、定位、推送、统计、buggly、第三方登录,我只用过高德定位和百度定位、极光推送、友盟推送也只是负责埋点,还没了解完整流程。下一阶段需要统统使用一次。

在项目过程中还发现一个问题,在A项目中集成了分享的Module,结果导入到B中还是有问题,结果还得需要一个人重新集成一次,效率不高。需要注意不要实现功能就算了,要提成一个单独的、可插拔的Module,写好回调,做到一键集成。

5.工具方面: 

AndroidStudio还是很强大的,很多细小的功能点需要我们花时间去发现,比如说内存使用监控、截屏、gif录制、当前git分支名称等等都可以找到,不了解的话太可惜了,国内有个镜像网站可以直接更新SDK,很强大http://www.androiddevtools.cn/

Gradle真是个神奇的东西,又能构建、又能依赖更新、还能多渠道打包,需要好好看看;Gradle官方文档

Git现在公司使用率还是比较高的,使用不好,同步时各种问题太蛋疼,有效使用能在development和release版本间实现良好控制、友好协作;Git详解

Maven 这个是因为身边人基本都会,好像在项目管理、减少本地代码方面有很大作用,需要学习学习;

6.工具类方面: 

做个2个商业项目 ,发现工具类基本都是通用的,所以有必要维护自己的工具类,工具类的内容包括且不限于:

本地缓存类、文件管理类、Json处理类、日志类、网络情况类、字符串类、手机系统信息类、线程池类、常用的UI类、6.0以后还需要权限类(用于动态提示用户是否提供某权限)等等等等,遇到新功能就添进去,这就是自己的行走江湖的利器之一啊。

7.架构方面:

其实我是拒绝谈架构的,毕竟我还是只小菜鸟。但是最近项目要更换MVP架构,我也学了学,发现了一个重要的问题—–>要想懂新架构的好旧架构的差,不学点设计模式是不行的。所以设计模式是下一阶段的重要任务。

MVP现在我还理解不太深,还是先做好第一步吧:减轻Activity任务,把点击响应尽量放到自定义View或者Fragment里;

一说架构、性能,就得考虑线程管理方面的东西,虽然我现在接触的不多,但是好的应用要经得起高并发才对,所以这也不能忘记。

8.渠道发布: 

每个App上线的毕竟之路就是打包、发布,鉴于国内应用市场杂乱,多达900+个市场,如果没有很好的打包方式,一个个打岂不是累死。好在现在gradle打包很强大,不过我也只是听说,还没有亲手接触过,需要先学好Gradle,然后体会体会它的便捷啊!

   two,学会使用Android开发的书籍,教程,工具

入门书籍

《Google Androidsdk揭秘》


《Learning Android(中文版)》

本书为Android开发入门图书,循序渐进地介绍了如何利用Android基本构件来实现构造用户界面、存储数据、连接网络等实践中必不可少的需求。作者以亲手实现一个类Twitter的应用为主线,通过不断为其添加功能展开讲解。而这一学习过程积累出的代码模式库,可以灵活运用于真实Android应用开发。本书内容包括但不限于:Android平台概览、Android设计架构、Android开发环境配置、基本Android的用户界面构建、UI元件组织方法、构建执行后台任务与更新数据的服务、AIDL与NDK介绍等。

《Android疯狂讲义》

课程涵盖全部Android应用开发的基础,根据技能点的作用分为5个篇章,包括环境篇、控件篇、布局篇、组件篇和通用篇,本课程的目标就是“看得懂、学得会、做得出”,为后续的学习打下夯实的基础。

《密西西比河谷州立大学:Android应用程序开发(英文视频,中文字幕)》

课程介绍基于Android平台上的应用开发,课程分为六个模块,分别涉及Android平台概述和其基本构件、初次编写Android应用程序及接收和显示网络数据等内容。通过本课程,你将很好地了解Android平台,了解如何运用Android平台,以及进一步研究该平台的学习方向。

《Android攻城狮》

课程由浅入深地带您学会Android的常用控件的开发和使用,以知识概念为主导,实例代码为驱动,带您走入一个神奇的移动开发世界。


课程讲带你熟悉Android开发中常用的调试方式,各种对话框,各种提示菜单,各种动画效果等,来进一步充实你的Android知识。

进阶

《Android的设计与实现:卷1》

本书是Android应用开发工程师和Android系统工程师进阶修炼的必读之作。它由资深Android内核专家亲自执笔,从源代码角度,系统、深入、透彻剖析Android系统框架层(Framework)的设计思想和实现原理,为Android应用工程师和系统工程师解决实际工作中的各种难题提供了原理性的指导。为了降低读者的阅读成本,《Android的设计与实现:卷1》使用了大量简单的UML类图和序列图来展示类的层次结构和方法的调用流程,使读者能迅速读完《Android的设计与实现:卷1》并领会其精髓!

深入探索

《深入理解Android:卷1》

这是一本以情景方式对Android的源代码进行深入分析的书。内容广泛,以对Framework层的分析为主,兼顾Native层和Application层;分析深入,每一部分源代码的分析都力求透彻;针对性强,注重实际应用开发需求,书中所涵盖的知识点都是Android应用开发者和系统开发者需要重点掌握的。

《深入理解Android:卷2》

“深入理解Android”系列的第2本,第1本书上市后获得广大读者高度评价,在Android开发者社群内口口相传。《深入理解Android:卷2》不仅继承了第1本书的优点并改正了其在细微处存在的一些不足,而且还在写作的总体思想上进行了创新,更强调从系统设计者的角度去分析Android系统中各个模块内部的实现原理和工作机制。从具体内容上讲,重点是Android Framework的Java层,对Java层涉及的核心模块和服务进行了深入而细致的分析。通过《深入理解Android:卷2》,读者不仅能对Android系统本身有更深入的理解,而且还能掌握分析大型复杂源代码的能力。

《Android应用性能优化》

今天的Android应用开发者经常要想尽办法来提升程序性能。由于应用越来越复杂,这个问题也变得越来越棘手。《Android应用性能优化》主要介绍如何快速高效地优化应用,让应用变得稳定高效,你将学会利用Android SDK和NDK来混合或单独使用Java、C/C++来开发应用。《Android应用性能优化中还特别讲解了如下内容:一些OpenGL的优化技术以及RenderScript(Android的新特性)的基础知识;利用SDK来优化应用的Java代码的技巧;通过高效使用内存来提升性能的技巧;延长电池使用时间的技巧;使用多线程的时机及技巧;评测剖析代码的技巧。

《Android软件安全与逆向分析》

由浅入深、循序渐进地讲解了Android系统的软件安全、逆向分析与加密解密技术。包括Android软件逆向分析和系统安全方面的必备知识及概念、如何静态分析Android软件、如何动态调试Android软件、Android软件的破解与反破解技术的探讨,以及对典型Android病毒的全面剖析。

工具和资源

官方资源

Android开发者中心

Android开发官方文档。这个不必多说了。

《Android 设计指南(官方英文版))》(非官方简体中文版 )

指导你设计一款Android应用。内容涉及:Android应用设计原则、UI概览、风格、模式和控件设计方面。

技术问答

Stack Overflow。Stack Overflow Android的标签页包括很完整的信息,很有参考价值!

周刊和聚合

Android周刊(英文) (中文)

每周更新的Android开发周刊。内容包括:开发资讯、技术文章、App设计、工具等。

Android开发话题

伯乐头条的Android开发话题聚合了很多Android开发相关的文章、教程、资讯等。内容来自Android原创技术博客作者或者读者分享,伯乐在线安卓频道团队整理。这个话题即将提供订阅功能,关注话题后即可收到每周推送精选内容推送。

工具和开发库

Android开源工具和库

GitHub上最热门的Android开源工具和开发库。

Android开发工具(中文)

由伯乐在线安卓开发频道团队整理。持续更新各种优秀的Android开发工具。

其实Android的书籍很多,最基本的是掌握Android按钮的代码功能实现,其次多看看安卓巴士网站的代码

三,Android常用代码片段

  

1
2
3
4
5
6
7
8
9
publicstaticintdip2px(Context context, floatdpValue) {
    finalfloatscale = context.getResources().getDisplayMetrics().density;
    return(int) (dpValue * scale + 0.5f);
}
 
publicstaticintpx2dip(Context context, floatpxValue) {
    finalfloatscale = context.getResources().getDisplayMetrics().density;
    return(int) (pxValue / scale + 0.5f);
}

px-sp转换

1
2
3
4
5
6
7
8
9
publicstaticintpx2sp(Context context, floatpxValue) {
        finalfloatfontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return(int) (pxValue / fontScale + 0.5f);
    }
 
publicstaticintsp2px(Context context, floatspValue) {
        finalfloatfontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return(int) (spValue * fontScale + 0.5f);
    }

把一个毫秒数转化成时间字符串


格式为小时/分/秒/毫秒(如:24903600 –> 06小时55分03秒600毫秒)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
     * @param millis
     *            要转化的毫秒数。
     * @param isWhole
     *            是否强制全部显示小时/分/秒/毫秒。
     * @param isFormat
     *            时间数字是否要格式化,如果true:少位数前面补全;如果false:少位数前面不补全。
     * @return 返回时间字符串:小时/分/秒/毫秒的格式(如:24903600 --> 06小时55分03秒600毫秒)。
     */
    publicstaticString millisToString(longmillis, booleanisWhole,
            booleanisFormat) {
        String h = "";
        String m = "";
        String s = "";
        String mi = "";
        if(isWhole) {
            h = isFormat ? "00小时": "0小时";
            m = isFormat ? "00分": "0分";
            s = isFormat ? "00秒": "0秒";
            mi = isFormat ? "00毫秒": "0毫秒";
        }
 
        longtemp = millis;
 
        longhper = 60* 60* 1000;
        longmper = 60* 1000;
        longsper = 1000;
 
        if(temp / hper > 0) {
            if(isFormat) {
                h = temp / hper < 10? "0"+ temp / hper : temp / hper + "";
            } else{
                h = temp / hper + "";
            }
            h += "小时";
        }
        temp = temp % hper;
 
        if(temp / mper > 0) {
            if(isFormat) {
                m = temp / mper < 10? "0"+ temp / mper : temp / mper + "";
            } else{
                m = temp / mper + "";
            }
            m += "分";
        }
        temp = temp % mper;
 
        if(temp / sper > 0) {
            if(isFormat) {
                s = temp / sper < 10? "0"+ temp / sper : temp / sper + "";
            } else{
                s = temp / sper + "";
            }
            s += "秒";
        }
        temp = temp % sper;
        mi = temp + "";
 
        if(isFormat) {
            if(temp < 100&& temp >= 10) {
                mi = "0"+ temp;
            }
            if(temp < 10) {
                mi = "00"+ temp;
            }
        }
 
        mi += "毫秒";
        returnh + m + s + mi;
    }

格式为小时/分/秒/毫秒(如:24903600 –> 06小时55分03秒)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
     *
     * @param millis
     *            要转化的毫秒数。
     * @param isWhole
     *            是否强制全部显示小时/分/秒/毫秒。
     * @param isFormat
     *            时间数字是否要格式化,如果true:少位数前面补全;如果false:少位数前面不补全。
     * @return 返回时间字符串:小时/分/秒/毫秒的格式(如:24903600 --> 06小时55分03秒)。
     */
    publicstaticString millisToStringMiddle(longmillis, booleanisWhole,
            booleanisFormat) {
        returnmillisToStringMiddle(millis, isWhole, isFormat, "小时", "分钟", "秒");
    }
 
    publicstaticString millisToStringMiddle(longmillis, booleanisWhole,
            booleanisFormat, String hUnit, String mUnit, String sUnit) {
        String h = "";
        String m = "";
        String s = "";
        if(isWhole) {
            h = isFormat ? "00"+ hUnit : "0"+ hUnit;
            m = isFormat ? "00"+ mUnit : "0"+ mUnit;
            s = isFormat ? "00"+ sUnit : "0"+ sUnit;
        }
 
        longtemp = millis;
 
        longhper = 60* 60* 1000;
        longmper = 60* 1000;
        longsper = 1000;
 
        if(temp / hper > 0) {
            if(isFormat) {
                h = temp / hper < 10? "0"+ temp / hper : temp / hper + "";
            } else{
                h = temp / hper + "";
            }
            h += hUnit;
        }
        temp = temp % hper;
 
        if(temp / mper > 0) {
            if(isFormat) {
                m = temp / mper < 10? "0"+ temp / mper : temp / mper + "";
            } else{
                m = temp / mper + "";
            }
            m += mUnit;
        }
        temp = temp % mper;
 
        if(temp / sper > 0) {
            if(isFormat) {
                s = temp / sper < 10? "0"+ temp / sper : temp / sper + "";
            } else{
                s = temp / sper + "";
            }
            s += sUnit;
        }
        returnh + m + s;
    }

把一个毫秒数转化成时间字符串。格式为小时/分/秒/毫秒(如:24903600 –> 06小时55分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
     *
     * @param millis
     *            要转化的毫秒数。
     * @param isWhole
     *            是否强制全部显示小时/分。
     * @param isFormat
     *            时间数字是否要格式化,如果true:少位数前面补全;如果false:少位数前面不补全。
     * @return 返回时间字符串:小时/分/秒/毫秒的格式(如:24903600 --> 06小时55分钟)。
     */
    publicstaticString millisToStringShort(longmillis, booleanisWhole,
            booleanisFormat) {
        String h = "";
        String m = "";
        if(isWhole) {
            h = isFormat ? "00小时": "0小时";
            m = isFormat ? "00分钟": "0分钟";
        }
 
        longtemp = millis;
 
        longhper = 60* 60* 1000;
        longmper = 60* 1000;
        longsper = 1000;
 
        if(temp / hper > 0) {
            if(isFormat) {
                h = temp / hper < 10? "0"+ temp / hper : temp / hper + "";
            } else{
                h = temp / hper + "";
            }
            h += "小时";
        }
        temp = temp % hper;
 
        if(temp / mper > 0) {
            if(isFormat) {
                m = temp / mper < 10? "0"+ temp / mper : temp / mper + "";
            } else{
                m = temp / mper + "";
            }
            m += "分钟";
        }
 
        returnh + m;
    }

把日期毫秒转化为字符串

1
2
3
4
5
6
7
8
9
10
11
12
/**
     * @param millis
     *            要转化的日期毫秒数。
     * @param pattern
     *            要转化为的字符串格式(如:yyyy-MM-dd HH:mm:ss)。
     * @return 返回日期字符串。
     */
    publicstaticString millisToStringDate(longmillis, String pattern) {
        SimpleDateFormat format = newSimpleDateFormat(pattern,
                Locale.getDefault());
        returnformat.format(newDate(millis));
    }

把日期毫秒转化为字符串(文件名)

1
2
3
4
5
6
7
8
9
10
11
/**
     * @param millis
     *            要转化的日期毫秒数。
     * @param pattern
     *            要转化为的字符串格式(如:yyyy-MM-dd HH:mm:ss)。
     * @return 返回日期字符串(yyyy_MM_dd_HH_mm_ss)。
     */
    publicstaticString millisToStringFilename(longmillis, String pattern) {
        String dateStr = millisToStringDate(millis, pattern);
        returndateStr.replaceAll("[- :]", "_");
    }

转换当前时间为易用时间格式


1小时内用,多少分钟前; 超过1小时,显示时间而无日期; 如果是昨天,则显示昨天 超过昨天再显示日期; 超过1年再显示年。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
publicstaticlongoneHourMillis = 60* 60* 1000; // 一小时的毫秒数
publicstaticlongoneDayMillis = 24* oneHourMillis; // 一天的毫秒数
publicstaticlongoneYearMillis = 365* oneDayMillis; // 一年的毫秒数
 
publicstaticString millisToLifeString(longmillis) {
        longnow = System.currentTimeMillis();
        longtodayStart = string2Millis(millisToStringDate(now, "yyyy-MM-dd"),
                "yyyy-MM-dd");
 
        // 一小时内
        if(now - millis <= oneHourMillis && now - millis > 0l) {
            String m = millisToStringShort(now - millis, false, false);
            return"".equals(m) ? "1分钟内": m + "前";
        }
 
         // 大于今天开始开始值,小于今天开始值加一天(即今天结束值)
        if(millis >= todayStart && millis <= oneDayMillis + todayStart) {
            return"今天 "+ millisToStringDate(millis, "HH:mm");
        }
 
         // 大于(今天开始值减一天,即昨天开始值)
        if(millis > todayStart - oneDayMillis) {
            return"昨天 "+ millisToStringDate(millis, "HH:mm");
        }
 
        longthisYearStart = string2Millis(millisToStringDate(now, "yyyy"),
                "yyyy");
         // 大于今天小于今年
        if(millis > thisYearStart) {
            returnmillisToStringDate(millis, "MM月dd日 HH:mm");
        }
 
        returnmillisToStringDate(millis, "yyyy年MM月dd日 HH:mm");
    }

字符串解析成毫秒数

1
2
3
4
5
6
7
8
9
10
11
publicstaticlongstring2Millis(String str, String pattern) {
        SimpleDateFormat format = newSimpleDateFormat(pattern,
                Locale.getDefault());
        longmillis = 0;
        try{
            millis = format.parse(str).getTime();
        } catch(ParseException e) {
            Log.e("TAG", e.getMessage());
        }
        returnmillis;
    }

手机号码正则


1
publicstaticfinalString REG_PHONE_CHINA = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";

邮箱正则

最后一点,就是使用Android测试工具,会测试Android程序bug更好,公司不想花钱请测试人员,Java程序使用junit测试工具,Android呢?----UI antomator,appium,monkeyrunner等Android测试工具.


  最后一点,就是感谢上帝,共享源代码


end

adiOS


2021最新Android笔试题总结美团Android岗职能要求

2021最新Android笔试题总结美团Android岗职能要求

image

优秀的战士需要出色的剑才能战斗。同样,在现代IT中,每个编码人员都需要最好的Android开发人员工具来提高他们的技能和效率。在Android应用程序开发这个残酷的竞争行业中,只有优秀的开发人员才能生存下去。您需要向客户展示您拥有最佳技术和资源。

它不仅仅是展示您的设备以吸引客户,还要确保您的Android应用程序在高效的时间线内以最高质量构建。那么哪些是最好的Android开发者工具?嗯,那里有很多工具,但并非所有工具都足够好。在不浪费任何时间的情况下,让我们来看看最好的Android应用开发工具。

Android开发面试的几部分

1、基础知识

基础知识包括几个部分:Java(JDK、JVM)、Android、数据结构和算法、计算机基础、设计模式,有的还会问Flutter。

2020Android面经:教你增加拿到BAT等大厂offer几率( 面试技巧)

Java部分:

不太推荐这部分只看博客,因为很多博客并不系统也不完整,推荐完整看一遍《深入理解Java虚拟机》这本书,基本上这里面涵盖了JVM相关的所有面试问题,包括内存分区、GC机制、内存模型、锁、字节码、类加载等。JDK的部分会杂一些,基本上可以归类为几种:容器(必问HashMap、CurrentHashMap、ArrayList等)、线程池(必问)、注解、同步工具、动态代理、notify/wait/sleep。这部分可以从一些JDK相关的文章中去整理一遍。

Android部分:

这部分必问的是Handler机制、触摸事件传递、四大组件启动流程、View绘制流程、Binder机制、生命周期。经典的问题比如:

触摸事件一定是先传递到Activity然后才传递给View的么?

  • 如果要在Application的onCreate生命周期之前执行一些逻辑,可以放在哪里?
  • draw、onDraw、dispatchDraw执行的顺序如何?
  • View真正显示是在onResume之前还是之后?
  • Activity中嵌套有一个Fragment,startActivity之后,
  • Activity和Fragment的生命周期顺序是如何的?

数据结构与算法:

这部分在客户端开发中考察的比例会小一些,不过也有很多公司非常重视手写代码。经常碰到的问题有二叉树遍历、有序二维数组查找,排序等,基本上以《剑指offer》或者《编程之美》为准就可以了。

计算机基础:

这部分涵盖的比较杂,比如计算机网络(7层网络模型、Http和Https)、git工作流和命令、计算机缓存策略、UML、信息安全等等。

设计模式:

在自己的项目中一定要多总结和使用设计模式,面试官问你使用过什么设计模式的时候,如果能回答出比如状态模式、责任链、装饰等等,肯定会加分不少。如果只是回答单例和建造者模式,那就会逊色多了。这部分推荐看《研磨设计模式》。

开源项目:

这个也是必问的环节之一,建议从自己项目使用的开源项目入手,跟着代码,画一遍流程图和架构图,仔细体会这里面设计的优点和缺点。

比如OkHttp、LeakCanery等等,这些都可以。开源项目根据面试官的个人经验不同,问的深入程度也不一样,这时候就需要你个人的一些引导技巧,比如面试官可能不是很熟悉这个框架,这时候你能主动表达出这里面设计的一些细节,也能给面试官留下不错的印象。切忌在简历上写只是用过但是没看过源码的框架。

2020Android面经:教你增加拿到BAT等大厂offer几率( 面试技巧)

重点项目经历

小厂的项目往往追求业务快速迭代上线,相对比较能容忍一些技术难点,但是这些技术难点往往是能体现一个人技术能力的。所以你如果在小厂,就不能过分陷入到业务中,必须跳出来找一些有亮点的技术点,做好做精。这些技术点才是一个程序员安身立命的本钱,否则公司业务陷入困境了,你的价值也就不复存在了。在如何准备面试中项目这部分,小编有几点心得:

分成三部分:

  1. 为什么要做这个点
  2. 考虑了什么因素并且是如何做的
  3. 取得了什么效果(比如启动速度从1000ms降到200ms,这种具体的数据)

可以把几个小点串起来,这样可以显得更系统和全面一些,比如代码优化和性能优化结合在一起讲。

小厂的一些优化点往往只能做性价比最高的一部分,这时候还可以讲一下业界还有哪些更优的方案

最好再准备一下“如果你没有离职项目中还有有什么不足和可以优化的地方?”(笔者面试中碰到过几次这样的问题)

**小厂跳大厂,技术基础可以准备得很充分,但是项目绝对是一个弱项。**大厂一个App可能就有几十上百人在做,小厂很多所谓的优化在大厂的面试官眼中可能都是他们很早就做过了,这时候项目本身做了哪些东西并不是太重要,需要着重体现你解决问题的思路以及你是一个有追求的程序员,至少你要能做到业界的平均水平,这样才能有机会通过面试。

技术以外的东西

大厂面试中技术以外的部分也是非常重要的一部分,这部分大多是一些软技能:

自我驱动和追求

这部分主要是在平时工作中的一些自我驱动学习和自主追求一些技术能力,而不仅仅是完成需求就可以了,这样才能体现出你个人的成长性和潜力。

沟通和协作

这部分会评估你未来在团队的融入程度以及跟团队跨团队协作和沟通的能力。比如你在工作中是否能正确看待和解决一些跨部门沟通的问题。

我的面经总结

主要分为四个方面:

Android

  1. Android各个版本的特性(例如6.0,动态权限)
  2. ANR的原因
  3. Android中进程间通信的方式和对比
  4. AsyncTask的实现原理及优缺点
  5. Handler机制
  6. 自定义view三部曲,流程(或者实际场景题,例如实现一个表盘,怎么做)
  7. FrameWork层次结构
  8. Activity生命周期(A跳转到B,A和B中生命周期的执行顺序)
  9. Dvlik、ART、JVM三种虚拟机的区别
  10. 能不能在非UI线程更新UI
  11. Service和IntentService的区别
  12. Thread和HandlerThread的区别
  13. Touch事件分发机制
  14. 怎么知道一个Activity是否发生了泄露
  15. 从点击一个APP图标开始发生了什么
  16. Activity启动流程(源码看一遍,能大致讲出来就行)
  17. Activity启动模式(四种)
  18. Intent启动Activity的方式
  19. onStart和onResume的区别
  20. LruCache以及实现原理
  21. Android中的MVC、MVP、MVVM的区别和联系
  22. Parcelable和Serializable的区别
  23. Service生命周期
  24. Service的启动方式及区别
  25. Binder机制
  26. onSaveInstanceState和onRestoreInstanceState的调用时机(需要理解,一般会出个场景让你判断)
  27. onNewIntent的调用时机(同上)
  28. Android中的性能优化和内存优化
  29. RxJava说一下(常用操作符,背压,线程切换原理,实际应用)
  30. RxAndroid说一下
  31. EventBus原理
  32. Volley原理
  33. OkHttp原理
  34. Retrofit原理
  35. RxJava和Retrofit结合
  36. Dagger2原理
  37. 组件化介绍一下(为什么要有组件化,怎么解决合并冲突问题)
  38. 快应用介绍一下
  39. 屏幕适配的常用方法
  40. Service、进程保活
  41. Glide原理
  42. AsyncTask原理
  43. SurfaceView和普通View的区别
  44. 依赖注入框架的实现原理
  45. Git项目管理
  46. 什么是三级缓存

Java

  1. 常用的设计模式(掌握常见的大约10种就Ok)
  2. ThreadLoal的作用及原理
  3. 线程的实现方式和实现原理
  4. 线程的状态,怎么创建线程,线程状态之间的转化(例如什么情况下线程会由运行变为阻塞)
  5. 进程的状态
  6. transient关键字的作用
  7. sleep()和wait()的区别
  8. hashcode()方法的作用
  9. ==和equals()方法区别(为什么重写了equals()方法还要重写hashcode方法)
  10. Java的三大特性
  11. 接口和抽象类的区别
  12. 进程和线程的区别
  13. 线程池各参数的含义,以及四种常用线程池的应用场景
  14. 阻塞队列的特点和原理和应用场景
  15. yield()方法的作用
  16. 多线程锁的类型,ReentrantLock,Lock,synchrnoized关键字等
  17. volatile关键字的作用(在单例中的作用)
  18. 闭包的概念
  19. 泛型(什么是类型擦除,为什么要有泛型)
  20. 锁优化方式(自旋锁、自适应自旋、锁消除、锁粗化、偏向锁)
  21. 类执行顺序
  22. StringBuffer和StringBuilder的区别
  23. HashMap的实现原理(最好是1.8之前和1.8之后都要知道)
  24. ConcurrentHashMap的实现原理(同1.8之前和之后都要知道)
  25. ArrayList和LinkedList的区别
  26. Java内存区域(堆区和栈区的区别,什么是运行时常量池)
  27. 装箱和拆箱
  28. OOM异常
  29. GC机制(GC区域,如何判断一个对象是否可以回收,GC算法,GC策略)
  30. 类加载机制(类加载过程双亲委派模型)
  31. Java内存模型(主内存与工作内存)
  32. 原子性(AtomicInteger原理)、可见性(volatile关键字)、有序性,如何保证
  33. 先行发生原则(happens-before原则)
  34. 线程安全的实现方法
  35. 锁的类型(悲观锁,乐观锁CAS算法,公平锁,非公平锁等怎么实现)
  36. StringBuffer和StringBuilder区别
  37. 注解(自定义注解)
  38. 四种引用的概念和区别

计算机网络

  1. HTTP协议(头结构,几种请求方法,缓存机制,各版本的区别)
  2. HTTPS协议,和HTTP协议的区别,密钥协商的过程,SSL机制
  3. 对称加密和非对称加密
  4. TCP和UDP的区别
  5. TCP的三次握手、四次挥手(包括状态码,以及四次挥手后为什么要等待2*TIME_WAIT)
  6. TCP拥塞控制机制
  7. TCP滑动窗口机制
  8. TCP超时重传机制
  9. DNS协议
  10. 五层网络模型(物理层,数据链路层,网络层,传输层,应用层)
  11. IP头部
  12. 在浏览器地址输入一个网址,发生了什么

数据结构及算法

  1. 链表(单向链表,双向链表)
  2. 数组(旋转数组)
  3. 队列(双端队列,阻塞队列)
  4. 堆(大根堆、小根堆)
  5. 二叉树(完全二叉树、满二叉树、二叉排序树、平衡二叉树,红黑树)(深度遍历,层序遍历)
  6. 图(重点掌握图的深度优先遍历和广度优先遍历)
  7. 七大排序算法(冒泡,选择,插入,归并排序,快速排序,堆排序,希尔排序)
  8. 剑指offer上的算法题(能够对照目录,一看题目,能有思路,就ok)
  9. 二分查找
  10. 两个栈实现一个队列,两个队列实现一个栈
  11. 海量数据排序
  12. topK问题,有1千万个数,怎么快速找出最大的100个
  13. 合并两个有序数组,合并两个有序链表
  14. 杨氏矩阵(横向递增,纵向递增)中如何找到指定的数字
  15. 翻转一句话,例如I am 3 years old,翻转后,old years 3 am I
  16. 有10亿条数据,现在只有200M内存,怎么找出这10亿条数据中出现次数最多的100条数据

题外话

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊~

最后如果马化腾把腾讯给你一天,你会来做什么?欢迎评论区讨论。

识点里往死里凿,你也能应付如流啊~**

到此这篇关于2021最新Android笔试题总结美团Android岗职能要求的文章就介绍到这了,更多相关Android笔试题总结内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:
  • Android自定义轮播图效果
  • Android自定义RecyclerView Item头部悬浮吸顶
  • Android端权限隐私的合规化处理实战记录
  • Android实现简单点赞动画
  • Android实现声音采集回声与回声消除
  • Android集成Flutter
  • Android多返回栈技术
  • Android LayoutParams使用案例详解

Android Studio 3,阿里+头条+腾讯等大厂Android笔试题目分享

Android Studio 3,阿里+头条+腾讯等大厂Android笔试题目分享


在创建应用程序项目后立即启用基础模块,如下所示:

    1. 通过 从菜单栏中选择View > Tool Windows > Project来打开“ 项目”面板。
    1. 右键单击通常称为“应用程序”的基本模块,然后选择Refactor > Enable Instant Apps Support.>“启用即时应用程序支持”。
  • 3.在出现的对话框中,从下拉菜单中选择基本模块。
  • 4.单击确定。

注意:从“Create New Project ”向导中即时启用基本应用程序模块的选项已删除。

五、在APK分析器中反混淆类和方法字节码

使用APK Analyzer检查DEX文件时,可以按以下步骤对类和方法字节码进行模糊处理:

  • 1.从菜单栏中选择 Build > Analyze APK
  • 2.在出现的对话框中,导航到要检查的APK,然后选择它。
  • 3.点击打开。
  • 4.在 APK 分析器中,选择要检查的 DEX 文件。
  • 5.在DEX文件查看器中,为要分析的APK 加载 ProGuard 映射文件。
  • 6.右键单击要检查的类或方法,然后选择Show bytecode

六、Native Tooling

以下更新支持 Android Studio 中的本机(C / C ++)开发。

1. Kotlin支持

以前由 Java 支持的 Android Studio 支持的 NDK 功能,现在 Kotlin 也能够支持了:

  • 从JNI声明导航到 C / C ++ 中的相应实现函数。将鼠标悬停在托管源代码文件中行号附近的C或C ++项目标记上,可以查看此映射。
  • 为JNI声明自动创建存根实现功能。首先定义JNI声明,然后在C / C ++文件中键入“ jni”或方法名称来激活。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S7podVAc-1646112221627)(https://user-gold-cdn.xitu.io/2020/2/25/1707a963ed735bd4?imageslim)]

  • 未使用的 native implementation functions 在源代码中突出显示为警告。缺少实现的JNI声明也将突出显示为错误。
  • 重命名(重构) native implementation functions 时,所有对应的JNI声明都会更新。重命名 JNI 声明以更新 native implementation functions 功能。
  • 对隐式绑定的 JNI 实现进行签名检查。

2. 其他JNI改进

Android Studio中 的代码编辑器现在支持更无缝的 JNI 开发工作流程,包括改进的类型提示,自动完成,检查和代码重构。

3. 重新加载本机库的APK

在 IDE 外部更新项目中的 APK 时不再需要创建新项目。Android Studio会检测APK 中的更改,并提供重新导入 APK 的选项。

七、Attach Kotlin-only APK Sources

现在分析和调试预建的APK时,可以附加仅 Kotlin 的外部 APK 源:developer.android.com/studio/debu… 。

八、内存探查器中的泄漏检测

现在,在 Memory Profiler 中分析堆转储时,可以过滤 Android Studio 认为可能表明应用程序中的内存泄漏 ActivityFragment 实例的性能分析数据。

筛选器显示的数据类型包括:

  • Activity 实例已被销毁,但仍在引用中。
  • Fragment 实例无效 FragmentManager 但仍在引用中。

在某些情况下,例如以下情况,过滤器可能会产生误报:

  • A Fragment 已创建,但尚未使用。
  • 一个 Fragment 被缓存,但不作为的一部分FragmentTransaction

要使用此功能,请先捕获堆转储将堆转储文件导入Android Studio。要显示可能正在泄漏内存的碎片和活动,请在“内存探查器”的堆转储窗格中选中“活动/碎片泄漏”复选框。(筛选堆转储以防止内存泄漏。)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ow5nLNem-1646112221628)(https://user-gold-cdn.xitu.io/2020/2/25/1707a9867b1d210a?imageView2/0/w/1280/h/960/ignore-error/1)]

九、模拟器

Android Studio 3.6 可以利用 Android Emulator 29.2.7 及更高版本中包含的多个更新,如下所述。

1. 改进的位置支持

Android Emulator 29.2.7 及更高版本为仿真GPS坐标和路线信息提供了额外的支持。当打开 Emulators Extended controls, 控件时, Location 选项卡中的选项现在组织在两个选项卡下:“Single points”“Routes”

Single points

在 Single points 标签中,可以使用 Google Maps Webview 搜索感兴趣的点,就像在手机或浏览器上使用Google Maps一样。搜索或单击地图中的位置时,可以通过选择地图底部附近的保存点来保存位置。所有保存的位置都列在扩展控件窗口的右侧 。

要将“模拟器”位置设置在地图上选择的位置,请单击 Extended controls 窗口右下角附近的“ 设置位置”按钮 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vaVNhSnN-1646112221628)(https://user-gold-cdn.xitu.io/2020/2/25/1707a99384fd6c2b?imageView2/0/w/1280/h/960/ignore-error/1)]

Routes

与 Single points 标签类似, Routes 标签提供了Google Maps Web视图,可用于在两个或多个位置之间创建路线。要创建和保存路线,请执行以下操作:

  • 1.在地图视图中,使用文本字段搜索路线中的第一个目的地。
  • 2.从搜索结果中选择位置。
  • 3.选择 Navigate 按钮

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGiAaB0Z-1646112221629)(https://user-gold-cdn.xitu.io/2020/2/25/1707a99680e30256?imageView2/0/w/1280/h/960/ignore-error/1)]

  • 4.从地图上选择路线的起点。
  • 5.(可选)点击添加目的地,将更多停靠点添加到路线中。
  • 7.为路线指定名称,然后点击保存。

要按照保存的路线模拟模拟器,请从 Saved routes 列表中选择路线,然后单击 Extended controls 窗口右下角附近的 Play route 。要停止模拟,请点击停止路线。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwZ7nTvi-1646112221629)(https://user-gold-cdn.xitu.io/2020/2/25/1707a9a886664041?imageView2/0/w/1280/h/960/ignore-error/1)]

要按照指定的路径连续仿真模拟器,请启用重复播放旁边的开关。要更改仿真器遵循指定路线的速度,请从 Playback speed 下拉列表中选择一个选项。

2. 多显示器支持

- 3.6.1 新的更新中移除了多重预览功能已删除,4.0 版本才正式开放。

Android模拟器现在允许将应用程序部署到支持可自定义尺寸的多个显示器,并可以帮助测试支持多窗口和多显示器的应用程序 。在虚拟设备运行时,最多可以添加两个以下显示:

  • 1.打开扩展控件,然后导航到 displays 选项卡。
  • 2.通过单击添加 Add secondary display 来添加另一个显示。
  • 3.在 Secondary displays 菜单中,执行以下一项操作:
  • a.选择一种预设的宽高比
  • b.选择自定义并为自定义显示设置高度,宽度和dpi。
  • 4.(可选)单击 Add secondary display 以添加第三显示。
    1. 单击 Apply changes,将指定的显示添加到正在运行的虚拟设备。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4Drs8xS-1646112221629)(https://user-gold-cdn.xitu.io/2020/2/25/1707b1b9a6b95206?imageView2/0/w/1280/h/960/ignore-error/1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0PMSLCdK-1646112221629)(https://user-gold-cdn.xitu.io/2020/2/25/1707b1a32f9203c3?imageView2/0/w/1280/h/960/ignore-error/1)]

3. 适用于Android Automotive OS的新虚拟设备和项目模板

使用 Android Studio 创建新项目时,现在可以从 Create New Project 向导的 Automotive 选项卡中的三个模板中进行选择:No ActivityMedia serviceMessaging service

对于现有项目,可以通过从菜单栏中选择 File > New > New Module,然后选择 Android Automotive 来添加对 设备的支持。然后, Create New Module 向导将指导使用 Android Automotive 项目模板之一创建新模块。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BAQuIUjd-1646112221630)(https://user-gold-cdn.xitu.io/2020/2/25/1707a9c2f4e6f199?imageView2/0/w/1280/h/960/ignore-error/1)]

此外,现在可以通过在 Virtual Device Configuration 向导的Automotive选项卡中选择以下选项之一,为Android Automotive OS设备 create an Android Virtual Device (AVD)

  • 1.Polestar 2:创建一个模拟Polestar 2主机的AVD。
  • 2.Automotive (1024p landscape):为一般的1024 x 768 px Android车载主机创建一个AVD。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bry5x1vK-1646112221630)(https://user-gold-cdn.xitu.io/2020/2/25/1707a9dff23e2dbb?imageView2/0/w/1280/h/960/ignore-error/1)]

十、可恢复的SDK下载

现在,当使用 SDK Manager 下载 SDK 组件和工具时,Android Studio 现在允许恢复被中断(例如由于网络问题)的下载,而无需从头开始重新开始下载。当互联网连接不可靠时,此增强功能对于大型下载(例如Android模拟器或系统映像)特别有用。

另外,如果在后台运行SDK下载任务,则现在可以使用状态栏中的控件暂停或继续下载。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOZ0msHM-1646112221630)(https://user-gold-cdn.xitu.io/2020/2/25/1707a9e3466d8201?imageView2/0/w/1280/h/960/ignore-error/1)]

状态栏中的后台下载任务,带有新控件,可暂停或继续下载。

十一、Win32已弃用

Windows 32 位版本的Android Studio在 2019 年 12 月之后将不再获得更新,并且在 2020 年 12 月之后将不再获得支持。开发者可以继续使用 Android Studio。但是,要接收其他更新,请将工作站升级到 Windows 的 64 位版本。

十二、优化 Gradle 同步时间的新选项

在以前的版本中,Android Studio 在 Gradle Sync 期间检索了所有 Gradle 任务的列表。对于大型项目,检索任务列表可能会导致同步时间变慢。

请将工作站升级到 Windows 的 64 位版本。

十二、优化 Gradle 同步时间的新选项

在以前的版本中,Android Studio 在 Gradle Sync 期间检索了所有 Gradle 任务的列表。对于大型项目,检索任务列表可能会导致同步时间变慢。

android从入门到精通!阿里内部Android笔记火爆IT圈,挥泪整理面经

android从入门到精通!阿里内部Android笔记火爆IT圈,挥泪整理面经

Android开发前景怎么样?

很多人说Android开发前景越来越差了 我觉得这个回答是片面的

首先Android应用开发前景差是在最近两年出现的,也就是从2018开始,从那时起移动端的程序员已经慢慢出现供大于求的局面,本人作为移动端开发,深知这一点。

然而也必须说明一点,不论是Android开发还是iOS开发,虽然都出现了相关的程序员供大于求的情况,但市场仍然是有需求的,特别是对资深的开发人员及拥有相关底层开发知识的应用程序员市场及发展还是很多的;这里所讲的就业难都是相对于初级开发人员。

为什么会在18年出现应用端就业难?这是由于在前几年App风盛行,那几年只要是个和互联网的公司要是没个自己的App那都不好意思叫互联网公司,所以一般的互联网公司成立之初就会着手开发自己的App,不管是否是刚需,但市场终究是严峻的;App虽然好,能快速开展本公司的业务,但App的运营成本还是很高的,一个App在早期就开发团队来说一般都是需要至少Android开发一人,iOS开发一人,后台开发2人以上,还有UI及产品等等,当然最主要的问题是一般的公司对于这种模式都会面临回报周期长的问题,App开发完了还面临着推广的问题,市场抢占不了,也许之前的都会付之东流。于是在早期很多学校特别是培训机构就针对市场推出了应用端的培训课程,短到三个月长到半年的课程,于是乎每一个月都会有成千上万的应用端走向市场,所以时间一长,市场慢慢趋于稳定。应用端的人员慢慢就变得供大于求了,这也是慢慢出现应用端求职难的问题了。

以上说的是其中一方面,成本高和其他低成本,低运营的技术相继出现也是应用端求职难现象的助推手。

这里主要讲下2016年出现的小程序对于App的冲击,原生App有着开发周期长,运营成本高及回报周期长的特点,所以近年来很多公司都慢慢削去了应用端从而将资金放在其他部分,直到小程序的出现,开发成本一下就可以降下不少,为什么呢,因为小程序的开发语言可以由前端的开发人员承担,而且开发一个小程序不需要维护两套代码也就是不需要像原生App一样,需要Andoid端和iOS端,因为小程序是运行在微信中的,所以只要开发人员维护一套代码就够了,这大大的降低了前期的开发成本,其他细节在这就不细讲了,总之小程序的出现不同程度上更加冲击了应用端的竞争市场,从而也使得应用端就业竞争更加激烈。

当然市场还是需要应用端的人才的,这里所说的人才指的是那些会顺应技术发展和时代的有着不断学习力的开发人员,对于那些初级的程序员市场终究对于他们是残酷的,所以提升自己的竞争力在任何职业任何时候都是非常重要的,只有这样才不会被市场所抛弃。

就先写到这,手机码字很痛苦,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我 我会定期分享一些关于Android进阶方面的知识,也会分享一下最新的面试题~

快看看下面这些面试真题,是不是都能答得上来?

  1. HttpUrlConnection 和 okhttp关系?
  2. okhttp的特点、缺点,用到了哪些设计模式?
  3. 说下okhttp原理
  4. okhttp如何处理网络缓存的
  5. okhttp中的线程池的参数为什么要这么设置?
  6. 自己去设计网络请求框架,怎么做?
  7. 说下Glide的优点
  8. Glide中的三级缓存是哪三级?
  9. Glide内存缓存如何控制大小?
  10. 知道活动缓存怎么做的吗?为什么要这样划分层级呢?好处是什么?
  11. ……

近期有出去面试过的朋友就该知道,现在的面试从头到尾都是比较有深度的技术问题,虽然那些问题看上去在网上都能查到相关的资料,但面试官基本都是根据你的回答持续深入,如果没有真正对技术原理和底层逻辑有一定的了解是无法通过的。

这也是广大Android开发者感觉 “面试造火箭,工作拧螺丝” 的来源。

img

所以,从根本上来讲,技术的高低才是决定你是否能够通过面试的核心原因。如果你面试都能对答如流的话,面试10家公司,至少能拿到7家offer。而且一般都能给出较高的薪水和福利待遇。

最后说一下我的学习路线

其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:

  1. 架构师筑基必备技能
  2. Android框架体系架构(高级UI+FrameWork源码)
  3. 360°Androidapp全方位性能调优
  4. 设计思想解读开源框架
  5. NDK模块开发
  6. 移动架构师专题项目实战环节
  7. 移动架构师不可不学习微信小程序
  8. 混合开发的Flutter

Android学习的资料

我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。需要的小伙伴可以私信我【进阶】我免费分享给大家,或者直接点击下面链接领取,谢谢大家这么久以来的支持。

Android学习PDF+架构视频+面试文档+源码笔记

如果你有其他需要的话,也可以在GitHub上查看,下面的资料也会陆续上传到Github

330页PDF Android学习核心笔记(内含上面8大板块)

Android学习的系统对应视频

总结

我希望通过我自己的学习方法来帮助大家去提升技术:

  • 1、多看书、看源码和做项目,平时多种总结

  • 2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理

  • 3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习

  • 4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!

    进阶学习资料领取方式:GitHub

BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!

Android多线程断点续传下载原理及实现,2021阿里Android笔试总结

Android多线程断点续传下载原理及实现,2021阿里Android笔试总结

  • @param url 下载url
  • @param callback 获取文件长度CallBack
    */
    void getTotalSize(String url, NetCallback callback);

/**

  • 获取InputStream
  • @param url 下载url
  • @param start 开始位置
  • @param end 结束位置
  • @param callback 获取字节流的CallBack
    */
    void getStreamByRange(String url, long start, long end, NetCallback callback);
    }

子任务实现

成员变量及解释

我们先从上到下,从子任务开始实现。在我的设计中,它具有如下的成员变量:

@Entity
public class SubDownloadTask implements Runnable {
public static final int BUFFER_SIZE = 1024 * 1024;
private static final String TAG = SubDownloadTask.class.getSimpleName();

@Id
private Long id;
private String url; // 文件下载的 url
private String taskTag; // 父任务的 Tag
private long taskSize; // 子任务大小
private long completedSize; // 子任务完成大小
private long startPos; // 开始位置
private long currentPos; // 当前位置
private long endPos; // 结束位置
private volatile int status; // 当前下载状态
@Transient
private SubDownloadListener listener; // 子任务下载监听,主要用于提示父任务
@Transient
private File saveFile; // 要保存到的文件


}

由于这里的数据库的操作是用 GreenDao 实现,因此这里有一些相关注解,各位可以忽略。

InputStream 获取

可以看到,子任务是一个 Runnable,我们可以通过其 run 方法开始下载,这样就可以通过如 ExecutorService 来开启多个线程执行子任务。

我们看到其 run 方法:

@Override
public void run() {
status = DownloadStatus.DOWNLOADING;
DownloadManager.getInstance()
.getHttpHelper()
.getStreamByRange(url, currentPos, endPos, new NetCallback() {
@Override
public void onResult(InputStream inputStream) {
listener.onSubStart();
writeFile(inputStream);
}
@Override
public void one rror(String message) {
listener.onSubError(“文件流获取失败”);
status = DownloadStatus.ERROR;
}
});
}

可以看到,我们获取了其从 currentPosendPos 端的字节流,通过其 Response Body 拿到了它的 InputStream,然后调用了 writeFile(InputStream) 方法进行文件的写入。

文件写入
接下来看到 writeFile 方法:

private void writeFile(InputStream in) {
try {
RandomAccessFile file = new RandomAccessFile(saveFile, “rwd”); // 通过 saveFile 建立RandomAccessFile
file.seek(currentPos); // 跳转到对应位置

byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
// 循环读取 InputStream,直到暂停或读取结束
if (status != DownloadStatus.DOWNLOADING) {
// 状态不为 DOWNLOADING,停止下载
break;
}

int offset = in.read(buffer, 0, BUFFER_SIZE);
if (offset == -1) {
// 读取不到数据,说明读取结束
break;
}

// 将读取到的数据写入文件
file.write(buffer, 0, offset);
// 下载数据并在数据库中更新
currentPos += offset;
completedSize += offset;
DownloadManager.getInstance()
.getDbHelper()
.update(this);
// 通知父任务下载进度
listener.onSubDownloading(offset);
}
if(status == DownloadStatus.DOWNLOADING) {
// 下载完成
status = DownloadStatus.COMPLETED;
// 通知父任务下载完成
listener.onSubComplete(completedSize);
}
file.close();
in.close();
} catch (IOException e) {
e.printstacktrace();
listener.onSubError(“文件下载失败”);
status = DownloadStatus.ERROR;
resetTask();
}
}

具体流程可以看代码中的注释。可以看到,子任务实际上就是循环读取 InputStream,并写入文件,同时将下载进度同步到数据库。

父任务实现

父任务也就是我们具体的下载任务,我们同样先看到成员变量:

public class DownloadTask implements SubDownloadListener {
private static final String TAG = DownloadTask.class.getSimpleName();
private String tag; // 下载任务的 Tag,用于区分不同下载任务
private String url; // 下载 url
private String savePath; // 保存路径
private String fileName; // 保存文件名
private DownloadListener listener; // 下载监听
private long completeSize; // 下载完成大小
private long totalSize; // 下载任务总大小
private int status; // 当前下载进度
private int threadNum; // 线程数(由外部设置的每个任务的下载线程数)
private File file; // 保存文件
private List subTasks; // 子任务列表
private ExecutorService mExecutorService; // 线程池,用于执行子任务


}

下载功能

对于一个下载任务,可以通过 download 方法开始执行:

public void download() {
listener.onStart();
subTasks = querySubTasks();
status = DownloadStatus.DOWNLOADING;
if (subTasks.isEmpty()) {
// 是新任务
downloadNewTask();
} else if (subTasks.size() == threadNum) {
// 不是新任务
downloadExistTask();
} else {
// 不是新任务,但下载线程数有误
listener.onError(“断点数据有误”);
resetTask();
}
}

可以看到,我们先将子任务列表从数据库中读取出来。

  • 如果子任务列表为空,则说明还没有下载记录,也就是说是一个新任务,调用 downloadNewTask 方法。
  • 如果子任务列表大小等于线程数,则说明其不是新任务,调用 downloadExistTask 方法。
  • 如果子任务列表大小不等于线程数,说明当前的下载记录已不可用,于是重置下载任务,从新下载。

下载新任务

我们先看到 downloadNewTask 方法:

DownloadManager.getInstance()
.getHttpHelper()
.getTotalSize(url, new NetCallback() {
@Override
public void onResult(Long total) {
completeSize = 0L;
totalSize = total;
initSubTasks();
startAsyncDownload();
}

@Override
public void one rror(String message) {
error(“获取文件长度失败”);
}
});

可以看到,获取到总长度后,通过调用 initSubTasks 方法,对子任务列表进行了初始化(计算子任务长度等),然后调用了 startAsyncDownload 方法后通过 ExecutorService 运行子任务进入子任务进行下载。

我们看到 initSubTasks 方法:

private void initSubTasks() {
long averageSize = totalSize / threadNum;
for (int taskIndex = 0; taskIndex < threadNum; taskIndex++) {
long taskSize = averageSize;
if (taskIndex == threadNum - 1) {
// 最后一个任务,则 size 还需要加入剩余量
taskSize += totalSize % threadNum;
}
long start = 0L;
int index = taskIndex;
while (index > 0) {
start += subTasks.get(index - 1).getTaskSize();
index–;
}
long end = start + taskSize - 1; // 注意这里
SubDownloadTask subTask = new SubDownloadTask();
subTask.setUrl(url);
subTask.setStatus(DownloadStatus.IDLE);
subTask.setTaskTag(tag);
subTask.setCompletedSize(0);
subTask.setTaskSize(taskSize);
subTask.setStartPos(start);
subTask.setCurrentPos(start);
subTask.setEndPos(end);
subTask.setSaveFile(file);
subTask.setListener(this);
DownloadManager.getInstance()
.getDbHelper()
.insert(subTask);
subTasks.add(subTask);
}
}

可以看到就是计算每个任务的大小及开始及结束点的位置,这里要注意的是 endPos 需要 -1,否则各个任务的下载位置会重叠,并且最后一个任务会多下载一个字节导致如文件损坏等影响。具体原因就是比如一个大小为 500 的文件,则应当是 0-499 而不是 0-500。

恢复旧任务

接下来我们看看 downloadExistTask 方法:

private void downloadExistTask() {
// 不是新任务,且下载线程数无误,计算已下载大小
completeSize = countCompleteSize();
totalSize = countTotalSize();
startAsyncDownload();
}

这里其实很简单,遍历子任务列表计算已下载量及总任务量,并调用 startAsyncDownload 开始多线程下载。

执行子任务

具体执行子任务我们可以看到 startAsyncDownload 方法:

private void startAsyncDownload() {
for (SubDownloadTask subTask : subTasks) {
if (subTask.getCompletedSize() < subTask.getTaskSize()) {
// 只下载没有下载结束的子任务
mExecutorService.execute(subTask);
}
}
}

可以看到,这里其实只是通过 ExecutorService 执行对应子任务(Runnable)而已。

####暂停功能
我们接下来看到 pause 方法:

public void pause() {
stopAsyncDownload();
status = DownloadStatus.PAUSE;
listener.onPause();
}

可以看到,这里只是调用了 stopAsyncDownload 方法停止子任务。

看到 stopAsyncDownload 方法:

private void stopAsyncDownload() {
for (SubDownloadTask subTask : subTasks) {
if (subTask.getStatus() != DownloadStatus.COMPLETED) {
// 下载完成的不再取消
subTask.cancel();
}
}
}

可以看到,调用了子任务的 cancel 方法。

继续看到子任务的 cancel方法:

void cancel() {
status = DownloadStatus.PAUSE;
listener.onSubCancel();
}

这里很简单,仅仅是将下载状态设置为了 PAUSE,这样在写入文件的下一次 while 循环时便会中止循环从而结束 Runnable 的执行。

取消功能

看到 cancel方法:

public void cancel() {
stopAsyncDownload();
resetTask();
listener.onCancel();
}

可以看到和暂停的逻辑差不多,只是在暂停后还需要对子任务重置从而使得下次下载从头开始。

底层到上层的通知机制

前面提到,外部可以通过 DownloadListener 监听下载的进度,下面是 DownloadListener接口的定义:

public interface DownloadListener {
[外链图片转存中…(img-GsBDHGVe-1643606831114)]

public void cancel() {
stopAsyncDownload();
resetTask();
listener.onCancel();
}

可以看到和暂停的逻辑差不多,只是在暂停后还需要对子任务重置从而使得下次下载从头开始。

底层到上层的通知机制

前面提到,外部可以通过 DownloadListener 监听下载的进度,下面是 DownloadListener接口的定义:

public interface DownloadListener {

关于android笔记选android 笔记的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于2021最新Android笔试题总结美团Android岗职能要求、Android Studio 3,阿里+头条+腾讯等大厂Android笔试题目分享、android从入门到精通!阿里内部Android笔记火爆IT圈,挥泪整理面经、Android多线程断点续传下载原理及实现,2021阿里Android笔试总结的相关知识,请在本站寻找。

本文标签: