古古天


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

Annotation-元注解

发表于 2017-07-27 | 分类于 Java | | 阅读次数

元注解

Java里面定义了4个标准的元注解:

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

这四个元注解的作用是负责注解其他注解,其他所有的注解都是通过元注解扩散开来。

Target

这个元注解的作用是负责指明注解的作用域。如果指明类型为METHOD,说明这个注解只能用在方法上面。
以下是可以指明Target的所有作用类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public enum ElementType {
ANNOTATION_TYPE,
CONSTRUCTOR,
FIELD,
LOCAL_VARIABLE,
METHOD,
PACKAGE,
PARAMETER,
TYPE,
TYPE_PARAMETER,
TYPE_USE;
private ElementType() {
}
}

Retention

这个元注解的作用是对Annotation的作用时间进行限制:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取。
以下是可以指明Retention的所有作用类型:

1
2
3
4
5
6
7
8
public enum RetentionPolicy {
CLASS,
RUNTIME,
SOURCE;
private RetentionPolicy() {
}
}

  • RetentionPolicy.SOURCE:在源文件中有效
  • RetentionPolicy.CLASS:在class文件中有效
  • RetentionPolicy.RUNTIME:在运行时有效

Documented

这个元注解的作用是标识是否能够在生成JavaDoc文档是,注解依然保存。这个只是一个标记注解,没有任何成员。
如下:
1.标记有Documented的Annotation与生成的JavaDoc文档

1
2
3
4
5
6
7
8
9
@Target(ElementType.CONSTRUCTOR)
@Inherited
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {
String name();
String age();
String gender() default "Male";
}

annotation-person-documented
2.没有标记的Annotation与生成的JavaDoc文档

1
2
3
4
5
6
7
8
@Target(ElementType.CONSTRUCTOR)
@Inherited
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {
String name();
String age();
String gender() default "Male";
}

annotation-person-not-documented

