Android Notes (System ADB UI)

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(解压后包含内容中有)
      • AndroidManifeset
      • Dalvik Classes
      • Resource bundle
      • NDK APP中包含JNI lib
  • 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时的大小
1
2
3
4
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 宽高值自定义,在wrap_content时如果没有指定大小就会默认填充整个父布局
*/
private int measureWidth(int measureSpec){
int result=0;
int specMode=MeasureSpec.getMoode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);

if(specMode=MeasureSpec.EXACTLY){
result=specSize;
}else{
result=200;
if(specMode=MeasureSpec.AT_MOST){
result=Math.min(result,specSize)
}
}
return result;
}
  • View的绘制

    • 重写onDraw(Canvas canvas)
      • Canvas 画板
        1
        2
        3
        4
        5
        6
        7
        canvas.drawBitmap(bitmap1,0,0,null);
        canvas.drawBitmap(bitmap2,0,0,null);
        装载画布
        Canvas mCanvas=new Canvas(bitmap2);
        mCanvas.drawXXX
        刷新view的时候,bitmap2发生改变,没有把图形直接绘制在canvas,而是通过改变bitmap2
        绘制复杂图形,绘制多个拆分的小图形单元
  • 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
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
protected void onSizeChanged(int w,int h,int oldw,int oldh){
super.onSizeChanged(w,h,oldw,oldh);
if(mViewWidth==0){
mViewWidth=getMeasureWidth();
if(mViewWidth>0){
//获取Paint,设置LinearGradient
mPaint=getPaint();
mLinearGradient=new LinearGradient(0,0,mViewWidth,0,new int[] {Color.BLUE,Color.RED},null,Shader.TitleMode.CLAMP);
//设置着色器 mPaint.setShader(mLinearGradient)
//平移变换矩阵
mGradientMatrix=new Matrix();
}
}
}

public void onDraw(Canvas canvas){
if(mGradientMatrix!=null){
mTranslate+=mViewWidth/5;
if(mTranslate>2*mViewWidth){
mTranslate=-mViewWidth;
}
mGradientMatrix.setTranslate(mTranslate,0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
postInvalidateDelayed(100);
}
}
  • 复合控件继承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
      8
      mBtn1=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
      14
      private 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
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!