├── Android
├── Android 动画学习笔记
│ ├── Android动画之 Property Animation.md
│ ├── Android动画之Frame Animation.md
│ └── Android动画之Tween Animation.md
├── RxJava 学习笔记
│ ├── RxJava 学习笔记2.md
│ ├── RxJava 学习笔记3.md
│ └── 与RxJava 1.x的差异.md
├── 《Android 进阶之光》读书笔记
│ └── 函数响应式编程.md
├── 《Android开发艺术探索》笔记
│ ├── Activity的生命周期和启动模式.md
│ ├── Android 动画深入分析.md
│ ├── Android 的 Drawable.md
│ ├── Android 的消息机制.md
│ ├── Android 的线程和线程池.md
│ ├── Android性能优化.md
│ ├── Bitmap 的加载和 Cache.md
│ ├── IPC机制.md
│ ├── JNI 和 NDK 编程.md
│ ├── View 的工作原理.md
│ ├── View的事件体系.md
│ ├── 四大组件的工作过程.md
│ ├── 理解 RemoteViews.md
│ ├── 理解 Window 和 WindowManager.md
│ └── 综合技术.md
└── 笔记
│ ├── Android 中常见的内存泄漏.md
│ ├── Android 自定义 View 的知识点.md
│ ├── Android中的Theme.md
│ ├── Android面试题整理.md
│ ├── GreenDao 的简单使用.md
│ ├── Service小记.md
│ └── 单例总结.md
├── ES6笔记
├── ES6 声明变量的六种方法.md
├── const.md
├── let.md
├── 变量的解构赋值.md
└── 字符串的扩展.md
├── Git
└── Git.md
├── Java
├── Java基础知识.md
└── 《Java编程思想》笔记
│ ├── 第10章 内部类.md
│ ├── 第11章 持有对象.md
│ ├── 第12章 通过异常处理错误.md
│ ├── 第13章 字符串.md
│ ├── 第14章 类型信息.md
│ ├── 第15章 泛型.md
│ ├── 第16章 数组.md
│ ├── 第19章 枚举类型.md
│ ├── 第20章 注解.md
│ ├── 第4章 控制执行流程.md
│ ├── 第5章 初始化与清理.md
│ ├── 第6章 访问权限控制.md
│ ├── 第7章 复用类.md
│ ├── 第8章 多态.md
│ └── 第9章 接口.md
├── README.md
├── kotlin
└── kotlin 笔记.md
└── 微信小程序笔记
└── JSON 配置.md
/Android/Android 动画学习笔记/Android动画之 Property Animation.md:
--------------------------------------------------------------------------------
1 | # Android 动画之 Property Animation
2 |
3 | 属性动画是控制属性来实现动画。最为强大的动画,弥补了补间动画的缺点,实现位置+视觉的变化。并且可以自定义插值器,实现各种效果
4 |
5 | ## Property Animation相关类
6 | |类名|用途|
7 | |----|:-|
8 | |ValueAnimator|属性动画主要的计时器,也计算动画后的属性的值,动画的执行类|
9 | |ObjectAnimator|ValueAnimator的一个子类,允许你设置一个目标对象和对象的属性进行动画,动画的执行类|
10 | |AnimatorSet|提供组织动画的结构,使它们能相关联得运行,用于控制一组动画的执行|
11 | |AnimatorInflater|用户加载属性动画的xml文件|
12 | |Evaluators|属性动画计算器,告诉了属性动画系统如何计算给出属性的值|
13 | |Interpolators|动画插入器,定义动画的变化率|
14 |
15 | 关系如下:
16 |
17 | 
18 |
19 | ## 被定义在XML文件中
20 | 如果是被定义在 XML 中,需要注意是的属性动画文件存放目录为 res/animator,文件名可以作为资源 ID 在代码中引用,
21 |
22 | xml 代码:
23 |
24 | ````xml
25 |
26 |
28 |
37 |
45 |
46 |
47 | ...
48 |
49 |
50 | ````
51 | **注意:** XML 文件的根元素必须为 或者 , or 。你也可以在一个 set 中放置不同的动画,来嵌套其他元素。
52 |
53 | ### 元素介绍
54 | 动画集合节点,有个 ordering 属性,表示它的子动画启动方式是先后有序还是同时。
55 |
56 | |属性|说明|
57 | |-|-|
58 | |sequentially|动画按照先后顺序|
59 | |together(default)|动画同时启动|
60 |
61 | ###
62 | 属性:
63 |
64 | - android:propertyName:String类型,必须要设定的值,代表要执行动画的属性,通过名字引用,比如你可以指定了一个View的”alpha” 或者 backgroundColor”,这个objectAnimator元素没有暴露target属性,因此比不能够在XML中执行一个动画,必须通过调用loadAnimator() 填充你的XML动画资源,并且调用setTarget() 应用到拥有这个属性的目标对象上。
65 | - android:valueTo:Float、int或者color,也是必须值,表明了动画结束的点,颜色由6位十六进制的数字表示。
66 | - android:valueFrom:相对应valueTo,动画的起始点,如果没有指定,系统会通过属性身上的get方法获取,颜色也是6位十六进制的数字表示。
67 | - android:duration:动画的时长,int类型,以毫秒为单位,默认为300毫秒。
68 | - android:startOffset:动画延迟的时间,从调用start方法后开始计算,int型,毫秒为单位,
69 | - android:repeatCount:一个动画的重复次数,int型,”-1“表示无限循环,”1“表示动画在第一次执行完成后重复执行一次,也就是两次,默认为0,不重复执行。
70 | - android:repeatMode:重复模式:int型,当一个动画执行完的时候应该如何处理。该值必须是正数或者是-1,
71 | “reverse”会使得按照动画向相反的方向执行,可实现类似钟摆效果。“repeat”会使得动画每次都从头开始循环。
72 | - android:valueType:关键参数,如果该value是一个颜色,那么就不需要指定,因为动画框架会自动的处理颜色值。
73 | 有intType和floatType两种:分别说明动画值为int和float型。
74 |
75 | ###
76 | 在一个特定的时间里执行一个动画。相对应的是 ValueAnimator 所有的属性和一样
77 |
78 | - android:valueTo
79 | - android:valueFrom
80 | - android:duration
81 | - android:startOffset
82 | - android:repeatCount
83 | - android:repeatMode
84 | - android:valueType
85 |
86 | valueType 的值有两种:
87 |
88 | - intType
89 | - floatType(default)
90 |
91 | 举个例子
92 | ````xml
93 |
94 |
95 |
100 |
105 |
106 |
110 |
111 | ````
112 | 为了执行该动画,必须在代码中将该动画资源文件填充为一个AnimationSet对象,然后在执行动画前,为目标对象设置所有的动画集合。
113 | 简便的方法就是通过setTarget方法为目标对象设置动画集合,代码如下:
114 | ````java
115 | AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
116 | set.setTarget(myObject);
117 | set.start();
118 | ````
119 | ## 由编码实现
120 | ### ObjectAnimator 对象
121 | 对于java代码实现,ObjectAnimator 提供了以下几个方法:ofFloat(),ofInt(),ofObject(),ofArgb(),ofPropertyValuesHolder()这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。
122 | 举个例子:
123 |
124 | 以渐变效果为例:
125 | ````java
126 |
127 | ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);
128 | objectAnimator.setDuration(500);
129 | objectAnimator.setRepeatCount(1);
130 | objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
131 | objectAnimator.start();
132 | ````
133 |
134 | ### ValueAnimator(差值动画)
135 | ValueAnimator是ObjectAnimator的父类,它继承自抽象类Animator,它作用于一个值,将其由一个值变化为另外一个值,然后根据值的变化,按照一定的规则,动态修改View的属性,比如View的位置、透明度、旋转角度、大小等,即可完成了动画的效果。
136 |
137 | 示例:
138 | ````java
139 | ValueAnimator valueAnimator =ValueAnimator.ofFloat( 0f, 126512.36f);
140 | valueAnimator.setDuration(2000);
141 | valueAnimator.setInterpolator(new LinearInterpolator());
142 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
143 | @Override
144 | public void onAnimationUpdate(ValueAnimator animation) {
145 | float money= (float) animation.getAnimatedValue();
146 | mTextView.setText(String.format("%.2f", money));
147 | }
148 | });
149 | valueAnimator.start();
150 | ````
151 | 这里通过ofFloat()方法构造一个ValueAnimator实例,除此之外还提供了其他函数ofInt()、ofObject()、ofPropertyValuesHolder()函数,api 21之后又提供了ofArgb(),每个函数都是可以传入多个改变值。
152 |
153 | ## 实现一个组合动画
154 | 举例我们同时对一个控件进行宽高两个维度的缩放
155 |
156 | 方式一:使用 AnimatorSet
157 |
158 | ````xml
159 |
160 |
162 |
170 |
178 |
179 |
180 | ````
181 | 加载xml动画
182 | ````java
183 | Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator_scale);
184 | anim.setTarget(imageView);
185 | anim.start();
186 | ````
187 | 纯Java代码实现:
188 | ````java
189 | AnimatorSet animatorSet = new AnimatorSet();
190 |
191 | ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 1.5f);
192 | scaleXAnimator.setDuration(500);
193 | scaleXAnimator.setRepeatCount(1);
194 | scaleXAnimator.setRepeatMode(ValueAnimator.REVERSE);
195 | scaleXAnimator.start();
196 |
197 | ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 1.5f);
198 | scaleYAnimator.setDuration(500);
199 | scaleYAnimator.setRepeatCount(1);
200 | scaleYAnimator.setRepeatMode(ValueAnimator.REVERSE);
201 |
202 | animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
203 | animatorSet.start();
204 | ````
205 | 上述代码通过playTogether函数实现两个动画同时执行,如果不想同时执行,也可以调用play函数返回AnimatorSet.Builder实例,AnimatorSet.Builder提供了如下几个函数用于实现动画组合:
206 |
207 | - after(Animator anim) 将现有动画插入到传入的动画之后执行
208 | - after(long delay) 将现有动画延迟指定毫秒后执行
209 | - before(Animator anim) 将现有动画插入到传入的动画之前执行
210 | - with(Animator anim) 将现有动画和传入的动画同时执行
211 |
212 | 也可以调用playSequentially函数实现分布执行动画。
213 |
214 |
215 | 方式二:使用PropertyValuesHolder
216 |
217 | ````java
218 | PropertyValuesHolder scaleXValuesHolder = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
219 | PropertyValuesHolder scaleYValuesHolder = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 1.5f);
220 | ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, scaleXValuesHolder, scaleYValuesHolder);
221 | objectAnimator.setDuration(500);
222 | objectAnimator.setRepeatCount(1);
223 | objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
224 | objectAnimator.start();
225 |
226 | ````
227 | 通过这种方式只能实现同时执行的动画组合相比AnimatorSet就没那么丰富了,PropertyValuesHolder 提供的函数方法有如下几种:ofInt()、ofFloat()、ofObject()、ofKeyframe()。
228 |
229 | 方式三:使用ViewPropertyAnimator
230 | ````java
231 | ViewPropertyAnimator viewPropertyAnimator=imageView.animate();
232 | viewPropertyAnimator.scaleXBy(1.0f).scaleX(1.5f).scaleYBy(1.0f).scaleY(1.5f).setDuration(500).start();
233 | ````
234 | 多属性动画,作用于View,能够实现的动画相对单一,只能实现比如缩放,透明度改变,平移、旋转等,具体函数名字:平移 translationX,translationY, X,Y,缩放 scaleX,scaleY, 旋转 rotationX, rotationY,透明度 alpha
235 |
236 |
237 | ## 设置动画监听器
238 | 有时候我们可能要在某一个动画执行之前 或者动画结束之后进行一些其他的操作,这个时候就要借助动画监听器了。
239 |
240 | ````java
241 | objectAnimator.addListener(new Animator.AnimatorListener() {
242 | @Override
243 | public void onAnimationStart(Animator animation) {
244 | //TODO 动画开始前的操作
245 | }
246 |
247 | @Override
248 | public void onAnimationEnd(Animator animation) {
249 | //TODO 动画结束的操作
250 | }
251 |
252 | @Override
253 | public void onAnimationCancel(Animator animation) {
254 | //TODO 动画取消的操作
255 | }
256 |
257 | @Override
258 | public void onAnimationRepeat(Animator animation) {
259 | //TODO 动画重复的操作
260 | }
261 | });
262 | ````
263 | 如果我们需要简单动画执行过程中的变化可以使用AnimatorUpdateListener
264 | ````java
265 | objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
266 | @Override
267 | public void onAnimationUpdate(ValueAnimator animation) {
268 | float value = (float) animation.getAnimatedValue();
269 | //可以根据自己的需要来获取动画更新值。
270 | Log.e("AnimatorUpdateListener", "the animation value is " + value);
271 | }
272 | });
273 | ````
274 |
--------------------------------------------------------------------------------
/Android/Android 动画学习笔记/Android动画之Frame Animation.md:
--------------------------------------------------------------------------------
1 | # Android 动画之 Frame Animation
2 | Frame动画是一系列图片按照一定的顺序展示的过程,和放电影的机制很相似,我们称为逐帧动画。Frame动画可以被定义在XML文件中,也可以完全编码实现。
3 |
4 | ## 被定义在XML文件中
5 | 如果是被定义在 XML 中,可以放置在 /res 下的 anim 或 drawable 目录中 (/res/[anim|drawable]/filename.xml),文件名可以作为资源 ID 在代码中引用,
6 | 语法如下:
7 | ````xml
8 |
9 |
11 |
14 |
15 | ````
16 | **注意:**
17 |
18 | 元素是必须的,并且必须作为根元素,可以包含一个或多个 - 元素;android:onshot 如果定义为 true 的话,此动画只会执行一次,如果为 false 则一直循环。
19 |
20 |
- 代表一帧动画,android:drawable 指定此帧动画所对应的图片资源,android:duration 代表此帧持续的时间,整数,单位为毫秒。
21 |
22 | **问题:**
23 |
24 | 为什么在 onCreate() 方法中执行动画会失败?
25 | > 这种现象是因为当我们在 onCreate 中调用 AnimationDrawable 的 start 方法时,窗口 Window 对象还没有完全初始化,AnimationDrawable 不能完全追加到窗口 Window 对象中,那么该怎么办呢?我们需要把这段代码放在 onWindowFocusChanged 方法中,当 Activity 展示给用户时,onWindowFocusChanged 方法就会被调用,我们正是在这个时候实现我们的动画效果。当然,onWindowFocusChanged 是在 onCreate 之后被调用的。
26 |
27 |
28 |
29 |
30 |
31 |
32 | ## 由编码实现
33 | 如果完全由编码实现,我们需要用到 AnimationDrawable 对象。
--------------------------------------------------------------------------------
/Android/Android 动画学习笔记/Android动画之Tween Animation.md:
--------------------------------------------------------------------------------
1 | # Android 动画之 Tween Animation
2 | Tween动画是操作某个控件让其展现出旋转、渐变、移动、缩放的这么一种转换过程,我们成为补间动画。我们可以以XML形式定义动画,也可以编码实现。
3 | ## 以XML形式定义动画
4 | 如果以XML形式定义一个动画,我们按照动画的定义语法完成XML,并放置于/res/anim目录下,文件名可以作为资源ID被引用;
5 | ````xml
6 |
7 |
10 |
13 |
20 |
25 |
30 |
31 | ...
32 |
33 |
34 | ````
35 | xml 文件中必须有一个根元素,可以是、、、中的任意一个,也可以来管理一个由前面几个组成的动画的集合。
36 |
37 | ###
38 | 是一个动画容器,管理多个动画的群组,与之相对性的 Java 对象是 AnimationSet。有两个属性:
39 |
40 | - android:interpolator ---- 代表一个插值器资源,可以引用系统自带查之前资源,也可以用自定义插值器资源,默认值是匀速插值器。
41 | - android:shareInterpolator ---- 代表多个动画是否要共享插值器,默认值为 true,即共享插值器;如果设置为 false,那么插值器就不再起作用,需要在每个动画中加入插值器。
42 |
43 | ###
44 | 是渐变动画,可以实现 fadeIn 和 fadeOut 的效果,与之对应的 Java 对象是 AlphaAnimation。
45 |
46 | - android:fromAlpha 属性代表起始 alpha 值,浮点值,范围在 0.0-1.0 之间,分别代表透明和完全不透明。
47 | - android:toAlpha 属性代表结尾 alpha 值,浮点值,范围在 0.0-1.0 之间。
48 |
49 | ###
50 | 是缩放动画,可以实现动态调整控件尺寸的效果,与之对应的 Java 对象是 ScaleAnimation。
51 |
52 | - android:fromXScale 属性代表起始的 X 方向上相对自身的缩放比例,浮点值。1.0代表自身无变化,0.5代表起始时缩小一倍,2.0代表放大一倍。
53 | - android:toXScale 属性代表结尾的X方向上相对自身的缩放比例,浮点值。
54 | - android:fromYScale 属性代表起始的Y方向上相对自身的缩放比例,浮点值。
55 | - android:toYScale 属性代表结尾的Y方向上相对自身的缩放比例,浮点值。
56 | - android:pivotX 属性代表缩放的中轴点X坐标,浮点值。
57 | - android:pivotY 属性代表缩放的中轴点Y坐标,浮点值,
58 |
59 | 如果我们想表示中轴点为图像的中心,我们可以把 **android:pivotX** 和 **android:pivotY** 两个属性值定义成0.5或者50%。
60 |
61 | ###
62 | 是位移动画,代表一个水平、垂直的位移。
63 |
64 | - android:fromXDelta 属性代表起始X方向的位置;
65 | - android:toXDelta 代表结尾X方向上的位置;
66 | - android:fromYScale 属性代表起始Y方向上的位置;
67 | - android:toYDelta 属性代表结尾Y方向上的位置。
68 |
69 | 以上四个属性都支持三种表示方式:浮点数、num%、num%p;
70 |
71 | - 如果以浮点数字表示,代表相对自身原始位置的像素值;
72 | - 如果以 num% 表示,代表相对于自己的百分比,比如 toXDelta 定义为100%就表示在 X 方向上移动自己的1倍距离;
73 | - 如果以 num%p 表示,代表相对于父类组件的百分比。
74 |
75 | ###
76 | 是旋转动画,与之对应的 Java 对象是 RotateAnimation。
77 |
78 | - android:fromDegrees 属性代表起始角度,浮点值,单位:度;
79 | - android:toDegrees 属性代表结尾角度,浮点值,单位:度;
80 | - android:pivotX 属性代表旋转中心的X坐标值,
81 | - android:pivotY 属性代表旋转中心的Y坐标值,
82 |
83 | 这两个属性也有三种表示方式:
84 |
85 | - 数字方式代表相对于自身左边缘的像素值,
86 | - num% 方式代表相对于自身左边缘或顶边缘的百分比,
87 | - num%p 方式代表相对于父容器的左边缘或顶边缘的百分比。
88 |
89 | 另外,在动画中,如果我们添加了android:fillAfter="true"后,这个动画执行完之后保持最后的状态;android:duration="integer"代表动画持续的时间,单位为毫秒。
90 |
91 | 如果要把定义在XML中的动画应用在一个ImageView上,代码是这样的:
92 | ````xml
93 | ImageView image = (ImageView) findViewById(R.id.image);
94 | Animation testAnim = AnimationUtils.loadAnimation(this, R.anim.test);
95 | image.startAnimation(testAnim);
96 | ````
97 |
98 | ### 插值器
99 | 在补间动画中,我们一般只定义关键帧(首帧或尾帧),然后由系统自动生成中间帧,生成中间帧的这个过程可以成为“插值”。插值器定义了动画变化的速率,提供不同的函数定义变化值相对于时间的变化规则,可以定义各种各样的非线性变化函数,比如加速、减速等。下面是几种常见的插值器:
100 |
101 | |Interpolator对象|资源ID|功能作用|
102 | |--|:-:|:-:|
103 | |AccelerateDecelerateInterpolator|@android:anim/accelerate_decelerate_interpolator| 先加速再减速|
104 | |AccelerateInterpolator|@android:anim/accelerate_interpolator|加速|
105 | AnticipateInterpolator|@android:anim/anticipate_interpolator|先回退一小步然后加速前进|
106 | AnticipateOvershootInterpolator|@android:anim/anticipate_overshoot_interpolator|在上一个基础上超出终点一小步再回到终点
107 | |BounceInterpolator|@android:anim/bounce_interpolator最后阶段弹球效果|
108 | |CycleInterpolator|@android:anim/cycle_interpolator|周期运动|
109 | |DecelerateInterpolator|@android:anim/decelerate_interpolator|减速|
110 | |LinearInterpolator|@android:anim/linear_interpolator匀速|
111 | |OvershootInterpolator|@android:anim/overshoot_interpolator|快速到达终点并超出一小步最后回到终点|
112 |
113 | #### 使用插值器:
114 | ````xml
115 |
116 | ...
117 |
118 | ````
119 |
120 | ````xml
121 |
123 | ````
124 |
125 | #### 修改插值器
126 | 有时候会不满足现有的插值器,就可以试试个性化插值器。我们可以创建 XML 资源文件,然后将其放于 /res/anim 下,然后在动画元素中引用。
127 | 几种常见的插值器可调整的属性:
128 |
129 | - 无;
130 | - android:factor 浮点值,加速速率,默认为1;
131 | - android:tension 浮点值,起始点后退的张力、拉力数,默认为2;
132 | - android:tension 同上 android:extraTension浮点值,拉力的倍数,默认为1.5(2 * 1.5);
133 | - 无;
134 | - android:cycles 整数值,循环的个数,默认为1;
135 | - android:factor 浮点值,减速的速率,默认为1;
136 | - 无;
137 | - 浮点值,超出终点后的张力、拉力,默认为2;
138 |
139 | 举个例子:
140 | ````xml
141 |
142 |
144 | ````
145 | 然后引用;
146 | ````xml
147 |
150 | ````
151 |
152 | 我们也可以实现 Interpolator 接口,因为所有的 Interpolator 都实现了 Interpolator 接口,这个接口定义了一个方法:
153 | > float getInterpolation(float input);
154 |
155 | 此方法由系统调用,input 代表动画的时间,在0和1之间,也就是开始和结束之间。
156 | 举个例子:
157 |
158 | 性(匀速)插值器定义如下:
159 | ````java
160 | public float getInterpolation(float input) {
161 | return input;
162 | }
163 | ````
164 | 加速减速插值器定义如下:
165 | ````java
166 | public float getInterpolation(float input) {
167 | return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
168 | }
169 |
170 | ````
171 |
172 |
173 | ## 由编码实现动画
174 | 如果由编码实现,我们需要使用到 Animation 对象。与之对应的Java对象是TranslateAnimation。
175 |
--------------------------------------------------------------------------------
/Android/RxJava 学习笔记/RxJava 学习笔记2.md:
--------------------------------------------------------------------------------
1 | # RxJava 学习笔记2
2 | Observable : 被观察者。
3 | Observer : 观察者;有时也叫做 Subscriber、Watcher、Reactor。
4 |
5 | ## 异步模型中的流程
6 | 1. 定义一个方法,它完成某些任务,然后从异步调用中返回一个值,这个方法是观察者的一部份
7 | 2. 将这个异步调用本身定义为一个 Observable
8 | 3. 观察者通过订阅(Subscribe)操作关联到那个 Observable
9 | 4. 继续你的业务逻辑,等方法返回时,Observable 会发射结果,观察者的方法会开始处理结果或结果集
10 |
11 | ````
12 | def myOnNext = {it -> do something useful with it};
13 |
14 | def myObservable = someObservable(itsParameters);
15 |
16 | myObservable.subscribe(myOnNext);
17 | ````
18 |
19 | ## 回调方法(onNext,onCompleted,onError)
20 | Subscribe 方法用于将观察者连接到 Observable,你的观察者需要实现以下方法的一个子集:
21 |
22 | - onNext(T item)
23 | Observable 调用这个方法发射数据,方法的参数就是 Observable 发射的数据,这个方法可能会被调用多次。
24 | - onError(Exception ex)
25 | 当 Observable 遇到错误或者无法返回期望的数据时会调用这个方法,这个调用会终止 Observable,后续不会再调用 onNext 和 onCompleted,onError 方法的参数是抛出的异常。
26 | - onComplete
27 | 正常终止,如果没有遇到错误,Observable 在最后哦一次调用 onNext 之后调用此方法。
28 | 根据 Observable 协议的定义,onNext 可能会被调用零次或者很多次,最后会有一次 onCompleted 或者 onError 调用(不会同时),传递数据给 onNext 通常被称作发射,onCompleted 和 onError 被称作通知。
29 | ````
30 | def myOnNext = {item -> /* do something useful with item */};
31 |
32 | def myError = {throwable -> /*react sensibly to a failed call*/};
33 |
34 | def myComplete = {/* clean up after the final response */};
35 |
36 | def myObserable = someMethod(itsParameters);
37 |
38 | myObservable.subscribe(myOnNext,myError,myComplete);
39 | ````
40 |
41 | ## 取消订阅(Unsubscribing)
42 | 取消订阅的结果会传递这个 Observable 的操作符链,而且会导致这个链条上的每个环节都停止发射数据项。这些并不保证会立即发生,然而,对一个 Observable 来说,及时没有观察者了,它也可以在一个 while 循环中继续生产并尝试发射数据项。
43 |
44 | ## 操作符
45 | 1. 创建操作:Create , Defer , Empty/Never/Throw , From , Interval , Just , Range , Repeat , Start , Timer
46 | 2. 变换操作: Buffer , FlatMap , GroupBy , Map , Scan , Window
47 | 3. 过滤操作: Debounce , Distinct , ElementAt , Filter , IgnoreElements , Last , Sample , Skip , SkipLast , Take , TakeLast
48 | 4. 组合操作: And/Then/When , CombineLatest , Join , Merge , StartWith , Switch , Zip
49 | 5. 错误处理: Catch , Retry
50 | 6. 辅助操作: Delay , Do , Materialize/Dematerialize , ObserbeOn , Serialize , Subscribe , SubscribeOn , TimeInterval , Timeout , Timestamp , Using
51 | 7. 条件和布尔操作: All , Amb , Contains , DefaultlfEmpty , SequeneceEqual , SkipUntil , SkipWhile , TakeUntil , TakeWhile
52 | 8. 算术和集合操作: Average , Concat , Count , Max , Min , Reduce , Sum
53 | 9. 转换操作: To
54 | 10. 连接操作: Connect , Publish , RefCount , Replay
55 | 11. 反压操作: 用于增加特殊的流程控制策略的操作符。
56 |
57 | ## RxJava
58 | 在 RxJava 中,一个实现了 Observable 接口的对象可以订阅(subscribe) 一个 Observable 类的实例,订阅者(subscriber)对 Observable 发射(emit)的任何数据或者序列作出响应,这种模式简化了并发操作,因为它不需要阻塞等待 Observable 发射数据,而是创建一个处于待命状态的观察者哨兵,哨兵在未来某个时刻响应 Observable 的通知。
--------------------------------------------------------------------------------
/Android/RxJava 学习笔记/RxJava 学习笔记3.md:
--------------------------------------------------------------------------------
1 | # RxJava 学习笔记3
2 |
3 | ## Scheduler(调度器)
4 | 如果想给 Observable 操作符链添加多线程功能,可以指定操作符(或者特定的 Observable) 在特定的调度器(Scheduler) 上执行。
5 |
6 | 使用 ObserveOn 和 SubscribeOn 操作符,你可以让 Observable 在一个特定的调度器上执行,ObserveOn 指示一个 Observable 在一个特定的调度器上调用观察者的 onNext,onError 和 onCompleted 方法,SubscribeOn 更进一步,它指示 Observable 将全部的处理过程(包括发射数据和通知)放在特定的调度器上执行。
7 |
8 | ### 调度器的种类
9 | |调度器类型|效果|
10 | |:-------- |:--|
11 | |Schedulers.computation()|用于计算任务,如事件循环或回调处理,不要用于 IO 操作(IO 操作请使用 Schedulers.io());默认线程数等于处理器的数量|
12 | |Schedulers.from(executor)|使用指定的 Executor 作为调度器|
13 | |Schedulers.immediate()|在当前线程立即开始执行任务|
14 | |Schedulers.io()|用于 IO 密集型任务,如异步阻塞 IO 操作,这个调度器的线程池会根据需要增长;对于普通的计算任务,请使用 Schedulers.computation();Schedulers.io() 默认是一个 CachedThreadScheduler,很像一个有线程缓存的新线程调度器|
15 | |Schedulers.newThread()|为每个任务创建一个新线程|
16 | |Schedulers.trampoline()|当其它排队的任务完成后,在当前线程排队开始执行|
--------------------------------------------------------------------------------
/Android/RxJava 学习笔记/与RxJava 1.x的差异.md:
--------------------------------------------------------------------------------
1 |
2 | - Nulls
3 | 这是一个很大的变化,RxJava 1.x 是允许我们在发射事件的时候传入 null 值的,但现在我们的 2.x 不支持了,这意味着 Observable 不再发射任何值,而是正常结束或者抛出空指针。
4 | - Flowable
5 | 在 RxJava 1.x 中关于介绍 backpressure 部分有一个小小的遗憾,那就是没有用一个单独的类,而是使用 Observable 。而在 2.x 中 Observable 不支持背压了,将用一个全新的 Flowable 来支持背压。
6 | 或许对于背压,有些小伙伴们还不是特别理解,这里简单说一下。大概就是指在异步场景中,被观察者发送事件的速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。感兴趣的小伙伴可以模拟这种情况,在差距太大的时候,我们的内存会猛增,直到OOM。而我们的 Flowable 一定意义上可以解决这样的问题,但其实并不能完全解决,这个后面可能会提到。
7 | - Single/Completable/Maybe
8 | 其实这三者都差不多,Single 顾名思义,只能发送一个事件,和 Observable接受可变参数完全不同。而 Completable 侧重于观察结果,而 Maybe 是上面两种的结合体。也就是说,当你只想要某个事件的结果(true or false)的时候,你可以使用这种观察者模式。
9 | - 线程调度相关
10 | 这一块基本没什么改动,但 RxJava 2.x 中已经没有了 Schedulers.immediate() 这个线程环境,还有 Schedulers.test()。
11 | - Function相关
12 | 熟悉 1.x 的小伙伴一定都知道,我们在1.x 中是有 Func1,Func2.....FuncN的,但 2.x 中将它们移除,而采用 Function 替换了 Func1,采用 BiFunction 替换了 Func 2.....FuncN。并且,它们都增加了 throws Exception,也就是说,妈妈再也不用担心我们做某些操作还需要 try-catch 了。
13 | - 其他操作符相关
14 | 如 Func1...N 的变化,现在同样用 Consumer 和 BiConsumer 对 Action1 和 Action2 进行了替换。后面的 Action 都被替换了,只保留了 ActionN。
15 |
16 | 
17 | 
18 | 
19 | 
20 | 
21 | 
22 | 
23 | 
24 |
25 |
--------------------------------------------------------------------------------
/Android/《Android 进阶之光》读书笔记/函数响应式编程.md:
--------------------------------------------------------------------------------
1 | # 函数响应式编程
2 | >这本书里使用的是RxJava 1。
3 |
4 | 函数式编程是一种编程范式;响应式编程是一种面向数据流和变化传播的编程范式,数据更新是相关联的。
5 | ## RxJava 基本用法
6 | 1. RxJava 概述
7 | 基本用法
8 | - 创建 Observer(观察者)
9 | ````java
10 | Subscriber subscriber = new Subscriber{
11 |
12 | @Override
13 | public void onCompleted(){
14 | Log.d(TAG,"onCompleted");
15 | }
16 |
17 | @Override
18 | public void onError(Throwable e){
19 | Log.d(TAG,"onError");
20 | }
21 |
22 | @Override
23 | public void onNext(String s){
24 | Log.d(TAG,"onNext");
25 | }
26 |
27 | @Override
28 | public void onStart(){
29 | Log.d(TAG,"onStart");
30 | }
31 | }
32 | ````
33 | 其中,onCompleted、onError 和 onNext 是必须要实现的方法。
34 | - onCompleted:
35 | 事件队列完结。当不会有新的 onNext 发出时,需要触发 onCompleted() 方法作为完成标志。
36 | - onError:
37 | 事件队列异常。在事件处理过程中出现异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。
38 | - onNext:
39 | 普通的事件。将要处理的事件添加到事件队列中。
40 | - onStart:
41 | 会在事件还未发送之前被调用,可以用于一些准备工作,如数据的清零或重置。
42 | - 创建 Observable(被观察者)
43 | ````java
44 | Observable observable = Observable.create(new Observable.onSubscrible(){
45 | @Override
46 | public void call(Subscriber super String > subscriber){
47 | subscriber.onNext("杨影枫");
48 | subscriber.onNext("月眉儿");
49 | subscriber.onCompleted();
50 | }
51 | });
52 | ````
53 | 也可以用 from 来实现
54 | ````java
55 | String[] words = {"杨影枫","月眉儿"};
56 | Observable observable = Observable.from(words);
57 | ````
58 | - Subscribe(订阅)
59 | ````java
60 | observable.subscribe(subscriber);
61 | ````
62 |
63 | 2. RxJava 的不完整定义回调
64 | Action 后的数字代表回调的参数类型数量
65 |
66 | ````java
67 | Action1 onNextAction = new Action1(){
68 | @Override
69 | public void call(String s){
70 | Log.d(TAG,"onCompleted");
71 | }
72 | };
73 |
74 | Action1 onErrorAction = new Action1(){
75 | @Override
76 | public void call(Throwable throwable){
77 |
78 | }
79 | };
80 |
81 | Action0 onCompletedAction = new Action0(){
82 | @Override
83 | public void call(){
84 | Log.d(TAG,"onNext" + s);
85 | }
86 | };
87 |
88 | observable.subscribe(onNextAction,onErrorAction,onCompleteAction);
89 | ````
90 | 定义 onNextAction 来处理 onNext 回调,定义 onErrorAction 来处理 onError 的回调,定义 onCompletedAction 来处理 onCompleted 的回调,最后传给 subscribe 方法。
91 |
92 | ## RxJava 的Subject
93 | 1. Subject 既可以是一个 Observer,也可以是一个 Observable,是连接着 Observer 和 Observable 的桥梁。Subject = Observable + Observer。
94 | 2. RxJava 提供了4种 Subject:
95 | - PublishSubject
96 | PublishSubject 只会把在订阅发生时间点之后来自原始 Observable 的数据发射给观察者。PublishSubject 可能会一创建完成就立刻发射数据,因此有在 PublishSubject 被创建后到有观察者订阅它之前这个时间段内,一个或多个数据可能会丢失的风险。
97 | - BehaviorSubject
98 | 当 Observer 订阅 BehaviorSubject 时,它开始发射原始 Observable 最近发射的数据。还没有收到数据则会发射一个默认值,然后继续发射来自其他任何来自原始 Observable 的数据。如果原始的 Observable 发生一个错误而终止,BehaviorSubject 将不会发射任何数据,但会向 Observer 传递一个异常通知。
99 | - ReplaySubject
100 | 不管 Observer 何时订阅 ReplaySubject,ReplaySubject 均会发射所有来自原始 Observable 的数据给 Observer。
101 | - AsyncSubject
102 | 当 Observer 完成时,AsyncSubject 只会发射来自原始 Observable 的最后一个数据。如果原始的 Observable 发生一个错误而终止,AsyncSubject 将不会发射任何数据,但会向 Observer 传递一个异常通知。
103 |
104 | ## RxJava 操作符入门
105 | 1. 创建操作符
106 | - interval:创建一个固定时间间隔发射整数序列的 Observable,相当于定时器。
107 | ````java
108 | Observable.interval(3,TimeUnit.SECONDS)
109 | .subscribe(new Action1(){
110 | @Override
111 | public void call(Long mLong){
112 | Log.d(TAG,"INTERVAL:" + mLong.intValue());
113 | }
114 | });
115 | ````
116 | - range:创建发射指定范围的整数序列的 Observable,可以代替 for 循环,发射一个范围内的有序整数序列。第一个参数是起始值,并且不小于0,第二个参数为终值,左闭右开。
117 | ````java
118 | Observable.range(0,5)
119 | .subscribe(new Action1(){
120 | @Override
121 | public void call(Integer integer){
122 | Log.d(TAG,"range:" + integer.intValue());
123 | }
124 | });
125 | ````
126 | - repeat:创建一个 N 次重复发射特定数据的 Observable。
127 | ````java
128 | Observable.range(0,3)
129 | .repeat(2)
130 | .subscribe(new Action1(){
131 | @Override
132 | public void call(Integer integer){
133 | Log.d(TAG,"repeat:" + integer.intValue());
134 | }
135 | });
136 | ````
137 |
138 | 2. 变换操作符
139 | - map:能通过指定一个 Func 对象,将 Observable 转换为一个新的 Observable 对象并发射,观察者将收到新的 Observable 处理。
140 | - flatMap、cast:
141 | + flatMap 操作符将 Observable 发射的数据集合变换为 Observable 集合,然后将这些 Observable 发射的数据平坦化地放进一个单独的 Observable。
142 | + cast 操作符的作用是强制将 Observable 发射的所有数据转换为指定类型。
143 | - concatMap:与 flatMap 的功能一致,解决了 flatMap 交叉问题,提供了一种能够把发射的值连续在一起的函数,而不是合并。
144 | - flatMapIterable:将数据包装成 Interable。
145 | - buffer:将源 Observable 变换为一个新的 Obserable,这个新的 Obserable 每次发射一组列表值而不是一个一个发射。
146 | - groupBy:用于分组元素,将源 Observable 变换成一个发射 Observable 的新 Observable(分组后)。它们中的每一组新 Observable 都发射一组指定的数据。
147 |
148 | 3. 过滤操作符
149 | - filter:是对源 Observable 产生的结果自定义规则进行过滤,只有满足条件的结果才会提交给订阅者。
150 | - elementAt:用来返回指定位置的数据。
151 | - distinct:用来去重,只允许还没有发射过的数据项通过。
152 | - skip、take:
153 | + skip 操作符将源 Observable 发射的数据过滤掉前 n 项;
154 | + take 操作符只取前 n 项。
155 | - ignoreElements:忽略所有源 Observable 产生的结果,只把 Observable 的 onCompleted 和 onError 事件通知给订阅者。
156 | - throttleFirst:会定期发射这个时间段里源 Observable 发射的第一个数据。默认在 computation 调度器上执行。
157 | - throttleWithTimeOut:通过时间来限流,源 Observable 每次发射出来一个数据后就会进行计时。
158 |
159 | 4. 组合操作符
160 | - startWith:会在源 Observable 发射的数据前面插上一些数据。
161 | - merge:将多个 Observable 合并到一个 Observable 中进行发射,可能会让合并的 Observable 发射的数据交错。
162 | - concat:将多个 Observable 发射的数据进行合并发射,concat 严格按照顺序发射数据。
163 | - zip:合并两个或者多个 Observable 发射出的数据项,根据指定的函数变换它们,并发射一个新值。
164 | - combineLastest:当两个 Observable 中的任何一个发射了数据时,使用一个函数结合每个 Observable 发射的最近数据项,并且基于这个函数的结果发射数据。
165 |
166 | 5. 辅助操作符
167 | - delay:让原始 Observable 在发射每项数据之前都暂停一段指定的时间段。
168 | - Do:Do 系列操作符就是为原始 Observable 的生命周期事件注册一个回调,当 Observable 的某个事件发生时就会调用这些回调。
169 | + doOnEach:当 Observable 每次发射一项数据时就好调用它一次,包括 onCompleted、onError 和 onNext。
170 | + doOnNext:只有执行 onNext 的时候会被调用。
171 | + doOnUnsubscribe:当观察者取消订阅 Observable 时就会被调用;Observable 通过 onError 或者 onComplete 结束时,会取消订阅所有的 Subscriber。
172 | + doOnCompleted:当 Observable 正常终止调用 onCompleted 时会被调用。
173 | + doOnError:当 Observable 异常终止调用 onError 时会被调用。
174 | + doOnTerminate:当 Observable 终止(无论是正常还是异常终止)之前会被调用。
175 | + finallyDo:当 Observable 终止(无论是正常还是异常终止)之后会被调用。
176 | - subscribeOn、observeOn:
177 | + subscribeOn用于指定 Observable 自身在哪个线程上运行。
178 | + observeOn 用来指定 Observer 所运行的线程。
179 | - timeout:在原始 Observable 过了指定的一段时长没有发射任何数据,timeout 会以一个 onError 通知终止这个 Observable,或者继续执行一个备用的 Observable。
180 |
181 | 6. 错误处理操作符
182 | - catch:拦截原始 Observable 的 onError 通知,将它替换为其他数据项或数据序列,让产生的 Observable 能够正常终止或者根本不终止。
183 | RxJava 将 catch 实现为以下3个不同的操作符:
184 | - onErrorReturn:Observable 遇到错误时返回原有 Observable 行为的备用 Observable,备用 Observable 会忽略原有 Observable 的 onError 调用,不会将错误传递给观察者。作为替代,会发射一个特殊的项并调用观察者的 onCompleted 方法。
185 | - onErrorResumeNext:Observable 遇到错误时返回原有 Observable 行为的备用 Observable,备用 Observable 会忽略原有 Observable 的 onError 调用,不会将错误传递给观察者。作为替代,会发射备用 Observable 的数据。
186 | - onExceptionResumeNext:和 onErrorResumeNext 类似,不同的是如果 onError 收到的 Throwable 不是一个 Exception,它会将错误传递给观察者的 onError 方法,不会使用备用的 Observable。
187 | - retry:不会将原始 Observable 的 onError 通知传递给观察者,会订阅这个 Observable,再给它一次机会无错误地完成其数据序列。
188 |
189 | 7. 条件操作符和布尔操作符
190 | - 布尔操作符:
191 | + all:根据一个函数对源 Observable 发射的所有数据进行判断,最终返回的结果就是这个判断结果。
192 | + contains、isEmpty:
193 | * contains:用来判断源 Observable 所发射的数据是否包含某一个数据。
194 | * isEmpty:用来判断源 Observable 是否发射过数据。
195 | - 条件操作符:
196 | + amb:对于给定两个或多个 Observable,只发射首先发射数据或通知的那个 Observable 的所以数据。
197 | + defaultIfEmpty:发射来自原始 Observable 的数据。如果原始 Observable 没有发射数据,就发射一个默认数据。
198 |
199 | 8. 转换操作符
200 | - toList:将发射多项数据且为每一项数据调用 onNext 方法的 Observable 发射的多项数据组合成一个 List,然后调用一次 onNext 方法传递整个列表。
201 | - toSortedList:类似于 toList,不同的是会对产生的列表排序,默认是自然升序。
202 | - toMap:收集原始 Observable 发射的所有数据项到一个 Map(默认是 HashMap),然后发射这个Map。
203 |
204 | ## RxJava 的操作符
205 | 1. 内置的 Scheduler
206 | - Schedulers.immediate():直接运行在当前进程,是 timeout、timeInterval 和 timestamp 操作符的默认调度器。
207 | - Schedulers.newThread():总是启用新线程,并在新线程执行操作。
208 | - Schedulers.io():I/O操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是用一个无数量上限的线程池,可以重用空闲的线程,有效率。
209 | - Schedulers.computation():计算所使用的 Scheduler,这个 Scheduler 使用固定线程池,大小为 CPU 的核数。不要把 I/O 操作放在 computation()中,否则 I/O 操作的等待时间会浪费 CPU。是 buffer、debounce、delay、interval、sample 和 skip 操作符的默认调度器。
210 | - Schedulers.trampoline():当我们想在当前线程执行一个任务时,并不是立即时,可以用 .trampoline() 将它入队。这个调度器将会处理它的队列并且按序运行队列中的每一个任务。是 repeat 和 retry 操作符默认的调度器。
211 | 另外,RxAndroid 也提供了一个 Scheduler:
212 | - AndroidSchedulers.mainThread() --- RxAndroid 库中提供的 Scheduler,它指定的操作在主线程中运行。
213 | 2. 控制线程
214 | 在 RxJava 中用 subscribeOn 和 observeOn 操作符来控制线程。
215 |
216 |
217 |
218 |
219 | 2017.9.2
220 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Activity的生命周期和启动模式.md:
--------------------------------------------------------------------------------
1 | # Activity的生命周期和启动模式
2 | ## Activity的生命周期
3 |
4 | #### 典型情况下的生命周期(指在有用户参与的情况下,Activity所经过的生命周期的改变)。
5 | ---
6 | - onCreate:表示Activity正在被创建,是生命周期的第一个方法。这个方法里可以做一些初始化工作。
7 | - onRestart:表示Activity正在重新启动。一般当当前Activity从不可见重新变为可见状态时会调用这个方法。如按下Home键或者打开一个新的Acticity,会执行onPause,onStop方法,接着又回到这个Activity,就会调用这个方法。
8 | - onStar:表示Activity正在被启动,即将开始。此时Activity已经可见,但还没有出现在前台,无法和用户交互。
9 | - onResume:表示Activity已经可见,并且出现在前台并开始活动。
10 | - onPause:表示Activity正在停止,正常情况下会紧接着调用onStop(特殊情况是如果这时候快速回到当前Activity,会调用onResume)。此时可以做一些存储数据,停止动画等工作,但不能太耗时。
11 | - onStop表示Activity即将停止。可以做一些稍微重量级的回收工作,但也不能太耗时。
12 | - onDestroy:表示Activity即将停止,是Activity生命周期中的最后一个回调。可以做一些回收工作和最终的资源释放。
13 | 
14 |
15 | 附加说明:
16 | ---
17 | - 针对一个特定的Activity,第一次启动,回调如下:onCreate→onStart→onResume。
18 | - 当用户打开新的Activity或者切换到桌面,回调如下:onPause→onStop。有一种特殊情况是如果新的Activity采用了透明主题,那么当前的Activity不会回调onStop。
19 | 当用户再次回到原Activity时,回调如下:onRestart→onStart→onResume。
20 | - 当按Back键回退时,回调如下:onPause→onStop→onDestory。
21 | - 当Activity被系统回收后再次打开,生命周期方法回调过程和正常一样,但只是生命周期方法一样,不代表所有过程都一样。
22 | - 从整个生命周期来说,onCreate和onDestroy是配对的,分别标志着Actovity的创建和销毁,并且只可能有一次调用。从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次;从Activity是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次。
23 |
24 | #### 异常情况下的生命周期(指Activity被系统回收或者由于设备的Configuration发生改变从而导致Activitu被销毁重建)
25 | ---
26 |
27 | - 资源相关的系统配置发生改变导致Activity被杀死并重新创建。
28 | >默认情况下,如果Activity不做特殊处理,当系统配置发生改变后,Activity就好被销毁并重新创建,其生命周期如下。
29 |
30 | 
31 |
32 | onSaveInstanceState方法只会在Activity被异常终止的情况下调用,调用时机是在onStop之前,但和onPause没有既定的时序关系,可能在onPause之前调用,也可能在onPause之后调用。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。onRestoreInstanceState方法在onStart方法之后调用。
33 |
34 | - 关于保存和恢复View层次结构,系统工作的流程是:
35 | + 首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据
36 | + 然后Activity会委托Window去保存数据
37 | + 接着Window在委托它上面的顶级容器去保存数据,顶级容器是一个ViewGroup,一般来说很可能是一个DecorView
38 | + 最后顶层容器再去一一通知它的子元素来保存数据
39 | - 接收的位置选择onRestoreInstanceState或onCreate的区别:
40 | >onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有价值的,不需要额外地判断是否为空;但是onCreate不行吗,onCreate如果正常启动的话,其参数Bundle savedInstanceState为null,所以必须额外判断。通时官方建议建议使用onRestoreInstanceState去恢复数据。
41 | - 针对onSaveInstanceState方法,需要说明的是系统只会在Activity即将被销毁并且有机会重新显示的情况下才会回去调用。
42 |
43 | - 资源内存不足导致低优先级的Activity被杀死。
44 |
45 | 1. Activity按照优先级从高到低,分为如下三种:
46 | - 前台Activity---正在和用户交互的Activity,优先级最高;
47 | - 可见但非前台Activity---比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互;
48 | - 后台Activity---已经被暂停的Activity,比如执行了onStop,优先级最低。
49 | >当系统内存严重不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。另外一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程容易被杀死,比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,从而不会轻易地被系统杀死。
50 |
51 | 2. 解决当系统配置发生改变后,Activity会被重建的问题
52 | 如果当某项内容发生改变后,不想系统重建Activity,可以给Activity指定configChanges属性。
53 | 比如不想让Activity在屏幕旋转的时候重新创建,就可以给configChanges属性添加orientation值。
54 | ````
55 | android:configChanges = "orientation"
56 | ````
57 | 系统会去调用Activity的onConfigurationChangesd方法。
58 | 3.系统配置的项目和含义
59 | 
60 |
61 | >注意:
62 | screenSize和smallestScreenSize两个比较特殊,它们的行为和编译选项有关,和运行环境无关。
63 |
64 | ## Activity的启动模式
65 | 1. Android四种启动模式:
66 | - standard: 标准模式,是系统的默认模式,每次启动一个Activity都会重新创建一个新的实例,即使实例已经存在。创建的实例的生命周期符合典型情况下的Activity生命周期。一个任务栈中可以有多个实例,每个实例可以属于不同的任务栈。谁启动了Activity,那这个Activity,就在启动它的那个Activity所在的栈中运行。当用ApplicationContext去启动standard模式的Activity时就会出错,因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但Context属于非Activity类型,并没有所谓的任务栈。解决方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,为它在启动的时候创建一个新的任务栈,但这时候实际是以singleTask模式启动。
67 | >当我们使用ApplicationContext去启动standard模式的Activity的时候会报错,因为非Activity类型的Context并没有所谓的任务栈,解决的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会创建一个新的任务栈,其实这时候待启动的Activity实际上是以singleTask模式启动的。
68 | - singleTop: 栈顶复用模式。在这模式下,若新的Activity已经位于任务栈的栈顶,那么此Activity就不会被重新创建,同时它的onNewIntent方法会被回调,我们可以通过此方法取出当前请求的信息。另外,该Activity的onCreat和onStart方法不会被系统调用。如果新Activity的实例已存在,但不位于栈顶,仍然会被重建。
69 | - singleTask: 栈内复用模式。是一种单实例模式,只要Activity在一个栈中存在,即使多次启动Activity都不会重建实例,系统也会回调其onNewIntent方法。另外,singleTask模式的Activity切换到栈顶会导致它之上的栈内的Activity出栈。
70 | 
71 | - singleInstance: 单实例模式。是一种加强的singleTask模式,具有此种模式的Activity只能单独位于一个任务栈中,并且由于栈内复用的特性,后续的请求均不会创建新的Activity,除非任务栈被系统销毁。
72 | 2. Activity所需任务栈。
73 | 首先从**TaskAffinity**这个参数说起,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有的Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singletTask启动模式或者allowTaskReparenting属性配对使用。另外任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态。
74 | 当TaskAffinity和singletTask启动模式配对使用的时候,是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。
75 | 当TaskAffinity和allowTaskReparenting结合的时候,会产生特殊的效果。当一个应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那个当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。
76 | 3. Activity指定启动模式的方法。
77 |
78 | - 第一种是通过AndroidManifest为Activity指定启动模式。
79 | ````java
80 |
85 | ````
86 | - 第二种是通过在Intent中设置标志位来为Activity指定启动模式。
87 | ````java
88 | Intent intent = new Intent();
89 | intent.setClass(ManiActivity.this,SecondActivity.class);
90 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
91 | startActivity(intent);
92 | ````
93 | 区别:
94 | - 首先,优先级上第二种方式要高于第一种。当两种同时存在时,以第二种方式为准。
95 | - 其次,两种方式在限定范围上有所不同,第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,第二种方式无法为Activity指定singleInstance模式。
96 | >Tips:
97 | singleTask模式的Activity切换到栈顶会导致在它之上的栈内的Activity出栈。
98 |
99 | 4. Activity的Flags
100 |
101 | - FLAG_ACTIVITY_NEW_TASK
102 | >这个标记位的作用是为Activity我都指定“singleTask”启动模式,其效果和在XML中指定该启动模式相同。
103 | - FLAG_ACTIVITY_SINGLE_TOP
104 | >这个标记的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式相同。
105 | - FLAG_ACTIVITY_CLEAR_TOP
106 | >具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个模式一般需要和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,被启动的Activity的实例如果存在,那么系统就会调用它的onNewIntent;如果被启动的Activity采用standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶,
107 | - FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
108 | >具有这个标记的Activity不会出现在历史的Activity的列表中,当某些情况下,我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用,等同于XML中指定Activity的属性android:excludeFromRecents="true"。
109 |
110 | ## IntentFilter的匹配规则
111 | 1. Activity的启动分为两种
112 |
113 | - 显示调用
114 | - 隐式调用
115 | 显示调用需要明确地指定被启动对象的组件信息,包括包名和类名;隐式调用则不需要明确地指定组件信息。原则上一个Intent不应该即时显示调用又是隐式调用,如果二者共存的话以显示调用为主。
116 | IntentFilter中的过滤信息有action,category,data。
117 | 下面是一个过滤规则:
118 | ````java
119 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | ````
135 |
136 | 2. 只有一个Intent同时匹配action类别,category类别,data类别才算是完全匹配,只有完全匹配才能成功启动目标Activity。另外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
137 | 3. 匹配规则。
138 |
139 | - action匹配规则:
140 | 只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,action匹配区分大小写。
141 | 一个过滤规则中可以有多个action,那么只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。
142 | action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同。
143 | - category匹配规则:
144 | Intent中如果有category那么所有的category都必须和过滤规则中的其中一个category相同,如果没有category的话那么就是默认的category,即android.intent.category.DEFAULT,所以为了Activity能够接收隐式调用,配置多个category的时候必须加上默认的category。
145 | 也就是说,Intent中出现了category,不管有几个category,对于每个category来说,它必须是过滤规则中已经定义了的category。当然,Intent也可以没有actegory,因为系统在用startActivity或者startActivityForResult的时候会默认为Intent加上"android.intent.category.DEFAULT"这个category。
146 | - data匹配规则:
147 | Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。
148 | data的结构很复杂,语法大致如下:
149 | ````java
150 |
157 |
158 | ````
159 | 主要由mimeType和URI组成,其中mimeType代表媒体类型,如image/jpeg、audio/mpeg4-generic和video、*等,可以表示图片、文本、视频等不同的媒体格式;而URI的结构也复杂,大致如下:
160 | ````
161 | ://:/[]|[]|[pathPattern]
162 | ````
163 | 例如:
164 | ````
165 | content://com.example.project:200/folder/subfolder/etc
166 | http://www.baidu.com:80/search/info
167 | ````
168 | scheme:URI的模式,比如http、file、content等。
169 | host:URI的主机名,比如www.baidu.com.
170 | port:URI的端口号,比如80.
171 | 其中如果scheme或者host未指定那么URI就无效。
172 | path、pathPattern、pathPrefix都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面包含了通配符"*",表示0个或者多个任意字符。
173 | **data匹配规则的几种情况:**
174 | 1.
175 | ````java
176 | //URI有默认值
177 |
178 |
179 | ...
180 |
181 | ````
182 | 如果过滤规则中的mimeType指定为"image/*"或者"text/*"等这种类型的话,那么即使过滤规则中没有指定URI,URI有默认的scheme是content和file!如果过滤规则中指定了scheme的话那就不是默认的scheme了。
183 | 例:
184 | ````
185 | intent.setDataAndType(Uri.parse("file://abc"),"image/png");
186 | ````
187 | 如果要为Intent指定完整的data,必须要调用setDataAndType方法!
188 | 不能先调用setData然后调用setType,因为这两个方法会彼此清除对方的值。
189 | ````java
190 | public Intent setData(Uri data){
191 | mData= data;
192 | mType = null;
193 | return this;
194 | }
195 | ````
196 | 可以发现,setData会把mimeType置为null,setType也会把URI置为null。
197 | 2.
198 | ````java
199 |
200 |
201 |
202 |
203 | ````
204 | 这种规则指定了两组data规则,且每个data都指定了完整的属性值,既有URI又有mimeType。
205 | 例:
206 | ````java
207 | intnet.setDataAndType(Uri.parse("http://abc"),"video/mpeg");
208 | ````
209 | 或
210 | ````java
211 | intnet.setDataAndType(Uri.parse("http://abc"),"audio/mpeg");
212 | ````
213 | 另外,data的下面两种写法作用是一样的:
214 | ````java
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | ````
223 | Intent-filter的匹配规则对于Service和BroadcastReceiver也同样道理,不过系统对于Service的建议是尽量使用显示调用方式来启动服务。
224 | **如何判断是否有Activity能够匹配我们的隐式Intent?**
225 | - PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
226 | - PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity信息。
227 | 针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息,例如queryIntentServices、queryBroadcastReceivers等方法。有一类action和category比较重要,它们在一起用来标明这是一个入口Activity,并且会出现在系统的应用列表中。
228 | 在action和category中,有一类action和category比较重要。
229 | ````
230 |
231 |
232 | ````
233 | 这二者共同作用是用来标明这是一个入口Activity并且会出现在系统的应用列表中,缺一不可。
234 |
235 | 2017/8/13
236 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Android 动画深入分析.md:
--------------------------------------------------------------------------------
1 | # Android 动画深入分析
2 | ## View 动画
3 | 1. View 动画的作用对象是 View,支持4种动画效果,分别是平移、缩放动画、旋转动画和透明度动画。帧动画也属于 View 动画。
4 | 2. View 动画的种类
5 | View 动画的四种变换效果对应着 Animation 的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation 和 AlphaAnimation。
6 |
7 | View动画的四种变换
8 | 
9 |
10 | 标签表示动画集合,对呀 AnimationSet 类,可以包含若干个动画。
11 | 两个属性:
12 |
13 | - android:interpolator
14 | 表示动画集合所采用的插值器,插值器影响动画的速度。
15 | - android:shareInterpolator
16 | 表示集合中的动画是否和集合共享同一个插值器,如果集合不指定插值器,那么子动画就需要单独指定所需要的插值器或使用默认值。
17 |
18 | 标签表示平移动画,对应 TranslateAnimation 类,可以使一个 View 在水平和竖直方向完成平移的动画效果。
19 | 属性含义:
20 |
21 | - android:fromXDelta --- 表示 x 的起始值;
22 | - android:toXDelta --- 表示 x 的结束值;
23 | - android:fromYDelta --- 表示 y 的起始值;
24 | - android:toYDelta --- 表示 y 的结束值。
25 |
26 | 标签表示缩放动画,对应 ScaleAnimation,可以使 View 具有放大或者缩小的动画效果。
27 | 属性含义:
28 |
29 | - android:fromXScale --- 水平方向缩放的起始值;
30 | - android:toXScale --- 水平方向缩放的结束值;
31 | - android:fromYScale --- 竖直方向缩放的起始值;
32 | - android:toYScale --- 竖直方向缩放的结束值;
33 | - android:pivotX --- 缩放的轴点的 x 坐标,它会影响缩放的效果;
34 | - android:pivotY --- 缩放的轴点的 y 坐标,它会影响缩放的效果。
35 |
36 | 标签表示旋转动画,对应 RotateAnimation,可以使 View 具有旋转的动画效果。
37 | 属性含义:
38 |
39 | - android:fromDegrees --- 旋转开始的角度;
40 | - android:toDegrees --- 旋转结束的角度;
41 | - android:pivotX --- 旋转的轴点的 x 坐标;
42 | - android:pivotY --- 旋转的轴点的 y 坐标。
43 |
44 | 标签表示透明度的动画,对应 AlphaAnimation,可以改变 View 的透明度。
45 | 属性含义:
46 |
47 | - android:fromAlpha --- 表示透明度的起始值;
48 | - android:toAlpha --- 表示透明度的结束值。
49 |
50 | View 动画的一些常用属性:
51 |
52 | - android:duration --- 动画的持续时间;
53 | - android:fillAfter --- 动画结束以后 View 是否停留在结束位置,true 表示 View 停留在结束位置,false 则不停留。
54 |
55 | 3. 自定义 View 动画,需要继承 Animation 这个抽象了,重写它的 initialize 和 applyTransformation 方法,在 initialize 方法中做一些初始化,在 applyTransformation 中进行相应的矩阵变换,需要采用 Camera 来简化矩阵变换的过程。
56 | 4. 帧动画是顺序播放一组预先定义好的图片,类似于电影播放。另外帧动画比如容易引起 OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片。
57 |
58 | ## View 动画的特殊使用场景
59 | 1. LayoutAnimation
60 | LayoutAnimation 作用于 ViewGroup。
61 | 步骤:
62 | - 定义 LayoutAnimation
63 | 相关属性
64 | - android:delay --- 表示子元素开始动画的时间延迟。
65 | - android:animationOrder --- 表示子元素动画的顺序,有三种选项:
66 | + normal:表示顺序显示;
67 | + reverse :表示逆向显示;
68 | + random:表示随机播放入场动画。
69 | - anroid:animation --- 为子元素指定具体的入场动画。
70 | - 为子元素指定具体的如此动画
71 | - 为 ViewGroup 指定 android:layoutAnimation 属性:android:layoutAnimation = "@anim/anim_layout"。
72 | 2. Activity 的切换效果
73 | 主要用到 overridePendingTransition(int enterAnim,int exitAnim)这个方法,这个方法必须在 startActivity(Intent) 或者 finish() 之后调用才能生效。
74 | 参数含义:
75 |
76 | - enterAnim --- Activity 被打开时,所需要的动画资源 id;
77 | - exitAnim --- Activity 被暂停时,所需要的动画资源 id。
78 |
79 | ## 属性动画
80 | 1. 属性动画可以对任意对象的属性进行动画而不仅仅是 View,动画默认时间间隔300ms,默认帧率是10ms/帧。
81 | 属性动画需要定义在 **res/animator/**目录下。
82 |
83 | 2. 标签属性的含义
84 | - android:propertyName --- 表示属性动画的作用对象的属性的名称;
85 | - android:duration --- 表示动画的时长;
86 | - android:valueFrom --- 表示属性的起始值;
87 | - android:valueTo --- 表示属性的结束值;
88 | - android:startOffset --- 表示动画的延迟时间,当动画开始画,需要延迟多少毫秒才会真正播放此动画;
89 | - android:repeatCount --- 表示动画的重复次数,默认值是0,-1表示无限循环;
90 | - android:repeatMode --- 表示动画的重复模式,有“repeat”和“reverse”选项,分别表示连续重复和逆向重复;
91 | - android:valueType --- 表示 android:propertyName 所指定的属性的类型,有“intType”和“floatType” 两个可选项,分别表示属性的类型为整型和浮点型。另外,如果 android:propertyName 所指定的属性表示的是颜色,那么不需要指定 android:valueType,系统会自动对颜色类型的属性做处理。
92 |
93 | 3. TimeInterpolator,时间插值器,作业是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有
94 | - LinearInterpolator:线性插值器:匀速动画;
95 | - AccelerateDecelerateInterpolator:加速减速插值器:动画两头慢中间快;
96 | - DecelerateInterpolator:减速插值器:动画越来越慢。
97 |
98 | 4. TypeEvaluator,估值器,作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有
99 | - IntEvaluator:针对整型属性;
100 | - FloatEvaluator:针对浮点型属性;
101 | - ArgbEvaluator:针对 Color 属性。
102 |
103 | 5. 自定义插值器需要实现 Interpolator 或者 TimeInterpolator,自定义估值算法需要实现 TypeEvaluator。如果要实现对其他类型(非 int、float、Color)做动画,那么必须要自定义类型估值算法。
104 |
105 | 6. 属性动画提供了监听器用于监听动画的播放过程,主要有两个接口
106 | - AnimatorUpdateListener:可以监听动画开始、结束、取消以及重复播放;
107 | - AnimatorListener:会监听整个动画过程。
108 |
109 | 7. 属性动画的原理:属性动画要求动画作用的对象提供该属性的 get 和 set 方法,属性动根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用 set 方法,每次传递给 set 方法的值都不一样,确切来说是随时间的推移,所传递的值越来约接近最终值。
110 |
111 | 8. 使用动画的注意事项
112 | - OOM 问题
113 | 主要出现在帧动画中,实际开发中尽量避免使用帧动画。
114 | - 内存泄漏
115 | 主要出现在属性动画中的无限循环动画,需要在 Activity 退出时及时停止。
116 | - 兼容性问题
117 | - View 动画的问题
118 | 当出现动画完成后 View 无法隐藏的现象,即 setVisiblity(View.GONE)失效时,需要调用 view.clearAnimation() 清楚 View 动画来解决此问题。
119 | - 不要使用 px
120 | - 动画元素的交互
121 | - 硬件加速
122 |
123 |
124 | 2017.8.21
125 | W.Z.H
126 |
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Android 的 Drawable.md:
--------------------------------------------------------------------------------
1 | # Android 的 Drawable
2 | ## Drawable 简介
3 | 1. Drawable 是一个抽象类,它是所有 Drawable 对象的基类,每个具体的 Drawable 都是它的子类。
4 | 2. Drawable 的内部宽/高这个参数比较重要,通过 getIntrinsicWidth 和 getIntrinsicHeight 这两个方法可以获取到它们。但是并不是所以的 Drawable 都有内部宽/高。Drawable 的内部宽/高不等同于它的大小。Drawable 是没有大小的概念的。
5 |
6 | ## Drawable 的分类
7 | 1. BitmapDrawable
8 | 表示的是一张图片。
9 | 各个属性的含义:
10 |
11 | - android:src
12 | 图片的资源 id。
13 | - android:antialias
14 | 是否开启抗锯齿功能。
15 | - android:dither
16 | 是否开启抖动效果。
17 | - android:filter
18 | 是否开启过滤效果。
19 | - android:gravity
20 | gravity属性的可选项:
21 | 
22 | - android:mipMap
23 | 纹理映射。
24 | - android:tileMode
25 | 平铺模式。
26 |
27 | - disable
28 | 表示关闭平铺模式。
29 | - repeat
30 | 表示的是简单的水平和竖直方向上的平铺效果。
31 | - mirror
32 | 表示一种在水平和竖直方向上的镜面投影效果。
33 | - clamp
34 | 表示的效果是图片四周的像素会扩展到周围区域。
35 |
36 | 2. ShapeDrawable
37 | 通过颜色来构造的图形。
38 |
39 | - android:shape
40 | 表示图形的形状,有四个选项:rectangle(矩形)、oval(椭圆)、line(横线)和 ring(圆环)。
41 |
42 | ring的属性值:
43 | 
44 | -
45 | 表示 shape 的四个角度,只适用于矩形 shape,有如下五个属性
46 | - andrdoid:radius --- 为四个角同时设定相同的角度,优先级较低,会被其他四个属性覆盖。
47 | - andrdoid:topLeftRadius --- 设定最上角的角度;
48 | - andrdoid:topRightRadius --- 设定右上角的角度;
49 | - andrdoid:bottomLeftRadius --- 设定最下角的角度;
50 | - andrdoid:bottomRightRadius --- 设定右下角的角度。
51 | -
52 | 与标签是互斥的,其中 solid 表示纯色填充,gradient 表示渐变效果。gradient 有如下几个属性:
53 | - android:angle --- 渐变的角度,默认为0,其值必须是45的倍数,0表示从左到右,90表示从上到下。
54 | - android:centerX --- 渐变的中心点的横坐标;
55 | - android:centerY --- 渐变的中心点的纵坐标;
56 | - android:startColor --- 渐变的起始色;
57 | - android:centerColor --- 渐变的中间色;
58 | - android:endColor --- 渐变的结束色;
59 | - android:gradientRadius --- 渐变半径,仅当 android:type = "radial"时有效;
60 | - android:useLevel --- 一般为 false,当 Drawable 作为 StateListDrawable 使用时为 true。
61 | -
62 | 这个标签表示纯色填充,通过 android:color 即可指定 shape 中填充的颜色。
63 | -
64 | Shape 的描边,有如下几个属性:
65 | - android:width --- 描边的宽度,越大则 shape 的边缘就会看起来越粗;
66 | - android:color --- 描边的颜色;
67 | - android:dashWidth --- 组成虚线的线段的宽度;
68 | - android:dashGap: --- 组成虚线的线段之间的间隔,间隔越大则虚线看起来空隙就越大。
69 | **注意:**如果 android:dashWidth 和 android:dashGap 有任何一个为0,那么虚线的效果将不能生效。
70 | -
71 | 表示空白,是包含它的 View 的空白,有四个属性:
72 | - android:left;
73 | - android:top;
74 | - android:right;
75 | - android:bottom。
76 | -
77 | shape 的固有大小,有两个属性:
78 | - android:width
79 | - android:height
80 | 分别表示 shape 的宽/高。
81 |
82 | 3. LayerDrawable
83 | LayerDrawable 对应于 XML 标签是,表示一种层次化的 Drawable 集合,通过将不同的 Drawable 放置在不同的层上面从而达到一种叠加后的效果。
84 | 默认情况下,layer-list 中的所有的 Drawable 都会被缩放至 View 的大小,对于 bitmap 来说,需要使用 android:gravity 属性才能控制图片的显示效果。
85 |
86 | 4. StateListDrawable
87 | StateListDrawable 对应于 标签,也是 Drawable 集合,主要用于设置可单击的 View 的背景。
88 | 属性的含义:
89 |
90 | - android:constantSize
91 | StateListDrawable 的固定大小是否不随着其状态的改变而改变。
92 | - android:dither
93 | 是否开启抖动效果。
94 | - android:variablePadding
95 | StateListDrawable 的 padding 表示是否随着其状态的改变而改变。
96 |
97 | View 的常见状态
98 | 
99 |
100 | 5. LevelListDrawable
101 | LevelListDrawable 对应于 标签,也表示一个 Drawable 集合,集合中的每个 Drawable 都有 一个等级的概念,由 **android:minLevel** 和 **android:maxLevel** 来指定,在最小值和最大值之间的等级会对应此 item 中的 Drawable,Drawable 的等级范围是0~1000,最小等级是0,最大等级是10000。
102 |
103 | 6. TransitionDrawable
104 | TransitionDrawable 对应于 标签,用于实现两个 Drawable 之间的淡入淡出效果。
105 |
106 | 7. InsetDrawable
107 | InsetDrawable 对应于 标签,可以将其他 Drawable 内嵌到自己当中,并可以在四周留出一定的间距。
108 |
109 | 8. ScaleDrawable
110 | ScaleDrawable 对应于 标签,可以根据自己的等级将指定的 Drawable 缩放到一定的比例。
111 | 如果 ScaleDrawable 的级别越大那么内部的 Drawable 看起来就越小,如果 ScaleDrawable 的 XML 中所定义的缩放比例越大,那么内部的 Drawable 看起来就越小。ScaleDrawable 的作用更偏向于缩小一个特定的 Drawable。
112 |
113 | 9. ClipDrawable
114 | ScaleDrawable 对应于 标签,可以根据自己当前的等级来裁剪另一个 Drawable。
115 |
116 | ClipDrawable 的 gravity 属性
117 | 
118 |
119 |
120 | 2017.8.20
121 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Android 的消息机制.md:
--------------------------------------------------------------------------------
1 | # Android 的消息机制
2 | 1. Android 的消息机制主要是指 Hadnler,Hnadler 的运行需要底层的 MessageQueue 和 Lopper 支撑。MessageQueue 是消息队列,它的内部存储了一组信息,以队列的形式对外提供插入和删除的工作;Looper 是指消息循环,会以无限循环的形式去查找是否有新消息,如果有的话就处理,否则一直等待。ThreadLocal 是 Looper 中的一个特殊概念,作用是可以在每个线程中存储数据。ThreadLocal 可以在不同的线程中互不干扰地存储并提供数据,通过 ThreadLocal 可以获取每个线程的 Looper,另外线程是默认没有 Looper 的,需要使用 Handler 就必须为线程创建 Looper。
3 |
4 | ## Android 的消息机制概述
5 | 1. 系统不允许在子线程中访问 UI 的原因:因为 Android 的 UI 控件不是线程安全的,如果在多线程中并发访问可能会导致 UI 控件处于不可预期的状态。如果对 UI 控件的访问加上锁机制会有两个缺点:首先加上锁机制会让 UI 访问的逻辑变得复杂;其次锁机制会降低 UI 访问的效率,因为锁机制会阻塞某些线程的执行。
6 | 2. Handler 的工作过程
7 | 
8 |
9 | ## Android 的消息机制分析
10 | 1. ThreadLocal 的工作原理
11 | ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,其他线程则无法获取到数据。
12 | 当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。
13 |
14 | 存储规则:ThreadLocal 的值在 table 数组中的存储位置总是为 ThreadLocal 的 reference 字段所标识的对象的下一个位置。
15 |
16 | ThreadLocal 可以在多个线程中互不干扰地存储和修改数据,是因为 ThreadLocal 的 set 和 get 方法所操作的对象都是当前线程的 localValues 对象的 table 数组,可以在不同线程中访问同一个 ThreadLocal 的 set 和 get 方法,它们对 ThreadLocal 所做的读/写操作仅限于各自线程发内部。
17 |
18 | 2. 消息队列的工作原理
19 | 消息队列在 Android 中指的是 MessageQueue,MessageQueue主要包含插入和读取两个操作,对应 enqueueMessage(作用是往消息队列中插入一条消息) 和 next(作用是从消息队列中取出一条消息并将其从消息队列中移除) 两个方法。
20 |
21 | 3. Looper 的工作原理
22 | Looper.prepare() 为当前线程创建一个 Looper,接着通过 Looper.loop() 来开启消息循环。
23 | prepareMainLooper 方法主要是给主线程 ActivityThread 创建 Looper 使用的。
24 | Looper 提供的 getMainLooper 方法可以在任何地方获取到主线程的 looper。
25 | Looper 提供了 quit 和 quitSafely 来退出 Looper,区别是:quit 会直接退出 Looper;quitSafely 只是设定了一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。
26 | 在子线程中,如果收到为其创建了Looper,那么所有的事情完成以后应该调用 quit 方法来终止消息循环,否则这个子线程就会一直处于等待状态。
27 | Looper 最重要的一个方法是 loop 方法,只有调用了 loop 方法,消息循环系统才会真正地起作用。loop 是一个死循环,唯一跳出循环的方式是 MessageQueue 的 next 方法返回 null。
28 |
29 | 4. Handler 的工作原理
30 | Handler 的工作主要包含消息的发生和接收过程,消息的发送可以通过 post 的一系列方法以及 send 的一系列方法来实现,post 的一系列方法最终是通过 send 的一系列方法来实现的。
31 | Handler 发生消息的过程仅仅是向消息队列中插入一条消息,MessageQueue 的 next 方法就会返回这条消息给 Looper,Looper 收到消息后就开始处理,最终消息由 Looper 交由 Hnadler 处理,即 Handler 的 dispatchMessage 方法会被调用,这时 Hnadler 就进入了处理消息的阶段。
32 |
33 | Hnadler 出来消息的过程:
34 |
35 | - 首先,检查 Message 的 callback 是否为 null,不为 null 就通过 handleCallback 来处理消息。callback 是一个 Runnable 对象,实际上就是 Handler 的 post 方法所传递的 Runnable 参数。
36 | - 其次,检查 mCallback 是否为 null,不为 null 就调用 mCallback 的 handleMessage 方法来处理消息。Callback 是个接口,其意义是可以用来创建一个 Handler 的实例但不需要派生 Handler 的子类。
37 | - 最后,调用 Handler 的 handleMessage 方法来处理消息。
38 | Handler 处理消息流程图
39 | 
40 |
41 | ## 主线程的消息循环
42 | 1. Android 的主线程就是 ActivityThread,主线程的入口方法为 main,在 main 方法中系统会通过 Looper.prepareMainLooper() 来创建主线程的 Looper 以及 MessageQueue,并通过 Looper.loop() 来开启主线程的消息循环。
43 | 主线程的消息循环开始后,ActivityThread 还需要一个 Handler 来和消息队列进行交互,这个 Handler 即 ActivityThread.H,它内部定义了一组消息类型,包含了四大组件的启动和停止等过程。
44 |
45 | ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信, AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ActivityThread 中的 Binder 方法,然后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
46 |
47 |
48 |
49 |
50 | 2017.8.24
51 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Android 的线程和线程池.md:
--------------------------------------------------------------------------------
1 | # Android 的线程和线程池
2 | 从用途上分,线程分为主线程和子线程;主线程主要处理和界面相关的事情,子线程则往往用于耗时操作。
3 |
4 | ## 主线程和子线程
5 | 主线程是指进程所拥有的线程。Android 中主线程交 UI 线程,主要作用是运行四大组件以及处理它们和用户的交互;子线程的作业则是执行耗时任务。
6 |
7 | ## Android 中的线程形态
8 | 1. AsyncTask
9 | AsyncTask 是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新 UI,
10 | AsyncTask 是一个抽象的泛型类,提供了 Params(参数的类型)、Progress(后台任务执行进度的类型) 和 Result(后台任务的返回结果的类型) 这三个泛型参数,
11 | AsyncTask 提供了4个核心方法
12 | - onPreExcute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。
13 | - doInBackground(Params...params),在线程池中执行,此方法用于执行异步任务,params 参数表示异步任务的输入参数。在此方法中可以通过 publishProgress 方法来更新任务的进度,publishProgress 方法会调用 onProgressUpdate 方法,另外此方法需要返回计算结果给 onPostExecute 方法。
14 | - onProgressUpdate(Progress...values),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
15 | - onPostExecute(Resukt result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中 result 参数是后台任务的返回值,即 doInBackground 的返回值。
16 |
17 | onPreExcute 先执行,接着是 doInBackground,最后才是 onPostExecute。
18 | 当异步任务被取消时,onCancelled() 方法会被调用,这个时候 onPostExecute 则不会被调用。
19 | 2. AsyncTask 在具体的使用过程中的一些限制条件
20 |
21 | - AsyncTask 的类必须在主线程中加载;
22 | - AsyncTask 的对象必须在 UI 线程中创建;
23 | - 不要在程序中直接调用 onPreExecute、onPostExecute、doInBackground 和 onProgressUpdate 方法。
24 | - 一个 AsyncTask 对象只能执行一次,即只能调用一次 execute 方法,否则会报运行时异常。
25 | - 在 Android 1.6之前,AsyncTask 是串行执行任务的,Android 1.6的时候 AsyncTask 开始采用线程池处理并行任务,但是从 Android 3.0开始为了避免 AsyncTask 所带来的并发错误,AsyncTask 又采用一个线程来串行执行任务。但是在 Android 3.0 以及后续的版本中,仍然可以通过 AsyncTask 的 executeOnExecutor 方法来并行地执行任务。
26 |
27 | 3. AsyncTask 的工作原理
28 | AsyncTask 中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR) 和一个 Handler(InternalHandler),线程池 SerialExecutor 用于任务的排队,线程池 THREAD_POOL_EXECUTOR 用于真正地执行任务,InternalHandler 用于将执行环境从线程池切换到主线程。
29 |
30 | 4. HandlerThread
31 | HandlerThread 继承了 Thread,是一种可以使用 Handler 的 Thread, 它的实现就是在 run 方法中通过 Looper.prepare() 来创建消息队列,并通过 Looper.loop() 来开启消息循环。
32 |
33 | 与普通的 Thread 相比,普通 Thread 主要用于在 run 方法中执行一个耗时任务,而 HandlerThread 在内部创建了消息队列,外界需要通过 Handler 的消息方式来通知 HandlerThread 执行一个具体的任务。
34 |
35 | 由于 HandlerThread 的 run 方法是一个无限循环,因此当明确不需要在使用 HandlerThread 时,可以通过它的 quit 或者 quitSafely 方法来终止线程的执行。
36 |
37 | 5. IntentService
38 | IntentService 是一种特殊的 Service,继承了 Service 并且是一个抽象类,必须创建它的子类才能使用 IntentService。IntentService可用于执行后台耗时任务,任务执行后会自动停止,并且它的优先级比单纯的线程要高很多,不容易被系统杀死。在实现上,IntentService 封装了 HandlerThread 和 Handler。
39 |
40 | ## Android 中的线程池
41 | 1. 线程池的优点
42 | - 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销;
43 | - 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象;
44 | - 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
45 |
46 | 2. ThreadPoolExecutor
47 | ThreadPoolExecutor 是线程的真正实现。
48 | ````java
49 | public ThreadPoolExecutor(int corePoolSize,
50 | int maximumPoolSize,
51 | long keepAliveTime,
52 | TimeUnit unit,
53 | BlockingQueue workQueue,
54 | ThreadFactory threadFactory)
55 | ````
56 |
57 | - corePoolSize
58 | 线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,及时处于闲置状态。
59 | - maximumPoolSize
60 | 线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。
61 | - keepAliveTime
62 | 非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
63 | - unit
64 | 用于指定 keepAliveTime 参数的时间单位,这是一个枚举,常用的有 TimeUnit、MILLSECONDS(毫秒)、TimeUnit.SECONDS(秒) 以及 TimeUnit.MINUTES(分钟)。
65 | - workQueue
66 | 线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。
67 | - threadFactory
68 | 线程工厂,为线程池通过创建新线程的功能。ThreadFactory 是一个接口,只有 一个方法:Thread newThread(Runnable r)。
69 |
70 | ThreadPoolExecutor 执行任务时遵循的规则
71 |
72 | - 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务;
73 | - 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行;
74 | - 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量为达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
75 | - 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务, ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectedExecution 方法来通知调用者。
76 |
77 | 3. 线程池的分类
78 | - FixedThreadPool
79 | 通过 Executors 的 newFixedThreadPool 方法来创建。是一种线程数量固定的线程池,当线程处于空闲状态时并不会被回收,除非线程池被关闭。FixedThreadPool 中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制。
80 | - CachedThreadPool
81 | 通过 Executors 的 newCachedThreadPool 方法来创建。是一种线程数量不定的线程池,只有非核心线程,最大线程数为 Integer.MAX_VALUE。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收。CachedThreadPool 的任务队列相当于一个空集合,这样会导致任何任务都会被立即执行。
82 | - ScheduledThreadPool
83 | 通过 Executors 的 newScheduledThreadPool 方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。ScheduledThreadPool 这类线程池主要用于执行定时任务和具有固定周期的重复任务。
84 | - SingleThreadExecutor
85 | 通过 Executors 的 newSingleThreadExecutor 方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor 的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。
86 |
87 |
88 | 系统预置4种线程池的典型使用方法:
89 |
90 | ````java
91 | Runnable command = new Runnable(){
92 | @Override
93 | public void run(){
94 | SystemClock.sleep(2000);
95 | }
96 |
97 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
98 | fixedThreadPool.execute(command);
99 |
100 | ExecutorService cachedThreadPool =Executors.newCachedThreadPool();
101 | cachedThreadPool.execute(command);
102 |
103 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
104 | // 2000ms 后执行 command
105 | scheduledThreadPool.schedule(command,2000,TimeUnit.MILLISECONDS);
106 | // 延迟10ms,每个1000ms执行一次 command
107 | scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
108 |
109 | ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
110 | singleThreadExecutor.execute(command);
111 |
112 | }
113 | ````
114 |
115 |
116 | 2017.8.24
117 | W.Z.H
118 |
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Android性能优化.md:
--------------------------------------------------------------------------------
1 | # Android 性能优化
2 | ## Android 的性能优化方法
3 | 1. 布局优化
4 | 布局优化的思想就是尽量减少布局文件的层级。
5 | 如何进行布局优化:
6 | 首先删除布局中无用的控件和层级;其次有选择地使用性能较低的 ViewGroup。
7 | 布局优化的另一种手段是采用标签、标签和 ViewStub
8 |
9 | 标签:
10 | >主要用于布局重用。
11 | 可以将一个指定的布局文件加载到当前布局文件中,
12 | 标签只支持以 android:layout_开头的属性,其他属性是不支持的。android:id 这个属性例外。
13 |
14 | 标签:
15 | >标签一般和标签一起使用从而减少布局的层级,
16 |
17 | ViewStub:
18 | >ViewStub 继承了 View,非常轻量且宽/高都是0。ViewStub 的意义在于按需即在所需的布局文件。
19 |
20 | 2. 绘制优化
21 | 绘制优化是指 View 的 onDraw 方法要避免执行大量的操作,主要体现在:
22 | - 首先,onDraw 中不要创建新的局部对象,因为 onDraw 方法可能会被频繁调用,会在一瞬间产生大量的临时对象,不仅占用过多内存还导致系统更加频繁 gc,降低程序的执行效率。
23 | - 另一方面,onDraw 方法中不要做耗时的任务,也不能执行成千上万次的循环操作。官方的标准中 View 的绘制帧率保证60fps 是最佳的,这就要求每帧的绘制时间不超过16ms(16ms = 1000/60)。
24 |
25 | 3. 内存泄漏优化
26 | 泄漏发生的场景:
27 |
28 | - 静态变量导致的内存泄漏
29 | - 单例模式导致的内存泄漏
30 | - 属性动画导致的内存泄漏
31 |
32 | 4. 响应速度优化和 ANR 日志分析
33 | Android 规定,Activity 如果5秒钟之内无法响应屏幕触摸事件或者键盘输入事件就会出现 ANR,BroadcastReceiver 如果10秒钟之内还未执行玩操作会出现 ANR。
34 | 当一个进程发生 ANR 之后,系统会在/data/anr 目录下创建一个文件 traces.txt,通过分析这个文件可以定位出 ANR 的原因。
35 |
36 | 5. ListView 和 Bitmap 优化
37 | ListView:
38 | 首先要采用 ViewHolder 并避免在 getView 执行耗时操作;
39 | 其次要根据列表的滑动状态来控制任务的执行频率;
40 | 最后可以尝试开启硬件的加速来使 ListView 的滑动更加流畅。
41 |
42 | 6. 线程优化
43 | 线程优化的思想是采用线程池,避免程序中存在大量的 Thread。
44 |
45 | 7. 一些性能建议
46 | - 避免创建过多的对象;
47 | - 不要过多使用枚举,枚举占用的内存空间比整型大;
48 | - 常量请使用 static final 来修饰;
49 | - 使用一些 Android 特有的数据结构;
50 | - 适当使用软引用和弱引用;
51 | - 采用内存缓存和磁盘缓存;
52 | - 尽量采用静态内部类,可以避免潜在的由于内部类而导致的内存泄漏。
53 |
54 | ## 提高程序的可维护性
55 | - 命名要规范,要正确地传达出变量或者方法的含义,少用缩写。
56 | - 代码的排版上需要留出合理的空白区分不同的代码块,其中同类变量的声明要放在一组,两类变量之间要留出一行空白作为区分。
57 | - 仅为非常关键的代码添加注释,其他地方不写注释。
58 |
59 |
60 |
61 |
62 | 2017.8.30
63 | W.Z.H
64 |
65 |
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/Bitmap 的加载和 Cache.md:
--------------------------------------------------------------------------------
1 | # Bitmap 的加载和 Cache
2 | ## Bitmap 的高效加载
3 | 1. Bitmap 在 Android 中指的是一张图片。
4 | 2. BitmapFactory 类提供了四类方法:decodeFile、decodeResource、decodeStream 和 decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载一个 Bitmap 对象,其中 decodeFile 和 decodeResource 又间接调用了 decodeStream 方法。
5 | 3. 高效地加载 Bitmap 的核心思想就是采用 BitmapFactory.Options 来加载所需尺寸的图片。主要用到了 inSampleSize 参数,即采样率。inSampleSize 的取值应该总是为2的指数,如果外界传递给系统的 inSampleSize 不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。
6 | 4. 获取采样率的流程:
7 | - 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 true并加载图片;
8 | - 从 BitmapFactory.Options 中取出图片的原始宽高信息,它们对应于 outWidth 和 outHeight 参数;
9 | - 根据采样率的规则并结合目标 View 的所需大小计算出采样率 inSampleSize;
10 | - 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false,然后重新加载图片。
11 |
12 | 5. 代码实现:
13 | ````java
14 | public static Bitmap decodeSampleBitmapFromResource(Resource res,int resId,int reqWidth,int reqHeight){
15 | //First decode with inJustDecodeBounds = true to check dimensions
16 |
17 | final BitmapFactory.Options options = new BitmapFactory.Options();
18 | options.inJustDecodeBounds = true;
19 | BitmapFactory.decodeResource(res,redId,options);
20 |
21 | // Calculate inSampleSize
22 | options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
23 | return BitmapFactory.decodeResource(res,resId,options);
24 | }
25 |
26 |
27 | public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
28 | //Raw height and width of image
29 | final int height = options.outHeight;
30 | final int width = options.outWidth;
31 | int inSampleSize = 1;
32 |
33 | if(height > reqHeigth || width > reqWidth){
34 | final int halfHeight = height / 2;
35 | final int halfWidth = width / 2;
36 | //Calculate the largest inSampleSize value that is a power of 2 and keeps both height and width larger than the requested height and width.
37 |
38 | while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth){
39 | inSampleSize *= 2;
40 | }
41 | }
42 | return inSampleSize;
43 | }
44 | ````
45 | 加载并显示图片
46 | ````java
47 | mImageView.setImageBitmap(
48 | decodeSampleBitmapFromResource(getResource(),R.id.myimage,100,100));
49 | ````
50 |
51 | ## Android 中的缓存策略
52 | 1. 一般来说,缓存策略主要包含缓存的添加、获取和删除这三类操作。
53 | 2. 目前常用的一种算法是 LRU,是近期最少使用算法,核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用 LRU 算法的缓存有两种:LruCache(用于实现内存缓存) 和 DiskLruCache(充当存储设备缓存),
54 | 3. LruCache
55 | LruCache 是一个泛型类,内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象,提供了 get 和 put 方法来完成缓存的获取和添加操作,当缓存满时,LruCache 会移除较早使用的缓存对象,然后再添加新的缓存对象。
56 |
57 | - 强引用:直接的对象引用;
58 | - 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被 gc 回收;
59 | - 弱引用: 当一个对象只有弱引用存在时,此对象会随时被 gc 回收。
60 |
61 | LruCache 是线程安全的。
62 | LruCache 的典型的初始化过程:
63 | ````java
64 | int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
65 | int cacheSize = maxMemory / 8;
66 | mMenoryCache = new LruCache(cacheSize){
67 | @Override
68 | // sizeOf() 方法的作用是计算缓存对象的大小
69 | protected int sizeOf(String key, Bitmap bitmap){
70 | return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
71 | }
72 | };
73 | ````
74 |
75 | 从 LruCache 中获取一个缓存对象
76 | ````java
77 | mMenoryCache.put(key,bitmap);
78 | ````
79 |
80 | 4. DiskLruCache
81 | DiskLruCache 用于实现存储设备缓存,即磁盘缓存,通过将缓存对象写入文件系统从而实现缓存的效果。
82 |
83 | DiskLruCache 的创建
84 | DiskLruCache 并不能通过构造方法来创建,提供了 open 方法用于创建自身。
85 | ````java
86 | public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
87 | ````
88 | open 方法有四个参数:
89 |
90 | - 第一个参数表示磁盘缓存在文件系统中的存储路径;
91 | - 第二个参数表示应用的版本号,一般设为1即可;
92 | - 第三个参数表示单节点所对应的数据的个数,一般设为1即可。
93 | - 第四个参数表示缓存的总大小,当缓存大小超出这个设定值之后, DiskLruCache 会清楚一些缓存从而保证大小不大于这个设定值。
94 |
95 | DiskLruCache 的缓存添加
96 | DiskLruCache 的缓存添加操作是通过 Editor 完成的,Editor 表示一个缓存对象的编辑对象。
97 |
98 | DiskLruCache 的缓存查找
99 | 缓存查找过程需要将 url 转换为 key,然后通过 DiskLruCache 的 get 方法得到一个 Snapshot 对象,接着再通过 Snapshot 对象即可得到缓存的文件输入流,有了文件输出流,自然就可以得到 Bitmap 对象。
100 |
101 | 5. ImageLoader 的实现
102 | 一般来说,ImageLoader 应该具备如下功能:
103 | - 图片的同步加载;
104 | - 图片的异步加载;
105 | - 图片的压缩;
106 | - 内存缓存;
107 | - 磁盘缓存;
108 | - 网络垃圾。
109 |
110 |
111 |
112 |
113 | 2017.8.29
114 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/IPC机制.md:
--------------------------------------------------------------------------------
1 | # IPC机制
2 | ## Android IPC简介
3 | 1. IPC: Inter-Process Communication 的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
4 | 2. 进程与线程。
5 | 按照操作系统中的描述,线程是 CPU 调度最小单元,是一种有限的系统资源;进程一般指一个执行单元,在 PC 或者移动设备上指一个程序或者与一个应用。
6 | 一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。
7 | ANR:Application Not Responding,应用无响应。解决这个问题就需要用到线程,把一些耗时的任务放在线程中即可。
8 |
9 | ## Android中的多进程模式
10 | 1. 正常情况下,在 Android 中多进程是指一个应用中存在多个进程的情况。
11 | 2. 在 Andorid 中使用多进程只有一种方法,就是给四大组件在 AndroidManifest 中指定 android:process 属性。
12 | 还有另一种方法就是通过 JNI 在 native 层去 fork 一个新的进程。
13 | 3. 默认进程就是包名。
14 | 4. ":remote"和"com.ryg.chapter_2.remote" 这两种方式的区别:
15 | - 首先,":"的含义是指要在当前的进程名前面附加上当前的包名,是一种简写的方法。而另一种是完整的命名方式,不会附加包名信息。
16 | - 其次,进程名以":"开头的进程属于当前应用的私有进程,进程名不以":"开头的进程属于全局进程,其他应用通过 ShareID 方式可以和它跑在同一个进程中。
17 | 5. Android 系统会为每个应用分配一个唯一的 UID ,具有相同 UID 的应用才能共享数据。另外,两个应用需要有相同的 ShareUID 且签名相同才能跑在同一个进程中。可以共享 data 目录、组件信息,还可以共享内存数据。
18 | 6. Android 为每一个应用分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,导致在不同的虚拟机中访问同一个类的对象会产生多份副本。
19 | 7. 所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败。
20 | 8. 使用多进程会造成的问题:
21 | - 静态成员和单例模式完全失效。
22 | - 线程同步机制完全失效。
23 | - SharePreferences 的可靠性下降。因为 SharePreferences 不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失,因为 SharePreferences 底层是通过读/写XML文件来实现的,并发写显然是可能出现问题的。
24 | - Application 会多次创建。运行在同一个进程中的组件是属于同一个虚拟机和同一个 Application 的,同理,运行在不同进程中的组件是属于两个不同的虚拟机和 Application 的。
25 |
26 | ## IPC基础概念介绍
27 | 1. Serializable 是 Java 提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。
28 | ````java
29 | public class User implements Serializable{
30 | private static final long serialVersionUID = 519061723721295573l;
31 |
32 | public int userId;
33 | public String userName;
34 | public boolean isMale;
35 | ...
36 | }
37 | ````
38 | 只需要采用 ObjectOutputStream 和 ObjectInputStream 即可实现对象的序列化和反序列化。
39 | ````java
40 | //序列化过程
41 | User user = new User(0,"jake",true);
42 | ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
43 | out.writeObject(user);
44 | out.close();
45 |
46 | //反序列化过程
47 | ObjectInputStream int= new ObjectInputStream(new FileInputStream("cache.txt"));
48 | User newUser = in.readObject();
49 | in.close();
50 | ````
51 | 2. serialVersionUID 是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的 serialVersionUID 只有和当前类的 serialVersionUID 相同才能够正常地被反序列化。
52 | serialVersionUID 的工作机制:
53 | > 序列化的时候系统会把当前类的 serialVersionUID 写入序列化的文件中(也可能是其他中介),当反序列化的时候系统就会去检测文件中的 serialVersionUID,看它是否和当前类的 serialVersionUID 一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数量、类型可能发生了改变,这个时候是无法正常反序列化的。
54 |
55 | 一般我们应该手动指定 serialVersionUID 的值,这样序列化和反序列化时两者的 serialVersionUID 是相同的,可以正常进行反序列化;不手动指定 serialVersionUID 的值,反序列化时当前类有所改变,那么系统就会重新计算当前类的 hash 值并把它赋值给 serialVersionUID ,这时候当前类的 serialVersionUID 就会和序列化的数据中的 serialVersionUID 不一致,导致反序列化失败,程序出现crash。
56 | 静态成员变量属于类不属于对象,不会参与序列化过程。
57 | 用 transient 关键字标记的成员变量不参与序列化过程。
58 |
59 | 3. Parcelable 接口是 Android 中提供的新的序列化方式,实现这个接口,一个类的对象就可以实现序列化并可以通过 Intent 和 Binder 传递。
60 | ````java
61 | public class User implements Parcelable{
62 | public int userId;
63 | public String userName;
64 | public boolean isMale;
65 |
66 | public Book book;
67 |
68 | public User(int userId,String userName,boolean isMale){
69 | this.userId = userId;
70 | this.userName = userName;
71 | this.isMale = isMale;
72 | }
73 |
74 | //内容描述功能
75 | public int describeContents(){
76 | return 0; //几乎在所有情况下这个方法都返回0,仅当当前对象中存在文件描述时,返回1。
77 | }
78 |
79 | //序列化
80 | public void writeToParcel(Parcel out, int flags){
81 | out.writeInt(userId);
82 | out.writeString(userName);
83 | out.writeInt(isMale ? 1 : 0);
84 | out.writeParcelable(book,0);
85 | }
86 |
87 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){
88 | //创建序列化对象
89 | public User createFromParcel(Parcel in){
90 | return new User(in);
91 | }
92 | //创建序列化数组
93 | public User[] newArray(int size){
94 | return new User[size];
95 | }
96 | };
97 |
98 | //完成反序列化
99 | private User(Parcel in){
100 | userId = in.readInt();
101 | userName = in.readString();
102 | isMale = in.readInt() == 1;
103 | book = in.readParcelable(Thread.currentThread().getContextClassLoader());
104 | }
105 | }
106 | ````
107 | Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。
108 |
109 | Parcelable的方法说明。
110 | 1. createFromParcel(Parcel in):从序列化后的对象中创建原始对象。
111 | 2. newArray(int size) :创建指定长度的原始对象数组。
112 | 3. User(Parcel in):从序列化后的对象中创建原始对象。
113 | 4. writeToParcel(Parcel out,int flags):将当前对象写入序列化结构中,其中 flags 标识两种值:0或者1(标记位:PARCELABLE_WRITE_RETURN_VALUE)。为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0。
114 | 5. describeContents:返回当前对象的内容描述,如果含有文件描述符,返回1(标记位:CONTENTS_FILE_DESCRIPTOR),否则返回0,几乎所有情况都为0。
115 | 4. Serializable 和 Parcelable 的区别。
116 | Serializable 是 Java 中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作,Parcelable 是 Android 中的序列化方式,主要用在内存序列化上,适合在 Android 平台上使用,缺点就是使用起来稍微麻烦,但是效率很高。如果要将对象序列化到存储设备中或者将对象序列化后通过网络传输建议使用 Serializable。
117 |
118 | ## Binder
119 |
120 | 1. Binder 是 Android 中的一个类,实现了 IBinder 接口。
121 | 从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式。
122 | 从 Android Framework 角度来说,Binder 是 ServiceManager 链接各种 Manager(ActivityManager、WindowManager)和相应 ManagerService 的桥梁。
123 | 从 Android 应用层来说,Binder 是客户端和服务端进行通信的媒介。
124 | Android 开发中,Binder 主要用 Service 中,包括 AIDL 和 Messenger。
125 | 2.解释
126 | - DESCRIPTOR
127 | Binder 的唯一标识,一般用当前 Binder 的类名表示。
128 | - asInterface(android.os.IBinder obj)
129 | 用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,此方法返回的就是服务端的 Stub 对象本身,否则返回的是系统封装后的 Stub.proxy 对象。
130 | - asBinder
131 | 此方法用于返回当前 Binder 对象。
132 | - onTransact
133 | 这个方法运行在服务端中的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。如果此方法返回 false,那么客户端的请求会失败。
134 | - Proxy#getBookList
135 | - Proxy#addBook
136 |
137 | **Binder的工作机制**
138 | 
139 |
140 | 3. 手动实现一个Binder
141 | 1. 声明一个AIDL性质的接口,只需要继承 IInterface 接口即可,IInterface 接口中只有一个 asBinder 方法。
142 | 2. 实现 Stub 类和 Stub 类中的 Proxy 代理类。
143 |
144 | 4. Binder 中提供了两个配对的方法 linkToDeath 和 unlinkToDeath,通过 linkToDeath 可以给 Binder 设置一个死亡代理,当 Binder 死亡时,就会收到通知,从而重新发起连接请求来恢复连接。
145 | 首先声明一个 DeathRecipient 对象,DeathRecipient 是一个接口,其内部只有一个方法 binderDied,当 Binder 死亡的时候,系统就会回调 binderDied 方法,然后移除之前绑定的 binder 代理并重新绑定远程服务:
146 | ````java
147 | private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
148 | @override
149 | public void binderDied(){
150 | if(mBookManager == null)
151 | return;
152 | mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
153 | mBookManager = null;
154 | //TODO: 这里重新绑定远程 Service
155 | }
156 | }
157 | ````
158 | 其次,在客户端绑定远程服务成功后,给 binder 设置死亡代理:
159 | ````java
160 | mSerivce = IMessageBoxManager.Stub.asInterface(binder);
161 | binder,linkToDeath(mDeathRecipient,0);
162 | ````
163 |
164 | ## Android中的IPC方式
165 | 1. 文件共享适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。
166 | 2. 不建议在进程间通信中使用 SharePreferences,因为由于系统对它的读/写有一定的缓存策略,即在内存中会有一份 SharePreferences 文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问,SharePreferences 有很大几率会丢失数据。
167 | 3. Messenger 可以在不同进程中传递 Message 对象,它的底层实现是 AIDL。
168 | 4. Messenger 的工作原理
169 | 
170 | 5.AIDL 文件支持的数据类型
171 |
172 | - 基本数据类型(int、long、char、boolean、double等);
173 | - String 和 CharSequence;
174 | - List:只支持 ArrayList,里面每个元素都必须能够被 AIDL 支持;
175 | - Map: 只支持 HashMap,里面的每个元素都必须被 AIDL 支持,包括 key 和 value;
176 | - Parcelable:所有实现了 Parcelable 接口的对象;
177 | - AIDL:所有 AIDL 接口本身也可以在 AIDL 文件中使用。
178 |
179 | 6. AIDL 中除了基本数据类型,其他类型的参数必须标上方向:in、out或者inout, in 表示输入型参数,out 表示输出型参数,inout 表示输入输出型参数。
180 | 7. RemoteCallbackList 是系统专门提供的用于删除跨进程 listener 的接口,是一个泛型,支持管理任意的 AIDL 接口。工作原理是在它的内部有一个 Map 结构专门用来保存所有的 AIDL 回调,这个 Map 的 key 是 IBinder 类型,value 是 Callback 类型。
181 | 8. Binder 意外死亡后重新连接服务。
182 |
183 | - 给 Binder 设置 DeathRecipient 监听,当 Binder 死亡时,会收到 binderDied 方法的回调,在 binderDied 方法中可以重连远程服务。
184 | - 在 onServiceDisconnected 中重连远程服务。
185 | 区别在于:onServiceDisconnected 在客户端的 UI 线程中被回调,而 binderDied 在客户端的 Binder 线程池中被回调。
186 |
187 | 9. 如何在 AIDL 中进行权限验证。
188 |
189 | - 可以在 onBind 中进行验证,验证不通过就直接返回 null。验证方式比如使用 permission 验证,在 AndroidMenifest 中声明所需的权限
190 | ````java
191 |
194 | ````
195 | 如果是自己内部的应用想绑定到我们的服务中,只需要在它的 AndroidMenifest 文件中采用如下方式使用 permission :
196 | ````java
197 |
198 | ````
199 | - 可以在服务端的 onTransact 方法中进行权限验证,如果验证失败就直接返回false,这样服务端就不会终止执行 AIDL 中的方法从而达到保护服务端的效果。
200 |
201 | 10. ContentProvider 的六个抽象方法:
202 |
203 | - onCreate:代表 ContentProvider 的创建,做一些初始化操作。
204 | - query:查找。
205 | - update:更新。
206 | - insert:插入。
207 | - delete:删除。
208 | - getType:用来返回一个 Uri 请求所对应的 MIME 类型(媒体类型),比如图片、视频等,如果不关心可以直接在方法中返回 null 或者 "\*/\*"。
209 | onCreate 由系统回调并运行在主线程里,其他五个方法均由外界回调并运行在 Binder 线程池中。
210 |
211 | 11. ContentProvider 的权限可以细分为读权限和写权限,分别对应 android:readPermission 和 android:writePermission 属性,如果分别声明了读权限和写权限,那么外界应用也必须依次声明相应的权限才可以进行读/写操作,否则外界应用会异常终止。
212 | 12. android:authorities 是 ContentProvider 的唯一标识,通过这个属性外部应用就可以访问。
213 |
214 | ## Binder 连接池
215 | 1. Binder 连接池的工作原理
216 | 
217 |
218 | ## 选择合适的IPC方式
219 | 1. IPC 方式的优缺点和适用场景
220 | 
221 |
222 |
223 | 2017.8.15
224 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/JNI 和 NDK 编程.md:
--------------------------------------------------------------------------------
1 | # JNI 和 NDK 编程
2 | 1. Java JNI 本意是 Java Native Interface (Java 本地接口),是为了方便 Java 调用 C、C++等本地代码所封装的一层接口。
3 | 2. NDK 是 Android 所提供的一个工具集合,通过 NDK 可以在 Android 中更加方便地通过 JNI 来访问本地代码。
4 | 使用 NDK 的好处:
5 | - 提高代码的安全性;
6 | - 可以很方便地使用目前已有的 C/C++开源库;
7 | - 便于平台间的移植;
8 | - 通过程序在某些特定情形下的执行效率,但是并不能明显提升 Android 程序的性能。
9 |
10 | ## JNI 的开发流程
11 | 1. JNI 的开发流程:
12 |
13 | - 在 Java 中声明 native 方法;
14 | - 编译 Java 源文件得到 class 文件,然后通过 Java 命令导出 JNI 的头文件;
15 | - 实现 JNI 方法;
16 | - 编译 so 库并在 Java 中调用。
17 |
18 | ## NDK 的开发流程
19 | 1. NDK 的开发流程:
20 |
21 | - 下载并配置 NDK;
22 | - 创建一个 Android 项目,并声明所需的 native 方法;
23 | - 实现 Android 项目中所声明的 native 方法;
24 | - 切换到 jni 目录的父目录,然后通过 ndk-build 命令编译产生 so 库。
25 |
26 | ## JNI 的数据类型和类型签名
27 | 1. JNI 基本数据类型的对应关系
28 | 
29 | 2. JNI 引用类型的对应关系
30 | 
31 | 3. 基本数据类型的签名
32 | 
33 |
34 | ## JNI 调用 Java 方法的流程
35 | 1. JNI 调用 Java 方法的流程是先通过类名找到类,然后再根据方法名找到方法的 id,最后调用这个方法。
36 |
37 |
38 |
39 | 2017.8.30
40 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/View 的工作原理.md:
--------------------------------------------------------------------------------
1 | # View 的工作原理
2 | ## ViewRoot 和 DecorView
3 | 1. ViewRoot 对应于 ViewRootImpl 类,是连接 WindowManager 和 DecorView 的纽带,View 的三大流程均是通过 ViewRoot 来完成的。View 绘制流程是从 ViewRoot 的 performTraversals 方法开始的,经过 measure、layout 和 draw 三个过程才能最终将一个 View 绘制出来,其中 measure 用来测量 View 的宽和高,layout 用来确定 View 在父容器中的放置位置,draw 负责将 View 绘制在屏幕上。
4 |
5 | **performTraversals的大致流程**
6 | 
7 | performTraversals 会依次调用 performMeasure、performLayout 和 preformDraw 三个方法,这三个方法分别完成了顶级 View 的 measure、layout 和 draw 这三大流程,其中 performMeasure 中会调用 measure 方法,在 measure 方法中又回调用 onMeasure 方法,在 onMeasure 方法中则会对所有子元素进行 measure 过程,这个时候 measure 流程就就从父容器传递到子元素中,完成一次 measure 过程,接着子元素会重复父容器的 measure 过程,如此反复就完成了整个 View 树的遍历。
8 |
9 | ## MeasureSpec
10 | 1. MeasureSpec 代表一个32为 int 值,高2位代表 SpecMode,低30位代表 SpecSize,SpecMode 是指测量模式,SpecSize 是指在某种测量模式下的规格大小。
11 | 2. SpecMode有三类
12 |
13 | - UNSPECIFIED
14 | 父容器不对 View 有任何限制,这种情况一般用于系统内部,表示一种测量状态。
15 | - EXACTLY
16 | 父容器已经检测出 View 所需要的精确大小,这个时候 View 的最终代大小就是 SpecSize 所指定的值,对应于 LayoutParams 中的 match_parent 和具体的数值这两种模式。
17 | - AT_MOST
18 | 父容器指定了一个可用大小即 SpecSize,View 的大小不能大于这个值,具体要看不同 View 的具体实现,对应于 LayoutParams 中的 wrap_content。
19 |
20 | 3. MeasureSpec 不是唯一由 LayoutParams 决定的,LayoutParams 需要和父容器一起才能决定 View 的 MeasureSpec,从而进一步决定 View 的宽/高。
21 | 对于 DecorView,其 MeasureSpec 由窗口的尺寸和其自身的 LayoutParams 来共同确定; 对于普通 View,其 MeasureSpec 由父容器的 MeasureSpec 和自身的 LayoutParams 来共同决定。
22 | 4. DecorView 的 MeasureSpec 的产生过程遵守如下规则:
23 |
24 | - LayoutParams.MATCH_PARENT:精确模式,大小就是窗口的大小。
25 | - LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口的大小。
26 | - 固定大小:精确模式,大小为 LayoutParams 中指定的大小。
27 |
28 | 5. 当 View 采用固定宽/高的时候,不过父容器的 MeasureSpec 是什么,View 的 MeasureSpec 都是精确模式并且其大小遵循 LayoutParams 中的大小。
29 | 当 View 的宽/高是 match_parent 时,如果父容器的模式是精准模式,那么 View 也是精准模式并且其大小是父容器的剩余空间; 如果父容器是最大模式,那么 View 也是最大模式并且其大小不会超过父容器的剩余空间。
30 | 当 View 的宽/高是 wrap_content 时,不管父容器的模式是精准还是最大化,View 的模式总是最大化并且大小不能超过父容器的剩余空间。
31 |
32 | 6. 普通View的MeasureSpec的创建规则
33 | 
34 |
35 | ## View 的工作流程
36 | 1. View 的工作流程主要是指 measure、layout、draw 这三大流程,即测量、布局、绘制。其中 measure 确定 View 的测量宽/高,layout 确定 View 的最终宽/高和四个顶点的位置,drea 则将 View 绘制到屏幕上。
37 | 2. measure过程
38 |
39 | - View 的 measure 过程
40 | View 的 measure 过程由其 measure 方法来完成,measure 方法是一个 final 类型方法,在 View 的 measure 方法中回去调用 View 的 onMeasure 方法。
41 |
42 | getSuggestedMinimumWidth 的逻辑:如果 View 没有设置背景,那么返回 android:miniWidth 这个属性所指定的值,这个值可以为0;如果 View 设置了背景,则返回 android:minWidth 和背景的最小宽度这两者中的最大值,getSuggestedMinimumWidth 和 getSuggestedMinimumHeight 的返回值就是 View 在 UNSPECIFIED 情况下的测量宽/高。
43 |
44 | 直接继承 View 的自定义控件需要重写 onMeasure 方法并设置 wrap_content 时的自身大小,否则在布局中使用 wrap_content 就相当于使用 match_parent。
45 |
46 | - ViewGroup 的 measure 过程
47 | 对于 ViewGroup 来说,除了完成自己的 measure 过程以外,还会遍历去调用所有子元素的 measure 方法,各个子元素再递归去执行这个过程。和 View 不同的是,ViewGroup 是一个抽象类,没有重写 View 的 onMeasure 方法,但是提供了一个 measureChildren 的方法。
48 |
49 | measureChildren 的思想就是取出子元素的 LayoutParams,然后再通过 getChildMeasureSpec 来创建子元素的 MeasureSpec,接着将 MeasureSpec直接传递给 View 的 measure 方法来进行测量。
50 |
51 | 实际上在 onCreate、onStart、onResume 中均无法正确得到某个 View 的宽/高信息,因为 View 的 measure 过程和 Activity 的生命周期方法不是同步执行的,无法保证 Activity 执行了 onCreate、onStart、onResume 时某个 View 已经测量完毕了。
52 | 解决方法:
53 |
54 | - Activity/View#onWindowFocusChanged
55 | 这个方法的含义是:View 已经初始化完毕,宽/高已经准备好了,这个时候去获取宽/高是没有问题,但是 onWindowFocusChanged 会被调用多次,当 Activity 的窗口得到焦点和失去焦点时均会被调用一次。
56 | - view.post(runnable)
57 | 通过 post 可以将一个 runnable 投递到消息队列的尾部,然后等待 Looper 调用此 runnable 的时候,View 也已经初始化好了。
58 | - ViewTreeObserver
59 | - view.measure(int widthMeasureSpec,int heightMeasureSpec)
60 | 通过手动对 View 进行 measure 来得到 View 的宽/高。这个需要根据 View 的 LayoutParams 来分情况处理:
61 | 1. match_parent:
62 | 直接放弃,无法 measure 出具体的宽/高,因为我们无法知道 parentSize 的大小,理论上不可能测量出 View 的大小。
63 | 2. 具体的数值(dp/px)
64 | 3. wrap_content
65 |
66 | **两种错误的用法**
67 | 一是违背了系统内部实现规范;其次不能保证一定能 measure 出正确的结果。
68 | 第一种错误用法
69 | ````java
70 | int widthMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
71 | int heightMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
72 | view.measure(widthMeasureSpec,heightMeasureSpec);
73 | ````
74 | 第二种错误用法
75 | ````java
76 | view.measure(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
77 | ````
78 |
79 | - layout 过程
80 | Layout 的作用是 ViewGroup 用来确定子元素的位置,当 ViewGroup 的位置被确定后,它在 onLayout 中会遍历所有子元素并调用其 layout 方法,在 layout 方法中 onLayout 方法又会被调用。layout 方法确定 View 本身的位置,onLayout 方法则会确定所有子元素的位置。
81 |
82 | 大致流程:
83 | 首先会通过 setFrame 方法来设定 View 的四个顶点的位置,即初始化 mLeft、mRight、mTop 和 mBottom 这四个值,从而确定 View 在父容器中的位置。
84 | 接着会调用 onLayout 方法,用途是父容器确定子元素的位置。
85 |
86 | 和 onMeasure 方法类似,onLayout 的具体实现同样和具体的布局有关,所以 View 和 ViewGroup 均没有真正实现 onLayout 方法。
87 |
88 | - draw 过程
89 | 作用是将 View 绘制到屏幕上。
90 | View 的绘制过程遵循如下几步:
91 |
92 | - 绘制背景 background.draw(canvas);
93 | - 绘制自己 (onDraw);
94 | - 绘制 children(dispatchDraw);
95 | - 绘制装饰(onDrawScrollBars)。
96 |
97 | ## 自定义 View
98 | 1. 自定义 View 的分类
99 |
100 | - 继承 View 重写 onDraw 方法
101 | 主要用于实现一些不规则的效果往往需要静态或者动态地实现一些不规则的图形。采用这种方式需要自己支持 wrap_content,并且 padding 也需要自己处理。
102 | - 继承 ViewGroup 派生特殊的 Laout
103 | 主要用于实现自定义的布局,需要合适地处理 ViewGroup 的测量、布局这两个过程,并同时处理子元素的测量和布局过程。
104 | - 继承特定的 View(比如 TextView)
105 | 用于扩展某种已有的 View 的功能。
106 | - 继承特定的 ViewGroup (比如 LinearLayout)
107 | 与方法2的区别是方法2更接近 View 的底层。
108 |
109 | 2. 自定义 View 须知
110 |
111 | - 让 View 支持 wrap_content;
112 | - 如果有必要,让 View 支持 padding;
113 | - 尽量不要在 View 中使用 Handler,没必要;
114 | - View 中如果有线程或者动画,需要及时停止,参考 View#onDtachedFromWindow;
115 | - View 带有滑动嵌套情形时,需要处理好滑动冲突;
116 |
117 | 3. 添加自定义属性的方法
118 |
119 | - 在 values 目录下面创建自定义属性的 XML,在 XML 中声明一个自定义属性的集合;
120 | - 在 View 的构造方法中解析自定义属性的值并做相应处理;
121 | - 在布局文件中使用自定义属性。
122 | 需要注意的是为了使用自定义属性,必须在布局文件中添加 schemas 声明
123 | ````
124 | xmlns:app = http://schemas.android.com/apk/res-auto
125 | ````
126 | 或
127 | ````
128 | xmlns:app = http://schemas.android.com/apk/res-auto/com.ryg_chapter_4
129 | ````
130 |
131 |
132 |
133 | 2017.8.18
134 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/View的事件体系.md:
--------------------------------------------------------------------------------
1 | # View 的事件体系
2 | ## View
3 | 1. View 是 Android 中所有控件的基类,是一种界面层的抽象,代表了一个控件。ViewGroup 也继承了 View,意味着 View 本身就可以是单个控件也可以是由多个控件组成的一组控件。
4 | **View 树的结构**
5 | 
6 | 2. View 的位置主要由它的四个顶点来决定,分别对应于 View 的四个属性:
7 | - top:左上角纵坐标;
8 | - left:左上角横坐标;
9 | - right:右下角横坐标;
10 | - bottom:右下角纵坐标;
11 | 需要注意的是这些坐标是相对于 View 的父容器来说的,是一种相对坐标。
12 | **View 的位置坐标和父容器的关系**
13 | 
14 | Viewd 的宽高和坐标的关系:
15 | >width = right - left
16 | height = bottom - top
17 |
18 | View 四个参数的获取方式
19 |
20 | - Left = getLeft();
21 | - Right = getRight();
22 | - Top = getTop();
23 | - Bottom = getBottom();
24 |
25 | Android 3.0后,View 新增了 x、y、translationX 和 translationY 四个参数。其中 x 和 y 是 View 左上角的坐标,translationX 和 translationY 是 View 左上角相对于父容器的偏移量,并且 translationX 和 translationY 的默认值为0。
26 | View 在平移过程中,top 和 left 表示的是原始左上角的位置信息,其值不会发生改变,发生改变的是 x、y、translationX 和 translationY。
27 |
28 | 3. MotionEvent
29 | 在手指接触屏幕后所产生的一系列事件中,典型的事件类型有:
30 |
31 | - ACTION_DOWN --- 手指刚接触屏幕;
32 | - ACTION_MOVE --- 手指在屏幕上移动;
33 | - ACTION_UP --- 手指从屏幕上松开的一瞬间;
34 | 正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件:
35 |
36 | - 点击屏幕后离开松开,事件序列为 DOWN -> UP;
37 | - 点击屏幕滑动一会再松开,事件序列为 DOWN -> MOVE -> ... -> MOVE -> UP。
38 |
39 | getX/getY 返回的是相对于当前 View 左上角的 x 和 y 坐标,getRawX/getRawY 返回的是相对于手机屏幕左上角的 x 和 y 坐标。
40 |
41 | 4. TouchSlop 是系统所能识别出的被认为是滑动的最小的距离,这是一个常量,和设备有关,可以通过 **ViewConfiguration.get(getContext()).getScaledTouchSlop()** 来获取这个常量。
42 |
43 | 5. VelocityTracker
44 | 速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。
45 | **使用方法:**
46 | 首先在 View 的 onTouchEvent 方法中追踪当前单击事件的速度:
47 | ````java
48 | VelocityTracker velocityTracker = VelocityTracker.obtained();
49 | velocityTracker.addMovenment(event);
50 | ````
51 | 接着当先知道当前的滑动速度时,可以采用如下方式获取当前的速度:
52 | ````java
53 | velocityTracker.computeCurrentVelocity(1000); //获取速度之前必须先计算速度,所以这个方法必须放在下面2个方法前面。
54 | int xVelocity = (int) velocityTracker.getXVelocity();
55 | int yVelocity = (int) velocityTracker.getYVelocity();
56 | ````
57 | 这里的速度是指一段时间内手指所滑过的像素数,可以为负数,即当手指从右往左滑动时水平方向速度即为负数,公式:
58 | **速度 = (终点位置 - 起点位置)/ 时间段**
59 | 最后,当不需要使用的时候,调用 clear 方法来重置并回收内存
60 | ````java
61 | velocityTracker.clear();
62 | velocityTracker.recycle();
63 | ````
64 |
65 | 6. GustureDetector
66 | 手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。
67 | **使用方法:**
68 | 首先创建一个 GustureDetector 对象并实现 OnGestureListener 接口,也可以实现 OnDoubleTapListener 从而能够监听双击行为:
69 | ````java
70 | GustureDetector mGustureDetector = new GustureDetector(this);
71 | //解决长按屏幕后无法拖动的现象
72 | mGustureDetector.setIsLongpressEnabled(false);
73 | ````
74 | 接着接管目标 View 的 onTouchEvent 方法,在监听 View 的 onTouchEvent 方法中添加如下实现:
75 | ````java
76 | boolean consume = mGustureDetector.onTouchEvent(event);
77 | return consume;
78 | ````
79 | **GustureDetector和onDoubleTaoListener中的方法介绍**
80 | 
81 | >参考:
82 | 如果只是监听相关滑动,建议自己在 onTouchEvent 中实现,如果要监听双击这种行为的话,就使用 GestureDetector。
83 |
84 | 7. Scoller
85 | 弹性滑动对象,用于实现 View 弹性滑动,需要与 View 的 computeScroll 方法配合使用。
86 | **使用方法:**
87 | ````java
88 | Scroller scroller = new Scroller(mContext);
89 |
90 | //缓慢滚动到指定位置
91 | private void smoothScrollTo(int destX,int destY){
92 | int scrollX = getScrollX();
93 | int delta = destX - scrollX;
94 | //1000ms 内滑向 destX,效果就是慢慢滑动
95 | mScroller.startScroll(scrollX,0,dalta,0,1000);
96 | invalidate();
97 | }
98 |
99 | @override
100 | public void computeScroll(){
101 | if(mScroller.computeScrollOffset()){
102 | scrollTp(mScroller.getCurrX(),mScroller.getCurrY());
103 | postInvalidate();
104 | }
105 | }
106 | ````
107 |
108 | ## View 的滑动
109 | 1. 通过三种方式可以实现 View 的滑动:
110 | - 通过 View 本身提供的 scrollTo/scrollBy 方法来实现滑动。
111 | **mScrollX 和 mScrollY 的改变规则:**
112 | 在滑动的过程中,mScrollX 的值总是等于 View 左边缘和 View 内容左边缘在水平方向的距离,mScrollY 的值总是等于 View 上边缘和 View 的内容上边缘在竖直方向的距离。View 边缘是指 View 的位置,由四个顶点组成,View 的内容边缘是指 View 中的内容的边缘,scrollTo 和 scrollBy 只能改变 View 内容的位置而不能改变 View 在布局中的位置,mScrollX 和 mScrollY 的单位为像素,当 View 左边缘在 View 内容边缘的右边时,mScrollX 为正值,反之为负值;当 View 上边缘在 View 内容上边缘的下边时,mScrollY 为正值,反之为负。也就是说如果从左往右滑动,那么 mScrollX 为负值,反之为正值;如果从上往下滑动,那么 mScrollY 为负值,反之为正值。
113 |
114 | 使用 scrollTo 和 scrollBy 来实现 View 的滑动,只能将 View 的内容进行移动,并不能将 View 本身进行移动,也就是说,不管怎么滑动,也不可能将当前 View 滑动到附近 View 所在的区域。
115 |
116 | - 通过动画给 View 施加平移效果来实现滑动。
117 | 通过动画能够让一个 View 进行平移,平移是一种滑动,使用动画来移动 View ,主要操作 View 的translationX 和 translationY 属性,既可以采用传统的 View 动画,也可以采用属性动画。
118 | 使用动画来做 View 的滑动需要注意一点,View 动画是对 View 的影像做操作,并不能真正改变 View 的位置参数,包括宽/高,如果希望动画后的状态得以保留还必须将 fillAfter 属性设置为 true,否则动画完成后其动画结果会消失。使用属性动画不存在这种问题,但是3.0以下无法使用属性动画。
119 | - 通过改变 View 的 LayoutParams 使得 View 重新布局从而实现滑动。
120 | ````java
121 | MarginLayoutParams params = (MarginLayoutParams) mButton1.getLayoutParams();
122 | params.width += 100;
123 | params.leftMargin += 100;
124 | mButton1.requestLayout();
125 | //或者 mButton1.setLayoutParams(params);
126 | ````
127 | 2. 各种滑动方式的对比
128 |
129 | - scrollTo/scrollBy:操作简单,适合对 View 内容的滑动;
130 | - 动画:操作简单,主要适用于没有交互的 View 和实现复杂的动画效果;
131 | - 改变布局参数:操作稍微负责,适用于没有交互的 View。
132 |
133 | ## 弹性滑动
134 | 1. Scoller 如何让 View 弹性滑动的
135 | invalidate 方法会导致 View 重绘,在 View 的 draw 方法中又会去调用 computeScroll 方法,computeScroll 方法会在 View 中是一个空实现,因为这个 computeScroll 方法, View 才能实现弹性滑动。
136 | 2. Scroller 的工作原理
137 | Scroller 本身并不能实现 View 的滑动,需要配合 View 的 computeScroll 方法才能完成弹性滑动的效果,它不断让 View 重绘,每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔 Scroller 就可以得出 View 当前的滑动位置,知道了滑动位置就可以通过 scrollTo 方法来完成 View 的滑动。就这样,View 的每一次重绘都会导致 View 进行小幅度的滑动,而多次的小幅度滑动就组成了弹性滑动。
138 | 3. 延时策略的核心思想是通过发送一系列延时消息从而达到一种渐进式的效果,具体来说可以用 Handler 和 View 的 postDelayed 方法,也可以使用线程的 sleep 方法。
139 |
140 | ## View 的事件分发机制
141 | 1. 所谓点击事件的事件分发,其实就是对 MotionEvent 事件的分发过程,即当一个 MotionEvent 产生了以后,系统需要把这个时间传递给一个具体的 View ,而这个传递的过程就是分发过程。
142 | 点击事件的分发过程由三个方法共同完成。
143 |
144 | - public boolean dispatchTouchEvent(MotionEvent ev)
145 | 用来进行事件的分发。如果事件能够传递给当前 View ,那么此方法一定会被调用,返回结果受当前 View 的 onTouchEvent 和下级 View 的 dispatchTouchEvent 方法的影响,表示是否消耗当前事件。
146 | - public boolean onInterceptTouchEvent(MotionEvent event)
147 | 在上述方法内部调用,用来判断是否拦截某个事件,如果当前 View 拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
148 | - public boolean onTouchEvent(MotionEvent event)
149 | 在 dispatchTouchEvent 方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前 View 无法再次接收到事件。
150 |
151 | **用伪代码表示此三者的关系:**
152 | ````java
153 | public boolean dispatchTouchEvent(MotionEvent ev){
154 | boolean consume = false;
155 | if(onInterceptTouchEvent(ev)){
156 | consume = onTouchEvent(ev);
157 | }else{
158 | consume = child.dispatchTouchEvent(ev);
159 | }
160 | return consume;
161 | }
162 | ````
163 | **传递规则:**
164 | 对于一个根 ViewGroup 来说,点击事件产生后,首先会传递给它,这是它的 dispatchTouchEvent 就会被调用,如果这个 ViewGroup 的 onInterceptTouchEvent 方法返回 true 就表示它要拦截当前事件,接着事件就会交个这个 ViewGroup 处理,即它的 onTouchEvent 方法就会被调用;如果 ViewGroup 的 onInterceptTouchEvent 返回 false 就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的 dispatchTouchEvent 方法就会被调用,如此反复我都直到事件被最终处理。
165 |
166 | 当一个 View 需要处理事件时,如果设置了 OnTouchListener,那么 OnTouchListener 中的 onTouch 方法会被调用,这时事件如何处理还要看 onTouch 的返回值,如果返回 false,则当前 View 的 onTouchEvent 方法会被调用,如果返回 true,那么 onTouchEvent 方法将不会被调用。所以,给 View 设置的 OnTouchListener 其优先级比 onTouchEvent 要高,onClickListener 的优先级最低。
167 |
168 | 当一个事件产生后,它的传递过程遵循如下顺序:Activity -> Window -> View ,即事件总是先传递给 Activity,Activity 再传递给 Window,最后 Window 再传递给顶级 View,顶级 View 接收到事件后,就会按照事件分发机制去分发事件。
169 |
170 | 2. 当 ViewGroup 决定拦截事件后,那么后续的点击事件将会默认交给它处理并且不再调用它的 onInterceptTouchEvent 方法,FLAG_DISALLOW_INTERCEPT 这个标志的作用是让 ViewGroup 不再拦截事件,但前提是 ViewGroup 不拦截 ACTION_DOWN 事件。
171 |
172 | ## View 的滑动冲突
173 | 1. 常见的滑动冲突场景分为三种
174 |
175 | - 场景1 --- 外部滑动方向和背部滑动方向不一致;
176 | - 场景2 --- 外部滑动方向和内部滑动方向一致;
177 | - 场景3 --- 上两种情况的嵌套。
178 |
179 | 2. 处理规则
180 |
181 | - 对于场景1,处理规则是:当用户左右滑动时,需要让外包的 View 拦截点击事件,当用户上下滑动时,需要让内部 View 拦截点击事件。具体来说就是根据滑动是水平滑动还是竖直滑动来判断到底由谁来拦截事件。
182 | - 对于场景2,一般都能在业务上找到突破点。
183 | - 对于场景3,一般都能在业务上找到突破点。
184 |
185 | 3. 滑动冲突的解决方式
186 |
187 | - 外部拦截法
188 | 是指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,不需要就不拦截。外部拦截法需要重写父容器的 onInterceptTouchEvent 方法,在内部做相应的拦截即可。
189 | - 内部拦截法
190 | 是指父容器不拦截任何事件,所有事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理,需要配合 requestDisallowInterceptTouchEvent 方法才能正常工作,需要重写子元素的 dispatchTouchEvent 方法。
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/四大组件的工作过程.md:
--------------------------------------------------------------------------------
1 | # 四大组件的工作过程
2 | ## 四大组件的运行状态
3 | 1. Activity 是一种展示型组件,用于向用户直接地展示一个界面,并且可以接收用户的输入信息从而进行交互。Activity 的启动由 Intent 触发,Intent 分为显示和隐式,显示 Intent 可以明确地指向一个 Activity 组件,隐式 Intent 则指向一个或多个目标 Activity 组件。在实际开发中可以通过 Activity 的 finish 方法来结束一个 Activity 组件的运行。
4 | 2. Service 是一种计算型组件,用于在后台执行一系列计算任务。Service 组件有启动状态和绑定状态,当处于启动状态时,Service 内部可以做一些后台计算,并且不需要和外界有直接的交互。当 Service 组件处于绑定状态时,Service 内部也同样可以进行后台计算,可以很方便地和 Service 组件进行通信。Service 组件的停止需要灵活采用 stopService 和 unBindService 这两个方法才能完全停止一个 Service 组件。
5 | 3. BroadcastReceiver 是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。BroadcastReceiver 也叫广播,有静态注册和动态注册两种注册方式。静态注册是指在 AndroiManifest 中注册广播,此种形式的广播不需要启动就可以收到相应的广播;动态注册需要通过 Context.registerReceiver() 来实现,在不需要的时候要通过 Context.unRegisterReceiver() 来解除广播,此种形态的广播必须要应用启动才能注册并接收广播,实际开发中通过 Context 的一系列 send 方法来发生广播,被发送的广播会被系统发送给感兴趣的广播接收者,发送和接收过程的匹配是通过广播接收者 来描述的。BroadcastReceiver 组件没有停止的概念,不需要停止。
6 | 4. ContentProvicder 是一种数据共享型组件,用于想其他组件乃至其他应用共享数据。它的内部需要实现增删改查这四种操作,内部维持着一份数据集合, ContentProvicder 对数据集合的具体实现并没有任何要求,也不需要手动停止。
7 |
8 | ## Activity 的工作过程
9 | 1.
10 | Activity的启动过程在ActivityStackSypervisor和ActivityStack之间传递顺序
11 | 
13 | 2. performLaunchActivity 这个方法主要完成的工作
14 | - 从 ActivityClientRecord 中获取待启动的 Activity 的组件信息;
15 | - 通过 Instrumentation 的 newsActivity 方法使用类加载器创建 Activity 对象;
16 | - 通过对 LoadedApk 的 makeApplication 方法来尝试创建 Application 对象;
17 | - 创建 ContextImpl 对象并通过 Activity 的 attach 方法来完成一些重要数据的初始化,
18 | - 调用 Activity 的 onCreate 方法。
19 |
20 | ## Service 的工作过程
21 | 1. Service 分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其它组件和 Service 的交互。Service 的这两种状态是可以共存的,即 Service 既可以处于启动状态也可以同时处于绑定状态。
22 | 2. Service 的启动过程
23 | 1. handleCreateService 主要完成的工作
24 | - 首先通过类加载器创建 Service 的实例;
25 | - 然后创建 Application 对象并调用其 onCreate;
26 | - 接着创建 ContextImpl 对象并通过 Service 的 attach 方法建立二者之间的关系;
27 | - 最后调用 Service 的 onCreate 方法将 Service 对象存储到 ActivityThread 中的一个列表中。
28 | 3. Service 的绑定过程
29 | 1. bindServiceCommon 方法主要完成的工作
30 | - 首先将客户端的 ServiceConnection 对象转化为 ServiceDispatcher.InnerConnection 对象;
31 | - 接着 bindServiceCommon 方法会通过 AMS 来完成 Service 的具体绑定过程,对应于 AMS 的 bindService 方法。
32 |
33 | ## BroadcastReceiver 的工作过程
34 | 1. 广播的发送类型有
35 | - 普通广播
36 | - 有许广播
37 | - 粘性广播
38 | 2. 广播的发送和接收其本质是一个过程的两个阶段。
39 | 3. 两个标记位
40 | - FLAG_INCLUDE_STOPPED_PACKAGES:表示包含已经停止的应用,这个时候广播会发生给已经停止的应用。
41 | - FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含已经停止的应用,这个时候广播不会发送给已经停止的应用。
42 |
43 | ## ContentProvicder 的工作过程
44 | 1. ContentProvicder 的启动过程
45 | 
46 |
47 | 2. 一般来说,ContentProvicder 都应该是单实例的,但由它的 android:multiprocess 属性来决定的,当 android:multiprocess 为 false 时,ContentProvicder 是单实例;当 android:multiprocess 为 true 时,ContentProvicder 为多实例,这个时候在每个调用者的进程中都存在一个 ContentProvicder 对象。
48 | 3. ActivityThread 的 handleBindApplication 完成 Application 的创建以及 ContentProvicder 的创建的步骤:
49 | - 创建 ContextImpl 和 Instrumentation;
50 | - 创建 Application 对象;
51 | - 启动当前进程的 ContentProvicder 并调用其 onCreate 方法;
52 | - 调用 Application 的 onCreate 方法。
53 |
54 |
55 |
56 |
57 | 2017.8.24
58 | W.Z.H
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/理解 RemoteViews.md:
--------------------------------------------------------------------------------
1 | # 理解 RemoteViews
2 | RemoteViews 表示的是一个 View 结构,可以在其他进程中显示,RemoteViews 提供了一组基础的操作用于跨进程更新它的界面。RemoteViews 在 Android 中的使用场景有两种:通知栏和桌面小部件。
3 |
4 | ## RemoteViews 的应用
5 | 1. AppWidgetProvider 是 Android 中提供的用于实现桌面小部件的类,其本质是一个广播,即 BroadcastReceiver。
6 |
7 | - onEnable:当该窗口小部件第一次添加到桌面时调用该方法,可添加多次但只在带一次调用。
8 | - onUpdate:小部件被添加时或者每次小部件更新时都会调用一次该方法,小部件的更新时机由 updatePeriodMillis 来指定,每个周期小部件都会自动更新一次。
9 | - onDeleted:每删除一次桌面小部件就调用一次。
10 | - onDisabled:当最后一个该类型的桌面小部件被删除时调用该方法,注意是最后一个。
11 | - onReceive:这是广播的内置方法,用于分发具体的事件给其他方法。会根据不同的 Action 来分别调用 onEnable、onDisabled 和 onUpdate 等方法。
12 |
13 | 2. PendingIntent
14 | PendingIntent 表示一种处于 pending 状态的意图,而 pending 状态表示的是一种待定、等待、即将发生的意思。和 Intent 的区别在于:PendingIntent 是在将来的某个待定的时刻发生,而 Intent 是立刻发生。PendingIntent 通过 send 和 cancel 方法来发送和取消特定的待定 Intent。
15 |
16 | PendingIntent 支持三种待定意图:启动 Activity、启动 Service 和发送广播
17 | **PendingIntent的主要方法**
18 | 
19 | requestCode 表示 PendingIntent 发送方的请求码,多数情况下设为0即可,也会影响到 flags 的效果。flags 常见类型有:
20 |
21 | - FLAG_ONE_SHOT
22 | 当前描述的 PendingIntent 只能使用一次,然后它就会自动 cancel,如果后续还有相同的 PendingIntent,那么它们的 send 方法就会调用失败。对于通知栏消息来说,如果采用此标记位,那么同类的通知只能使用一次,后续的通知单击后将无法打开。
23 | - FLAG_NO_CREATE
24 | 当前描述的 PendingIntent 不会主动创建,如果当前 PendingIntent 之前不存在,那么 getActivity、getService 和 getBroadcast 方法就会直接返回 null。这个标记位无法单独使用。
25 | - FLAG_CANCEL_CURRENT
26 | 当前描述的 PendingIntent 如果已经存在,那么它都会被 cancel,然后系统会创建一个新的 PendingIntent。对于消息通知栏来说,那些被 cancel 的消息单击后将无法打开。
27 | - FLAG_UPDATE_CURRENT
28 | 当前描述的 PendingIntent 如果已经存在,那么它们都会被更新,即它们的 Intent 中的 Extras 会被替换成新的。
29 |
30 | PendingIntent 的匹配规则:如果两个 PendingIntent 它们内部的 Intent 相同并且 requestCode 也相同,那么这两个 PendingIntent 就是相同的。
31 | Intent 的匹配规则:如果两个 Intent 的 ComponentName 和 intent-filter 都相同,那么这两个 Intent 就是相同的。
32 |
33 | ## RemoteViews 的内部机制
34 | 1. RemoteViews 支持的类型:
35 |
36 | - Layout
37 | FrameLayout、LinearLayout、RelativeLayout、GridLayout
38 | - View
39 | AnalogClock、Button、Chronometer、ImageButton、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper、ViewStub
40 |
41 | 2. RemoteViews 的部分 set 方法
42 | 
43 |
44 | 3. RemoteViews 的内部机制
45 | 
46 |
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/理解 Window 和 WindowManager.md:
--------------------------------------------------------------------------------
1 | # 理解 Window 和 WindowManager
2 | Window 是一个抽象类,它的具体实现是 PhoneWindow,创建一个 Window 只需要通过 WindowManager 即可完成,WindowManager 是外界访问 Window 的人口,Window 的具体实现位于 WindowManagerService 中,WindowManager 和 WindowManagerService 的交互是一个 IPC 过程,Android 中所有的视图都是通过 Window 来呈现的,Window 实际是 View 的直接管理者。
3 |
4 | ## Window 和 WindowManager
5 | 1. WindowManager.LayoutParams 中的 flags 和 type 参数。
6 | Flags 参数表示 Window 的属性,
7 | - FLAG_NOT_FOCUSABLE:表示 Window 不需要获取焦点,也不需要接受各种输入事件,同时会开启 FLAG_NOT_TOUVH_MODAL,最终事件会直接传递给下层的具有焦点的 Window。
8 | - FLAG_NOT_TOUVH_MODAL:在此模式下,系统会将当前 Window 区域以外的单击事件传递给底层的 Window,当前 Window 区域以内的单击事件则自己处理。
9 | - FLAG_SHOW_WHEN_LOCKED:开启此模式可以让 Window 显示在锁屏的界面上。
10 |
11 | Type 参数表示 Window 的类型。Window有三种类型:
12 |
13 | - 应用 Window:对应着一个 Activity;
14 | - 子 Window:不能单独存在,需要附属在特定的父 Window 之中;
15 | - 系统 Window:是需要声明权限才能创建的 Window。
16 |
17 | Window 是分层的,每个 Window 都有对应的 z-ordered,层级大的会覆盖在层级小的 Window 上面,应用 Window 的层级范围是1-999,子 Window 的层级范围是 1000-1999,系统 Window 的层级范围是 2000-2999。这些层级范围对应着 WindowManager.LayoutParams 的 type 参数。
18 |
19 | WindowManager 提供的三个常用方法:添加 View,更新 View,删除 View。
20 |
21 | ## Window 的内部机制
22 | 1. Window 是一个抽象的概念,每一个 Window 都对应着一个 View 和一个 ViewRootImpl,Window 和 View 通过 ViewRootImpl 来建立联系,因此 Window 并不是实际存在的,是以 View 的形式存在,View 才是 Window 存在的实体。
23 |
24 | 2. Window 的添加过程
25 | Window 的添加过程需要通过 WindowManager 的 addView 来实现。
26 | - 检查参数是否合法,如果是子 Window 那么还需要调整一些布局参数;
27 | - 创建 ViewRootImpl 并将 View 添加到列表中;
28 | - 通过 ViewRootImpl 来更新界面并完成 Window 的添加过程。
29 |
30 | 3. Window 的删除过程
31 | 先通过 WindowManagerImpl,再通过 WindowManagerGlobal 来实现。
32 | removeView 的逻辑是首先通过 findViewLocked 来查找待删除的 View 的索引,这个查找过程就是建立的数组遍历,然后再调用 removeViewLocked 来做进一步的删除。
33 | 在 WindowManager 中提供了两种删除接口 removeView 和 removeViewImmediate,分别表示异步删除和同步删除。
34 | 异步删除操作:由 ViewRootImpl 的 die 方法完成,在异步删除的情况下,die 方法只是发生了一个请求删除的消息后就立刻返回,这个时候 View 并没有完成删除操作,所以最后会将其添加到 mDyingViews 中,mDyingViews 表示待删除的 View 列表。
35 | dispatchDetachedFromWindow 方法主要做四件事:
36 | - 垃圾回收相关的工作,比如清除数据和消息、移除回调;
37 | - 通过 Session 的 remove 方法删除 Window:mWindowSession.remove(mWindow),这同样是一个 IPC 过程,最终会调用 WindowManagerService 的 removeWindow 方法。
38 | - 调用 View 的 dispatchDetachedFromWindow 方法,在内部会调用 View 的 onDetachedFromWindow() 以及 onDetachedFromWindowInternal()。
39 | - 调用 WindowManagerGlobal 的 doRemoveView 方法刷新数据,包括 mRoots、mParams 以及 mDyingViews,需要将当前 Window 所关联的这三类对象从列表中删除。
40 |
41 | 4. Window 的更新过程
42 | updateViewLayout 方法:
43 | 首先需要更新 View 的 LayoutParams 并替换掉老的 LayoutParams,接着再更新 ViewRootImpl 中的 LayoutParams,这是通过 ViewRootImpl 的 setLayoutParams 方法来实现的。在 ViewRootImpl 中会通过 scheduleTraversals 方法来对 View 重新布局,包括测量、布局、重绘这三个过程。ViewRootImpl 还会通过 WindowSession 来更新 Window 的视图,这个过程最终室友 WindowManagerService 的 relayoutWindow() 来具体实现,同样也是一个 IPC。
44 |
45 | ## Window 的创建过程
46 | 1. Activity 的 Window 的创建过程
47 | PhoneWindow 的 setContentView 方法大致遵循如下几个步骤
48 | - 如果没有 DecorView,那么就创建它;
49 | DecorView 的创建过程由 installDecor 方法来完成,在方法内部会通过 generateDecor 方法来直接创建 DecorView。为了初始化 DecorView 的结构,PhoneWindow 还需要通过 generateLayout 方法来加载具体的布局文件到 DecorView 中。
50 | - 将 View 添加到 DecorView 的 mContentParent 中;
51 | mLayoutInflater.inflate(layoutResID,mContentParent);
52 | - 回调 Activity 的 onContentChanged 方法通知 Activity 视图已经发生改变。
53 |
54 | 在 ActivityThread 的 handleResumeActivity 方法中,首先会调用 Activity 的 onResume 方法,接着会调用 Activity 的 makeVisible(),这时候 DecorView 才真正地完成了添加和显示这两个过程,Activity 才能被用户看到。
55 |
56 | 2. Dialog 的 Window 的创建过程
57 | Dialog 的 Window 的创建过程和 Activity 类似,
58 | - 创建 Window
59 | - 初始化 DecorView 并将 Dialog 的视图添加到 DecorView 中
60 | - 将 DecorView 添加到 Window 中并显示
61 |
62 | 当 Dialog 被关闭时,它会通过 WindowManager 来移除 DecorView:mWindowManager.removeViewImmediate(mDecor)。
63 |
64 | 3. Toast 的 Window 的创建过程
65 | 在 Toast 内部有两个 IPC 过程:第一类是 Toast 访问 NotificationManagerService,第二类是 NotificationManagerService 回调 Toast 里的 TN 接口。
66 | Toast 的显示过程:它调用了 NMS 中的 enqueueToast 方法,enqueueToast 方法第一个参数表示当前应用的包名,第二个参数 tn 表示远程回调,第三个参数表示 Toast 的时长,enqueueToast 首先将 Toast 请求封装为 ToastRecord 对象并将其添加到一个名为 mToastQueue 的队列中,mToastQueue 其实是一个 ArrayList,mToastQueue 中最多能同时存在50个 ToastRecord,这样做是为了放置 DOS;当 ToastRecord 被添加到 mToastQueue 中后,NMS 就好通过 showNextToastLocked 方法来显示当前的 Toast,Toast 的显示是由 ToastRecord 的 callback 来完成的,这个 callback 实际上就是 Toast 中的 TN 对象的远程 Binder;Toast 显示以后, NMS 还会通过 scheduleTimeoutLocked 方法来发送一个延时消息。
67 |
68 | Toast 的隐藏也是通过 ToastRecord 的 callback 来完成的,也是一次 IPC 过程。
69 |
70 | Toast 的显示和隐藏过程实际上是通过 Toast 中的 TN 这个类来实现的,有 show 和 hide 两个方法,分别对应 Toast 的显示和隐藏,因为这两个方法是被 NMS 以跨进程的方式调用的,运行在 Binder 线程池中,内部使用了 Handler 来将执行环境切换到 Toast 请求所在的线程,两个方法中的 mShow 和 mHide 是两个 Runnable,内部分别调用了 handleShow 和 handleHide 方法,handleShow 和 handleHide 才是真正完成显示和隐藏 Toast 的地方,TN的 handleShow 中会将 Toast 的视图添加到 Window 中,handleHide 中会将 Toast 的视图从 Window 中移除。
71 |
72 |
73 |
74 | 2017.8.22
75 | W.Z.H
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Android/《Android开发艺术探索》笔记/综合技术.md:
--------------------------------------------------------------------------------
1 | # 综合技术
2 | ## 使用 CrashHandler 来获取应用的 crash 信息
3 | 1. 获取应用 crash 信息的方式:
4 | - 首先需要实现一个 UncaughtExceptionHandler 对象,在它的 uncaughtException 方法中获取异常信息并将其存储在 SD 卡中或者上传到服务器供开发人员分析;
5 | - 然后调用 Thread 中的 setDefaultUncaughtExceptionHandler 方法将它设置为默认线程的异常处理器,由于默认异常处理器是 Thread 类的静态成员,它的作用是当前进程的所有线程。
6 |
7 | ## 使用 multidex 来解决方法数越界
8 | 1. 在代码中加入支持 multidex 的功能有三种方案:
9 |
10 | - 在 manifest 文件中指定 Application 为 MultidexApplication
11 | ````java
12 |
16 |
17 | ````
18 | - 让应用的 Application 继承 MultiDexApplication,
19 | ````java
20 | public class TestApplication extends MultiDexApplication{
21 | ...
22 | }
23 | ````
24 | - 如果不想让应用的 Application 继承 MultiDexApplication,可以选择继承重写 Application 的 attachBaseContext 方法,这个方法比 Application 的 onCreate 要先执行,
25 | ````java
26 | public class TestApplication extends Application{
27 | @Override
28 | protected void attachedBaseContext(Context base){
29 | super.attachBaseContext(base);
30 | MultiDex.install(this);
31 | }
32 | }
33 | ````
34 |
35 | 2. multidex 可能带来的问题:
36 |
37 | - 应用启动速度会降低,甚至可能出现 ANR 现象
38 | - 由于 Dalvik linearAlloc 的 bug,这可能导致使用 multidex 的应用无法在 Android 4.0 以前的手机上运行,因此需要做大量的兼容性测试。
39 |
40 | ## Android 的动态加载技术
41 | 1. 宿主是指普通的 apk,而插件一般是指经过处理的 dex 或者 apk,在主流的插件化框架中多采用经过特殊处理的 apk 来作为插件。
42 | 2. 插件化的三个基础性问题:
43 |
44 | - 资源访问
45 | 插件中凡是以 R 开头的资源都不能访问,因为宿主程序中并没有插件资源。
46 | 解决方法:
47 | 加载资源的方法是通过反射,通过调用 AssetManager 中的 addAssetPath 方法,我们可以将一个 apk 中的资源加载到 Resource 对象中,由于 addAssetPath 是隐藏的 API 我们无法直接调用,只能通过反射;接着在代理 Activity 中实现 getAsset() 和 getResource()。
48 | - Activity 生命周期的管理
49 | 管理 Activity 的生命周期的方式有许多,如 反射方式和接口方式。
50 | 反射方式:
51 | 首先通过 Java 的反射去获取 Activity 的各种生命周期的方法,然后在代理 Activity 中去调用插件 Activity 对应的生命周期方法即可。
52 | 缺点是:一方面反射代码写起来比较复杂,另一方面是过多使用反射会有一定的性能开销。
53 | 接口方式:
54 | 这种方式是将 Activity 的生命周期方法提取出来作为一个接口,然后通过代理 Activity 去调用插件 Activity 的生命周期方法。
55 | - 插件 ClassLoader 的管理
56 |
57 | ## 反编译
58 |
59 |
60 |
61 | 2017.8.30
62 | W.Z.H
--------------------------------------------------------------------------------
/Android/笔记/Android 中常见的内存泄漏.md:
--------------------------------------------------------------------------------
1 | # Android 中常见的内存泄漏
2 | ## Activity 对象未被回收
3 | ### 静态变量引用 Activity 对象
4 | 通过静态变量引用 Activity 对象时,会导致 Activity 对象所占内存泄漏。主要是因为静态变量是驻扎在 JVM 的方法区,因此,静态变量引用的对象是不会被 GC 回收的,因为它们所引用的对象本身就是 GC ROOT,即最终导致 Activity 对象不被回收,从而也就造成内存泄漏。
5 | ### 静态 View
6 | 有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View是一个静态变量,所以导致Activity不被回收。因此在使用静态View时,需要确保在资源回收时,将静态View detach掉。
7 | ### 内部类
8 | 因为非静态内部类持有外部类的引用,所以如果我们在一个外部类中定义一个静态变量,这个静态变量是引用内部类对象,将会导致内存泄漏,因为这相当于间接导致静态类引用外部类。
9 | ### 匿名类
10 | 与内部类一样,匿名类也会持有外部类的引用
11 | ### Handler
12 | 如果在Activity中定义Handler对象,那么Handler肯定是持有Activty的引用。而每个Message对象是持有Handler的引用的(Message对象的target属性持有Handler引用),从而导致Message间接引用到了Activity。如果在Activty destroy之后,消息队列中还有Message对象,Activty是不会被回收的。当然,如果消息正在准备(处于延时入队期间)放入到消息队列中也是一样的。
13 | 解决办法就是,将Handler放入单独的类或者将Handler放入到静态内部类中(静态内部类不会持有外部类的引用)。如果想要在handler内部去调用所在的外部类Activity,可以在handler内部使用弱引用的方式指向所在Activity,这样不会导致内存泄漏。
14 | ### Threads和TimerTask
15 | Threads和Timer导致内存泄漏的原因跟内部类一样。虽然在新的线程中创建匿名类,但是只要是匿名类/内部类,它都会持有外部类引用。
16 | ### 监听器
17 | 当我们需要使用系统服务时,比如执行某些后台任务、为硬件访问提供接口等等系统服务。我们需要把自己注册到服务的监听器中。然而,这会让服务持有 activity 的引用,如果程序员忘记在 activity 销毁时取消注册,那就会导致 activity 泄漏了。
18 | ## 集合对象造成的泄漏
19 | 当我们定义一个静态的集合类时,这可能会导致内存泄漏!静态变量所引用的对象是不会被回收掉的。而静态集合类中,包含有大量的对象,这些对象不会被回收。另外,如果集合中保存的对象又引用到了其他的大对象,如超长字符串、Bitmap、大数组等,很容易造成OOM。
20 | ## 资源对象没关闭造成内存泄漏
21 | 当打开资源时,一般都会使用缓存。比如读写文件资源、打开数据库资源、使用Bitmap资源等等。当我们不再使用时,应该关闭它们,使得缓存内存区域及时回收。
22 | ## 使用对象池避免频繁创建对象
23 | 在我们需要频繁创建使用某个类时,或者是在for循环里面创建新的对象时,导致JVM不断创建同一个类。
--------------------------------------------------------------------------------
/Android/笔记/Android 自定义 View 的知识点.md:
--------------------------------------------------------------------------------
1 | # Android 自定义 View 的知识点
2 | ## Android 中 getWidth() 和 getMeasuredWidth() 之间的区别
3 | getMeasuredWidth() 获取的是 View 原始的大小,也就是这个 View 在 XML 文件中配置或者是代码中设置的大小。getWidth() 获取的是这个 View 最终显示的大小,这个大小有可能等于原始的大小也有可能不等于原始大小。
4 |
5 | ## View 的坐标系
6 | 屏幕的左上角为坐标的原点,屏幕上边缘往右为X轴正方向,屏幕左边缘往下为Y轴正方向。
7 |
8 | - View 自身坐标:
9 | - getLeft():子 View 的左边缘相对于父 View 左边缘的距离,单位是像素。
10 | - getTop():子 View 的右边缘相对于父 View 左边缘的距离,单位是像素。
11 | - getRight():子 View 的上边缘相对于父 View 上边缘的距离,单位是像素。
12 | - getBottom():子 View 的下边缘相对于父 View 上边缘的距离,单位是像素。
13 | - View 自身宽高: getWidth(),getMeasuredWidth(),getHeight(),getMeasuredHeight()
14 | - MotionEvent 获取坐标:
15 | - getX():点击事件的点在控件中的X坐标,即点相对于控件左边缘的距离。
16 | - getY():点击事件的点在控件中的Y坐标,即点相对于控件上边缘的距离:
17 | - getRawX():点击事件的点在整个屏幕中的X坐标,即点相对于屏幕左边缘的距离。
18 | - getRawY():点击事件的点在整个屏幕中的Y坐标,即点相对于屏幕上边缘的距离。
19 | - View 的 Padding:
20 | - getPaddingLeft():View 里的 content 距离 View 左边缘的距离。
21 | - getPaddingTop():View 里的 content 距离 View 上边缘的距离。
22 | - getPaddingRight():View 里的 content 距离 View 右边缘的距离。
23 | - getPaddingBottom():View 里的 content 距离 View 下边缘的距离。
24 |
25 | 
27 | **图片摘自:http://blog.csdn.net/jason0539/article/details/42743531**
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Android/笔记/Android中的Theme.md:
--------------------------------------------------------------------------------
1 | # Androdi中的Theme
2 | 在AndroidManifest.xml文件中有
3 | ````
4 | android:theme="@style/AppTheme"
5 | ````
6 | 这段代码,用来引用res/values/styles.xml 中的主题样式。
7 | ````
8 |
9 |
14 | ````
15 | AppTheme的主题样式继承自parent中定义的主题,我们修改parent后的值可以实现不同的主题。
16 |
17 | 使用android系统中自带的主题要加上“android:”,如:android:Theme.Black
18 | 使用v7兼容包中的主题不需要前缀,直接:Theme.AppCompat
19 |
20 | 下面列举下一些主题
21 | 系统自带主题:
22 | ````
23 | API 1:
24 | android:Theme 根主题
25 | android:Theme.Black 背景黑色
26 | android:Theme.Light 背景白色
27 | android:Theme.Wallpaper 以桌面墙纸为背景
28 | android:Theme.Translucent 透明背景
29 | android:Theme.Panel 平板风格
30 | android:Theme.Dialog 对话框风格
31 |
32 | API 11:
33 | android:Theme.Holo Holo根主题
34 | android:Theme.Holo.Black Holo黑主题
35 | android:Theme.Holo.Light Holo白主题
36 |
37 | API 14:
38 | Theme.DeviceDefault 设备默认根主题
39 | Theme.DeviceDefault.Black 设备默认黑主题
40 | Theme.DeviceDefault.Light 设备默认白主题
41 |
42 | API 21: (网上常说的 Android Material Design 就是要用这种主题)
43 | Theme.Material Material根主题
44 | Theme.Material.Light Material白主题
45 |
46 |
47 | 兼容包v7中带的主题:
48 | Theme.AppCompat 兼容主题的根主题
49 | Theme.AppCompat.Black 兼容主题的黑色主题
50 | Theme.AppCompat.Light 兼容主题的白色主题
51 | ````
52 |
53 | Theme.AppCompat主题是兼容主题,是指如果运行程序的手机API是21则就相当于是Material主题,如果运行程序的手机API是11则就相当于是Holo主题,以此类推
54 |
55 | ---
56 | 参考:
57 | [总结一下Android中主题(Theme)的正确玩法](http://www.cnblogs.com/zhouyou96/p/5323138.html)
--------------------------------------------------------------------------------
/Android/笔记/Android面试题整理.md:
--------------------------------------------------------------------------------
1 | # Android面试题整理
2 |
3 | 1. 什么是ANR和Force Close?如何避免?
4 | ANR: Application Not Responding
5 | 产生原因:
6 | - 主线程(UI线程)响应用户操作事件事件超过5秒。
7 | - BroadcastReceive超过10秒钟未执行完毕。
8 | 避免方法:
9 | Android应用程序完全运行在一个独立的线程中。任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。因此,需要消耗大量事件的操作如访问网络和数据库,都要放到子线程中或者使用异步方式来完成。
10 |
11 | Force Close
12 | 产生原因:
13 | 程序出现异常,一般像空指针、数组越界、类型转换异常等。
14 | 避免方法:
15 | 编写程序时要思维缜密,异常出现时可以通过logcat查看抛出异常代码出现的位置,然后去程序中进行修改。
16 |
17 | 2. 请描述下Activity的生命周期。
18 | 
19 |
20 | - onCreate
21 | >在Activity第一次被创建的时候调用,可以在这个方法中完成Activity的初始化操作,比如加载布局,绑定事件。
22 | - onStart
23 | >在Activity由不可见变为可见时候调用。
24 | - onResume
25 | >在Activity准备好和用户交互时调用,此时Activity一定位于返回栈的栈顶,并处于运行状态。
26 | - onPause
27 | >在系统准备去启动或者恢复另一个Activity时候调用。可以在这个方法中将一些消耗CPU的资源释放掉,以及保持一些关键数据(但不能做耗时操作,否则影响新的栈顶Activity的使用)。
28 | - onStop
29 | >在Activity完全看不见的时候调用。与onPause的区别:如果启动一个对话框。那么onPause会执行,onStop不会。
30 | - onDestroy
31 | >在Activity被销毁前调用,之后活动变为销毁状态。
32 | - onRestart
33 | >这个活动由停住状态变化运行状态之前调用,也就是被重新启动了。
34 |
35 | 我们可以将Activity分为三种生存周期
36 |
37 | - 完整生存期
38 | >由 onCreate->onDestroy
39 | - 可见生存期
40 | >由 onStart->onStop
41 | - 前台生存期
42 | >由 onResume->onPause
43 |
44 |
45 | 3. 多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
46 | 多线程的实现方法:
47 |
48 | - 继承Thread类。
49 | 1. 自定义类MyThread继承Thread类。
50 | 2. MyThread类里面重写run()。
51 | 3. 创建对象。
52 | 4. 启动线程。
53 | - 实现Runnable结口。
54 | 1. 自定义类MyRunnable实现Runnable接口。
55 | 2. 重新run()方法。
56 | 3. 创建MyRunnable类对象。
57 | 4. 创建Thread类的对象,并把上一步的对象作为构造参数传递。
58 | - 实现Callable接口,重写call()方法。但是这个方法不常用。
59 |
60 | 同步的实现方法:
61 |
62 | - synchronized关键字:包括synchronized方法和synchronized块。
63 | - wait()方法和notify()方法。
64 | - Lock接口及其实现类RenntrantLock。
65 |
66 | 4. 如何退出Activity?如何安全退出已调起多个Activity的Application?
67 | - finish() 。
68 | - 新建一个类ActivityCollector用于管理全部的Activity对象,每生成一个Activity对象就将其添加到ActivityCollector一个List中,在ActivityCollector中实现一个finishAll()方法,用于结束list中所有的Activity对象。
69 |
70 | 5. 用至少两种方式实现一个Singleton(单例模式)。
71 | - 静态内部类实现:这种方式是 Singleton 类被装载了, instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有显示通过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instance 。
72 | ````
73 | public class Signleton{
74 | private static class SingletonHolder{
75 | private static final Singleton INSTANCE = new Singleton();
76 | }
77 | private Singleton(){}
78 | public static final Singleton getInstance(){
79 | return SingletonHolder.INSTANCE;
80 | }
81 | }
82 | ````
83 | - 双重检查锁:在JDK1.5之后,双重检查锁定才能够正常达到单例效果。(因为volitile关键字)。
84 | ````
85 | public class Signleton{
86 | private volatile static Singleton singleton;
87 | private Singleton(){}
88 | public static Singleton getSingleton(){
89 | if(singleton == null){
90 | synchronized(Singleton.class){
91 | if(singleton == null){
92 | singleton = new Singleton();
93 | }
94 | }
95 | }
96 | return singleton;
97 | }
98 | }
99 | ````
100 |
101 | 6. Intent传递数据时,下列的数据类型哪些可以被传递?
102 | - Serializable:将Java对象序列化为二进制文件的Java序列化技术是Java系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现Serializable接口,使用ObjectInputStream和ObjectOutputStream进行对象的读写。
103 | - charsequence:在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这四个类。CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。
104 | 提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,如果有必要,重载比较好,避免用instanceof这个操作符。
105 | - Parcelable:android提供了一只新的类型:Parcel。本类被用作封装数据的容器,封装后的数据可以通过Intent或IPC传递。除了基本类型以外,只有实现了Parcelable接口类才能被放入Parcel中。
106 | - Bunldle:Bundle是将数据传递到另一个上下文中或报酬或回复你自己状态的数据存储方式。它的数据不是持久化状态。
107 |
108 | 7. 一个GLSurfaceView类 , 具有以下特点 :
109 | - 管理一个平面, 这个平面是一个特殊的内存块 , 它可以和 android 视图系统混合 。
110 | - 管理一个EGL 显示 , 它能够让 OpenGL 渲染到一个平面 。
111 | - 接受一个用户提供的实际显示的Renderer 对象 。
112 | - 使用一个专用线程去渲染从而和UI 线程解耦 。
113 | - 支持on-demand 和连续的渲染。
114 | - 可选的包, 追踪 和 / 或者错误检查这个渲染器的 OpenGL 调用 。
115 |
116 | 8. 简述Andriod如何处理UI与耗时操作的通信,有哪些方式及各自的优缺点。
117 |
118 | 主要有三种方法,一为Handler,二为AsyncTask,三为自己开子线程执行耗时操作,然后调用Activity的runOnUiThread()方法更新ui。
119 | - handler机制是,在主线程中创建handler对象,当执行耗时操作时,创建一个线程,在这个线程中执行耗时操作,通过调用handler的sendMessage,post等方法,更新ui界面。
120 | handler机制的优点是结构清晰,功能明确,但是代码过多。
121 | - AysncTask本质上是一个线程池,所有的异步任务都会在这个线程池中的工作线程中执行,当需要操作ui界面时,会和工作线程通过handler传递消息。
122 | AysncTask简单,快捷,但是可能会新开大量线程,消耗系统资源,造成Force Close。
123 | - 自己开子线程执行耗时操作,然后调用Activity的runOnUiThread()方法更新ui。这种方法需要把context对象强制转换成activity后使用。
124 | 第三种方法最好用,代码也非常简单,只是需要传递context对象。
125 |
126 | 9. Android中Looper的实现原理,为什么调用Looper.prepare()就在当前线程关联了一个Looper对象,它是如何实现的。
127 |
128 | - 线程间通信机制
129 | 首先,looper、handler、messagequeue三者共同实现了android系统里线程间通信机制。如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套looper、handler、messagequeue,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。这个消息被Handler发送到B线程的messageQueue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue,一旦发现该消息队列里收到新的消息,就会对消息进行处理,处理过程中会回调自身Handler的headleMessage方法。从而实现了不同线程间通信。
130 | - Looper实现原理
131 | Looper类里包含了一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(looper.loop()),会自动进入无限for循环中,不断去遍历消息队列,如果没有消息阻塞,有消息则回调handler的handlemessage方法进行处理。
132 | - Looper.prepare()
133 | 首先,要使用Looper机制一般会在当前线程创建Hnadler对象,里面会自动创建一个looper对象消息队列,这里面的消息队列属于当前线程空间。但此时的looper还不会去遍历,也没有绑定到当前线程。其中,looper对象内部也包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让控线程也指向对其线程,从而实现绑定。
134 |
135 | 10. 分辨率相关
136 |
137 | 在 Android 中, 1pt 大概等于 2.22sp以上,
138 | 与分辨率无关的度量单位可以解决这一问题。Android支持下列所有单位。
139 | px(像素):屏幕上的点。
140 | in(英寸):长度单位。
141 | mm(毫米):长度单位。
142 | pt(磅):1/72英寸。
143 | dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
144 | dip:与dp相同,多用于android/ophone示例中。
145 | sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。
146 | 分辨率:整个屏是多少点,比如800x480,它是对于软件来说的显示单位,以px为单位的点。
147 | density(密度)值表示每英寸有多少个显示点,与分辨率是两个概念。apk的资源包中,
148 | 当屏幕density=240时使用hdpi标签的资源
149 | 当屏幕density=160时,使用mdpi标签的资源
150 | 当屏幕density=120时,使用ldpi标签的资源。
151 | 一般android设置长度和宽度多用dip,设置字体大小多用sp. 在屏幕密度为160,1dp=1px=1dip, 1pt = 160/72 sp 1pt = 1/72 英寸.当屏幕密度为240时,1dp=1dip=1.5px.
152 |
153 | 11. andorid数据持久化的方法。
154 | Android数据持久化有五种方式:
155 |
156 | - SharedPreferences
157 | - 内部存储(例如通过openFileOutput()打开一个文件输入输出流)
158 | - SQLite Database
159 | - 网络连接(将数据存储到服务器上)
160 | - 外部存储(SD卡)
--------------------------------------------------------------------------------
/Android/笔记/GreenDao 的简单使用.md:
--------------------------------------------------------------------------------
1 | # GreenDao 的简单使用
2 |
3 | ## GreenDao 的使用方法
4 | 1. app 目录下的 build.gradle 配置如下:
5 | ````groovy
6 | apply plugin: 'org.greenrobot.greendao'
7 |
8 | ...
9 | dependencies {
10 | implementation 'org.greenrobot:greendao:3.2.2'
11 | }
12 | ````
13 |
14 | 2. 在bulid.gradle下进行配置
15 | ````groovy
16 | buildscript {
17 | repositories {
18 | jcenter()
19 | mavenCentral()
20 | }
21 |
22 | dependencies {
23 | classpath 'com.android.tools.build:gradle:3.1.1'
24 | classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
25 | }
26 | }
27 | ````
28 |
29 | 3. 对greendao的generator生成文件进行配置
30 | ````groovy
31 | greendao {
32 | schemaVersion 1 // 版本
33 | daoPackage '生成文件包名' // 一般为app包名+生成文件的文件夹名
34 | targetGenDir 'src/main/java' // 生成文件路径
35 | }
36 | ````
37 |
38 | 4. 使用 GreenDao
39 | 新建一个 MyApplication 类,继承自 Application,
40 | ````java
41 |
42 | public class MyApplication extends Application {
43 |
44 | private static volatile MyApplication instance = null;
45 |
46 | private DaoMaster.DevOpenHelper mHelper;
47 |
48 | private SQLiteDatabase db;
49 |
50 | private DaoMaster mDaoMaster;
51 |
52 | private DaoSession daoSession;
53 |
54 | private MyApplication(){}
55 |
56 | public static synchronized MyApplication getInstance(){
57 | if (instance == null) {
58 | instance = new MyApplication();
59 | }
60 | return instance;
61 | }
62 |
63 | @Override
64 | public void onCreate(){
65 | super.onCreate();
66 | setDataBase();
67 | }
68 |
69 |
70 | /**
71 | *配置数据库
72 | *
73 | */
74 | private void setDataBase(){
75 | // 创建数据库 shop.db
76 | mHelper = new DaoMaster.DevOpenHelper(this,"shop.db",null);
77 | // 获取可写数据库
78 | db = mHelper.getWritableDatabase();
79 | // 回去数据库对象
80 | DaoMaster = new DaoMaster(db);
81 | // 获取Dao 对象管理者
82 | daoSession = mDaoMaster.newSession();
83 |
84 | }
85 |
86 | public DaoSession gerDaoSession(){
87 | return daoSession;
88 | }
89 |
90 | public SQLiteDatabase getDb(){
91 | return db;
92 | }
93 |
94 | }
95 |
96 | ````
97 |
98 | ## 操作
99 | ### 增
100 |
101 | insert(Entity entity):插入一条记录
102 |
103 | ````java
104 | /**
105 | * 增
106 | */
107 | public void insert(){
108 | String date = new Date().toString();
109 | //第一个是id值,因为是自增长所以不用传入
110 | mDayStep = new dayStep(null,date,0);
111 | dao.insert(mDayStep);
112 | }
113 |
114 | ````
115 |
116 | ### 删
117 |
118 | deleteByKey(Long key):根据主键删除一条记录。
119 | delete(Entity entity):根据实体类删除一条记录,一般结合查询方法没,查询出一条记录之后删除。
120 | deleteAll():删除所有记录。
121 |
122 | ````java
123 | /**
124 | * 删
125 | * @param i 删除数据的id
126 | */
127 | public void delete(long i){
128 | //当然Greendao还提供了其他的删除方法,只是传值不同而已
129 | dao.deleteByKey(i);
130 | }
131 |
132 | ````
133 | ### 改
134 |
135 | update(Entitiy entity):更新一条记录;
136 | ````java
137 | /**
138 | *改
139 | * @param i
140 | * @param date
141 | */
142 | public void correct(long i,String date){
143 | mDayStep = new dayStep((long) i,date,0);
144 | dao.update(mDayStep);
145 | }
146 |
147 | ````
148 |
149 | ### 查
150 |
151 | - loadAll() : 查询所有记录;
152 | - load(Long key) : 根据主键查询一条记录;
153 | - queryBuilder().list() : 返回:List;
154 | - queryBuilder.where(UserDao.Properties.Name.eq("")).list() : 返回:List;
155 | - queryRaw(String where,String selectionArg) : 返回:List。
156 |
157 | ````java
158 |
159 | /**
160 | * 查
161 | */
162 | public void Search(){
163 | //方法一
164 | List mDayStep = dao.loadAll();
165 | //方法二
166 | //List mDayStep = dao.queryBuilder().list();
167 | //方法三 惰性加载
168 | //List mDayStep = dao.queryBuilder().listLazy();
169 | for (int i = 0; i < mDayStep.size(); i++) {
170 | String date = "";
171 | date = mDayStep.get(i).getDate();
172 | Log.d("cc", "id: "+i+"date: "+date);
173 | }
174 |
175 | }
176 |
177 |
178 | /**
179 | *查找符合某一字段的所有元素
180 | */
181 | public void searchEveryWhere(String str){
182 | List mList = dao.queryBuilder()
183 | .where(dao.date
184 | .eq(str))
185 | .build()
186 | .listLazy();
187 | }
188 |
189 |
190 | ````
191 | ### 修改或者替换
192 | ````java
193 | /**
194 | *修改或者替换(有的话就修改,没有则替换)
195 | */
196 | public void insertOrReplace(long i,String date){
197 | mDayStep = new dayStep((long) i,date,0);
198 | dao.insertOrReplace(mDayStep);
199 | }
200 |
201 | ````
202 | ## Greendao注解含义
203 |
204 | @Entity 实体标识:
205 |
206 | - schema:告知GreenDao当前实体属于哪个schema;
207 | - active:标记一个实体处于活跃状态,活动实体有更新、删除和刷新方法;
208 | - nameInDb:在数据库中使用的别名,默认使用的是实体的类名;
209 | - indexs:定义索引,可以跨越多个列;
210 | - createInDb:标记创建数据库表。
211 |
212 | 基础属性注解:
213 |
214 | - @Id:主键 Long 型,可以通过@Id(autoincrement = true)设置自增长;
215 | - @Property:设置一个非默认关系映射所对应的列名,默认是使用字段名。例如: @Property(nameInDb = "name");
216 | - @NotNull:设置数据库表当前列不能为空;
217 | - @Transient:添加此标记后不会生成数据库表的列。
218 |
219 | 索引注解:
220 |
221 | - @Index:使用@Index作为一个属性来创建一个索引,通过name设置索引别名,也可以通过unique给索引添加约束;
222 | - @Unique:向数据库添加了一个唯一的约束。
223 |
224 | 关系注解:
225 |
226 | - @ToOne:定义与另一个实体(一个实体对象)的关系;
227 | - @ToMany:定义与多个实体对象的关系。
228 |
229 |
230 |
--------------------------------------------------------------------------------
/Android/笔记/Service小记.md:
--------------------------------------------------------------------------------
1 | # Service 小记
2 | ## 先来扯点
3 | Service作为Android四大组件之一,相信学过Android的都知道这货,它在每一个应用程序中都扮演着重要的角色.
4 | Service是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件.Service可由其他应用组件启动,而且即使用户切换到其他应用,Service仍将在后台继续运行.Service主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务.必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态.
5 | Service和Activity很相似,但是区别在于:Service一直在后台运行,没有用户界面,所以不会到前台,如果Service被启动起来,就和Activity一样,具有自己的声明周期.
6 |
7 | >注:在开发中,Activity和Service的选择标准是:如果程序组件在需要在运行时向用户呈现某种界面,或者程序需要与用户进行交互,那么使用Activity,否则考虑使用Service.
8 |
9 | 另外,需要注意的是,Service和Thread不是一个意思,不要被Service的后台概念所迷惑.实际上Service并不会自动开启线程,所有的代码都是默认运行在主线程中的.因此,我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则可能造成ANR的问题.
10 |
11 | ## Service的形式
12 | Service基本上分为两种形式:
13 |
14 | - Started(启动的)
15 | + 当应用组件(如 Activity)通过调用 startService() 启动Service时,Service即处于“启动”状态.一旦启动,Service即可在后台无限期运行,即使启动Service的组件已被销毁也不受影响.通常,一个开启的Service执行单一操作并且不会给调用者返回结果.例如,它可能通过网络下载或上传文件.操作完成后,Service会自行停止运行.
16 | - Bound(绑定的)
17 | + 当应用组件通过调用 bindService() 绑定到Service时,Service即处于“绑定”状态.一个绑定的Service提供客户端/服务器接口允许组件和Service交互,甚至跨进程操作使用进行间通信(IPC).仅当与另一个应用组件绑定时,绑定服务才会运行.多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁.
18 |
19 | ## Service的使用
20 | ### 几个方法
21 | 想要创建一个service,你必须创建一个Service的子类(或一个它的存在的子类).在你的实现中,你需要重写一些回调方法来处理Service生命周期的关键方面并且对于组件绑定到Service提供一个机制.应重写的最重要的回调方法包括:
22 |
23 | - onStartCommand()
24 | + 当其它组件(例如activity)通过调用**startService()** 来请求Service启动时系统会调用这个方法.一旦这个方法执行,service就启动并且可以无限地运行在后台.如果你实现这个,它主要负责当任务完成后停止service,通过调用**stopSelf()** 或 **stopService()**.(如果你只想提供绑定,你不需要实现这个方法.)
25 | - onBind()
26 | + 当其它组件通过调用**bindService()**来绑定到Service时系统会调用这个方法.在实现的这个方法中,你必须提供一个客户端用来和Service交互的接口,通过返回一个IBinder.你必须实现这个方法,但如果你不想允许绑定,你应该返回null.
27 | - onCreate()
28 | + 首次创建Service时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前).如果服务已在运行,则不会调用此方法.
29 | - onDestroy()
30 | + 当Service不再使用且将被销毁时,系统将调用此方法.Service应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等.这是Service接收的最后一个调用.
31 |
32 |
33 | >如果希望在Service组件做某些事情,那么只要在onCreate()或onStratCommand()方法中定义相关业务代码即可.而当服务销毁时,我们应该在onDestroy()方法中去回收那些不再使用的资源.
34 |
35 | ### 使用清单文件声明服务
36 | 如同 Activity(以及其他组件)一样,您必须在应用的清单文件中声明所有服务.
37 | ````java
38 |
39 | ...
40 |
41 |
42 | ...
43 |
44 |
45 | ````
46 |
47 | ### 创建启动Service
48 | 直接用代码了,比较清楚.
49 |
50 | ````java
51 | @Override
52 | public void onCreate() {
53 | super.onCreate();
54 | Log.d(TAG, "onCreate() executed");
55 | }
56 |
57 | @Override
58 | public int onStartCommand(Intent intent, int flags, int startId) {
59 | Log.d(TAG, "onStartCommand() executed");
60 | return super.onStartCommand(intent, flags, startId);
61 | }
62 |
63 | @Override
64 | public void onDestroy() {
65 | super.onDestroy();
66 | Log.d(TAG, "onDestroy() executed");
67 | }
68 |
69 | @Override
70 | public IBinder onBind(Intent intent) {
71 | return null;
72 | }
73 | ````
74 | 然后在activity_main中添加2个按钮.
75 | ````java
76 |
80 |
81 |
86 |
87 |
92 |
93 |
94 | ````
95 |
96 | 接着在MainActivity中添加以下代码.
97 | ````java
98 | public class MainActivity extends Activity implements OnClickListener {
99 | private Button startService;
100 | private Button stopService;
101 |
102 | @Override
103 | protected void onCreate(Bundle savedInstanceState) {
104 | super.onCreate(savedInstanceState);
105 | setContentView(R.layout.activity_main);
106 | startService = (Button) findViewById(R.id.start_service);
107 | stopService = (Button) findViewById(R.id.stop_service);
108 | startService.setOnClickListener(this);
109 | stopService.setOnClickListener(this);
110 | }
111 |
112 | @Override
113 | public void onClick(View v) {
114 | switch (v.getId()) {
115 | case R.id.start_service:
116 | Intent startIntent = new Intent(this, MyService.class);
117 | startService(startIntent);
118 | break;
119 | case R.id.stop_service:
120 | Intent stopIntent = new Intent(this, MyService.class);
121 | stopService(stopIntent);
122 | break;
123 | default:
124 | break;
125 | }
126 | }
127 | }
128 | ````
129 | 最后在AndroidManifest.xml中注册.
130 | ````java
131 |
132 | ````
133 |
134 | 这里我们重写了onCreate()、onStartCommand()、onDestroy()这三个方法.
135 |
136 | - onCreate():在服务创建的时候调用.
137 | - onStartCommand():在每次服务启动的时候调用.
138 | - onDestroy():在服务销毁的时候调用.
139 | 运行之后,我们做了以下事情.
140 | 当点击STARTSERVICE按钮后,控制台打印的Log如图:
141 | 
142 | 当在此点击STARTSERVICE按钮后,控制台打印的Log如图:
143 | 
144 | 我们发现此时onCreate()方法没有执行,onStartCommand()继续执行了.多点几次后还是这样.为什么会这样呢?这是由于onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行.因此你可以再多点击几次Start Service按钮试一次,每次都只会有onStartCommand()方法中的打印日志.
145 | 当点击STOPSERVICE按钮后,控制台打印的Log如图:
146 | 
147 |
148 | >注:在MyService的任何一个位置调用stopSelf()方法就能让Service自己停下来
149 |
150 | ### Service和Activity通信
151 | 接下来,我们让Service和Activity的关系更紧密一些,来实现在Activity中指挥Service去干活.而这需要借助onBind()方法.
152 | 还是看代码吧.
153 | 修改MyService里面的代码:
154 | ````java
155 | ...
156 | private MyBinder mBinder = new MyBinder();
157 | ...
158 | @Nullable
159 | @Override
160 | public IBinder onBind(Intent intent) {
161 | return mBinder;
162 | }
163 |
164 | class MyBinder extends Binder {
165 | public void startDownload(){
166 | Logger.t(TAG).d("执行startDownload()方法");
167 | //执行具体下载任务
168 | }
169 | }
170 |
171 | ````
172 | 这里我们新增了一个MyBinder类继承自Binder类,然后在MyBinder中添加了一个startDownload()方法用于在后台执行下载任务.
173 | 然后再在activity_main中添加2个按钮
174 | ````
175 |
180 |
181 |
187 | ````
188 | 修改MainActivity的代码:
189 | ````java
190 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
191 |
192 | private Button start, stop;
193 | private Button bindService;
194 |
195 | private Button unbindService;
196 |
197 | private MyService.MyBinder myBinder;
198 |
199 | private ServiceConnection connection = new ServiceConnection() {
200 |
201 | @Override
202 | public void onServiceDisconnected(ComponentName name) {
203 | }
204 |
205 | @Override
206 | public void onServiceConnected(ComponentName name, IBinder service) {
207 | myBinder = (MyService.MyBinder) service;
208 | myBinder.startDownload();
209 | }
210 | };
211 |
212 |
213 | @Override
214 | protected void onCreate(Bundle savedInstanceState) {
215 | super.onCreate(savedInstanceState);
216 | setContentView(R.layout.activity_main);
217 |
218 | start = (Button) findViewById(R.id.Start_Service);
219 | stop = (Button) findViewById(R.id.Stop_Service);
220 | bindService = (Button) findViewById(R.id.bind_service);
221 | unbindService = (Button) findViewById(R.id.unbind_service);
222 | bindService.setOnClickListener(this);
223 | unbindService.setOnClickListener(this);
224 |
225 |
226 | start.setOnClickListener(this);
227 | stop.setOnClickListener(this);
228 | }
229 |
230 | @Override
231 | public void onClick(View view) {
232 | switch (view.getId()) {
233 | case R.id.Start_Service:
234 | Intent startIntent = new Intent(this,MyService.class);
235 | startService(startIntent);
236 | break;
237 | case R.id.Stop_Service:
238 | Intent stopIntent = new Intent(this,MyService.class);
239 | stopService(stopIntent);
240 | break;
241 | case R.id.bind_service:
242 | Intent bindIntent = new Intent(this, MyService.class);
243 | bindService(bindIntent, connection, BIND_AUTO_CREATE);
244 | break;
245 | case R.id.unbind_service:
246 | unbindService(connection);
247 | break;
248 | }
249 | }
250 | }
251 |
252 |
253 | ````
254 | 可以看到,这里我们首先创建了一个ServiceConnection的匿名类,在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,这两个方法分别会在Activity与Service建立关联和解除关联的时候调用.在onServiceConnected()方法中,我们又通过向下转型得到了MyBinder的实例,有了这个实例,Activity和Service之间的关系就变得非常紧密了.
255 | 当然,现在Activity和Service其实还没关联起来了呢,这个功能是在Bind Service按钮的点击事件里完成的.可以看到,这里我们仍然是构建出了一个Intent对象,然后调用bindService()方法将Activity和Service进行绑定.*bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位.*这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行.
256 | 然后如何我们想解除Activity和Service之间的关联怎么办呢?调用一下unbindService()方法就可以了,这也是Unbind Service按钮的点击事件里实现的逻辑.
257 | 好的,我们点击试试.
258 | 点击BIND SERVICE按钮,输出如图:
259 | 
260 | 当再多次点击则不会再有Log输出.
261 | 点击UBIND SERVICE按钮,输出如图:
262 | 
263 |
264 | >注:任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity建立关联,还可以和任何一个Activity建立关联,而且在建立关联时它们都可以获取到相同的MyBinder实例.
265 |
266 | ### 销毁Service的方法
267 | 根据之前的测试,我们知道,点击STARTSERVICE按钮启动Service,再点击STOPSERVICE按钮停止Service,这样MyService就被销毁了.
268 | 如果我们是点击的Bind Service按钮呢?由于在绑定Service的时候指定的标志位是BIND_AUTO_CREATE,说明点击BindService按钮的时候Service也会被创建,这时应该呢?其实也很简单,点击一下UnbindService按钮,将Activity和Service的关联解除,从而销毁Service.
269 | 如果我们既点击了StartService按钮,又点击了BindService按钮,这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁.也就是说,点击StopService按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁.
270 | 我们测试下.
271 | ````java
272 | public void onClick(View v) {
273 | switch (v.getId()) {
274 | case R.id.start_service:
275 | Intent startIntent = new Intent(this, MyService.class);
276 | startService(startIntent);
277 | break;
278 | case R.id.stop_service:
279 | Log.d("MyService", "click Stop Service button");
280 | Intent stopIntent = new Intent(this, MyService.class);
281 | stopService(stopIntent);
282 | break;
283 | case R.id.bind_service:
284 | Intent bindIntent = new Intent(this, MyService.class);
285 | bindService(bindIntent, connection, BIND_AUTO_CREATE);
286 | break;
287 | case R.id.unbind_service:
288 | Log.d("MyService", "click Unbind Service button");
289 | unbindService(connection);
290 | break;
291 | default:
292 | break;
293 | }
294 | }
295 |
296 | ````
297 | 重新运行程序,先点击一下STARTSERVICE按钮,再点击一下BindService按钮,这样就将Service启动起来,并和Activity建立了关联.然后点击StopService按钮后Service并不会销毁,再点击一下UnbindService按钮,Service就会销毁了,打印日志如下所示:
298 | 
299 |
300 | >注:根据Android系统的机制,一个服务只要被启动或者被绑定之后,就会一直处于运行状态
301 |
302 | ### Service的生命周期
303 | 服务的生命周期比 Activity 的生命周期要简单得多.但是,密切关注如何创建和销毁服务反而更加重要,因为服务可以在用户没有意识到的情况下运行于后台.
304 | 服务生命周期(从创建到销毁)可以遵循两条不同的路径:
305 |
306 | - 启动服务
307 | + 该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行.此外,其他组件也可以通过调用 stopService() 来停止服务.服务停止后,系统会将其销毁.
308 | - 绑定服务
309 | + 该服务在另一个组件(客户端)调用 bindService() 时创建.然后,客户端通过 IBinder 接口与服务进行通信.客户端可以通过调用 unbindService() 关闭连接.多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务. (服务不必自行停止运行.)
310 | 这两条路径并非完全独立.也就是说,您可以绑定到已经使用 startService() 启动的服务.例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务.随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务.在这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会真正停止服务.
311 | ### 实现生命周期回调
312 | 与 Activity 类似,服务也拥有生命周期回调方法,您可以实现这些方法来监控服务状态的变化并适时执行工作.
313 | 看代码:
314 | ````java
315 | public class ExampleService extends Service {
316 | int mStartMode; // indicates how to behave if the service is killed
317 | IBinder mBinder; // interface for clients that bind
318 | boolean mAllowRebind; // indicates whether onRebind should be used
319 |
320 | @Override
321 | public void onCreate() {
322 | // The service is being created
323 | }
324 | @Override
325 | public int onStartCommand(Intent intent, int flags, int startId) {
326 | // The service is starting, due to a call to startService()
327 | return mStartMode;
328 | }
329 | @Override
330 | public IBinder onBind(Intent intent) {
331 | // A client is binding to the service with bindService()
332 | return mBinder;
333 | }
334 | @Override
335 | public boolean onUnbind(Intent intent) {
336 | // All clients have unbound with unbindService()
337 | return mAllowRebind;
338 | }
339 | @Override
340 | public void onRebind(Intent intent) {
341 | // A client is binding to the service with bindService(),
342 | // after onUnbind() has already been called
343 | }
344 | @Override
345 | public void onDestroy() {
346 | // The service is no longer used and is being destroyed
347 | }
348 | }
349 | ````
350 |
351 | >注:与 Activity 生命周期回调方法不同,您不需要调用这些回调方法的超类实现.
352 |
353 | 
354 | 服务生命周期左图显示了使用 startService() 所创建的服务的生命周期,右图显示了使用 bindService() 所创建的服务的生命周期.对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态).对于绑定服务,有效生命周期在 onUnbind() 返回时结束.
355 |
356 | 通过实现这些方法,您可以监控服务生命周期的两个嵌套循环:
357 |
358 | - 服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束.与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源.例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程.
359 | 无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法.
360 | - 服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始.每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService().
361 |
362 | >注:尽管启动服务是通过调用 stopSelf() 或 stopService() 来停止,但是该服务并无相应的回调(没有 onStop() 回调).因此,除非服务绑定到客户端,否则在服务停止时,系统会将其销毁—onDestroy() 是接收到的唯一回调.
363 |
364 |
365 | ---
366 | 参考:
367 | [Service](https://developer.android.com/guide/components/services.html)
368 | [Android Service完全解析,关于服务你所需知道的一切(上)](http://blog.csdn.net/guolin_blog/article/details/11952435#t0)
369 | [ Android Service完全解析,关于服务你所需知道的一切(下)](http://blog.csdn.net/guolin_blog/article/details/9797169)
370 | [Android开发指南——Service](http://yuweiguocn.github.io/2016/04/02/android-guide-service/)
371 | 《第一行代码》第9章
372 | 《疯狂Android讲义第2版》第10章
--------------------------------------------------------------------------------
/Android/笔记/单例总结.md:
--------------------------------------------------------------------------------
1 | # 单例总结
2 | ## Eager initialization(饿汉式)
3 | 如果程序一开始就需要某个单例,并且创建这个单例并不那么费时,我们可以考虑用这种方式:
4 | ````java
5 | public class Singleton{
6 | private static final Singleton INSTANCE = new Singleton();
7 |
8 | private Singleton(){}
9 |
10 | public static Singleton getInstace(){
11 | return INSTANCE;
12 | }
13 | }
14 | ````
15 | 特点:
16 |
17 | - 实例一开始就被创建(Eager initialization)。
18 | - getInstance()方法不需要加synchronize来解决多线程同步的问题。
19 | - final关键字确保了实例不可变,并且只会存在一个。
20 |
21 | ## Lazy initialization(懒汉式)
22 | ````java
23 | public final class LazySingleton{
24 | private static volatile LazySingleton instance = null;
25 |
26 | private LazySingleton(){}
27 |
28 | public static LazySingleton getInstance(){
29 | if (instance == null) {
30 | synchronized(LazySingleton.class){
31 | instance = new LazySingleton();
32 | }
33 | }
34 | return instance;
35 | }
36 | }
37 | ````
38 | **注意:** 上面的写法其实是非线程安全的,假设两个Thread同时进入了getInstance方法,都判断到instance==null,然后会因为synchronized的原因,逐个执行,这样就得到了2个实例。解决这个问题,需要用到典型的**Double CheckLock**方式。
39 |
40 | ## Double CheckLock(双重锁判断机制)
41 | 方式一:
42 | ````java
43 | public class LazySingleton{
44 | private static volatile LazySingleton instance = null;
45 |
46 | private LazySingleton(){}
47 |
48 | public static LazySingleton getInstance(){
49 | if (instance == null) {
50 | synchronized(LazySingleton.class){
51 | if (instance == null) {
52 | instance = new LazySingleton();
53 | }
54 | }
55 | }
56 | return instance;
57 | }
58 | }
59 | ````
60 |
61 | 方式二:
62 | ````java
63 | public class LazySingleton{
64 | private static volatile LazySingleton instance = null;
65 |
66 | private LazySingleton(){}
67 |
68 | public static synchronized LazySingleton getInstance(){
69 | if (instance == null) {
70 | instance =new LazySingleton();
71 | }
72 | return instance;
73 | }
74 | }
75 | ````
76 | ## Static block initialization
77 | 如果我们对程序的加载顺序有点了解的话,会知道Static block的初始化是执行在加载类之后,Constructor被执行之前。
78 | ````java
79 | public class StaticBlockSingleton{
80 | private static final StaticBlockSingleton INSTANCE;
81 |
82 | static{
83 | try{
84 | INSTANCE = new StaticBlockSingleton();
85 | }catch(Exception e){
86 | throw new RuntimeException("Error, You Know This, Haha!",e);
87 | }
88 | }
89 |
90 | public static StaticBlockSingleton getInstance(){
91 | return INSTANCE;
92 | }
93 |
94 | private StaticBlockSingleton(){}
95 | }
96 | ````
97 | **弊端:** 如果我们类有若干个static的变量,程序的初始化却只需要其中的1,2个的话,我们会做多余的static initialization。
98 |
99 | ## Bill Pugh solution
100 | ````java
101 | public class Singleton{
102 | private Singleton(){}
103 |
104 | private static class SingletonHolder{
105 | public static final Singleton INSTANCE = new Singleton();
106 | }
107 |
108 | public static Singleton getInstance(){
109 | return SingletonHolder.INSTANCE;
110 | }
111 | }
112 | ````
113 | SingletonHolder类会在你需要的时候才会被初始化,而且它不影响Singleton类的其他static成员变量的使用。这个方法是线程安全的并且避免了使用volatile与synchronized。
114 |
115 | ## 枚举
116 | 这是最简便安全的方法。没有明显的缺点,并且避免了下面要讲到的序列化的隐患。
117 | ````java
118 | public enum Singleton{
119 | INSTANCE;
120 | public void execute(String arg){
121 |
122 | }
123 | }
124 | ````
125 |
126 | ## Serialize and de-serialize
127 | 在某些情况下,需要实现序列化的时候,普通的单例模式需要添加readResolve的方法,不然会出现异常。
128 | ````java
129 | public class DemoSingleton implements Serializable{
130 | private volatile static DemoSingleton instance = null;
131 |
132 | public static DemoSingleton getInstance(){
133 | if (instance == null) {
134 | instance = new DemoSingleton();
135 | }
136 | return instance;
137 | }
138 |
139 | protected Object readResolve(){
140 | return instance;
141 | }
142 |
143 | private int i = 10;
144 |
145 | public int getI() {
146 | return i;
147 | }
148 |
149 | public void setI(int i) {
150 | this.i = i;
151 | }
152 |
153 | }
154 | ````
155 | 仅仅有上面的还不够,我们需要添加 serialVersionUID。
156 |
157 | ## 总结
158 | 实现一个功能完善,性能更佳,不存在序列化等问题的单例,建议使用下面两个方式之一:
159 |
160 | 方式一:
161 | Bill Pugh(Inner Holder)
162 | ````java
163 | public class DemoSingleton implements Serializable{
164 | private static final long serialVersionUID = 1L;
165 |
166 | private DemoSingleton(){}
167 |
168 | private static class DemoSingletonHolder{
169 | public static final DemoSingleton INSTANCE = new DemoSingleton();
170 | }
171 |
172 | public static DemoSingleton getInstance(){
173 | return DemoSingletonHolder.INSTANCE;
174 | }
175 |
176 | protected Object readResolve(){
177 | return getInstance();
178 | }
179 | }
180 | ````
181 | 方式二:
182 | Enum
183 | ````java
184 | public enum Singleton {
185 | INSTANCE;
186 | public void execute (String arg) {
187 | // perform operation here
188 | }
189 | }
190 | ````
--------------------------------------------------------------------------------
/ES6笔记/ES6 声明变量的六种方法.md:
--------------------------------------------------------------------------------
1 | # ES6 声明变量的六种方法
2 | ES5 只有两种声明变量的方法:var 命令和 function 命令。
3 |
4 | ES6 除了添加 let 和 const 命令,另外两种声明变量的方法:import 命令和 class 命令。所以,ES6 一共有 6 种声明变量的方法。
5 |
6 | # 顶层对象的属性
7 |
8 | 顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
9 |
10 | ES6 为了改变这一点,一方面规定,为了保持兼容性,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
11 |
12 | # global 对象
13 |
14 | ES5 的顶层对象,本身也是一个问题,因为它在各种实现里面是不统一的。
15 |
16 | - 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。
17 | - 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。
18 | - Node 里面,顶层对象是global,但其他环境都不支持。
19 |
20 | 同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性。
21 |
22 | - 全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
23 | - 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。
24 | - 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全政策),那么eval、new Function这些方法都可能无法使用。
25 |
26 | 两种勉强取到顶层对象的方法:
27 | ````js
28 | //方法一
29 | (typeof window !== 'undefined'
30 | ? window
31 | :(typeof process === 'object' &&
32 | typeof require === 'function' &&
33 | typeof global === 'object')
34 | ? global
35 | : this);
36 |
37 | // 方法二
38 | var getGlobal = function(){
39 | if(typeof self !== 'undefined'){return self;}
40 | if(typeof window !== 'undefined'){return window;}
41 | if(typeof global !== 'undefined'){return global;}
42 | throw new Error('unable to locate global object');
43 | };
44 | ````
45 |
--------------------------------------------------------------------------------
/ES6笔记/const.md:
--------------------------------------------------------------------------------
1 | # const 命令
2 |
3 | const 声明一个只读的常量。一旦声明,常量的值就不能改变。
4 |
5 | const 声明的变量不得改变值,这意味着,const 一旦声明变量,就必须立即初始化,不能留到以后赋值。
6 |
7 | const 的作用域与 let 命令相同:只在声明所在的块级作用域内有效。
8 |
9 | const 命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
10 |
11 | const 声明的常量,也与 let 一样不可重复声明。
12 |
13 | const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个**内存地址**不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个**内存地址**,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的**内存地址**,保存的只是一个**指针**,const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
14 |
15 | 将对象彻底冻结的函数:
16 |
17 | ````js
18 | var constantize = (obj) =>{
19 | Object.freeze(obj);
20 | Object.keys(obj).foreach((key,i) => {
21 | if(typeof obj[key] === 'object'){
22 | constantize(obj[key]);
23 | }
24 | });
25 | };
26 | ````
--------------------------------------------------------------------------------
/ES6笔记/let.md:
--------------------------------------------------------------------------------
1 | # let 命令
2 | ES6 新增了 **let** 命令来声明变量。
3 |
4 | 它的用法类似于 var,但是所声明的变量只能在 **let**命令所在的代码块内有效。
5 |
6 | > 例子
7 |
8 | 使用 var:
9 | ````js
10 | var a = [];
11 | for(var i = 0; i < 10 ; i++){
12 | a[i] = function(){
13 | console.log(i);
14 | };
15 | }
16 | a[6]();
17 | ````
18 | 上面代码中,变量 i 是 var 命令声明的,在全局范围内都有效,所以全局只有一个变量 i。每一次循环,变量 i 的值都会发生改变,而循环内被赋给数组 a 的函数内部的 console.log(i),里面的 i 指向的就是全局的 i。也就是说,所有数组 a 的成员里面的 i,指向的都是同一个 i,导致运行时输出的是最后一轮的 i 的值,也就是 10。
19 |
20 | 使用 let :
21 | ````js
22 | var a = [];
23 | for(let i = 0; i<10; i++){
24 | a[i] = function(){
25 | console.log(i);
26 | };
27 | }
28 | a[6]();
29 | ````
30 | 上面代码中,变量 i 是 let 声明的,当前的 i 只在本轮循环有效,所以每一次循环的 i 其实都是一个新的变量,所以最后输出的是 6。
31 |
32 | for 循环的一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的自作用域。
33 |
34 | let 不存在变量提升的问题,即所声明的变量一定要在声明后使用,否则报错。而 var 命令会发生变量提升的现象,即变量可以在声明之前使用,值为 undefined。
35 |
36 | > 示例
37 |
38 | ````js
39 | //var 情况
40 | console.log(foo); // 输出 undefined
41 | var foo = 2;
42 |
43 | // let 的情况
44 | console.log(bar); // 报错ReferenceError
45 | let bar = 2;
46 | ````
47 |
48 | 只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
49 | ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
50 | 总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
51 |
52 | 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
53 | “暂时性死区”也意味着 typeof 不再是一个百分之百安全的操作。
54 |
55 | let不允许在相同作用域内,重复声明同一个变量。
56 |
57 | > 例子
58 |
59 | ````js
60 | // 报错
61 | function func() {
62 | let a = 10;
63 | var a = 1;
64 | }
65 |
66 | // 报错
67 | function func() {
68 | let a = 10;
69 | let a = 1;
70 | }
71 | ````
72 | 因此,不能在函数内部重新声明参数。
73 |
74 | ````js
75 | function func(arg) {
76 | let arg; // 报错
77 | }
78 |
79 | function func(arg) {
80 | {
81 | let arg; // 不报错
82 | }
83 | }
84 | ````
85 | let 实际上为 JavaScript 新增了块级作用域。
86 | ES6 允许块级作用域的任意嵌套,外层作用域无法读取内层作用域的变量;内层作用域可以定义外层作用域的同名变量;
87 |
88 | ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
89 |
90 | 考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
91 | ````js
92 | // 函数声明语句
93 | {
94 | let a = 'secret';
95 | function f() {
96 | return a;
97 | }
98 | }
99 |
100 | // 函数表达式
101 | {
102 | let a = 'secret';
103 | let f = function () {
104 | return a;
105 | };
106 | }
107 | ````
108 |
109 | 另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
110 | ````js
111 | // 不报错
112 | 'use strict';
113 | if (true) {
114 | function f() {}
115 | }
116 |
117 | // 报错
118 | 'use strict';
119 | if (true)
120 | function f() {}
121 | ````
122 |
--------------------------------------------------------------------------------
/ES6笔记/变量的解构赋值.md:
--------------------------------------------------------------------------------
1 | # 变量的解构赋值
2 |
3 | ## 数组的解构赋值
4 | ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,被称为解构。
5 |
6 | ## 默认值
7 | ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
8 | 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
9 |
10 | ## 对象的解构赋值
11 | 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
12 |
13 | 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
14 |
15 | ## 不能使用圆括号的情况
16 |
17 | 1. 变量声明语句
18 | ````js
19 | // 全部报错
20 | let [(a)] = [1];
21 |
22 | let {x: (c)} = {};
23 | let ({x: c}) = {};
24 | let {(x: c)} = {};
25 | let {(x): c} = {};
26 |
27 | let { o: ({ p: p }) } = { o: { p: 2 } };
28 | ````
29 | 它们都是变量声明语句,模式不能使用圆括号。
30 |
31 | 2. 函数参数
32 | 函数参数也属于变量声明,因此不能带有圆括号。
33 | ````js
34 | // 报错
35 | function f([(z)]) { return z; }
36 | // 报错
37 | function f([z,(x)]) { return x; }
38 | ````
39 |
40 | 3. 赋值语句的模式
41 | ````js
42 | // 全部报错
43 | ({ p: a }) = { p: 42 };
44 | ([a]) = [5];
45 | ````
46 |
47 | ## 可以使用圆括号的情况
48 | 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
49 | ````js
50 | [(b)] = [3]; // 正确
51 | ({ p: (d) } = {}); // 正确
52 | [(parseInt.prop)] = [3]; // 正确
53 | ````
54 |
55 | ## 用途
56 | 1. 交换变量的值;
57 | 2. 从函数返回多个值;
58 | 3. 函数参数的定义;
59 | 4. 提取 JSON 数据;
60 | 5. 函数参数的默认值;
61 | 6. 遍历 Map 结构。
62 | 7. 输入模块的指定方法。
--------------------------------------------------------------------------------
/ES6笔记/字符串的扩展.md:
--------------------------------------------------------------------------------
1 | # 字符串的扩展
2 |
--------------------------------------------------------------------------------
/Git/Git.md:
--------------------------------------------------------------------------------
1 | # Git 操作
2 | ## 创建版本库
3 | 创建一个空目录
4 | ````java
5 | $ mkdir MyNote
6 | $ cd MyNote
7 | $ pwd
8 | /f/MyNote
9 | ````
10 | **pwd**命令用于显示当前目录。
11 | 通过**git init**命令把这个目录变成Git可以管理的仓库:
12 | ````java
13 | $ git init
14 | ````
15 | 仓库建好了。
16 | ## 把文件添加到版本库
17 | 编写一个**ReadMe.md**文件,然后放到Git仓库。
18 | 第一步,用命令**git add**告诉Git,把文件添加到仓库:
19 | ````java
20 | $ git add ReadMe.md
21 | ````
22 | 第二步,用命令**git commit**告诉Git,把文件提交到仓库:
23 | ````java
24 | $ git commit -m "create ReadMe.md" //-m后面输入的是本次提交的说明,可以输入任意内容。
25 | ````
26 | ## 添加到GitHub
27 | 先在github创建新的仓库,完成后在本地的**MyNote**仓库下运行命令:
28 | ````java
29 | $ git remote add origin git@github.com:wuzhaohui026/MyNote.git
30 | $ git push -u origin master //把本地库的所有内容推送到远程库上
31 |
32 | ````
33 | 从现在起,只要本地作了提交,就可以通过命令:
34 | ````java
35 | $ git push origin master
36 | ````
37 | ## 分支
38 | 创建**dev**分支
39 | ````java
40 | $ git checkout -b dev
41 | ````
42 | **git checkout**命令加上**-b**参数表示创建并切换,相当于以下两条命令:
43 | ````java
44 | $ git branch dev
45 | $ git checkout dev
46 | ````
47 | 用**git branch**命令查看当前分支:
48 | ````java
49 | $ git branch
50 | ````
51 | **git branch**命令会列出所有分支,当前分支前面会标一个*号。
52 | 当**dev**分支的工作完成,我们就可以切换回**master**分支:
53 | ````java
54 | $ git checkout master
55 | ````
56 | 把**dev**分支的工作成果合并到**master**分支上:
57 | ````java
58 | $ git merge dev //git merge命令用于合并指定分支到当前分支。
59 | ````
60 | 删除dev分支了
61 | ````java
62 | $ git branch -d dev
63 | ````
64 | ## 冲突
65 | 当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
66 | ## 相关命令
67 | - **git status**:可以让我们时刻掌握仓库当前的状态。
68 | - **git diff**:顾名思义就是查看difference,显示的格式正是Unix通用的diff格式。
69 | - **git log**:显示从最近到最远的提交日志。如果嫌输出信息太多,看得眼花缭乱的,可以试试加上** - -pretty=oneline**参数
70 | - **git reset - -hard 版本号**:版本回退
71 | > 在Git中,用**HEAD**表示当前版本,上一个版本就是**HEAD^**,上上一个版本就是**HEAD^^**,当然往上100个版本写100个^比较容易数不过来,所以写成 **HEAD~100**。
72 | - **git reflog**:用来记录你的每一次命令。
73 | - **git checkout - - file**:意思就是把文件在工作区的修改全部撤销。
74 | - **git reset HEAD file**:可以把暂存区的修改撤销掉(unstage),重新放回工作区。
75 | - **rm file**删除文件或者 **git rm file**删掉,并且**git commit**。
76 | - **git clone**:克隆一个本地库。
77 | - **git branch**:查看分支。
78 | - **git branch **:创建分支。
79 | - **git checkout **:切换分支。
80 | - **git checkout -b **:创建+切换分支。
81 | - **git merge **:合并某分支到当前分支。
82 | - **git branch -d **:删除分支。
83 | - **git log - -graph**:查看分支合并图。
84 | - ** git merge - -no-ff -m "merge with no-ff" dev**:- -no-ff参数,表示禁用Fast forward。
85 | - **$ git stash**:把当前工作现场“储藏”起来,等以后恢复现场后继续工作。
86 | - **git stash list**:查看工作现场。
87 | - **git stash apply**:恢复现场,再用**git stash drop**来删除。
88 | - **git stash pop**:恢复的同时把stash内容也删了。
89 | - **git branch -D **:强行删除一个没有被合并过的分支。
90 | - **git remote**:查看远程库的信息。
91 | - **git remote -v**:显示更详细的信息。
92 | - **git push origin dev**:推送其他分支。
93 | - **git pull**:把最新的提交抓下来。
94 | - **git tag **:打标签。
95 | - **git show **:查看标签信息。
96 | - **git tag -a -m "blablabla..."**:可以指定标签信息。
97 | - **git tag -s -m "blablabla..."**:可以用PGP签名标签。
98 | - **git push origin **:可以推送一个本地标签。
99 | - **git push origin --tags**:可以推送全部未推送过的本地标签。
100 | - **git tag -d **:可以删除一个本地标签。
101 | - **git push origin :refs/tags/**:可以删除一个远程标签。
--------------------------------------------------------------------------------
/Java/Java基础知识.md:
--------------------------------------------------------------------------------
1 | # Java基础知识
2 | 1. JDK安装路径下的文件路径包含的内容:
3 | bin:该路径下存放了JDK的各种工具命令,常用的javac,java等命令就放在该路径下。
4 | db:该路径是安装Java DB的路径。
5 | demo:该路径下存放了JDK提供的演示带代码,初学者可以参考这些演示代码。
6 | jre:该路径下安装的就是运行Java程序所需要的JRE环境。
7 | lib:该路径下存放的是JDK工具命令的实际执行程序。
8 | sample:该路径下存放了JDK提供的一些简单示例代码,初学者可以参考这些示例代码。
9 | src.zip:该压缩文件里存放的就是Java所以核心类库的源代码。
10 | README和LICE等说明性文档。
11 |
12 | 2. 什么是环境变量:
13 | 环境变量通常是指在操作系统当中,用来指定系统运行时需要的一些参数。环境变量通常为一系列的键值对。
14 |
15 | 3. PATH环境变量的作用:
16 | Path环境变量是操作系统外部命令搜索路径。
17 |
18 | 4. CLASSPATH环境变量的作用:
19 | CLASSPATH环境变量是类文件搜索路径。
20 |
21 | 5. 什么是JRE:
22 | JRE是Java Runtime Environment,即Java运行环境,包括:Java虚拟机。Java平台核心类文件,其他支持文件。
23 |
24 | 6. Java的数据类型:
25 | 
26 |
27 | 7. Java中的**“=”**不是等号,而是赋值号,将赋值号右边的值赋值给赋值号左边的变量。
28 |
29 | 8. 变量的命名规范:
30 | 1.变量命名语法规范:
31 | 1.应该以字母、下划线或者$开头;
32 | 2.后面跟字母、下划线、$或者是数字;
33 | 3.Java变量名没有长度限制;
34 | 4.Java变量名对大小写敏感
35 | 2.驼峰命名法:
36 | 1.变量名应该用有意义的英文单词;
37 | 2.变量名如果只有一个单词,则所有的字母小写;
38 | 3.变量名如果由多个英文单词组成,则从第二个单词开始首字母大写。
39 |
40 | 9. Java当中的运算符
41 | 算术运算符:+,-,*,/,%,++,-- 。
42 | 关系运算符:> , < , >= , <= , == , != 。
43 | 布尔逻辑运算符:!,&,|,^ , && ,|| 。
44 | 
45 | 位运算符:& , | , ^ , ~ , >> , << , >>> 。
46 | 赋值运算符:= 。
47 | 扩展运算符:+= , -= ,*= , /= 。
48 | 字符串连接运算符: + 。
49 |
50 | 10. i++和++i的区别是什么?
51 | i++和++i都是把i的值加1
52 | i++是先将i的值进行运算,然后再加1。
53 | ++i是先将i的值加1,再进行运算。
54 |
55 | 11. 重载的定义:
56 | 1.两个或者多个函数在同一个类当中;
57 | 2.函数名相同;
58 | 3.参数列表不同。
59 |
60 | 12. 构造方法:
61 | 使用new+构造方法创建一个新的对象;
62 | 没有返回值,名字必须和类名相同;
63 | 作用是用来生成对象;
64 | 构造方法是定义在Java类中的一个用来初始化对象的方法;
65 | 没有构造方法时,系统会自动添加无参构造方法,有就不会添加。
66 | 构造方法的重载:方法名相同,但参数不同的多个方法,调用时会自动根据不同的参数选择相应的方法。
67 | 构造方法不但可以给对象的属性赋值,还可以保证给对象的属性赋一个合理的值。
68 |
69 | 13. 继承:
70 | 在面向对象的世界当中,继承就是一个类得到另一个类当中的成员变量和成员方法。
71 | 在Java中只支持**单继承**,不允许多继承。即一个子类只允许继承一个父类。
72 | private修饰的属性是无法继承的。
73 | **继承的初始化顺序:**
74 | 1.初始化父类再初始化子类
75 | 2.先执行初始化对象中的属性,再执行构造方法中的初始化。
76 | **final关键字:**
77 | final可以修饰类,方法,属性和变量;
78 | finally修饰类,则该类不允许被继承;
79 | final修饰方法,则该方法不允许被重写;
80 | final修饰属性,则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(只能选其一);
81 | fianl修饰变量,则改变量的值只能赋一次值即变为常量。
82 | **super的应用:**
83 | 子类的构造过程当中必须调用其父类的构造方法;
84 | 如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法;
85 | 如果显示的调用构造方法,必须在子类构造方法的第一行;
86 | 如果子类构造方法中既没有显示调用父类的构造方法,而父类又没有无参的构造方法,则编译出错。
87 |
88 | 14. 重写:
89 | override,也被称为覆盖或者重写。
90 | 1.在具有父子关系的两个类当中
91 | 2.父类和子类各有一个函数,这两个函数的定义(返回值型、函数名和参数列表)完全相同。
92 |
93 | 15. 接口的基本语法:
94 | 1.使用interface定义;
95 | 2.接口当中的方法都是抽象方法;
96 | 3.接口当中的方法都是public权限;
97 | 4.实现接口使用implements关键字;
98 | 5.一个类可以实现多个接口;
99 | 6.一个接口可以继承多个接口;
100 | 7.接口中方法不能有方法体,同时方法的访问修饰符不能是 private 和 protected。
101 |
102 | 16. 判断方法虫重载的依据:
103 | 1.必须在同一个类中;
104 | 2.方法名相同;
105 | 3.方法参数的个数、顺序或类型不同;
106 | 4.与方法的修饰符或返回值没有关系。
107 |
108 | 17. 不容忽视的小陷阱:
109 | 1.如果返回方法的返回类型为void,则方法中不能使用return返回值!
110 | 2.方法的返回值最多只能有一个,不能返回多个值;
111 | 3.方法返回值的类型必须兼容。
112 |
113 | 18. 成员变量和局部变量的区别:
114 | 1.作用域不同:
115 | 局部变量的作用域仅限于定义它的方法;
116 | 成员变量的作用域在整个类内部都是可见的;
117 | 2.初始值不同:
118 | Java会给成员变量一个初始值;
119 | Java不会给局部变量赋予初始值;
120 | 3.在同一个方法中,不允许有同名局部变量;
121 | 在不同方法中,可以有同名局部变量;
122 | 4.两类变量同名时,局部变量具有更高的优先级。
123 |
124 | 19. 静态变量:
125 | 1.静态变量可以直接使用类名来访问,无需创建类的对象:
126 | 类名.变量名
127 | 2.使用对象名来访问静态变量:
128 | 对象名.变量名
129 | 3.静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员;
130 | 如果希望在静态方法中调用非静态变量,可以通过创建类的对象,
131 | 然后通过对象来访问非静态变量
132 | 4.在普通成员方法中,则可以直接访问同类的非静态变量和静态变量;
133 | 5.静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,
134 | 不能初始化普通的成员变量。
135 | 20. 封装:
136 | 1.将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
137 | 2.封装的好处:
138 | 只能通过规定的方法访问数据;
139 | 隐藏类的实例细节,方便修改和实现。
140 | 3.实现步骤:
141 | 1.修改属性的可见性;(设为private)
142 | 2.创建getter/setter方法;(用于属性的读写)
143 | 3.在getter/setter方法中加入属性控制语句。(对属性值的合法性进行判断)
144 |
145 | 21. 内部类:
146 | 1.内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。
147 | 2.作用:
148 | 1.内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类;
149 | 2.内部类的方法可以直接访问外部类的所有数据,包括私有的数据;
150 | 3.内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。
151 | 3.种类:
152 | 1.成员内部类
153 | 2.静态内部类
154 | 3.方法内部类
155 | 4.匿名内部类
156 | 4.定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,
157 | 即:内部类 对象名 = 外部类对象.new 内部类( );
158 | 5.注意:
159 | 1.外部类是不能直接使用内部类的成员和方法。
160 | 2.如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,
161 | 如果要访问外部类的成员变量,可以使用 this 关键字。
162 | 6.静态内部类是 static 修饰的内部类,
163 | 这种内部类的特点是:
164 | 1.静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问;
165 | 2.如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;
166 | 如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员;
167 | 3.创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
168 | 7.方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
169 | 注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。
170 |
171 | 22. 多态:
172 | 对象的多种形态
173 | 1.引用多态:
174 | 父类的引用可以指向本类的对象;
175 | 父类的引用可以指向子类的对象;
176 | 2.方法多态:
177 | 创建本类对象时,调用的方法为本类方法;
178 | 创建子类对象时,调用的方法为子类重写的方法或者继承的方法。
179 |
180 | 23. 抽象类:
181 | abstract定义抽象类;
182 | abstract定义抽象方法,只有声明,不需要实现;
183 | 包含抽象方法的类是抽象类;
184 | 抽象类中可以包含普通的方法,也可以没有抽象方法;
185 | 抽象类不能直接创建,可以定义引用变量。
186 |
187 | 24. StringBuilder 类和 StringBuffer 类之间的区别:
188 | StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。
189 | 如何定义 StringBuilder 类的对象:
190 | StringBuilder str1 = new StringBuilder();
191 | //创建一个空的StringBuilder对象
192 | StringBuilder str2 = new StringBuilder("aaa");
193 | System.out.println(str2);
194 |
195 | 25. 基本类型转换为字符串有三种方法:
196 | 1.使用包装类的 toString() 方法;
197 | 2.使用String类的 valueOf() 方法;
198 | 3.用一个空字符串加上基本类型,得到的就是基本类型数据对应的字符串;
199 |
200 | 26. Calendar 类的应用
201 | // 创建Canlendar对象
202 | Calendar c = Calendar.getInstance();
203 | // 将Calendar对象转换为Date对象
204 | Date date = c.getTime();
205 | // 创建SimpleDateFormat对象,指定目标格式
206 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
207 | // 将日期转换为指定格式的字符串
208 | String now = sdf.format(date);
209 | System.out.println("当前时间:" + now);
210 |
211 | 27. 集合的作用:
212 | 在类的内部,对数据进行组织;
213 | 简单而快速的搜索大数量的条目;
214 | 有点集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关元素;
215 | 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型。
216 | 
217 | Collection接口:
218 | 是List、Set和Queue接口的父接口;
219 | 定义了可用于操作List、Set和Queue的方法-----增删查改;
220 |
221 | 28. List接口及其实现类---ArrayList
222 | List是元素有序并且可以重复的集合,被称为序列;
223 | List可以精确的控制每个元素的插入位置,或删除某个位置元素。
224 | ArrayList----数组序列,是List的一个重要实现类;
225 | ArrayList底层是由数组实现的。
226 |
227 | 29. Set接口及其实现类----HsahSet
228 | Set是元素无序并且不可以重复的集合,被成为集;
229 | HashSet-----哈希集,是Set的一个重要实现类。
230 |
231 | 30. Map接口:
232 | Map提供了一种映射关系,其中的元素是以键值对的形式存储的,能够实现根据key快速查找value;
233 | Map中的键值对以Entry类型的对象实例形式存在;
234 | 键不可重复,value值可以;
235 | 每个键最大只能映射到一个值;
236 | Map接口提供了分别返回key值集合、value值集合以及Entry集合的方法;
237 | Map支持泛型,形式如:Map。
238 |
239 | 31. HashMap类:
240 | HashMap是Map的一个重要实现类,也是最常用的,基于哈希表实现;
241 | HashMap中的Entry对象是无序排列的;
242 | Key和Value值都可以为null,但是一个HashMap只能有一个key值为null的映射(key值不可重复)。
243 |
244 | 32. Comparable接口------可比较的
245 | 实现该接口表示:这个类的实例可以比较大小,可以进行自然排序;
246 | 定义了默认的比较规则;
247 | 其实现类需实现compare TO()方法;
248 | compare TO()方法返回正数表示大,负数表示小,0表示相等。
249 |
250 | 33. Comparator接口------比较工具接口
251 | 用于定义临时比较规则,而不是默认比较规则;
252 | 其实现类需要实现compare()方法;
253 | Comparator和Comparable都是Java集合框架的成员。
254 |
255 | 34. JAVA中的异常分为编译时异常即CheckedException和运行时异常RuntimeException.
256 | Checked异常处理方法有两种:
257 |
258 | - 当前方法知道如何处理该异常,则用try...catch块来处理该异常
259 | - 当前方法不知道如何处理,则在定义该方法时抛出该异常。
260 | 运行时异常只有当代码在运行时才发生的异常,由系统自动检测并将他们交给缺省的异常处理程序。
261 |
262 | 35. Java中的几种类型的流:
263 | 字节流和字符流。
264 | 字节流继承于InputStream和OutputStream.
265 | 字符流继承于InputStreamReader和OutputStreamWriter.
266 |
267 | 36. 字节流转为字符流:
268 | 字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。
269 | 字节输出流转字符输出流通过OutputStreamWriter实现,该类的构造函数可以传入OutputStream对象。
270 |
271 | 37. 在JAVA中wait和sleep方法的不同
272 | 在等待时wait会释放锁,而sleep一直持有锁。
273 | wait通常被用于线程间交互,sleep通常被用于暂停执行。
274 |
275 | 38. synchronized和volatile关键字的作用
276 | 一旦一个共享变量(类的成员变量,类的静态成员变量)被volatile修饰之后,就具备两层含义:
277 | ````
278 | - 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
279 | - 禁止进行指令重排序。
280 | ````
281 |
282 | + volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
283 | synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
284 | + volatile仅能使用在变量级别;
285 | synchronized则可以使用在变量、方法和类级别。
286 | + volatile仅能实现变量的修改可见性,并不能保证原子性;
287 | synchronized则可以保证变量的修改可见性和原子性。
288 | + volatile不会造成线程阻塞;
289 | synchronized可能会造成线程的阻塞。
290 | + volatile标记的变量不会被编译器优化;
291 | synchronized标记的变量可以被编译器优化。
292 |
293 | 39. 什么是线程池
294 | 线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高了代码的执行效率。
295 | 在JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。
296 |
297 | - ExecutorService newCachedThreadPool = Executors.newCacheThreadPool();
298 | - ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
299 | - ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);
300 | - ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
301 |
302 | 然后调用他们的execute()方法即可。
303 |
304 | 40. JAVA中的反射
305 | JAVA中的反射首先是能够获取到JAVA中要反射类的字节码,获取字节码又有三种方法:
306 |
307 | - Class.forName(className)
308 | - 类名.class
309 | - this.getClass()
310 | 然后将字节码中的方法,变量,构造函数等映射成相应的Method,Field,Constructor等类,这些类提供了丰富的方法供开发者使用。
311 |
312 | 41. Java中的设计模式
313 | 一般认为有23种设计模式,总体来说设计模式分为三大类。
314 |
315 | - 创建型模式:
316 | **工厂方法模式**
317 | **抽象工厂模式**
318 | **单例模式**
319 | **建造者模式**
320 | 原型模式
321 |
322 | - 结构型模式:
323 | **适配器模式**
324 | 装饰器模式
325 | **代理模式**
326 | 外观模式
327 | 桥接模式
328 | 组合模式
329 |
330 | - 行为型模式
331 | **策略模式**
332 | 模板方法模式
333 | **观察者模式**
334 | 迭代子模式
335 | 责任链模式
336 | 命令模式
337 | 备忘录模式
338 | 状态模式
339 | 访问者模式
340 | 中介者模式
341 | 解释权模式
342 |
343 | 42. 单例设计模式
344 | 分为懒汉式和饿汉式。
345 |
346 | - 饿汉式
347 | ````java
348 | public class Singleton{
349 | //直接创建对象
350 | public static Singleton instance = new Singleton();
351 |
352 | //私有化构造函数
353 | private Singleton(){ }
354 |
355 | //返回对象实例
356 | public static Singleton getInstance(){
357 | return instance;
358 | }
359 | }
360 | ````
361 | - 懒汉式
362 | ````java
363 | public class Singleton{
364 | //声明变量
365 | private static volatile Singleton singleton2 = null;
366 | //私有构造函数
367 | private Singleton2(){ }
368 |
369 | //提供对外方法
370 | public static Singleton2 getInstance(){
371 | if(singleton2 == null){
372 | sychronized(Singleton2.class){
373 | if(singleton == null){
374 | singleton = new Singleton();
375 | }
376 | }
377 | }
378 | return singleton;
379 | }
380 | }
381 | ````
382 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第10章 内部类.md:
--------------------------------------------------------------------------------
1 | # 第10章 内部类
2 | 1. 可以将一个类的定义放在另一个类的定义内部,这就是内部类。
3 | 2. 如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法中那样,具体地指明这个对象的类型:OuterClassName.InnerClassName。
4 | 3. 内部类拥有其外围类的所有元素的访问权。
5 | 4. 内部类如何做到拥有其外围类的所有元素的访问权?
6 | ````
7 | 当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用,然后,在你访问此外围
8 | 类的成员时,就是用那个引用来选择外围类的成员。
9 | ````
10 | 5. 如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。
11 | 6. 要想直接创建内部类对象,必须使用外部类的对象来创建内部类对象。
12 | ````
13 | OuterClassName ocn = new OuterClassName();
14 | OuterClassName.InnerClassName ocni = ocn.new InnerClassName();
15 | ````
16 | 7. 在拥有外部类对象之前是不可能创建内部类对象的,因为内部类对象会暗暗地连接到创建它的外部类对象上,但是如果创建的是嵌套类,就不需要对外部类对象的引用。
17 | 8. 在匿名内部类末尾的分号,并不是用来标记此内部类结束的,它标记的是表达式的结束,只不过这个表达式正好包含了匿名内部类罢了。
18 | 9. 通过实例初始化,可以达到为匿名内部类创建一个构造器的效果,但不能重载实例初始化方法。
19 | 10. 如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
20 | 11. 嵌套类:如果不需要内部类对象与其外围对象之间有联系,那么可以将内部类声明为static。
21 | 12. 普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。
22 | 13. 当内部类是static时,嵌套意味着:
23 | - 要创建嵌套类的对象,并不需要其外围类的对象
24 | - 不能从嵌套类的对象中访问非静态的外围类对象
25 | 14. 普通内部类的字段与方法,只能放在类的外部层次上,普通的内部类不能有static数据和static字段,也不能包含嵌套类;但是嵌套类可以包含所有这些东西。
26 | 15. 使用内部类的原因:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于都没有影响。
27 | 16. 内部类允许继承多个非接口类型(类或抽象类)。
28 | 17. 使用内部类,可以获得一些特性:
29 | - 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
30 | - 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
31 | - 创建内部类对象的时刻并不依赖于外围类对象的创建。
32 | - 内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。
33 | 18. 局部内部类不能访问说明符,因为不是外围类的一部分,它可以访问当前代码块内的常量,以及此外围类的所有成员。
34 | 19. 内部类标识符:外围类的名字,加上“$”,再加上内部类的名字。
35 | 如果内部类是匿名的,编译器会简单地产生一个数字作为其标识符,如果内部类是嵌套在别的内部类之中,只需要直接将它们的名字加在其外围类标识符与“$”的后面。
36 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第11章 持有对象.md:
--------------------------------------------------------------------------------
1 | # 第11章 持有对象
2 | 1. Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
3 | - Collection。一个独立元素序列,这些元素都服从一条或多条规则。List不许按照插入的顺序保存元素;Set不能有重复元素。Quene按照排队规则来确定对象产生的顺序。
4 | - Map。一组成对的“键值对”对象,允许你使用键来查找值。ArryList允许使用数字来查找值;映射表允许使用另一个对象来查找某个对象。
5 | 2. Array.asList()方法接受一个数组或是一个用逗号分割的元素列表(使用可变参数),并将其转换为一个List对象。
6 | 3. Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。
7 | 4. Collection在每个槽中只能保存一个元素。
8 | - List,它以特定的顺序保存一组元素;
9 | - Set,元素不能重复;
10 | - Queue,只允许在容器的一“端”插入对象,并从另一“端”移除对象。
11 | Map在每个槽内保存两个对象,即键和与之相关联的值。
12 | 5. Map.put(key,value)方法将增加一个值,并将与它某个键关联起来。
13 | 6. Map.get(key)方法将产生与这个键相关联的值。
14 | 7. 键和值在Map中保存的顺序并不是它们的插入顺序。
15 | - HashMap没有按照任何明显的顺序来保存其元素。
16 | - TreeMap按照比较结果的升序保存键。
17 | - LinkedHashMap按照插入顺序保存键,还保留了HashMap的查询速度。
18 | 8. List承诺可以将元素维护在特定的序列中。
19 | - 基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢
20 | - LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。
21 | 9. 迭代器是一个对象,用来遍历并选择序列中的对象。
22 | 10. Java中的Iterator只能单向移动,只能用来:
23 | - 使用方法iterator()要求容器返回一个Iterator,Iterator将准备好返回序列的第一个元素。
24 | - 使用Next()获得序列中的下一个元素
25 | - 使用hasNext()检查序列中是否还有元素
26 | - 使用remove()将迭代器新近返回的元素删除
27 | 11. ListIterator是一个更加强大的Iterator的子类型,只能用于各种List类的访问,可以双向移动。
28 | 12. 栈通常是指“后进先出”的容器,也称叠加栈。
29 | 13. 实际上Set就是Collection,只是行为不同,Set是基于对象的值来确定归属性的。
30 | 14. 队列是一个电信的先进先出的容器,即从容器的一端放入事务,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。
31 | 15. 先进先出声明的是下一个元素应该是等待事件最长的元素,优先级队列声明下一个弹出元素是最需要的元素。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第12章 通过异常处理错误.md:
--------------------------------------------------------------------------------
1 | # 第12章 通过异常处理错误
2 | 1. Java的基本理念是“结构不佳的代码不能运行”。
3 | 2. 异常情形是指阻止当前方法或作用域继续执行的问题。
4 | 3. 所有标注异常类都有2个构造器:
5 | - 一个是默认构造器
6 | - 另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器
7 | 4. 监控区域:是一段可能产生异常的代码,并且后面跟着处理这些异常的代码。
8 | 5. 异常处理理论上有两种基本模型:
9 | - 终止模型
10 | - 恢复模型
11 | 6. 对异常来说,最重要的部分就是类名
12 | 7. 重抛异常会把异常抛给上一级环境中的异常处理程序,同一个try块后续catch子句将被忽略。
13 | 8. 在Throwable的子类中,只有Error,Exception以及RunntimrException三种基本的异常类提供了带cause参数的构造器。
14 | 9. Throwable对象可以分为两种类型:
15 | - Error用来便是编译时和系统错误
16 | - Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception型异常。
17 | 10. 如果对null引用进行调用,Java会自动抛出NullPointerException异常。
18 | 11. 当要把除内存之外的资源不想恢复到它们的初始状态,就要用的finally子句。
19 | 12. 应该在下列情况下使用异常:
20 | - 在恰当的级别处理问题
21 | - 解决问题并且重新调用产生异常方法
22 | - 进行少许修补,然后绕过异常发生的地方继续执行
23 | - 用别的数据进行计算,以代替方法预计会返回的值
24 | - 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层
25 | - 把当前运行环境下能做的事情尽量做完,然后把不同的异常重抛到更高层
26 | - 终止程序
27 | - 进行简化
28 | - 让类库和程序更安全
29 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第13章 字符串.md:
--------------------------------------------------------------------------------
1 | # 第13章 字符串
2 | 1. String对象是不可变的。
3 | 2. String类中每一个看起来会修改String值的方法,实际上都是创建了一个权限的String对象,以包含修改后的字符串内容,最初的String对象则丝毫未动。
4 | 3. String对象具有只读特性。
5 | 4. 用于String的“+”与“+=”是Java中仅有的两个重载过的操作符,Java并不允许重载任何操作符。
6 | 5. find()可以在输入的任意位置定位正则表达式;lookingAt()和matches()只有在正则表达式与输入的最开始处就开始匹配时才会成功。matches()只有在整个输入都匹配正则表达式时才回成功,lookingAt()只要输入的第一部分匹配就会成功。
7 | 6. Scanner的构造器可以接受任何类型的输入对象。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第14章 类型信息.md:
--------------------------------------------------------------------------------
1 | # 第14章 类型信息
2 | 1. forName()是取得Class对象的引用的一种方法,是一个包含目标类的文本名的String作输入参数,返回的是一个Class对象的引用。
3 | 2. 当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。
4 | 3. Class引用总是指向某个Class对象,可以制造类的实例,并包含可作用于这限额实例的所有方法代码,还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。
5 | 4. RTTI形式:
6 | - 传统的类型转换
7 | - 代表对象的类型的Class对象
8 | - 关键字instanceof,返回一个布尔值,告诉我们对象是不是某个特定类型的实例
9 | 5. 远程方法调用(RMI):允许一个Java程序将对象分布到多台机器上。
10 | 6. RTTI和反射的真正区别:
11 | >对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射机制来说,.class文件
12 | 在编译时是不可获取的,所以是在运行时打开和检查.class文件。
13 | 7. interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。
14 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第15章 泛型.md:
--------------------------------------------------------------------------------
1 | # 第15章 泛型
2 | 1. 在面向对象编程语言中,多态是一种泛化机制。
3 | 2. Java泛型的核心概念:
4 | > 告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
5 | 3. 元组:是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。元组可以具有任性长度,元组中的对象可以说任意不同的类型。
6 | 4. 元组隐含地保持了元素的次序。
7 | 5. 泛型也可应用于接口。
8 | 6. Java泛型的一个局限性:基本类型无法作为参数类型。
9 | 7. 对于一个static的方法而言,无法访问泛型类的类型参数,如果static方法需要使用泛型能力,就必须使其成为泛型方法。
10 | 8. 要定义泛型方法,只需要将泛型参数列表置于返回值之前。
11 | 9. 类型参数推断:使用泛型方法时,通常不必指明参数类型,编译器会为我们找出具体的类型。
12 | 10.
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第16章 数组.md:
--------------------------------------------------------------------------------
1 | # 第16章 数组
2 | 1. 数组与其他种类的容器之间的区别有三方面:效率、类型和保存基本类型的能力。
3 | 2. 在Java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。数组是一简单的线性序列,优点是元素访问速度非常快,缺点是数组对象的大小固定,并且在其生命周期中不可改变。
4 | 3. 数组标识符只是一个引用,指向在堆中创建的一个真实对象,这个对象用以保存指向其他对象的引用。
5 | 4. length是数组的大小,不是实际保存的元素个数。
6 | 5. 粗糙数组:数组中构成矩阵的每个向量都可以具有任意的长度。
7 | 6. 不能实例化具有参数化类型的数组,可以参数化数组本身的类型。
8 | 7. Arrays类中常用的六个基本方法:
9 | - equals():用于比较两个数组是否相等
10 | - fill():填充整个数组
11 | - sort():用于对数组的排序
12 | - binarySearch():用于在**已经排序**的数组中查找元素
13 | - toString():产生数组的String表示
14 | - hashCode():产生数组的散列码
15 | 8. 复制数组:System.arraycopy(),它复制数组比用for循环要快。参数有
16 | - 源数组
17 | - 表示从源数组中的什么位置开始复制的偏移量
18 | - 表示从目标数组的什么位置开始复制的偏移量
19 | - 需要复制的元素个数
20 | 9. 如果需要对没有重复元素的数组排序,可以使用TreeSet(保持排序顺序),或者LinkedHashSet(保持插入顺序)。
21 | 10. 基本类型数组无法使用Comparatoru进行排序。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第19章 枚举类型.md:
--------------------------------------------------------------------------------
1 | # 第19章 枚举类型
2 | 1. ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始。
3 | 2. 可以使用==来比较enum实例,具有compareTo()方法。
4 | 3. 如果你打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号,同时,Java要求必须先定义enum实例。
5 | 4. 一旦enum的定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。
6 | 5. EnumSet中的元素必须来自一个enum,可以应用于最多不超过64个元素的enum但可以超过,必要的时候Enum会增加一个long。
7 | 6. EnumMap是一种特殊的Map,要求其中的键必须来自一个enum。
8 | 7. 要实现常量相关的方法,需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第20章 注解.md:
--------------------------------------------------------------------------------
1 | # 第20章 注解
2 | 1. 注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后某个时刻非常方便地使用这些数据
3 | 2. 通过使用注解,我们可以将这些元数据保存在Java源代码中,并利用annotation API为自己的注解构造处理工具,代码更干净易读,编译期类型检查。
4 | 3. 注解的语法:除了使用@符号的使用之外,基本与Java的固有语法一致。
5 | 4. 内置的注解:
6 | - @Override,表示当前的方法定义将覆盖超类中的方法。
7 | - @Deprecated,如果使用了注解为它的元素,那么编译器会发出警告信息
8 | - @SuppressWarnings,关闭不当的编译器警告信息。
9 | - @Target,表示该注解可以用于什么地方。
10 | - @Retention,表示需要在什么级别保护该注解信息。
11 | - @Documented,将此注解包含在Javadoc中。
12 | - @Inherited,允许子类继承父类中的注解。
13 | 5. 定义注解时,会需要一些元注解,在注解中,一般都会包含一些元素以表示某些值,没有元素的注解称为标记注解。
14 | 6. getAnnoation()方法返回指定类型的注解对象。
15 | 7. 注解元素可用的类型如下所示:
16 | - 所有基本类型(int,float,boolean等)
17 | - String
18 | - Class
19 | - enum
20 | - Annotation
21 | - 以上类型的数组
22 | 8. 注解也可以作为元素的类型,也就是说注解可以嵌套。
23 | 9. 编译器对元素的默认值过分挑剔,首先,元素不能有不确定的值。其次,对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值,一般我们是自己定义一些特殊的值,如空字符串或负数,以此表示某个元素不存在。
24 | 10. 快捷方式:
25 | > 如果程序员的注解中定义了名为value的元素,并且在应用该注解的时候,如果该元素是唯一需要赋值的一个元素,那么此时无需使用名-值对的这种语法,而只需在括号内给出value元素所需要的值即可。这可以应用于任何合法类型的元素。同时也限制了必须将此元素命名为value。
26 | 11. 注解不支持继承,不能使用关键字extends来继承某个@interface。
27 | 12. 单元测试是对类中的每个方法提供一个或多个测试的一种实践,其目的是为了有规律地测试一个类的各个部分是否具有正确的行为。
28 | 13. 所有测试的保留属性必须是RUNTIME,因为@Unit系列必须在编译后的代码中查询这些注解。
29 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第4章 控制执行流程.md:
--------------------------------------------------------------------------------
1 | # 第4章 控制执行流程
2 |
3 | 1. 所有条件语句都利用条件表达式的真或假来决定执行路径。
4 | 2. Java不允许将一个数字作为布尔值使用。
5 | 3. while和do-while唯一的区别就是都-while中的语句至少会执行一次。
6 | 4. Java里用到逗号操作符的地方就是for循环的控制表达式。
7 | 5. return关键词哟两方面的作用:一方面指定一个方法返回什么值,另一方面会导致当前的方法退出,并返回那个值。
8 | 6. 在任何迭代语句的主体部分,都可以用break和continue控制循环的流程,其中,break用于强行退出循环,不再执行循环中的剩余的语句;continue则停止执行当前的迭代,然后退回循环起始处,开始下一次迭代。
9 | 7. goto是Java中的一个保留字,但在语言中并未使用它,Java没有goto。
10 | 8. 在Java里需要使用标签的唯一理由就是因为有循环嵌套存在,而且想从多层嵌套中break或continue
11 | 9. 规则:
12 | - 一般的continue会退回最内层循环的开头(顶部),并继续执行。
13 | - 标签的continue会到达标签的位置,并重新进入紧接在那个标签后面的循环。
14 | - 一般的break会中断并跳出当前循环。
15 | - 带标签的break会中断并跳出标签所指的循环。
16 | 10. switch中最后的default语句没有break。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第5章 初始化与清理.md:
--------------------------------------------------------------------------------
1 | # 第5章 初始化与清理
2 |
3 | 1. Java中构造器采用与类相同的名称。
4 | 2. 不接受任何参数的构造器叫作默认构造器,即无参构造器。
5 | 3. 构造器是一种特殊类型的方法,没有返回值。
6 | 4. 如果类中没有构造器,则编译器会自动创建一个默认构造器,如果有则不创造。
7 | 5. this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。如果在方法内部调用同一个类的另一个方法,就不必使用this,直接调用即可。只有当需要明确指出对当前对象的引用时,才需要使用this关键字。
8 | 6. 除构造器外,编译器禁止在其他任何方法中调用构造器。
9 | 7. static方法就是没有this的方法,在static方法内部不能调用非静态方法,反过来可以。可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。
10 | 8.
11 | - 对象可能不被垃圾回收;
12 | - 垃圾回收不等于“析构”;
13 | - 垃圾回收只与内存有关。
14 | 9. Java不允许创建局部对象,必须使用new创建对象。
15 | 10. Java尽量保证:所有变量在使用前能得到恰当的初始化。对于方法的局部变量,Java以编译时错误的形式来贯彻这种保证。
16 | 11. 在类里定义一个对象引用时,如果不将其初始化,此引用就会获得一个特殊值null。
17 | 12. 无法阻止自动初始化的进行,它将在构造器被调用之前发生。
18 | 13. 在类的内部,变量的定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用自谦得到初始化。
19 | 14. 无论创建多少个对象,静态数据都只占有一份存储区域,static关键字不能应用于局部变量,只能作用于域。
20 | 15. 如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果是一个对象的引用,那么它的默认初始值就是null。
21 | 16.
22 | - 即使没有显示地使用static关键字,构造器实际上也是静态方法。
23 | - 静态初始化只在Class对象首次加载的时候进行一次。
24 | 17. 数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。
25 | 18. 编译器不允许知道数组的大小。
26 | 19. Java数组计数是从第0个元素开始,最大下标是length-1。
27 | 20. 将0个参数传递给参数列表是可行的。
28 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第6章 访问权限控制.md:
--------------------------------------------------------------------------------
1 | # 第6章 访问权限控制
2 | 1. 访问权限控制的等级,从最大权限到最小权限依次为:
3 | - public :接口访问权限
4 | - protected:继承访问权限
5 | - 包访问权限(没有关键词)
6 | - private:其他类无法访问
7 | 2. 包访问权限允许将包内所有相关的组合起来,以使它们彼此之间可以轻松地相互作业。
8 | 3. 取得对某成员的访问权的唯一途径是:
9 | - 使该成员成为public
10 | - 通过不加访问权限修饰词并将其他类放置于同一个包内的方式给成员赋予包访问权限
11 | - 继承
12 | - 提供访问器和变异器方法,以读取和改变数值
13 | 4. protected也提供包访问权限,相同包内的其他类可以访问protected元素。
14 | 5. 限制:
15 | - 每个编译单元(文件)都只能有一个public类。
16 | - public类的名称必须完全与含有该编译单元的文件名相匹配,包括大小写。
17 | - 编译单元内完全不带public类也是可能的。
18 |
19 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第7章 复用类.md:
--------------------------------------------------------------------------------
1 | # 第7章 复用类
2 | 1. 可以进行初始化引用的位置:
3 | - 在定义对象的地方
4 | - 在类的构造器中
5 | - 在正要使用的对象之前(惰性初始化)
6 | - 使用实例初始化
7 | 2. 继承是所有OOP语言和Java语言不可缺少的组成部分。
8 | 3. 为了继承,一般的规则是将所有的数据成员都指定为private,将所有的方法指定为public。
9 | 4. 为了解决会产生递归的问题,Java用super关键字表示超类的意思,当前类就是充超类继承来的。
10 | 5. 调用一个带参数的基类构造器,必须用关键字super显式地编写调用基类构造器的语句,并且配以适当的参数列表。
11 | 6. Java中的代理(Java语言不直接支持代理)可以解决当我们将一个成员对象置于要构造的类中(组合)的时候在新类中暴露了该成员对象的所有方法(继承)。
12 | 7. 如果Java的基类拥有某个已被多次重载的方法名称,那么在到处类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。
13 | 8. 组合和继承都允许在新的类中放置子对象,组合是显示地这样做,而继承则是隐式地做。
14 | 组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形。
15 | 在继承的时候,使用某个现有类,并开发一个他的特殊版本。
16 | 9. protected的作用:就类而言,这是private的,但对于任何继承于此类的导出类或其他任何位于同一个包内的类来说,确是可以访问的。
17 | 10. 向上转型是从一个较专用类型向较通用类型转换,所以总是很安全的。
18 | 11. 导出类是一个基类的超集。
19 | 12. 到底用组合还是继承,一个最清晰的判断方法就是问自己是否需要从新类向基类进行向上转型,如果必须向上转型,则继承是必须的,但如果不需要,则应当好好考虑自己是否需要向上继承。
20 | 13. 一个既是static又是final的域只占据一段不能改变的存储空间。
21 | 14. 对于基本类型,final使数值恒定不变,对于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法在把它改为指向另一个对象,对象其自身却是可以被修改的。
22 | 15. 带有恒定初始值的final static基本类型全用大写字母命名,并且字与字之间用下划线隔开。
23 | 16. Java允许生成“空白final”,是指被声明为final但又未给定初值的域。
24 | 17. Java允许在参数列表中以声明的方式将参数指明为final,意味着你无法在方法中更改参数引用所指向的对象。
25 | 18. 类中所有的private方法都隐式地指定为final的。
26 | 19. 构造器也是static方法。
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第8章 多态.md:
--------------------------------------------------------------------------------
1 | # 第8章 多态
2 | 1. 多态的作用是消除类型之间的耦合关系。
3 | 2. 将一个方法调用同一个方法主体关联起来被称作绑定。
4 | 3. Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。
5 | 4. 只有非private方法才可以被覆盖。
6 | 5. 只有普通的方法调用可以是多态的。
7 | 6. 动态绑定在以下两种情况会失效:
8 | - 基类方法是private或final修饰的
9 | - 因为private说明该方法对子类是不可见的,子类再写一个同名的方法并不是对父类方法进行复写(Override),而是重新生成一个新的方法,也就不存在多态的问题了。同理也可以解释final,因为方法同样是不可覆盖的。
10 | - 方法是static修饰的
11 | - 静态方法是与类而不是与某个对象相关联,所以尽量不要使用实例变量去调用静态方法,避免混淆。
12 | 7. 构造器具有一项特殊的任务:检查对象是否被正确地构造。
13 | 8. 复杂对象调用构造器要遵造下面的顺序:
14 | - 调用基类构造器
15 | - 按声明顺序调用成员的初始化方法
16 | - 调用导出类构造器的主体
17 | 9. 初始化的实际过程
18 | - 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
19 | - 调用基类构造器
20 | - 按声明顺序调用成员的初始化方法
21 | - 调用导出类构造器的主体
22 | 10. 协变返回类型,表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
23 |
--------------------------------------------------------------------------------
/Java/《Java编程思想》笔记/第9章 接口.md:
--------------------------------------------------------------------------------
1 | # 第9章 接口
2 | 1. 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
3 | 2. 抽象方法,仅有声明没有方法体。
4 | 3. 包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。
5 | 4. interface不仅仅是一个极度抽象的类,它允许人们通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。
6 | 5. 当要实现一个接口时,在接口中被定义的方法必须被定义为是public的,否则将只能得到默认的包访问权限。
7 | 6. 使用接口的愿意:
8 | - 为了能够向上转型为多个基类型
9 | - 防止客户端程序员创建该类的对象
10 | 7. 在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,请尽量避免这种情况。
11 | 8. 在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。域的值被存储在该接口的静态存储区域内。
12 | 9. 接口可以嵌套在类或者其他接口中。
13 | 10. 实现private接口只是一种方式,可以强制该接口中的方法定义不要添加任何类型信息,也就是不允许向上转型。
14 | 11. 当实现某个接口时,并不需要实现嵌套在其内部的任何接口,而且,private接口不能在定义它的类之外被实现。
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 关于
2 | 我的博客:[wuzhaohui026.github.io](https://wuzhaohui026.github.io/)
3 |
4 | # 目录
5 |
6 | ## Android
7 |
8 |
9 | ### Android 动画学习笔记
10 |
11 | 1. [Android 动画之Tween Animation](https://github.com/wuzhaohui026/MyNote/blob/master/Android/Android%20%E5%8A%A8%E7%94%BB%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Android%E5%8A%A8%E7%94%BB%E4%B9%8BTween%20Animation.md)
12 | 2. [Android 动画之Frame Animation](https://github.com/wuzhaohui026/MyNote/blob/master/Android/Android%20%E5%8A%A8%E7%94%BB%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Android%E5%8A%A8%E7%94%BB%E4%B9%8BFrame%20Animation.md)
13 | 3. [Android 动画之 Property Animation](https://github.com/wuzhaohui026/MyNote/blob/master/Android/Android%20%E5%8A%A8%E7%94%BB%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Android%E5%8A%A8%E7%94%BB%E4%B9%8B%20Property%20Animation.md)
14 |
15 | ### RxJava 学习笔记
16 |
17 | 1. [与RxJava 1.x的差异](https://github.com/wuzhaohui026/MyNote/blob/master/Android/RxJava%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E4%B8%8ERxJava%201.x%E7%9A%84%E5%B7%AE%E5%BC%82.md)
18 | 2. [RxJava 学习笔记2](https://github.com/wuzhaohui026/MyNote/blob/master/Android/RxJava%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/RxJava%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B02.md)
19 |
20 | ### 笔记
21 |
22 | 1. [Android面试题整理](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E7%AC%94%E8%AE%B0/Android%E9%9D%A2%E8%AF%95%E9%A2%98%E6%95%B4%E7%90%86.md)
23 | 2. [Service小记](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E7%AC%94%E8%AE%B0/Service%E5%B0%8F%E8%AE%B0.md)
24 | 3. [Androdi中的Theme](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E7%AC%94%E8%AE%B0/Android%E4%B8%AD%E7%9A%84Theme.md)
25 | 4. [Android 中常见的内存泄漏](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E7%AC%94%E8%AE%B0/Android%20%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F.md)
26 |
27 | ### 《Android 进阶之光》读书笔记
28 |
29 | 1. [函数响应式编程](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%20%E8%BF%9B%E9%98%B6%E4%B9%8B%E5%85%89%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%87%BD%E6%95%B0%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B.md)
30 |
31 | ### 《Android 艺术开发探索》读书笔记
32 |
33 | 1. [Activity的生命周期和启动模式](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Activity%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%92%8C%E5%90%AF%E5%8A%A8%E6%A8%A1%E5%BC%8F.md)
34 | 2. [IPC机制](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/IPC%E6%9C%BA%E5%88%B6.md)
35 | 3. [View 的事件体系](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/View%E7%9A%84%E4%BA%8B%E4%BB%B6%E4%BD%93%E7%B3%BB.md)
36 | 4. [View 的工作原理](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/View%20%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.md)
37 | 5. [理解 RemoteViews](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%90%86%E8%A7%A3%20RemoteViews.md)
38 | 6. [Android 的 Drawable](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Android%20%E7%9A%84%20Drawable.md)
39 | 7. [Android 动画深入分析](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Android%20%E5%8A%A8%E7%94%BB%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90.md)
40 | 8. [理解 Window 和 WindowManager](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%90%86%E8%A7%A3%20Window%20%E5%92%8C%20WindowManager.md)
41 | 9. [四大组件的工作过程](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E7%9A%84%E5%B7%A5%E4%BD%9C%E8%BF%87%E7%A8%8B.md)
42 | 10. [Android 的消息机制](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Android%20%E7%9A%84%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.md)
43 | 11. [Android 的线程和线程池](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Android%20%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%B1%A0.md)
44 | 12. [Bitmap 的加载和 Cache](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Bitmap%20%E7%9A%84%E5%8A%A0%E8%BD%BD%E5%92%8C%20Cache.md)
45 | 13. [综合技术](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%BB%BC%E5%90%88%E6%8A%80%E6%9C%AF.md)
46 | 14. [JNI 和 NDK 编程](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/JNI%20%E5%92%8C%20NDK%20%E7%BC%96%E7%A8%8B.md)
47 | 15. [Android 性能优化](https://github.com/wuzhaohui026/MyNote/blob/master/Android/%E3%80%8AAndroid%E5%BC%80%E5%8F%91%E8%89%BA%E6%9C%AF%E6%8E%A2%E7%B4%A2%E3%80%8B%E7%AC%94%E8%AE%B0/Android%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96.md)
48 |
49 | ## Git
50 | 1. [Git](https://github.com/wuzhaohui026/MyNote/blob/master/Git/Git.md)
51 |
52 | ## Java
53 | 1. [Java基础知识](https://github.com/wuzhaohui026/MyNote/blob/master/Java/Java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.md)
54 | ### 《Java编程思想》笔记
55 | 1. [第4章 控制执行流程](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC4%E7%AB%A0%20%E6%8E%A7%E5%88%B6%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.md)
56 | 2. [第5章 初始化与清理](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC5%E7%AB%A0%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E6%B8%85%E7%90%86.md)
57 | 3. [第6章 访问权限控制](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC6%E7%AB%A0%20%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6.md)
58 | 4. [第7章 复用类](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC7%E7%AB%A0%20%E5%A4%8D%E7%94%A8%E7%B1%BB.md)
59 | 5. [第8章 多态](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC8%E7%AB%A0%20%E5%A4%9A%E6%80%81.md)
60 | 6. [第9章 接口](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC9%E7%AB%A0%20%E6%8E%A5%E5%8F%A3.md)
61 | 7. [第10章 内部类](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC10%E7%AB%A0%20%E5%86%85%E9%83%A8%E7%B1%BB.md)
62 | 8. [第11章 持有对象](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC11%E7%AB%A0%20%E6%8C%81%E6%9C%89%E5%AF%B9%E8%B1%A1.md)
63 | 9. [第12章 通过异常处理错误](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC12%E7%AB%A0%20%E9%80%9A%E8%BF%87%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF.md)
64 | 10. [第13章 字符串](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC13%E7%AB%A0%20%E5%AD%97%E7%AC%A6%E4%B8%B2.md)
65 | 11. [第14章 类型信息](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC14%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF.md)
66 | 12. [第15章 泛型](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC15%E7%AB%A0%20%E6%B3%9B%E5%9E%8B.md)
67 | 13. [第16章 数组](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC16%E7%AB%A0%20%E6%95%B0%E7%BB%84.md)
68 | 14. [第19章 枚举类型](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC19%E7%AB%A0%20%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B.md)
69 | 15. [第20章 注解](https://github.com/wuzhaohui026/MyNote/blob/master/Java/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B0/%E7%AC%AC20%E7%AB%A0%20%E6%B3%A8%E8%A7%A3.md)
70 |
71 | ## 微信小程序笔记
72 |
73 | 1. [JSON 配置](https://github.com/wuzhaohui026/MyNote/blob/master/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%AC%94%E8%AE%B0/JSON%20%E9%85%8D%E7%BD%AE.md)
--------------------------------------------------------------------------------
/kotlin/kotlin 笔记.md:
--------------------------------------------------------------------------------
1 | # Kotlin 笔记
2 |
--------------------------------------------------------------------------------
/微信小程序笔记/JSON 配置.md:
--------------------------------------------------------------------------------
1 | # JSON 配置
2 | 嗯,就是 **.json** 后缀的 JSON 配置文件。
3 | 在项目的根目录有一个 app.json 和 project.config.json,此外在 pages/logs 目录下还有一个 logs.json。
4 |
5 | ## 小程序配置 app.json
6 | app.json 是对当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。
7 | ````json
8 | {
9 | "pages": [],
10 | "window": {},
11 | "tabBar": {
12 | "list": [{}, {}]
13 | },
14 | "networkTimeout": {},
15 | "debug": true
16 | }
17 | ````
18 | 含义:
19 |
20 | - pages字段 —— 用于描述当前小程序所有页面路径,String Array 类型,必填项。
21 | + 接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对 pages 数组进行修改。
22 | - window字段 —— 小程序所有页面的顶部背景颜色,文字颜色,Object 类型,选填项。
23 | + 用于设置小程序的状态栏、导航条、标题、窗口背景色。
24 |
25 | | 属性 | 类型 | 默认值 | 描述 |
26 | | -------- | -----: | :----: | :----: |
27 | |navigationBarBackgroundColor|HexColor|#000000|导航栏背景颜色|
28 | |navigationBarTextStyle|String|white|导航栏标题颜色|
29 | |navigationBarTitleText|String| |导航栏标题文字内容|
30 | |navigationStyle|String|default|导航栏样式,仅支持 default/custom。custom 模式可自定义导航栏,只保留右上角胶囊状的按钮|
31 | |backgroundColor|HexColor|#ffffff|窗口的背景色|
32 | |backgroundTextStyle|String|dark|下拉背景字体、loading 图的样式,仅支持 dark/light|
33 | |enablePullDownRefresh|Boolean|false|是否开启下拉刷新|
34 | |onReachBottomDistance|Number|50|页面上拉触底事件触发时距页面底部距离,单位为px|
35 |
36 | >注:navigationStyle 只在 app.json 中生效。开启 custom 后,低版本客户端需要做好兼容。开发者工具基础库版本切到 1.7.0(不代表最低版本,只供调试用) 可方便切到旧视觉。
37 |
38 | - tabBar ——— 设置底部 tab 的表现,Object 类型,选填项。
39 | + 当设置 position 为 top 时,将不会显示 icon。
40 | + tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
41 | + 属性说明:
42 |
43 | |属性|类型|必填|默认值|描述|
44 | | -------- | -----: | :----: |:----: |:----: |
45 | | color|HexColor | 是 | | tab 上的文字默认颜色 |
46 | | selectedColor | HexColor | 是 | | tab 上的文字选中时的颜色 |
47 | | backgroundColor | HexColor | 是 | | tab 的背景色 |
48 | | borderStyle|String |否|black| tabbar上边框的颜色, 仅支持 black/white |
49 | | list | Array | 是 | | tab 的列表,详见 list 属性说明,最少2个、最多5个 tab |
50 | | position | String | 否 | bottom | 可选值 bottom、top |
51 |
52 | 其中 list 接受一个数组,数组中的每个项都是一个对象,其属性值如下:
53 |
54 | |属性|类型|必填|说明|
55 | | -------- | -----: | :----: |:----: |
56 | | pagePath | String | 是 |页面路径,必须在 pages 中先定义|
57 | | text | String | 是 | tab 上按钮文字 |
58 | | iconPath | String | 否 | 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片 |
59 | | selectedIconPath | String | 否 | 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效 |
60 |
61 | - networkTimeout ———— 设置网络超时时间,Object 类型,选填项。
62 | + 属性说明:
63 |
64 | |属性|类型|必填|说明|
65 | | -------- | -----: | :----: |:----: |
66 | | request | Number | 否 | wx.request的超时时间,单位毫秒,默认为:60000 |
67 | | connectSocket | Number | 否 | wx.connectSocket的超时时间,单位毫秒,默认为:60000 |
68 | | uploadFile | Number | 否 | wx.uploadFile的超时时间,单位毫秒,默认为:60000 |
69 | | downloadFile | Number | 否 | wx.downloadFile的超时时间,单位毫秒,默认为:60000 |
70 |
71 | - debug ———— 设置是否开启 debug 模式,Boolean 类型,选填项。
72 | + 可以在开发者工具中开启 debug 模式,在开发者工具的控制台面板,调试信息以 info 的形式给出,其信息有Page的注册,页面路由,数据更新,事件触发 。
73 |
74 |
75 | ## 工具配置 project.config.json
76 | 这个没事好说的,就是省去了你如果换了开发环境得每次重新配置的麻烦。你只要载入同一个项目的代码包,开发者工具就自动会帮你恢复到当时你开发项目时的个性化配置,其中会包括编辑器的颜色、代码上传时自动压缩等等一系列选项。
77 |
78 | 在项目根目录使用 project.config.json 文件对项目进行配置。
79 |
80 | |字段名|类型|说明|
81 | | -------- | -----: | :----: |
82 | | miniprogramRoot | Path String | 指定小程序源码的目录(需为相对路径) |
83 | | qcloudRoot | Path String | 指定腾讯云项目的目录(需为相对路径) |
84 | | setting | Object | 项目设置 |
85 | | libVersion | String | 基础库版本 |
86 | | appid | String | 项目的 appid,只在新建项目时读取 |
87 | | projectname | String | 项目名字,只在新建项目时读取 |
88 |
89 | setting 中可以指定以下设置:
90 |
91 | |字段名|类型|说明|
92 | | -------- | -----: | :----: |
93 | | es6 | Boolean | 是否启用 es5 转 es6 |
94 | | postcss | Boolean | 上传代码时样式是否自动补全 |
95 | | minified | Boolean | 上传代码时是否自动压缩 |
96 | | urlCheck | Boolean | 是否检查安全域名和 TLS 版本 |
97 |
98 |
99 | ## 页面配置 page.json
100 | 用来表示 pages/logs 目录下的 logs.json 这类和小程序页面相关的配置,让开发者可以独立定义每个页面的一些属性。
101 |
102 | 每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。
103 | 页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键。如下所示:
104 | ````json
105 | {
106 | "navigationBarBackgroundColor": "#ffffff",
107 | "navigationBarTextStyle": "black",
108 | "navigationBarTitleText": "微信接口功能演示",
109 | "backgroundColor": "#eeeeee",
110 | "backgroundTextStyle": "light"
111 | }
112 | ````
113 |
--------------------------------------------------------------------------------