使用注解的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Person {
private String name;
@SourceAnnotation(
name="Person",
age="1"
)
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Inherited

这个元注解的作用是来阐述某个被标注的类型是可以被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。


Run for a better life

发表于 2016-09-11 | | 阅读次数

本文最初分享在卡牛玩卡互助学堂微信群,后期整理而成。


我大概从高中起就有跑步的习惯,不过之前都是短距离慢跑,三五公里的样子,现在倾向于长跑(>10km),时间足够的情况下长跑是很舒服的。

关于呼吸

一开始我也是用嘴呼吸的,后来别人看我跑起来总是气喘吁吁的,于是教我用鼻子呼吸,适应了几次后,我觉得这种方式非常好,呼吸起来有节奏感,跑起来也不感觉有那么累。上次焦总带来的教练也说了,用嘴或者用鼻子呼气都行,关键要有呼吸节奏。在没有加速的前提下,如果越跑呼吸越大,说明节奏是有问题的。

关于跑步姿势

头部不要前倾,目视前方,双肩放松,前后摆臂,前脚掌先落地,不要把力量集中在后脚跟,落地轻盈,膝盖处减少用力。跑步的姿势很重要,如果不对的话长时间跑步会对膝盖造成损伤。
跑步姿势

关于跑步速度

速度是跑步成绩的直接体现,刚开始时很多人关注速度胜过关注距离。我的经验是可以更多的关注距离、时间以及身体的感受。特别是对于初跑者,倾听身体的声音,更有利于提高成绩,跑步速度按照不会气喘,能正常交谈为宜。

关于长距离跑

其实跑步就是一项考验耐力的运动,就是身体承受力。如果节奏对了,身体又承受得起,一直跑下去都没有问题。不要在觉得很累的时候停下来,有时候跨过了这个很累的点,你又能多跑几公里,可以通过这样子锻炼自己的耐力。之前我很累的时候就是脚都几乎抬不起来了,以这样的方式坚持几分钟,会发现跑起来又轻松许多。LSD是长距离慢跑,这种方式也可以提高耐力,一般是20~35km。更多的跑者常见术语,可以看这里

关于装备

对于装备控而言,装备不是表明要取得多好的成绩,而是表示坚持跑步的决心,只要决心有了,不用太烧装备,如果还没有决心,就通过烧装备建立吧。跑鞋是最基本的装备,既然不能通过成绩表明我们是个高手,那至少要用跑鞋证明我们是跑步爱好者。经济又实惠的跑鞋可以到迪卡侬购买,我现在穿的就是KALENJI系列的跑鞋,更专业的可以考虑asics跑鞋,价格略贵。

配速表和心率监控对于常跑者也是基本装备。心率可以通过心率带来监控,通过心率既能了解自己的身体状况,保证自己的安全,也能根据当前心率调整自己的跑步节奏。配速表的品牌就比较多了,Garmin是专业运动手表,SUnnto是专业户外品牌。我自己配备的是Garmin FENIX3HR,配备了光电心率功能,主要用于跑步、骑车和游泳。

跑步也要量力而行,对于没有经常跑步的同学,一下子就跑得很远这样很容易受伤,之前跟我一起跑步的跑友就是因为这样子,跑了几次后脚疼,去医院看骨伤科,休养了几个月,现在才可以勉强跑起来。

webview问题

发表于 2015-11-09 | | 阅读次数

1.Uncaught ReferenceError: NPObject deleted
如果js注入遇到此类问题,无法执行,可以考虑一下是不是用了iframe框架导致的。

2.uri的getQueryParameter()引起的问题
用这个方法找寻url里面的参数时,最好检查一下该uri是否是isHierarchical(),不然会throw new UnsupportedOperationException(NOT_HIERARCHICAL)

Chrome自定义UserAgent

发表于 2015-11-06 | 分类于 Chrome | | 阅读次数

怎么样用Chrome实现自定义UserAgent呢

目前我的Chrome版本为44.0.2403.157 (64-bit),可以采用下面的方式实现:

1.点击右键-审查元素(Ctrl+Shift+I),调出调试界面,进入右上方的设置界面;

Custom-User-Agent

2.设置页面有一个Devices选项,选择Add Custom device

Custom-User-Agent

3.填入对应的信息保存就可以了.

Custom-User-Agent

4.需要用该设备时,在调试页面的左上方选择你建立的Device就可以了

Custom-User-Agent

自定义View的一般步骤

发表于 2015-08-31 | 分类于 Android | | 阅读次数

自定义View应该是Android开发比较深入的内容,但又是在开发过程中必不可少的技能,总体来说,分为一下几步:

  • 在attr.xml文件里声明自定义View需要用到的参数,这些参数也是可以在布局文件中直接使用;
  • 布局文件中使用该View
  • 构造方法中获取自定义属性
  • 重写onMeasure()方法
  • 重写layout()方法
  • 重写onDraw()方法
  • 如有需要继续重写View继承过来的其他方法,前面的步骤也不是全部必须的,应根据项目需求灵活使用

    下面以自定义QQ5.0主页菜单SlidingMenu为例,介绍自定义View,项目Demo地址:

1.如果没有attr.xml文件,可以建立

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="rightPadding" format="dimension"></attr>
<declare-styleable name="SlidingMenu">
<attr name="rightPadding"/>
</declare-styleable>
</resources>

rightPadding的含义为菜单展示时,菜单右侧距内容页面的距离

2.布局文件中使用该View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sliding="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/qq_com_bg_pic">
<com.gugutian.qqwapper.widget.SlidingMenu
android:id="@+id/main_sm"
android:layout_width="match_parent"
sliding:rightPadding="50dp"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<include layout="@layout/menu_main"/>
<LinearLayout
android:id="@+id/main_content_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/main_content_fragment"/>
</LinearLayout>
</LinearLayout>
</com.gugutian.qqwapper.widget.SlidingMenu>
</LinearLayout>

SlidingMenu为自定义View,包含了菜单页跟内容页,具体可以参考QQ5.0页面。sliding:rightPadding="50dp"这里是使用自定义属性,前面记得还要声明该命名空间xmlns:sliding="http://schemas.android.com/apk/res-auto"

3.构造方法中获取自定义属性
具体获取自定义View的属性在携带三个参数的构造方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mLeftMenuPaddingRight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
mLeftMenuPaddingRight,
context.getResources().getDisplayMetrics());
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0);
for(int i = 0, n = typedArray.getIndexCount(); i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.SlidingMenu_rightPadding:
mLeftMenuPaddingRight = typedArray.getDimensionPixelSize(attr, (int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
mLeftMenuPaddingRight,
context.getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
}

4.重写onMeasure()方法
该方法测绘及设定该View所有的ChildView的大小

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(!mOnce) {
mWapper = (LinearLayout) getChildAt(0);
mLeftMenuVg = (ViewGroup) mWapper.getChildAt(0);
mMainContentVg = (ViewGroup) mWapper.getChildAt(1);
mLeftMenuWidth = mLeftMenuVg.getLayoutParams().width = mScreenWidth - mLeftMenuPaddingRight;
mMainContentVg.getLayoutParams().width = mScreenWidth;
mOnce = !mOnce;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

5.重写layout()方法
设定该View所有ChildView的位置并在界面中展现

1
2
3
4
5
6
7
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(changed) {
scrollTo(mLeftMenuWidth, 0);
}
}

6.其他

下面是完整的SlidingMenu的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
public class SlidingMenu extends HorizontalScrollView{
private LinearLayout mWapper;
private ViewGroup mLeftMenuVg;
private ViewGroup mMainContentVg;
private boolean mOnce = false;
private int mScreenWidth;
// dp
private int mLeftMenuPaddingRight = 50;
private int mLeftMenuWidth;
private boolean mIsLeftMenuShown = false;
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mLeftMenuPaddingRight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
mLeftMenuPaddingRight,
context.getResources().getDisplayMetrics());
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0);
for(int i = 0, n = typedArray.getIndexCount(); i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.SlidingMenu_rightPadding:
mLeftMenuPaddingRight = typedArray.getDimensionPixelSize(attr, (int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
mLeftMenuPaddingRight,
context.getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(!mOnce) {
mWapper = (LinearLayout) getChildAt(0);
mLeftMenuVg = (ViewGroup) mWapper.getChildAt(0);
mMainContentVg = (ViewGroup) mWapper.getChildAt(1);
mLeftMenuWidth = mLeftMenuVg.getLayoutParams().width = mScreenWidth - mLeftMenuPaddingRight;
mMainContentVg.getLayoutParams().width = mScreenWidth;
mOnce = !mOnce;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(changed) {
scrollTo(mLeftMenuWidth, 0);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if(scrollX >= mLeftMenuWidth / 2) {
smoothScrollTo(mLeftMenuWidth, 0);
mIsLeftMenuShown = false;
} else {
smoothScrollTo(0, 0);
mIsLeftMenuShown = true;
}
return true;
}
return super.onTouchEvent(ev);
}
public void showLeftMenu() {
if(!isLeftMenuShown()) {
smoothScrollTo(0, 0);
mIsLeftMenuShown = true;
}
}
public void hideLeftMenu() {
if(isLeftMenuShown()) {
smoothScrollTo(mLeftMenuWidth, 0);
mIsLeftMenuShown = false;
}
}
public boolean isLeftMenuShown() {
return mIsLeftMenuShown;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = l * 1.0f / mLeftMenuWidth;//1.0~0 注意1.0f的乘法,使之成为float类型
float contentScale = 0.8f + 0.2f * scale;
ViewHelper.setPivotX(mMainContentVg, 0);
ViewHelper.setPivotY(mMainContentVg, mMainContentVg.getHeight() / 2);
ViewHelper.setScaleX(mMainContentVg, contentScale);
ViewHelper.setScaleY(mMainContentVg, contentScale);
float menuTranslationShow = 0.7f;
ViewHelper.setTranslationX(mLeftMenuVg, l * menuTranslationShow);
float menuScale = 1.0f - 0.2f * scale;
ViewHelper.setScaleX(mLeftMenuVg, menuScale);
ViewHelper.setScaleY(mLeftMenuVg, menuScale);
float menuAlphaScale = 1.0f - 0.5f * scale;
ViewHelper.setAlpha(mLeftMenuVg, menuAlphaScale);
}
}

基本上只要按照以上几步实现,理清其中的逻辑关系,这种模板式的自定义View还是比较简单的。

代码地址:https://github.com/xing634325131/QQWapper

ubuntu动态截图,制作GIF动画

发表于 2015-07-31 | 分类于 Ubuntu | | 阅读次数

1. 安装 gtk-recordmydesktop 来录制屏幕, 安装 mplayer 將视频分解成单帧图片, 安装 imagemagick 將单帧图片压缩成一张 gif:

1
sudo apt-get install imagemagick mplayer gtk-recordmydesktop

2. 命令行下执行, 录制并保存文件为 out.ogv:

1
gtk-recordmydesktop

3. 执行如下命令將 out.ogv 分解成单帧图片:

1
mplayer -ao null out.ogv -vo jpeg:outdir=.

4. 执行如下命令將单帧图片压缩成 gif 图片:

1
convert *.jpg out.gif

5. 执行如下命令將 gif 图片进行压缩:

1
convert out.gif -fuzz 10% -layers Optimize optimized.gif

6.最后一步,删除切分出来的jpg图片

1
rm *.jpg

解决StateButton适配的问题

发表于 2015-07-31 | 分类于 Android | | 阅读次数

定制一个以图片为背景的按钮,并且在按下时设置其透明度为原来的35%,之前我们是这么实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.kingtime.xingpeng.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.Button;
/**
* Created by xingpeng on 15-7-31.
*/
public class StateButton extends Button{
private boolean mHasSetBackground = false;
private Drawable mBackground = null;
public StateButton(Context context) {
super(context);
}
public StateButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StateButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if(!mHasSetBackground) {
resetBackground();
mHasSetBackground = true;
}
super.onLayout(changed, left, top, right, bottom);
}
//设置按住时透明度背景为35%
private void resetBackground() {
Drawable normalDrawable = this.getBackground();
int width = this.getWidth();
int height = this.getHeight();
Bitmap pressBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(pressBitmap);
BitmapDrawable pressDrawable = new BitmapDrawable(null, pressBitmap);
normalDrawable.setBounds(0, 0, width , height);
normalDrawable.setAlpha(81);
normalDrawable.draw(canvas);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_focused}, pressDrawable);
stateListDrawable.addState(new int[]{}, normalDrawable);
mBackground = stateListDrawable;
mBackground.setCallback(this);
this.setBackgroundDrawable(mBackground);
}
}

