Android系统架构
- Android系统分四层:Linux内核层,库和运行时,Framework层和应用层。
- Linux包括硬件驱动,进程管理,安全系统
- Dalvik虚拟机,运行时编译;ART虚拟机安装时编译
- Framework
- ActivityManager
- BackupManager
- Bluetooth
- ContentProviders
- LocationManger
- Map libraries
- MediaPlayer
- NotificationManager
- PackageManager
- ResourceManager
- SearchManager
- SharedPreference
- TelephoneManager
- WidgetProvider
- WindowManager
- ViewSystem
- XMPPService
- 标准库
- Apache HTTP
- OpenGL ES
- Open SSL
- SQLite
- Webkit
- ICU
- Android NDK/SDK APP(解压后包含内容中有)
App组件
- Activity
- BroardCastReceiver
- ContentProvider
- Service
Context
- Application Context(应用启动时创建,整个生命周期)
- Activity Context
- Service Context
Android 源代码目录与系统目录
- Android源代码目录
- Makefile (自动化编译,定制规则)
- bionic (bionic C库)
- bootable (启动引导相关代码)
- build (系统编译规则等基础开发包配置)
- cts (Google兼容性测试标准)
- dalvik (dalvik虚拟机)
- development (应用程序开发)
- external (开源模块)
- frameworks (框架)
- hardware (硬件适配层HAL代码)
- out (编译完成后代码输出目录)
- packages (应用程序包)
- prebuilt (x86 arm 架构下预编译资源)
- sdk
- system (底层文件系统库,应用及组件)
- vendor (厂商定制代码)
- Android 系统目录 (ADB ls命令查看系统目录)
- /system/app/ 系统app
- /system/bin/ Linux自带组件
- /system/build.prop/ 系统属性信息
- /system/fonts/ 字体
- /system/framework/ 核心文件,框架层
- /system/lib/ .so文件
- /system/media/ 音频,闹钟,短信,来电等铃声
- /system/usr/ 用户配置文件,键盘布局,共享,时区文件
- /data/app/ 安装或升级的app
- /data/data/ App数据文件,数据库等信息
- /data/system/ 系统信息
- /data/misc/ WIFI,蓝牙,签名,VPN等信息
Android 开发工具
- SDK镜像地址 android-studio
- Genymotion 安装arm架构Genymotion-ARM-Translation
- Android Debug
- ADB Command (SDK platform-tools)
- adb shell
- ls|grep “data” / cd data
- android list target 显示系统Android平台 id:1 or android-19
- adb install -r a.apk 重新安装保留数据,安装位置/data/local/tmp/a.apk
- adb push a.apk /system/app 文件写入存储系统,有权限时可以安装系统应用
- adb push /system/temp/ c:\Desktop 从手机获取文件
- adb shell -> logcat|grep “abc” 查看log
- adb remount(重新挂载系统分区,使系统分区重新可写) -> adb shell -> cd system/app -> rm a.apk
- adb shell df 查看系统盘符
- adb shell pm list packages -f 输出所有安装应用
- adb shell input keyevent 3 模拟按键输入 keycode
- adb shell input touchscreen swipe 18 655 18 500 模拟滑动输入
- adb shell dumpsys activity activities|grep “tencent” 列出activity 运行状态,过滤”tencent”
- pm list packages -f 列出所有package
- adb shell am start -n com.qiyi.video/org.qiyi.android.video.MainActivity 启动一个Activity
- adb shell screenrecord /sdcard/a.mp4 录制屏幕
- adb reboot 重启
- adb 命令来源
- /frameworks/base/cmds/
- /system/core/toolbox/
Android 自定义控件
控件架构 ContentView id为content的FrameLayout, onCreate()方法中调用setContentView()后ActivityManagerService会回调onResume(),此时系统才会把DecorView添加到PhoneWindow中,显示并完成绘制
View的测量
- onMeasure() MeasureSpec 32位int值,高2位为测量的模式,低30位为测量的大小
- EXACTLY 精确值模式,具体数值
- AT_MOST 最大值模式,尺寸不超过父控件允许的最大尺寸
- UNSPECIFIED 不指定,自定义view时使用,如果View要支持wrap_content,重写onMeasure()指定wrap_content时的大小
- onMeasure() MeasureSpec 32位int值,高2位为测量的模式,低30位为测量的大小
1 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
1 | /** |
View的绘制
- 重写onDraw(Canvas canvas)
- Canvas 画板
1
2
3
4
5
6
7canvas.drawBitmap(bitmap1,0,0,null);
canvas.drawBitmap(bitmap2,0,0,null);
装载画布
Canvas mCanvas=new Canvas(bitmap2);
mCanvas.drawXXX
刷新view的时候,bitmap2发生改变,没有把图形直接绘制在canvas,而是通过改变bitmap2
绘制复杂图形,绘制多个拆分的小图形单元
- Canvas 画板
- 重写onDraw(Canvas canvas)
ViewGroup的测量
在wrap_content时遍历子View的大小,决定自己的大小,其他模式下通过具体的指定值设置大小。遍历子View,调用子View的Measure方法获得每一个子View 的测量结果,然后遍历子View进行布局Layout,重写onLayout()控制子View显示位置的逻辑,如果支持wrap_content需要重写onMeasure()- ViewGroup的绘制 绘制背景颜色以及遍历子View的绘制
- 自定义View比较重要的回调方法
- onFinishInflate()
- onSizeChanged()
- onMeasure()
- onLayout()
- onDraw()
- onTouchEvent()
对原生控件扩展
改变绘制顺序,需要改变画布位置状态时,先保存状态,等改变后,再恢复
1
2
3
4
5....
canvas.save();
canvas.translate(10,0);
super.onDraw(canvas);
canvas.restore();LinearGradient,Matrix实项文字闪动效果,onSizeChanged()中初始化,根据宽度设置LinearGradient渐变渲染器
1 | protected void onSizeChanged(int w,int h,int oldw,int oldh){ |
复合控件继承ViewGroup
定义属性 res/value/attrs.xml
1
2
3
4
5
6
7
8
9
10
11
12<declare-styleable name="V">
<attr name="title|size|background" format="string|dimension| reference|color"/>
</declare-styleable>
//获取TypedArray,存储了属性值
TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.V);
ta.getColor();
ta.getString();
ta.getDrawable();
ta.getDimension();
//资源回收
ta.recycle();组合控件
1
2
3
4
5
6
7
8mBtn1=new Button();
mBtn2=new Button();
mTextView=new TextView();
mParams=new LayoutParams();
mParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,true);
addView(mBtn1,mParams);
addView(mBtn2,mParams);
addView(mTextView,mParams);- 定义接口,添加事件
- public method
- include layout
重写View
比例图 中间圆形加中心文字,外圈一定比例的圆弧 length 正方形边长
1
2
3
4
5
6
7
8
9
10
11- 圆
mCircle=length/2;mRadius=length/4; drawCirle()
- 圆弧
//所处的矩形框
mRect=new Rect(0.1*length,0.1*length,0.9*length,0.9*length);
drawArc(mRect,270,mSweepAngle,false,mArcPaint);
- 文字
drawText();
setSweepValue(100);音频条形图
- onSizeChanged()设置渐变色
- onDraw() 绘制多个小矩形
- postInvalidateDelayed(300)
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@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
Log.e("Audio", "mWidth=" + mWidth + " w=" + w + " h=" + h
+ " oldw=" + oldw + " oldh=" + oldh);
mRectHeight = getHeight();
mRectWidth = (int) (mWidth * 0.6 / mCount);
mLinearGradient = new LinearGradient(0, 0, mRectWidth, mRectHeight, Color.YELLOW, Color.RED, Shader.TileMode.CLAMP);
mMatrix = new Matrix();
mPaint.setShader(mLinearGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTranslate += mRectWidth / 5;
if (mTranslate > 2 * mRectWidth) {
mTranslate = -mRectWidth;
}
mMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mMatrix);
for (int i = 0; i < mCount; i++) {
mCurrentHeight = (float) (mRectHeight * Math.random());
canvas.drawRect((float) (mWidth * 0.2 + offset + mRectWidth * i), mCurrentHeight,
(float) (mWidth * 0.2 + mRectWidth * (i + 1)), mRectHeight, mPaint);
}
postInvalidateDelayed(300);
}
自定义ViewGroup
- 重写onMeasure() onLayout() onTouchEvent()
自定义ScrollView,滑动后回位到item的起始位置
- 遍历的方式通知子View对自身测量
1
2
3
4
5
6
7
8
9
10
11
12
13
14private void init() {
mScroller = new OverScroller(getContext());
mScreenHeight = getResources().getDisplayMetrics().heightPixels;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count=getChildCount();
for (int i = 0; i < count; i++) {
View child=getChildAt(i);
measureChild(child,widthMeasureSpec,heightMeasureSpec);
}
}- 对子View进行位置放置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int screenHeight = getResources().getDisplayMetrics().heightPixels;
//获取整个ViewGroup高度
MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
layoutParams.height = childCount * screenHeight;
setLayoutParams(layoutParams);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.layout(l, screenHeight * i, r, screenHeight * (i + 1));
}
}
}- 在onTouchEvent()中添加滑动事件,使用scrollBy()
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@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = event.getY();
//起点
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
float dy = mLastY - event.getY();
Log.d("Scroll", "======" );
Log.d("Scroll", "dy = " + dy);
Log.d("Scroll", "getScrollY() = " + getScrollY());
Log.d("Scroll", "getHeight() = " + getHeight());
Log.d("Scroll", "mScreenHeight() = " + mScreenHeight);
Log.d("Scroll", "getHeight() - mScreenHeight = " + (getHeight() - mScreenHeight));
Log.d("Scroll", "mLastY = " + mLastY);
Log.d("Scroll", "mStart = " + mStart);
if (getScrollY() < 0) {
//最顶端,超过0时,不再下拉
//不设置这个,getScrollY一直是负数
dy = 0;
}
if (getScrollY() > getHeight() - mScreenHeight) {
//滑到最底端时,不再滑动
//不设置这个,getScrollY一直是大于getHeight() - mScreenHeight的数
dy = 0;
}
scrollBy(0, (int) dy);
//不断的设置Y,在滑动的时候子view就会比较顺畅
mLastY = event.getY();
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int dScrollY = mEnd - mStart;
//向上,向下滑动,超过1/3屏幕高度,
//结束滑动时滚动到下个位置,没有超过时复位
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(0, mEnd,
0, -dScrollY, 200);
} else {
mScroller.startScroll(0, mEnd, 0,
mScreenHeight - dScrollY, 200);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(0, mEnd,
0, -dScrollY, 200);
} else {
mScroller.startScroll(0, mEnd,
0, -(mScreenHeight + dScrollY), 200);
}
}
break;
}
// 重绘执行computeScroll()
postInvalidate();
//需要返回true否则down后无法执行move和up操作
return true;
}
/**
* Scroller只是个计算器,提供插值计算,让滚动过程具有动画属性,
* 但它并不是UI,也不是滑动辅助UI运动,反而是单纯地为滑动提供计算
* 需要invalidate()之后才会调用,这个方法在onDraw()中调用
*/
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
事件拦截机制,涉及触摸事件MotionEvent(),onTouchEvent(),dispatchTouchEvent(),onInterceptTouchEvent(),多个View,ViewGroup之间对事件的处理
ViewGroup A,B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("A","dispatchTouchEvent "+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("A","onInterceptTouchEvent "+ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e("A","onTouchEvent "+ev.getAction());
return super.onTouchEvent(ev);
}View
1
2
3
4
5
6
7
8
9
10
11@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("MyView","dispatchTouchEvent "+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e("MyView","onTouchEvent "+ev.getAction());
return super.onTouchEvent(ev);
}View位置顺序是A最外层,B中间,MyView最底层。传递分发机制是A->B->MyView,处理机制时MyView->B->A
1
2
3事件传递返回值:True 拦截,不继续传递;False,不拦截,继续传递
事件处理返回值:True 处理了不用传递回上级,False 给上级处理
初始情况都是False