标签: Android开发

Android开发的经典入门教材和学习路线

Android开发书籍推荐:从入门到精通系列学习路线书籍介绍

(https://www.diycode.cc/wiki/androidbook)

很多时候我们都会不断收到新手的提问“Android开发的经典入门教材和学习路线?”、“Android 开发入门教程有哪些推荐?”等类似的问题,我们不断重复回答这些问题,这让我们萌生了做《 Android开发书籍推荐:从入门到精通系列学习路线书籍介绍》的想法,整理收集开发大牛的学习经验,以便让我们少走弯路,更快速成长。希望这个系列可以成为大家手头应对新手的好答案。

JAVA入门书籍:

《Introduction to java programming》

《Core java》中文译名《Java核心技术》 分为基础知识和高级特性

《Java核心技术(卷1):基础知识(原书第9版) [Core Java Volume I—Fundamentals (Ninth Edition)]》

《Java核心技术(卷2):高级特性(原书第9版) [Core Java, Volume II–Advanced Features]》

JAVA进阶书籍:

《Thinking in java》

《Effective Java》

汤涛推荐理由:第二本要反复多看几遍,另外Java学习还有个技巧,把各种代码检查工具报告的警告都正确处理掉,一个不漏,保证你成长飞快。

stormzhang推荐理由:公认的Java进阶必备,《Effective Java》是一本实用至上的书,78条建议,满满的干货。

袁辉辉推荐理由:《Thinking in java》:非常经典的Java书籍,有些人觉得这个书不适合初学者,可就是我看的*个本Java书,或许是当初自学Java没有高人指点,便挑选了经典书来入手。看一本经典书,*遍能理解个大概,能对整体有一个概念,这就可以了,反复多读几遍,细细咀嚼,每一遍都会有不同的领悟。

一、Android入门:

Android Training (http://hukai.me/android-training-course-in-chinese/index.html)

Android API Guides (https://developer.android.com/guide/index.html)

胡凯推荐理由:入门使用官方的这两份文档是*好不过的了,没有比这个更权威,更准确的Android学习资料了。中文书可以随便买两本入门,配合一起看看就好了。在实践的过程中多参考官方的Sample Code,多按照官方的推荐进行代码实践!

汤涛推荐理由:官方文档,权威专业,入门不二之选,正确的入门姿势,对后续的成长帮助非常之大。

《*行代码》

《疯狂Android讲义》

《Android4高级编程》

《Android编程权威指南》

徐宜生推荐理由:全面、基础,内容丰富!基础类型的书只要看一本就够了,用来全面了解知识体系和结构,不用全部精读,只需要有概念即可。

CJJ推荐理由:《*行代码》作者郭霖,看了郭霖很多博客文章,每一篇都写的很详细,也很用心。这本书大概浏览了一遍,内容浅显易懂,非常适合初学者!

任玉刚推荐理由:《*行代码》作者郭霖,手把手教你入门,清晰易懂。

袁辉辉推荐理由:《疯狂Android讲义》正是这样一本书,也是我看过的*本Android书籍,书中并没有深奥的理论,有大量的实例,边看的过程中,边在电脑面前跟着敲了一遍实例代码,*好能做到理解性地敲,而非看一个单词再电脑面前敲一个。我大概花了一周时间把这本书看完并把大部分的实例代码都亲手敲了一遍。《*行代码》作者郭霖,网上有不少人都推荐这本书作为Android入门书,我大概扫了一遍,知识点较基础,作者文笔不错,书中还穿插了不少打怪涨经验升级的片段,比较风趣。

二、Android进阶:

《App研发录》

《Android群英传》

《深入理解Android》

《Android开发艺术探索》

《Android系统源代码情景分析》

袁辉辉推荐理由:《深入理解Android》邓凡平,作者功力深厚,以情景为分支,从framework源代码层面来,深入分析Android系统,非常适合高级应用开发工程师和系统工程师阅读。《Android系统源代码情景分析 》罗升阳,对Android系统的理解非常深,老罗知识体系很全,文章从app/framework/native/kernel等全方面剖析,这是Android界的尽人皆知的大牛,“老罗栽树,大家乘凉”,非常值得一看,前提要是有扎实基础。

stormzhang推荐理由:《Android开发艺术探索》这是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点,Android开发进阶值得拥有!

任玉刚推荐理由:《Android开发艺术探索》,作者任玉刚。分析android核心知识点,直指高级工程师进阶要点!(作为艺术探索一书的读者,我也是激励推荐的!!非常赞的一本书!)

《Clean Code》

《Clean Coder》

汤涛推荐理由:进阶是个大话题,只看一两本是不够的,甚至只看书也是不够的,能进阶到什么程度只能靠自己积*主动的积累。 硬要推荐的话,就不说 Android 的书了,市面上几本热门书都可以看看。这里推荐的两本,同一人所写,都有中文译本。教你代码怎么写得更好,以及怎么做一个更职业的程序员。

《HeadFirst设计模式》

《重构:改善既有代码的设计》

胡凯推荐理由:在大量实践Android程序之后,我们需要会分辨哪种写法是更优秀的,通过重构来改善既有的代码,通过设计模式的不断理解实践对既有的框架进行优化,追求更加设计良好的程序。

三、Android底层:

《深入理解Android ***》系列书籍,邓凡平老师写的系列。

《Android源码设计模式》,结合设计模式分析源码

《Android框架揭秘》,底层架构的一本好书

徐宜生推荐理由:底层书籍对于应用开发者来说,不用过于深入,主要用于学习设计思路和了解底层设计机制

《Linux内核设计与实现》

《深入理解Linux内核》

袁辉辉推荐理由:Android底层是基于Linux Kernel,所以想成为Android全栈工程师,了解Linux Kernel是非常有必要的。这方面书籍较多,我就列举两本《Linux内核设计与实现》,《深入理解Linux内核》。*阶段只需加深对Android系统整体性的理解,不必拘泥于每个细节都理解,看完能大抵理解kernel进程的管理和调度机制,内存管理和内存寻址,I/O驱动设备(字符设备、块设备、网络设备)和调度机制等有所了解就够了;如果都理解了也就够了,如果想再深入,可以结合Kernel代码多看两遍。

小结:

不管看多少书,更重要的是自己思考,动手重复的实践!也许这个过程很耗时间,但是,这个不断以代码去验证自己的某些猜想的过程,便是技术成长的历程!

本系列书籍推荐方法:

1、按照自身的学习路程,亲自看过的书籍;

2、写一个小小邀请,邀请一些开发牛人给列一个书单,然后综合筛选;

3、整理完毕了,网络上让大家继续推荐,随时更新;

4、你的一些整理方法,总之做出一份优质的推荐书籍就好哈;

5、因为Android发展太快了,所以一些时间特别久远的书籍可能不合适了,比如2012年前出版的一些Android开发书籍已经不适用了;

6、需要有一些适当的JAVA基础书籍推荐。

 

另一份书籍推荐

一个老鸟发的公司内部整理的 Android 学习路线图(https://www.diycode.cc/topics/122)

———————————————————————————————————————-

一个老鸟发的公司内部整理的 Android 学习路线图 Markdown 版本

(https://www.diycode.cc/topics/122)

jixiaohua发了一篇一个老鸟也发了一份他给公司内部小伙伴整理的路线图。另一份 Android 开发学习路线图。可惜不是MarkDown格式的,所以jixiaohua直接上传的截图,在jixiaohua的呼吁下,我花了些时间,把这篇大牛的推荐清单编辑成了Markdown格式,方便大家浏览,学习。

有一些链接可能还不是特别准确,因为我只能根据图片上的书或者资源的名字去Google可能的书籍,所以链接上有什么不对的,欢迎大家评论指出,我会及时更正。请参考原文:另一份 Android 开发学习路线图。帮助修改。谢谢。

1.基础工具部分: 中文手册,我猜测是Maven中文手册,可是我并没有找到这样的资 源,欢迎知道的朋友告诉我;

2.Android部分有 『第三方库集合』,我没能找到资源地址;

3.书籍我大多是给的豆瓣链接,如果觉得不合适可以替换一下;

程序设计

一、java

(a)基本语法(如继承、异常、引用、泛型等)

*Java核心技术 卷I(适合入门)

(https://book.douban.com/subject/25762168/)

*进阶

*Effective Java中文版(如何写好的Java代码)

(https://book.douban.com/subject/3360807/)

*Java解惑(介绍烂Java代码是什么样的)

(https://book.douban.com/subject/5362860/)

(b)多线程、并发

*Java并发编程实战 (系统全面的介绍了Java的并发,如何设计支持并发的数据结构)

(https://book.douban.com/subject/10484692/)

(c)Java 7

*Java程序员修炼之道 (详细的介绍Java 7 的新特性)

(https://book.douban.com/subject/24841235/)

(d)Java 8

*写给大忙人看的Java SE 8

(https://book.douban.com/subject/26274206/)

*函数式编程思维

(https://book.douban.com/subject/26587213/)

(e)Java虚拟机

*深入理解Java虚拟机 (并不是那么难,Java程序员都该看看)

(https://book.douban.com/subject/24722612/)

(f)性能优化

*Java性能优化权威指南 (后面的章节好像用处不大,前面有些部分还是值得看)

(https://book.douban.com/subject/25828043/)

二、算法与数据结构

*算法时间复杂度、空间复杂度的基本认知

*熟悉常用数据结构:链表、队列、散列表、树等;

*递归、分支等基本思想;

*常用算法应用:排序、查找、比较等

*数据结构与算法分析 (涵盖面比较全、示例是Java语言)

(https://book.douban.com/subject/1139426/)

*算法设计与分析基础 (实用主义的典型、偏算法设计)

(https://book.douban.com/subject/26337727/)

*编程珠玑 (实践型算法数据)

(https://book.douban.com/subject/3227098/)

三、操作系统

*对Linux/OS的基本认知

*Linux的常用命令

*鸟哥的Linux私房菜

(https://book.douban.com/subject/4889838/)

*Linux内核设计与实现(原书第3版) (很精炼的语言描述清楚了内核算法)

(https://book.douban.com/subject/6097773/)

四、网络

*Http/Https

*TCP/IP

*图解HTTP

(https://book.douban.com/subject/25863515/)

*图解TCP/IP

(https://book.douban.com/subject/24737674/)

*进阶

*TCP/IP详解

(https://book.douban.com/subject/1088054/)

五、Android

*四大组件(服务、广播、ContentProvider、页面容器)

*基础UI组件(ListView、ViewPager)

*异步任务机制(AsyncTask、Handler、线程池)

*布局优化(层级、绘制、碎片化处理)

*图片加载(Bitmap、缓冲区)

*UniversalMusicePlayer (通过学习一个音乐播放器的代码能很快了解四大组件)

(https://github.com/googlesamples/android-UniversalMusicPlayer)

*Android Training官方课程

(http://hukai.me/android-training-course-in-chinese/index.html)

*Android一些重要知识点解析整理

(https://github.com/FX-Max/Point-of-Android)

*Android UI/UX库(各类常用组件及扩展组件的集合)

(https://github.com/wasabeef/awesome-android-ui)

*Picasso 、 Glide (两个图片加载库)

(http://square.github.io/picasso/)

(https://github.com/bumptech/glide)

*The Google I/O 2015 Android App (Google大会官方的App,适合学习各类实现)

(https://github.com/google/iosched)

*Android开发技术前线 (定期翻译、发布国内外Android优质的技术、开源库、软件架构设计、测试等文章)

(http://www.devtf.cn/)

*进阶

*第三方库集合 (列举了常见的各方向第三方库)

软件工程

一、基础工具

*IDE、Git、Maven

*Android Studio

(https://developer.android.com/studio/index.html)

*Git权威指南中文手册

(http://iissnan.com/progit/html/zh/ch1_0.html)

二、软件质量

*代码整洁

*码质量

*码重构

*编写可读代码的艺术 (来自Google工程师,专注于代码可读性)

(http://iissnan.com/progit/html/zh/ch1_0.html)

*代码整洁之道(使用面向对象+敏捷开发原则编写清晰可维护的代码)

(https://book.douban.com/subject/4199741/)

*重构-改善既有代码的设计 (学习改善已有代码)

(https://book.douban.com/subject/4262627/)

*重构手册 (改善代码的实际操作)

(https://book.douban.com/subject/1173730/)

三、设计模式

*23种常见设计模式

*大话设计模式

(https://book.douban.com/subject/2334288/)

*Head First设计模式(两本入门级的设计模式书籍)

(https://book.douban.com/subject/2243615/)

*进阶

*设计模式-可复用面向对象软件的基础(设计模式在实际中的应用)

(https://book.douban.com/subject/1052241/)

四、敏捷开发

*解析*限编程

(https://book.douban.com/subject/1790225/)

*敏捷开发的艺术

(https://book.douban.com/subject/4037534/)

*进阶

*敏捷软件开发-原则、模式与实践

(https://book.douban.com/subject/5348122/)

五、专业开发

*序员职业素养

*更高效、更实效

*程序员的是职业素养

(https://book.douban.com/subject/11614538/)

*程序员修炼之道-从小工到专家

(https://book.douban.com/subject/5387402/)

六、思考人生

*黑客与画家 (硅谷创业之父Paul Craham 的文集,主要介绍黑客及优秀程序员的爱好和动机)

(https://book.douban.com/subject/6021440/)

 

另一份Android开发学习路线

因为表格不是按照MD编辑器做的,目前这份是截图啦

%title插图%num

 

国内中高级的Android技术人才仍然稀缺?

前言

看到一篇文章中提到“*近几年国内的初级Android程序员已经很多了,但是中高级的Android技术人才仍然稀缺“,这的确不假,从我在百度所进行的一些面试来看,找一个适合的高级Android工程师的确不容易,一般需要进行大量的面试才能挑选出一个比较满意的。为什么中高级Android程序员不多呢?这是一个问题,我不好回答,但是我想写一篇文章来描述下Android的学习路线,期望可以帮助更多的Android程序员提升自己。由于我也是从一个菜鸟过来的,所以我会结合我的个人经历以及我对Android学习过程的认识来写这篇文章,这会让这篇文章更加真实,而并非纸上谈兵。

我的工作经历

我也是从一个Android菜鸟过来的。其实这句话放在任何人的身上都是适用的,即大家都是一步步过来的,因此作为初学者也不必因为技术差而郁闷,同理,高手也不要看不起一些所谓的菜鸟,因为这不公平,技术只有在同等的工作年限上才具有一定的可比性,也许你眼中的菜鸟只是个工作半年的新手,而你已经工作5年,可想而知,这根本不具有可比性,搞不好别人5年后可以达到比你更高的技术高度。
我是硕士研究生毕业,我已经工作3年零3个月了,职位上从*开始的腾讯初级工程师变成了现在的百度Android资深工程师。*开始我并不是做Android的,先是做了半年的C++,接着做了3个月的Web前端,然后公司内部转岗做Android到至今,纯Android工作年限的话其实是2.5年。但是我认为我的Android工作经验(注:工作年限不等同于工作经验)不止2.5年,我投入大量的业余时间在Android开发上,并且我习惯去思考问题、总结问题并将其融会贯通,因此我的Android技术在短时间内有了很大的提升。
在Android学习过程中,初学者踩过的坑我也都踩过,我也深深地知道大家在学习过程中*棘手的问题是什么。举个例子,在3年前,我想在SlidingMenu中放入一个ListView,发现二者总是不能很好地一起工作,这肯定是由于滑动冲突的问题,我也知道,但是不知道怎么解决。我就去学校图书馆翻遍了所有的Android书籍,无果。大家肯定都知道原因,为什么我无法从书中查找到问题的答案,因为入门书不讲滑动冲突,所谓的高级编程书也不讲。还有一个问题,我曾经以为view.post(runnable)可以让runnable的run方法在线程中执行,所以我就在run方法里面去做耗时的操作,现在想想我当时是多菜啊,因此我曾经也是菜鸟。
直到若干年后的某一天,我终于琢磨透了滑动冲突的事,然后我就把解决问题的思想写在了CSDN博客上,但是好像看得人并不多,很多人并不能体会我的用心,后来我博客的访问量越来越大,这才慢慢地得到了一些人的关注。后来有一天我有了写书的契机,我想到了我*开始学习Android时所踩过的坑,想到滑动冲突对我的困扰,为了更好地传播我的技术经验,为了让更多的人少踩一些坑,为了让更多地人成为Android高级工程师,我毅然决定将Android开发中*重要的、*疑难的、*容易困扰大家的、成为高级工程师所必备的知识点和盘托出,这就是《Android开发艺术探索》存在的原因以及意义。书的反响怎么样呢?从目前读者的评价来看,内容基本无差评,我收到了很多读者的肯定以及感谢,这说明很多人能够理解我的用心。
说了那么多,言归正传,下面说下Android学习路线的话题,本文打算从4个阶段来对Android的学习过程做一个全面的分析,分别为Android初级、中级、高级以及资深工程师,具体请看下面的分析。同理,本篇学习路线仍然只针对Android应用开发,不针对Rom开发和逆向工程等。这里虚拟一位“小明”的角色,在这里小明就是Android初学者的代表。
初级工程师

小明之前完全没接触过Android开发,是个应届生,他的待遇是13k,然后小明以校招的身份进入了百度,然后小明需要怎么学习才能成为初级工程师呢?这个时候,小明对编程基础、数据结构、C语言都有一定基础,Java语法什么的也都掌握的比较好,Android才有java语言,无奈的是小明并不会搞Android。
小明首先需要购买一本Android入门的书籍,为了更快地学习Android,小明业余时间也都用来一边看书一边照着书中的例子敲代码,结果2周时间小明就把这本书学了一遍。看完这本书后,小明对Android的历史、结构、代码规范等都有了一个大概的了解,并且,小明已经可以写出一些简单的Activity了。这个时候在小明眼里,Android开发很简单很好玩,通过在xml中摆放一些按钮文本框什么的就可以做一些界面了。
小明开始跟着他的技术导师做需求,一些简单的小需求小明自然是不在话下了。突然有一天来了一个需求,该需求要求小明在Activity中为一个button加一个动画效果,小明慌了:“完全没接触过,书上也没有讲,怎么办呢?”小明冷静了下,打开了百度搜索,输入“Android 动画”,打开前几个链接,小明恍然大悟,照着网上的例子把需求给实现了。后来导师告诉他:“学好Android,官方文档是必须看的,既全面又权威”。然后小明如获至宝,花了一年时间把上面的guide和training都看了一遍,并且他还动手抄了几个小例子。
有一天,小明又需要做一个动画相关的需求,这可难不倒小明,它熟练地打开了www.baidu.com,输入“Android 动画”,突然他楞了一下:”总不能每次写动画都要百度一下吧!“,于是他在CSDN开了一个博客,把动画相关的知识点都写上去,为的是后面再写动画相关的代码就不用百度去搜了,事实如何呢?后面再写动画相关的代码,小明的确不用再去百度搜了,因为通过写一篇动画博客,他把动画相关的细节都已经记住了,这样他就可以不用再去参考任何文档了,后来小明还学会了把一些琐碎的不方便放在博客上的东西写到了印象笔记上面,什么时候忘了10秒钟以内都可以快速找回来,而不是花10分钟去再次搜索一遍。
这里总结一下,Android入门的时候,需要有一本入门书,好好学习书中的内容,同时花一年时间把Android官方文档中的training和guide看一遍,同时通过写博客和记笔记的方式来做总结,建议让自己的每篇博客都有价值些。通过一年时间的学习,相信每个人都可以达到中级工程师的水平。
技术要求:

  • 基本知识点
    比如四大组件如何使用、如何创建Service、如何进行布局、简单的自定义View、动画等常见技术
  • 书籍推荐
    《*行代码 Android》、《疯狂Android》
    中级工程师
    小明经过一年的努力学习终于成为Android中级工程师了,月薪变成了17k。到了中级工程师,已经可以在公司里干很多体力活了,但是一些很重要的任务小明还不能一个人承担起来,这个时候小明需要学习的内容就很多了,如下所示:
  • AIDL:熟悉AIDL,理解其工作原理,懂transact和onTransact的区别;
  • Binder:从Java层大概理解Binder的工作原理,懂Parcel对象的使用;
  • 多进程:熟练掌握多进程的运行机制,懂Messenger、Socket等;
  • 事件分发:弹性滑动、滑动冲突等;
  • 玩转View:View的绘制原理、各种自定义View;
  • 动画系列:熟悉View动画和属性动画的不同点,懂属性动画的工作原理;
  • 懂性能优化、熟悉mat等工具
  • 懂点常见的设计模式
    学习方法
    阅读进阶书籍,阅读Android源码,阅读官方文档并尝试自己写相关的技术文章,需要有一定技术深度和自我思考。在这个阶段的学习过程中,有2个点是比较困扰大家的,一个是阅读源码,另一个是自定义View以及滑动冲突。
    如何阅读源码呢?这是个头疼的问题,但是源码必须要读。阅读源码的时候不要深入代码细节不可自拔,要关注代码的流程并尽量挖掘出对应用层开发有用的结论。另外仔细阅读源码中对一个类或者方法的注释,在看不懂源码时,源码中的注释可以帮你更好地了解源码中的工作原理,这个过程虽然艰苦,但是别无他法。
    如何玩转自定义View呢?我的建议是不要通过学习自定义view而学习自定义view。为什么这么说呢?因为自定义view的种类太多了,各式各样的绚丽的自定义效果,如何学的玩呢!我们要透过现象看本质,更多地去关注自定义view所需的知识点,这里做如下总结:
  • 搞懂view的滑动原理
  • 搞懂如何实现弹性滑动
  • 搞懂view的滑动冲突
  • 搞懂view的measure、layout和draw
  • 然后再学习几个已有的自定义view的例子
  • *后就可以搞定自定义view了,所谓万变不离其宗
    大概再需要1-2年时间,即可达到高级工程师的技术水平。我个人认为通过《Android开发艺术探索》和《Android群英传》可以缩短这个过程为0.5-1年。注意,达到高级工程师的技术水平不代表就可以立刻成为高级工程师(受机遇、是否跳槽的影响),但是技术达到了,成为高级工程师只是很简单的事。
    技术要求:
  • 稍微深入的知识点
    AIDL、Messenger、Binder、多进程、动画、滑动冲突、自定义View、消息队列等
  • 书籍推荐
    《Android开发艺术探索》、《Android群英传》
    高级工程师
    小明成为了梦寐以求的高级工程师,月薪达到了20k,还拿到了一丢丢股票。这个时候小明的Android水平已经不错了,但是小明的目标是资深工程师,小明听说资深工程师月薪可以达到30k+。
    为了成为Android资深工程师,需要学习的东西就更多了,并且有些并不是那么具体了,如下所示:
  • 继续加深理解”稍微深入的知识点“中所定义的内容
  • 了解系统核心机制:
  1. 了解SystemServer的启动过程
  2. 了解主线程的消息循环模型
  3. 了解AMS和PMS的工作原理
  4. 能够回答问题”一个应用存在多少个Window?“
  5. 了解四大组件的大概工作流程
  • 基本知识点的细节
  1. Activity的启动模式以及异常情况下不同Activity的表现
  2. Service的onBind和onReBind的关联
  3. onServiceDisconnected(ComponentName className)和binderDied()的区别
  4. AsyncTask在不同版本上的表现细节
  5. 线程池的细节和参数配置
  6. 熟悉设计模式,有架构意识

Android多线程方式

1、前言
在Android开发中经常会使用到多线程,这里主要是总结Android开发中常见的多线程实现方式,以及这些多线程实现方式的一些特点
多线程实现方式主要有:

实现Thread的run()方法或者实现Runable接口
HandlerThread
AsyncTask
LoaderManager
2、Thread方式
一般使用异步操作*常见的一种方式,我们可以继承Thread,并重写run()方法,如下所示:

Thread syncTask = new Thread() {
@Override
public void run() {
// 执行耗时操作
}
};

syncTask.start();

还有另外一种启动线程的方式,即在创建Thread对象时,传入一个实现了Runable接口的对象,如下所示:

Thread syncTask = new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时操作
}
});

syncTask.start();

Thread类中有几个方法的作用有些模糊,这里给出说明:

interrupt( ):
我们一般会使用该方法中断线程的执行,但该方法并不会中断线程,它的作用只是设置一个中断标志位,我们还得在run( )方法中判断这个标志位,并决定是否继续执行,通过这样的方式来达到中断的效果。 但该方法根据线程状态的不同,会有不同的结果。结果如下:
当线程由于调用wait( )、join( )、sleep( )而阻塞时,中断标志位将会被清空,并接收到InterruptedException异常。
当线程由于正在进行InterruptibleChannel类型的I/O操作而阻塞时,中断标志位将会置位,并接收到ClosedByInterruptException异常(I/O流也会自动关闭)
当线程由于进行Selector操作而阻塞时,中断标志位将会置位,但不会接收到异常
interrupted( )和isInterrupted( )的区别:
两个方法都是判断当前线程的中断标志位是否被置位,但调用interrupted( )方法后,中断标志位将会重置,而isInterrupted()不会被重置。

join( )和sleep( )的区别:两个方法都会让线程暂停执行
join()方法是让出执行资源(如:CPU时间片),使得其它线程可以获得执行的资源。所以调用join()方法会使进入阻塞状态,该线程被唤醒后会进入runable状态,等待下一个时间片的到来才能再次执行。
sleep( )不会让出资源,只是处于睡眠状态(类似只执行空操作)。调用sleep()方法会使进入等待状态,当等待时间到后,如果还在时间片内,则直接进入运行状态,否则进入runable状态,等待下个时间片。

3、HandlerThread
有些需求需要子线程不断的从一个消息队列中取出消息,并进行处理,处理完毕以后继续取出下一个处理。对于这个需求我们可以使用*种方式,实现一个Thread对象,并创建一个消息队列,在Thread对象的run方法中不断的从消息队列中取出消息进行处理。多以该线程的这些特点有点像一个Looper线程,我们可复用Handler机制提供的消息队列MessageQueue,而无需自己重新创建。
HandlerThread的内部实现机制很简单,在创建新的线程后,使该线程成为一个Looper线程,让该线程不断的从MessageQueue取出消息并处理。我们看一下HandlerThread的实现:

public class HandlerThread extends Thread {
int mPriority;
Looper mLooper;

/**
* Constructs a HandlerThread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}

@Override
public void run() {
// 要想让某个线程成为Looper线程,先调用Looper.prepare()为该线程创建一个Looper对象,并初始化MessageQueue对象
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
// 调用Looper.loop(),让该线程的Looper实例循环从MessageQueue中取出Message进行处理
Looper.loop();
}
}

4、AsyncTask
这是我们*经常使用的一种异步方式,在前面的两种多线程方式中,如果在子线程中进行了耗时的处理操作(如:网络请求、读写数据库等),当操作完毕后,我们需要更新UI上的显示状态,但在Android开发中我们是不能在子线程中更新UI界面的,所以还得在子线程中发送一个通知到主线程,让主线程去更新UI。这样的操作流程有些复杂,且都是重复性的工作。所以Android sdk中为我们抽象出AsyncTask这个类。

public class CustomAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
// 在开始执行异步操作前回调,该方法在主线程中执行
}

@Override
protected String doInBackground(String… strings) {
// 在该方法中进行异步操作,参数strings是在启动异步任务时execute(…)传递进来的
// 该异步任务放回的结果类型为String
return null;
}

@Override
protected void onProgressUpdate(Integer… values) {
// 该方法用户通知用户doInBackground()方法的处理进度,在主线程中被回调,所以可在该方法中更新UI
// 参数values用于指示处理进度
}

@Override
protected void onPostExecute(String result) {
// 该方法是在异步操作doInBackground()处理完毕后回调,参数result是doInBackground()的处理结果
// 该方法在主线程中被回调,可直接更新UI
}

@Override
protected void onCancelled(String result) {
super.onCancelled(result);

// 当调用cancel(boolean), 则在doInBackground()完成后回调该方法
// 注意: 参数result可能为null,
}
}

AsyncTask的内部使用了两个线程池,我们大概看一下AsyncTask的内部实现

// 顺序执行任务的线程池,注意这个线程池是静态的,每个AsyncTask对象共用这个线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

// 我们启动异步任务的三个方法,都是向SerialExecutor.execute(runable)传递一个runable对象
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params… params) {

exec.execute(mFuture);

return this;
}

public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

看一下SerialExecutor的实现

private static class SerialExecutor implements Executor {
// 存储待执行的异步任务
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
// 其实并没有马上执行,而是添加到队列mTasks中, 进行一个排队
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
// 一个任务执行完后,再执行下一个
scheduleNext();
}
}
});

// 当前没有异步任务执行时,启动开始执行
if (mActive == null) {
scheduleNext();
}
}

protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
// 使用另外一个线程池分配线程,并执行任务
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

所以在使用AsyncTask执行异步操作时,会先在SerialExecutor进行一个顺序排队, 后再用ThreadPoolExcutor线程池为你分配一个线程并执行。而整个应用的AsyncTask任务都在排同一条队,有可能等待排队的任务很多,所以一般不会使用AsyncTask执行一些优先级比较高的异步任务。
当然我们是可以跳过不需要进行排队,直接就通过线程池分配一个线程并执行异步任务,但需要注意同时执行太多的异步任务,会影响用户体验,我想Google就是为了限制同时创建太多的线程才会采用一个排队机制的

/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}

该方法是隐藏,但可使用反射,设置一个线程池。

5、Loader&LoaderManager
上面三种异步方式都可以用来加载一些耗时的数据,但有时我们加载数据的过程与Activity、Fragment的生命息息相关的。所以在使用上面说的那几种异步方式进行异步数据加载时,是需要去考虑Activity(Fragment)的生命周期是处于哪个阶段的。于是Android在Android 3.0以后引入了LoaderManager,主要用于执行一些耗时的异步数据加载操作,并根据Activity生命周期对异步处理进行调整,LoaderManager可以解决的问题包括:

加载的数据有变化时,会自动通知我们,而不自己监控数据的变化情况,如:用CursorLoader来加载数据库数据,当数据库数据有变化时,可是个展示变化的数据
数据的请求处理时机会结合Activity和Fragment的生命周期进行调整,如:若Acivity销毁了,那就不会再去请求新的数据
使用该方法加载数据涉及到两个类重要的类,Loader和LoaderManager:

Loader:该类用于数据的加载 ,类型参数D用于指定Loader加载的数据类型

public class Loader<D> {
}

一般我们不直接继承Loader,而是继承AsyncTaskLoader,因为Loader的加载工作并不是在异步线程中。而AsyncTaskLoader实现了异步线程,加载流程在子线程中执行。注意:对该类的调用应该在主线程中完成。

LoaderManager:
LoaderManager用于管理与Activity和Fragment关联的Loader实例,LoaderManager负责根据的Activity的生命周期对Loader的数据加载器进行调度,所以这里分工明确,Loader负责数据加载逻辑,LoaderManager
负责Loader的调度,开发者只需要自定义自己的Loader,实现数据的加载逻辑,而不再关注数据加载时由于Activity销毁引发的问题。

注意:其实AsyncTaskLoader内部实现异步的方式是使用AsyncTask完成的,上面我们说过AsyncTask的内部是有一个排队机制,但AsyncTaskLoader内部使用AsyncTask进行数据异步加载时,异步任务并不进行排队。而直接又线程池分配新线程来执行。

6、总结
我们来总结一下异步处理的方式,以及每种处理方式适合什么样的场景

直接使用Thread实现方式,这种方式简单,但不是很优雅。适合数量很少(偶尔一两次)的异步任务,但要处理的异步任务很多的话,使用该方式会导致创建大量的线程,这会影响用户交互。
HandlerThread,这种方式适合子线程有序的执行异步操作,异步任务的执行一个接着一个。
AsyncTask, 通常用于耗时的异步处理,且时效性要求不是非常高的那种异步操作。如果时效性要求非常高的操作,不建议使用这个方式,因为AsyncTask的默认实现是有内部排队机制,且是整个应用的AsyncTask的任务进行排队,所以不能保证异步任务能很快的被执行。
LoaderManager,当请求处理时机需要根据Activity的生命周期进行调整,或需要时刻监测数据的变化,那LoaderManager是很不错的解决方案。

2020 Google 开发者大会主题演讲,都在这里

 

%title插图%num

今年大会上,谷歌为开发者带来了一大波开发技术和工具更新,助力更高效、更轻松的开发体验。

Android 11:以人为本,控制和隐私

在 Android 11 中,我们专注于三个关键主题:以人为本,控制和隐私,并将这些更新带到众多 Android 设备上!

Android团队也一直在努力改善移动开发者的体验。Android Studio 4.1 和 4.2 Canary 更新,提高开发效率。Kotlin 和 Jetpack 库的完善,可以帮助开发者快速构建应用。

%title插图%num

TensorFlow 2.4:解决各行各业的实际问题

TensorFlow 带来了 2.4 版本更新,介绍了在汽车、商业、游戏等行业的应用。从研究人员,到数据科学家、工程师、开发人员,TensorFlow 都有相应的方案,帮助高效解决问题

%title插图%num

Flutter:增强开放,拓展开发场景

作为全球增长速度第二的开源项目,越来越多国内开发者使用 Flutter 实现跨平台开发,包括腾讯英语君团队、阿里闲鱼团队等等。其在开放性上的进步,得益于开源社区、生态建设、对 Web 的支持。

%title插图%num

Web:多方面增强用户体验

团队以 Private Sandbox 提升用户隐私保护,加入 Core Web Vitals 核心网页指标,增强用户体验。PWA 与 Android 集成、Google Play 商店连接,Chrome Dev Tools 的更新,为开发者带来更多用户,也助其更轻松开发。

%title插图%num

Firebase :提高应用质量

Firebase 整合谷歌各产品和云服务,让开发者在单个平台上轻松构建移动端、 Web 端的应用。Firebase 模拟套件和性能监测工具,则提高应用质量,让开发更快更简单。

%title插图%num

Wear OS :发展生态,提高生产力

Wear OS 今年从开机速度、配对速度、续航能力多个方面实现提升,从硬件和软件层面发展合作伙伴生态,帮助人们获得实时信息,提高生产力

%title插图%num

ARCore:打造有趣且真实的应用

ARCore 已被应用在 7 亿台设备和成千上万款 APP 上,包括美图、滴滴、有道少儿词典等。瞬间放置 API 、深度 API 以及持久云锚点能帮助开发者打造有趣、真实的应用,并使用在更丰富的场景中。

%title插图%num

Google Assistant:搭建全面的智能家居生态

Google Assistant 发布了兼容拓展的娱乐智能家居(SHED),用户用 Google Assistant 控制娱乐设备,结合 Android 11、APP Flip 增加操作便捷性。与开发者共同打造的 Project Connected Home Over IP ,覆盖主流互联网设备,能搭建更全面的智能家居功能。

%title插图%num

%title插图%num

人才培养是创新关键,今年大会重磅宣布 Codelabs 首次发布中文版,提供手把手的代码教程,让国内开发者们可以在线进行编码实践,学习之路更加顺畅。

 

大会上,谷歌正式宣布和网易有道达成合作,在中国大学 MOOC 上线 Grow with Google 成长计划的学习专区。首发三门课程:TensorFlow 入门实操课程、ARCore 开发入门课程和海外数字营销系列课程,助力人才成长与发展。

 

%title插图%num

来自中央美术学院的邬建安教授,结合 TensorFlow 技术带来了“心面孔”艺术作品,打破艺术品的边界,使一件静止的艺术品,变成了互动的艺术表达。

%title插图%num

%title插图%num

 

包容一向是谷歌关注的议题,无论是此前“谷歌编程女神范 (Google Girl Hackathon)”项目,还是 11 月 19 日的 Google 女性开发者职业发展座谈会、11 月 12 日- 19 日的# ImRemarkable 互动周等活动,都致力于帮助女性展现充满自信的“她力量”。对活动感兴趣,快点击上述链接参与吧!

包容性还体现在开发产品中,通过语音访问、实时字幕等辅助功能,我们鼓励开发者实现无障碍开发,让技术惠及所有用户。

此外,DevFest 在 11 月陆续举行,多个城市的 GDG 社区举办了 GDS viewing party ,让开发者欢聚一堂,共同享受这场科技盛宴。

%title插图%num

看完这些,是不是还意犹未尽?明天,我们将带来 Android 、Google Play 和 Chrome OS 的主题演讲,别忘了 13:00 准时来官网观看哦!连续 5 天,每天 13:00 主题技术演讲等你来!

这么精彩的内容别忘了分享!快带上# Google 开发者大会 #的话题标签,转发这篇推文给需要的朋友吧!

 

 

安卓毕业设计-图书馆管理系统-新手练手项目

图书馆管理系统
目的:1,用于毕业设计

2,用于用于新手的练手项目

开发环境:win7,android studio3.0.1,模拟器api:7.0.1

开发语言:Java

代码量:Java代码5000行左右,

涉及到知识点:

1,activity,DrawerLayout(抽屉布局),CardView,

2,安卓本地数据库

3,自定义view

4,RecyclerView

5,MVP开发模式,实际中,M层的数据处理我放到了P层,简单的从数据库拿数据,就没有单独写一个M类出来了

6,Material Design

实现的功能:

1,用户登录注册;———访问数据库验证登录,插入数据库实现用户注册

2,浏览全部书籍;———用RecyclerView展示

3,查看书籍详情;———书籍的名称,出版社,浏览量,藏书量等

4,借书还书;      ———-借书后,book表藏书量-1,还书后,book表中藏书量+1

5,浏览自己所有借书;—–用RecyclerView展示

6,用户修改自己信息;—–sql的update语句实现

7,增删改书籍;    ———管理员拥有的功能

8,管理员修改普通用户信息;

9,外借书籍详细信息——-管理员可查看外借书的详细,包括时间,人物,书籍名称

10,软件的所有操作记录;–管理员可查看所有操作:登录,注册,借书,换书,修改个人信息,浏览书籍等记录

数据库:四个表:book:所有图书表

user:用户表,管理员,普通用户通过level来划分

outbook:外借图书表

operation_record:左右操作记录表

 

效果图:
%title插图%num

%title插图%num

%title插图%num

Android开发 设置手机壁纸

内容概要
可以选择自己喜欢的图片进行壁纸设置

所需方法
1、使用WallpaperManager的setResource(int ResourceID)方法

2、使用WallpaperManager的setBitmap(Bitmap bitmap)方法

3、重写ContextWrapper 类中提供的setWallpaper()

4.传入9张自己喜欢的图片,命名image1-image9

Mainfest中加入权限:
<uses-permission android:name=”android.permission.SET_WALLPAPER”/>

布局代码
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:orientation=”vertical”
android:background=”#000000″
tools:context=”.MainActivity” >
<ImageSwitcher
android:id=”@+id/ImageSwitcher”
android:layout_width=”fill_parent”
android:layout_height=”370dp”>
</ImageSwitcher>
<Gallery
android:id=”@+id/Gallery”
android:layout_width=”fill_parent”
android:layout_height=”80dp”
android:layout_below=”@+id/ImageSwitcher” />
<Button
android:id=”@+id/BtnGo”
android:layout_width=”wrap_content”
android:layout_height=”40dp”
android:layout_below=”@+id/Gallery”
android:layout_alignParentBottom=”true”
android:layout_centerHorizontal=”true”
android:text=”@string/BtnGo” />
</RelativeLayout>

ImageAdapter类
使用Gallery来实现一个可以供用户选择的缩略图列表,当用户选择列表中的图像时,会在ImageSwitcher控件中显示出当前图像,当点击Button时,当前图片将被设置为壁纸。其实这里的ImageSwitcher完全可以替换为ImageView,考虑到ImageSwitcher可以提供较好的动画效果,所以我们在这里选择了ImageSwitcher。同样地,我们继续使用Android开发学习之Gallery中的那个ImageAdapter类:

代码:

package com.android.gallery2switcher;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter{

//类成员myContext为context父类
private Context myContext;
private int[] myImages;

//构造函数,有两个参数,即要存储的Context和Images数组
public ImageAdapter(Context c,int[] Images)
{
// TODO Auto-generated constructor stub
this.myContext=c;
this.myImages=Images;
}

//返回所有的图片总数量
@Override
public int getCount()
{

return this.myImages.length;
}

//利用getItem方法,取得目前容器中图像的数组ID
@Override
public Object getItem(int position)
{
return position;
}

@Override
public long getItemId(int position)
{
return position;
}

//取得目前欲显示的图像的VIEW,传入数组ID值使之读取与成像
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView image=new ImageView(this.myContext);
image.setImageResource(this.myImages[position]);
image.setScaleType(ImageView.ScaleType.FIT_XY);
image.setAdjustViewBounds(true);
return image;
}

}

main代码
package com.android.gallery2switcher;
import java.io.IOException;

import android.os.Bundle;
import android.app.Activity;
import android.app.WallpaperManager;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.Gallery.LayoutParams;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.ViewSwitcher.ViewFactory;

public class MainActivity extends Activity {

Gallery mGallery;
ImageSwitcher mSwitcher;
Button BtnGo;
int[] Resources=new int[]{R.drawable.image0,R.drawable.image1,R.drawable.image2,R.drawable.image3,
R.drawable.image4,R.drawable.image5,R.drawable.image6,R.drawable.image7,R.drawable.image8};
int index;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//不显示标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
mGallery=(Gallery)findViewById(R.id.Gallery);
mSwitcher=(ImageSwitcher)findViewById(R.id.ImageSwitcher);
//实现ImageSwitcher的工厂接口
mSwitcher.setFactory(new ViewFactory()
{
@Override
public View makeView()
{
ImageView i = new ImageView(MainActivity.this);
i.setBackgroundColor(0xFF000000);
i.setScaleType(ImageView.ScaleType.FIT_CENTER);
i.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
return i;
}
});
//设置资源
mSwitcher.setImageResource(Resources[0]);
//设置动画
mSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_in));
mSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_out));
BtnGo=(Button)findViewById(R.id.BtnGo);
BtnGo.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
SetWallPaper();
}
});
ImageAdapter mAdapter=new ImageAdapter(this,Resources);
mGallery.setAdapter(mAdapter);
mGallery.setOnItemSelectedListener(new OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> Adapter, View view,int position, long id)
{
//设置图片
mSwitcher.setImageResource(Resources[position]);
//获取当前图片索引
index=position;
}
@Override
public void onNothingSelected(AdapterView<?> arg0)
{

}

});

}
//设置壁纸
public void SetWallPaper()
{
WallpaperManager mWallManager=WallpaperManager.getInstance(this);
try
{
mWallManager.setResource(Resources[index]);
}
catch (IOException e)
{
e.printStackTrace();
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
return true;
}

}

实现效果图

%title插图%num %title插图%num

可左右拖动:
点击设置壁纸后:

%title插图%num

Android+Java设置开机启动

Android+Java设置开机启动—开机解锁并直接进入应用

 使用Java在Android环境下开发应用,设置该应用为开机自动运行,使用一个简单的Hello,World!程序进行测试。

1.首先写一个简单的只显示一行文字的代码如下,类SayHello.class:

package com.example.bootstartdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.*;

public class SayHello extends Activity{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD,
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
setContentView(R.layout.activity_main);
}
}
创建一个只显示一行文字的Activity,并将开机锁定功能关闭
getWindow().setFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD,
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
对应的XML代码如下:

<FrameLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:id=”@+id/container”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
tools:context=”com.example.bootstartdemo.MainActivity”
tools:ignore=”MergeRootFrame” >

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical” >

<TextView
android:id=”@+id/textView1″
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_weight=”0.70″
android:text=”Hello,Android,this is Shine!” />
</LinearLayout>

</FrameLayout>

2.接收广播消息类 BroadcastReceiver.class
package com.example.bootstartdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class BootBroadcastReceiver extends BroadcastReceiver{
static final String ACTION = “android.intent.action.BOOT_COMPLETED”;
@Override
public void onReceive(Context context,Intent intent){
if(intent.getAction().equals(ACTION))
{
Intent sayHelloIntent = new Intent(context,SayHello.class);
sayHelloIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(sayHelloIntent);
}
}
}
该类继承自BroadcastReceiver,并重写方法onReceive。使用Intent判断是否是进行的动作,即开机完成BOOT_COMPLETED,如果是,则下一步关联到SayHello类进行处理,SayHelloIntent将上下文与SayHello类进行绑定,作为无参数的activity跳转。(Intent是一种传参方式。)

3.配置文件:AndroidManifest.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.bootstartdemo”
android:versionCode=”1″
android:versionName=”1.0″ >
<uses-sdk
android:minSdkVersion=”8″
android:targetSdkVersion=”15″ />
<application
android:allowBackup=”true”
android:icon=”@drawable/ic_launcher”
android:label=”@string/app_name”
android:theme=”@style/AppTheme” >
<activity
android:name=”com.example.bootstartdemo.SayHello”
android:label=”@string/app_name” >
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<receiver android:name=”.BootBroadcastReceiver” >
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED” />
</intent-filter>
</receiver>
</application>
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”></uses-permission>
</manifest>
其中

<receiver android:name=”.BootBroadcastReceiver” >
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED” />
</intent-filter>
</receiver>
表示接受者为BootBroadcastReceiver可接收的动作是BOOT_COMPLETED,根据该配置,在进行该动作时,与该类关联。要配置
android.permission.RECEIVE_BOOT_COMPLETED
权限,否则设置不成功。

Android开发之应用锁解锁方式:指纹和密码

先上效果图:
主要实现的是通过自定义密码或者指纹,给应用锁进行解锁,然后进入APP。

%title插图%num %title插图%num
主要代码:LoginActivity.java
package com.fyang21117.rdiot1.second;

import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.fyang21117.rdiot1.FingerprintUtil;
import com.fyang21117.rdiot1.KeyguardLockScreenManager;
import com.fyang21117.rdiot1.MainActivity;
import com.fyang21117.rdiot1.R;
import com.fyang21117.rdiot1.core.FingerprintCore;

public class LoginActivity extends AppCompatActivity implements View.OnClickListener {

private FingerprintCore mFingerprintCore;
private KeyguardLockScreenManager mKeyguardLockScreenManager;//指纹管理
private Toast mToast;
private Handler mHandler = new Handler(Looper.getMainLooper());//主线程
private EditText editText;
private String string;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setTitle(“应用锁”);

initFingerprintCore();

Button psw_login = findViewById(R.id.psw_login);
Button use_fingerprint = findViewById(R.id.use_fingerprint);
editText = findViewById(R.id.unlock_num);
psw_login.setOnClickListener(this);
use_fingerprint.setOnClickListener(this);
}

@Override
public void onClick(View v) {
final int viewId = v.getId();
switch (viewId) {
case R.id.use_fingerprint:
//调用非活动布局的控件
View view= getLayoutInflater().inflate(R.layout.fingerprint_dialog, null);
final MyDialog mMyDialog= new MyDialog(this, 0, 0, view, R.style.DialogTheme);
Button cancel_fingerprint = view.findViewById(R.id.cancel_unlock);
mMyDialog.setCancelable(true);
mMyDialog.show();
startFingerprintRecognition();
cancel_fingerprint.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mMyDialog.dismiss();
}
});
break;
case R.id.psw_login:
string =editText.getText().toString();
if(string.equals(“1028″))
MainActivity.actionStart(this);
else
Toast.makeText(this,”Unlocking failed!Please input again!”,Toast.LENGTH_SHORT).show();
break;
}
}
/*** 指纹识别初始化*/
private void initFingerprintCore() {
mFingerprintCore = new FingerprintCore(this);
mFingerprintCore.setFingerprintManager(mResultListener);
mKeyguardLockScreenManager = new KeyguardLockScreenManager(this);
}

/*** 进入系统设置指纹界面*/
private void enterSysFingerprintSettingPage() {
FingerprintUtil.openFingerPrintSettingPage(this);
}
private void startFingerprintRecognitionUnlockScreen() {
if (mKeyguardLockScreenManager == null) {
return;
}
if (!mKeyguardLockScreenManager.isOpenLockScreenPwd()) {
FingerprintUtil.openFingerPrintSettingPage(this);
return;
}
mKeyguardLockScreenManager.showAuthenticationScreen(this);
}

/*** 开始指纹识别*/
private void startFingerprintRecognition() {
if (mFingerprintCore.isSupport()) {
if (!mFingerprintCore.isHasEnrolledFingerprints()) {
toastTipMsg(R.string.fingerprint_recognition_not_enrolled);
FingerprintUtil.openFingerPrintSettingPage(this);
return;
}

toastTipMsg(R.string.fingerprint_recognition_tip);
if (mFingerprintCore.isAuthenticating()) {
toastTipMsg(R.string.fingerprint_recognition_authenticating);
} else {
mFingerprintCore.startAuthenticate();
}
} else {
toastTipMsg(R.string.fingerprint_recognition_not_support);
}
}

private void IntoActivity() {
MainActivity.actionStart(this);//识别成功
}

private FingerprintCore.IFingerprintResultListener mResultListener
= new FingerprintCore.IFingerprintResultListener() {
@Override
public void onAuthenticateSuccess() {
toastTipMsg(R.string.fingerprint_recognition_success);
IntoActivity();
}
@Override
public void onAuthenticateFailed(int helpId) {
toastTipMsg(R.string.fingerprint_recognition_failed);
}
@Override
public void onAuthenticateError(int errMsgId) {
toastTipMsg(R.string.fingerprint_recognition_error);
}
@Override
public void onStartAuthenticateResult(boolean isSuccess) {
}
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == KeyguardLockScreenManager.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
// Challenge completed, proceed with using cipher
if (resultCode == RESULT_OK) {
toastTipMsg(R.string.sys_pwd_recognition_success);
} else {
toastTipMsg(R.string.sys_pwd_recognition_failed);
}
}
}

private void toastTipMsg(int messageId) {
if (mToast == null) {
mToast = Toast.makeText(this, messageId, Toast.LENGTH_SHORT);
}
mToast.setText(messageId);
mToast.cancel();
mHandler.removeCallbacks(mShowToastRunnable);
mHandler.postDelayed(mShowToastRunnable, 0);
}

private void toastTipMsg(String message) {
if (mToast == null) {
mToast = Toast.makeText(this, message, Toast.LENGTH_LONG);
}
mToast.setText(message);
mToast.cancel();
mHandler.removeCallbacks(mShowToastRunnable);
mHandler.postDelayed(mShowToastRunnable, 200);
}

private Runnable mShowToastRunnable = new Runnable() {
@Override
public void run() {
mToast.show();
}
};

@Override
protected void onDestroy() {
if (mFingerprintCore != null) {
mFingerprintCore.onDestroy();
mFingerprintCore = null;
}
if (mKeyguardLockScreenManager != null) {
mKeyguardLockScreenManager.onDestroy();
mKeyguardLockScreenManager = null;
}
mResultListener = null;
mShowToastRunnable = null;
mToast = null;
super.onDestroy();
}
}
activity_login.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”>

<LinearLayout
android:id=”@+id/digital_layout”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:layout_gravity=”center_vertical”
android:gravity=”center_vertical”>

<EditText
android:id=”@+id/unlock_num”
android:layout_width=”270dp”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:gravity=”center_horizontal”
android:inputType=”number”
android:hint=”input your unlocking nums:”
android:textAllCaps=”false”/>
<Button
android:id=”@+id/psw_login”
android:layout_width=”270dp”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:gravity=”center_horizontal”
android:text=”@string/login2″
android:textAllCaps=”false”
android:textSize=”20sp”/>
<Button
android:id=”@+id/use_fingerprint”
android:layout_width=”270dp”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:gravity=”center_horizontal”
android:text=”@string/use_fingerprint”
android:textAllCaps=”false”
android:textSize=”20sp”/>
</LinearLayout>

</LinearLayout>
解锁只是 个人工程的一部分,具体可参考下面链接:
完整代码见:https://github.com/fyang21117/RDIOT1

Android开发之多Fragment切换优化

问题分析

 


 

一直在简书里看别人的技术贴,今天我也来写点自己的心得!*近在写一个项目用到大量的Fragment后的总结!

我想刚刚接触安卓的同学或许会这么写:

  1. FragmentManager     fragmentManager=getSupportFragmentManager();
  2. FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
  3. fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
  4. fragmentTransaction.commit();

基础更好一点的同学会用show和hide方法

  1.  FragmentManager fm = getSupportFragmentManager();
  2. FragmentTransaction ft = fm.beginTransaction();
  3. ft.hide(new FirstFragment())
  4.        .show(new SecondFragment())
  5.        .commit();

诚然这两种都可以切换Fragment,但是面对用户大量点击来回切换,或者你的Fragment本来就很多,每次都这样操作,那么很快你的应用就会OOM,就算不崩那也会异常的卡顿!so why?

当我们replace时发生了以下的生命周期:

 

%title插图%num

想想看每次都replace一下!!这世界会有多美好!!!那么问题出在哪?回过头看看代码就会发现每次在add/replace或者show/hide都会new 一个新的实例,这就是致命原因!!!!!

废话少说,开始优化

 


 

方案一:

预加载模式:

  1. //首先需要先实例好三个全局Fragment
  2. FragmentManager fm = getSupportFragmentManager();
  3. FragmentTransaction ft = fm.beginTransaction();
  4. ft.add(R.id.fragment, FirstFragment.getInstance());
  5. ft.add(R.id.fragment, SecondFragment.getInstance());
  6. ft.add(R.id.fragment, ThirdFragment.getInstance());
  7. ft.hide(SecondFragment.getInstance());
  8. ft.hide(ThirdFragment.getInstance());
  9. ft.commit();

在加载*个Fragment时就把全部Fragment加载好,下次使用直接调用如:

  1. FragmentManager fm = getSupportFragmentManager();
  2. FragmentTransaction ft = fm.beginTransaction();
  3. ft.hide(FirstFragment.getInstance())
  4.        .show(SecondFragment.getInstance())
  5.        .commit();

是不是总觉怪怪的,虽然比之前的代码好,但是这种做法很Java,当然需要预加载的朋友依然是不二之选!!!

那有没有更好的方法呢?答案是肯定的

方案二:

动态加载模式:

 


//首先需要先实例好n个全局Fragment

//private  Fragment  currentFragment=new Fragment();(全局)

 

private  FragmentTransaction switchFragment(Fragment targetFragment) {    FragmentTransaction transaction = getSupportFragmentManager()            .beginTransaction();    if (!targetFragment.isAdded()) {        //*次使用switchFragment()时currentFragment为null,所以要判断一下        if (currentFragment != null) {            transaction.hide(currentFragment);            }        transaction.add(R.id.fragment, targetFragment,targetFragment.getClass().getName());        } else {            transaction                    .hide(currentFragment)                    .show(targetFragment);        }        currentFragment = targetFragment;       return   transaction;    }

在点击切换Fragment时:

  1. @Override
  2. public void onTabSelected(@IdRes int tabId) {
  3.        if (tabId == R.id.tab_one){
  4.            switchFragment(first).commit();
  5.        }
  6.        if (tabId == R.id.tab_two){
  7.            switchFragment(second).commit();
  8.        }
  9.        if (tabId == R.id.tab_three){
  10.            switchFragment(third).commit();
  11.        }
  12.    }

现在你的Fragment无论怎么切都不会出现卡顿了,因为你的所有Fragment只会被实例化一次!实例一次的Fragment会被存入内存中,下次切换会判断内存中是否含有要切换的Fragment,如果有就直接复用,没有就add一个新的!优化大法完成!

外番

 


 

WHAT?等等!只实例一次,那我的Fragment里的数据要更新怎么办?我的回答是——软件关了再次重启!

 

%title插图%num

要是这样,这样的软件真的要逆天了!好在官方提供了onHiddenChanged方法,每次切换hide或者show时该方法会被执行,可以在这里面更新数据!


//此方法在Fragment中

@Override public void onHiddenChanged(boolean hidden) {    super.onHiddenChanged(hidden);    if (hidden){       //Fragment隐藏时调用    }else {        //Fragment显示时调用    } }

此方法是不是比每次add或replace更新数据执行一大坨的生命周期要优雅的多的多!

Android开发各类常见错误解决方案

本文属于个人平时项目开发过程遇到的一些问题,记录下来并总结解决方案,希望能帮到大家解决问题,有些问题的解决方案是在StackoverFlow上找到的,建议大家遇到问题多去上面找,基本上都能找到解决方案的。

(1)将Eclipse项目导入到Android studio 中 很多点9图出现问题解决方法: 在build.gradle里添加以下两句:

com.android.bui" data-snippet-id="ext.fe2015d4a16367e45ed83e2595c5311a" data-snippet-saved="false" data-codota-status="done">Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. > com.android.bui
  • 解决方法:
    在build.grade中添加以下代码:
  1. android{
  2. packagingOptions {
  3. exclude ‘META-INF/DEPENDENCIES.txt’
  4. exclude ‘META-INF/NOTICE’
  5. exclude ‘META-INF/NOTICE.txt’
  6. exclude ‘META-INF/LICENSE’
  7. exclude ‘META-INF/LICENSE.txt’
  8. }
  9. }

(4)未知错误

  1. Error:Timeout waiting to lock cp_proj class cache for build file ‘/Users/Mr.xiao/Desktop/AndroidShopNC2014MoblieNew/androidShopNC2014Moblie/build.gradle’
  2. (/Users/Mr.xiao/.gradle/caches/2.10/scripts/build_3cyr7hzjurcc62ge3ixidshos/cp_proj).
  3. It is currently in use by another Gradle instance.
  4. Owner PID: unknown
  5. Our PID: 1412
  6. Owner Operation: unknown
  7. Our operation: Initialize cache
  8. Lock file: /Users/Mr.xiao/.gradle/caches/2.10/scripts/build_3cyr7hzjurcc62ge3ixidshos/cp_proj/cache.properties.lock
  • 解决方案
    以上是错误提示。
    解决的思路很简单只需要把cache.properties.lock文件删除了就可以了。当时我们删除的时候会被占用这时候需要进入任务管理器结束关于java的进程就行比如 java 的jdk 删除后重启让java jdk启动 启动Android Studio就能启动APK了。

(5)修改了Android项目的*小SDK版本之后出现很多stysle文件找不到

  • 解决方案
  1. compileSdkVersion 23
  2. buildToolsVersion “23.0.3”
  3. defaultConfig {
  4. applicationId “net.mmloo2014.android”
  5. minSdkVersion 14
  6. targetSdkVersion 23
  7. }

compileSdkVersion 是多少版本的

那么compile ‘com.android.support:appcompat-v7:23.2.1’ 就是啥版本的。

(6)Android studio 编译问题:finished with non-zero exit value 2

  • 问题:
  1. Error:Execution failed for task ‘:androidShopNC2014Moblie:transformClassesWithDexForDebug’.
  2. >
  3. com.android.build.api.transform.TransformException:
  4. com.android.ide.common.process.ProcessException:
  5. java.util.concurrent.ExecutionException:
  6. com.android.ide.common.process.ProcessException:
  7. org.gradle.process.internal.ExecException:
  8. Process ‘command ‘/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java finished with non-zero exit value 2
  • 解决方案
    这个错误在app的build.gradle里面添加下面这句就好了。
  1. android {
  2. defaultConfig {
  3. multiDexEnabled true
  4. }
  5. }

(7)Android studio 编译问题:finished with non-zero exit value 1(由于导入的依赖出现重复造成的)

  • 问题:
  1. Error:Execution failed for task ‘:app:transformClassesWithDexForDebug’.
  2. > com.[Android](http://lib.csdn.net/base/15).build.api.transform.TransformException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process ‘command ‘F:\Program Files (x86)\[Java](http://lib.csdn.net/base/17)\jdk1.8.0_31\bin\java.exe” finished with non-zero exit value 1
  • 解决方案
    这个是因为依赖包重复了 (像v4和nineoldandroids),app中实现了对easeUI的依赖,但是app和easeUI都添加了对这个包的依赖。所以就报这个错误,修改之后再报,就clean,rebuild一下。

(8)问题

  1. Error:Execution failed for task
  2. ‘:app:transformClassesWithJarMergingForDebug’.>
  3. com.android.build.api.transform.TransformException:
  4. java.util.zip.ZipException:
  5. duplicate entry: org/apache/http/ConnectionClosedException.class
  • 解决方案
    这个是在我们启动的时候报错的,而不是在编译的时候,原因是这样的,报这个错是因为有2个库中存在相同的类。大家可以看到stackoverflow上有人也提了这样的问题。只需要删除其中的一个就可以解决了。

(9)添加第三方依赖出现的问题

  1. Error:Execution failed for task ‘:app:processDebugManifest’.
  2. >
  3. Manifest merger failed :
  4. uses-sdk:minSdkVersion 14 cannot be smaller than version 19 declared in library [com.github.meikoz:basic:2.0.3]
  5. /AndroidStudioCode/EnjoyLife/app/build/intermediates/exploded-aar/
  6. com.github.meikoz/basic/2.0.3/AndroidManifest.xml
  7. Suggestion: use tools:overrideLibrary=“com.android.core” to force usage
  • 错误原因
    出现这个错误的原因是我引入的第三方库*低支持版本高于我的项目的*低支持版本,异常中的信息显示:我的项目的*低支持版本为14,而第三方库的*低支持版本为19,所以抛出了这个异常。
  • 解决方案
    在AndroidManifest.xml文件中标签中添加
  1. <uses-sdk tools:overrideLibrary=“xxx.xxx.xxx”/>

其中的xxx.xxx.xxx为第三方库包名,如果存在多个库有此异常,则用逗号分割它们,例如:

<uses-sdk tools:overrideLibrary="xxx.xxx.aaa, xxx.xxx.bbb"/>

这样做是为了项目中的AndroidManifest.xml和第三方库的AndroidManifest.xml合并时可以忽略*低版本限制。

(10)Android studio 编译问题:finished with non-zero exit value 1(由于buildtools版本太高造成的)

  • 错误
  1. Error:Execution failed for task ‘:app:transformClassesWithDexForDebug’.
  2. > com.android.ide.common.process.ProcessException:
  3. org.gradle.process.internal.ExecException:
  4. Process ‘command ‘/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java finished with non-zero exit value 1
  • 错误原因
    buildToolsVersion版本太高,我原来的 buildToolsVersion “24.0.0” 需要jdk1.8,而我的是jdk1.7,所以一直报这个错,刚开始以为是v4包和V7包冲突,因为之前遇到这样的问题,而这次删除V4包之后依然报这个错,上stackoverflow搜了一下,把buildTools版本降下来就好了。
  • 解决方案
  1. android {
  2. compileSdkVersion 23
  3. buildToolsVersion “23.0.3”
  4. }

(11)Android studio 编译问题:Gradle DSL not found ‘android()’

  • 问题
clipboard.png

clipboard.png
  • 解决方案
  • 配置build.gradle:
  1. buildscript {
  2. repositories {
  3. jcenter()
  4. }
  5. dependencies {
  6. classpath ‘com.android.tools.build:gradle:2.1.2’
  7. }
  8. }
  9. allprojects {
  10. repositories {
  11. jcenter()
  12. }
  13. }
  14. buildscript {
  15. repositories {
  16. jcenter()
  17. }
  18. dependencies {
  19. classpath ‘com.android.tools.build:gradle:2.1.2’
  20. }
  21. }
  22. allprojects {
  23. repositories {
  24. jcenter()
  25. }
  26. }
  • 配置app/build.gradle:
  1. apply plugin: ‘com.android.application’android {
  2. compileSdkVersion 23
  3. buildToolsVersion ‘23.0.3’
  4. defaultConfig {
  5. minSdkVersion 9
  6. targetSdkVersion 23
  7. versionCode 1
  8. versionName ‘1.0’
  9. }
  10. }
  11. dependencies {
  12. compile ‘com.android.support:appcompat-v7:23.2.1’
  13. }

*后再同步一下sync即可。

(12)Android studio 编译问题:Gradle DSL not found ‘android()’

  • 问题描述
  1. Error:(51, 52) 错误: -source 1.6 中不支持 diamond 运算符
  2. (请使用 -source 7 或更高版本以启用 diamond 运算符)
  • 解决方案
  • 方案一
将标红处设置为1.7.png

将标红处设置为1.7.png
修改soure为1.7.png

修改soure为1.7.png
  • 方案二
    在build gradle中进行配置如下代码:
  1. android {
  2. compileOptions {
  3. sourceCompatibility JavaVersion.VERSION_1_7
  4. targetCompatibility JavaVersion.VERSION_1_7
  5. }
  6. }

*后同步一下即可

(13)Glide使用问题:使用Glide加载圆角图片,*次显示占位图

  • 问题描述
    *近在项目中使用Glide加载圆形图片,并且设置placehloder和error两个占位图,运行发现,*次加载图片只显示占位图,需要第二次进入的时候才会正常显示。
    如果你刚好使用了这个圆形Imageview库或者其他的一些自定义的圆形Imageview,而你又刚好设置了占位的话,那么,你就会遇到*个问题。如何解决呢?
  • 方案一
    不设置占位图
  • 方案二
    使用Glide的Transformation API自定义圆形Bitmap的转换
  1. /**
  2. * Glide圆形图片处理
  3. */
  4. static class CircleTransform extends BitmapTransformation {
  5. public CircleTransform(Context context) {
  6. super(context);
  7. }
  8. @Override
  9. protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
  10. return circleCrop(pool, toTransform);
  11. }
  12. private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
  13. if (source == null) return null;
  14. int size = Math.min(source.getWidth(), source.getHeight());
  15. int x = (source.getWidth() – size) / 2;
  16. int y = (source.getHeight() – size) / 2;
  17. Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
  18. Bitmap result = pool.get(size, size, Bitmap.Config.RGB_565);
  19. if (result == null) {
  20. result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
  21. }
  22. Canvas canvas = new Canvas(result);
  23. Paint paint = new Paint();
  24. paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
  25. paint.setAntiAlias(true);
  26. float r = size / 2f;
  27. canvas.drawCircle(r, r, r, paint);
  28. return result;
  29. }
  30. @Override
  31. public String getId() {
  32. return getClass().getName();
  33. }
  34. }

使用方法:

 Glide.with(context).load(imageUrl).placeholder(placeholder).error(errorImage).transform(new CircleTransform(context)).into(imageView);

方案三
重写Glide的图片加载监听方法,具体如下:

  1. Glide.with(mContext)
  2. .load(url)
  3. .placeholder(R.drawable.loading_drawable)
  4. .into(new SimpleTarget<Bitmap>(width, height) {
  5. @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
  6. // setImageBitmap(bitmap) on CircleImageView
  7. }
  8. });

注意事项:
该方法在listview上复用有问题的bug,如果在listview中加载CircleImageView,请不要使用该方法。

方案四:不使用Glide的默认动画:

  1. Glide.with(mContext)
  2. .load(url)
  3. .dontAnimate()
  4. .placeholder(R.drawable.loading_drawable)
  5. .into(circleImageview);

(14)json数据解析问题:json串头部出现字符:”\ufeff” 解决方法
异常信息

org.json.JSONException: Value  of type java.lang.String cannot be converted to JSONObject

解析服务器返回 的json格式数据时,我们可能会发现,数据格式上是没有问题的,但是仔细对比会发现,在json串头部发现字符:”\ufeff”

客户端解决方案:

  1. /**
  2. * 异常信息:org.json.JSONException: Value of type java.lang.String cannot be converted to JSONObject
  3. * json串头部出现字符:”\ufeff” 解决方法
  4. * @param data
  5. * @return
  6. */
  7. public static final String removeBOM(String data) {
  8. if (TextUtils.isEmpty(data)) {
  9. return data;
  10. }
  11. if (data.startsWith(“\ufeff”)) {
  12. return data.substring(1);
  13. }
  14. else {
  15. return data;
  16. }
  17. }

服务器端解决方案:
将输出此json的php源码重新用editplus之类用utf-8无BOM的编码保存。不要用windows系统自带的记事本编辑php源码,这个BOM就是记事本这些windows自带的编辑器引入的。

(15)Android studio编译问题:not found ndk()
问题

Error:(15, 0) Gradle DSL method not found: 'ndk()' method-not-found-ndk

解决方案
出现该问题,可能是由于ndk配置在build.gradle配置文件中位置弄错导致的

  1. apply plugin: ‘com.android.application’
  2. android {
  3. compileSdkVersion 23
  4. buildToolsVersion “23.0.2”
  5. defaultConfig {
  6. applicationId “com.guitarv.www.ndktest”
  7. minSdkVersion 17
  8. targetSdkVersion 23
  9. versionCode 1
  10. versionName “1.0”
  11. ndk {
  12. moduleName = “HelloJNI”
  13. }
  14. sourceSets.main {
  15. jni.srcDirs = []
  16. jniLibs.srcDir “src/main/libs”
  17. }
  18. }
  19. buildTypes {
  20. release {
  21. minifyEnabled false
  22. proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
  23. }
  24. }
  25. }

(16)Android studio导入其他的项目:UnsupportedMethodException
问题

  1. UnsupportedMethodException
  2. Unsupported method: AndroidProject.getPluginGeneration().
  3. The version of Gradle you connect to does not support that method.
  4. To resolve the problem you can change/upgrade the target version of Gradle you connect to.
  5. Alternatively, you can ignore this exception and read other information from the model.
错误截图

错误截图

解决方案

将根目录中的build.gradle文件中的gradle版本号,出现错误之前,我的是1.3.0,修改成2.2.0之后重新编译一下就可以运行了。

  1. dependencies {
  2. classpath ‘com.android.tools.build:gradle:1.3.0’
  3. }

将这个版本号改成你其他项目能够运行成功的版本号即可

(17)Android studio更新到2.1.1之后使用CollapsingToolbarLayout出现Error inflating class CollapsingToolbarLayout
之前在项目中使用了CollapsingToolbarLayout,效果还是可以的,但是Android stuido更新到2.1.1版本之后出现Error inflating class CollapsingToolbarLayout 异常崩溃
异常信息如下所示:

  1. com.test.android/com.test.android.ui.activity.RandomActivity}: android.view.InflateException: Binary XML file line #22: Error inflating class android.support.design.widget.CollapsingToolbarLayout
  2. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
  3. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
  4. at android.app.ActivityThread.access$800(ActivityThread.java:151)
  5. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
  6. at android.os.Handler.dispatchMessage(Handler.java:102)
  7. at android.os.Looper.loop(Looper.java:135)
  8. at android.app.ActivityThread.main(ActivityThread.java:5254)
  9. at java.lang.reflect.Method.invoke(Native Method)
  10. at java.lang.reflect.Method.invoke(Method.java:372)
  11. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
  12. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
  13. Caused by: android.view.InflateException: Binary XML file line #22: Error inflating class android.support.design.widget.CollapsingToolbarLayout
  14. at android.view.LayoutInflater.createView(LayoutInflater.java:633)
  15. at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
  16. at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
  17. at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
  18. at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
  19. at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
  20. at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
  21. at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
  22. at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:276)
  23. at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:136)
  24. at com.test.android.ui.activity.RefreshableActivity.onCreate(RefreshableActivity.java:31)
  25. at android.app.Activity.performCreate(Activity.java:5990)
  26. at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
  27. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
  28. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
  29. at android.app.ActivityThread.access$800(ActivityThread.java:151)
  30. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
  31. at android.os.Handler.dispatchMessage(Handler.java:102)
  32. at android.os.Looper.loop(Looper.java:135)
  33. at android.app.ActivityThread.main(ActivityThread.java:5254)
  34. at java.lang.reflect.Method.invoke(Native Method)
  35. at java.lang.reflect.Method.invoke(Method.java:372)
  36. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
  37. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
  38. Caused by: java.lang.NoSuchMethodError: No static method setLayoutDirection(Landroid/graphics/drawable/Drawable;I)V in class Landroid/support/v4/graphics/drawable/DrawableCompat; or its super classes (declaration of ‘android.support.v4.graphics.drawable.DrawableCompat’ appears in /data/app/com.test.android-1/base.apk)
  39. at android.support.design.widget.CollapsingToolbarLayout.setStatusBarScrim(CollapsingToolbarLayout.java:663)
  40. at android.support.design.widget.CollapsingToolbarLayout.<init>(CollapsingToolbarLayout.java:197)
  41. at android.support.design.widget.CollapsingToolbarLayout.<init>(CollapsingToolbarLayout.java:132)
  42. at java.lang.reflect.Constructor.newInstance(Native Method)
  43. at java.lang.reflect.Constructor.newInstance(Constructor.java:288)
  44. at android.view.LayoutInflater.createView(LayoutInflater.java:607)
  45. at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
  46. at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
  47. at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
  48. at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
  49. at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
  50. at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
  51. at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
  52. at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:276)
  53. at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:136)
  54. at com.test.android.ui.activity.RefreshableActivity.onCreate(RefreshableActivity.java:31)
  55. at android.app.Activity.performCreate(Activity.java:5990)
  56. at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
  57. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
  58. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
  59. at android.app.ActivityThread.access$800(ActivityThread.java:151)
  60. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
  61. at android.os.Handler.dispatchMessage(Handler.java:102)
  62. at android.os.Looper.loop(Looper.java:135)
  63. at android.app.ActivityThread.main(ActivityThread.java:5254)
  64. at java.lang.reflect.Method.invoke(Native Method)
  65. at java.lang.reflect.Method.invoke(Method.java:372)

解决方案
在项目的build.gradle文件中添加下面一行,同步一下即可

  1. compile (‘com.android.support:support-v4:23.4.0’){
  2. force = true;
  3. }

StackOverFlow解决方案

(18)Android studio gradle编译异常

java.lang.UnsupportedClassVersionError: com/android/build/gradle/AppPlugin : Unsupported major.minor version 52.0

很显然是class版本不支持。经查询,Android Studio2.2必须使用JDK8及以上版本,而且是强制的。
所以呢,赶紧下了个JDK8*新版的。安装完毕,把JAVA_HOME指向了JDK8,实测JDK7和8是可以共存的。
那么,重启Android Studio后问题解决,Build Successful !

(19)电脑突然断电,Android studio 工程代码全部报错,找不到android sdk 的依赖包,clean、重启都没有用
前几天公司搬家,正准备同步代码,突然断电、等把电脑搬到新办公楼,打开AS发现所有的项目代码报错,找不到android 依赖包,clean、重启都没有用,

(20)recycleview嵌套列表项显示不全问题
解决方案:

*个RecyclerView的Adapter(即父RecyclerView):

  1. @Override
  2. public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  3. View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.shop_item,null); 解决条目显示不全
  4. MyHolder holder = new MyHolder(view);
  5. return holder;
  6. }

第二个RecyclerView的Adapter(即子RecyclerView):

  1. @Override
  2. public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  3. View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.check_item, parent,false);//解决宽度不能铺满
  4. MyHolder holder = new MyHolder(view);
  5. return holder;
  6. }

(21)Android手机真机调试,日志不打印的解决方案:

  1. 1、在拨号界面输入:*#*#2846579#*#* 进入测试菜单界面。
  2. 2、Project Menu–后台设置–LOG设置
  3. 3LOG开关–LOG打开 LOG级别设置–VERBOSE
  4. 4、Dump&Log– 全部选中
  5. 5、重启手机

(22)java.lang.IndexOutOfBoundsException Inconsistency detected. Invalid item position 2(offset:2).state:4
解决方案:
Recyclerview在下拉刷新时,如果在数据没更新到之前将list clear 之后,迅速滑动会造成crash,所以一般在下拉刷新之前,等数据刷新回来再把之前的数据进行清除。

(23)**使用友盟分享——微信、朋友圈分享出现java.lang.NoClassDefFoundError: org.apache.http.entity.mime.MultipartEntity
**
解决方案: 造成这样的原因是因为缺少httpmime_jar,添加是httpmime_jar包之后即可正常分享

(24)Fragment中调用getActivity()出现空指针异常
解决方案:

  1. 对于上面的问题,可以考虑下面这两种解决办法:
  2. 1、不保存fragment的状态:在MyActivity中重写onSaveInstanceState方法,将super.onSaveInstanceState(outState);注释掉,让其不再保存Fragment的状态,达到fragment随MyActivity一起销毁的目的。
  3. 2、重建时清除已经保存的fragment的状态:在恢复Fragment之前把Bundle里面的fragment状态数据给清除。方法如下:
  4. if(savedInstanceState!= null)
  5. {
  6. String FRAGMENTS_TAG = “android:support:fragments”;
  7. savedInstanceState.remove(FRAGMENTS_TAG);
  8. }

(25)RecyclerView嵌套使用切换页面出现自动滚动问题
原因:

造成这样的原因是由于子RecyclerView抢占焦点导致的,如果你去查看RecyclerView的源码会发现,它会在构造方法中调用setFocusableInTouchMode(true),所以,设为false可以解决这个问题。
解决方案
在子RecyclerView中调用如下方法

  1. //设置焦点不需要
  2. secondRvList.setFocusableInTouchMode(false);
  3. secondRvList.requestFocus();

(26)Android 7.0设备拍照闪退问题
原因:

Android 7.0 做了一些系统权限更改,为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问,此设置可防止私有文件的元数据泄漏,如它们的大小或存在性。而此权限更改有多重副作用,其中之一就是当传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider。在应用间共享文件对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。要在应用间共享文件,应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的*简单方式是使用 FileProvider 类。点击查看Android官方说明
解决方案
1.在清单文件添加如下代码

  1. <provider
  2. android:name=“android.support.v4.content.FileProvider”
  3. android:authorities=“你的应用包名.fileProvider”
  4. android:exported=“false”
  5. android:grantUriPermissions=“true”>
  6. <meta-data
  7. android:name=“android.support.FILE_PROVIDER_PATHS”
  8. android:resource=“@xml/provider_paths”/>
  9. </provider>
android:authorities="com.alex.demo.FileProvider" 自定义的权限  
android:exported="false" 是否设置为独立进程  
android:grantUriPermissions="true" 是否拥有共享文件的临时权限  
android:resource="@xml/external_storage_root" 共享文件的文件根目录,名字可以自定义  

2.在xml文件夹目录下新建provider_paths文件,名字自定义,添加如下代码

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <resources>
  3. <paths>
  4. <external-path
  5. name=“camera_photos”
  6. path=“” />
  7. </paths>
  8. </resources>

3.调用系统相机处代码处理

  1. //调用系统相机拍照
  2. Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  3. if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null) {
  4. cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, parUri(tempFile));
  5. startActivityForResult(cameraIntent, REQUEST_CAMERA);
  6. }
  7. /**
  8. * 生成uri
  9. *
  10. * @param cameraFile
  11. * @return
  12. */
  13. private Uri parUri(File cameraFile) {
  14. Uri imageUri;
  15. String authority = getContext().getPackageName()+ “.provider”;
  16. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  17. //通过FileProvider创建一个content类型的Uri
  18. imageUri = FileProvider.getUriForFile(getContext(), authority, cameraFile);
  19. } else {
  20. imageUri = Uri.fromFile(cameraFile);
  21. }
  22. return imageUri;
  23. }
友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速