可以直接在布局文件里面用,并且设定一张背景图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical">
<com.kingtime.xingpeng.widget.StateButton
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@drawable/icon_camera"/>
</LinearLayout>

界面预览:

StateButton

很长的一段时间里,这个是没有出现问题的,但是最近在5.0的系统上发现并不能用,按钮一直是灰色的,如下:

StateButton

查找了很久这个问题,发现是这一句出现了问题normalDrawable.setAlpha(81),本来在这里已经改变了正常图片的透明度,而后面依然将其设为正常的图片背景stateListDrawable.addState(new int[]{}, normalDrawable);,在5.0以上跟以下的区别,可能是对象的引用出现了问题,我们有两种方法解决这个问题.

第一种是在5.0以上的系统单独设置按下时背景的透明度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//设置按住时透明度背景为35%
private void resetBackground() {
Drawable normalDrawable = this.getBackground();
int width = this.getWidth();
int height = this.getHeight();
Bitmap pressBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(pressBitmap);
BitmapDrawable pressDrawable = new BitmapDrawable(null, pressBitmap);
normalDrawable.setBounds(0, 0, width, height);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
pressDrawable.setAlpha(81);
} else {
normalDrawable.setAlpha(81);
}
normalDrawable.draw(canvas);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_focused}, pressDrawable);
stateListDrawable.addState(new int[]{}, normalDrawable);
mBackground = stateListDrawable;
mBackground.setCallback(this);
this.setBackgroundDrawable(mBackground);
}

