├── README.md ├── algorithm └── swordForOffer │ ├── 二叉搜索树与双向链表.md │ ├── 二叉树中 和为某值 的所有路径.md │ ├── 二叉树的镜像.md │ ├── 二维数组中的查找.md │ ├── 二进制中1的个数.md │ ├── 从上往下打印二叉树.md │ ├── 从尾到头打印链表.md │ ├── 判断二叉搜索树的后序遍历序列.md │ ├── 判断栈的弹出序列.md │ ├── 判断树B是不是树A的子结构.md │ ├── 包含min函数的栈.md │ ├── 反转链表.md │ ├── 变态跳台阶.md │ ├── 合并两个排序链表.md │ ├── 复杂链表的复制.md │ ├── 字符串中空格替换.md │ ├── 字符串的顺序全排列.md │ ├── 数组中出现次数超过一半的数字.md │ ├── 斐波那契数列.md │ ├── 旋转数组的最小数字.md │ ├── 浮点数的整数次方.md │ ├── 用两个栈实现队列.md │ ├── 矩形覆盖.md │ ├── 调整数组顺序使奇数位于偶数前面.md │ ├── 跳台阶.md │ ├── 重建二叉树.md │ ├── 链表中倒数第k个结点.md │ └── 顺时针打印矩阵.md ├── android ├── AIDL的使用情况和实例介绍.md ├── Android SQLite的使用入门.md ├── Android View事件分发机制源码分析.md ├── Android 名企面试题及答案整理(一).md ├── Android 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析.md ├── Android 数据存储五种方式使用与总结.md ├── Android 源码中的设计模式(你需要知道的设计模式全在这里).md ├── Android 缓存机制.md ├── Android 自定义ViewGroup入门实践.md ├── Android 自定义View入门.md ├── Android5.0、6.0、7.0新特性.md ├── Android中弱引用与软引用的应用场景.md ├── Android启动过程图解.md ├── Android异步任务机制之AsycTask.md ├── Android长连接,怎么处理心跳机制.md ├── Asset目录与res目录的区别.md ├── Binder机制原理和底层实现.md ├── BroadcastReceiver使用总结.md ├── ContentProvider实例详解.md ├── Fragment 全解析.md ├── IntentService使用详解和实例介绍.md ├── Json优劣势.md ├── ListView优化.md ├── Service全面总结.md ├── activity切换动画.md ├── android中图片缓存.md ├── 两类动画.md ├── 五大布局易混淆知识.md ├── 保证service不被杀死.md ├── 全面了解Activity.md ├── 加速启动activity.md └── 怎样退出终止App.md ├── data structure ├── [数据结构] AVL树和AVL旋转、哈夫曼树和哈夫曼编码.md ├── [数据结构] B(B-)树、B+树、B树.md ├── [数据结构] Hash表、Hash函数及冲突解决.md ├── [数据结构] KMP的一个简单解释.md ├── [数据结构] 九大基础排序总结与对比.md ├── [数据结构] 二分查找与变种二分查找.md ├── [数据结构] 二叉树前中后、层次遍历算法.md ├── [数据结构] 图的BFS、DFS、prim、Dijkstra算法.md ├── [数据结构] 字符串操作.md ├── [数据结构] 数组与链表的优缺点和区别.md ├── [数据结构] 红黑树.md └── [数据结构] 队列和栈.md ├── java ├── Java与C++对比.md ├── [Java] ArrayList、LinkedList、Vector的区别.md ├── [Java] Collection包结构,与Collections的区别.md ├── [Java] Excption与Error包结构,OOM和SOF.md ├── [Java] HashMap和HashTable的区别.md ├── [Java] HashMap源码分析.md ├── [Java] Hashcode的作用.md ├── [Java] Map、Set、List、Queue、Stack的特点与用法.md ├── [Java] Object有哪些公用方法?.md ├── [Java] Override和Overload的使用规则和区别.md ├── [Java] Switch能否用string做参数?.md ├── [Java] ThreadLocal的使用规则和源码分析.md ├── [Java] ThreadPool用法与示例.md ├── [Java] equals与==的区别.md ├── [Java] try catch finally,try里有return,finally还执行么?.md ├── [Java] 九种基本数据类型的大小,以及他们的封装类.md ├── [Java] 从源码分析String、StringBuffer与StringBuilder区别和联系.md ├── [Java] 多线程下生产者消费者问题的五种同步方法实现.md ├── [Java] 实现多线程的两种方法.md ├── [Java] 接口(Interface)与 抽象类 (Abstract)使用规则和区别.md ├── [Java] 方法锁、对象锁和类锁的意义和区别.md ├── [Java] 的四种引用,强弱软虚,用到的场景.md ├── [Java] 线程同步的方法:sychronized、lock、reentrantLock分析.md ├── [Java] 集合框架的层次结构和使用规则梳理.md ├── [Java] 面向对象的三个特征与含义.md ├── [java] static的作用和意义.md ├── [java] 多态实现的JVM调用过程.md ├── git命令使用.md └── wait()和sleep()的区别.md └── 简历模板.docx /algorithm/swordForOffer/二叉搜索树与双向链表.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/二叉搜索树与双向链表.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/二叉树中 和为某值 的所有路径.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/二叉树中 和为某值 的所有路径.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/二叉树的镜像.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/二叉树的镜像.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/二维数组中的查找.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/二维数组中的查找.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/二进制中1的个数.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/二进制中1的个数.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/从上往下打印二叉树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/从上往下打印二叉树.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/从尾到头打印链表.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/从尾到头打印链表.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/判断二叉搜索树的后序遍历序列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/判断二叉搜索树的后序遍历序列.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/判断栈的弹出序列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/判断栈的弹出序列.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/判断树B是不是树A的子结构.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/判断树B是不是树A的子结构.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/包含min函数的栈.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/包含min函数的栈.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/反转链表.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/反转链表.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/变态跳台阶.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/变态跳台阶.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/合并两个排序链表.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/合并两个排序链表.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/复杂链表的复制.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/复杂链表的复制.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/字符串中空格替换.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/字符串中空格替换.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/字符串的顺序全排列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/字符串的顺序全排列.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/数组中出现次数超过一半的数字.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/数组中出现次数超过一半的数字.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/斐波那契数列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/斐波那契数列.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/旋转数组的最小数字.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/旋转数组的最小数字.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/浮点数的整数次方.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/浮点数的整数次方.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/用两个栈实现队列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/用两个栈实现队列.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/矩形覆盖.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/矩形覆盖.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/调整数组顺序使奇数位于偶数前面.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/调整数组顺序使奇数位于偶数前面.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/跳台阶.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/跳台阶.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/重建二叉树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/重建二叉树.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/链表中倒数第k个结点.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/链表中倒数第k个结点.md -------------------------------------------------------------------------------- /algorithm/swordForOffer/顺时针打印矩阵.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/algorithm/swordForOffer/顺时针打印矩阵.md -------------------------------------------------------------------------------- /android/AIDL的使用情况和实例介绍.md: -------------------------------------------------------------------------------- 1 | #AIDL是什么? 2 | 3 |   AIDL (Android Interface Definition Language), Android接口定义语言,Android提供的IPC (Inter Process Communication,进程间通信)的一种独特实现。 4 | 5 | #什么情况下要使用AIDL 6 | 7 | 8 |   使用AIDL只有在你允许来自不同应用的客户端跨进程通信访问你的service,并且想要在你的service种处理**多线程**的时候才是必要的。 如果你不需要执行不同应用之间的IPC并发,你应该通过实现Binder建立你的接口,或者如果你想执行IPC,但是不需要处理多线程。那么使用Messenger实现你的接口。 9 | 10 | #定义一个AIDL接口的步骤 11 | 12 |   必须在一个.aidl文件中使用java编程语言语法定义你的AIDL接口,然后在提供service的应用中和任何绑定到这个service的应用中的源代码中(在src目录吓)保存它。 13 |    14 |   当你编译包含.aidl文件的应用时,Android SDK工具基于这个.aidl文件生成一个IBinder接口,并且把它保存到项目的gen目录吓.service必须恰当的实现这个IBinder接口 之后客户端应用可以绑定到这个服务上,然后从IBinder调用方法来执行IPC。 15 | 16 | 使用AIDL建立一个邻接的service需要遵循下面的步骤: 17 | 18 | 1. 建立.aidl文件  19 | 20 |   这个文件使用方法签名定义了语言接口  21 |    22 | 2.实现这个接口  23 | 24 |   Android SDk工具基于你的.aidl文件使用java语言生成一个接口 这个接口有一个内部抽象类,叫做Stub,它是继承Binder并且实现你AIDL接口的 你必须继承这个Stub类并且实现这些方法 25 |    26 | 3.暴露这个接口给客户端  27 | 28 |   实现一个service并且覆盖onBind()方法返回你的Stub实现类。 29 | 30 | > 你的.aidl文件必须被复制到其他应用程序中来让他们访问你service的接口,你必须维护原始接口的支持(向后兼容)。 31 | 32 | #用一个实例来分步骤说明 33 | 34 | ##在server项目中建立.aidl文件  35 | 36 | ![这里写图片描述](http://img.blog.csdn.net/20160504180944041) 37 | 38 |   AIDL使用一个简单的语法让你声明一个带有一个或者多个带有参数和返回值方法的接口 参数和返回值可以是任何类型,甚至是AIDL生成的接口。 39 |    40 |   IService.aidl 41 |    42 | 43 | ``` 44 | 45 | package com.example.aidl; 46 | 47 | 48 | interface IService { 49 | 50 | String hello(String name); 51 | } 52 | ``` 53 | 54 | ##在server项目中建立服务类 55 | 56 |   当你编译你的应用时,Android SDK工具生成一个.java接口文件用你的.aidl文件命名生成的接口包含一个名字为Stub的子类,这是一个它父类的抽象实现,并且声明了.aidl中所有的方法。 57 |    58 |   Stub也定义了一些辅助的方法,最显著的就是asInterface(),它是用来接收一个IBinder(通常IBinder传递给客户端的onServiceConnected()回调方法)并且返回一个Stub接口的实例 。 59 | 60 |   一旦你为service实现了接口,你需要把它暴露给客户端,这样他们才能绑定到上面 为了给你的service暴露接口,继承Service并且实现onBind()方法返回一个你实现生成的Stub类。 61 | 62 |    AIDLService.java 63 | 64 | ``` 65 | public class AIDLService extends Service { 66 | 67 | 68 | @Override 69 | public void onCreate() { 70 | super.onCreate(); 71 | } 72 | 73 | @Override 74 | public IBinder onBind(Intent intent) { 75 | // Return the interface 76 | return new IService.Stub() { 77 | @Override 78 | public String hello(String name) throws RemoteException { 79 | // TODO Auto-generated method stub 80 | return "hello"+name; 81 | } 82 | }; 83 | } 84 | ``` 85 | 86 | ##在server项目AndroidManifest中申明Service 87 | 88 | ``` 89 | 91 |    92 | 93 |    94 | 95 | ``` 96 | 97 | ##把server项目中的aidl文件带包拷贝到client项目中(包名要相同) 98 | 99 | ![这里写图片描述](http://img.blog.csdn.net/20160504181850678) 100 | 101 | MainActivity.java 102 | 103 | ``` 104 | public class MainActivity extends Activity { 105 | 106 | IService RemoteService; //监听服务 107 | private ServiceConnection mConnection = new ServiceConnection() { 108 | 109 | @Override 110 | public void onServiceConnected(ComponentName name, IBinder service) { 111 | // TODO Auto-generated method stub 112 | Log.i("mConnection", service+""); 113 | RemoteService = IService.Stub.asInterface(service); 114 | 115 | try { 116 | String s= RemoteService.hello("finch"); 117 | Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show(); 118 | } catch (RemoteException e) { 119 | e.printStackTrace(); 120 | } 121 | } 122 | 123 | @Override 124 | public void onServiceDisconnected(ComponentName name) { 125 | // TODO Auto-generated method stub 126 | 127 | } 128 | 129 | }; 130 | 131 | @Override 132 | protected void onCreate(Bundle savedInstanceState) { 133 | super.onCreate(savedInstanceState); 134 | setContentView(R.layout.activity_main); 135 | 136 | initService(); 137 | } 138 | //连接服务 139 | private void initService() { 140 | Intent i = new Intent( ); 141 | i.setAction("android.intent.action.AIDLService"); 142 | boolean ret = bindService(i, mConnection, Context.BIND_AUTO_CREATE); 143 | } 144 | 145 | //断开服务 146 | private void releaseService() { 147 | unbindService(mConnection); 148 | mConnection = null; 149 | } 150 | 151 | @Override 152 | protected void onDestroy() { 153 | super.onDestroy(); 154 | releaseService(); 155 | } 156 | } 157 | ``` 158 | 159 | 运行结果: 160 | 161 | 162 | 163 | 164 | [文章中AIDL例子代码下载](http://download.csdn.net/detail/amazing7/9510133) -------------------------------------------------------------------------------- /android/Android SQLite的使用入门.md: -------------------------------------------------------------------------------- 1 | #概述 2 | 3 |   Android 也提供了几种方法用来保存数据,使得这些数据即使在程序结束以后依然不会丢失。这些方法有:      4 | 5 | - 文本文件: 6 |   可以保存在应用程序自己的目录下,安装的每个app都会在/data/data/目录下创建个文件夹,名字和应用程序中AndroidManifest.xml文件中的package一样。    7 | - SDcard保存: 8 | 9 | - Preferences保存: 10 |   这也是一种经常使用的数据存储方法,因为它们对于用户而言是透明的,并且从应用安装的时候就存在了。 11 | - Assets保存: 12 |   用来存储一些只读数据,Assets是指那些在assets目录下的文件,这些文件在你将你的应用编译打包之前就要存在,并且可以在应用程序运行的时候被访问到。 13 | 14 |   但有时候我们需要对保存的数据进行一些复杂的操作,或者数据量很大,超出了文本文件和Preference的性能能的范围,所以需要一些更加高效的方法来管理,从Android1.5开始,Android就自带SQLite数据库了。 15 |   SQLite它是一个独立的,无需服务进程,支持事务处理,可以使用SQL语言的数据库。 16 | 17 | #SQLite的特性 18 | 19 | 1、 ACID事务   20 |    21 | 22 | >ACID: 23 | >   指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。 24 | 25 | 2、 零配置 – 无需安装和管理配置   26 | 27 | 3、储存在单一磁盘文件中的一个完整的数据库   28 | 29 | 4、数据库文件可以在不同字节顺序的机器间自由的共享 30 | 31 | 5、支持数据库大小至2TB   32 | 33 | 6、 足够小, 大致3万行C代码, 250K   34 | 35 | 7、比一些流行的数据库在大部分普通数据库操作要快   36 | 37 | 8、简单, 轻松的API   38 | 39 | 9、 包含TCL绑定, 同时通过Wrapper支持其他语言的绑定   40 | 41 | > http://www.sqlite.org/tclsqlite.html 42 | 43 | 10、良好注释的源代码, 并且有着90%以上的测试覆盖率 44 | 45 | 11、 独立: 没有额外依赖 46 | 47 | 12、 Source完全的Open, 你可以用于任何用途, 包括出售它 48 | 49 | 13、支持多种开发语言,C,PHP,Perl,Java,ASP.NET,Python 50 | 51 | 52 | #Android 中使用 SQLite 53 | 54 |   Activites 可以通过 Content Provider 或者 Service 访问一个数据库。 55 | 56 | ##创建数据库 57 | 58 |   Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类根据开发应用程序的需要,封装创建和更新数据库使用的逻辑就行了。  59 |    60 |   SQLiteOpenHelper 的子类,至少需要实现三个方法:  61 | 62 | ``` 63 | public class DatabaseHelper extends SQLiteOpenHelper { 64 | 65 | /** 66 | * @param context 上下文环境(例如,一个 Activity) 67 | * @param name 数据库名字 68 | * @param factory 一个可选的游标工厂(通常是 Null) 69 | * @param version 数据库模型版本的整数 70 | * 71 | * 会调用父类 SQLiteOpenHelper的构造函数 72 | */ 73 | public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { 74 | super(context, name, factory, version); 75 | 76 | } 77 | 78 | /** 79 | * 在数据库第一次创建的时候会调用这个方法 80 | * 81 | *根据需要对传入的SQLiteDatabase 对象填充表和初始化数据。 82 | */ 83 | @Override 84 | public void onCreate(SQLiteDatabase db) { 85 | 86 | } 87 | 88 | /** 89 | * 当数据库需要修改的时候(两个数据库版本不同),Android系统会主动的调用这个方法。 90 | * 一般我们在这个方法里边删除数据库表,并建立新的数据库表. 91 | */ 92 | @Override 93 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 94 | //三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号 95 | 96 | } 97 | 98 | @Override 99 | public void onOpen(SQLiteDatabase db) { 100 | // 每次成功打开数据库后首先被执行 101 | super.onOpen(db); 102 | } 103 | } 104 | ``` 105 | 106 | 继承SQLiteOpenHelper之后就拥有了以下两个方法: 107 | 108 | - getReadableDatabase()  创建或者打开一个查询数据库 109 | 110 | - getWritableDatabase() 创建或者打开一个可写数据库 111 | 112 | ``` 113 | DatabaseHelper database = new DatabaseHelper(context);//传入一个上下文参数 114 | SQLiteDatabase db = null; 115 | db = database.getWritableDatabase(); 116 | ``` 117 |   上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就可以查询或者修改数据库。  118 | 119 | SQLiteDatabase类为我们提供了很多种方法,而较常用的方法如下: 120 | 121 | > (int) delete(String table,String whereClause,String[] whereArgs) 122 | 123 |   删除数据行 124 | 125 | > (long) insert(String table,String nullColumnHack,ContentValues values) 126 | 127 |    添加数据行 128 | 129 | > (int) update(String table, ContentValues values, String whereClause, String[] whereArgs) 130 | 131 |   更新数据行 132 | 133 | > (void) execSQL(String sql) 134 | 135 |    执行一个SQL语句,可以是一个select或其他的sql语句 136 | 137 | > (void) close() 138 | 139 |    关闭数据库 140 | 141 | > (Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) 142 | 143 |   查询指定的数据表返回一个带游标的数据集。 144 | 145 | 各参数说明: 146 | table:表名称 147 | colums:列名称数组 148 | selection:条件子句,相当于where 149 | selectionArgs:条件语句的参数数组 150 | groupBy:分组 151 | having:分组条件 152 | orderBy:排序类 153 | limit:分页查询的限制 154 | Cursor:返回值,相当于结果集ResultSet 155 | 156 | 157 | > (Cursor) rawQuery(String sql, String[] selectionArgs) 158 | 159 |   运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别就是防止SQL注入) 160 | 161 | 162 |   当你完成了对数据库的操作(例如你的 Activity 已经关闭),需要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库连接。 163 | 164 | ##创建表和索引 165 | 166 |   为了创建表和索引,需要调用 SQLiteDatabase 的 execSQL() 方法来执行 DDL 语句。如果没有异常,这个方法没有返回值。 167 |   例如,你可以执行如下代码: 168 |    169 | 170 | ``` 171 | db.execSQL("CREATE TABLE user(_id INTEGER PRIMARY KEY 172 | AUTOINCREMENT, username TEXT, password TEXT);"); 173 | ``` 174 | 175 |   这条语句会创建一个名为 user的表,表有一个列名为 _id,并且是主键,这列的值是会自动增长的整数。另外还有两列:username( 字符 ) 和 password( 字符 )。 SQLite 会自动为主键列创建索引。 176 |   通常情况下,第一次创建数据库时创建了表和索引。要 删除表和索引,需要使用 execSQL() 方法调用 DROP INDEX 和 DROP TABLE 语句。 177 | 178 | ##添加数据  179 | 180 |   有两种方法可以给表添加数据。 181 | 182 | ①可以使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于所有不返回结果的 SQL 语句。例如: 183 | 184 | ``` 185 | String sql = "insert into user(username,password) values ('finch','123456');//插入操作的SQL语句 186 | db.execSQL(sql);//执行SQL语句 187 | ``` 188 | 189 | ②使用 SQLiteDatabase 对象的 insert()。 190 | 191 |    192 | 193 | ``` 194 | ContentValues cv = new ContentValues(); 195 | cv.put("username","finch");//添加用户名 196 | cv.put("password","123456"); //添加密码 197 | db.insert("user",null,cv);//执行插入操作 198 | ``` 199 | 200 | ##更新数据(修改) 201 | 202 | ①使用SQLiteDatabase 对象的 update()方法。 203 | 204 | ``` 205 | ContentValues cv = new ContentValues(); 206 | cv.put("password","654321");//添加要更改的字段及内容 207 | String whereClause = "username=?";//修改条件 208 | String[] whereArgs = {"finch"};//修改条件的参数 209 | db.update("user",cv,whereClause,whereArgs);//执行修改 210 | ``` 211 | 该方法有四个参数:  212 |   表名; 213 |   列名和值的 ContentValues 对象;  214 |   可选的 WHERE 条件;  215 |   可选的填充 WHERE 语句的字符串,这些字符串会替换 WHERE 条件中的“?”标记,update() 根据条件,更新指定列的值.  216 | 217 | ②使用execSQL方式的实现 218 | 219 | ``` 220 | String sql = "update [user] set password = '654321' where username="finch";//修改的SQL语句 221 | db.execSQL(sql);//执行修改 222 | ``` 223 | 224 | 225 | ##删除数据 226 | 227 | ①使用SQLiteDatabase 对象的delete()方法。 228 | 229 | ``` 230 | String whereClause = "username=?";//删除的条件 231 | String[] whereArgs = {"finch"};//删除的条件参数 232 | db.delete("user",whereClause,whereArgs);//执行删除 233 | ``` 234 | 235 | ②使用execSQL方式的实现 236 | 237 | ``` 238 | String sql = "delete from user where username="finch";//删除操作的SQL语句 239 | db.execSQL(sql);//执行删除操作 240 | ``` 241 | 242 | ##查询数据 243 | 244 | ①使用 rawQuery() 直接调用 SELECT 语句 245 | 246 | ``` 247 | Cursor c = db.rawQuery("select * from user where username=?",new Stirng[]{"finch"}); 248 | 249 | if(cursor.moveToFirst()) { 250 | String password = c.getString(c.getColumnIndex("password")); 251 | } 252 | ``` 253 | 254 |   返回值是一个 cursor 对象,这个对象的方法可以迭代查询结果。 255 | 如果查询是动态的,使用这个方法就会非常复杂。例如,当你需要查询的列在程序编译的时候不能确定,这时候使用 query() 方法会方便很多。 256 | 257 | ②通过query实现查询 258 | 259 |   query() 方法用 SELECT 语句段构建查询。 260 |   SELECT 语句内容作为 query() 方法的参数,比如:要查询的表名,要获取的字段名,WHERE 条件,包含可选的位置参数,去替代 WHERE 条件中位置参数的值,GROUP BY 条件,HAVING 条件。 261 |   除了表名,其他参数可以是 null。所以代码可写成: 262 | 263 | ``` 264 | Cursor c = db.query("user",null,null,null,null,null,null);//查询并获得游标 265 | if(c.moveToFirst()){//判断游标是否为空 266 | for(int i=0;i 在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解android事件响应机制才能解决,事件响应机制已经是android开发者必不可少的知识。 2 | 3 | # 1.涉及到事件响应的常用方法构成 4 | 5 |   用户在手指与屏幕接触过程中通过MotionEvent对象产生一系列事件,它有四种状态: 6 |    7 | 8 | - MotionEvent.ACTION_DOWN :手指按下屏幕的瞬间(一切事件的开始) 9 | 10 | - MotionEvent.ACTION_MOVE :手指在屏幕上移动 11 | 12 | - MotionEvent.ACTION_UP :手指离开屏幕瞬间 13 | 14 | - MotionEvent.ACTION_CANCEL  :取消手势,一般由程序产生,不会由用户产生 15 | 16 |   Android中的事件onClick, onLongClick,onScroll, onFling等等,都是由许多个Touch事件构成的(一个ACTION_DOWN, n个ACTION_MOVE,1个ACTION_UP)。 17 | 18 |   android 事件响应机制是先 **分发**(先由外部的View接收,然后依次传递给其内层的最小View)再 **处理** (从最小View单元(事件源)开始依次向外层传递。)的形式实现的。 19 | 20 |   复杂性表现在:可以控制每层事件是否继续传递(分发和拦截协同实现),以及事件的具体消费(事件分发也具有事件消费能力)。 21 | 22 | # 2.android事件处理涉及到的三个重要函数 23 | 24 | > **事件分发:public boolean dispatchTouchEvent(MotionEvent ev)** 25 | 26 |    当有监听到事件时,首先由Activity进行捕获,进入事件分发处理流程。(因为activity没有事件拦截,View和ViewGroup有)会将事件传递给最外层View的dispatchTouchEvent(MotionEvent ev)方法,该方法对事件进行分发。 27 |    28 | 29 | - return true :表示该View内部消化掉了所有事件。 30 | 31 | - return false :事件在本层不再继续进行分发,并交由**上层**控件的onTouchEvent方法进行消费(如果本层控件已经是Activity,那么事件将被系统消费或处理)。  32 | 33 | - 如果事件分发返回系统默认的 super.dispatchTouchEvent(ev),事件将分发给本层的事件拦截onInterceptTouchEvent 方法进行处理 34 | 35 | > **事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)** 36 | 37 | - return true :表示将事件进行拦截,并将拦截到的事件交由本层控件 的 onTouchEvent 进行处理; 38 | 39 | - return false :则表示不对事件进行拦截,事件得以成功分发到子View。并由子View的dispatchTouchEvent进行处理。  40 | 41 | - 如果返回super.onInterceptTouchEvent(ev),默认表示拦截该事件,并将事件传递给当前View的onTouchEvent方法,和return true一样。 42 | 43 | 44 | > **事件响应:public boolean onTouchEvent(MotionEvent ev)** 45 | 46 |   在dispatchTouchEvent(事件分发)返回super.dispatchTouchEvent(ev)并且onInterceptTouchEvent(事件拦截返回true或super.onInterceptTouchEvent(ev)的情况下,那么事件会传递到onTouchEvent方法,该方法对事件进行响应。 47 | 48 |   49 | 50 | - 如果return true,表示onTouchEvent处理完事件后消费了此次事件。此时事件终结; 51 | 52 | - 如果return fasle,则表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的onTouchEvent方法返回true,如果到了最顶层View还是返回false,那么认为该事件不消耗,则在同一个事件系列中,当前View无法再次接收到事件,该事件会交由Activity的onTouchEvent进行处理;   53 | - 如果return super.dispatchTouchEvent(ev),则表示不响应事件,结果与return false一样。 54 | 55 | 56 | > 从以上过程中可以看出,dispatchTouchEvent无论返回true还是false,事件都不再进行分发,只有当其返回super.dispatchTouchEvent(ev),才表明其具有向下层分发的愿望,但是是否能够分发成功,则需要经过事件拦截onInterceptTouchEvent的审核。事件是否向上传递处理是由onTouchEvent的返回值决定的。 57 | 58 | 59 | ![这里写图片描述](http://img.blog.csdn.net/20160428161104339) 60 | 61 | (图来自网络) 62 | 63 | # 3.View源码分析 64 | 65 | 66 |   Android中ImageView、textView、Button等继承于View但没有重写的dispatchTouchEvent方法,所以都用的View的该方法进行事件分发。 67 |    68 |   看View重要函数部分源码: 69 | 70 | ``` 71 | public boolean dispatchTouchEvent(MotionEvent event) { 72 | //返回true,表示该View内部消化掉了所有事件。返回false,表示View内部只处理了ACTION_DOWN事件,事件继续传递,向上级View(ViewGroup)传递。 73 | 74 | if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && 75 | mOnTouchListener.onTouch(this, event)) { 76 | //此处的onTouch方式就是回调的我们注册OnTouchListener时重写的onTouch()方法 77 | return true; 78 | } 79 | return onTouchEvent(event); 80 | } 81 | ``` 82 | 83 |  首先进行三个条件的判断: 84 | 85 | (1)查看是否给button设置了OnTouchListener()事件; 86 | 87 | (2)控件是否Enable;(控件默认都是enable的) 88 | 89 | (3)button里面实现的OnTouchListener监听里的onTouch()方法是否返回true; 90 | 91 |  如果条件都满足,则该事件被消耗掉,不再进入onTouchEvent中处理。否则将事件将交给onTouchEvent方法处理。 92 | 93 | 94 | ``` 95 | public boolean onTouchEvent(MotionEvent event) { 96 | ... 97 | 98 | /* 当前onTouch的组件必须是可点击的比如Button,ImageButton等等,此处CLICKABLE为true,才会进入if方法,最后返回true。 99 | 如果是ImageView、TexitView这些默认为不可点击的View,此处CLICKABLE为false,最后返回false。当然会有特殊情况,如果给这些View设置了onClick监听器,此处CLICKABLE也将为true  */ 100 | 101 | if (((viewFlags & CLICKABLE) == CLICKABLE || 102 | (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 103 | switch (event.getAction()) { 104 | case MotionEvent.ACTION_UP: 105 | ... 106 | if (!post(mPerformClick)) { 107 | performClick();// 实际就是回调了我们注册的OnClickListener中重新的onClick()方法 108 | } 109 | ... 110 | break; 111 | 112 | case MotionEvent.ACTION_DOWN: 113 | ... 114 | break; 115 | 116 | case MotionEvent.ACTION_CANCEL: 117 | ... 118 | break; 119 | 120 | case MotionEvent.ACTION_MOVE: 121 | ... 122 | break; 123 | } 124 | return true; 125 | } 126 | 127 | return false; 128 | } 129 | ``` 130 | ``` 131 | public boolean performClick() { 132 | ... 133 | // 134 | if (li != null && li.mOnClickListener != null) { 135 | ... 136 | li.mOnClickListener.onClick(this); 137 | return true; 138 | } 139 | 140 | return false; 141 | } 142 | ``` 143 | ``` 144 | public void setOnClickListener(OnClickListener l) { 145 | if (!isClickable()) { 146 | setClickable(true); 147 | } 148 | getListenerInfo().mOnClickListener = l; 149 | } 150 | ``` 151 |    152 | 153 | > 只有我们注册OnTouchListener时重写的 154 | > onTouch()方法中 155 | > 156 | > 返回false —> 执行onTouchEvent方法 —> 导致onClick()回调方法执行  157 | > 158 | 返回true —> onTouchEvent方法不执行 —> 导致onClick()回调方法不会执行 159 | 160 | 161 | # 4.ViewGroup源码分析 162 | 163 |   Android中诸如LinearLayout等的五大布局控件,都是继承自ViewGroup,而ViewGroup本身是继承自View,所以ViewGroup的事件处理机制对这些控件都有效。 164 | 165 |    166 | 部分源码: 167 | ``` 168 | public boolean dispatchTouchEvent(MotionEvent ev) { 169 | final int action = ev.getAction(); 170 | final float xf = ev.getX(); 171 | final float yf = ev.getY(); 172 | final float scrolledXFloat = xf + mScrollX; 173 | final float scrolledYFloat = yf + mScrollY; 174 | final Rect frame = mTempRect; 175 | 176 | //这个值默认是false, 然后我们可以通过requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法 177 | //来改变disallowIntercept的值 178 | boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 179 | 180 | //这里是ACTION_DOWN的处理逻辑 181 | if (action == MotionEvent.ACTION_DOWN) { 182 | //清除mMotionTarget, 每次ACTION_DOWN都很设置mMotionTarget为null 183 | if (mMotionTarget != null) { 184 | mMotionTarget = null; 185 | } 186 | 187 | //disallowIntercept默认是false, 就看ViewGroup的onInterceptTouchEvent()方法 188 | if (disallowIntercept || !onInterceptTouchEvent(ev)) { //第一点 189 | ev.setAction(MotionEvent.ACTION_DOWN); 190 | final int scrolledXInt = (int) scrolledXFloat; 191 | final int scrolledYInt = (int) scrolledYFloat; 192 | final View[] children = mChildren; 193 | final int count = mChildrenCount; 194 | //遍历其子View 195 | for (int i = count - 1; i >= 0; i--) { //第二点 196 | final View child = children[i]; 197 | 198 | //如果该子View是VISIBLE或者该子View正在执行动画, 表示该View才 199 | //可以接受到Touch事件 200 | if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 201 | || child.getAnimation() != null) { 202 | //获取子View的位置范围 203 | child.getHitRect(frame); 204 | 205 | //如Touch到屏幕上的点在该子View上面 206 | if (frame.contains(scrolledXInt, scrolledYInt)) { 207 | // offset the event to the view's coordinate system 208 | final float xc = scrolledXFloat - child.mLeft; 209 | final float yc = scrolledYFloat - child.mTop; 210 | ev.setLocation(xc, yc); 211 | child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 212 | 213 | //调用该子View的dispatchTouchEvent()方法 214 | if (child.dispatchTouchEvent(ev)) { 215 | // 如果child.dispatchTouchEvent(ev)返回true表示 216 | //该事件被消费了,设置mMotionTarget为该子View 217 | mMotionTarget = child; 218 | //直接返回true 219 | return true; 220 | } 221 | // The event didn't get handled, try the next view. 222 | // Don't reset the event's location, it's not 223 | // necessary here. 224 | } 225 | } 226 | } 227 | } 228 | } 229 | 230 | //判断是否为ACTION_UP或者ACTION_CANCEL 231 | boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || 232 | (action == MotionEvent.ACTION_CANCEL); 233 | 234 | if (isUpOrCancel) { 235 | //如果是ACTION_UP或者ACTION_CANCEL, 将disallowIntercept设置为默认的false 236 | //假如我们调用了requestDisallowInterceptTouchEvent()方法来设置disallowIntercept为true 237 | //当我们抬起手指或者取消Touch事件的时候要将disallowIntercept重置为false 238 | //所以说上面的disallowIntercept默认在我们每次ACTION_DOWN的时候都是false 239 | mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 240 | } 241 | 242 | // The event wasn't an ACTION_DOWN, dispatch it to our target if 243 | // we have one. 244 | final View target = mMotionTarget; 245 | //mMotionTarget为null意味着没有找到消费Touch事件的View, 所以我们需要调用ViewGroup父类的 246 | //dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法 247 | if (target == null) { 248 | // We don't have a target, this means we're handling the 249 | // event as a regular view. 250 | ev.setLocation(xf, yf); 251 | if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 252 | ev.setAction(MotionEvent.ACTION_CANCEL); 253 | mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 254 | } 255 | return super.dispatchTouchEvent(ev); 256 | } 257 | 258 | //这个if里面的代码ACTION_DOWN不会执行,只有ACTION_MOVE 259 | //ACTION_UP才会走到这里, 假如在ACTION_MOVE或者ACTION_UP拦截的 260 | //Touch事件, 将ACTION_CANCEL派发给target,然后直接返回true 261 | //表示消费了此Touch事件 262 | if (!disallowIntercept && onInterceptTouchEvent(ev)) { 263 | final float xc = scrolledXFloat - (float) target.mLeft; 264 | final float yc = scrolledYFloat - (float) target.mTop; 265 | mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 266 | ev.setAction(MotionEvent.ACTION_CANCEL); 267 | ev.setLocation(xc, yc); 268 | 269 | if (!target.dispatchTouchEvent(ev)) { 270 | } 271 | // clear the target 272 | mMotionTarget = null; 273 | // Don't dispatch this event to our own view, because we already 274 | // saw it when intercepting; we just want to give the following 275 | // event to the normal onTouchEvent(). 276 | return true; 277 | } 278 | 279 | if (isUpOrCancel) { 280 | mMotionTarget = null; 281 | } 282 | 283 | // finally offset the event to the target's coordinate system and 284 | // dispatch the event. 285 | final float xc = scrolledXFloat - (float) target.mLeft; 286 | final float yc = scrolledYFloat - (float) target.mTop; 287 | ev.setLocation(xc, yc); 288 | 289 | if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 290 | ev.setAction(MotionEvent.ACTION_CANCEL); 291 | target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 292 | mMotionTarget = null; 293 | } 294 | 295 | //如果没有拦截ACTION_MOVE, ACTION_DOWN的话,直接将Touch事件派发给target 296 | return target.dispatchTouchEvent(ev); 297 | } 298 | ``` 299 | 300 | > 1、dispatchTouchEvent作用:决定事件是否由onInterceptTouchEvent来拦截处理。 301 | 返回super.dispatchTouchEvent时,由onInterceptTouchEvent来决定事件的流向 302 | 返回false时,会继续分发事件,自己内部只处理了ACTION_DOWN 303 | 返回true时,不会继续分发事件,自己内部处理了所有事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP) 304 | 305 | > 2、onInterceptTouchEvent作用:拦截事件,用来决定事件是否传向子View 306 | 返回true时,拦截后交给自己的onTouchEvent处理 307 | 返回false时,拦截后交给子View来处理 308 | 309 | > 3、onTouchEvent作用:事件最终到达这个方法 310 | 返回true时,内部处理所有的事件,换句话说,后续事件将继续传递给该view的onTouchEvent()处理 311 | 返回false时,事件会向上传递,由onToucEvent来接受,如果最上面View中的onTouchEvent也返回false的话,那么事件就会消失 312 | 313 |    314 | # 5.总结 315 | 316 | - 如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;  317 | 318 | - 可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法。 319 | - 子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;   320 | 321 | - 一个点击事件产生后,它的传递过程如下: 322 | Activity->Window->View。顶级View接收到事件之后,就会按相应规则去分发事件。如果一个View的onTouchEvent方法返回false,那么将会交给父容器的onTouchEvent方法进行处理,逐级往上,如果所有的View都不处理该事件,则交由Activity的onTouchEvent进行处理。  323 | 324 | - 如果某一个View开始处理事件,如果他不消耗ACTION_DOWN事件(也就是onTouchEvent返回false),则同一事件序列比如接下来进行ACTION_MOVE,则不会再交给该View处理。 325 | 326 | - ViewGroup默认不拦截任何事件。  327 | 328 | - 诸如TextView、ImageView这些不作为容器的View,一旦接受到事件,就调用onTouchEvent方法,它们本身没有onInterceptTouchEvent方法。正常情况下,它们都会消耗事件(返回true),除非它们是不可点击的(clickable和longClickable都为false),那么就会交由父容器的onTouchEvent处理。  329 | 330 | - 点击事件分发过程如下 dispatchTouchEvent—->OnTouchListener的onTouch方法—->onTouchEvent-->OnClickListener的onClick方法。也就是说,我们平时调用的setOnClickListener,优先级是最低的,所以,onTouchEvent或OnTouchListener的onTouch方法如果返回true,则不响应onClick方法... 331 | 332 | -------------------------------------------------------------------------------- /android/Android 名企面试题及答案整理(一).md: -------------------------------------------------------------------------------- 1 | - 下面异常是属于Runtime Exception 的是(abcd)(多选) 2 | 3 | A、ArithmeticException   4 | B、IllegalArgumentException   5 | C、NullPointerException   6 | D、BufferUnderflowException 7 | 8 | 解析: 9 | Java提供了两类主要的异常:runtime exception和checked exception。checked 异常也就是我们经常遇到的IO异常,以及SQL异常都是这种异常。对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。 10 | 11 | 出现运行时异常后,系统会把异常一直往上层抛,一直遇到处理代码。如果没有处理块,到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是Exception的子类,也有一般异常的特点,是可以被Catch块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。 12 | 13 | 编译时被检查的异常和运行时异常的区别: 14 |  编译被检查的异常在函数内被抛出,函数必须要声明,否编译失败。 15 |  声明的原因:是需要调用者对该异常进行处理。 16 |  运行时异常如果在函数内被抛出,在函数上不需要声明。 17 | 18 | - Math.round(11.5)等于多少(). Math.round(-11.5)等于多少(c) 19 | 20 | A、11 ,-11 B、11 ,-12 C、12 ,-11 D、12 ,-12 21 | 22 | 解析: 23 | Math.ceil()用作向上取整。 24 | Math.floor()用作向下取整。 25 | Math.round() 我们数学中常用到的四舍五入取整。 26 | 27 | - 对一些资源以及状态的操作保存,最好是保存在生命周期的哪个函数中进行(d) 28 | 29 | A、onPause() B、onCreate() C、 onResume() D、onStart() 30 | 31 | 解析: 32 | ![这里写图片描述](http://img.blog.csdn.net/20160411181000798) 33 | 34 | 系统杀死程序会调用onSaveInstanceState(Bundle)进行数据保存,这里保存的数据会出现在在程序下一次onStart(Bundle)这个Bundle中,onStart时可以将Bundle中数据取出。 35 | 36 | - Intent传递数据时,下列的数据类型哪些可以被传递(abcd)(多选) 37 | 38 | A、Serializable B、charsequence C、Parcelable D、Bundle 39 | 40 | 解析: 41 | Serializable :将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用ObjectInputStream 和 ObjectOutputStream 进行对象的读写。 42 | 43 | charsequence : 44 | 实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。 45 | 46 | CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer类一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。 47 | 48 | 提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,感觉如果有必要,还是重载的比较好,避免用instaneof这个操作符。 49 | 50 | Parcelable : 51 | android提供了一种新的类型:Parcel。本类被用作封装数据的容器,封装后的数据可以通过Intent或IPC传递。 除了基本类型以 52 | 外,只有实现了Parcelable接口的类才能被放入Parcel中。 53 | 是GOOGLE在安卓中实现的另一种序列化,功能和Serializable相似,主要是序列化的方式不同 54 | 55 | Bundle是将数据传递到另一个上下文中或保存或回复你自己状态的数据存储方式。它的数据不是持久化状态。 56 | 57 | - 下列属于SAX解析xml文件的优点的是(b) 58 | 59 | A、将整个文档树在内存中,便于操作,支持删除,修改,重新排列等多种功能 60 | B、不用事先调入整个文档,占用资源少 61 | C、整个文档调入内存,浪费时间和空间 62 | D、不是长久驻留在内存,数据不是持久的,事件过后,若没有保存数据,数据就会消失 63 | 64 | 解析: 65 | 在Android中提供了三种解析XML的方式:SAX(Simple API XML),DOM(Document Objrect Model),以及Android推荐的Pull解析方式。 66 | 67 | SAX: 是事件驱动型XML解析的一个标准接口,简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。 68 | 69 | DOM:即对象文档模型,它是将整个XML文档载入内存(所以效率较低,不推荐使用),使用DOM API遍历XML树、检索所需的数据,每一个节点当做一个对象。 70 | 71 | Pull:运行方式与 SAX 解析器相似。它提供了类似的事件,SAX解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。pull是一个while循环,随时可以跳出,而sax是只要解析了,就必须解析完成。 72 | 73 | - 在android中使用Menu时可能需要重写的方法有(ac)。(多选) 74 | 75 | A、onCreateOptionsMenu() 76 | B、onCreateMenu() 77 | C、onOptionsItemSelected() 78 | D、onItemSelected() 79 | 80 | 解析: 81 | android中有三种菜单 82 | 83 | 1.选项菜单Options menus :一个Activity只能有一个选项菜单,在按下Menu键时,显示在屏幕下方。 84 | 85 | 重写 onCreateContextMenu 用以创建上下文菜单 86 | 重写 onContextItemSelected 用以响应上下文菜单 87 | 2.上下文菜单Context menus :为Activity中的任何一个视图注册一个上下文菜单,“长按”出现。 88 | 89 | 重写 onCreateOptionsMenu 用以创建选项菜单 90 | 重写 onOptionsItemSelected 用以响应选项菜单 91 | 92 | 3.弹出式菜单Popup menus :依赖于Activity中的某个一个视图 93 | 94 | - android 关于service生命周期的onCreate()和onStart()说法正确的是(ad)(多选题) 95 | 96 | A、当第一次启动的时候先后调用onCreate()和onStart()方法 97 | B、当第一次启动的时候只会调用onCreate()方法 98 | C、如果service已经启动,将先后调用onCreate()和onStart()方法 99 | D、如果service已经启动,只会执行onStart()方法,不在执行onCreate()方法 100 | 101 | 解析: 102 | ![这里写图片描述](http://img.blog.csdn.net/20160411181016027) 103 | 104 | 1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。 105 | 106 | 2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。 107 | 108 | 3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。 109 | 4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。 110 | 特别注意: 111 | 112 | 1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止); 113 | 2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService; 114 | 3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止; 115 | 4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。 116 | 5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。 117 | 118 | - 下面是属于GLSurFaceView特性的是(abc)(多选) 119 | 120 | A、管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。 121 | B、管理一个EGL display,它能让opengl把内容渲染到上述的surface上。 122 | C、让渲染器在独立的线程里运作,和UI线程分离。 123 | D、可以直接从内存或者DMA等硬件接口取得图像数据 124 | 125 | 解析: 126 | Android游戏当中主要的除了控制类外就是显示类View。SurfaceView是从View基类中派生出来的显示类。android游戏开发中常用的三种视图是:view、SurfaceView和GLSurfaceView。 127 | 128 |   View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。 129 | 130 |   SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。 131 | 132 |   GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。 133 | 134 | GLSurfaceView提供了下列特性: 135 | 1.管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。 136 | 2.管理一个EGL display,它能让opengl把内容渲染到上述的surface上。 137 | 3.用户自定义渲染器(render)。 138 | 4 . 让渲染器在独立的线程里运作,和UI线程分离。 139 | 5.支持按需渲染(on-demand)和连续渲染(continuous)。 140 | 6.一些可选工具,如调试。 141 | 142 | - 关于ContenValues类说法正确的是(a) 143 | 144 | A、他和Hashtable比较类似,也是负责存储一些名值对,但是他存储的名值对当中的名是String类型,而值都是基本类型 145 |   B、他和Hashtable比较类似,也是负责存储一些名值对,但是他存储的名值对当中的名是任意类型,而值都是基本类型 146 |   C、他和Hashtable比较类似,也是负责存储一些名值对,但是他存储的名值对当中的名,可以为空,而值都是String类型 147 |   D、他和Hashtable比较类似,也是负责存储一些名值对,但是他存储的名值对当中的名是String类型,而值也是String类型 148 | 149 | 解析: 150 | ContentValues 和HashTable类似都是一种存储的机制 但是两者最大的区别就在于,contenvalues Key只能是String类型,values只能存储基本类型的数据,像string,int之类的,不能存储对象这种东西。ContentValues 常用在数据库中的操作。 151 | 152 | HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。 153 | 154 | - 下面退出Activity错误的方法是(c) 155 | 156 | A、finish() 157 | B、抛异常强制退出 158 | C、System.exit() 159 | D、onStop() 160 | 161 | 解析: 162 | ![这里写图片描述](http://img.blog.csdn.net/20160411181031423) 163 | 164 | 165 | finish():在你的activity动作完成的时候,或者Activity需要关闭的时候,调用此方法。当你调用此方法的时候,系统只是将最上面的Activity移出了栈,并没有及时的调用onDestory()方法,其占用的资源也没有被及时释放。因为移出了栈,所以当你点击手机上面的“back”按键的时候,也不会再找到这个Activity。finish函数仅仅把当前Activity退出了,但是并没有释放他的资源。安卓系统自己决定何时从内存中释放应用程序。当系统没有可用内存到时候,会按照优先级,释放部分应用。 166 | 167 | onDestory():系统销毁了这个Activity的实例在内存中占据的空间。 168 | 在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了。当重新进入此Activity的时候,必须重新创建,执行onCreate()方法。 169 | 170 | System.exit(0):退出整个应用程序(不仅仅是当前activity)。将整个进程直接Kill掉。 171 | 172 | - 关于res/raw目录说法正确的是(a) 173 | 174 | A、 这里的文件是原封不动的存储到设备上不会转换为二进制的格式 175 | B、这里的文件是原封不动的存储到设备上会转换为二进制的格式 176 | C、 这里的文件最终以二进制的格式存储到指定的包中 177 | D、这里的文件最终不会以二进制的格式存储到指定的包中 178 | 179 | 解析: 180 | res/raw和assets的相同点: 181 | 两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。 182 | 183 | res/raw和assets的不同点: 184 | 1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。 185 | 186 | - android中常用的四个布局是framlayout,linenarlayout,relativelayout和tablelayout。 187 | 188 | - android 的四大组件是activiey,service,broadcast和contentprovide。 189 | 190 | - activity一般会重载7个方法用来维护其生命周期,除了onCreate(),onStart(),onDestory() 外还有onpause,onresume,onstop,onrestart。 191 | 192 | - android的数据存储的方式sharedpreference,文件,SQlite,contentprovider,网络。 193 | 194 | 195 | 196 | - 程序运行的结果是:good and gbc 197 | 198 | ``` 199 | public classExample{ 200 |   String str=new String("good"); 201 |   char[]ch={'a','b','c'}; 202 |   public static void main(String args[]){ 203 |   Example ex=new Example(); 204 |   ex.change(ex.str,ex.ch); 205 |   System.out.print(ex.str+" and "); 206 |   Sytem.out.print(ex.ch); 207 |   } 208 |   public void change(String str,char ch[]){ 209 |   str="test ok"; 210 |   ch[0]='g'; 211 |   } 212 |   } 213 | ``` 214 | 215 | 解析: 216 | public void change(String str,char ch[]) 217 | str是按值传递,所以在函数中对它的操作只生效于它的副本,与原字符串无关。 218 | ch是按址传递,在函数中根据地址,可以直接对字符串进行操作。 219 | 220 | - 在android中,请简述jni的调用过程。 221 | 222 | 1)安装和下载Cygwin,下载 Android NDK 223 |   2)在ndk项目中JNI接口的设计 224 |   3)使用C/C++实现本地方法 225 |   4)JNI生成动态链接库.so文件 226 |   5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可 227 | 228 | - Android应用程序结构: 229 | 230 | Linux Kernel(Linux内核)、Libraries(系统运行库或者是c/c++核心库)、Application Framework(开发框架包)、Applications (核心应用程序) 231 | 232 | ![这里写图片描述](http://img.blog.csdn.net/20160411181237328) 233 | 234 | - 请继承SQLiteOpenHelper实现创建一个版本为1的“diaryOpenHelper.db”的数据库,同时创建一个 “diary” 表(包含一个_id主键并自增长,topic字符型100长度, content字符型1000长度),在数据库版本变化时请删除diary表,并重新创建出diary表。 235 | 236 | ``` 237 | public class DBHelper extends SQLiteOpenHelper{ 238 |   public final static String DATABASENAME ="diaryOpenHelper.db"; 239 |   public final static int DATABASEVERSION =1; 240 |   //创建数据库 241 |   public DBHelper(Context context,Stringname,CursorFactory factory,int version) 242 |   { 243 |   super(context, name, factory,version); 244 |   } 245 |   //创建表等机构性文件 246 |   public void onCreate(SQLiteDatabase db) 247 |   { 248 |   String sql ="create tablediary"+ 249 |   "("+ 250 |   "_idinteger primary key autoincrement,"+ 251 |   "topicvarchar(100),"+ 252 |   "contentvarchar(1000)"+ 253 |   ")"; 254 |   db.execSQL(sql); 255 |   } 256 |   //若数据库版本有更新,则调用此方法 257 |   public void onUpgrade(SQLiteDatabasedb,int oldVersion,int newVersion) 258 |   { 259 |   String sql = "drop table ifexists diary"; 260 |   db.execSQL(sql); 261 |   this.onCreate(db); 262 |   } 263 |   } 264 | ``` 265 | 266 | - 页面上现有ProgressBar控件progressBar,请用书写线程以10秒的的时间完成其进度显示工作。 267 | 268 | 269 | 270 | ``` 271 | public class ProgressBarStu extends Activity { 272 | 273 |   private ProgressBar progressBar = null; 274 |   protected void onCreate(BundlesavedInstanceState) { 275 |   super.onCreate(savedInstanceState); 276 |   setContentView(R.layout.progressbar); 277 |   //从这到下是关键 278 |   progressBar = (ProgressBar)findViewById(R.id.progressBar); 279 |   Thread thread = new Thread(newRunnable() { 280 |   @Override 281 |   public void run() { 282 |   int progressBarMax =progressBar.getMax(); 283 |   try { 284 |   while(progressBarMax!=progressBar.getProgress()) 285 |   { 286 |   intstepProgress = progressBarMax/10; 287 |   intcurrentprogress = progressBar.getProgress(); 288 |   progressBar.setProgress(currentprogress+stepProgress); 289 |   Thread.sleep(1000); 290 |   } 291 |   } catch(InterruptedException e) { 292 |   // TODO Auto-generatedcatch block 293 |   e.printStackTrace(); 294 |   } 295 |   } 296 |   }); 297 |   thread.start(); 298 |   //关键结束 299 |   } 300 |   } 301 | ``` 302 | 303 | - onFreeze() renamed to onSaveInstanceState(),以便恢复在onCreate(Bundle)里面设置的状态。 304 | 305 | - 如果后台的Activity由于某原因被系统回收了,onSaveInstanceState()在被系统回收之前(onPause()前面)保存当前状态。 306 | 307 | 当你的程序中某一个Activity A在运行时,主动或被动地运行另一个新的Activity B,这个时候A会执行onSaveInstanceState()。B完成以后又会来找A,这个时候就有两种情况:一是A被回收,二是A没有被回收,被回收的A就要重新调用onCreate()方法,不同于直接启动的是这回onCreate()里是带上了参数savedInstanceState;而没被收回的就直接执行onResume(),跳过onCreate()了。 308 | 309 | - ContentProvider: 310 | 311 | 提供了我们在应用程序之前共享数据的一种机制,而我们知道每一个应用程序都是运行在不同的应用程序的,数据和文件在不同应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,比如有时候我们需要操作手机里的联系人,手机里的多媒体等一些信息,我们都可以用到这个ContentProvider来达到我们所需。 312 | 313 | 1)、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。 314 | 2)、使用ContentProvider可以在不同的应用程序之间共享数据。 315 | 3)、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。 316 | 总的来说使用ContentProvider对外共享数据的好处是统一了数据的访问方式。 317 | 318 | Uri为系统的每一个资源给其一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。 319 | 320 | - 请解释下Android程序运行时权限与文件系统权限的区别。 321 | 322 | 运行时权限Dalvik( android授权) 323 | 文件系统 linux 内核授权 324 | 325 | - 什么是ANR 如何避免它? 326 | 327 | 在Android里,应用程序的响应性是由Activity Manager和Window Manager系统服务监视的。当它监测到以下情况中的一个时,Android就会针对特定的应用程序显示ANR: 328 | 329 | 在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸) 330 | BroadcastReceiver在10秒内没有执行完毕。 331 | 332 | Android应用程序通常是运行在一个单独的线程(例如,main)里。这意味着你的应用程序所做的事情如果在主线程里占用了太长的时间的话,就会引发ANR对话框,因为你的应用程序并没有给自己机会来处理输入事件或者Intent广播。 333 | 334 | 在主线程里尽量的少做事情,比如高耗时的计算和网络、数据库等潜在的耗时操作都应该放在子线程来完成。 335 | -------------------------------------------------------------------------------- /android/Android 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 |
4 | 5 | 1、Handler的由来 6 | 7 |
10 | ## 11 |   当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread)来负责处理与UI相关的事件,我们叫做UI线程。 12 | 13 |   Android的UI操作并不是线程安全的(出于性能优化考虑),意味着如果多个线程并发操作UI线程,可能导致线程安全问题。 14 | 15 |   为了解决Android应用多线程问题—Android平台只允许UI线程修改Activity里的UI组建,就会导致新启动的线程无法改变界面组建的属性值。 16 | 17 |   **简单的说:**当主线程队列处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的异常,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信。 18 | 19 | 20 | 21 | 26 | 27 |
22 | 23 | 2、Handler的作用 24 | 25 |
28 | ## 29 | 30 | 2.1 让线程延时执行 31 | 32 | 主要用到的两个方法: 33 | 34 | - final boolean postAtTime(Runnable r, long uptimeMillis) 35 | 36 | - final boolean postDelayed(Runnable r, long delayMillis) 37 | 38 | 2.2 让任务在其他线程中执行并返回结果 39 | 40 | 分为两个步骤: 41 | 42 | - 在新启动的线程中发送消息 43 | 44 |   使用Handler对象的sendMessage()方法或者SendEmptyMessage()方法发送消息。 45 | 46 | - 在主线程中获取处理消息 47 | 48 |   重写Handler类中处理消息的方法(void handleMessage(Message msg)),当新启动的线程发送消息时,消息发送到与之关联的MessageQueue。而Hanlder不断地从MessageQueue中获取并处理消息。 49 | 50 | 51 | 52 | 57 | 58 |
53 | 54 | 3、Handler更新UI线程一般使用 55 | 56 |
59 | ## 60 | 61 |    62 | 63 | - 首先要进行Handler 申明,复写handleMessage方法( 放在主线程中) 64 | 65 | ``` 66 | private Handler handler = new Handler() { 67 | 68 | @Override 69 | public void handleMessage(Message msg) { 70 | // TODO 接收消息并且去更新UI线程上的控件内容 71 | if (msg.what == UPDATE) { 72 | // 更新界面上的textview 73 | tv.setText(String.valueOf(msg.obj)); 74 | } 75 | super.handleMessage(msg); 76 | } 77 | }; 78 | ``` 79 | 80 | - 子线程发送Message给ui线程表示自己任务已经执行完成,主线程可以做相应的操作了。 81 | 82 | ``` 83 | new Thread() { 84 | @Override 85 | public void run() { 86 | // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值 87 | try { 88 | //do something 89 | 90 | Message msg = new Message(); 91 | msg.what = UPDATE; 92 | msg.obj = "更新后的值" ; 93 | handler.sendMessage(msg); 94 | } 95 | } catch (InterruptedException e) { 96 | e.printStackTrace(); 97 | } 98 | } 99 | }.start(); 100 | ``` 101 | 102 | 103 | 104 | 109 | 110 |
105 | 106 | 4、Handler原理分析 107 | 108 |
111 | ## 112 | 113 | 4.1  Handler的构造函数 114 | 115 | > ① public Handler() 116 | ② public Handler(Callbackcallback) 117 | ③ public Handler(Looperlooper) 118 | ④ public Handler(Looperlooper, Callbackcallback)   119 | 120 | 121 | - 第①个和第②个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。   122 |   下面来看①②个函数源码: 123 |    124 | 125 | ``` 126 | 113 public Handler() { 127 | 114 this(null, false); 128 | 115 } 129 | 130 | 127 public Handler(Callback callback) { 131 | 128 this(callback, false); 132 | 129 } 133 | 134 | //他们会调用Handler的内部构造方法 135 | 136 | 188 public Handler(Callback callback, boolean async) { 137 | 189 if (FIND_POTENTIAL_LEAKS) { 138 | 190 final Class klass = getClass(); 139 | 191 if ((klass.isAnonymousClass() ||klass.isMemberClass() 140 | || klass.isLocalClass()) && 141 | 192 (klass.getModifiers() & Modifier.STATIC) == 0) { 142 | 193 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 143 | 194 klass.getCanonicalName()); 144 | 195 } 145 | 196 } 146 | 197/************************************ 147 | 198 mLooper = Looper.myLooper(); 148 | 199 if (mLooper == null) { 149 | 200 throw new RuntimeException( 150 | 201 "Can't create handler inside thread that has not called Looper.prepare()"); 151 | 202 } 152 | 203 mQueue = mLooper.mQueue; 153 | 204 mCallback = callback; 154 | 205 mAsynchronous = async; 155 | 206 } 156 | 157 | ``` 158 | 159 |   我们看到暗红色的重点部分: 160 | 161 |   通过Looper.myLooper()获取了当前线程保存的Looper实例,又通过这个Looper实例获取了其中保存的MessageQueue(消息队列)。**每个Handler 对应一个Looper对象,产生一个MessageQueue** 162 |    163 | 164 | - 第③个和第④个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。 165 |   下面来看③④个函数源码: 166 | 167 | ``` 168 | 136 public Handler(Looper looper) { 169 | 137 this(looper, null, false); 170 | 138 }  171 | 172 | 147 public Handler(Looper looper, Callback callback) { 173 | 148 this(looper, callback, false); 174 | 149 } 175 | //他们会调用Handler的内部构造方法 176 | 177 | 227 public Handler(Looper looper, Callback callback, boolean async) { 178 | 228 mLooper = looper; 179 | 229 mQueue = looper.mQueue; 180 | 230 mCallback = callback; 181 | 231 mAsynchronous = async; 182 | 232 } 183 | 184 | ``` 185 | 186 | - 第②个和第④个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下: 187 | 188 | ``` 189 | 80 public interface Callback { 190 | 81 public boolean More ...handleMessage(Message msg); 191 | 82 } 192 | ``` 193 | 194 |   Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法: 195 |    196 |  1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法   197 |    198 |  2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法   199 |     200 |    也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。 201 | 202 | 4.2 Handle发送消息的几个方法源码 203 | 204 | ``` 205 | public final boolean sendMessage(Message msg) 206 | { 207 | return sendMessageDelayed(msg, 0); 208 | } 209 | ``` 210 | 211 | ``` 212 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 213 | Message msg = Message.obtain(); 214 | msg.what = what; 215 | return sendMessageDelayed(msg, delayMillis); 216 | } 217 | ``` 218 | 219 | ``` 220 | public final boolean sendMessageDelayed(Message msg, long delayMillis) 221 | { 222 | if (delayMillis < 0) { 223 | delayMillis = 0; 224 | } 225 | return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 226 | } 227 | ``` 228 | 229 | ``` 230 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 231 | MessageQueue queue = mQueue; 232 | if (queue == null) { 233 | RuntimeException e = new RuntimeException( 234 | this + " sendMessageAtTime() called with no mQueue"); 235 | Log.w("Looper", e.getMessage(), e); 236 | return false; 237 | } 238 | return enqueueMessage(queue, msg, uptimeMillis); 239 | } 240 | ``` 241 | 242 | 我们可以看出他们最后都调用了sendMessageAtTime(),然后返回了enqueueMessage方法,下面看一下此方法源码: 243 | 244 | ``` 245 | 626 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 246 |       //把当前的handler作为msg的target属性 247 | 627 msg.target = this; 248 | 628 if (mAsynchronous) { 249 | 629 msg.setAsynchronous(true); 250 | 630 } 251 | 631 return queue.enqueueMessage(msg, uptimeMillis); 252 | 632 } 253 | ``` 254 | 255 | 在该方法中有两件事需要注意: 256 | 257 | 1. msg.target = this 258 | 259 |   该代码将Message的target绑定为当前的Handler 260 | 2. queue.enqueueMessage 261 |    262 |   变量queue表示的是Handler所绑定的消息队列MessageQueue,通过调用queue.enqueueMessage(msg, uptimeMillis)我们将Message放入到消息队列中。 263 | 264 | 过下图可以看到完整的方法调用顺序: 265 | 266 | ![这里写图片描述](http://img.blog.csdn.net/20160516125245477)  267 | 268 | 269 | 270 | 275 | 276 |
271 | 272 | 5、Looper原理分析 273 | 274 |
277 | ## 278 | 279 |   我们一般在主线程申明Handler,有时我们需要继承Thread类实现自己的线程功能,当我们在里面申明Handler的时候会报错。其原因是主线程中已经实现了两个重要的Looper方法,下面看一看ActivityThread.java中main方法的源码: 280 | 281 | ``` 282 | public static void main(String[] args) { 283 | //......省略 284 | 5205 Looper.prepareMainLooper();//> 285 | 5206 286 | 5207 ActivityThread thread = new ActivityThread(); 287 | 5208 thread.attach(false); 288 | 5209 289 | 5210 if (sMainThreadHandler == null) { 290 | 5211 sMainThreadHandler = thread.getHandler(); 291 | 5212 } 292 | 5213 293 | 5214 AsyncTask.init(); 294 | 5215 295 | 5216 if (false) { 296 | 5217 Looper.myLooper().setMessageLogging(new 297 | 5218 LogPrinter(Log.DEBUG, "ActivityThread")); 298 | 5219 } 299 | 5220 300 | 5221 Looper.loop();//> 301 | 5222 302 | 5223 throw new RuntimeException("Main thread loop unexpectedly exited"); 303 | 5224 } 304 | 5225} 305 | ``` 306 | 307 | 5.1 首先看prepare()方法 308 | 309 |    310 | 311 | ``` 312 | 70 public static void prepare() { 313 | 71 prepare(true); 314 | 72 } 315 | 73 316 | 74 private static void prepare(boolean quitAllowed) { 317 |      //证了一个线程中只有一个Looper实例 318 | 75 if (sThreadLocal.get() != null) { 319 | 76 throw new RuntimeException("Only one Looper may be created per thread"); 320 | 77 } 321 | 78 sThreadLocal.set(new Looper(quitAllowed)); 322 | 79 } 323 | ``` 324 | 该方法会调用Looper构造函数同时实例化出MessageQueue和当前thread. 325 | 326 | ``` 327 | 186 private Looper(boolean quitAllowed) { 328 | 187 mQueue = new MessageQueue(quitAllowed); 329 | 188 mThread = Thread.currentThread(); 330 | 189 } 331 | 332 | 182 public static MessageQueue myQueue() { 333 | 183 return myLooper().mQueue; 334 | 184 } 335 | 336 | ``` 337 | 338 | 339 |   prepare()方法中通过ThreadLocal对象实现Looper实例与线程的绑定。(不清楚的可以查看 [ThreadLocal的使用规则和源码分析](http://blog.csdn.net/amazing7/article/details/51313851))  340 | 341 | 342 | 5.2  loop()方法 343 | 344 |    345 | 346 | ``` 347 | 109 public static void loop() { 348 | 110 final Looper me = myLooper(); 349 | 111 if (me == null) { 350 | 112 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 351 | 113 } 352 | 114 final MessageQueue queue = me.mQueue; 353 | 115 354 | 118 Binder.clearCallingIdentity(); 355 | 119 final long ident = Binder.clearCallingIdentity(); 356 | 120 357 | 121 for (;;) { 358 | 122 Message msg = queue.next(); // might block 359 | 123 if (msg == null) { 360 | 124 361 | 125 return; 362 | 126 } 363 | 127 364 | 129 Printer logging = me.mLogging; 365 | 130 if (logging != null) { 366 | 131 logging.println(">>>>> Dispatching to " + msg.target + " " + 367 | 132 msg.callback + ": " + msg.what); 368 | 133 } 369 | //重点**** 370 | 135 msg.target.dispatchMessage(msg); 371 | 136 372 | 137 if (logging != null) { 373 | 138 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 374 | 139 } 375 | 140 376 | 142 // identity of the thread wasn't corrupted. 377 | 143 final long newIdent = Binder.clearCallingIdentity(); 378 | 144 if (ident != newIdent) { 379 | 145 Log.wtf(TAG, "Thread identity changed from 0x" 380 | 146 + Long.toHexString(ident) + " to 0x" 381 | 147 + Long.toHexString(newIdent) + " while dispatching to " 382 | 148 + msg.target.getClass().getName() + " " 383 | 149 + msg.callback + " what=" + msg.what); 384 | 150 } 385 | 151 386 | 152 msg.recycleUnchecked(); 387 | 153 } 388 | 154 } 389 | ``` 390 |   首先looper对象不能为空,就是说loop()方法调用必须在prepare()方法的后面。 391 | 392 |  Looper一直在不断的从消息队列中通过MessageQueue的next方法获取Message,然后通过代码msg.target.dispatchMessage(msg)让该msg所绑定的Handler(Message.target)执行dispatchMessage方法以实现对Message的处理。 393 | 394 | Handler的dispatchMessage的源码如下: 395 | 396 | ``` 397 | 93 public void dispatchMessage(Message msg) { 398 | 94 if (msg.callback != null) { 399 | 95 handleCallback(msg); 400 | 96 } else { 401 | 97 if (mCallback != null) { 402 | 98 if (mCallback.handleMessage(msg)) { 403 | 99 return; 404 | 100 } 405 | 101 } 406 | 102 handleMessage(msg); 407 | 103 } 408 | 104 } 409 | ``` 410 |   我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。 411 | 412 | 413 | 414 | 415 | 420 | 421 |
416 | 417 | 6、如何在子线程中使用Handler 418 | 419 |
422 | ## 423 | 424 |   Handler本质是从当前的线程中获取到Looper来监听和操作MessageQueue,当其他线程执行完成后回调当前线程。 425 | 426 |   子线程需要先prepare()才能获取到Looper的,是因为在子线程只是一个普通的线程,其ThreadLoacl中没有设置过Looper,所以会抛出异常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是设置了Looper的。 427 | 428 | 6.1 实例代码 429 | 430 |  定义一个类实现Runnable接口或继承Thread类(一般不继承)。 431 | 432 | ``` 433 | class Rub implements Runnable { 434 | 435 | public Handler myHandler; 436 | // 实现Runnable接口的线程体 437 | @Override 438 | public void run() { 439 | 440 | /*①、调用Looper的prepare()方法为当前线程创建Looper对象并, 441 | 创建Looper对象时,它的构造器会自动的创建相对应的MessageQueue*/ 442 | Looper.prepare(); 443 | 444 | /*.②、创建Handler子类的实例,重写HandleMessage()方法,该方法处理除当前线程以外线程的消息*/ 445 | myHandler = new Handler() { 446 | @Override 447 | public void handleMessage(Message msg) { 448 | String ms = ""; 449 | if (msg.what == 0x777) { 450 | 451 | } 452 | } 453 | 454 | }; 455 | //③、调用Looper的loop()方法来启动Looper让消息队列转动起来 456 | Looper.loop(); 457 | } 458 | } 459 | ``` 460 | 461 | 注意分成三步:  462 | 463 | 1.调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。   464 | 465 | 2.有了Looper之后,创建Handler子类实例,重写HanderMessage()方法,该方法负责处理来自于其他线程的消息。   466 | 467 | 3.调用Looper的looper()方法启动Looper。 468 | 469 |   然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。 470 | 471 | 472 | 473 | 474 | 479 | 480 |
475 | 476 | 7、总结 477 | 478 |
481 | ## 482 |    483 | 484 | **Handler**: 485 |       发送消息,它能把消息发送给Looper管理的MessageQueue。 486 |       处理消息,并负责处理Looper分给它的消息。 487 | **Message**: 488 |       Handler接收和处理的消息对象。 489 | **Looper**: 490 |       每个线程只有一个Looper,它负责管理对应的MessageQueue,会不断地从MessageQueue取出消息,并将消息分给对应的Hanlder处理。   491 |        492 |       主线程中,系统已经初始化了一个Looper对象,因此可以直接创建Handler即可,就可以通过Handler来发送消息、处理消息。 程序自己启动的子线程,程序必须自己创建一个Looper对象,并启动它,调用Looper.prepare()方法。 493 | 494 | prapare()方法:保证每个线程最多只有一个Looper对象。   495 | 496 | looper()方法:启动Looper,使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给对应的Handler进行处理。   497 | 498 | MessageQueue:由Looper负责管理,它采用先进先出的方式来管理Message。  499 | 500 |   Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。  501 |    502 |   Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。 503 | 504 | -------------------------------------------------------------------------------- /android/Android 缓存机制.md: -------------------------------------------------------------------------------- 1 | >  移动开发本质上就是手机和服务器之间进行通信,需要从服务端获取数据。反复通过网络获取数据是比较耗时的,特别是访问比较多的时候,会极大影响了性能,Android中可通过缓存机制来减少频繁的网络操作,减少流量、提升性能。 2 | 3 | # 实现原理 4 | 5 |   把不需要实时更新的数据缓存下来,通过时间或者其他因素 来判别是读缓存还是网络请求,这样可以缓解服务器压力,一定程度上提高应用响应速度,并且支持离线阅读。 6 |    7 | 8 | #Bitmap的缓存 9 | 10 |   在许多的情况下(像 ListView, GridView 或 ViewPager 之类的组件 )我们需要一次性加载大量的图片,在屏幕上显示的图片和所有待显示的图片有可能需要马上就在屏幕上无限制的进行滚动、切换。 11 | 12 |   像ListView, GridView 这类组件,它们的子项当不可见时,所占用的内存会被回收以供正在前台显示子项使用。垃圾回收器也会释放你已经加载了的图片占用的内存。如果你想让你的UI运行流畅的话,就不应该每次显示时都去重新加载图片。保持一些内存和文件缓存就变得很有必要了。 13 | 14 | ##使用内存缓存 15 | 16 |   通过预先消耗应用的一点内存来存储数据,便可快速的为应用中的组件提供数据,是一种典型的以**空间换时间**的策略。 17 |   LruCache 类(Android v4 Support Library 类库中开始提供)非常适合来做图片缓存任务 ,它可以使用一个LinkedHashMap 的强引用来保存最近使用的对象,并且当它保存的对象占用的内存总和超出了为它设计的最大内存时会把**不经常使用**的对象成员踢出以供垃圾回收器回收。 18 | 19 |   给LruCache 设置一个合适的内存大小,需考虑如下因素: 20 | 21 | - 还剩余多少内存给你的activity或应用使用 22 | - 屏幕上需要一次性显示多少张图片和多少图片在等待显示 23 | - 手机的大小和密度是多少(密度越高的设备需要越大的 缓存) 24 | - 图片的尺寸(决定了所占用的内存大小) 25 | - 图片的访问频率(频率高的在内存中一直保存) 26 | - 保存图片的质量(不同像素的在不同情况下显示) 27 | 28 | 29 | 具体的要根据应用图片使用的具体情况来找到一个合适的解决办法,一个设置 LruCache 例子: 30 | 31 | ``` 32 | private LruCache mMemoryCache; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | ... 37 | // 获得虚拟机能提供的最大内存,超过这个大小会抛出OutOfMemory的异常 38 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 39 | 40 | // 用1/8的内存大小作为内存缓存 41 | final int cacheSize = maxMemory / 8; 42 | 43 | mMemoryCache = new LruCache(cacheSize) { 44 | @Override 45 | protected int sizeOf(String key, Bitmap bitmap) { 46 | // 这里返回的不是item的个数,是cache的size(单位1024个字节) 47 | return bitmap.getByteCount() / 1024; 48 | } 49 | }; 50 | ... 51 | } 52 | 53 | public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 54 | if (getBitmapFromMemCache(key) == null) { 55 | mMemoryCache.put(key, bitmap); 56 | } 57 | } 58 | 59 | public Bitmap getBitmapFromMemCache(String key) { 60 | return mMemoryCache.get(key); 61 | } 62 | ``` 63 |   当为ImageView加载一张图片时,会先在LruCache 中看看有没有缓存这张图片,如果有的话直接更新到ImageView中,如果没有的话,一个后台线程会被触发来加载这张图片。 64 | 65 | ``` 66 | public void loadBitmap(int resId, ImageView imageView) { 67 | final String imageKey = String.valueOf(resId); 68 | 69 | // 查看下内存缓存中是否缓存了这张图片 70 | final Bitmap bitmap = getBitmapFromMemCache(imageKey); 71 | if (bitmap != null) { 72 | mImageView.setImageBitmap(bitmap); 73 | } else { 74 | mImageView.setImageResource(R.drawable.image_placeholder); 75 | BitmapWorkerTask task = new BitmapWorkerTask(mImageView); 76 | task.execute(resId); 77 | } 78 | } 79 | ``` 80 | 在图片加载的Task中,需要把加载好的图片加入到内存缓存中。 81 | 82 | ``` 83 | class BitmapWorkerTask extends AsyncTask { 84 | ... 85 | // 在后台完成 86 | @Override 87 | protected Bitmap doInBackground(Integer... params) { 88 | final Bitmap bitmap = decodeSampledBitmapFromResource( 89 | getResources(), params[0], 100, 100)); 90 | addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); 91 | return bitmap; 92 | } 93 | ... 94 | } 95 | ``` 96 | 97 | ##使用磁盘缓存 98 | 99 |   内存缓存能够快速的获取到最近显示的图片,但不一定就能够获取到。当数据集过大时很容易把内存缓存填满(如GridView )。你的应用也有可能被其它的任务(比如来电)中断进入到后台,后台应用有可能会被杀死,那么相应的内存缓存对象也会被销毁。 当你的应用重新回到前台显示时,你的应用又需要一张一张的去加载图片了。 100 | 101 |  磁盘文件缓存能够用来处理这些情况,保存处理好的图片,当内存缓存不可用的时候,直接读取在硬盘中保存好的图片,这样可以有效的减少图片加载的次数。读取磁盘文件要比直接从内存缓存中读取要慢一些,而且需要在一个UI主线程外的线程中进行,因为磁盘的读取速度是不能够保证的,磁盘文件缓存显然也是一种以**空间换时间**的策略。 102 | 103 |   如果图片使用非常频繁的话,一个 ContentProvider 可能更适合代替去存储缓存图片,比如图片gallery 应用。 104 | 105 |   下面是一个DiskLruCache的部分代码: 106 | 107 | ``` 108 | private DiskLruCache mDiskLruCache; 109 | private final Object mDiskCacheLock = new Object(); 110 | private boolean mDiskCacheStarting = true; 111 | private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB 112 | private static final String DISK_CACHE_SUBDIR = "thumbnails"; 113 | 114 | @Override 115 | protected void onCreate(Bundle savedInstanceState) { 116 | ... 117 | // 初始化内存缓存 118 | ... 119 | // 在后台线程中初始化磁盘缓存 120 | File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR); 121 | new InitDiskCacheTask().execute(cacheDir); 122 | ... 123 | } 124 | 125 | class InitDiskCacheTask extends AsyncTask { 126 | @Override 127 | protected Void doInBackground(File... params) { 128 | synchronized (mDiskCacheLock) { 129 | File cacheDir = params[0]; 130 | mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE); 131 |   mDiskCacheStarting = false; // 结束初始化 132 |   mDiskCacheLock.notifyAll(); // 唤醒等待线程 133 | } 134 | return null; 135 | } 136 | } 137 | 138 | class BitmapWorkerTask extends AsyncTask { 139 | ... 140 | // 在后台解析图片 141 | @Override 142 | protected Bitmap doInBackground(Integer... params) { 143 | final String imageKey = String.valueOf(params[0]); 144 | 145 | // 在后台线程中检测磁盘缓存 146 | Bitmap bitmap = getBitmapFromDiskCache(imageKey); 147 | 148 | if (bitmap == null) { // 没有在磁盘缓存中找到图片 149 | final Bitmap bitmap = decodeSampledBitmapFromResource( 150 | getResources(), params[0], 100, 100)); 151 | } 152 | 153 | // 把这个final类型的bitmap加到缓存中 154 | addBitmapToCache(imageKey, bitmap); 155 | 156 | return bitmap; 157 | } 158 | ... 159 | } 160 | 161 | public void addBitmapToCache(String key, Bitmap bitmap) { 162 | // 先加到内存缓存 163 | if (getBitmapFromMemCache(key) == null) { 164 | mMemoryCache.put(key, bitmap); 165 | } 166 | 167 | //再加到磁盘缓存 168 | synchronized (mDiskCacheLock) { 169 | if (mDiskLruCache != null && mDiskLruCache.get(key) == null) { 170 | mDiskLruCache.put(key, bitmap); 171 | } 172 | } 173 | } 174 | 175 | public Bitmap getBitmapFromDiskCache(String key) { 176 | synchronized (mDiskCacheLock) { 177 | // 等待磁盘缓存从后台线程打开 178 | while (mDiskCacheStarting) { 179 | try { 180 | mDiskCacheLock.wait(); 181 | } catch (InterruptedException e) {} 182 | } 183 | if (mDiskLruCache != null) { 184 | return mDiskLruCache.get(key); 185 | } 186 | } 187 | return null; 188 | } 189 | 190 | public static File getDiskCacheDir(Context context, String uniqueName) { 191 | // 优先使用外缓存路径,如果没有挂载外存储,就使用内缓存路径 192 | final String cachePath = 193 | Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || 194 | !isExternalStorageRemovable() ?getExternalCacheDir(context).getPath():context.getCacheDir().getPath(); 195 | 196 | return new File(cachePath + File.separator + uniqueName); 197 | } 198 | ``` 199 | 200 |   不能在UI主线程中进行这项操作,因为初始化磁盘缓存也需要对磁盘进行操作。上面的程序片段中,一个锁对象确保了磁盘缓存没有初始化完成之前不能够对磁盘缓存进行访问。 201 | 202 |    内存缓存在UI线程中进行检测,磁盘缓存在UI主线程外的线程中进行检测,当图片处理完成之后,分别存储到内存缓存和磁盘缓存中。 203 | 204 | ##设备配置参数改变时加载问题 205 | 206 |   由于应用在运行的时候设备配置参数可能会发生改变,比如设备朝向改变,会导致Android销毁你的Activity然后按照新的配置重启,这种情况下,我们要避免重新去加载处理所有的图片,让用户能有一个流畅的体验。 207 | 208 |  使用Fragment 能够把内存缓存对象传递到新的activity实例中,调用setRetainInstance(true)) 方法来保留Fragment实例。当activity重新创建好后, 被保留的Fragment依附于activity而存在,通过Fragment就可以获取到已经存在的内存缓存对象了,这样就可以快速的获取到图片,并设置到ImageView上,给用户一个流畅的体验。 209 | 210 | 下面是一个示例程序片段: 211 | 212 | ``` 213 | private LruCache mMemoryCache; 214 | 215 | @Override 216 | protected void onCreate(Bundle savedInstanceState) { 217 | ... 218 | RetainFragment mRetainFragment = RetainFragment.findOrCreateRetainFragment(getFragmentManager()); 219 | mMemoryCache = RetainFragment.mRetainedCache; 220 | if (mMemoryCache == null) { 221 | mMemoryCache = new LruCache(cacheSize) { 222 | ... //像上面例子中那样初始化缓存 223 | } 224 | mRetainFragment.mRetainedCache = mMemoryCache; 225 | } 226 | ... 227 | } 228 | 229 | class RetainFragment extends Fragment { 230 | private static final String TAG = "RetainFragment"; 231 | public LruCache mRetainedCache; 232 | 233 | public RetainFragment() {} 234 | 235 | public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { 236 | RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG); 237 | if (fragment == null) { 238 | fragment = new RetainFragment(); 239 | } 240 | return fragment; 241 | } 242 | 243 | @Override 244 | public void onCreate(Bundle savedInstanceState) { 245 | super.onCreate(savedInstanceState); 246 | // 使得Fragment在Activity销毁后还能够保留下来 247 | setRetainInstance(true); 248 | } 249 | } 250 | ``` 251 | 252 |   可以在不适用Fragment(没有界面的服务类Fragment)的情况下旋转设备屏幕。在保留缓存的情况下,你应该能发现填充图片到Activity中几乎是瞬间从内存中取出而没有任何延迟的感觉。任何图片优先从内存缓存获取,没有的话再到硬盘缓存中找,如果都没有,那就以普通方式加载图片。 253 |    254 | 参考: 255 | 256 | [Caching Bitmaps](http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html) 257 | 258 | [LruCache](http://developer.android.com/reference/android/util/LruCache.html) 259 | 260 | #使用SQLite进行缓存 261 | 262 |   网络请求数据完成后,把文件的相关信息(如url(一般作为唯一标示),下载时间,过期时间)等存放到数据库。下次加载的时候根据url先从数据库中查询,如果查询到并且时间未过期,就根据路径读取本地文件,从而实现缓存的效果。 263 | 264 |   注意:缓存的数据库是存放在/data/data//databases/目录下,是占用内存空间的,如果缓存累计,容易浪费内存,需要及时清理缓存。 265 | 266 | #文件缓存 267 | 268 |   思路和一般缓存一样,把需要的数据存储在文件中,下次加载时判断文件是否存在和过期(使用File.lastModified()方法得到文件的最后修改时间,与当前时间判断),存在并未过期就加载文件中的数据,否则请求服务器重新下载。 269 | 270 |   注意,无网络环境下就默认读取文件缓存中的。 271 | 272 | -------------------------------------------------------------------------------- /android/Android 自定义ViewGroup入门实践.md: -------------------------------------------------------------------------------- 1 | 2 | >   对自定义view还不是很了解的码友可以先看[自定义View入门](http://blog.csdn.net/Amazing7/article/details/51303289)这篇文章,本文主要对自定义ViewGroup的过程的梳理,废话不多说。 3 | 4 | #1.View 绘制流程 5 | 6 |   ViewGroup也是继承于View,下面看看绘制过程中依次会调用哪些函数。 7 | 8 |  ![这里写图片描述](http://img.blog.csdn.net/20160617180933525) 9 | 10 | 说明: 11 | 12 | - measure()和onMeasure() 13 | 14 |   在View.Java源码中: 15 |    16 | ``` 17 | public final void measure(int widthMeasureSpec,int heightMeasureSpec){ 18 | ... 19 | onMeasure 20 | ... 21 | } 22 | 23 | protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) { 24 | setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 25 | getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 26 | } 27 | ``` 28 | 29 |   可以看出measure()是被final修饰的,这是不可被重写。onMeasure在measure方法中调用的,当我们继承View的时候通过重写onMeasure方法来测量控件大小。 30 | 31 |   layout()和onLayout(),draw()和onDraw()类似。 32 | 33 | - dispatchDraw() 34 | 35 |   View 中这个函数是一个空函数,ViewGroup 复写了dispatchDraw()来对其子视图进行绘制。自定义的 ViewGroup 一般不对dispatchDraw()进行复写。 36 | 37 | - requestLayout() 38 | 39 |   当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。 40 | 41 | 42 | 自定义ViewGroup的时候一般复写 43 | 44 | > onMeasure()方法: 45 | > 46 |   计算childView的测量值以及模式,以及设置自己的宽和高  47 |    48 | >onLayout()方法, 49 | 50 | > 对其所有childView的位置进行定位 51 | 52 | View树: 53 | 54 | ![这里写图片描述](http://img.blog.csdn.net/20160617181010906) 55 | 56 |  树的遍历是有序的,由父视图到子视图,每一个 ViewGroup 负责测绘它所有的子视图,而最底层的 View 会负责测绘自身。 57 | 58 | - **measure:** 59 | 60 |   自上而下进行遍历,根据父视图对子视图的MeasureSpec以及ChildView自身的参数,通过   61 |    62 | 63 | ``` 64 | getChildMeasureSpec(parentHeightMeasure,mPaddingTop+mPaddingBottom,lp.height) 65 | ``` 66 |   获取ChildView的MeasureSpec,回调ChildView.measure最终调用setMeasuredDimension得到ChildView的尺寸: 67 | 68 | ``` 69 | mMeasuredWidth 和 mMeasuredHeight 70 | ``` 71 | 72 | - **Layout :** 73 | 74 |    也是自上而下进行遍历的,该方法计算每个ChildView的ChildLeft,ChildTop;与measure中得到的每个ChildView的mMeasuredWidth 和 mMeasuredHeight,来对ChildView进行布局。 75 |     76 | 77 | ``` 78 | child.layout(left,top,left+width,top+height) 79 | ``` 80 | 81 | 82 | #2.onMeasure过程 83 | 84 |   measure过程会为一个View及所有子节点的mMeasuredWidth 85 | 和mMeasuredHeight变量赋值,该值可以通过getMeasuredWidth()和getMeasuredHeight()方法获得。 86 | 87 | **onMeasure过程传递尺寸的两个类:** 88 | 89 | - **ViewGroup.LayoutParams** (ViewGroup 自身的布局参数) 90 | 91 |   用来指定视图的高度和宽度等参数,使用 view.getLayoutParams() 方法获取一个视图LayoutParams,该方法得到的就是其所在父视图类型的LayoutParams,比如View的父控件为RelativeLayout,那么得到的 LayoutParams 类型就为RelativeLayoutParams。 92 | 93 | 94 | > ①具体值   95 | > 96 | ②MATCH_PARENT 表示子视图希望和父视图一样大(不包含 padding 值)   97 | > 98 | ③WRAP_CONTENT 表示视图为正好能包裹其内容大小(包含 padding 值) 99 |    100 | 101 | - **MeasureSpecs** 102 | 103 |   测量规格,包含测量要求和尺寸的信息,有三种模式: 104 | 105 | 106 | 107 | > ①UNSPECIFIED 108 | > 109 |   父视图不对子视图有任何约束,它可以达到所期望的任意尺寸。比如 ListView、ScrollView,一般自定义 View 中用不到 110 |    111 | > ②EXACTLY  112 | > 113 |   父视图为子视图指定一个确切的尺寸,而且无论子视图期望多大,它都必须在该指定大小的边界内,对应的属性为 match_parent 或具体值,比如 100dp,父控件可以通过MeasureSpec.getSize(measureSpec)直接得到子控件的尺寸。 114 | 115 | > ③AT_MOST  116 | > 117 |    父视图为子视图指定一个最大尺寸。子视图必须确保它自己所有子视图可以适应在该尺寸范围内,对应的属性为 wrap_content,这种模式下,父控件无法确定子 View 的尺寸,只能由子控件自己根据需求去计算自己的尺寸,这种模式就是我们自定义视图需要实现测量逻辑的情况。  118 | 119 | #3.onLayout 过程 120 | 121 |   子视图的具体位置都是相对于父视图而言的。View 的 onLayout 方法为空实现,而 ViewGroup 的 onLayout 为 abstract 的,因此,如果自定义的自定义ViewGroup 时,必须实现 onLayout 函数。 122 |    123 |   在 layout 过程中,子视图会调用getMeasuredWidth()和getMeasuredHeight()方法获取到 measure 过程得到的 mMeasuredWidth 和 mMeasuredHeight,作为自己的 width 和 height。然后调用每一个子视图的layout(l, t, r, b)函数,来确定每个子视图在父视图中的位置。 124 | 125 | #4.示例程序 126 | 127 | 先上效果图: 128 | 129 | ![这里写图片描述](http://img.blog.csdn.net/20160617215723283) 130 | 131 | 代码中有详细的注释,结合上文中的说明,理解应该没有问题。这里主要贴出核心代码。 132 | 133 | FlowLayout.java中(参照阳神的慕课课程) 134 | 135 | > onMeasure方法 136 | 137 | ``` 138 | @Override 139 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 140 | { 141 | // 获得它的父容器为它设置的测量模式和大小 142 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); 143 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec); 144 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); 145 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec); 146 | 147 | // 用于warp_content情况下,来记录父view宽和高 148 | int width = 0; 149 | int height = 0; 150 | 151 | // 取每一行宽度的最大值 152 | int lineWidth = 0; 153 | // 每一行的高度累加 154 | int lineHeight = 0; 155 | 156 | // 获得子view的个数 157 | int cCount = getChildCount(); 158 | 159 | for (int i = 0; i < cCount; i++) 160 | { 161 | View child = getChildAt(i); 162 | // 测量子View的宽和高(子view在布局文件中是wrap_content) 163 | measureChild(child, widthMeasureSpec, heightMeasureSpec); 164 | // 得到LayoutParams 165 | MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 166 | 167 | // 根据测量宽度加上Margin值算出子view的实际宽度(上文中有说明) 168 | int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; 169 | // 根据测量高度加上Margin值算出子view的实际高度 170 | int childHeight = child.getMeasuredHeight() + lp.topMargin+ lp.bottomMargin; 171 | 172 | // 这里的父view是有padding值的,如果再添加一个元素就超出最大宽度就换行 173 | if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) 174 | { 175 | // 父view宽度=以前父view宽度、当前行宽的最大值 176 | width = Math.max(width, lineWidth); 177 | // 换行了,当前行宽=第一个view的宽度 178 | lineWidth = childWidth; 179 | // 父view的高度=各行高度之和 180 | height += lineHeight; 181 | //换行了,当前行高=第一个view的高度 182 | lineHeight = childHeight; 183 | } else{ 184 | // 叠加行宽 185 | lineWidth += childWidth; 186 | // 得到当前行最大的高度 187 | lineHeight = Math.max(lineHeight, childHeight); 188 | } 189 | // 最后一个控件 190 | if (i == cCount - 1) 191 | { 192 | width = Math.max(lineWidth, width); 193 | height += lineHeight; 194 | } 195 | } 196 | /** 197 | * EXACTLY对应match_parent 或具体值 198 | * AT_MOST对应wrap_content 199 | * 在FlowLayout布局文件中 200 | * android:layout_width="fill_parent" 201 | * android:layout_height="wrap_content" 202 | * 203 | * 如果是MeasureSpec.EXACTLY则直接使用父ViewGroup传入的宽和高,否则设置为自己计算的宽和高。 204 | */ 205 | setMeasuredDimension( 206 | modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), 207 | modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop()+ getPaddingBottom() 208 | ); 209 | 210 | } 211 | ``` 212 | > onLayout方法 213 | 214 | ``` 215 | //存储所有的View 216 | private List> mAllViews = new ArrayList>(); 217 | //存储每一行的高度 218 | private List mLineHeight = new ArrayList(); 219 | 220 | @Override 221 | protected void onLayout(boolean changed, int l, int t, int r, int b) 222 | { 223 | mAllViews.clear(); 224 | mLineHeight.clear(); 225 | 226 | // 当前ViewGroup的宽度 227 | int width = getWidth(); 228 | 229 | int lineWidth = 0; 230 | int lineHeight = 0; 231 | // 存储每一行所有的childView 232 | List lineViews = new ArrayList(); 233 | 234 | int cCount = getChildCount(); 235 | 236 | for (int i = 0; i < cCount; i++) 237 | { 238 | View child = getChildAt(i); 239 | MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 240 | 241 | int childWidth = child.getMeasuredWidth(); 242 | int childHeight = child.getMeasuredHeight(); 243 | 244 | lineWidth += childWidth + lp.leftMargin + lp.rightMargin; 245 | lineHeight = Math.max(lineHeight, childHeight + lp.topMargin+ lp.bottomMargin); 246 | lineViews.add(child); 247 | 248 | // 换行,在onMeasure中childWidth是加上Margin值的 249 | if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) 250 | { 251 | // 记录行高 252 | mLineHeight.add(lineHeight); 253 | // 记录当前行的Views 254 | mAllViews.add(lineViews); 255 | 256 | // 新行的行宽和行高 257 | lineWidth = 0; 258 | lineHeight = childHeight + lp.topMargin + lp.bottomMargin; 259 | // 新行的View集合 260 | lineViews = new ArrayList(); 261 | } 262 | 263 | } 264 | // 处理最后一行 265 | mLineHeight.add(lineHeight); 266 | mAllViews.add(lineViews); 267 | 268 | // 设置子View的位置 269 | 270 | int left = getPaddingLeft(); 271 | int top = getPaddingTop(); 272 | 273 | // 行数 274 | int lineNum = mAllViews.size(); 275 | 276 | for (int i = 0; i < lineNum; i++) 277 | { 278 | // 当前行的所有的View 279 | lineViews = mAllViews.get(i); 280 | lineHeight = mLineHeight.get(i); 281 | 282 | for (int j = 0; j < lineViews.size(); j++) 283 | { 284 | View child = lineViews.get(j); 285 | // 判断child的状态 286 | if (child.getVisibility() == View.GONE) 287 | { 288 | continue; 289 | } 290 | 291 | MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 292 | 293 | int lc = left + lp.leftMargin; 294 | int tc = top + lp.topMargin; 295 | int rc = lc + child.getMeasuredWidth(); 296 | int bc = tc + child.getMeasuredHeight(); 297 | 298 | // 为子View进行布局 299 | child.layout(lc, tc, rc, bc); 300 | 301 | left += child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin; 302 | } 303 | left = getPaddingLeft() ; 304 | top += lineHeight ; 305 | } 306 | 307 | } 308 | 309 | /** 310 | * 因为我们只需要支持margin,所以直接使用系统的MarginLayoutParams 311 | */ 312 | @Override 313 | public LayoutParams generateLayoutParams(AttributeSet attrs) 314 | { 315 | return new MarginLayoutParams(getContext(), attrs); 316 | } 317 | ``` 318 | 319 | > 以及MainActivity.java 320 | 321 | ``` 322 | public class MainActivity extends Activity { 323 | 324 | LayoutInflater mInflater; 325 | @InjectView(R.id.id_flowlayout1) 326 | FlowLayout idFlowlayout1; 327 | @InjectView(R.id.id_flowlayout2) 328 | FlowLayout idFlowlayout2; 329 | private String[] mVals = new String[] 330 | {"Do", "one thing", "at a time", "and do well.", "Never", "forget", 331 | "to say", "thanks.", "Keep on", "going ", "never give up."}; 332 | 333 | @Override 334 | protected void onCreate(Bundle savedInstanceState) { 335 | super.onCreate(savedInstanceState); 336 | setContentView(R.layout.activity_main); 337 | ButterKnife.inject(this); 338 | mInflater = LayoutInflater.from(this); 339 | initFlowlayout2(); 340 | } 341 | 342 | public void initFlowlayout2() { 343 | for (int i = 0; i < mVals.length; i++) { 344 | final RelativeLayout rl2 = (RelativeLayout) mInflater.inflate(R.layout.flow_layout, idFlowlayout2, false); 345 | TextView tv2 = (TextView) rl2.findViewById(R.id.tv); 346 | tv2.setText(mVals[i]); 347 | rl2.setTag(i); 348 | idFlowlayout2.addView(rl2); 349 | rl2.setOnClickListener(new View.OnClickListener() { 350 | @Override 351 | public void onClick(View v) { 352 | int i = (int) v.getTag(); 353 | addViewToFlowlayout1(i); 354 | rl2.setBackgroundResource(R.drawable.flow_layout_disable_bg); 355 | rl2.setClickable(false); 356 | } 357 | }); 358 | 359 | } 360 | } 361 | public void addViewToFlowlayout1(int i){ 362 | RelativeLayout rl1 = (RelativeLayout) mInflater.inflate(R.layout.flow_layout, idFlowlayout1, false); 363 | ImageView iv = (ImageView) rl1.findViewById(R.id.iv); 364 | iv.setVisibility(View.VISIBLE); 365 | TextView tv1 = (TextView) rl1.findViewById(R.id.tv); 366 | tv1.setText(mVals[i]); 367 | rl1.setTag(i); 368 | idFlowlayout1.addView(rl1); 369 | rl1.setOnClickListener(new View.OnClickListener() { 370 | @Override 371 | public void onClick(View v) { 372 | int i = (int) v.getTag(); 373 | idFlowlayout1.removeView(v); 374 | View view = idFlowlayout2.getChildAt(i); 375 | view.setClickable(true); 376 | view.setBackgroundResource(R.drawable.flow_layout_bg); 377 | } 378 | }); 379 | } 380 | ``` 381 | 382 | > 这个项目源码已经上传,想要看源码的朋友可以 383 | 384 | >点击 [FlowLayout](https://github.com/fanrunqi/FlowLayout) 385 | > 386 | > 如果有什么疑问可以给我留言,不足之处欢迎在github上指出,谢谢! -------------------------------------------------------------------------------- /android/Android 自定义View入门.md: -------------------------------------------------------------------------------- 1 | > 在android应用开发过程中,固定的一些控件和属性可能满足不了开发的需求,所以在一些特殊情况下,我们需要自定义控件与属性。 2 | 3 | #一、实现步骤 4 | 5 |    6 | 7 |   1. 继承View类或其子类  8 | 9 |   2. 复写view中的一些函数 10 | 11 |  3.为自定义View类增加属性(两种方式) 12 | 13 |  4.绘制控件(导入布局) 14 | 15 |  5.响应用户事件 16 | 17 |  6.定义回调函数(根据自己需求来选择) 18 | 19 | #二、哪些方法需要被重写 20 | 21 |    22 | 23 | - onDraw() 24 | 25 |   view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的绘制。对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的(但必须实现dispatchDraw()函数,告诉子view绘制自己)。 26 | 27 | - onLayout() 28 | 29 |   主要是为viewGroup类型布局子视图用的,在View中这个函数为空函数。 30 | 31 | - onMeasure() 32 | 33 |   用于计算视图大小(即长和宽)的方式,并通过setMeasuredDimension(width, height)保存计算结果。 34 | 35 | - onTouchEvent 36 | 37 |   定义触屏事件来响应用户操作。 38 |    39 | 40 | 还有一些不常用的方法: 41 | >   onKeyDown 当按下某个键盘时   42 | > 43 |   onKeyUp 当松开某个键盘时   44 |    45 |   onTrackballEvent 当发生轨迹球事件时   46 |    47 |   onSizeChange() 当该组件的大小被改变时   48 |    49 |   onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法   50 |    51 |   onWindowFocusChanged(boolean) 当该组件得到、失去焦点时   52 |   onAttachedToWindow() 当把该组件放入到某个窗口时   53 |    54 |   onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法   55 |    56 |   onWindowVisibilityChanged(int): 当包含该组件的窗口的可见性发生改变时触发的方法   57 | 58 | **View的绘制流程** 59 | 绘制流程函数调用关系如下图: 60 | 61 | ![这里写图片描述](http://img.blog.csdn.net/20160617150747985) 62 | 63 | 我们调用requestLayout()的时候,会触发measure 和 layout 过程,调用invalidate,会执行 draw 过程。 64 | 65 | #三.自定义控件的三种方式 66 | 67 |    68 | 69 | 1. 继承已有的控件 70 | 71 |   当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。 72 | 73 | 2. 继承一个布局文件 74 | 75 |   一般用于自定义组合控件,在构造函数中通过inflater和addView()方法加载自定义控件的布局文件形成图形界面(不需要onDraw方法)。 76 | 77 | 3.继承view 78 | 79 |   通过onDraw方法来绘制出组件界面。 80 | 81 | #四.自定义属性的两种方法 82 | 83 |   1.在布局文件中直接加入属性,在构造函数中去获得。 84 | 85 | 86 | 布局文件: 87 | 88 | ``` 89 | 93 | 98 | 99 | 100 | ``` 101 | 获取属性值: 102 | 103 | ``` 104 | public myView(Context context, AttributeSet attrs) { 105 | super(context, attrs); 106 | // TODO Auto-generated constructor stub 107 | int textId = attrs.getAttributeResourceValue(null, "Text", 0); 108 | String text = context.getResources().getText(textId).toString(); 109 | } 110 | ``` 111 | 112 | 2.在res/values/ 下建立一个attrs.xml 来声明自定义view的属性。 113 | 114 | 115 | 可以定义的属性有: 116 | ``` 117 | 118 | //参考某一资源ID (name可以随便命名) 119 | 120 | //颜色值 121 | 122 | //布尔值 123 | 124 | //尺寸值 125 | 126 | //浮点值 127 | 128 | //整型值 129 | 130 | //字符串 131 | 132 | //百分数 133 | 134 | 135 | //枚举值 136 | 137 | 138 | 139 | 140 | 141 | //位或运算 142 | 143 | 144 | 145 | 146 | 147 | //多类型 148 | 149 | 150 | ``` 151 | 152 | - attrs.xml进行属性声明 153 | 154 |    155 | 156 | ``` 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | ``` 166 | 167 | - 添加到布局文件 168 | 169 |    170 | 171 | ``` 172 | 177 | 183 | 184 | 185 | ``` 186 | 这里注意命名空间: 187 | xmlns:前缀=”http://schemas.android.com/apk/res/包名(或res-auto)”, 188 | 189 | 前缀:TextColor 使用属性。 190 | 191 | - 在构造函数中获取属性值 192 | 193 |    194 | 195 | ``` 196 | public myView(Context context, AttributeSet attrs) { 197 | super(context, attrs); 198 | // TODO Auto-generated constructor stub 199 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 200 | String text = a.getString(R.styleable.myView_text); 201 | int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE); 202 | 203 | a.recycle(); 204 | } 205 | ``` 206 | 207 |  或者: 208 | 209 |   210 | 211 | ``` 212 | public myView(Context context, AttributeSet attrs) { 213 | super(context, attrs); 214 | // TODO Auto-generated constructor stub 215 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 216 | int n = a.getIndexCount(); 217 | for(int i=0;i 238 | 239 | 240 | 实现上面的效果我们大致需要分成这几步 241 | 242 | - 在res/values/ 下建立一个attrs.xml 来声明自定义view的属性 243 | - 一个继承View并复写部分函数的自定义view的类 244 | - 一个展示自定义view 的容器界面 245 | 246 | 1.自定义view命名为myView,它有一个属性值,格式为color、 247 | 248 | ``` 249 | 250 | 251 | 252 | 253 | 254 | 255 | ``` 256 | 257 | 2.在构造函数获取获得view的属性配置和复写onDraw和onTouchEvent函数实现绘制界面和用户事件响应。 258 | 259 | ``` 260 | public class myView extends View{ 261 | //定义画笔和初始位置 262 | Paint p = new Paint(); 263 | public float currentX = 50; 264 | public float currentY = 50; 265 | public int textColor; 266 | 267 | public myView(Context context, AttributeSet attrs) { 268 | super(context, attrs); 269 | //获取资源文件里面的属性,由于这里只有一个属性值,不用遍历数组,直接通过R文件拿出color值 270 | //把属性放在资源文件里,方便设置和复用 271 | TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView); 272 | textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK); 273 | array.recycle(); 274 | } 275 | 276 | @Override 277 | protected void onDraw(Canvas canvas) { 278 | super.onDraw(canvas); 279 | //画一个蓝色的圆形 280 | p.setColor(Color.BLUE); 281 | canvas.drawCircle(currentX,currentY,30,p); 282 | //设置文字和颜色,这里的颜色是资源文件values里面的值 283 | p.setColor(textColor); 284 | canvas.drawText("BY finch",currentX-30,currentY+50,p); 285 | } 286 | 287 | @Override 288 | public boolean onTouchEvent(MotionEvent event) { 289 | 290 | 291 | currentX = event.getX(); 292 | currentY = event.getY(); 293 | invalidate();//重新绘制图形 294 | return true; 295 | } 296 | } 297 | ``` 298 | 299 |   这里通过不断的更新当前位置坐标和重新绘制图形实现效果,要注意的是使用TypedArray后一定要记得recycle(). 否则会对下次调用产生影响。 300 |   ![这里写图片描述](http://img.blog.csdn.net/20160503144335969)  301 | 302 | 303 | 3.把myView加入到activity_main.xml布局里面 304 | 305 |    306 | 307 | ``` 308 | 309 | 319 | 320 | 321 | 326 | 327 | ``` 328 | 329 | 4.最后是MainActivity 330 | 331 | ``` 332 | public class MainActivity extends AppCompatActivity { 333 | 334 | @Override 335 | protected void onCreate(Bundle savedInstanceState) { 336 | super.onCreate(savedInstanceState); 337 | setContentView(R.layout.activity_main); 338 | } 339 | } 340 | ``` 341 | 342 | - 具体的view要根据具体的需求来,比如我们要侧滑删除的listview我们可以继承listview,监听侧滑事件,显示删除按钮实现功能。 -------------------------------------------------------------------------------- /android/Android5.0、6.0、7.0新特性.md: -------------------------------------------------------------------------------- 1 | Android 7.0 2 | 3 | 分屏多任务支持 4 | 5 | 画中画 6 | 7 | 通知栏快速回复 8 | 9 | OpenJDK替换Java API 10 | 11 | Android 7.0中采用了一项具有实时代码剖析功能的ARI JIT编译器,它能够在安卓应用程序在运行时不断提高自身的性能 12 | 13 | [art虚拟机介绍](http://www.cnblogs.com/manuosex/p/3634375.html) 14 | http://www.cnblogs.com/manuosex/p/3634375.html 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- 22 | Android6.0 23 | 24 | 1、大量漂亮流畅的动画 25 | 安卓6.0系统增加了大量漂亮的过度动画,可以从视觉上减少卡顿感,给用户带来流畅的体验。 26 | 27 | 2、相机新增专业模式 28 | 一直以来,原生的安卓相机都长被吐槽太过简单甚至简陋了,在此次的安卓6.0中,相机中新增了Pro专业模式,增加了快门速度调节和曝光度调节等新功能。 29 | 30 | 3、全新的电源键菜单 31 | 一般来说,安卓的电源键菜单都是关机/重启/飞行,安卓6.0变成了关机/重启/紧急,关机和重启就不用赘述了,这个紧急模式是为了手机快没电的时候设计的,相当于飞行模式的高级版,可以关闭一切耗电应用,尽最大可能节省电量。 32 | 33 | 4、可自定义锁界面样式 34 | 支持电话、信息、相机等快捷方式在锁屏界面的定制,用户可以根据自己的喜好调整这些图标的位置,或者开启或关闭这些快捷方式。 35 | 36 | 5、全新的快速设置风格 37 | 不但是锁屏界面可以定制,安卓6.0还采用了全新的快速面板的色彩方案,用户可以通过更换主题换颜色。 38 | 39 | 6、支持快速充电的切换 40 | 快速充电是手机厂商们的一大新发明,很多厂商都声称“充电X分钟,通话两小时”,这个功能虽然方便,但其实也有弊端,容易造成手机和电池发热。所以除非是在紧急情况下,一般不建议快速充电,安卓6.0原生支持关闭和开启快速充电功能。 41 | 42 | 7、支持文件夹拖拽应用 43 | 可在应用从一个文件夹内直接拖到另一个文件夹,简化了此前繁琐的操作方式,拖拽的过程和Windows的拖拽功能有点相似。 44 | 45 | 8、原生的应用权限管理 46 | 无需第三方应用和Root权限,原生的安卓6.0就支持应用权限管理,用户可以在安装应用时选择关闭一些应用权限,这一功能非常方便,再也不用担心流量偷跑和扣费了。 47 | 48 | 9、Now on Tap功能 49 | “Now on Tap ”功能,是指将Google Now(一种语音助手)作为底层植入到安卓6.0系统中,用户只要只要双击home键启动Google Now,“这意味着用户随时都能启动搜索功能,目前暂时不知道这个功能进入国内会不会阉割掉。 50 | 51 | 10、支持RAW格式照片 52 | RAW格式的支持是众多拍照爱好者梦寐以求的, 然而绝大多数的安卓手机都没有或者剔除了这项功能。由于照片保存为jpg格式时或多或少都会损失一些画质,所以支持RAW格式是非常明智的。 53 | 54 | 55 | -------------------------------------------------------------------------------- 56 | Android5.0 57 | 58 | - 全新 Material Design 设计风格 59 | 60 | - 支持多种设备(手机、平板电脑、笔记本电脑、智能电视、汽车、智能手表甚至是各种家用电子产品) 61 | 62 | - 全新的通知中心设计(在锁屏界面也可以直接查看通知消息了,用户还可以直接在锁屏的情况下就行回复或进入应用。) 63 | 64 | - 支持 64 位 ART 虚拟机 65 |   66 | >   新系统不仅在视觉效果上带来了巨大的变化,Android Lollipop 还在内部的性能上进行了飞跃。首先,新系统放弃了之前一直使用的 Dalvik 虚拟机,改用了 ART 模式,实现了真正的跨平台编译,在 ARM、X86、MIPS 等,无处不在。 67 |   ART 虚拟机编译器在内存占用及应用程序加载时间上进行了大幅提升,谷歌承诺所有性能都会比原来提升一倍。另外,对 64 位的支持也让 ART 虚拟机如鱼得水,开发者可以针对像 ARM Cortex-A57 这样的 64 位架构核心开发应用程序。Android Lollipop 支持更大的寄存器,支持新的指令集,提升了内存寻址空间,未来 Android 智能手机将支持 4GB 以上的内存。 68 | 69 | - Project Volta 电池续航改进计划 70 | 71 | 增加了 Battery Saver 模式,在低电量的时候系统会自动降低屏幕亮度、限制自动更换背景等功能。 72 | 73 | - 全新的“最近应用程序” 74 | 75 | 除了界面风格设计的改变之外,新的最近应用界面还借鉴了 Chrome 浏览器的理念,采用单独的标签展示方式。更重要的是,谷歌已经向开发者开放了 API,所以第三方开发人员可以利用这个改进为特定的应用增加全新的功能。 76 | 77 | - 新的 API 支持,蓝牙 4.1、USB Audio、多人分享等其它特性 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /android/Android中弱引用与软引用的应用场景.md: -------------------------------------------------------------------------------- 1 |   在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。 2 | 下面以使用软引用为例来详细说明。弱引用的使用方式与软引用是类似的。 3 | 4 |   假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软引用技术来避免这个问题发生。 5 | 6 |    使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。 7 |     8 |   需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。 -------------------------------------------------------------------------------- /android/Android启动过程图解.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Android手机开机执行过程图: 4 | 5 | ![这里写图片描述](http://img.blog.csdn.net/20160603133621981)  6 | 7 | 从开机到桌面的过程为: 8 | 9 | **Bootloader** ➪**Kernel** ➪**Init进程** ➪ **Zygote** ➪ **SystemServer** ➪ **ServiceManager** ➪ **Home Launcher** 10 | 11 | 12 |   Android服务包括系统服务和应用服务,系统服务是指Android系统在启动过程就已经启动实现了的服务,对于系统服务又分为Java服务和本地服务,Java服务是由Java代码编写而成,由SystemServer进程提供,而本地服务是由C/C++实现的服务,由Init进程在系统启动时启动的服务。应用服务是由开发者自行实现的某些特定服务。 13 | 14 | 15 | 20 | 21 |
16 | 17 | 1、Bootloader 18 | 19 |
22 | ## 23 | 24 |   当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。 25 |    26 |   BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。 27 | 28 |    29 | 30 | 31 | 36 | 37 |
32 | 33 | 2、Kernel 34 | 35 |
38 | ## 39 | 40 |   Android内核启动时,会设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。 41 | 42 | 43 | 44 | 45 | 50 | 51 |
46 | 47 | 3、init进程 48 | 49 |
52 | ## 53 | 54 |   init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。init始终是第一个进程。 55 | 56 |   启动过程就是代码init.c中main函数执行过程:system\core\init\init.c在函数中执行了:**文件夹建立**,**挂载**,**rc文件解析**,**属性设置**,**启动服务**,**执行动作**,**socket监听**…… 57 | 58 | 59 | - rc文件解析 60 | 61 |   .rc文件是Android使用的初始化脚本文件 ,Android中有特定的格式以及规则。 62 | 63 | 64 | 65 | 66 | 71 | 72 |
67 | 68 | 4、Zygote 69 | 70 |
73 | ## 74 | 75 |   所有的应用程序进程以及系统服务进程(SystemServer)都是由Zygote进程孕育(fork)出来的,zygote本身是Native应用程序,与驱动内核无关。 76 | 77 |   我们知道,Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的(在系统启动脚本system/core/rootdir/init.rc文件中)。 78 | 79 |   在Java中,不同的虚拟机实例会为不同的应用分配不同的内存。假如Android应用应该尽可能快地启动,但如果Android系统为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了”Zygote”。Zygote是一个虚拟器进程,预加载以及初始化核心库类,让Dalvik虚拟机共享代码、降低内存占用和启动时间。 80 | 81 | **Zygote进程包含两个主要模块:** 82 | 83 | ①. Socket服务端,该Socket服务端用于接收启动新的Dalvik进程命令。 84 | 85 | ②. Framework共享类及共享资源,当Zygote进程启动后,会装载一些共享类和资源,共享类是在preload-classes文件中定义的,共享资源是在preload-resources文件中定义。因为其他Dalvik进程是由Zygote进程孵化出来的,因此只要Zygote装载好了这些类和资源后,新的Dalvik进程就不需要在装载这些类和资源了,它们共享Zygote进程的资源和类。 86 | 87 | 88 | **Zygote启动分为两个阶段:** 89 | 90 |  ①. **虚拟机启动 --- 通过native启动**  91 | 92 | - startVm(&mJavaVM, &env)   启动虚拟机  93 | 94 | - onVmCreated(env) 虚拟机启动后的初始化 95 | 96 | - startReg(env) 注册JNI函数 97 | 98 | - env->CallStaticVoidMethod(startClass, startMeth, strArray) 调用ZygoteInit类的main函数开创java世界  99 |              100 | ②. **SystemServer进程 --- 通过Java启动**  101 | 102 | - registerZygoteSocket()  为zygote进程注册监听socket 103 | 104 | - preload() 加载常用的JAVA类和系统资源 105 | 106 | - startSystemServer() 启动SystemServer进程 107 | 108 | - runSelectLoopMode() 进入循环监听模式 109 | 110 | - closeServerSocket() 进程退出时,关闭socket监听 111 | 112 | 113 | 114 | 119 | 120 |
115 | 116 | 5、启动系统服务 117 | 118 |
121 | ## 122 | 123 |   Zygote创建新的进程去启动系统服务。你可以在ZygoteInit类的”startSystemServer”方法中找到源代码。 124 | 125 | 核心服务: 126 | 127 | > 启动电源管理器;  128 | > 129 | 创建Activity管理器;  130 | 131 | >启动电话注册;  132 | 133 | >启动包管理器; 134 | > 135 | >设置Activity管理服务为系统进程; 136 | > 137 | >启动上下文管理器; 138 | > 139 | >启动系统Context Providers; 140 | > 141 | >启动电池服务; 142 | > 143 | >启动定时管理器; 144 | > 145 | >启动传感服务; 146 | > 147 | >启动窗口管理器; 148 | > 149 | >启动蓝牙服务; 150 | > 151 | >启动挂载服务。 152 | > 153 | 其他服务: 154 | 155 | 156 | 157 | 162 | 163 |
158 | 159 | 6、引导完成 160 | 161 |
164 | ## 165 | 166 |   一旦系统服务在内存中跑起来了,Android就完成了引导过程。在这个时候“ACTION_BOOT_COMPLETED”开机启动广播就会发出去。 -------------------------------------------------------------------------------- /android/Android异步任务机制之AsycTask.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > 在Android中实现异步任务机制有两种方式,**Handler**和**AsyncTask**。 4 | > 5 | > Handler已经在上一篇文章 [异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析](http://blog.csdn.net/amazing7/article/details/51424038#reply) 说过了。 6 | > 7 | > 本篇就说说AsyncTask的异步实现。 8 | 9 | 10 | 11 | 16 | 17 |
12 | 13 | 1、什么时候使用 AsnyncTask 14 | 15 |
18 | ## 19 | 20 |   在上一篇文章已经说了,主线程主要负责控制UI页面的显示、更新、交互等。 为了有更好的用户体验,UI线程中的操作要求越短越好。 21 | 22 |   我们把耗时的操作(例如网络请求、数据库操作、复杂计算)放到单独的子线程中操作,以避免主线程的阻塞。但是在子线程中不能更新UI界面,这时候需要使用handler。 23 | 24 |   但如果耗时的操作太多,那么我们需要开启太多的子线程,这就会给系统带来巨大的负担,随之也会带来性能方面的问题。在这种情况下我们就可以考虑使用类AsyncTask来异步执行任务,不需要子线程和handler,就可以完成异步操作和刷新UI。 25 | 26 | 27 |   不要随意使用AsyncTask,除非你必须要与UI线程交互.默认情况下使用Thread即可,要注意需要将线程优先级调低.AsyncTask适合处理短时间的操作,长时间的操作,比如下载一个很大的视频,这就需要你使用自己的线程来下载,不管是断点下载还是其它的. 28 | 29 | 30 | 31 | 36 | 37 |
32 | 33 | 2、AsnyncTask原理 34 | 35 |
38 | ## 39 | 40 |   AsyncTask主要有二个部分:一个是与主线程的交互,另一个就是线程的管理调度。虽然可能多个AsyncTask的子类的实例,但是AsyncTask的内部Handler和ThreadPoolExecutor都是进程范围内共享的,其都是static的,也即属于类的,类的属性的作用范围是CLASSPATH,因为一个进程一个VM,所以是AsyncTask控制着进程范围内所有的子类实例。  41 | 42 |   AsyncTask内部会创建一个进程作用域的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask的execute()方法后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。 43 | 44 | 45 | 46 | 51 | 52 |
47 | 48 | 3、AsyncTask介绍 49 | 50 |
53 | ## 54 |   Android的AsyncTask比Handler更轻量级一些(只是代码上轻量一些,而实际上要比handler更耗资源),适用于简单的异步处理。 55 |    56 |   Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),因为UI的更新只能在主线程中完成,因此异步处理是不可避免的。 57 | 58 |   AsyncTask:对线程间的通讯做了包装,是后台线程和UI线程可以简易通讯:后台线程执行异步任务,将result告知UI线程。 59 | 60 | 使用AsyncTask分为两步:  61 | 62 | ① 继承AsyncTask类实现自己的类 63 | 64 | ``` 65 | public abstract class AsyncTask { 66 | ``` 67 | 68 | > Params: 输入参数,对应excute()方法中传递的参数。如果不需要传递参数,则直接设为void即可。 69 | > 70 | > Progress:后台任务执行的百分比 71 | > 72 | > Result:返回值类型,和doInBackground()方法的返回值类型保持一致。 73 | 74 | ②复写方法 75 | 76 | 最少要重写以下这两个方法: 77 | 78 | - doInBackground(Params…) 79 | 80 |   在**子线程**(其他方法都在主线程执行)中执行比较耗时的操作,不能更新UI,可以在该方法中调用publishProgress(Progress…)来更新任务的进度。Progress方法是AsycTask中一个final方法只能调用不能重写。 81 | 82 | - onPostExecute(Result) 83 | 84 |   使用在doInBackground 得到的结果处理操作UI, 在主线程执行,任务执行的结果作为此方法的参数返回。 85 |    86 | 有时根据需求还要实现以下三个方法: 87 | 88 | - onProgressUpdate(Progress…) 89 | 90 |   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。 91 | 92 | - onPreExecute() 93 | 94 |   这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。 95 | 96 | - onCancelled() 97 | 98 |   用户调用取消时,要做的操作 99 | 100 | 101 | 102 | 103 | 108 | 109 |
104 | 105 | 4、AsyncTask示例 106 | 107 |
110 | ## 111 | 112 | 按照上面的步骤定义自己的异步类: 113 | 114 | ``` 115 | public class MyTask extends AsyncTask { 116 | //执行的第一个方法用于在执行后台任务前做一些UI操作 117 | @Override 118 | protected void onPreExecute() { 119 | 120 | } 121 | 122 | //第二个执行方法,在onPreExecute()后执行,用于后台任务,不可在此方法内修改UI 123 | @Override 124 | protected String doInBackground(String... params) { 125 | //处理耗时操作 126 | return "后台任务执行完毕"; 127 | } 128 | 129 | /*这个函数在doInBackground调用publishProgress(int i)时触发,虽然调用时只有一个参数 130 | 但是这里取到的是一个数组,所以要用progesss[0]来取值 131 | 第n个参数就用progress[n]来取值 */ 132 | @Override 133 | protected void onProgressUpdate(Integer... progresses) { 134 | //"loading..." + progresses[0] + "%" 135 | super.onProgressUpdate(progress); 136 | } 137 | 138 | /*doInBackground返回时触发,换句话说,就是doInBackground执行完后触发 139 | 这里的result就是上面doInBackground执行后的返回值,所以这里是"后台任务执行完毕" */ 140 | @Override 141 | protected void onPostExecute(String result) { 142 | 143 | } 144 | 145 | //onCancelled方法用于在取消执行中的任务时更改UI 146 | @Override 147 | protected void onCancelled() { 148 | 149 | } 150 | } 151 | ``` 152 | 153 | 在主线程申明该类的对象,调用对象的execute()函数开始执行。 154 | 155 | ``` 156 | MyTask t= new MyTask(); 157 | t.execute();//这里没有参数 158 | ``` 159 | 160 | 161 | 162 | 167 | 168 |
163 | 164 | 5、使用AsyncTask需要注意的地方 165 | 166 |
169 | ## 170 | 171 | - AsnycTask内部的Handler需要和主线程交互,所以AsyncTask的实例必须在UI线程中创建 172 | 173 | - AsyncTaskResult的doInBackground(mParams)方法执行异步任务运行在子线程中,其他方法运行在主线程中,可以操作UI组件。 174 | 175 | - 一个AsyncTask任务只能被执行一次。 176 | 177 | - 运行中可以随时调用AsnycTask对象的cancel(boolean)方法取消任务,如果成功,调用isCancelled()会返回true,并且不会执行 onPostExecute() 方法了,而是执行 onCancelled() 方法。 178 | 179 | - 对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask。默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了。 180 | 181 |    182 | 183 | -------------------------------------------------------------------------------- /android/Android长连接,怎么处理心跳机制.md: -------------------------------------------------------------------------------- 1 | > 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制。 2 | 3 | android系统的推送和iOS的推送有什么区别: 4 | 5 |    首先我们必须知道,所有的推送功能必须有一个客户端和服务器的长连接,因为推送是由服务器主动向客户端发送消息,如果客户端和服务器之间不存在一个长连接那么服务器是无法来主动连接客户端的。因而推送功能都是基于长连接的基础是上的。 6 |    7 |   IOS长连接是由系统来维护的,也就是说苹果的IOS系统在系统级别维护了一个客户端和苹果服务器的长链接,IOS上的所有应用上的推送都是先将消息推送到苹果的服务器然后将苹果服务器通过这个系统级别的长链接推送到手机终端上,这样的的几个好处为: 8 | 9 | 1.在手机终端始终只要维护一个长连接即可,而且由于这个长链接是系统级别的不会出现被杀死而无法推送的情况。   10 | 11 | 2.省电,不会出现每个应用都各自维护一个自己的长连接。  12 | 13 | 3.安全,只有在苹果注册的开发者才能够进行推送,等等。  14 | 15 |  android的长连接是由每个应用各自维护的,但是google也推出了和苹果技术架构相似的推送框架,C2DM,云端推送功能,但是由于google的服务器不在中国境内,其他的原因你懂的。所以导致这个推送无法使用,android的开发者不得不自己去维护一个长链接,于是每个应用如果都24小时在线,那么都得各自维护一个长连接,这种电量和流量的消耗是可想而知的。虽然国内也出现了各种推送平台,但是都无法达到只维护一个长连接这种消耗的级别。  16 |   17 | 推送的实现方式: 18 | 一:客户端不断的查询服务器,检索新内容,也就是所谓的pull 或者轮询方式  19 | 20 | 二:客户端和服务器之间维持一个TCP/IP长连接,服务器向客户端push  21 | 22 | 三:服务器有新内容时,发送一条类似短信的信令给客户端,客户端收到后从服务器中下载新内容,也就是SMS的推送方式  23 | 24 |   苹果的推送系统和googleC2DM推送系统其实都是在系统级别维护一个TCP/IP长连接,都是基于第二种的方式进行推送的。第三种方式由于运营商没有免费开放这种信令导致了这种推送在成本上是无法接受的,虽然这种推送的方式非常的稳定,高效和及时。 25 | 如果想了解android中各种推送方式请参考这个链接:Android实现推送方式解决方案 这篇博客已经介绍的非常好了。 26 | 27 |    所谓的心跳包就是客户端定时放送简单的信息给服务器端,告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务器端,服务器端回复一个固定信息。如果服务器端几分钟后没有收到客户端信息则视客户端断开。比如有些通信软件长时间不适用,要想知道它的状态是在线还是离线,就需要心跳包,定时发包收包。  28 |     29 |  心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活在。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。 30 | 在TCP机制里面,本身是存在有心跳包机制的,也就是TCP选项:SO_KEEPALIVE. 系统默认是设置的2小时的心跳频率。 31 | 32 |   心跳包的机制,其实就是传统的长连接。或许有的人知道消息推送的机制,消息推送也是一种长连接 ,是将数据有服务器端推送到客户端这边从而改变传统的“拉”的请求方式。下面我来介绍一下安卓和客户端两个数据请求的方式 33 | 1、push 这个也就是有服务器推送到客户端这边 现在有第三方技术 比如极光推送。 34 | 2、pull 这种方式就是客户端向服务器发送请求数据(http请求) 35 | 36 | 实现轮询  37 | 38 | ● 原理   39 | 40 |   其原理在于在android端的程序中,让一个SERVICE一直跑在后台,在规定时间之内调用服务器接口进行数据获取。这里的原理很简单,当然实现起来也不难;然后,这个类之中肯定要做网络了数据请求,所以我们在Service中建立一个线程(因为在android系统中网络请求属于长时间操作,不能放主线程,不然会导致异常),在线程中和服务器进行通信。 41 | 42 |   最后,这个逻辑写完后,我们需要考虑一个问题,如何进行在规定时间内调用该服务器,当然可以用Thread+Handler(这个不是那么稳定),也可以使用AlamManager+Thread(比较稳定),因为我们需要其在后台一直运行,所以可以依靠系统的Alammanager这个类来实现,Alammanager是属于系统的一个闹钟提醒类,通过它我们能实现在规定间隔时间调用,并且也比较稳定,这个service被杀后会自己自动启动服务。 43 | 44 | 45 | 46 | > 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制。 47 | 48 | android系统的推送和iOS的推送有什么区别: 49 | 50 |    首先我们必须知道,所有的推送功能必须有一个客户端和服务器的长连接,因为推送是由服务器主动向客户端发送消息,如果客户端和服务器之间不存在一个长连接那么服务器是无法来主动连接客户端的。因而推送功能都是基于长连接的基础是上的。 51 |    52 |   IOS长连接是由系统来维护的,也就是说苹果的IOS系统在系统级别维护了一个客户端和苹果服务器的长链接,IOS上的所有应用上的推送都是先将消息推送到苹果的服务器然后将苹果服务器通过这个系统级别的长链接推送到手机终端上,这样的的几个好处为: 53 | 54 | 1.在手机终端始终只要维护一个长连接即可,而且由于这个长链接是系统级别的不会出现被杀死而无法推送的情况。   55 | 56 | 2.省电,不会出现每个应用都各自维护一个自己的长连接。  57 | 58 | 3.安全,只有在苹果注册的开发者才能够进行推送,等等。  59 | 60 |  android的长连接是由每个应用各自维护的,但是google也推出了和苹果技术架构相似的推送框架,C2DM,云端推送功能,但是由于google的服务器不在中国境内,其他的原因你懂的。所以导致这个推送无法使用,android的开发者不得不自己去维护一个长链接,于是每个应用如果都24小时在线,那么都得各自维护一个长连接,这种电量和流量的消耗是可想而知的。虽然国内也出现了各种推送平台,但是都无法达到只维护一个长连接这种消耗的级别。  61 |   62 | 推送的实现方式: 63 | 一:客户端不断的查询服务器,检索新内容,也就是所谓的pull 或者轮询方式  64 | 65 | 二:客户端和服务器之间维持一个TCP/IP长连接,服务器向客户端push  66 | 67 | 三:服务器有新内容时,发送一条类似短信的信令给客户端,客户端收到后从服务器中下载新内容,也就是SMS的推送方式  68 | 69 |   苹果的推送系统和googleC2DM推送系统其实都是在系统级别维护一个TCP/IP长连接,都是基于第二种的方式进行推送的。第三种方式由于运营商没有免费开放这种信令导致了这种推送在成本上是无法接受的,虽然这种推送的方式非常的稳定,高效和及时。 70 | 如果想了解android中各种推送方式请参考这个链接:Android实现推送方式解决方案 这篇博客已经介绍的非常好了。 71 | 72 |    所谓的心跳包就是客户端定时放送简单的信息给服务器端,告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务器端,服务器端回复一个固定信息。如果服务器端几分钟后没有收到客户端信息则视客户端断开。比如有些通信软件长时间不适用,要想知道它的状态是在线还是离线,就需要心跳包,定时发包收包。  73 |     74 |  心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活在。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。 75 | 在TCP机制里面,本身是存在有心跳包机制的,也就是TCP选项:SO_KEEPALIVE. 系统默认是设置的2小时的心跳频率。 76 | 77 |   心跳包的机制,其实就是传统的长连接。或许有的人知道消息推送的机制,消息推送也是一种长连接 ,是将数据有服务器端推送到客户端这边从而改变传统的“拉”的请求方式。下面我来介绍一下安卓和客户端两个数据请求的方式 78 | 1、push 这个也就是有服务器推送到客户端这边 现在有第三方技术 比如极光推送。 79 | 2、pull 这种方式就是客户端向服务器发送请求数据(http请求) 80 | 81 | 实现轮询  82 | 83 | ● 原理   84 | 85 |   其原理在于在android端的程序中,让一个SERVICE一直跑在后台,在规定时间之内调用服务器接口进行数据获取。这里的原理很简单,当然实现起来也不难;然后,这个类之中肯定要做网络了数据请求,所以我们在Service中建立一个线程(因为在android系统中网络请求属于长时间操作,不能放主线程,不然会导致异常),在线程中和服务器进行通信。 86 | 87 |   最后,这个逻辑写完后,我们需要考虑一个问题,如何进行在规定时间内调用该服务器,当然可以用Thread+Handler(这个不是那么稳定),也可以使用AlamManager+Thread(比较稳定),因为我们需要其在后台一直运行,所以可以依靠系统的Alammanager这个类来实现,Alammanager是属于系统的一个闹钟提醒类,通过它我们能实现在规定间隔时间调用,并且也比较稳定,这个service被杀后会自己自动启动服务。 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /android/Asset目录与res目录的区别.md: -------------------------------------------------------------------------------- 1 | - res/raw和assets的相同点:  2 | 3 |   两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。 4 | 5 | - res/raw和assets的不同点: 6 | 7 |   res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。  8 | 9 |   res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹  10 | 11 | 读取文件资源:  12 | 13 |   读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作 14 | 15 | ``` 16 | InputStream is =getResources().openRawResource(R.id.filename); 17 | ``` 18 | 19 |   读取assets下的文件资源,通过以下方式获取输入流来进行写操作 20 | 21 | 22 | ``` 23 | /** 24 | * 从assets中读取图片 25 | */ 26 | private Bitmap getImageFromAssetsFile(String fileName) 27 | { 28 | Bitmap image = null; 29 | AssetManager am = getResources().getAssets(); 30 | try 31 | { 32 | InputStream is = am.open(fileName); 33 | image = BitmapFactory.decodeStream(is); 34 | is.close(); 35 | } 36 | catch (IOException e) 37 | { 38 | e.printStackTrace(); 39 | } 40 | return image; 41 | } 42 | ``` 43 | 44 | 注意1:Google的Android系统处理Assert有个bug,在AssertManager中不能处理单个超过1MB的文件,不然会报异常,raw没这个限制可以放个4MB的Mp3文件没问题。  45 | 46 | 注意2:assets 文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像 xml, java 文件被预编译,可以存放一些图片,html,js, css 等文件。 47 | 48 | -------------------------------------------------------------------------------- /android/Binder机制原理和底层实现.md: -------------------------------------------------------------------------------- 1 | 2 | [Binder机制1---Binder原理介绍](http://www.jcodecraeer.com/plus/view.php?aid=2619)  3 | 4 | [Binder机制2---Binder的数据结构以及Binder驱动](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0319/2620.html) 5 | 6 | [Binder机制3---Native层](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0319/2621.html) 7 | 8 | [Binder机制4---Framework层](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0319/2622.html) 9 | 10 | [Binder机制5--- Binder实现进程管理服务示例](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0319/2623.html) -------------------------------------------------------------------------------- /android/BroadcastReceiver使用总结.md: -------------------------------------------------------------------------------- 1 | # BroadcastReceiver的定义 2 | 3 |   广播是一种广泛运用的在应用程序之间传输信息的机制,主要用来监听系统或者应用发出的广播信息,然后根据广播信息作为相应的逻辑处理,也可以用来传输少量、频率低的数据。 4 | 5 |   在实现开机启动服务和网络状态改变、电量变化、短信和来电时通过接收系统的广播让应用程序作出相应的处理。 6 | 7 |   BroadcastReceiver 自身并不实现图形用户界面,但是当它收到某个通知后, BroadcastReceiver 可以通过启动 Service 、启动 Activity 或是 NotificationMananger 提醒用户。 8 | 9 | #BroadcastReceiver使用注意 10 | 11 |   当系统或应用发出广播时,将会扫描系统中的所有广播接收者,通过action匹配将广播发送给相应的接收者,接收者收到广播后将会产生一个广播接收者的实例,执行其中的onReceiver()这个方法;特别需要注意的是这个实例的生命周期只有10秒,如果10秒内没执行结束onReceiver(),系统将会报错。  12 |    13 |   在onReceiver()执行完毕之后,该实例将会被销毁,所以不要在onReceiver()中执行耗时操作,也不要在里面创建子线程处理业务(因为可能子线程没处理完,接收者就被回收了,那么子线程也会跟着被回收掉);正确的处理方法就是通过in调用activity或者service处理业务。 14 | 15 | #BroadcastReceiver的注册 16 | 17 |   BroadcastReceiver的注册方式有且只有两种,一种是静态注册(推荐使用),另外一种是动态注册,广播接收者在注册后就开始监听系统或者应用之间发送的广播消息。 18 | 19 | **接收短信广播示例**: 20 | 21 | 定义自己的BroadcastReceiver 类 22 | 23 | ``` 24 | public class MyBroadcastReceiver extends BroadcastReceiver { 25 | 26 | // action 名称 27 | String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ; 28 | 29 | public void onReceive(Context context, Intent intent) { 30 | 31 | if (intent.getAction().equals( SMS_RECEIVED )) { 32 | // 一个receiver可以接收多个action的,即可以有多个intent-filter,需要在onReceive里面对intent.getAction(action name)进行判断。 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | ##静态方式 39 | 40 |   在AndroidManifest.xml的application里面定义receiver并设置要接收的action。 41 | 42 | ``` 43 | < receiver android:name = ".MyBroadcastReceiver" > 44 | 45 | < intent-filter android:priority = "777" > 46 | 47 | 48 | 49 | 50 | ``` 51 | 52 |   这里的priority取值是 -1000到1000,值越大优先级越高,同时注意加上系统接收短信的限权。 53 | 54 | ``` 55 | < uses-permission android:name ="android.permission.RECEIVE_SMS" /> 56 | ``` 57 | 58 |   静态注册的广播接收者是一个常驻在系统中的全局监听器,当你在应用中配置了一个静态的BroadcastReceiver,安装了应用后而无论应用是否处于运行状态,广播接收者都是已经常驻在系统中了。同时应用里的所有receiver都在清单文件里面,方便查看。  59 |    60 |   要销毁掉静态注册的广播接收者,可以通过调用PackageManager将Receiver禁用。 61 | 62 | ##动态方式 63 | 64 |   在Activity中声明BroadcastReceiver的扩展对象,在onResume中注册,onPause中卸载. 65 |     66 | ``` 67 | public class MainActivity extends Activity { 68 | MyBroadcastReceiver receiver; 69 | @Override 70 | protected void onResume() { 71 | // 动态注册广播 (代码执行到这才会开始监听广播消息,并对广播消息作为相应的处理) 72 | receiver = new MyBroadcastReceiver(); 73 | IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED" ); 74 | registerReceiver( receiver , intentFilter); 75 | 76 | super.onResume(); 77 | } 78 | @Override 79 | protected void onPause() { 80 | // 撤销注册 (撤销注册后广播接收者将不会再监听系统的广播消息) 81 | unregisterReceiver(receiver); 82 | super.onPause(); 83 | } 84 | } 85 | ``` 86 | 87 | ##静态注册和动态注册的区别 88 | 89 |   1、静态注册的广播接收者一经安装就常驻在系统之中,不需要重新启动唤醒接收者; 90 |   动态注册的广播接收者随着应用的生命周期,由registerReceiver开始监听,由unregisterReceiver撤销监听,如果应用退出后,没有撤销已经注册的接收者应用应用将会报错。 91 | 92 |   2、当广播接收者通过intent启动一个activity或者service时,如果intent中无法匹配到相应的组件。  93 |   动态注册的广播接收者将会导致应用报错 94 |   而静态注册的广播接收者将不会有任何报错,因为自从应用安装完成后,广播接收者跟应用已经脱离了关系。  95 | 96 | #发送BroadcastReceiver 97 | 98 | 发送广播主要有两种类型: 99 | 100 | ##普通广播 101 | 102 | 应用在需要通知各个广播接收者的情况下使用,如 开机启动 103 | 104 | 使用方法:sendBroadcast() 105 | 106 | ``` 107 | Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED"); 108 | //通过intent传递少量数据 109 | intent.putExtra("data", "finch"); 110 | // 发送普通广播 111 | sendBroadcast(Intent); 112 | ``` 113 | 114 |   普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法。 115 |   同级别接收是先后是随机的;级别低的收到广播; 116 |   消息传递的效率比较高,并且无法中断广播的传播。 117 | 118 |    119 |    120 | ##有序广播 121 | 122 | 应用在需要有特定拦截的场景下使用,如黑名单短信、电话拦截。  123 | 124 | 使用方法: 125 | 126 |  sendOrderedBroadcast(intent, receiverPermission); 127 | 128 |  receiverPermission :一个接收器必须持以接收您的广播。如果为 null ,不经许可的要求(一般都为null)。 129 | 130 | ``` 131 | //发送有序广播 132 | sendOrderedBroadcast(intent, null); 133 | ``` 134 | 135 |   在有序广播中,我们可以在前一个广播接收者将处理好的数据传送给后面的广播接收者,也可以调用abortBroadcast()来终结广播的传播。 136 | 137 | ``` 138 | public void onReceive(Context arg0, Intent intent) { 139 |   //获取上一个广播的bundle数据 140 |   Bundle bundle = getResultExtras(true);//true:前一个广播没有结果时创建新的Bundle;false:不创建Bundle 141 |   bundle.putString("key", "777"); 142 |   //将bundle数据放入广播中传给下一个广播接收者 143 |   setResultExtras(bundle);  144 |    145 |   //终止广播传给下一个广播接收者 146 |   abortBroadcast(); 147 | } 148 | ``` 149 |   高级别的广播收到该广播后,可以决定把该广播是否截断掉。 150 |   同级别接收是先后是随机的,如果先接收到的把广播截断了,同级别的例外的接收者是无法收到该广播。 151 |    152 | ##异步广播 153 | 154 | 使用方法:sendStickyBroadcast() : 155 | 156 |   发出的Intent当接收Activity(动态注册)重新处于onResume状态之后就能重新接受到其Intent.(the Intent will be held to be re-broadcast to future receivers)。就是说sendStickyBroadcast发出的最后一个Intent会被保留,下次当Activity处于活跃的时候又会接受到它。 157 | 158 | 发这个广播需要权限: 159 | 160 | ``` 161 | 162 | ``` 163 | 164 | 卸载该广播: 165 | 166 | ``` 167 | removeStickyBroadcast(intent); 168 | ``` 169 |   在卸载之前该intent会保留,接收者在可接收状态都能获得。 170 | 171 | ##异步有序广播 172 | 173 |   使用方法:sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, 174 | initialCode, initialData, initialExtras): 175 | 176 |   这个方法具有有序广播的特性也有异步广播的特性; 177 |   同时需要限权: 178 | 179 | ``` 180 | 181 | ``` 182 | 183 | #总结 184 | 185 |    186 | 187 | - 静态广播接收的处理器是由PackageManagerService负责,当手机启动或者新安装了应用的时候,PackageManagerService会扫描手机中所有已安装的APP应用,将AndroidManifest.xml中有关注册广播的信息解析出来,存储至一个全局静态变量当中。 188 | 189 | - 动态广播接收的处理器是由ActivityManagerService负责,当APP的服务或者进程起来之后,执行了注册广播接收的代码逻辑,即进行加载,最后会存储在一个另外的全局静态变量中。需要注意的是: 190 | 191 |    1、 这个并非是一成不变的,当程序被杀死之后,已注册的动态广播接收器也会被移出全局变量,直到下次程序启动,再进行动态广播的注册,当然这里面的顺序也已经变更了一次。 192 | 193 |    2、这里也并没完整的进行广播的排序,只记录的注册的先后顺序,并未有结合优先级的处理。 194 | 195 | - 广播发出的时候,广播接收者接收的顺序如下: 196 | 197 |   1.当广播为**普通广播**时,有如下的接收顺序: 198 | 199 |   无视优先级 200 |   动态优先于静态 201 |   同优先级的动态广播接收器,**先注册的大于后注册的** 202 |   同优先级的静态广播接收器,**先扫描的大于后扫描的**  203 | 204 |   2.如果广播为**有序广播**,那么会将动态广播处理器和静态广播处理器合并在一起处理广播的消息,最终确定广播接收的顺序:  205 |    206 |   优先级高的先接收  207 |   同优先级的动静态广播接收器,**动态优先于静态** 208 |   同优先级的动态广播接收器,**先注册的大于后注册的** 209 |   同优先级的静态广播接收器,**先扫描的大于后扫描的**  210 | 211 | 212 | #一些常用的系统广播的action 和permission 213 | 214 | - 开机启动 215 | 216 |    217 | 218 | ``` 219 | 220 | ``` 221 | 222 | ``` 223 | 224 | ``` 225 | 226 | - 网络状态 227 | 228 | ``` 229 | 230 | ``` 231 | 232 | ``` 233 | 234 | ``` 235 | 236 |    网络是否可用的方法: 237 | 238 | ``` 239 | public static boolean isNetworkAvailable(Context context) { 240 | ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 241 | NetworkInfo[] info = mgr.getAllNetworkInfo(); 242 | if (info != null) { 243 | for (int i = 0; i < info.length; i++) { 244 | if (info[i].getState() == NetworkInfo.State.CONNECTED) { 245 | return true; 246 | } 247 | } 248 | } 249 | return false; 250 | } 251 | ``` 252 | 253 | - 电量变化 254 | 255 | ``` 256 | 257 | ``` 258 | BroadcastReceiver 的onReceive方法: 259 | 260 | ``` 261 | public void onReceive(Context context, Intent intent) { 262 | int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); //当前电量   263 | 264 | int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); //总电量 265 | int percent = currLevel * 100 / total; 266 | Log.i(TAG, "battery: " + percent + "%"); 267 | } 268 | ``` 269 | 270 | -------------------------------------------------------------------------------- /android/Fragment 全解析.md: -------------------------------------------------------------------------------- 1 | #1.概述 2 | 3 |   Fragment是Activity中用户界面的一个行为或者是一部分。主要是支持在大屏幕上动态和更为灵活的去组合或是交换UI组件,通过将activity的布局分割成若干个fragment,可以在运行时编辑activity的呈现,并且那些变化会被保存在由activity管理的后台栈里面。 4 | 5 |   **Fragment必须总是被嵌入到一个activity之中**,并且fragment的生命周期直接受其宿主activity的生命周期的影响。你可以认为fragment是activity的一个模块零件,它有自己的生命周期,接收它自己的输入事件,并且可以在activity运行时添加或者删除。 6 | 7 |   应该将每一个fragment设计为模块化的和可复用化的activity组件。也就是说,你可以在多个activity中引用同一个fragment,因为fragment定义了它自己的布局,并且使用它本身生命周期回调的行为。 8 | 9 | 10 | #2.Fragment的生命周期 11 | 12 | 先看fragment生命周期图: 13 | 14 | ![这里写图片描述](http://img.blog.csdn.net/20160429134558410) 15 | 16 |   fragment所生存的activity生命周期直接影响着fragment的生命周期,由此针对activity的每一个生命周期回调都会引发一个fragment类似的回调。例如,当activity接收到onPause()时,这个activity之中的每个fragment都会接收到onPause()。 17 |   [这有Activity的详细说明](http://blog.csdn.net/amazing7/article/details/51244219) 18 | 19 |   Fragment有一些额外的生命周期回调方法(创建和销毁fragment界面). 20 | 21 | - onAttach() 22 | 23 |   当fragment被绑定到activity时调用(Activity会被传入)。 24 | 25 | - onCreateView() 26 | 27 |   将本身的布局构建到activity中去(fragment作为activity界面的一部分) 28 |    29 | 30 | - onActivityCreated() 31 | 32 |   当activity的onCreate()函数返回时被调用。 33 | 34 | - onDestroyView() 35 | 36 |   当与fragment关联的视图体系正被移除时被调用。 37 | 38 | - onDetach() 39 | 40 |   当fragment正与activity解除关联时被调用。 41 | 42 | 当activity接收到它的onCreate()回调时,activity之中的fragment接收到onActivityCreated()回调。 43 | 44 |   一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只**有当activity处于resumed状态时**,fragment的生命周期才可以独立变化。 45 |    46 | fragment会在 activity离开恢复状态时 再一次被activity推入它的生命周期中。 47 | 48 | **管理fragment生命周期**与管理activity生命周期很相像。像activity一样,fragment也有三种状态: 49 | 50 | - Resumed 51 | 52 |   fragment在运行中的activity可见。 53 | 54 | - Paused 55 | 56 |   另一个activity处于前台且得到焦点,但是这个fragment所在的activity仍然可见(前台activity部分透明,或者没有覆盖全屏)。 57 | 58 | - Stopped 59 | 60 |   fragment不可见。要么宿主activity已经停止,要么fragment已经从activity上移除,但已被添加到后台栈中。一个停止的fragment仍然活着(所有状态和成员信息仍然由系统保留着)。但是,它对用户来讲已经不再可见,并且如果activity被杀掉,它也将被杀掉。 61 | 62 |   如果activity的进程被杀掉了,在activity被重新创建时,你需要恢复fragment状态。可以执行fragment的onSaveInstanceState()来保存状态(注意在fragment是在onCreate(),onCreateView(),或onActvityCreate()中进行恢复)。 63 | 64 |   在生命周期方面,activity与fragment之间一个**很重要的不同**,就是在各自的后台栈中是如何存储的。 65 |   当activity停止时,**默认**情况下activity被安置在由系统管理的activity后台栈中;  66 |   fragment仅当在一个事务被移除时,通过显式调用addToBackStack()请求保存的实例,该fragment才被置于由宿主activity管理的后台栈。  67 | 68 | **要创建一个fragment**,必须创建一个fragment的子类。一般情况下,我们至少需要实现以下几个fragment生命周期方法: 69 | 70 | > onCreate() 71 | 72 |   在创建fragment时系统会调用此方法。在实现代码中,你可以初始化想要在fragment中保持的那些必要组件,当fragment处于暂停或者停止状态之后可重新启用它们。 73 | 74 | >onCreateView() 75 | 76 |   在第一次为fragment绘制用户界面时系统会调用此方法。为fragment绘制用户界面,这个函数必须要返回所绘出的fragment的根View。如果fragment没有用户界面可以返回空。 77 | 78 |    79 | ``` 80 | @Override 81 | public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {  82 | 83 | // Inflate the layout for this fragment 84 | return inflater.inflate(R.layout.example_fragment, container, false); 85 | } 86 | ``` 87 | inflate()函数需要以下三个参数: 88 | 89 | ①要inflate的布局的资源ID。  90 | 91 | ②被inflate的布局的父ViewGroup。 92 | 93 | ③一个布尔值,表明在inflate期间被infalte的布局是否应该附上ViewGroup(第二个参数container)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到容器中(container)——传入true会在最终的布局里创建一个多余的ViewGroup。)  94 | 95 | > onPause() 96 | 97 |   系统回调用该函数作为用户离开fragment的第一个预兆(尽管这并不总意味着fragment被销毁)。在当前用户会话结束之前,通常要在这里提交任何应该持久化的变化(因为用户可能不再返回)。 98 | 99 | 100 | #3.将fragment添加到activity之中 101 | 102 |   可以通过在activity布局文件中声明fragment,用fragment标签把fragment插入到activity的布局中,或者是用应用程序源码将它添加到一个存在的ViewGroup中。  103 |    104 |   但fragment并不是一个定要作为activity布局的一部分,fragment也可以为activity隐身工作。 105 | 106 | 107 | 108 | ##3.1在activity的布局文件里声明fragment 109 | 110 |   可以像为view一样为fragment指定布局属性。例如: 111 | 112 | ``` 113 | 114 |   118 | 119 | 123 | 124 | 125 | ``` 126 |   fragment标签中的android:name 属性指定了布局中实例化的Fragment类。 127 | 128 |   当系统创建activity布局时,它实例化了布局文件中指定的每一个fragment,并为它们调用onCreateView()函数,以获取每一个fragment的布局。系统直接在元素的位置插入fragment返回的View。 129 | 130 |   注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且可用来捕捉fragment的事务处理,例如移除)。为fragment提供ID有三种方法: 131 | 132 | - 133 | 134 | 用android:id属性提供一个唯一的标识。  135 | 136 | - 用android:tag属性提供一个唯一的字符串。  137 | 138 | - 如果上述两个属性都没有,系统会使用其容器视图(view)的ID。  139 | 140 | ##3.2通过编码将fragment添加到已存在的ViewGroup中 141 | 142 |   在activity运行的任何时候,你都可以将fragment添加到activity布局中。 143 |    144 |   要管理activity中的fragment,可以使用FragmentManager。可以通过在activity中调用getFragmentManager()获得。使用FragmentManager 可以做如下事情,包括: 145 | 146 | - 使用findFragmentById()(用于在activity布局中提供有界面的fragment)或者findFragmentByTag()获取activity中存在的fragment(用于有界面或者没有界面的fragment)。   147 | 148 | - 使用popBackStack()(模仿用户的BACK命令)从后台栈弹出fragment。   149 | 150 | 151 | - 使用addOnBackStackChangedListener()注册一个监听后台栈变化的监听器。 152 | 153 | 在Android中,对Fragment的事务操作都是通过FragmentTransaction来执行。操作大致可以分为两类: 154 | 155 | - 显示:add() replace() show() attach()   156 | 157 | - 隐藏:remove() hide() detach()  158 | 159 | > 说明: 160 |   调用show() & hide()方法时,Fragment的生命周期方法并不会被执行,仅仅是Fragment的View被显示或者​隐藏。 161 | 162 | >  执行replace()时(至少两个Fragment),会执行第二个Fragment的onAttach()方法、执行第一个Fragment的onPause()-onDetach()方法,同时containerView会detach第一个Fragment的View。 163 | 164 | >  add()方法执行onAttach()-onResume()的生命周期,相对的remove()就是执行完成剩下的onPause()-onDetach()周期。 165 | 166 | 167 | 可以像下面这样从Activity中取得FragmentTransaction的实例: 168 | 169 | ``` 170 | FragmentManager fragmentManager = getFragmentManager()  171 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 172 | ``` 173 | 174 | 可以用add()函数添加fragment,并指定要添加的fragment以及要将其插入到哪个视图(view)之中(注意commit事务): 175 | 176 | ``` 177 | ExampleFragment fragment = new ExampleFragment(); 178 | fragmentTransaction.add(R.id.fragment_container, fragment); 179 | fragmentTransaction.commit(); 180 | ``` 181 | 182 | ##3.3添加没有界面的fragment 183 | 184 |   也可以使用fragment为activity提供后台动作,却不呈现多余的用户界面。 185 | 186 |   想要添加没有界面的fragment ,可以使用add(Fragment, String)(为fragment提供一个唯一的字符串“tag”,而不是视图(view)ID)。这样添加了fragment,但是,因为还没有关联到activity布局中的视图(view) ,收不到onCreateView()的调用。所以不需要实现这个方法。  187 |    188 |   对于无界面fragment,字符串标签是**唯一识别**它的方法。如果之后想从activity中取到fragment,需要使用findFragmentByTag()。  189 | 190 | 191 | #4.fragment事务后台栈 192 | 193 |   在调用commit()之前,可以将事务添加到fragment事务后台栈中(通过调用addToBackStatck())。这个后台栈由activity管理,并且允许用户通过按BACK键回退到前一个fragment状态。 194 | 195 |   下面的代码中一个fragment代替另一个fragment,并且将之前的fragment状态保留在后台栈中: 196 | 197 | ``` 198 | Fragment newFragment = new ExampleFragment(); 199 | FragmentTransaction transaction = getFragmentManager().beginTransaction(); 200 | 201 | transaction.replace(R.id.fragment_container, newFragment); 202 | transaction.addToBackStack(null); 203 | 204 | transaction.commit(); 205 | ``` 206 | 207 | > 注意: 208 | > 209 | >   如果添加多个变更事务(例如另一个add()或者remove())并调用addToBackStack(),那么在调用commit()之前的所有应用的变更被作为一个单独的事务添加到后台栈中,并且BACK键可以将它们一起回退。 210 | > 211 | >   当移除一个fragment时,如果调用了addToBackStack(),那么之后fragment会被停止,如果用户回退,它将被恢复过来。 212 | > 213 | >  调用commit()并不立刻执行事务,相反,而是采取预约方式,一旦activity的界面线程(主线程)准备好便可运行起来。然而,如果有必要的话,你可以从界面线程调用executePendingTransations()立即执行由commit()提交的事务。 214 | > 215 | >   只能在activity保存状态(当用户离开activity时)之前用commit()提交事务。如果你尝试在那时之后提交,会抛出一个异常。这是因为如果activity需要被恢复,提交后的状态会被丢失。对于这类丢失提交的情况,可使用commitAllowingStateLoss() 216 | 217 | 218 | #5.与Activity交互 219 | 220 |    221 | 222 | - Activity中已经有了该Fragment的引用,直接通过该引用进行交互。 223 | 224 | -如果没引用可以通过调用fragment的函数findFragmentById()或者findFragmentByTag(),从FragmentManager中获取Fragment的索引,例如: 225 | 226 |   227 | 228 | ``` 229 | ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); 230 | ``` 231 | 232 | - 在Fragment中可以通过getActivity得到当前绑定的Activity的实例。 233 | 234 | 235 | 236 | - 创建activity事件回调函数,在fragment内部定义一个回调接口,宿主activity来实现它。 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /android/IntentService使用详解和实例介绍.md: -------------------------------------------------------------------------------- 1 | #IntentService定义 2 | 3 |   IntentService继承与Service,用来处理异步请求。客户端可以通过startService(Intent)方法传递请求给IntentService。IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来依次处理所有Intent请求对象所对应的任务。  4 |    5 |   这样以免事务处理阻塞主线程(ANR)。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则**自动停止**Service;否则执行下一个Intent请求所对应的任务。  6 |    7 |   IntentService在处理事务时,还是采用的Handler方式,创建一个名叫ServiceHandler的内部Handler,并把它直接绑定到HandlerThread所对应的子线程。 ServiceHandler把处理一个intent所对应的事务都封装到叫做**onHandleIntent**的虚函数;因此我们直接实现虚函数onHandleIntent,再在里面根据Intent的不同进行不同的事务处理就可以了。 8 | 另外,IntentService默认实现了Onbind()方法,返回值为null。 9 | 10 | 使用IntentService需要实现的两个方法: 11 |    12 | 13 | - 构造函数  14 | 15 |   IntentService的构造函数一定是**参数为空**的构造函数,然后再在其中调用super("name")这种形式的构造函数。因为Service的实例化是系统来完成的,而且系统是用参数为空的构造函数来实例化Service的 16 | 17 | - 实现虚函数onHandleIntent 18 | 19 |   在里面根据Intent的不同进行不同的事务处理。  20 |    21 | 好处:处理异步请求的时候可以减少写代码的工作量,比较轻松地实现项目的需求。 22 | 23 | #IntentService与Service的区别 24 | 25 |   Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。 26 | 27 |   IntentService 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents(把intent插入到工作队列中)。通过工作队列把intent逐个发送给onHandleIntent()。  28 |    29 |   不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。 30 | 31 |    默认实现的onBind()返回null。 32 | 33 | #IntentService实例介绍 34 | 35 |   首先是myIntentService.java 36 | 37 | ``` 38 | public class myIntentService extends IntentService { 39 | 40 | //------------------必须实现----------------------------- 41 | 42 | public myIntentService() { 43 | super("myIntentService"); 44 | // 注意构造函数参数为空,这个字符串就是worker thread的名字 45 | } 46 | 47 | @Override 48 | protected void onHandleIntent(Intent intent) { 49 | //根据Intent的不同进行不同的事务处理 50 | String taskName = intent.getExtras().getString("taskName"); 51 | switch (taskName) { 52 | case "task1": 53 | Log.i("myIntentService", "do task1"); 54 | break; 55 | case "task2": 56 | Log.i("myIntentService", "do task2"); 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | //--------------------用于打印生命周期-------------------- 63 | @Override 64 | public void onCreate() { 65 | Log.i("myIntentService", "onCreate"); 66 | super.onCreate(); 67 | } 68 | 69 | @Override 70 | public int onStartCommand(Intent intent, int flags, int startId) { 71 | Log.i("myIntentService", "onStartCommand"); 72 | return super.onStartCommand(intent, flags, startId); 73 | } 74 | 75 | @Override 76 | public void onDestroy() { 77 | Log.i("myIntentService", "onDestroy"); 78 | super.onDestroy(); 79 | } 80 | } 81 | 82 | ``` 83 | 然后记得在Manifest.xml中注册服务 84 | 85 | ``` 86 | 87 | 88 | 89 | 90 | 91 | ``` 92 | 93 | 最后在Activity中开启服务 94 | 95 | ``` 96 | public class MainActivity extends Activity { 97 | @Override 98 | protected void onCreate(Bundle savedInstanceState) { 99 | // TODO Auto-generated method stub 100 | super.onCreate(savedInstanceState); 101 | 102 | //同一服务只会开启一个worker thread,在onHandleIntent函数里依次处理intent请求。 103 | 104 | Intent i = new Intent("cn.scu.finch"); 105 | Bundle bundle = new Bundle(); 106 | bundle.putString("taskName", "task1"); 107 | i.putExtras(bundle); 108 | startService(i); 109 | 110 | Intent i2 = new Intent("cn.scu.finch"); 111 | Bundle bundle2 = new Bundle(); 112 | bundle2.putString("taskName", "task2"); 113 | i2.putExtras(bundle2); 114 | startService(i2); 115 | 116 | startService(i); //多次启动 117 | } 118 | } 119 | ``` 120 | 121 | 运行结果: 122 | 123 | ![这里写图片描述](http://img.blog.csdn.net/20160513135411037)  124 | 125 |   IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来依次处理所有Intent请求对象所对应的任务。  126 |    127 |   通过onStartCommand()传递给服务intent被**依次**插入到工作队列中。工作队列又把intent逐个发送给onHandleIntent()。 128 | 129 |    130 | 131 | > 注意: 132 | > 它只有一个工作线程,名字就是构造函数的那个字符串,也就是“myIntentService”,我们知道多次开启service,只会调用一次onCreate方法(创建一个工作线程),多次onStartCommand方法(用于传入intent通过工作队列再发给onHandleIntent函数做处理)。 -------------------------------------------------------------------------------- /android/Json优劣势.md: -------------------------------------------------------------------------------- 1 | **JSON的定义** 2 | 3 |   一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为。 4 | 5 | **XML的定义**  6 | 7 |    扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。 8 | 9 | **JSON 和 XML 优缺点的比较**  10 | 11 | 1.在可读性方面,JSON和XML的数据可读性基本相同。JSON和XML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,很难分出胜负。  12 | 13 | 2.在可扩展性方面,XML天生有很好的扩展性,JSON当然也有,没有什么是XML能扩展,JSON不能的。  14 | 15 | 3.在编码难度方面,XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。  16 | 17 | 4.在解码难度方面,XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。  18 | 19 | 5.在流行度方面,XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON。到时Ajax应该变成Ajaj(Asynchronous JavaScript and JSON)了。  20 | 21 | 6.JSON和XML同样拥有丰富的解析手段。  22 | 23 | 7.JSON相对于XML来讲,数据的体积小。 24 | 25 | 8.JSON与JavaScript的交互更加方便。 26 | 27 | 9.JSON对数据的描述性比XML较差。 28 | 29 | 10.JSON的速度要远远快于XML。 30 | -------------------------------------------------------------------------------- /android/ListView优化.md: -------------------------------------------------------------------------------- 1 | 2 |   重用了convertView,很大程度上的减少了内存的消耗。通过判断convertView是否为null,是的话就需要产生一个视图出来,然后给这个视图数据,最后将这个视图返回给底层,呈献给用户。 3 | 4 |    每次在getVIew的时候,都需要重新的findViewById,重新找到控件,然后进行控件的赋值以及事件相应设置。这样其实在做重复的事情,因为的geiview中,其实包含有这些控件,而且这些控件的id还都是一样的,也就是其实只要在view中findViewById一次,后面无需要每次都要findViewById了。 5 | 6 |   通过线程来异步加载图片,把Http的相关操作放在线程里,最好使用线程池来控制线程数。返回的bitmap通过Handler来更新每个Item布局上的ImageView(就是赋上图片)。 7 | 8 |   当遇到大图片的话,可以对图片处理一下再使用,比如压缩,压缩到480*800。网上有很多关于图片压缩的资料。 9 | -------------------------------------------------------------------------------- /android/Service全面总结.md: -------------------------------------------------------------------------------- 1 | #什么是服务?   2 | 3 |   Service是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其它应用程序的组件启动,即使用户切换到另外的应用时还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,甚至能进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的。 4 | 5 | 6 | #Service 与 Thread 的区别 7 | 8 | 9 |   服务仅仅是一个组件,即使用户不再与你的应用程序发生交互,它仍然能在后台运行。因此,应该只在需要时才创建一个服务。 10 | 11 |   如果你需要在主线程之外执行一些工作,但仅当用户与你的应用程序交互时才会用到,那你应该创建一个新的线程而不是创建服务。 比如,如果你需要播放一些音乐,但只是当你的activity在运行时才需要播放,你可以在onCreate()中创建一个线程,在onStart()中开始运行,然后在onStop()中终止运行。还可以考虑使用AsyncTask或HandlerThread来取代传统的Thread类。 12 | 13 |   **由于无法在不同的 Activity 中对同一 Thread 进行控制**,这个时候就要考虑用服务实现。如果你使用了服务,它默认就运行于应用程序的主线程中。因此,如果服务执行密集计算或者阻塞操作,你仍然应该在服务中创建一个新的线程来完成(避免ANR)。 14 | 15 | #服务的分类 16 | 17 | ##按运行分类 18 | 19 |    20 | 21 | - 前台服务 22 | 23 |   前台服务是指那些经常会被用户关注的服务,因此内存过低时它不会成为被杀的对象。 前台服务必须提供一个状态栏通知,并会置于“正在进行的”(“Ongoing”)组之下。这意味着只有在服务被终止或从前台移除之后,此通知才能被解除。 24 |   例如,用服务来播放音乐的播放器就应该运行在前台,因为用户会清楚地知晓它的运行情况。 状态栏通知可能会标明当前播放的歌曲,并允许用户启动一个activity来与播放器进行交互。 25 | 26 |   要把你的服务请求为前台运行,可以调用startForeground()方法。此方法有两个参数:唯一标识通知的整数值、状态栏通知Notification对象。例如: 27 | 28 |   29 | 30 | ``` 31 | Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),System.currentTimeMillis()); 32 | 33 | Intent notificationIntent = new Intent(this,ExampleActivity.class); 34 | 35 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); 36 | 37 | notification.setLatestEventInfo(this, getText(R.string.notification_title), 38 | getText(R.string.notification_message), pendingIntent); 39 | 40 | startForeground(ONGOING_NOTIFICATION, notification); 41 | ``` 42 | 43 |   要从前台移除服务,请调用stopForeground()方法,这个方法接受个布尔参数,表示是否同时移除状态栏通知。此方法不会终止服务。不过,如果服务在前台运行时被你终止了,那么通知也会同时被移除。 44 | 45 | - 后台服务 46 | 47 | ##按使用分类   48 | 49 | - 本地服务 50 | 51 |   用于应用程序内部,实现一些耗时任务,并不占用应用程序比如Activity所属线程,而是单开线程后台执行。 52 |   调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。 53 | 54 | - 远程服务 55 | 56 |   用于Android系统内部的应用程序之间,可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。 57 | 58 | #Service生命周期 59 | 60 |   ![这里写图片描述](http://img.blog.csdn.net/20160503165018575) 61 | 62 | Service生命周期方法: 63 | ``` 64 | public class ExampleService extends Service { 65 | int mStartMode; // 标识服务被杀死后的处理方式 66 | IBinder mBinder; // 用于客户端绑定的接口 67 | boolean mAllowRebind; // 标识是否使用onRebind 68 | 69 | @Override 70 | public void onCreate() { 71 | // 服务正被创建 72 | } 73 | @Override 74 | public int onStartCommand(Intent intent, int flags, int startId) { 75 | // 服务正在启动,由startService()调用引发 76 | return mStartMode; 77 | } 78 | @Override 79 | public IBinder onBind(Intent intent) { 80 | // 客户端用bindService()绑定服务 81 | return mBinder; 82 | } 83 | @Override 84 | public boolean onUnbind(Intent intent) { 85 | // 所有的客户端都用unbindService()解除了绑定 86 | return mAllowRebind; 87 | } 88 | @Override 89 | public void onRebind(Intent intent) { 90 | // 某客户端正用bindService()绑定到服务, 91 | // 而onUnbind()已经被调用过了 92 | } 93 | @Override 94 | public void onDestroy() { 95 | // 服务用不上了,将被销毁 96 | } 97 | } 98 | ``` 99 | 100 | > 请注意onStartCommand()方法必须返回一个整数。这个整数是描述系统在杀死服务之后应该如何继续运行。onStartCommand()的返回值必须是以下常量之一: 101 | 102 | >START_NOT_STICKY   103 | 如果系统在onStartCommand()返回后杀死了服务,则不会重建服务了,除非还存在未发送的intent。 当服务不再是必需的,并且应用程序能够简单地重启那些未完成的工作时,这是避免服务运行的最安全的选项。  104 |   105 | >START_STICKY 106 | 如果系统在onStartCommand()返回后杀死了服务,则将重建服务并调用onStartCommand(),但不会再次送入上一个intent, 而是用null intent来调用onStartCommand() 。除非还有启动服务的intent未发送完,那么这些剩下的intent会继续发送。 这适用于媒体播放器(或类似服务),它们不执行命令,但需要一直运行并随时待命。  107 | 108 | >START_REDELIVER_INTENT 109 | 如果系统在onStartCommand()返回后杀死了服务,则将重建服务并用上一个已送过的intent调用onStartCommand()。任何未发送完的intent也都会依次送入。这适用于那些需要立即恢复工作的活跃服务,比如下载文件。 110 | 111 |   服务的生命周期与activity的非常类似。不过,更重要的是你需密切关注服务的创建和销毁环节,因为后台运行的服务是不会引起用户注意的。 112 | 113 |   服务的生命周期——从创建到销毁——可以有两种路径: 114 | 115 | - 一个started服务 116 | 117 |   这类服务由其它组件调用startService()来创建。然后保持运行,且必须通过调用stopSelf()自行终止。其它组件也可通过调用stopService() 终止这类服务。服务终止后,系统会把它销毁。 118 | 119 |   如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。 120 | 121 |    122 | 123 | -  一个bound服务 124 | 125 |   服务由其它组件(客户端)调用bindService()来创建。然后客户端通过一个IBinder接口与服务进行通信。客户端可以通过调用unbindService()来关闭联接。多个客户端可以绑定到同一个服务上,当所有的客户端都解除绑定后,系统会销毁服务。(服务不需要自行终止。) 126 | 127 |   如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。 128 | 129 | ![这里写图片描述](http://img.blog.csdn.net/20160503165622881)  130 | 131 |   这两条路径并不是完全隔离的。也就是说,你可以绑定到一个已经用startService()启动的服务上。例如,一个后台音乐服务可以通过调用startService()来启动,传入一个指明所需播放音乐的 Intent。 之后,用户也许需要用播放器进行一些控制,或者需要查看当前歌曲的信息,这时一个activity可以通过调用bindService()与此服务绑定。在类似这种情况下,stopService()或stopSelf()不会真的终止服务,除非所有的客户端都解除了绑定。 132 | 133 | >   当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了)。 134 | 135 | #在manifest中声明服务 136 | 137 |   无论是什么类型的服务都必须在manifest中申明,格式如下: 138 | 139 | ``` 140 | 141 | ... 142 | 143 | 144 | ... 145 | 146 | 147 | ``` 148 | 149 | Service 元素的属性有: 150 | 151 | > android:name  -------------  服务类名 152 | 153 | >android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名 154 | 155 | >android:icon  --------------  服务的图标 156 | 157 | >android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务 158 | 159 | >android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字 160 | 161 | >android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false 162 | 163 | >android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false  164 | 165 |   android:name是唯一必需的属性——它定义了服务的类名。与activity一样,服务可以定义intent过滤器,使得其它组件能用隐式intent来调用服务。如果你想让服务只能内部使用(其它应用程序无法调用),那么就不必(也不应该)提供任何intent过滤器。 166 |   此外,如果包含了android:exported属性并且设置为"false", 就可以确保该服务是你应用程序的私有服务。即使服务提供了intent过滤器,本属性依然生效。  167 | 168 | #startService 启动服务 169 | 170 |   从activity或其它应用程序组件中可以启动一个服务,调用startService()并传入一个Intent(指定所需启动的服务)即可。 171 | 172 | ``` 173 | Intent intent = new Intent(this, MyService.class); 174 | startService(intent); 175 | ``` 176 | 177 | 服务类: 178 | 179 | ``` 180 | public class MyService extends Service { 181 | 182 | /** 183 | * onBind 是 Service 的虚方法,因此我们不得不实现它。 184 | * 返回 null,表示客服端不能建立到此服务的连接。 185 | */ 186 | @Override 187 | public IBinder onBind(Intent intent) { 188 | // TODO Auto-generated method stub 189 | return null; 190 | } 191 | 192 | @Override 193 | public void onCreate() { 194 | super.onCreate(); 195 | } 196 | 197 | @Override 198 | public int onStartCommand(Intent intent, int flags, int startId)    { 199 | //接受传递过来的intent的数据 200 | return START_STICKY; 201 | }; 202 | 203 | @Override 204 | public void onDestroy() { 205 | super.onDestroy(); 206 | } 207 | 208 | } 209 | ``` 210 | 211 |   一个started服务必须自行管理生命周期。也就是说,系统不会终止或销毁这类服务,除非必须恢复系统内存并且服务返回后一直维持运行。 因此,服务必须通过调用stopSelf()自行终止,或者其它组件可通过调用stopService()来终止它。 212 | 213 | #bindService 启动服务   214 | 215 |   当应用程序中的activity或其它组件需要与服务进行交互,或者应用程序的某些功能需要暴露给其它应用程序时,你应该创建一个bound服务,并通过进程间通信(IPC)来完成。 216 | 217 | 方法如下: 218 | 219 | ``` 220 | Intent intent=new Intent(this,BindService.class); 221 | bindService(intent, ServiceConnection conn, int flags) 222 | 223 | ``` 224 | 225 | > 注意bindService是Context中的方法,当没有Context时传入即可。 226 | 227 | 在进行服务绑定的时,其flags有: 228 | 229 | - Context.BIND_AUTO_CREATE 230 | 231 |   表示收到绑定请求的时候,如果服务尚未创建,则即刻创建,在系统内存不足需要先摧毁优先级组件来释放内存,且只有驻留该服务的进程成为被摧毁对象时,服务才被摧毁  232 | 233 | - Context.BIND_DEBUG_UNBIND   234 | 235 |   通常用于调试场景中判断绑定的服务是否正确,但容易引起内存泄漏,因此非调试目的的时候不建议使用 236 | 237 | - Context.BIND_NOT_FOREGROUND   238 | 239 |   表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行。 240 | 241 | 服务类: 242 | 243 | ``` 244 | public class BindService extends Service { 245 | 246 | // 实例化MyBinder得到mybinder对象; 247 | private final MyBinder binder = new MyBinder(); 248 | 249 | /** 250 | * 返回Binder对象。 251 | */ 252 | @Override 253 | public IBinder onBind(Intent intent) { 254 | // TODO Auto-generated method stub 255 | return binder; 256 | } 257 | 258 | /** 259 | * 新建内部类MyBinder,继承自Binder(Binder实现IBinder接口), 260 | * MyBinder提供方法返回BindService实例。 261 | */ 262 |   public class MyBinder extends Binder{ 263 | 264 | public BindService getService(){ 265 | return BindService.this; 266 | } 267 | } 268 | 269 | 270 | @Override 271 | public boolean onUnbind(Intent intent) { 272 | // TODO Auto-generated method stub 273 | return super.onUnbind(intent); 274 | } 275 | } 276 | ``` 277 | 278 | 启动服务的activity代码: 279 | 280 | ``` 281 | public class MainActivity extends Activity { 282 | 283 | /** 是否绑定 */ 284 | boolean mIsBound = false; 285 | BindService mBoundService; 286 | @Override 287 | protected void onCreate(Bundle savedInstanceState) { 288 | super.onCreate(savedInstanceState); 289 | setContentView(R.layout.activity_main); 290 | doBindService(); 291 | } 292 | /** 293 | * 实例化ServiceConnection接口的实现类,用于监听服务的状态 294 | */ 295 | private ServiceConnection conn = new ServiceConnection() { 296 | 297 | @Override 298 | public void onServiceConnected(ComponentName name, IBinder service) { 299 | BindService mBoundService = ((BindService.MyBinder) service).getService(); 300 | 301 | } 302 | 303 | @Override 304 | public void onServiceDisconnected(ComponentName name) { 305 | mBoundService = null; 306 | 307 | } 308 | }; 309 | 310 | /** 绑定服务 */ 311 | public void doBindService() { 312 | bindService(new Intent(MainActivity.this, BindService.class), conn,Context.BIND_AUTO_CREATE); 313 | mIsBound = true; 314 | } 315 | 316 | /** 解除绑定服务 */ 317 | public void doUnbindService() { 318 | if (mIsBound) { 319 | // Detach our existing connection. 320 | unbindService(conn); 321 | mIsBound = false; 322 | } 323 | } 324 | 325 | @Override 326 | protected void onDestroy() { 327 | // TODO Auto-generated method stub 328 | super.onDestroy(); 329 | 330 | doUnbindService(); 331 | } 332 | } 333 | ``` 334 | 335 | > 注意在AndroidMainfest.xml中对Service进行显式声明 336 | 337 | 338 | 判断Service是否正在运行: 339 | 340 | ``` 341 | private boolean isServiceRunning() { 342 | ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 343 | 344 |  { 345 | if ("com.example.demo.BindService".equals(service.service.getClassName()))   { 346 | return true; 347 | } 348 | } 349 | return false; 350 | } 351 | ``` -------------------------------------------------------------------------------- /android/activity切换动画.md: -------------------------------------------------------------------------------- 1 | **android中设置activity切换动画有 两种实现方式** 2 | 3 | 1 . 在 AndroidManifest.xml 文件activity标签中,通过设置 android:theme 主题属性来自定义我们 Activity的切换动画( 主题可以定义很多属性,动画只是其中之一)。 4 | 5 | 6 | ``` 7 | 10 | 11 | ``` 12 | 13 | 首先要定义自己的主题,打开res/values/styles.xml文件,加入 14 | 15 | ``` 16 | 19 | ``` 20 | 然后继续定义具体切换动画样式,分为4种需要动画的情况。 21 | 22 | 打开新界面时 : 23 | 24 | 25 | - 新界面出现(android:activityOpenEnterAnimation) 26 | - 当前界面消失(android:activityOpenExitAnimation) 27 | 28 | 返回上一界面时: 29 | 30 | - 当前界面消失(android:activityCloseExitAnimation) 31 | - 上一界面出现(android:activityCloseEnterAnimation) 32 | 33 | > a、b、c、d为自定义在res/anim下的具体动画效果文件(下面贴出) 34 | 35 | ``` 36 | 42 | ``` 43 | 44 | 2 . 在Android的2.0版本之后,加入了overridePendingTransition()这个函数来帮我们实现activity切换动画,它**在startActivity()或者finish()函数之后调用** 45 | 46 | ![这里写图片描述](http://img.blog.csdn.net/20160408111715900) 47 | 48 | 它有两个参数, 49 | enterAnim是下一界面进入效果的xml文件的id, 50 | exitAnim是当前界面退出效果的xml文件id。 51 | 52 | > 我们可以看出它也实现了第一种方式的四种动画情况。 53 | 54 | 注意: 55 | 56 | - 当我们不想要动画时,设置为 0 即可。 57 | - 我们可以看出这个方法是定义在android.app.Activity下的,如果我们用自定义的view或fragment等嵌入到Activity中,调用这个函数时很可能不起作用。一般改写为this.getParent().overridePendingTransition 就可以解决。 58 | 59 | 3 .上 res/anim 下的各种动画文件 60 | 61 | zoomin.xml: 62 | 63 | ``` 64 | 65 | 67 | 68 | 75 | 76 | 80 | 81 | ``` 82 | zoomout.xml 83 | 84 | ``` 85 | 86 | 89 | 90 | 97 | 98 | 101 | 102 | ``` 103 | out_from_right.xml 104 | 105 | ``` 106 | 107 | 108 | 114 | 115 | 116 | ``` 117 | 118 | in_from_left.xml 119 | 120 | ``` 121 | 122 | 123 | 129 | 130 | 131 | ``` 132 | out_from_left.xml 133 | 134 | ``` 135 | 136 | 137 | 143 | 144 | 145 | ``` 146 | 147 | in_from_right.xml 148 | 149 | ``` 150 | 151 | 157 | 158 | 159 | ``` 160 | fade.xml 161 | 162 | ``` 163 | 164 | 165 | 169 | 170 | ``` 171 | 172 | hold.xml 173 | 174 | ``` 175 | 176 | 177 | 181 | 182 | ``` 183 | hyperspace_in.xml 184 | 185 | ``` 186 | 187 | 188 | 193 | 194 | ``` 195 | 196 | hyperspace_out.xml 197 | 198 | ``` 199 | 200 | 201 | 202 | 204 | 211 | 212 | 214 | 220 | 225 | 226 | 227 | 228 | 229 | ``` 230 | alpha_action.xml 231 | 232 | ``` 233 | 234 | 235 | 238 | 245 | 246 | ``` 247 | scale_action.xml 248 | 249 | ``` 250 | 251 | 252 | 261 | 262 | ``` 263 | 264 | scale_rotate.xml 265 | 266 | ``` 267 | 268 | 269 | 271 | 281 | 282 | 288 | 289 | 290 | ``` 291 | 292 | scale_translate.xml 293 | 294 | ``` 295 | 296 | 297 | 299 | 309 | 310 | 315 | 316 | 317 | ``` 318 | scale_translate_rotate.xml 319 | 320 | ``` 321 | 322 | 323 | 325 | 333 | 334 | 340 | 341 | 347 | 348 | 349 | ``` 350 | 351 | slide_down_out.xml 352 | 353 | ``` 354 | 355 | 357 | 360 | 361 | ``` 362 | 363 | wave_scale.xml 364 | 365 | ``` 366 | 367 | 368 | 370 | 373 | 374 | 381 | 382 | 390 | 391 | 392 | ``` -------------------------------------------------------------------------------- /android/android中图片缓存.md: -------------------------------------------------------------------------------- 1 | 1.简介   2 | 3 |   现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。 4 | 5 |   现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。 6 | 7 | 2.图片缓存的原理   8 | 9 |   实现图片缓存也不难,需要有相应的cache策略。一般采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。 10 | 11 | 关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。 -------------------------------------------------------------------------------- /android/两类动画.md: -------------------------------------------------------------------------------- 1 | android支持两种动画模式,tween animation,frame animation  2 | 3 |   View Animation(Tween Animation):补间动画,给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。 4 |   View animation只能应用于View对象,而且只支持一部分属性,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化. 5 | 6 |   Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影补间动画和帧动画。 7 | 补间动画和Frame动画的定义: 8 |   所谓补间动画,是指通过指定View的初末状态和变化时间、方式,对View的内容完成一系列的图形变换来实现动画效果。主要包括四种效果:Alpha、Scale、Translate和Rotate。 9 | 帧动画就是Frame动画,即指定每一帧的内容和停留时间,然后播放动画 -------------------------------------------------------------------------------- /android/五大布局易混淆知识.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- 3 | ● 线性布局(LiearLayout): 屏幕垂直或水平方向布局。 4 | 5 |    具有垂直方向与水平方向的布局方式,通过设置属性“android:orientation”控制方向,属性值垂直(vertical)和水平(horizontal),默认水平方向。android:layout_weight:权重,用来分配当前控件在剩余空间的大小。正常情况下,值越大占据高度或宽度越大。例外的情况,在LineayLayout布局中使用这个属性时需要注意: 当水平方向布局且子控件的宽度为fill_parent或match_parent时,值越小占据宽度越大,垂直方向也一样。 6 |       7 | ![这里写图片描述](http://img.blog.csdn.net/20160623095128454) 8 | 9 | 10 |     这里出现这种情况主要是因为match_parent或fill_parent引起的,系统先给第一个子控件分配parent_width(剩余空间),再给第二个分配parent_width,即分配了两个parent_width,此时剩余为parent_width-2parent_width=-parent_width,这里主要问题就在这里,剩余控件其实已经为一个负数了。接着,第一个控件占宽度:parent_width(当前已有宽度)+权重1/3*(-parent_width)=2/3parent_width;第二个控件占宽度:parent_width+权重2/3*(-parent_width)=1/3parent_width,所以当宽度都是match_parent时,剩余空间则为负数,谁的权重大谁就会减去越多。 11 |    12 | -------------------------------------------------------------------------------- 13 | 14 | ● 帧布局(FrameLayout):控件从屏幕左上角开始布局。  15 |     16 |     帧布局或叫层布局,从屏幕左上角按照层次堆叠方式布局,后面的控件覆盖前面的控件。比如百度地图。 17 |      18 | 19 |     20 | ● 相对布局(RelativeLayout): 以其他控件为参照布局。 21 | 22 | 相对布局可以让子控件相对于兄弟控件或父控件进行布局,可以设置子控件相对于兄弟控件或父控件进行上下左右对齐。RelativeLayout能替换一些嵌套视图,当我们用LinearLayout来实现一个简单的布局但又使用了过多的嵌套时,就可以考虑使用RelativeLayout重新布局。 23 | 24 | ● 绝对布局(AbsoluteLayout):以屏幕坐标布局。 25 | 26 |   绝对布局也叫坐标布局,指定控件的绝对位置,简单直接,直观性强,但是手机屏幕尺寸差别较大,适应性差,Android 1.5已弃用,可以用RelativeLayout替代。 27 | 28 | ● 表格布局(TableLayout):按照行列方式布局。 29 | 30 |   表格布局继承自LinearLayout,通过TableRow设置行,列数由TableRow中的子控件决定,直接在TableLayout中添加子控件会占据整个一行。 31 | 32 | -------------------------------------------------------------------------------- 33 | 性能优化: 34 | 35 | 使用抽象布局标签(include, viewstub, merge) 36 | 去除不必要的嵌套和View节点 37 | 在LinearLayout中慎用layout_weight(绘制2次); 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /android/保证service不被杀死.md: -------------------------------------------------------------------------------- 1 | 一、onStartCommand方法,返回START_STICKY 2 | 3 |   在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,因为保留在开始状态,在创建 service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。 4 | 5 |    【结论】 手动返回START_STICKY,亲测当service因内存不足被kill,当内存又有的时候,service又被重新创建,比较不错,但是不能保证任何情况下都被重建,比如进程被干掉了....  6 | 7 | 二、提升service优先级 8 | 9 |   在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。 10 | 11 | 三、提升service进程优先级 12 | 13 |   Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是: 14 | 1.前台进程( FOREGROUND_APP) 15 | 2.可视进程(VISIBLE_APP ) 16 | 3. 次要服务进程(SECONDARY_SERVER ) 17 | 4.后台进程 (HIDDEN_APP) 18 | 5.内容供应节点(CONTENT_PROVIDER) 19 | 6.空进程(EMPTY_APP) 20 | 当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。 21 | 22 | 在onStartCommand方法内添加如下代码: 23 | 24 | 25 | 26 | ``` 27 | Notification notification = new Notification(R.drawable.ic_launcher, 28 | getString(R.string.app_name), System.currentTimeMillis()); 29 | 30 | PendingIntent pendingintent = PendingIntent.getActivity(this, 0, 31 | new Intent(this, AppMain.class), 0); 32 | notification.setLatestEventInfo(this, "uploadservice", "请保持程序在后台运行", 33 | pendingintent); 34 | startForeground(0x111, notification); 35 | ``` 36 | 37 | 注意在onDestroy里还需要stopForeground(true),运行时在下拉列表会看到自己的APP在: 38 | 【结论】如果在极度极度低内存的压力下,该service还是会被kill掉,并且不一定会restart 39 | 40 | 四、onDestroy方法里重启service 41 | 42 |   直接在onDestroy()里startService  43 |   或 44 |   service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service; 45 | 46 | 【结论】当使用类似口口管家等第三方应用或是在setting里-应用-强制停止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,所以还是无法保证~.~ 47 | 48 | 五、监听系统广播判断Service状态 49 | 50 |   通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,别忘记加权限啊。 51 | 52 | 六、将APK安装到/system/app,变身系统级应用 53 | -------------------------------------------------------------------------------- /android/全面了解Activity.md: -------------------------------------------------------------------------------- 1 | #Activity是什么? 2 | 3 |   我们都知道android中有四大组件(Activity 活动,Service 服务,Content Provider 内容提供者,BroadcastReceiver 广播接收器),Activity是我们用的最多也是最基本的组件,因为应用的所有操作都与用户相关,Activity 提供窗口来和用户进行交互。 4 |   官方文档这么说: 5 |    6 | 7 | > An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View). 8 | > 9 | > 10 | > 大概的意思(原谅我): 11 | 12 | >  activity是独立平等的,用来处理用户操作。几乎所有的activity都是用来和用户交互的,所以activity类会创建了一个窗口,开发者可以通过setContentView(View)的接口把UI放到给窗口上。 13 | 14 |   Android中的activity全都归属于task管理 。task 是多个 activity 的集合,这些 activity 按照启动顺序排队存入一个栈(即“back stack”)。android默认会为每个App维持一个task来存放该app的所有activity,task的默认name为该app的packagename。 15 | 16 |   当然我们也可以在AndroidMainfest.xml中申明activity的taskAffinity属性来自定义task,但不建议使用,如果其他app也申明相同的task,它就有可能启动到你的activity,带来各种安全问题(比如拿到你的Intent)。 17 | 18 | #Activity的内部调用过程 19 | 20 |   上面已经说了,系统通过堆栈来管理activity,当一个新的activity开始时,它被放置在堆栈的顶部和成为运行活动,以前的activity始终保持低于它在堆栈,而不会再次到达前台,直到新的活动退出。 21 | 22 |   还是上这张官网的activity_lifecycle图: 23 |   ![这里写图片描述](http://img.blog.csdn.net/20160425171711054)  24 | 25 | 26 | 27 | - 首先打开一个新的activity实例的时候,系统会依次调用 28 | 29 | > onCreate() -> onStart() -> onResume() 然后开始running 30 | 31 |   running的时候被覆盖了(从它打开了新的activity或是被锁屏,但是它**依然在前台**运行, lost focus but is still visible),系统调用onPause(); 32 | 33 | 34 | > 该方法执行activity暂停,通常用于提交未保存的更改到持久化数据,停止动画和其他的东西。但这个activity还是完全活着(它保持所有的状态和成员信息,并保持连接到**窗口管理器**) 35 | 36 | 接下来它有三条出路 37 | ①用户返回到该activity就调用onResume()方法重新running 38 | 39 | ②用户回到桌面或是打开其他activity,就会调用onStop()进入停止状态(保留所有的状态和成员信息,**对用户不可见**) 40 | 41 | ③系统内存不足,拥有更高限权的应用需要内存,那么该activity的进程就可能会被系统回收。(回收onRause()和onStop()状态的activity进程)要想重新打开就必须重新创建一遍。 42 | 43 | 如果用户返回到onStop()状态的activity(又显示在前台了),系统会调用 44 | 45 | > onRestart() -> onStart() -> onResume() 然后重新running 46 | 47 | 在activity结束(调用finish ())或是被系统杀死之前会调用onDestroy()方法释放所有占用的资源。 48 | 49 | 50 | > activity生命周期中三个嵌套的循环 51 | 52 |   53 | 54 | - activity的完整生存期会在 onCreate() 调用和 onDestroy() 调用之间发生。  55 | 56 | - activity的可见生存期会在 onStart() 调用和 onStop() 调用之间发生。系统会在activity的整个生存期内多次调用 onStart() 和onStop(), 因为activity可能会在显示和隐藏之间不断地来回切换。  57 | 58 | - activity的前后台切换会在 onResume() 调用和 onPause() 之间发生。 59 | 因为这个状态可能会经常发生转换,为了避免切换迟缓引起的用户等待,这两个方法中的代码应该相当地轻量化。 60 | 61 | ## activity被回收的状态和信息保存和恢复过程 62 | 63 | ``` 64 | public class MainActivity extends Activity { 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | if(savedInstanceState!=null){ //判断是否有以前的保存状态信息 69 | savedInstanceState.get("Key"); 70 | } 71 | super.onCreate(savedInstanceState); 72 | setContentView(R.layout.activity_main); 73 | } 74 | @Override 75 | protected void onSaveInstanceState(Bundle outState) { 76 | // TODO Auto-generated method stub 77 | //可能被回收内存前保存状态和信息, 78 | Bundle data = new Bundle(); 79 | data.putString("key", "last words before be kill"); 80 | outState.putAll(data); 81 | super.onSaveInstanceState(outState); 82 | } 83 | @Override 84 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 85 | // TODO Auto-generated method stub 86 | if(savedInstanceState!=null){ //判断是否有以前的保存状态信息 87 | savedInstanceState.get("Key"); 88 | } 89 | super.onRestoreInstanceState(savedInstanceState); 90 | } 91 | } 92 | ``` 93 | 94 | > onSaveInstanceState方法 95 | 96 |   在activity 可能被回收之前 调用,用来保存自己的状态和信息,以便回收后重建时恢复数据(在onCreate()或onRestoreInstanceState()中恢复)。旋转屏幕重建activity会调用该方法,但其他情况在onRause()和onStop()状态的activity不一定会调用 ,下面是该方法的文档说明。 97 | 98 | ``` 99 | One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact. 100 | ``` 101 | 102 | 也就是说,系统灵活的来决定调不调用该方法,**但是如果要调用就一定发生在onStop方法之前,但并不保证发生在onPause的前面还是后面。** 103 | 104 | > onRestoreInstanceState方法 105 | 106 |   这个方法在onStart 和 onPostCreate之间调用,在onCreate中也可以状态恢复,但有时候需要所有布局初始化完成后再恢复状态。 107 | 108 |   onPostCreate:一般不实现这个方法,当程序的代码开始运行时,它调用系统做最后的初始化工作。 109 | 110 | #启动模式 111 | 112 | ##启动模式什么? 113 |    114 |   简单的说就是定义activity 实例与task 的关联方式。 115 |    116 | ##为什么要定义启动模式? 117 | 118 |    为了实现一些默认启动(standard)模式之外的需求: 119 |    120 | 121 | - 让某个 activity 启动一个新的 task (而不是被放入当前 task ) 122 | 123 | - 让 activity 启动时只是调出已有的某个实例(而不是在 back stack 顶创建一个新的实例)  124 | 125 | - 或者,你想在用户离开 task 时只保留根 activity,而 back stack 中的其它 activity 都要清空 126 | 127 | ##怎样定义启动模式? 128 | 129 |   定义启动模式的方法有两种: 130 | 131 | ### 使用 manifest 文件 132 | 133 |   在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。 134 | 135 |    136 | 137 | ``` 138 | 142 | ....... 143 | 144 | ``` 145 | 146 | > 注意: 你用 launchMode 属性为 activity 设置的模式可以被启动 activity 的 intent 标志所覆盖。 147 | 148 | ####有哪些启动模式? 149 | 150 | 151 | - "standard" (默认模式)  152 | 153 |   当通过这种模式来启动Activity时, Android总会为目标 Activity创建一个新的实例,并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。  154 |    155 | - "singleTop"  156 | 157 |   该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。 158 | 159 | - "singleTask" 160 | 161 |   Activity在同一个Task内只有一个实例。 162 |    163 |   如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;  164 | 165 |   如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。  166 | 167 |   如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶。 168 | 169 | - "singleInstance"  170 | 171 |   无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例). 172 | 173 |   如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。 174 | 175 |   如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。 176 | 177 | ### 使用 Intent 标志 178 | 179 |   在要启动 activity 时,你可以在传给 startActivity() 的 intent 中包含相应标志,以修改 activity 与 task 的默认关系。 180 | 181 |    182 | 183 | ``` 184 |      Intent i = new Intent(this,NewActivity.class); 185 | i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 186 | startActivity(i); 187 | ``` 188 | 189 | ####可以通过标志修改的默认模式有哪些? 190 | 191 |     192 | 193 | - FLAG_ACTIVITY_NEW_TASK 194 | 195 |   与"singleTask"模式相同,在新的 task 中启动 activity。如果要启动的 activity 已经运行于某 task 中,则那个 task 将调入前台。 196 | 197 | - FLAG_ACTIVITY_SINGLE_TOP 198 | 199 |   与 "singleTop"模式相同,如果要启动的 activity位于back stack 顶,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。 200 | 201 | - FLAG_ACTIVITY_CLEAR_TOP 202 | 203 |   **此种模式在launchMode中没有对应的属性值。**如果要启动的 activity 已经在当前 task 中运行,则不再启动一个新的实例,且所有在其上面的 activity 将被销毁。 204 | 205 | #### 关于启动模式的一些建议 206 | 207 |    一般不要改变 activity 和 task 默认的工作方式。 如果你确定有必要修改默认方式,请保持谨慎,并确保 activity 在启动和从其它 activity 返回时的可用性,多做测试和安全方面的工作。 208 | 209 | 210 | # Intent Filter 211 | 212 |   android的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。intent消息用于在运行时绑定不同的组件。 213 |   在 Android 的 AndroidManifest.xml 配置文件中可以通过 intent-filter 节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。 214 | 215 | ##intent-filter 的三大属性 216 | 217 | ###Action 218 | 219 |   一个 Intent Filter 可以包含多个 Action,Action 列表用于标示 Activity 所能接受的“动作”,它是一个用户自定义的字符串。 220 | 221 |    222 | 223 | ``` 224 | 225 | 226 | 227 | …… 228 | 229 | ``` 230 | 231 | 在代码中使用以下语句便可以启动该Intent 对象: 232 | 233 | ``` 234 | Intent i=new Intent(); 235 | i.setAction("com.scu.amazing7Action"); 236 | ``` 237 | Action 列表中包含了“com.scu.amazing7Action”的 Activity 都将会匹配成功 238 | 239 | ###URL 240 | 241 |   在 intent-filter 节点中,通过 data节点匹配外部数据,也就是通过 URI 携带外部数据给目标组件。 242 | 243 |    244 | 245 | ``` 246 | 251 | ``` 252 | 注意:只有data的所有的属性都匹配成功时 URI 数据匹配才会成功 253 | 254 | ###Category 255 | 256 |   为组件定义一个 类别列表,当 Intent 中包含这个类别列表的所有项目时才会匹配成功。 257 | 258 | ``` 259 | 260 | 261 | 262 | 263 | ``` 264 | 265 | ##Activity 种 Intent Filter 的匹配过程 266 | 267 |   ①加载所有的Intent Filter列表 268 |   ②去掉action匹配失败的Intent Filter 269 |   ③去掉url匹配失败的Intent Filter 270 |   ④去掉Category匹配失败的Intent Filter 271 |   ⑤判断剩下的Intent Filter数目是否为0。如果为0查找失败返回异常;如果大于0,就按优先级排序,返回最高优先级的Intent Filter 272 | 273 | 274 | 275 | # 开发中Activity的一些问题 276 | 277 | - 278 | 279 | 一般设置Activity为非公开的 280 | 281 |    282 | 283 | ``` 284 | 287 | ``` 288 | 289 | 注意:非公开的Activity不能设置intent-filter,以免被其他activity唤醒(如果拥有相同的intent-filter)。 290 | 291 | - 不要指定activity的taskAffinity属性 292 | 293 | - 不要设置activity的LaunchMode(保持默认) 294 | 295 |   注意Activity的intent最好也不要设定为FLAG_ACTIVITY_NEW_TASK 296 | 297 | - 在匿名内部类中使用this时加上activity类名(类名.this,不一定是当前activity) 298 | 299 | - 设置activity全屏 300 | 301 |   在其 onCreate()方法中加入: 302 | 303 | ``` 304 | // 设置全屏模式 305 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 306 | // 去除标题栏 307 | requestWindowFeature(Window.FEATURE_NO_TITLE); 308 | ``` 309 | 310 |    311 | 312 | 313 | 314 |    315 | 316 |    317 |    318 |    -------------------------------------------------------------------------------- /android/加速启动activity.md: -------------------------------------------------------------------------------- 1 |   减少onCreate的时间,那就精简onCreate里的代码。放在onResume里好了。为了用户体验更好一些,把页面显示的View细分一下,放在AsyncTask里逐步显示,如果你够熟练,用handler更好,这样用户的看到的就是有层次有步骤的一个个的view的展示,不会是先看到一个黑屏,然后一下显示所有view。最好作成动画,效果更自然些。利用多线程的目的就是尽可能的减少onCreate和onReume的时间,使得用户能尽快看到页面,操作页面。 2 |    3 |   但是,很多操作是只需要一次初始化的,都放在onResume里每次进入activity都会浪费初始化时间。这个好解决,做一个boolean变量,在onCreate里标记为true。在onResume里判断为true就进行初始化,初始化完成立刻置为false。搞定。 4 | 5 | 6 | 1.减小主线程的阻塞时间  7 | 8 |  若一个操作耗时教长(超过5秒 用户无响应5秒 网络和数据库阻塞10秒 广播接收者执行超过10秒会导致ANR),我们应该将其放入后台线程中执行,只在需要修改UI界面时通知主线程进行修改。 9 | Android已经提供了AsynTask以实现从主线程生成新的异步任务的方法。具体用法参见异步任务。  10 | 11 | 2.提高Adapter和AdapterView的效率 12 | (1)重用已生成过的Item View 13 | (2) 添加ViewHolder 14 | (3) 缓存Item的数据 15 | (4)分段显示  16 | 17 | 3.优化布局文件 18 | 如果我们的布局层次过多,那么在我们用findViewById的时间必然会变多,一个变多可能不要紧,但是有很多调用必然会影响性能。 19 | 20 | (1) 使用观察布局的工具 Hierarchy Viewer 21 | 22 | (2)使用布局优化工具: Layoutopt 23 | 24 | (3)优化布局的层次结构 25 | 26 | -------------------------------------------------------------------------------- /android/怎样退出终止App.md: -------------------------------------------------------------------------------- 1 | 2 | 1. 3 | 4 | 在application中定义一个单例模式的Activity栈来管理所有Activity。并提供退出所有Activity的方法。 5 | 6 | 2. 7 | 8 | AndroidManifest.xml 添加权限 9 | 10 | 11 | 退出应用的方法: 12 | 13 | ActivityManager am= (ActivityManager) this 14 | .getSystemService(Context.ACTIVITY_SERVICE); 15 | am.killBackgroundProcesses(this.getPackageName()); 16 | 17 | 3. 18 | 19 | Dalvik VM的本地方法 20 | android.os.Process.killProcess(android.os.Process.myPid()) //获取PID 21 | System.exit(0); //常规java、c#的标准退出法,返回值为0代表正常退出 22 | 23 | 4. 24 | 25 | Android的窗口类提供了历史栈,我们可以通过stack的原理来巧妙的实现,这里我们在A窗口打开B窗口时在Intent中直接加入标 志 Intent.FLAG_ACTIVITY_CLEAR_TOP,这样开启B时将会清除该进程空间的所有Activity。 26 | 在A窗口中使用下面的代码调用B窗口 27 | Intent intent = new Intent(); 28 | intent.setClass(this, B.class); 29 | intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); //注意本行的FLAG设置 30 | startActivity(intent); 31 | 接下来在B窗口中需要退出时直接使用finish方法即可全部退出。 -------------------------------------------------------------------------------- /data structure/[数据结构] AVL树和AVL旋转、哈夫曼树和哈夫曼编码.md: -------------------------------------------------------------------------------- 1 | #1. AVL树 2 | 3 |   AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。 4 | 5 |   节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要**重新平衡**这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。 6 | 7 | ##1.2AVL旋转 8 | 9 |   AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。 10 | 11 |   以下图表以 **四列** 表示四种情况,每行表示在该种情况下要进行的操作。在左左和右右的情况下,只需要进行一次旋转操作;在左右和右左的情况下,需要进行两次旋转操作。 12 | 13 | > Root是失去平衡树的根节点,Pivot是旋转后重新平衡树的根节点 14 | 15 | ![这里写图片描述](http://img.blog.csdn.net/20160612143136956) 16 | 17 | - 插入 18 | 19 |  向AVL树插入,可以透过如同它是未平衡的二叉查找树一样,把给定的值插入树中,接着自底往上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。 20 | 21 | - 删除 22 | 23 |  从AVL树中删除,可以透过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。 24 | 25 | - 搜寻 26 | 27 |  可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。 28 | 29 | ##1.3AVL节点数计算 30 | 31 | 高度为h的AVL树,节点数N最多 ![这里写图片描述](https://wikimedia.org/api/rest_v1/media/math/render/svg/f933ccc9d17f70cd03d6aa6fbf61b34a0d47ff62)。 32 | 33 | ![这里写图片描述](http://img.blog.csdn.net/20160612143832921)  34 | 35 | 想起了什么? Fibonacci 是不是...可以根据节点数n求出相应的高度h。 36 | 37 | 38 | #2.哈夫曼树 39 | 40 |   哈夫曼树又称最优二叉树,是一种**带权路径长度最短的二叉树**。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。 41 | 42 |   树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。 43 | 44 | #2.1哈夫曼树的构造 45 | 46 |   假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为: 47 |    48 | ① 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点); 49 | 50 | ②在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和; 51 | 52 | ③从森林中删除选取的两棵树,并将新树加入森林; 53 | 54 | ④重复②、③步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。 55 | 56 | #2.2哈夫曼编码 57 | 58 |   哈夫曼编码是可变字长编码(VLC)的一种。该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码。 59 | 60 | ![这里写图片描述](http://img.blog.csdn.net/20160612151017097)  61 | 62 | > 有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,对其进行哈夫曼编码。 63 | 64 | 首先把A,B,C,D,E构造成一棵哈夫曼树(2.1有说明); 65 | 66 | 通过从哈夫曼树根结点开始,对左子树分配代码“0”,右子树分配代码“1”,一直到达叶子结点为止,然后将从树根沿每条路径到达叶子结点的代码排列起来,便得到了哈夫曼编码。 67 | 68 | ![这里写图片描述](http://img.blog.csdn.net/20160612151250872)  69 | 70 | 所以各字符对应的编码为:A->11,B->10,C->00,D->011,E->010 71 | 72 | 霍夫曼编码是一种无前缀编码。解码时不会混淆。其主要应用在数据压缩,加密解密等场合。 -------------------------------------------------------------------------------- /data structure/[数据结构] B(B-)树、B+树、B树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] B(B-)树、B+树、B树.md -------------------------------------------------------------------------------- /data structure/[数据结构] Hash表、Hash函数及冲突解决.md: -------------------------------------------------------------------------------- 1 | #1.Hash表 2 | 3 |   哈希表(Hash table,也叫散列表),是根据key而**直接**进行访问的数据结构。也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 4 | 5 |   以数据中每个元素的关键字K为自变量,通过散列函数H(k)计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。 6 | 7 |   哈希表存储的是键值对,其查找的时间复杂度与元素数量多少无关,哈希表在查找元素时是通过计算哈希码值来定位元素的位置从而直接访问元素的,因此,哈希表查找的时间复杂度为O(1)。 8 | 9 | #2.哈希表的构造方法 10 | 11 | ##2.1直接定址法 12 | 13 |   取关键字或者关键字的某个**线性函数**值作为哈希地址,即  14 |    15 |   H(Key)=Key或者H(Key)=a*Key+b(a,b为整数)  16 |    17 |   这种散列函数也叫做自身函数.如果H(Key)的哈希地址上已经有值了,那么就往下一个位置找,直到找到H(Key)的位置没有值了就把元素放进去.  18 |   此法仅适合于:地址集合的大小 等于 关键字集合的大小 19 | 20 | ##2.2 数字分析法 21 | 22 |   分析一组数据,比如一组员工的出生年月,这时我们发现出生年月的前几位数字一般都相同,因此,出现冲突的概率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果利用后面的几位数字来构造散列地址,则冲突的几率则会明显降低. 23 |   因此数字分析法就是找出**数字的规律**,尽可能利用这些数据来构造冲突几率较低的散列地址.   24 |   此法适于:能预先估计出全体关键字的每一位上各种数字出现的频度。 25 | ##2.3 平方取中法  26 | 27 |   以关键字的平方值的中间几位作为存储地址(哈希地址)。求“关键字的平方值” 的目的是“扩大差别” ,同时平方值的中间各位又能受到整个关键字中各位的影响。  28 | 29 |   此法适于:关键字中的每一位都有某些数字重复出现频度很高的现象。 30 | ##2.4 折叠法  31 | 32 |    将关键字分割成若干部分,然后取它们的叠加和为哈希地址。两种叠加处理的方法:移位叠加:将分 割后的几部分低位对齐相加;间界叠加:从一端沿分割界来回折叠,然后对齐相加。 33 |    34 |   此法适于:关键字的**数字位数特别多**。  35 | 36 | ##2.5随机数法 37 | 38 |   设定哈希函数为:H(key) = Random(key)其中,Random 为伪随机函数 39 | 此法适于:对**长度不等的关键字**构造哈希函数。 40 | 41 | ##2.6除留余数法 42 | 43 |   取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址.即 44 |   哈希函数为:H(key) = key MOD p ( p≤m ),其中, m为表长,p 为不大于 m 的素数。 45 | 46 | #3.哈希表冲突解决方法 47 | 48 |   哈希表处理冲突主要有**开放寻址法**、**再散列法**、**链地址法**(拉链法)和建立一个**公共溢出区**四种方法。 49 |   通过构造性能良好的哈希函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法的另一个关键问题。 50 |  “处理冲突” 的实际含义是:**为产生冲突的关键字寻找下一个哈希地址。** 51 | ##3.1开放定址法 52 |    53 |   一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。 54 | 55 | ###3.1.1线性探测 56 | 57 |   冲突发生时,**顺序查看**表中下一单元,直到找出一个空单元或查遍全表。 58 | 59 |   公式: 60 |    61 | 62 | ``` 63 | fi(key) = (f(key)+di) MOD m (di=1,2,3,......,m-1) 64 | ``` 65 | 66 | ###3.1.2二次探测法 67 | 68 |   冲突发生时,在表的左右进行跳跃式探测,双向寻找到可能的空位置。 69 | 70 | 公式: 71 | 72 | ``` 73 | fi(key) = (f(key)+di) MOD m (di = 12, -12, 22, -22,……, q2, -q2, q <= m/2) 74 | ``` 75 | ###3.1.3随机探测法 76 | 77 |   在冲突时,对于位移量 di 采用随机函数计算得到,我们称之为随机探测法。 78 | 79 |  公式: 80 |   81 | 82 | ``` 83 | fi(key) = (f(key)+di) MOD m (di是一个随机数列) 84 | ``` 85 | 86 |   线性探测再散列容易产生“二次聚集”,即在处理同义词的冲突时又导致非同义词的冲突。 87 |   线性探测再散列的优点是:只要哈希表不满,就一定能找到一个不冲突的哈希地址,而二次探测再散列和伪随机探测再散列则不一定。 88 | 89 | ##3.2链地址法 90 | 91 |    将所有哈希地址相同的记录都链接在同一链表中。各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况。 92 |    处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短; 93 | 94 | ##3.3再哈希法 95 | 96 | 这种方法是同时构造多个不同的哈希函数: 97 | 98 | Hi=RH1(key),i=1,2,3,…,n. 99 | 100 | 当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。 101 | 102 | ##3.4建立公共溢出区 103 | 104 |   这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表.(注意:在这个方法里面是把元素分开两个表来存储) 105 | 106 | -------------------------------------------------------------------------------- /data structure/[数据结构] KMP的一个简单解释.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] KMP的一个简单解释.md -------------------------------------------------------------------------------- /data structure/[数据结构] 九大基础排序总结与对比.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 九大基础排序总结与对比.md -------------------------------------------------------------------------------- /data structure/[数据结构] 二分查找与变种二分查找.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 二分查找与变种二分查找.md -------------------------------------------------------------------------------- /data structure/[数据结构] 二叉树前中后、层次遍历算法.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 二叉树前中后、层次遍历算法.md -------------------------------------------------------------------------------- /data structure/[数据结构] 图的BFS、DFS、prim、Dijkstra算法.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 图的BFS、DFS、prim、Dijkstra算法.md -------------------------------------------------------------------------------- /data structure/[数据结构] 字符串操作.md: -------------------------------------------------------------------------------- 1 | > 在JAVA语言中,字符串数据实际上由String类所实现的。Java字符串类分为两类:一类是在程序中不会被改变长度的不变字符串;二类是在程序中会被改变长度的可变字符串。Java环境为了存储和维护这两类字符串提供了 String和StringBuffer两个类。 2 | 3 | #String的一些常用操作 4 | 5 | ##字符串创建 6 | 7 |   8 | 9 | ``` 10 | String str="This is a String"; 11 | ``` 12 | 或者 13 | 14 | ``` 15 | String str=new String("This is a String"); 16 | ``` 17 | 18 |   第一种的的效率比较高,详细的可以看: 19 |   [String、StringBuffer与StringBuilder区别和联系(源码)](http://blog.csdn.net/amazing7/article/details/51219397) 20 | 21 | ##字符串长度 22 | 23 | ``` 24 | String str= "This is a String"; 25 | int len =str.length(); 26 | ``` 27 | 28 | ##指定字符或子字符串的位置 29 | 30 |    31 | 32 | ``` 33 | String str="Thisis a String"; 34 | Int index1 =str.indexOf("i"); //2 35 | Int index2=str.lastIndexOf("i"); //12 36 | ``` 37 | 38 | ##判断两个字符串是否相等 39 | 40 | ``` 41 | String str="This is a String"; 42 | Boolean result=str.equals("This is another String"); 43 | ``` 44 | 45 | ##得到指定位置的字符 46 | 47 | ``` 48 | String str="This is a String"; 49 | char chr=str.charAt(3); 50 | ``` 51 | 52 | ##截取子字符串 53 | 54 | 55 | ``` 56 | str=str.substring(int beginIndex); 57 | ``` 58 |  截取掉str从首字母起长度为beginIndex的字符串,将剩余字符串赋值给str;   59 | 60 | ``` 61 | str=str.substring(int beginIndex,int endIndex); 62 | ``` 63 |  截取str中从beginIndex开始至endIndex结束时的字符串,并将其赋值给str; 64 | 65 | ##字符串合并 66 | 67 |    68 | 69 | ``` 70 | String str="This is a String"; 71 | String str1=str.concat("Test"); //str1="This is a String Test" 72 | ``` 73 | 74 | ##字符串大小写的转换 75 | 76 | ``` 77 | str.toLowerCase(); 78 | ``` 79 | 大写转小写 80 | 81 | ``` 82 | str.toUpperCase(); 83 | ``` 84 | 小写转大写 85 | 86 | ##去掉字符串中开头和结尾处的空格 87 | 88 | ``` 89 | String str="This is a String "; 90 | String str1=str.trim(); //str1="This is a String" 91 | ``` 92 | 93 | #StringBuffer的一些常用操作 94 | 95 | ##初始化 96 | 97 | ``` 98 | StringBuffer s = new StringBuffer(); 99 | ``` 100 | 这样初始化出的StringBuffer对象是一个空的对象。 101 | 如果需要创建带有内容的StringBuffer对象,则可以使用: 102 | 103 | ``` 104 | StringBuffer s = new StringBuffer(“abc”); 105 | ``` 106 | 107 | ##append方法 108 | 109 |   追加内容到当前StringBuffer对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer对象的内容也发生改变。 110 | 111 |    112 | 113 | ``` 114 | StringBuffer sb = new StringBuffer(“abc”); 115 | sb.append(true); 116 | ``` 117 | 对象sb的值将变成”abctrue” 118 | 119 | ##deleteCharAt方法 120 | 121 | 删除指定位置的字符,然后将剩余的内容形成新的字符串 122 | 123 | ``` 124 | StringBuffer sb = new StringBuffer(“Test”); 125 | sb. deleteCharAt(1); 126 | ``` 127 | 对象sb的值变为”Tst” 128 | 129 | 还存在一个功能类似的delete方法: 130 | 131 | 132 | ``` 133 | public StringBuffer delete(int start,int end) 134 | ``` 135 | 136 | 该方法的作用是删除指定区间以内的所有字符,包含start,不包含end索引值的区间。 137 | 138 | ##insert方法 139 | 140 | 在StringBuffer对象中插入内容,然后形成新的字符串。 141 | 142 | ``` 143 | StringBuffer sb = new StringBuffer(“TestString”); 144 |   sb.insert(4,false); 145 | ``` 146 | 在对象sb的索引值4的位置插入false值,形成新的字符串,则执行以后对象sb的值是”TestfalseString”。 147 | 148 | ##reverse方法 149 | 150 | 将StringBuffer对象中的内容反转,然后形成新的字符串 151 | 152 | ``` 153 | StringBuffer sb = new StringBuffer(“abc”); 154 | sb.reverse(); 155 | ``` 156 | 对象sb中的内容将变为”cba” 157 | 158 | ##setCharAt方法 159 | 160 | 修改对象中指定索引值位置的字符为新的字符 161 | 162 | ``` 163 | StringBuffer sb = new StringBuffer(“abc”); 164 | sb.setCharAt(1,’D’); 165 | ``` 166 | sb的值将变成”aDc” 167 | 168 | ##trimToSize方法 169 | 170 | 将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费。 171 | 172 | 173 | #常见问题 174 | 175 | - 字符串比较,使用 "==" 还是 equals() ? 176 | 177 |   [源码分析equals与==的区别](http://blog.csdn.net/amazing7/article/details/51219323) 178 | 179 | - 在switch语句中使用String作为case条件? 180 | 181 |   [Switch能否用string做参数?](http://blog.csdn.net/amazing7/article/details/51219315) 182 | 183 | - String转换为数字 184 | 185 | ``` 186 | int age = Integer.parseInt("10"); 187 | long id = Long.parseLong("190"); // 假如值可能很大. 188 | ``` 189 |   非常大的数字请使用Long 190 | 191 | - 通过空白字符拆分字符串 192 | 193 | ``` 194 | String[] strArray = aString.split("\\s+"); 195 | ``` 196 | 197 |   String 的 split()方法接收的字符串会被当做正则表达式解析,  198 |    199 |   "\s"代表空白字符,如空格" "(tab制表符"\t", 换行"\n",回车"\r") 200 | 201 |   而编译器在对源代码解析时,也会进行一次**字面量转码**,所以需要"\\s". 202 | 203 | - 将String转换为日期 204 | 205 | ``` 206 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 207 | String str = "2016-05-07"; 208 | Date date = format.parse(str); 209 | System.out.println(format.format(date)); 210 | ``` 211 | 212 | 213 | 214 | 215 |   -------------------------------------------------------------------------------- /data structure/[数据结构] 数组与链表的优缺点和区别.md: -------------------------------------------------------------------------------- 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 | 1、存取方式上,数组可以顺序存取或者随机存取,而链表只能顺序存取;  26 | 27 | 2、存储位置上,数组逻辑上相邻的元素在物理存储位置上也相邻,而链表不一定;  28 | 29 | 3、存储空间上,链表由于带有指针域,存储密度不如数组大;  30 | 31 | 4、按序号查找时,数组可以随机访问,时间复杂度为O(1),而链表不支持随机访问,平均需要O(n);  32 | 33 | 5、按值查找时,若数组无序,数组和链表时间复杂度均为O(1),但是当数组有序时,可以采用折半查找将时间复杂度降为O(logn);  34 | 35 | 6、插入和删除时,数组平均需要移动n/2个元素,而链表只需修改指针即可;  36 | 37 | 7、空间分配方面: 38 |   数组在静态存储分配情形下,存储元素数量受限制,动态存储分配情形下,虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且如果内存中没有更大块连续存储空间将导致分配失败; 39 |   链表存储的节点空间只在需要的时候申请分配,只要内存中有空间就可以分配,操作比较灵活高效; 40 | 41 | -------------------------------------------------------------------------------- /data structure/[数据结构] 红黑树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 红黑树.md -------------------------------------------------------------------------------- /data structure/[数据结构] 队列和栈.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/data structure/[数据结构] 队列和栈.md -------------------------------------------------------------------------------- /java/Java与C++对比.md: -------------------------------------------------------------------------------- 1 | 指针 2 | Java 没有指针的概念,从而有效地防止了在 C/C++语言中,容易出现的指针操作失误(如指针悬空所造成的系统崩溃)。在 C/C++中,指针操作内存时,经常会出现错误。在Java 中没有指针,更有利于 Java 程序的安全。 3 | 4 | 多重继承 5 | C++支持多重继承,它允许多父类派生一个子类。也就是说,一个类允许继承多个父类。尽管多重继承功能很强,但使用复杂,而且会引起许多麻烦,编译程序实现它也很不容易。所以 Java 不支持多重继承,但允许一个类实现多个接口。可见,Java 既实现了 C++多重继承的功能,又避免了 C++的许多缺陷。 6 | 7 | 数据类型 8 | Java 是完全面向对象的语言,所有方法和数据都必须是类的一部分。除了基本数据类型之外,其余类型的数据都作为对象型数据。例如对象型数据包括字符串和数组。类将数据和方法结合起来,把它们封装在其中,这样每个对象都可实现具有自己特点的行为。而 C++将函数和变量定义为全局的,然后再来调用这些函数和变量,从而增加了程序的负担。此外,Java 还取消了 C/C++中的结构和联合,使编译程序更简洁。 9 | 10 | 自动内存管理 11 | Java 程序中所有的对象都是用 new 操作符建立在堆栈上的,这个操作符类似于 C++的“new”操作符。Java 自动进行无用内存回收操作,不需要程序员进行删除。当 Java 中一个对象不再被用到时,无须使用内存回收器,只需要给它加上标签以示删除。无用内存的回收器在后台运行,利用空闲时间工作。而 C++中必须由程序释放内存资源,增加了程序设计者的负担。 12 | 13 | 操作符重载 14 | Java 不支持操作符重载,操作符重载被认为是 C++的突出特征。在 Java 中虽然类可以实现这样的功能,但不支持操作符重载,这样是为了保持 Java 语言尽可能简单。 15 | 16 | 预处理功能 17 | C/C++在编译过程中都有一个预编译阶段,即预处理器。预处理器为开发人员提供了方便,但增加了编译的复杂性。Java 允许预处理,但不支持预处理器功能,因为 Java 没有预处理器,所以为了实现预处理,它提供了引入语句(import),它与 C++预处理器的功能类似。 18 | 19 | Java 不支持缺省函数参数,而 C++支持。 20 | 在 C 中,代码组织在函数中,函数可以访问程序的全局变量。C++增加了类,提供了类算法,该算法是与类相连的函数,C++类方法与 Java 类方法十分相似。由于 C++仍然支持 C,所以 C++程序中仍然可以使用 C 的函数,结果导致函数和方法混合使用,使得 C++程序比较混乱。 21 | 22 | Java 没有函数,作为一个比 C++更纯的面向对象的语言。Java 强迫开发人员把所有例行程序包括在类中。事实上,用方法实现例行程序可激励开发人员更好地组织编码。 23 | 24 | 字符串 25 | C 和 C++不支持字符串变量,在 C 和 C++程序中使用“Null”终止符代表字符串的结束,在 Java 中字符串是用类对象(String 和 StringBuffer)来实现的,在整个系统中建立字符串和访问字符串元素的方法是一致的。Java 字符串类是作为 Java 语言的一部分定义的,而不是作为外加的延伸部分。此外,Java 还可以对字符串用“+”进行连接操作。 26 | 27 | goto 语句 28 | “可怕”的 goto 语句是 C 和 C++的“遗物”。它是该语言技术上的合法部分,引用 goto语句造成了程序结构的混乱,不易理解。goto 语句一般用于无条件转移子程序和多结构分支技术。Java 不提供 goto 语句,其虽然指定 goto 作为关键字,但不支持它的使用,这使程序更简洁易读。 29 | 30 | 类型转换 31 | 在 C 和 C++中,有时出现数据类型的隐含转换,这就涉及了自动强制类型转换问题。例如,在 C++中可将一个浮点值赋予整型变量,并去掉其尾数。Java 不支持 C++中的自动强制类型转换,如果需要,必须由程序显式进行强制类型转换。 32 | [图片] 33 | -------------------------------------------------------------------------------- /java/[Java] ArrayList、LinkedList、Vector的区别.md: -------------------------------------------------------------------------------- 1 | 首先我们来看一下继承关系: 2 | 3 | ![这里写图片描述](http://img.blog.csdn.net/20160408150531095) 4 | 5 | - 我们可以看出ArrayList、LinkedList、Vector都实现了List的接口。 6 | 接下来分别看一下三个数据结构的说明。 7 | 8 | > public class **ArrayList** extends AbstractList 9 | implements List, RandomAccess, Cloneable, Serializable 10 | 11 | >List 接口的**大小可变数组**的实现。实现了所有可选列表操作,并**允许包括 null 在内的所有元素**。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了**此类是不同步的**。) 12 | > 13 | 每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。 14 | 15 | >在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。 16 | > 17 | List list = Collections.synchronizedList(new ArrayList(...)); 18 | 19 | >此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。 20 | 注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。 21 | 22 | - 然后是LinkedList 23 | 24 | 25 | > public class **LinkedList** extends AbstractSequentialList 26 | implements List, Deque, Cloneable, Serializable 27 | 28 | >List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(**包括 null**)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。 29 | 30 | >此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。 31 | 32 | >所有操作都是按照**双重链接列表**的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。 33 | 34 | >注意,此实现**不是同步**的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过**对自然封装该列表的对象进行同步操作来完成**。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示: 35 | 36 | > List list = Collections.synchronizedList(new LinkedList(...)); 37 | > 38 | 此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。 39 | 40 | >注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。 41 | 42 | - 最后是Vector 43 | 44 | > public class **Vector** extends AbstractList 45 | implements List, RandomAccess, Cloneable, Serializable 46 | 47 | > Vector 类可以**实现可增长的对象数组**。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。 48 | 49 | > 每个向量会试图通过维护 capacity 和 capacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。 50 | 51 | > 由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。 52 | 53 | > 注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 54 | 55 | > 从 Java 2 平台 v1.2 开始,此类改进为可以实现 List 接口,使它成为 Java Collections Framework 的成员。与新 collection 实现不同,**Vector 是同步的**。 56 | 57 | - 区别 58 | 59 | 60 | ArrayList 本质上是一个可改变大小的**数组**.当元素加入时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问.元素顺序存储 ,**随机访问很快,删除非头尾元素慢,新增元素慢而且费资源** ,较适用于无频繁增删的情况 ,比数组效率低,如果不是需要可变数组,可考虑使用数组 ,**非线程安全**. 61 | 62 | LinkedList 是一个**双链表**,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList. 适用于 :没有大规模的随机读取,大量的增加/删除操作.**随机访问很慢,增删操作很快**,不耗费多余资源 ,允许null元素,**非线程安全.** 63 | 64 | Vector (类似于ArrayList)但其是**同步**的,开销就比ArrayList要大。如果你的程序本身是线程安全的,那么使用ArrayList是更好的选择。 65 | Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%. -------------------------------------------------------------------------------- /java/[Java] Collection包结构,与Collections的区别.md: -------------------------------------------------------------------------------- 1 | > Collection家族 2 | 3 | ![这里写图片描述](http://img.blog.csdn.net/20160408150531095) 4 | 5 | 6 | 7 | Collection是集合继承结构中的顶层接口 8 | 9 | Collections 是提供了对集合进行操作的强大方法的工具类 ,它包含有各种有关集合操作的静态多态方法。此类不能实例化 -------------------------------------------------------------------------------- /java/[Java] Excption与Error包结构,OOM和SOF.md: -------------------------------------------------------------------------------- 1 | 2 | >Java 异常类继承关系图 3 | 4 | ![这里写图片描述](http://img.blog.csdn.net/20160412143252629) 5 | 6 | 7 | (一)Throwable 8 | 9 |   Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类或其子类之一的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出,才可以是 catch 子句中的参数类型。 10 |   Throwable 类及其子类有两个构造方法,一个不带参数,另一个带有 String 参数,此参数可用于生成详细消息。 11 |   Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。 12 |    13 | Java将可抛出(Throwable)的结构分为三种类型: 14 |   错误(Error) 15 |   运行时异常(RuntimeException) 16 |   被检查的异常(Checked Exception) 17 | 18 |  1.**Error** 19 |   Error 是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获的严重问题。大多数这样的错误都是异常条件。 20 |   和RuntimeException一样, 编译器也不会检查Error。 21 |   当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误,程序本身无法修复这些错误的。 22 |    23 |  2.**Exception** 24 |   Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。 25 |    对于可以恢复的条件使用**被检查异常**(Exception的子类中除了RuntimeException之外的其它子类),对于程序错误使用运行时异常。  26 |    27 | >① ClassNotFoundException 28 |    29 | 当应用程序试图使用以下方法通过字符串名加载类时: 30 | Class 类中的 forName 方法。 31 | ClassLoader 类中的 findSystemClass 方法。 32 | ClassLoader 类中的 loadClass 方法。 33 | 但是没有找到具有指定名称的类的定义,抛出该异常。 34 | 35 | ② CloneNotSupportedException 36 | 37 | 当调用 Object 类中的 clone 方法复制对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。重写 clone 方法的应用程序也可能抛出此异常,指示不能或不应复制一个对象。 38 |   39 | 40 | > ③ IOException 41 | 当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操作生成的异常的通用类。 42 | 43 | -EOFException 44 |   当输入过程中意外到达文件或流的末尾时,抛出此异常。 45 | 此异常主要被**数据输入流**用来表明到达流的末尾。 46 | 注意:其他许多输入操作返回一个特殊值表示到达流的末尾,而不是抛出异常。 47 |      48 | -FileNotFoundException 49 |   当试图打开指定路径名表示的文件失败时,抛出此异常。 50 | 在不存在具有指定路径名的文件时,此异常将由 FileInputStream、FileOutputStream 和 RandomAccessFile 构造方法抛出。如果该文件存在,但是由于某些原因不可访问,比如试图打开一个只读文件进行写入,则此时这些构造方法仍然会抛出该异常。 51 | 52 | -MalformedURLException 53 |   抛出这一异常指示出现了错误的 URL。或者在规范字符串中找不到任何合法协议,或者无法解析字符串。  54 |   55 | -UnknownHostException 56 |   指示主机 IP 地址无法确定而抛出的异常。 57 | 58 | >④ RuntimeException 59 |    是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明。 60 |    Java编译器不会检查它。当程序中可能出现这类异常时,还是会编译通过。 61 |    虽然Java编译器不会检查运行时异常,但是我们也可以通过throws进行声明抛出,也可以通过try-catch对它进行捕获处理。 62 | 63 | -ArithmeticException 64 | 当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。 65 | 66 | -ClassCastException 67 |   当试图将对象强制转换为不是实例的子类时,抛出该异常。 68 | 例如:Object x = new Integer(0); 69 | 70 | -LllegalArgumentException 71 |   抛出的异常表明向方法传递了一个不合法或不正确的参数。 72 | 73 | -IllegalStateException 74 |   在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 75 | 76 | -IndexOutOfBoundsException  77 |   指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 78 | 应用程序可以为这个类创建子类,以指示类似的异常。 79 | 80 | -NoSuchElementException 81 |   由 Enumeration 的 nextElement 方法抛出,表明枚举中没有更多的元素。 82 | 83 | -NullPointerException 84 |   当应用程序试图在需要对象的地方使用 null 时,抛出该异常。这种情况包括: 85 | 调用 null 对象的实例方法。 86 | 访问或修改 null 对象的字段。 87 | 将 null 作为一个数组,获得其长度。 88 | 将 null 作为一个数组,访问或修改其时间片。 89 | 将 null 作为 Throwable 值抛出。 90 | 应用程序应该抛出该类的实例,指示其他对 null 对象的非法使用。 91 | 92 | 93 | 94 | (二) SOF (堆栈溢出 StackOverflow) 95 | 96 | > StackOverflowError 的定义: 97 | > 当应用程序递归太深而发生堆栈溢出时,抛出该错误。 98 | > 99 | 因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出。 100 | 101 | 栈溢出的原因: 102 | 103 | 递归调用 104 | 105 | 大量循环或死循环 106 | 107 | 全局变量是否过多 108 | 109 | 数组、List、map数据过大 110 | 111 | 112 | 113 | (三)Android的OOM(Out Of Memory) 114 | 115 |   当内存占有量超过了虚拟机的分配的最大值时就会产生内存溢出(VM里面分配不出更多的page)。 116 |    117 | 一般出现情况:加载的图片太多或图片过大时、分配特大的数组、内存相应资源过多没有来不及释放。 118 | 119 | 解决方法: 120 | ①在内存引用上做处理 121 | 122 | 软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。 123 | 124 |  ②对图片做边界压缩,配合软引用使用 125 |   126 |  ③显示的调用GC来回收内存  127 |   128 | 129 | ``` 130 | if(bitmapObject.isRecycled()==false) //如果没有回收 131 | bitmapObject.recycle(); 132 | ``` 133 |  ④优化Dalvik虚拟机的堆内存分配 134 |    135 |   1.增强程序堆内存的处理效率 136 |    137 | 138 | ``` 139 | //在程序onCreate时就可以调用 即可 140 | private final static floatTARGET_HEAP_UTILIZATION = 0.75f; 141 | 142 | VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 143 | 144 | ``` 145 |  2 .设置堆内存的大小 146 | ``` 147 | private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; 148 | //设置最小heap内存为6MB大小 149 | VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); 150 | ``` 151 | ⑤ 用LruCache 和 AsyncTask<>解决 152 | 153 |   从cache中去取Bitmap,如果取到Bitmap,就直接把这个Bitmap设置到ImageView上面。 154 |   如果缓存中不存在,那么启动一个task去加载(可能从文件来,也可能从网络)。 -------------------------------------------------------------------------------- /java/[Java] HashMap和HashTable的区别.md: -------------------------------------------------------------------------------- 1 | - HashTable 2 | 3 | 4 | > 5 | Hashtable继承于Dictionary字典,实现Map接口 6 | 7 | > 键、值都不能是空对象 8 | 9 | >多次访问,映射元素的顺序相同 10 | 11 | >线程安全 12 | > 13 | > 14 | >hash算法 ,Hashtable则直接利用key本身的hash码来做验证 15 | > 16 | >数据遍历的方式 Iterator (支持fast-fail)和 Enumeration (不支持fast-fail) 17 | > 18 | > 缺省初始长度为11,内部都为抽象方法,需要 它的实现类一一作自己的实现 19 | 20 | 备注:程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。 21 | 22 | - HashMap 23 | 24 | > HashMap继承于AbstractMap抽象类 25 | > 26 | > 键和值都可以是空对象 27 | > 28 | 多次访问,映射元素的顺序可能不同 29 | 30 | > 非线程安全 31 | HashMap可以通过下面的语句进行同步: 32 | Map m = Collections.synchronizeMap(hashMap); 33 | > 34 | > 检测是否含有key时,HashMap内部需要将key的hash码重新计算一边再检测 35 | > 36 | > 数据遍历的方式 Iterator (支持fast-fail) 37 | > 38 | > 缺省初始长度为16,其内部已经实现了Map所需 要做的大部分工作, 它的子类只需要实现它的少量方法 -------------------------------------------------------------------------------- /java/[Java] HashMap源码分析.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] HashMap源码分析.md -------------------------------------------------------------------------------- /java/[Java] Hashcode的作用.md: -------------------------------------------------------------------------------- 1 | 关于HashCode的官方文档定义: 2 | 3 | > hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。 hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 4 | 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 5 | 以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 6 | 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 7 | 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。 8 | 9 | - 以上这段官方文档的定义,我们可以抽出成以下几个关键点: 10 | 11 | 1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的; 12 | 13 | 2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同; 14 | 15 | 3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点; 16 | 17 | 4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。 18 | 19 | 再归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。 20 | 21 | - 总的来说,Java中的集合(Collection)有两类,一类 是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一 次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它 就要调用1000次equals方法。这显然会大大降低效率。 22 | 于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是 将数据依特定算法直接指定到一个地址上。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以 直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散 列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。 23 | 所以,Java对于eqauls方法和hashCode方法是这样规定的: 24 | 25 |   1、如果两个对象相同,那么它们的hashCode值一定要相同; 26 |   2、如果两个对象的hashCode相同,它们并不一定相同 -------------------------------------------------------------------------------- /java/[Java] Map、Set、List、Queue、Stack的特点与用法.md: -------------------------------------------------------------------------------- 1 | - Map 2 | 3 | 4 | 5 | > 键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 6 | 7 | >某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。 8 | 9 | >Map中元素,可以将key序列、value序列单独抽取出来。 10 | 使用keySet()抽取key序列,将map中的所有keys生成一个Set。 11 | 使用values()抽取value序列,将map中的所有values生成一个Collection。 12 | 为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。 13 | 14 | - Set 15 | 16 | 17 | 18 | >一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。 19 | > 20 | > 不可随机访问包含的元素 21 | > 22 | 只能用Iterator实现单向遍历 23 | > 24 | > Set 没有同步方法 25 | 26 | - List 27 | 28 | 29 | 30 | > 可随机访问包含的元素 31 | 元素是有序的 32 | 可在任意位置增、删元素 33 | 不管访问多少次,元素位置不变 34 | 允许重复元素 35 | 用Iterator实现单向遍历,也可用ListIterator实现双向遍历 36 | 37 | - Queue 38 | 39 | > 先进先出 40 | > 41 | > Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果要使用前端而不移出该元素,使用element()或者peek()方法。 42 | 值得注意的是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。 43 | 44 | > Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。 45 | 46 | - Stack 47 | 48 | > 后进先出 49 | > 50 | > Stack继承自Vector(可增长的对象数组),也是同步的 51 | > 它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到**堆栈顶**距离的 search 方法。 52 | 53 | - 用法 54 | 55 | 56 | > 如果涉及到堆栈、队列等操作,应该考虑用List; 57 | 58 | > 对于需要快速插入,删除元素,应该使用LinkedList; 59 | 60 | > 如果需要快速随机访问元素,应该使用ArrayList。 61 | 62 | > 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高 -------------------------------------------------------------------------------- /java/[Java] Object有哪些公用方法?.md: -------------------------------------------------------------------------------- 1 | > 官方文档 http://docs.oracle.com/javase/8/docs/api/ 2 | 3 | ![这里写图片描述](http://img.blog.csdn.net/20160406172807848) 4 | ![这里写图片描述](http://img.blog.csdn.net/20160406172424112) 5 | 6 | protected Object clone() 7 | 创建并返回此对象的一个副本。 8 | 9 | boolean equals(Object obj) 10 | 指示其他某个对象是否与此对象“相等”。 11 | 12 | protected void finalize() 13 | 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 14 | 15 | Class getClass() 16 | 返回此 Object 的运行时类。 17 | 18 | int hashCode() 19 | 返回该对象的哈希码值。 20 | 21 | void notify() 22 | 唤醒在此对象监视器上等待的单个线程。 23 | 24 | void notifyAll() 25 | 唤醒在此对象监视器上等待的所有线程。 26 | 27 | String toString() 28 | 返回该对象的字符串表示。 29 | 30 | void wait() 31 | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 32 | 33 | void wait(long timeout) 34 | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 35 | 36 | void wait(long timeout, int nanos) 37 | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 -------------------------------------------------------------------------------- /java/[Java] Override和Overload的使用规则和区别.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] Override和Overload的使用规则和区别.md -------------------------------------------------------------------------------- /java/[Java] Switch能否用string做参数?.md: -------------------------------------------------------------------------------- 1 | 1 . 在jdk 7 之前,switch 只能支持 byte、short、char、int 这几个基本数据类型和其对应的封装类型。switch后面的括号里面只能放int类型的值,但由于byte,short,char类型,它们会 **自动** 转换为int类型(精精度小的向大的转化),所以它们也支持。 2 | 3 | 对于精度比int大的类型,long、float、double,不会自动转换成int。要想使用就得加强转如(int)long。 4 | 5 | 另外boolean类型不参与转换,任何类型不能转换为boolean型. 6 | 7 | 2 .我们也可以用枚举类型实现switch可传入string参数 8 | 9 | ``` 10 | public enum En{ 11 | a,b,c 12 | } 13 | public static void main(String[] args) { 14 | En t = En.a; 15 | function(t); 16 | } 17 | public static void function(En type){ 18 | switch (type) { 19 | case a: 20 | System.err.println("a"); 21 | break; 22 | case b: 23 | System.err.println("b"); 24 | break; 25 | case c: 26 | System.err.println("c"); 27 | break; 28 | default: 29 | break; 30 | } 31 | } 32 | ``` 33 | 运行结果: 34 | 35 | ![这里写图片描述](http://img.blog.csdn.net/20160406161357320) 36 | 37 | 3 . jdk7之后java加入了switch对string的支持,就不用枚举来实现啦! 38 | 39 | ``` 40 | public static void main(String[] args) { 41 | String s = "a"; 42 | switch (s) { 43 | case "a": 44 | System.err.println("a"); 45 | break; 46 | case "b": 47 | System.err.println("b"); 48 | break; 49 | case "c": 50 | System.err.println("c"); 51 | break; 52 | default: 53 | break; 54 | } 55 | } 56 | ``` 57 | 运行结果: 58 | 59 | ![这里写图片描述](http://img.blog.csdn.net/20160406161357320) -------------------------------------------------------------------------------- /java/[Java] ThreadLocal的使用规则和源码分析.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] ThreadLocal的使用规则和源码分析.md -------------------------------------------------------------------------------- /java/[Java] ThreadPool用法与示例.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] ThreadPool用法与示例.md -------------------------------------------------------------------------------- /java/[Java] equals与==的区别.md: -------------------------------------------------------------------------------- 1 | - == 是一个运算符。 2 | equals则是string对象的方法。 3 | 4 | - java中 **值类型** 是存储在内存中的**栈**中。 5 | 而**引用类型**在栈中仅仅是存储引用类型变量的地址,而其本身则存储在**堆**中。所以字符串的内容相同,引用地址不一定相同,有可能创建了多个对象。 6 | 7 | - ==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。 8 | 9 | - equals将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。即堆中的内容是否相同。==比较的是2个对象的地址(栈中),而equals比较的是2个对象的内容(堆中)。所以当equals为true时,==不一定为true。 10 | 11 | > 下面是String类equals方法源码,它复写了类 Object 中的 equals方法。 12 | 13 | ``` 14 | public boolean equals(Object anObject) { 15 | 965 if (this == anObject) { 16 | 966 return true; 17 | 967 } 18 | 968 if (anObject instanceof String) { 19 | 969 String anotherString = (String)anObject; 20 | 970 int n = value.length; 21 | 971 if (n == anotherString.value.length) { 22 | 972 char v1[] = value; 23 | 973 char v2[] = anotherString.value; 24 | 974 int i = 0; 25 | 975 while (n-- != 0) { 26 | 976 if (v1[i] != v2[i]) 27 | 977 return false; 28 | 978 i++; 29 | 979 } 30 | 980 return true; 31 | 981 } 32 | 982 } 33 | 983 return false; 34 | 984 } 35 | ``` 36 |   上面已经说到equals是比较两个对象的内容,我们可以看到方法中,先是比较两个String对象是否为同一对象,如果是就直接返回true(两个对象为同一对象那他们的内容必然相等)。 37 |   如果不是同一对象,先确定传入的对象是否是String类型,如果是,则比较两对象的字符序列(String类内部存储是用char[]实现的,可以查看源码了解),遍历过程中只要有一个字符不相同,就返回false,否则返回true。这里**注意**比较次数为第一个String对象的长度n,而不是传入的String对象参数的长度。 38 | -------------------------------------------------------------------------------- /java/[Java] try catch finally,try里有return,finally还执行么?.md: -------------------------------------------------------------------------------- 1 | 2 | *Condition 1:* 如果try中**没有异常**且try中**有return** (执行顺序) 3 | 4 | ``` 5 | try ---- finally --- return 6 | ``` 7 | 8 | *Condition 2:* 如果try中**有异常**并且try中**有return** 9 | 10 | ``` 11 | try----catch---finally--- return 12 | ``` 13 | 14 | 总之 finally 永远执行! 15 | 16 | 17 | 18 | *Condition 3:* try中有异常,try-catch-finally里都没有return ,finally 之后有个return 19 | 20 | ``` 21 | try----catch---finally 22 | ``` 23 | 24 | try中有异常以后,根据java的异常机制先执行catch后执行finally,此时错误异常已经抛出,程序因异常而终止,所以你的return是不会执行的 25 | 26 | *Condition 4:* 当 try和finally中都有return时,finally中的return会覆盖掉其它位置的return(多个return会报unreachable code,编译不会通过)。 27 | 28 | *Condition 5:* 当finally中不存在return,而catch中存在return,但finally中要修改catch中return 的变量值时 29 | 30 | ``` 31 | int ret = 0; 32 | try{ 33 | throw new Exception(); 34 | } 35 | catch(Exception e) 36 | { 37 | ret = 1; return ret; 38 | } 39 | finally{ 40 | ret = 2; 41 | } 42 | 43 | ``` 44 | 最后返回值是1,因为return的值在执行finally之前已经确定下来了 -------------------------------------------------------------------------------- /java/[Java] 九种基本数据类型的大小,以及他们的封装类.md: -------------------------------------------------------------------------------- 1 |    变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。 2 | 内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。 3 | 因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。 4 | 5 | 6 | 7 | 8 | Java的两大数据类型: 9 | 10 | (一).内置数据类型(基本数据类型) 11 | 12 |   1 六种数字类型 ( byte, short, int, long, float, double) + void 13 | 14 |           8 16 32 64 32 64 位 15 | 16 | 17 |   2 一种字符类型 char 18 | 19 |           16位Unicode字符 20 | 21 | 22 |   3 一种布尔型 boolean 23 | 24 |           1位 25 | 26 | (二).引用数据类型 27 | 28 |   引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型。变量一旦声明后,类型就不能被改变了。 29 | 30 |   对象、数组都是引用数据类型,所有引用类型的默认值都是null。 31 | 32 | 33 | 34 | 基本数据类型只能按值传递,而封装类按引用传递。 35 | 36 |   Void无返回值类型,作为伪类型对应类的对象,也被认为是 基本数据类型 37 | -------------------------------------------------------------------------------- /java/[Java] 从源码分析String、StringBuffer与StringBuilder区别和联系.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] 从源码分析String、StringBuffer与StringBuilder区别和联系.md -------------------------------------------------------------------------------- /java/[Java] 多线程下生产者消费者问题的五种同步方法实现.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] 多线程下生产者消费者问题的五种同步方法实现.md -------------------------------------------------------------------------------- /java/[Java] 实现多线程的两种方法.md: -------------------------------------------------------------------------------- 1 | 2 | Java提供了两种创建线程方法: 3 | 4 | - 5 | 6 | 通过实现Runable接口; 7 | 8 | - 通过继承Thread类本身。 9 | 10 | 11 | 1 .声明实现 Runnable 接口的类,该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。例如,计算大于某一规定值的质数的线程可以写成: 12 | 13 | 14 | ``` 15 | class PrimeRun implements Runnable { 16 | long minPrime; 17 | PrimeRun(long minPrime) { 18 | this.minPrime = minPrime; 19 | } 20 | 21 | public void run() { 22 | // compute primes larger than minPrime 23 | . . . 24 | } 25 | } 26 | ``` 27 | 28 | 然后,下列代码会创建并启动一个线程: 29 | 30 | ``` 31 | PrimeRun p = new PrimeRun(143); 32 | new Thread(p).start(); 33 | ``` 34 | 35 | 2.将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。 36 | ``` 37 | class PrimeThread extends Thread { 38 | long minPrime; 39 | PrimeThread(long minPrime) { 40 | this.minPrime = minPrime; 41 | } 42 | 43 | public void run() { 44 | // compute primes larger than minPrime 45 | . . . 46 | } 47 | } 48 | ``` 49 | 50 | 然后,下列代码会创建并启动一个线程: 51 | 52 | ``` 53 | PrimeThread p = new PrimeThread(143); 54 | p.start(); 55 | ``` 56 | 57 |   当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止: 58 |    59 | 60 | 61 | - 62 | 63 | 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。 64 | 65 | - 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。 66 | 67 | 3.使用和区别 68 | 69 |   Runable源码: 70 |    71 | 72 | ``` 73 | public interface Runnable { 74 | /** 75 | * When an object implementing interface Runnable is used 76 | * to create a thread, starting the thread causes the object's 77 | * run method to be called in that separately executing 78 | * thread. 79 | *