这种可以解决问题,但是在5.0以下的系统也用这种方法会出现按钮按下时变为空白的情况.

第二种是单独生成一个Drawable,避免用其引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.kingtime.xingpeng.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.Button;
import com.kingtime.xingpeng.applicationdemo.R;
/**
* Created by gugutian on 15-7-31.
*/
public class StateButton extends Button{
private boolean mHasSetBackground = false;
private Drawable mBackground = null;
public StateButton(Context context) {
super(context);
}
public StateButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StateButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if(!mHasSetBackground) {
resetBackground();
mHasSetBackground = true;
}
super.onLayout(changed, left, top, right, bottom);
}
//设置按住时透明度背景为35%
private void resetBackground() {
Drawable normalDrawable = generateNewDrawable(this.getBackground());
int width = this.getWidth();
int height = this.getHeight();
Bitmap pressBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(pressBitmap);
BitmapDrawable pressDrawable = new BitmapDrawable(null, pressBitmap);
normalDrawable.setBounds(0, 0, width , height);
normalDrawable.setAlpha(81);
normalDrawable.draw(canvas);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_focused}, pressDrawable);
stateListDrawable.addState(new int[]{}, this.getBackground());
mBackground = stateListDrawable;
mBackground.setCallback(this);
this.setBackgroundDrawable(mBackground);
}
private Drawable generateNewDrawable(Drawable oldDrawable) {
int width = this.getWidth();
int height = this.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(bitmap);
BitmapDrawable drawable = new BitmapDrawable(null, bitmap);
oldDrawable.setBounds(0, 0, width, height);
oldDrawable.draw(canvas);
return drawable;
}
}

这样又能愉快的工作了.

StateButton

解决4.4以上系统无法根据Uri正确获取图片路径的问题

发表于 2015-07-31 | 分类于 Android | | 阅读次数

当需要获取一张手机里的图片时,我们会调起图片浏览器选择图片,图片浏览器会返回图片的Uri,然后根据Uri就可以得到图片的路径,这段代码在4.4一下的系统是没有问题的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 得到当前选中的图片的地址
*/
private String getFilePathFromContentUri(Uri selectedPicUri) {
String filePath = "";
try {
if ("file".equalsIgnoreCase(selectedPicUri.getScheme())) {
filePath = selectedPicUri.getPath();
} else {
String[] filePathColumn = {MediaColumns.DATA};
Cursor c = null;
try {
c = getContentResolver().query(selectedPicUri, filePathColumn, null, null, null);
if (c.moveToFirst()) {
filePath = c.getString(c.getColumnIndex(filePathColumn[0]));
}
} finally {
if (c != null && !c.isClosed()) {
c.close();
}
}
}
} catch (Exception e) {
filePath = "";
DebugUtil.exception(TAG, e);
}
return filePath;
}

然而在4.4的系统上,得到的路径是空的,是因为这句getContentResolver().query(selectedPicUri, filePathColumn, null, null, null);,由于Uri的格式在4.4以上的系统发生了改变,所有需要特殊处理一下,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public static String getPhotoPathFromContentUri(Context context, Uri uri) {
String photoPath = "";
if(context == null || uri == null) {
return photoPath;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
if(isExternalStorageDocument(uri)) {
String [] split = docId.split(":");
if(split.length >= 2) {
String type = split[0];
if("primary".equalsIgnoreCase(type)) {
photoPath = SdHelper.SD_DIR + File.separator + split[1];
}
}
}
else if(isDownloadsDocument(uri)) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
photoPath = getDataColumn(context, contentUri, null, null);
}
else if(isMediaDocument(uri)) {
String[] split = docId.split(":");
if(split.length >= 2) {
String type = split[0];
Uri contentUris = null;
if("image".equals(type)) {
contentUris = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if("video".equals(type)) {
contentUris = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
else if("audio".equals(type)) {
contentUris = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = new String[] { split[1] };
photoPath = getDataColumn(context, contentUris, selection, selectionArgs);
}
}
}
else if("file".equalsIgnoreCase(uri.getScheme())) {
photoPath = uri.getPath();
}
else {
photoPath = getDataColumn(context, uri, null, null);
}
return photoPath;
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
String column = MediaStore.Images.Media.DATA;
String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null && !cursor.isClosed())
cursor.close();
}
return null;
}

这样就没有问题了.

Android常见问题及开发经验总结

发表于 2015-07-31 | 分类于 Android | | 阅读次数

原文地址:http://blog.csdn.net/shaoyezhangliwei/article/details/46683705

1.如何更好的优化一个ListView,使其变得非常流畅?

  • Item布局,层级越少越好,使用hierarchyview工具查看优化。
  • 复用convertView
  • 使用ViewHolder
  • item中有图片时,异步加载
  • 快速滑动时,不加载图片
  • item中有图片时,应对图片进行适当压缩
  • 实现数据的分页加载

2.对于Android 的安全问题,一般都有哪些?

  • 错误导出组件
  • 参数校验不严
  • WebView引入各种安全问题,webview中的js(要注意代码混淆引起的问题)注入
  • 不混淆、不防二次打包
  • 明文存储关键信息
  • 错误使用HTTPS
  • 山寨加密方法
  • 滥用权限、内存泄露、使用debug签名

3. 如何缩减APK包大小?

代码

  • 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。
  • 使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。
  • native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。

资源

  • 使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。 assets目录下的资源请确保没有用不上的文件。
  • 生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。
  • jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。

策略

  • 有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。
  • 尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。
  • 能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。

4.Android与服务器交互的方式中的对称加密和非对称加密是怎么回事?

  • 对称加密,就是加密和解密数据都是使用同一个key,这方面的算法有DES。
  • 非对称加密,加密和解密是使用不同的key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有RSA。ssh 和 ssl都是典型的非对称加密。

5.设备横竖屏切换的时候,activity中生命周期的问题?

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  • 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  • 设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

6.Android启动Service的两种方式是什么?适用情况一般是哪些?

  • 如果后台服务开始后基本可以独立运行的话,可以用startService。音乐播放器就可以这样用。它们会一直运行直到你调用 stopSelf或者stopService。你可以通过发送Intent或者接收Intent来与正在运行的后台服务通信,但大部分时间,你只是启动服务并让它独立运行。如果你需要与后台服务通过一个持续的连接来比较频繁地通信,建议使用bind()。比如你需要定位服务不停地把更新后的地理位置传给UI。Binder比Intent开发起来复杂一些,但如果真的需要,你也只能使用它。
  • startService:生命周期与调用者不同。启动后若调用者未调用stopService而直接退出,Service仍会运行
  • bindService:生命周期与调用者绑定,调用者一旦退出,Service就会调用unBind->onDestroy

7.Android中Context一般怎么用?

  • Context:包含上下文信息(外部值) 的一个参数. Android 中的 Context 分三种,Application Context,Activity Context ,Service Context.
  • 它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等

8.Service的onCreate回调在UI线程中吗?

  • Service生命周期的各个回调和其他的应用组件一样,是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情

9.AsyncTask的内部实现,适用的场景是?

  • AsyncTask内部也是Handler机制来完成的,只不过Android提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以AsyncTask只应该用来执行耗时时间较短的任务,比如HTTP请求,大规模的下载和数据库的更改不适用于AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生AsyncTask根本执行不了的问题。