80 | * The general contract of the method run is that it may 81 | * take any action whatsoever. 82 | * 83 | * @see java.lang.Thread#run() 84 | */ 85 | public abstract void run(); 86 | } 87 | ``` 88 |   Thread 类实现了 Runnable。激活的意思是说某个线程已启动并且尚未停止。此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。**大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法**,那么应使用 Runnable 接口。这很重要,因为**除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类**。 89 | 90 |   继承Thread类实现多线程,要求放入多线程中的类不能继承其他类(Java的单继承特性),如果需要请用 Runnable 实现(接口可以多实现并不影响继承其他类)。 91 |    92 |   一个实现Runnable接口的类可以放在多个线程中执行,多个线程可以去执行同一资源;而继承Thread只能实现多个线程分别去处理自己的资源。(通过Runnable创建的多个线程可以由编程人员传入同一个Runnable对象,即执行同一个run方法,而通过Thread创建的多线程它们运行的都是自己的run方法)。 93 | -------------------------------------------------------------------------------- /java/[Java] 接口(Interface)与 抽象类 (Abstract)使用规则和区别.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] 接口(Interface)与 抽象类 (Abstract)使用规则和区别.md -------------------------------------------------------------------------------- /java/[Java] 方法锁、对象锁和类锁的意义和区别.md: -------------------------------------------------------------------------------- 1 | > 首先的明白java中锁的机制 2 | > 3 | > synchronized 4 | >   在修饰代码块的时候需要一个reference对象作为锁的对象. 5 |   在修饰方法的时候默认是当前对象作为锁的对象. 6 |   在修饰类时候默认是当前类的Class对象作为锁的对象. 7 |    8 | > [线程同步的方法:sychronized、lock、reentrantLock分析](http://blog.csdn.net/amazing7/article/details/51219714) 9 | 10 | #方法锁(synchronized修饰方法时) 11 | 12 | 通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。 13 | 14 | synchronized 方法控制对类成员变量的访问: 15 |   每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,**方法一旦执行,就独占该锁**,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中**至多只有一个**处于可执行状态,从而有效避免了类成员变量的访问冲突。 16 | 17 | 18 | # 对象锁(synchronized修饰方法或代码块) 19 | 20 |   当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁)        21 |    22 |   java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,**方法抛异常的时候,锁仍然可以由JVM来自动释放。**  23 | 24 | 对象锁的两种形式: 25 | 26 | ``` 27 | public class Test 28 | { 29 | // 对象锁:形式1(方法锁) 30 | public synchronized void Method1() 31 | { 32 | System.out.println("我是对象锁也是方法锁"); 33 | try 34 | { 35 | Thread.sleep(500); 36 | } catch (InterruptedException e) 37 | { 38 | e.printStackTrace(); 39 | } 40 | 41 | } 42 | 43 | // 对象锁:形式2(代码块形式) 44 | public void Method2() 45 | { 46 | synchronized (this) 47 | { 48 | System.out.println("我是对象锁"); 49 | try 50 | { 51 | Thread.sleep(500); 52 | } catch (InterruptedException e) 53 | { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | } 59 | } 60 | ``` 61 | 62 | # 类锁(synchronized 修饰静态的方法或代码块) 63 | 64 | 65 |   由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都**只有一份**。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。   66 |    67 |   **对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。**  68 |    69 |   类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。  70 |   java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是[类名.class]的方式。 71 | 72 | ``` 73 | public class Test 74 | { 75 |    // 类锁:形式1 76 | public static synchronized void Method1() 77 | { 78 | System.out.println("我是类锁一号"); 79 | try 80 | { 81 | Thread.sleep(500); 82 | } catch (InterruptedException e) 83 | { 84 | e.printStackTrace(); 85 | } 86 | 87 | } 88 | 89 | // 类锁:形式2 90 | public void Method2() 91 | { 92 | synchronized (Test.class) 93 | { 94 | System.out.println("我是类锁二号"); 95 | try 96 | { 97 | Thread.sleep(500); 98 | } catch (InterruptedException e) 99 | { 100 | e.printStackTrace(); 101 | } 102 | 103 | } 104 | 105 | } 106 | } 107 | ``` -------------------------------------------------------------------------------- /java/[Java] 的四种引用,强弱软虚,用到的场景.md: -------------------------------------------------------------------------------- 1 | > 从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。 2 | 3 | 1 . 强引用(StrongReference) 4 | 5 | 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。只要你给强引用对象s赋空值null,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用。 6 | 7 | 2 . 弱引用(WeakReference) 8 | 9 | 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象(s),不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象,弱引用非常适合存储元数据。另一个使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。 10 | 11 | 如下代码创建弱引用: 12 | 13 | ``` 14 | Counter counter = new Counter(); // strong reference - line 1 15 | WeakReference weakCounter = new WeakReference(counter); //weak reference 16 | counter = null; // now Counter object is eligible for garbage collection 17 | ``` 18 | 3 . 软引用(SoftReference) 19 | 20 | 如果一个对象(如 s)只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 21 | 22 | 可以使用如下代码创建软引用: 23 | ``` 24 | Counter prime = new Counter(); // prime holds a strong reference 25 | SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2 26 | 27 | prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory 28 | ``` 29 | 30 | 31 | 4 . 虚引用(PhantomReference) 32 | 33 | "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 34 | 虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用 **必须** 和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。 35 | 36 | 如下代码创建虚引用: 37 | 38 | ``` 39 | DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference – line 3 40 | PhantomReference phantom = new PhantomReference(digit); // phantom reference to object created at line 3 41 | 42 | digit = null; 43 | ``` 44 | 45 | 弱引用、软引用**可以和**一个引用队列(ReferenceQueue)联合使用,如果其所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 46 | 47 | 在创建任何弱引用、软引用和虚引用的过程中你可以通过如下代码提供引用队列ReferenceQueue。 48 | 49 | ``` 50 | ReferenceQueue refQueue = new ReferenceQueue(); //reference will be stored in this queue for cleanup 51 | DigitalCounter digit = new DigitalCounter(); 52 | PhantomReference phantom = new PhantomReference(digit, refQueue); 53 | ``` -------------------------------------------------------------------------------- /java/[Java] 线程同步的方法:sychronized、lock、reentrantLock分析.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] 线程同步的方法:sychronized、lock、reentrantLock分析.md -------------------------------------------------------------------------------- /java/[Java] 集合框架的层次结构和使用规则梳理.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/java/[Java] 集合框架的层次结构和使用规则梳理.md -------------------------------------------------------------------------------- /java/[Java] 面向对象的三个特征与含义.md: -------------------------------------------------------------------------------- 1 | > 对象 2 | > 是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。 3 | > 4 | > 类 5 | > 是一个模板,它描述一类对象的行为和状态。 6 | 7 | 1 . **封装性** 8 | 9 |   将对象的状态信息尽可能的隐藏在对象内部,只保留有限的接口和方法与外界进行交互,从而避免了外界对对象内部属性的破坏。 10 | 11 |   Java中使用访问控制符来保护对类、变量、方法和构造方法的访问。 12 |   Java支持4种不同的访问权限。  13 |    14 |   默认的,也称为default,在同一包内可见,不使用任何修饰符。 15 |   私有的,以private修饰符指定,在同一类内可见。 16 |   共有的,以public修饰符指定,对所有类可见。 17 |   受保护的,以protected修饰符指定,对同一包内的类和所有子类可见。 18 | 19 | 2. **继承** 20 | 21 |    java通过继承创建分等级层次的类,可以理解为一个对象从另一个对象获取属性的过程。 22 |    23 |    类的继承是单一继承,也就是说,一个子类只能拥有一个父类 24 |    下面的做法是不合法的: 25 | 26 | ``` 27 | public class extends Animal, Mammal{} 28 | ``` 29 |   但是我们可以用多继承接口来实现, 如: 30 |    31 | 32 | ``` 33 | public class Apple extends Fruit implements Fruit1, Fruit2{} 34 | ``` 35 | 36 | 37 |   继承中最常使用的两个关键字是extends(用于基本类和抽象类)和implements(用于接口)。 38 |    39 |    注意:子类拥有父类所有的成员变量,但对于父类private的成员变量却没有访问权限,这保障了父类的封装性。 40 |    下面是使用关键字extends实现继承。 41 |    42 | 43 | ``` 44 | public class Animal{ 45 | } 46 | 47 | public class Mammal extends Animal{ 48 | } 49 | 50 | public class Reptile extends Animal{ 51 | } 52 | 53 | public class Dog extends Mammal{ 54 | } 55 | ``` 56 |   通过使用关键字extends,子类可以继承父类所有的方法和属性,但是无法使用 private(私有) 的方法和属性。 57 | 我们通过使用instanceof 操作符能够确定一个对象是另一个对象的一个分类。 58 | 59 | ``` 60 | public class Dog extends Mammal{ 61 | 62 | public static void main(String args[]){ 63 | 64 | Animal a = new Animal(); 65 | Mammal m = new Mammal(); 66 | Dog d = new Dog(); 67 | 68 | System.out.println(m instanceof Animal); 69 | System.out.println(d instanceof Mammal); 70 | System.out.println(d instanceof Animal); 71 | } 72 | } 73 | ``` 74 | 结果如下: 75 | 76 | ``` 77 | true 78 | true 79 | true 80 | ``` 81 | Implements关键字使用在类继承接口的情况下, 这种情况不能使用关键字extends。 82 | 83 | ``` 84 | public interface Animal {} 85 | 86 | public class Mammal implements Animal{ 87 | } 88 | 89 | public class Dog extends Mammal{ 90 | } 91 | ``` 92 | 可以使用 instanceof 运算符来检验Mammal和dog对象是否是Animal类的一个实例。 93 | 94 | ``` 95 | interface Animal{} 96 | 97 | class Mammal implements Animal{} 98 | 99 | public class Dog extends Mammal{ 100 | public static void main(String args[]){ 101 | 102 | Mammal m = new Mammal(); 103 | Dog d = new Dog(); 104 | 105 | System.out.println(m instanceof Animal); 106 | System.out.println(d instanceof Mammal); 107 | System.out.println(d instanceof Animal); 108 | } 109 | } 110 | ``` 111 | 运行结果如下: 112 | 113 | ``` 114 | true 115 | true 116 | true 117 | ``` 118 | 119 | 3.**多态** 120 | 121 |   多态是同一个行为具有多个不同表现形式或形态的能力。 122 |   多态性是**对象多种表现形式的体现** 123 |    124 |   比如:我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性。  125 | 126 | 例子 127 | ``` 128 | public interface Vegetarian{} 129 | public class Animal{} 130 | public class Deer extends Animal implements Vegetarian{} 131 | ``` 132 | 因为Deer类具有多重继承,所以它具有多态性。 133 | 访问一个对象的唯一方法就是通过引用型变量 (编译时变量)。 134 | 引用型变量只能有一种类型,一旦被声明,引用型变量的类型就不能被改变了。 135 | 引用型变量不仅能够被重置为其他对象,前提是这些对象没有被声明为final。还可以引用和它类型相同的或者相兼容的对象。它可以声明为类类型或者接口类型。 136 | 137 | ``` 138 | Deer d = new Deer(); 139 | Animal a = d; 140 | Vegetarian v = d; 141 | Object o = d; 142 | ``` 143 | 所有的引用型变量d,a,v,o都指向堆中相同的Deer对象。 144 | 145 | 我们来看下面这个例子: 146 | 147 | ``` 148 | public class Animal { 149 | public String name = "父类name"; 150 | public void move(){ 151 | System.out.println("父类move"); 152 | } 153 | public void content(){ 154 | System.out.println("父类content"); 155 | } 156 | } 157 | ``` 158 | 159 | ``` 160 | public class Bird extends Animal{ 161 | public String name = "子类name"; 162 | @Override 163 | public void move() { 164 | // TODO Auto-generated method stub 165 | System.out.println("子类move"); 166 | } 167 | public void content(){ 168 | System.out.println("子类content"); 169 | } 170 | 171 | } 172 | ``` 173 | 174 | ``` 175 | public class Test { 176 | public static void main(String[] args) { 177 | Animal a = new Animal(); 178 | System.out.println(a.name); 179 | a.move(); 180 | a.content(); 181 | 182 | System.out.println("----------------------"); 183 | 184 | Animal b = new Bird(); //向上转型由系统自动完成 185 | //编译时变量 运行时变量 186 | System.out.println(b.name); 187 | b.move(); 188 | b.content(); 189 | 190 | System.out.println("----------------------"); 191 | 192 | Bird c = new Bird(); 193 | System.out.println(c.name); 194 | c.move(); 195 | c.content(); 196 | } 197 | } 198 | ``` 199 | 运行结果: 200 | 201 | ``` 202 | 父类name 203 | 父类move 204 | 父类content 205 | ---------------------- 206 | 父类name 207 | 子类move 208 | 子类content 209 | ---------------------- 210 | 子类name 211 | 子类move 212 | 子类content 213 | 214 | ``` 215 | 说明:Bird类继承Animal并重写了其方法。 216 |    因为Animal b = new Bird(),编译时变量和运行时变量不一样,所以多态发生了。可以从最后的运行结果中看出,调用了**父类的成员变量name和子类重写后的两个方法**。 217 |    上面继承说了,子类可以调用父类所有非private的方法和属性。因为name是一个String的对象,**与方法不同,对象的域不具有多态性**。通过引用变量来访问其包含的实例变量时,系统总是视图访问它编译时类型所定义的变量,而不是他运行时类型所定义的变量。 218 | 219 |   那么问题来了,如果我们把Animal的成员变量换成private,那会不会去调用Bird类的成员变量name来打印输出呢? 220 |   ![这里写图片描述](http://img.blog.csdn.net/20160413153304267) 221 | 222 | 也就是说 **系统访问的始终是去访问编译时类型所定义的变量**。 223 |     224 | >   重写定义:子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变。即外壳不变,核心重写! 225 | 226 | -------------------------------------------------------------------------------- /java/[java] static的作用和意义.md: -------------------------------------------------------------------------------- 1 | # static 静态修饰符 2 | 3 |   在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的。static修饰符表示静态的,在类加载时Jvm会把它放到**方法区**,被本类以及本类的所有实例所共用。在编译后所分配的内存会**一直存在**,直到程序退出内存才会释放这个空间。如果一个被所有实例共用的方法被申明为static,那么就可以节省空间,不用每个实例初始化的时候都被分配到内存。 4 | 5 | > java类被加载过程 6 | 7 | >类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成: 8 | >①加载(以二进制形式来生成Class对象) 9 | >②链接(又分为验证、准备和解析) 10 | >    校验:检查导入类或接口的二进制数据的正确性; 11 | >    准备:给类的静态变量分配并初始化存储空间; 12 | >    解析:将符号引用转成直接引用; 13 | >③初始化(激活类的静态变量和静态代码块、初始化Java代码) 14 | 15 | 16 | ##静态变量 17 | 18 |   Static关键字修饰成员变量被称为静态变量(也叫作类变量,同时 局部变量也能被声明为static),静态变量在内存中只有一个拷贝(节省内存,方便对象之间共享值),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(当然也可以通过对象来访问)。因为静态变量被类的所有实例共用,所以非线程安全的。 19 | 20 |   未被Static修饰的成员变量叫作实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量在内存中可以有多个拷贝(但互相不影响,更加灵活)。 21 | 22 | ``` 23 | //静态变量的申明 24 | private static int count = 0; 25 | public static String str; 26 | ``` 27 | ## 静态方法 28 | 29 |   静态方法可以直接通过类名调用,任何的实例也都可以调用。  30 |   **只能**访问所属类的静态成员变量和成员方法,静态方法中也不能用this和super关键字。 31 |   类似于静态变量,静态方法也属于类,不属于实例的。 32 | 33 | ``` 34 | //静态方法的申明 35 | public static void s(int param) { 36 | ...... 37 | } 38 | ``` 39 | 40 | ## 静态代码块  41 | 42 |   静态代码块就是在类加载器加载对象时,要执行的一组语句。静态块只会在类加载到内存中的时候执行一次,位置可以随便放,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。 43 | 44 | ``` 45 | static{ 46 | //在类被加载的时候用于初始化资源,仅能访问静态变量和静态方法 47 | System.out.println("StaticExample static block"); 48 | } 49 | ``` 50 | 51 | ## 静态类 52 | 53 |   **只能在内部类中定义静态类**,静态内部类与外层类绑定,即使没有创建外层类的对象,它一样存在。 54 |   静态类的方法可以是静态的方法也可以是非静态的方法,静态的方法可以在外层通过静态类调用,而非静态的方法必须要创建类的对象之后才能调用。 55 |   只能引用外部类的static成员变量(也就是类变量)。 56 | 57 | 58 |   如果一个内部类不是被定义成静态内部类,那么在定义成员变量或者成员方法的时候,是不能够被定义成静态的。 59 |    60 | 61 | 62 | ``` 63 | public class OuterClass { 64 | public static class InnerClass{ 65 | InnerClass(){ 66 | System.out.println("静态内部类"); 67 | } 68 | } 69 | } 70 | ``` 71 | 72 |    -------------------------------------------------------------------------------- /java/[java] 多态实现的JVM调用过程.md: -------------------------------------------------------------------------------- 1 | > 多态 2 | > 3 | >同一个类的不同表现形态,不同的形态是通过其不同的子类体现 4 | >java通过将子类对象引用赋值给超类对象变量, 来实现动态方法调用。 5 | > 6 | >[面向对象的三个特征与含义](http://blog.csdn.net/amazing7/article/details/51219687) 7 | 8 | 下面看例子: 9 | 10 | ``` 11 | public class A{ 12 | public String name = "父类name"; 13 | public void move(){ 14 | System.out.println("父类move"); 15 | } 16 | } 17 | ``` 18 | 19 | ``` 20 | public class B extends A{ 21 | public String name = "子类name"; 22 | @Override 23 | public void move() { 24 | // TODO Auto-generated method stub 25 | System.out.println("子类move"); 26 | } 27 | } 28 | ``` 29 | 30 | ``` 31 | public class Test { 32 | public static void main(String[] args) { 33 | A a = new B(); 34 | a.move(); 35 | } 36 | } 37 | 38 | ``` 39 | 40 | **类B是类A的子类, A a = new B() 编译时变量和运行时变量不一样,所以多态发生了。** 41 | 42 | 43 | ① A a 作为一个引用类型数据,存储在JVM栈的**本地变量表**中。 44 | ② new B()作为**实例对象数据**存储在堆中 45 |    B的对象实例数据(接口、方法、field、对象类型等)的地址也存储在堆中 46 |    B的对象的类型数据(对象实例数据的地址所执行的数据)存储在方法区中,方法区中 对象类型数据 中有一个指向该类方法的**方法表**。 47 | 48 | ③Java虚拟机规范中并未对引用类型访问具体对象的方式做规定,目前主流的实现方式主要有两种: 49 | 50 |  1. 通过句柄访问 51 | ![这里写图片描述](http://img.blog.csdn.net/20160414163216948) 52 | 53 |   在这种方式中,JVM堆中会专门有一块区域用来作为句柄池,存储相关句柄所执行的实例数据地址(包括在堆中地址和在方法区中的地址)。这种实现方法由于用句柄表示地址,因此十分**稳定**。 54 | 55 | 2.通过直接指针访问 56 | ![这里写图片描述](http://img.blog.csdn.net/20160414163449343)  57 | 58 |   通过直接指针访问的方式中,reference中存储的就是对象在堆中的实际地址,在堆中存储的对象信息中包含了在方法区中的相应类型数据。这种方法最大的优势是速度快,在HotSpot虚拟机中用的就是这种方式。 59 | 60 | ④实现过程 61 | 62 |   首先虚拟机通过reference类型(A的引用)查询java栈中的 本地变量表,得到堆中的 对象类型数据的地址,从而找到方法区中的 对象类型数据(B的对象类型数据) ,然后查询方法表定位到实际类(B类)的方法运行。 -------------------------------------------------------------------------------- /java/git命令使用.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Commit(提交)**: 点击会出现如下界面,commit message输入你提交的信息,一般是做了什么修改和工作,项目所有者和提交者会自动生成。下面Files选择哪些你做了修改的文件进行提交(这里注意你的.gitigonre文件的配置,没有的话新建一个加入相应的过滤规则,一般来说.class文件是不用提交的,否则别人提交就会有冲突)。最后Commit and push 是把你这次提交到本地仓库同时推送到服务器上的仓库中,而Commit是将本地修改保存到本地仓库中。 5 | 6 | ![这里写图片描述](http://img.blog.csdn.net/20160411174207005) 7 | 8 | 9 | **stashes(储藏)**: 举个例,今天测试发现我昨天提交代码中有bug,然而我今天来后又做了些新功能,测试又让我马上修复bug,没办法只能改为去修复bug。那么stashes命令就派上用处了,它会把今天做的工作储藏到缓冲区,把工作目录和索引里的内容恢复到上次提交的状态,接下来我就可以修改bug了。修改完成后我把项目再提交一个版本给测试,然后我再找到我stashes的相应节点恢复我的工作目录,继续写我今天的东西。 10 | 11 | 12 | **push to Upstream(推送到远程分支)**: 先说明下upstream是什么,举个例,我在本地仓库中新建了一个分支a,把代码推送到远程仓库的分支b,那么分支b就是分支a的 upstream,反之分支a是分支b的downstream。 13 | 14 | 15 | **Fetch from Upstream(拉取远程仓库分支到本地)**: upstream上面说了,我们来说说fetch和pull的区别,都是从远程仓库拉取,fetch直接获取最新版本不做合并,而pull是获取最新版本并合并到本地。简单的说就是 pull = fetch + merge 。 16 | 17 | 18 | **push branch 'master'**(推送到master分支): 在 Git 的实现中每次提交都会保存一个提交对象,该对象包含0个或多个(第一次提交没有父对象)指向该对象的父对象指针,包含一个指向暂存内容的指针和本次提交的作者、时间等相关附属信息。Git会使用master作为分支的默认名称。 19 | 20 | 21 | **Pull**(拉取远程仓库分支到本地并与本地分支合并): 建议少用 Pull 多用 Fetch 和 Merge,pull把很多细节都隐藏起来了,不利于理解结构同时一旦出错也不容易找到出错的地方。 22 | 23 | 24 | **Remote/Push**(推送到远程代码库): remote命令用于管理远程主机, Tag指某个分支某个时间的特定状态,一般用于标记版本号。下面两个在远程代码库地址改变时需要配置。 25 | 26 | ![这里写图片描述](http://img.blog.csdn.net/20160411174320053) 27 | 28 | 29 | **merge**(合并): 将其他分支的内容合并到当前分支中。 30 | 31 | 32 | **rebase**(修改合并): 将一个分支修改合并到当前分支。这里说一下merge和rebase的区别:merge是直接合并分支,如果有冲突,完成后在一并解决。 rebase则是按时间线依次合并,如果产生冲突,会提示你进行处理,处理好了可以continue直到rebase完成,如果冲突处理不好可以放弃rebase或skip。 33 | 34 | 35 | **Reset** (回溯):可以将当前分支切换到本分支以前的任何一个版本状态。 36 | 37 | 38 | **patch**(补丁): 生成文件的对比信息,可以通过diff(apply)生成的标准patch,或使用format-patch生成的git专用patch。 39 | 40 | 41 | **Index**(暂存区): 是一个包含文件索引的目录树,记录了一些文件的信息,如文件名、状态、时间戳、长度等。当解决完git冲突时要把文件 add to index 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /java/wait()和sleep()的区别.md: -------------------------------------------------------------------------------- 1 |   这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。 2 | sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。 3 | 4 |   锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。   5 |    6 |   sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。 7 | Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。 8 | 9 |  使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。 10 | 11 | ``` 12 | synchronized(x){ 13 | x.notify() 14 | //或者wait() 15 | } 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /简历模板.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyoungboy/AndroidInterview/b1d70e816c265fbf358061f0a501dc2c675b064b/简历模板.docx --------------------------------------------------------------------------------