10.binder机制的理解?

  • binder是一种IPC机制,进程间通讯的一种工具.
  • Java层可以利用aidl工具来实现相应的接口.

11.Android中进程间通信有哪些实现方式?

  • Intent,Binder(AIDL),Messenger,BroadcastReceiver

12.介绍下实现一个自定义view的基本流程

  • 自定义View的属性 编写attr.xml文件
  • 在layout布局文件中引用,同时引用命名空间
  • 在View的构造方法中获得我们自定义的属性 ,在自定义控件中进行读取(构造方法拿到attr.xml文件值)
  • 重写onMesure
  • 重写onDraw

13.Android中touch事件的传递机制是怎样的?

  • Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
  • Touch事件相关的类有View、ViewGroup、Activity
  • Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作
  • Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View。
  • 如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理.
  • onInterceptTouchEvent为ViewGroup特有,可以拦截事件.
  • Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它

14.Android多线程的实现方式有哪些?

  • Thread & AsyncTask
  • Thread 可以与Loop 和 Handler 共用建立消息处理队列
  • AsyncTask 可以作为线程池并行处理多任务

15.Android开发中何时使用多进程?使用多进程的好处是什么?

  • 要想知道如何使用多进程,先要知道Android里的多进程概念。一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
  • 那如何让自己的应用拥有多个进程?
  • 很简单,我们的四大组件在AndroidManifest文件中注册的时候,有个属性是android:process,
    1.这里可以指定组件的所处的进程。默认就是应用的主进程。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。你可以重载Application类的onCreate方法,打印出它的进程名称,就可以清楚的看见了。再设置android:process属性时候,有个地方需要注意:如果是android:process=”:deamon”,以:开头的名字,则表示这是一个应用程序的私有进程,否则它是一个全局进程。私有进程的进程名称是会在冒号前自动加上包名,而全局进程则不会。一般我们都是有私有进程,很少使用全局进程。他们的具体区别不知道有没有谁能补充一下。
    2.使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。应该还有还有其他好处,这里就不多说了。
    3.坏处的话,多占用了系统的空间,大家都这么用的话系统内存很容易占满而导致卡顿。消耗用户的电量。应用程序架构会变复杂,应为要处理多进程之间的通信。这里又是另外一个问题了。

16.ANR是什么?怎样避免和解决ANR?

  • ANR:Application Not Responding,即应用无响应
  • ANR一般有三种类型:
    1:KeyDispatchTimeout(5 seconds) –主要类型
    按键或触摸事件在特定时间内无响应
    2:BroadcastTimeout(10 seconds)
    BroadcastReceiver在特定时间内无法处理完成
    3:ServiceTimeout(20 seconds) –小概率类型
    Service在特定的时间内无法处理完成
    超时的原因一般有两种:
    (1)当前的事件没有机会得到处理(UI线程正在处理前一个事件没有及时完成或者looper被某种原因阻塞住)
    (2)当前的事件正在处理,但没有及时完成
    UI线程尽量只做跟UI相关的工作,耗时的工作(数据库操作,I/O,连接网络或者其他可能阻碍UI线程的操作)放入单独的线程处理,尽量用Handler来处理UI thread和thread之间的交互。
    UI线程主要包括如下:
    Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick()
    AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel()
    Mainthread handler: handleMessage(), post(runnable r)
    other

17.Android下解决滑动冲突的常见思路是什么?

  • 相关的滑动组件 重写onInterceptTouchEvent,然后判断根据xy值,来决定是否要拦截当前操作

18.如何把一个应用设置为系统应用?

  • 成为系统应用,首先要在 对应设备的 Android 源码 SDK 下编译,编译好之后:
  • 此 Android 设备是 Debug 版本,并且已经 root,直接将此 apk 用 adb 工具 push 到 system/app 或 system/priv-app 下即可。
  • 如果非 root 设备,需要编译后重新烧写设备镜像即可。
  • 有些权限(如 WRITE_SECURE_SETTINGS ),是不开放给第三方应用的,只能在对应设备源码中编译然后作为系统 app 使用。

19、Android内存泄露研究

  • Android内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
  • 场景
    类的静态变量持有大数据对象
    静态变量长期维持到大数据对象的引用,阻止垃圾回收。
    非静态内部类的静态实例
    非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。
    资源对象未关闭
    资源性对象如Cursor、File、Socket,应该在使用后及时关闭。未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。
    注册对象未反注册
    未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
    Handler临时性内存泄露
    Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。
    由于AsyncTask内部也是Handler机制,同样存在内存泄漏的风险。
    此种内存泄露,一般是临时性的。

20.内存泄露检测有什么好方法?

  • 检测:
    1、DDMS Heap发现内存泄露
    dataObject totalSize的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露
    2、使用Heap Tool进行内存快照前后对比
    BlankActivity手动触发GC进行前后对比,对象是否被及时回收

21.android.R.layout下面的布局都是什么意思?

  • 这是几个列表选项的布局文件。 在List声明实例化时需要传入的。
    android.R.layout.simple_list_item_1 列表选项的简单布局
    android.R.layout.simple_expandable_list_item_2 可展开列表选项的简单布局
    android.R.layout.simple_list_item_checked 列表被选中的选项的简单布局
    android.R.layout.simple_list_item_multiple_choice 可选列表的选项的简单布局 多选
    android.R.layout.simple_list_item_single_choice 可选列表的选项的简单布局 单选

Android常见问题及开发经验总结

发表于 2015-07-31 | 分类于 Android | | 阅读次数

原文地址:http://blog.csdn.net/shaoyezhangliwei/article/details/46683705

1.如何更好的优化一个ListView,使其变得非常流畅?

  • Item布局,层级越少越好,使用hierarchyview工具查看优化。
  • 复用convertView
  • 使用ViewHolder
  • item中有图片时,异步加载
  • 快速滑动时,不加载图片
  • item中有图片时,应对图片进行适当压缩
  • 实现数据的分页加载

2.对于Android 的安全问题,一般都有哪些?

  • 错误导出组件
  • 参数校验不严
  • WebView引入各种安全问题,webview中的js(要注意代码混淆引起的问题)注入
  • 不混淆、不防二次打包
  • 明文存储关键信息
  • 错误使用HTTPS
  • 山寨加密方法
  • 滥用权限、内存泄露、使用debug签名

3. 如何缩减APK包大小?

代码

  • 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。
  • 使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。
  • native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。

资源

  • 使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。 assets目录下的资源请确保没有用不上的文件。
  • 生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。
  • jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。

策略

  • 有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。
  • 尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。
  • 能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。

4.Android与服务器交互的方式中的对称加密和非对称加密是怎么回事?

  • 对称加密,就是加密和解密数据都是使用同一个key,这方面的算法有DES。
  • 非对称加密,加密和解密是使用不同的key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有RSA。ssh 和 ssl都是典型的非对称加密。

5.设备横竖屏切换的时候,activity中生命周期的问题?

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  • 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  • 设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

6.Android启动Service的两种方式是什么?适用情况一般是哪些?

  • 如果后台服务开始后基本可以独立运行的话,可以用startService。音乐播放器就可以这样用。它们会一直运行直到你调用 stopSelf或者stopService。你可以通过发送Intent或者接收Intent来与正在运行的后台服务通信,但大部分时间,你只是启动服务并让它独立运行。如果你需要与后台服务通过一个持续的连接来比较频繁地通信,建议使用bind()。比如你需要定位服务不停地把更新后的地理位置传给UI。Binder比Intent开发起来复杂一些,但如果真的需要,你也只能使用它。
  • startService:生命周期与调用者不同。启动后若调用者未调用stopService而直接退出,Service仍会运行
  • bindService:生命周期与调用者绑定,调用者一旦退出,Service就会调用unBind->onDestroy

7.Android中Context一般怎么用?

  • Context:包含上下文信息(外部值) 的一个参数. Android 中的 Context 分三种,Application Context,Activity Context ,Service Context.
  • 它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等

8.Service的onCreate回调在UI线程中吗?

  • Service生命周期的各个回调和其他的应用组件一样,是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情

9.AsyncTask的内部实现,适用的场景是?

  • AsyncTask内部也是Handler机制来完成的,只不过Android提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以AsyncTask只应该用来执行耗时时间较短的任务,比如HTTP请求,大规模的下载和数据库的更改不适用于AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生AsyncTask根本执行不了的问题。

10.binder机制的理解?

  • binder是一种IPC机制,进程间通讯的一种工具.
  • Java层可以利用aidl工具来实现相应的接口.

11.Android中进程间通信有哪些实现方式?

  • Intent,Binder(AIDL),Messenger,BroadcastReceiver

12.介绍下实现一个自定义view的基本流程

  • 自定义View的属性 编写attr.xml文件
  • 在layout布局文件中引用,同时引用命名空间
  • 在View的构造方法中获得我们自定义的属性 ,在自定义控件中进行读取(构造方法拿到attr.xml文件值)
  • 重写onMesure
  • 重写onDraw

13.Android中touch事件的传递机制是怎样的?

  • Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
  • Touch事件相关的类有View、ViewGroup、Activity
  • Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作
  • Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View。
  • 如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理.
  • onInterceptTouchEvent为ViewGroup特有,可以拦截事件.
  • Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它

14.Android多线程的实现方式有哪些?

  • Thread & AsyncTask
  • Thread 可以与Loop 和 Handler 共用建立消息处理队列
  • AsyncTask 可以作为线程池并行处理多任务

15.Android开发中何时使用多进程?使用多进程的好处是什么?

  • 要想知道如何使用多进程,先要知道Android里的多进程概念。一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
  • 那如何让自己的应用拥有多个进程?
  • 很简单,我们的四大组件在AndroidManifest文件中注册的时候,有个属性是android:process,
    1.这里可以指定组件的所处的进程。默认就是应用的主进程。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。你可以重载Application类的onCreate方法,打印出它的进程名称,就可以清楚的看见了。再设置android:process属性时候,有个地方需要注意:如果是android:process=”:deamon”,以:开头的名字,则表示这是一个应用程序的私有进程,否则它是一个全局进程。私有进程的进程名称是会在冒号前自动加上包名,而全局进程则不会。一般我们都是有私有进程,很少使用全局进程。他们的具体区别不知道有没有谁能补充一下。
    2.使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。应该还有还有其他好处,这里就不多说了。
    3.坏处的话,多占用了系统的空间,大家都这么用的话系统内存很容易占满而导致卡顿。消耗用户的电量。应用程序架构会变复杂,应为要处理多进程之间的通信。这里又是另外一个问题了。

16.ANR是什么?怎样避免和解决ANR?

  • ANR:Application Not Responding,即应用无响应
  • ANR一般有三种类型:
    1:KeyDispatchTimeout(5 seconds) –主要类型
    按键或触摸事件在特定时间内无响应
    2:BroadcastTimeout(10 seconds)
    BroadcastReceiver在特定时间内无法处理完成
    3:ServiceTimeout(20 seconds) –小概率类型
    Service在特定的时间内无法处理完成
    超时的原因一般有两种:
    (1)当前的事件没有机会得到处理(UI线程正在处理前一个事件没有及时完成或者looper被某种原因阻塞住)
    (2)当前的事件正在处理,但没有及时完成
    UI线程尽量只做跟UI相关的工作,耗时的工作(数据库操作,I/O,连接网络或者其他可能阻碍UI线程的操作)放入单独的线程处理,尽量用Handler来处理UI thread和thread之间的交互。
    UI线程主要包括如下:
    Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick()
    AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel()
    Mainthread handler: handleMessage(), post(runnable r)
    other

17.Android下解决滑动冲突的常见思路是什么?

  • 相关的滑动组件 重写onInterceptTouchEvent,然后判断根据xy值,来决定是否要拦截当前操作

18.如何把一个应用设置为系统应用?

  • 成为系统应用,首先要在 对应设备的 Android 源码 SDK 下编译,编译好之后:
  • 此 Android 设备是 Debug 版本,并且已经 root,直接将此 apk 用 adb 工具 push 到 system/app 或 system/priv-app 下即可。
  • 如果非 root 设备,需要编译后重新烧写设备镜像即可。
  • 有些权限(如 WRITE_SECURE_SETTINGS ),是不开放给第三方应用的,只能在对应设备源码中编译然后作为系统 app 使用。

19、Android内存泄露研究

  • Android内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
  • 场景
    类的静态变量持有大数据对象
    静态变量长期维持到大数据对象的引用,阻止垃圾回收。
    非静态内部类的静态实例
    非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。
    资源对象未关闭
    资源性对象如Cursor、File、Socket,应该在使用后及时关闭。未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。
    注册对象未反注册
    未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
    Handler临时性内存泄露
    Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。
    由于AsyncTask内部也是Handler机制,同样存在内存泄漏的风险。
    此种内存泄露,一般是临时性的。

20.内存泄露检测有什么好方法?

  • 检测:
    1、DDMS Heap发现内存泄露
    dataObject totalSize的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露
    2、使用Heap Tool进行内存快照前后对比
    BlankActivity手动触发GC进行前后对比,对象是否被及时回收

21.android.R.layout下面的布局都是什么意思?

  • 这是几个列表选项的布局文件。 在List声明实例化时需要传入的。
    android.R.layout.simple_list_item_1 列表选项的简单布局
    android.R.layout.simple_expandable_list_item_2 可展开列表选项的简单布局
    android.R.layout.simple_list_item_checked 列表被选中的选项的简单布局
    android.R.layout.simple_list_item_multiple_choice 可选列表的选项的简单布局 多选
    android.R.layout.simple_list_item_single_choice 可选列表的选项的简单布局 单选
12
古古天

古古天

这里是古古天的博客

15 日志
7 分类
19 标签
RSS
GitHub 微博 Facebook
友情链接
  • 电泡泡
  • 飘渺_鸿影
  • 大嘴鸟
  • 狐狸
  • 惊奇郭
  • ZZY
© 2015 - 2017 古古天
由 Hexo 强力驱动
主题 - NexT.Mist