-
dispatchMessage() 分发消息三种情况
- Message对象的callback不为空(runnable),交给callback处理
- handler的callback不为空,交给callback处理(如果你消息自己有callback的话,就会优先执行你的消息的callback)
- 前两种都没有的情况下交给handleMessag去处理
-
Message的Obtain()/recycleUnchecked()
-
DialogFragment至少重写下面一个方法
- onCreateView:自己通过XML定义布局
- onCreateDialog:使用系统内置对话框比如AlertDialog
-
swapCursor vs changeCursor
-
LayoutAnimation vs LayoutTransition
-
CursorTreeAdapter的使用简介
-
带你看懂LayoutInflater中inflate方法
-
自定义View构造函数参数
- 一个参数:一般在直接New一个View的时候调用
- 两个参数:一般在layout文件中使用的时候会调用,关于它的所有属性(AttributeSet)(包括自定义属性,直接引用的Style资源)都会包含在attrs中传递进来
- 三个参数:默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会生效(AttributeSet未找到则从这里找)
- 四个参数:API21的时候才添加上,当第三个参数不生效时才有作用
- 优先级从高到低依次是:直接在layout中设置View的XML属性值(AttributeSet)> 设置View的style属性 > defStyleAttr > defStyleRes
-
Android安全机制分为三层
- 最基础的一层,数据分为system和data两个区。其中system是只读的,data用来存放应用自己的数据,这保证了系统数据不会被随意改写
- 第二层用来使应用之间的数据相互独立。每个应用都会有一个user id和group id,只有相同的user id才能问它们的数据。通过对apk签名来标识自己。签名和uid构成了双重的保证
- 第三个层次就是权限体系
-
调用的系统API接口,有3种目录可以给我们写入文件
- 应用私有存储(内置存储)不需要申请
- Context.getFileDir():/data/data/应用包名/files/ 对应着App下清除数据
- Context.getCacheDir():/data/data/应用包名/cache/ 对应着清除缓存
- 应用扩展存储(SD卡)API < 19:需要&API >= 19:不需要
- Context.getExternalFilesDir():SDCard/Android/data/应用包名/files/
- Context.getExternalCacheDir():SDCard/Android/data/应用包名/cache/
- 公共存储(SD卡)需要申请
- Environment.getExternalStorageDirectory():SDCard/文件夹名字/
- 应用私有存储(内置存储)不需要申请
-
Bitmap 在内存当中占用的大小其实取决于
- 色彩格式,eg:如果是 ARGB8888 是像素4个字节,如果是 RGB565 是2个字节
- 原始文件存放的资源目录(是 hdpi 还是 xxhdpi 可不能傻傻分不清楚哈)
- 目标屏幕的密度(所以同等条件下,红米在资源方面消耗的内存肯定是要小于三星S6的)
-
解决屏幕旋转(状态发生变化)状态不能保持的问题
- 不好的实践:保存整个Activity使用设置android:configChanges属性,不止是屏幕旋转。比如修改设备默认语言,修改设备默认字体比例等都可会引起配置改变
- 已经被弃用的方法(API 13):重写
onRetainNonConfigurationInstance()、getLastNonConfigurationInstance()
方法 - 使用
onSaveInstanceState() 、onRestoreInstanceState()
进行数据保存与恢复 - 推荐的方法:在Retained Fragment中管理对象
- 扩展Fragment类,并声明对有状态对象的引用(onAttach引用Activity)
- 创建Fragment时调用setRetainInstance(boolean)
- 将片段添加到Activity
- 当Activity重新启动时,使用FragmentManager检索片段
-
FragmentTransaction中对Fragment的操作
-
add() & remove()
:add()
会执行onAttach()~onResume()
周期,remove()
执行onPause()~onDetach()
周期 -
show() & hide()
:生命周期方法不会执行,仅仅是View显示或者隐藏,并视情况调用onHiddenChanged()
-
attach() & detach ()
:onPause()、onDestroyView()
会被执行,Fragment的View也会被detach,但不会执行onDestroy()、onDetach()
-
replace()
:其实就是remove()+add()
-
-
TouchEvent事件,低8位表示touch事件的具体动作,比如按下,抬起,滑动,还有多点触控时的按下,抬起,这个和单点是区分开的
- getAction:触摸动作的原始32位信息,包括事件的动作,触控点信息
- getActionMask:触摸的动作,按下,抬起,滑动,多点按下,多点抬起
- getActionIndex:触控点信息
-
TypedArray中的函数是获取自定义属性的,Resources中的函数是获取android预置属性的
- getDimension()是基于当前DisplayMetrics进行转换,获取指定资源id对应的尺寸。函数的返回值是float,像素肯定是int
- getDimensionPixelSize()与getDimension()功能类似,不同的是将结果转换为int,并且小数部分四舍五入
- getDimensionPixelOffset()功能类似,不同的是将结果转换为int,并且偏移转换(offset)是直接截断小数位,即取整(其实就是把float强制转化为int)
-
系统属性SystemProperty
- 通过命令adb shell getprop查看手机上所有属性状态值
- 属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变
- 属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property
- 属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名
-
save&saveLayer
- save方法可以保存当前的matrix and clip,并且在restore把它恢复,一些平移,旋转,缩放等操作都会影响Canvas的matrix,所以save操作一般可以保存这些信息以及clip信息
- saveLayer则强大很多,它相当于另外起一张干净图层,并在上面进行绘制操作,然后在restoreToCount的时候,把刚才所绘制的重新绘制在原本的Canvas上。当时正如所知的那样,它会绘制两次,所以消耗是十分巨大
-
代码中获取attr属性的值
- 在xml里,我们可以简单的引用attr属性值,例如:
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize" - 代码中获取attr属性值:
TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.yourAttr, typedValue, true); // For string typedValue.string typedValue.coerceToString() // For other data typedValue.resourceId typedValue.data;
- 在xml里,我们可以简单的引用attr属性值,例如:
-
Activity启动过程
-
SparseArray
- SparseArray 在某些条件下性能更好,主要是因为它避免了对key的自动装箱,内部是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,但是由于其添加、查找、删除数据都需要先进行一次二分查找,所以在数据量大的情况下性能并不明显,将降低至少50%。
- 数据量不大,最好在千级以内
- key必须为int类型,这中情况下的HashMap可以用SparseArray代替:
-
ArrayMap
- ArrayMap是一个<key,value>映射的,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作,所以,应用场景和SparseArray的一样,如果在数据量比较大的情况下,那么它的性能将退化至少50%。
- 如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用
- 如果key类型为其它的类型(String),则使用ArrayMap
- 两个数据结构都适合数据量不是特别大的情况
-
编译时候加上(据说编译比较快)
android{ ... tasks.whenTaskAdded { task -> if (task.name.contains("lint") //如果instant run不生效,把clean这行干掉 ||task.name.equals("clean") //如果项目中有用到aidl则不可以舍弃这个任务 ||task.name.contains("Aidl") //用不到测试的时候就可以先关闭 ||task.name.contains("mockableAndroidJar") ||task.name.contains("UnitTest") ||task.name.contains("AndroidTest") //用不到NDK和JNI的也关闭掉 || task.name.contains("Ndk") || task.name.contains("Jni") ) { task.enabled = false } } }
-
ExecutorService
的关闭方法- shutdown():将线程池状态置为SHUTDOWN,并不会立即停止
- shutdownNow():将线程池状态置为STOP,企图立即停止,事实不一定
- awaitTermination(): 当前线程阻塞,直到等所有已提交的任务(包括正在跑的和队列中等待的)执行完
-
Service
- 启动服务的需要通过 stopSelf() 自行停止,或其他组件调用 stopService()
- onRebind()方法调用时机:
- onUnbind()方法返回值为true
- 启动服务同时和绑定服务,当Activity退出的时候,Service的onUnbind()方法就会被调用
-
内容提供着权限有:
- 统一读写提供程序级别权限
- 单独的读取和写入提供程序级别权限
- 路径级别权限
- 临时权限
-
Android 6.0 Marshmallow的运行时权限
- 检查系统版本号:对于6.0开始的版本,才需要做运行时的权限检查
- 检查申请的权限:
checkSelfPermission(Manifest.permission.xxx)
- 解释申请的权限:
shouldShowRequestPermissionRationale()
- 执行申请权限操作:
requestPermissions()
- 处理权限申请的结果:
onRequestPermissionsResult()
-
Android安全机制
- 进程沙箱隔离机制
- 应用程序签名机制
- 权限声明机制
- 访问控制机制
- 进程通信机制
-
android中延迟执行某个任务
- 倒计时类
- 用CountDownTimer
- 延迟类
- CountDownTimer,可巧妙的将countDownInterval设成和millisInFuture一样,这样就只会调用一次onTick和一次onFinish
- handler.sendMessageDelayed,可参考CountDownTimer的内部实现,简化一下,个人比较推荐这个
TimerTask,代码写起来比较乱
Thread.sleep,感觉这种不太好
- 定时类
- 参照延迟类的,自己计算好要延迟多少时间
handler.sendMessageAtTime
AlarmManager,适用于定时比较长远的时间,例如闹铃
- 参照延迟类的,自己计算好要延迟多少时间
- 倒计时类
-
setBackgroundXXX的用法
- setBackgroundDrawable 的参数为Drawable对象
- setBackgroundColor 的参数为Color对象,比如说Color.Red为红色,或Color.rgb(255,0,0)
- setBackgroundResource 的参数为资源ID,比如说R.drawable.icon
-
getDimension,getDimensionPixelOffset和getDimensionPixelSize
- getDimension和getDimensionPixelOffset的功能类似,都是获取某个dimen的值,但是如果单位是dp或sp,则需要将其乘以density
如果是px,则不乘。并且getDimension返回float,getDimensionPixelOffset返回"下限"的int值. - getDimensionPixelSize则不管写的是dp还是sp还是px,都会乘以denstiy,返回"四舍五入"的int值.
- getDimension和getDimensionPixelOffset的功能类似,都是获取某个dimen的值,但是如果单位是dp或sp,则需要将其乘以density
-
切换Fragment时实现数据保持
- replace方法会Fragment导致频繁的释放和创建,Fragment比较臃肿或者需要从网络加载数据这样就非常不好
- instantiateItem从FragmentManager中查找Fragment,找不到就getItem新建一个,setPrimaryItem设置隐藏和显示,最后finishUpdate提交事务
Fragment fragment = (Fragment) mFragmentPagerAdapter.instantiateItem(mContainer, buttonView.getId()); mFragmentPagerAdapter.setPrimaryItem(mContainer, 0, fragment); mFragmentPagerAdapter.finishUpdate(mContainer);
- 解决切换tab的时候重影现象
public void setMenuVisibility(boolean menuVisible) { super.setMenuVisibility(menuVisible); if (this.getView() != null) this.getView().setVisibility(menuVisible ? View.VISIBLE : View.GONE); }
-
Environment 常用方法
- getDataDirectory(),获取 Android 数据目录
- getDownloadCacheDirectory(),获取 Android 下载/缓存内容目录
- getExternalStorageDirectory(),获取外部存储目录即 SDCard
- getExternalStorageState(),获取外部存储设备的当前状态
- getRootDirectory(),获取 Android 的根目录
-
Scrollview怎么判断是否滑倒顶部底部
- 滚动到顶部判断:getScrollY() == 0
- 滚动到底部判断:
View contentView = getChildAt(0); contentView.getMeasuredHeight() <= getScrollY() + getHeight();
其中getChildAt表示得到ScrollView的child View,因为ScrollView只允许一个child view,所以contentView.getMeasuredHeight()表示得到子View的高度, getScrollY()表示得到y轴的滚动距离,getHeight()为scrollView的高度。当getScrollY()达到最大时加上scrollView的高度就的就等于它内容的高度了
-
Android Fragment嵌套使用存在的一些BUG
- 当使用Fragment去嵌套另外一些子Fragment的时候,我们需要去管理子Fragment,这时候需要调用ChildFragmentManager去管理这些子Fragment,由此可能产生的Exception主要是:
java.lang.IllegalStateException: No activity - 当我们从一个Activity启动了一个Fragment,然后在这个Fragment中又去实例化了一些子Fragment,在子Fragment中去有返回的启动了另外一个Activity,即通过startActivityForResult方式去启动,这时候造成的现象会是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式启动,那么只有Activity会接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式启动,那么只有父Fragment能接收(此时Activity也能接收),但无论如何子Fragment接收不到OnActivityResult,我们可以通过在子Fragment中以getParentFragment.startActivityForResult的方式来启动,然后在父Fragment中去接收数据,我们需要在子Fragment中提供一个方法,如:getResultData(Object obj),通过父Fragment中的子Fragment实例去调用这个方法,把相应的数据传过去,然后去更新子Fragment
- 当使用Fragment去嵌套另外一些子Fragment的时候,我们需要去管理子Fragment,这时候需要调用ChildFragmentManager去管理这些子Fragment,由此可能产生的Exception主要是:
-
布局文件后缀
- sw
dp,如layout-sw600dp, values-sw600dp,这里的sw代表smallwidth的意思,当你所有屏幕的最小宽度都大于600dp时,屏幕就会自动到带sw600dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每个屏幕都是固定的,不会随着屏幕横向纵向改变而改变 - w
dp 如layout-w600dp, values-w600dp,带这样后缀的资源文件的资源文件制定了屏幕宽度的大于Ndp的情况下使用该资源文件,但它和sw
dp不同的是,当屏幕横向纵向切换时,屏幕的宽度是变化的,以变化后的宽度来与N相比,看是否使用此资源文件下的资源 - h
dp 如layout-h600dp, values-h600dp,这个后缀的使用方式和w
dp一样,随着屏幕横纵向的变化,屏幕高度也会变化,根据变化后的高度值来判断是否使用h
dp ,但这种方式很少使用,因为屏幕在纵向上通常能够滚动导致长度变化,不像宽度那样基本固定
- sw
-
Android 手机上获取物理唯一标识码
- DEVICE_ID:可以同通过TelephonyManager.getDeviceId()获取,它根据不同的手机设备返回IMEI,MEID或者ESN码,但它在使用的过程中会遇到很多问题: 非手机设备、权限、BUG
- MAC ADDRESS:我们也可以通过手机的Wifi或者蓝牙设备获取MAC ADDRESS作为DEVICE ID,但是并不建议这么做,因为并不是所有的设备都有Wifi,并且,如果Wifi没有打开,那硬件设备无法返回MAC ADDRESS
- Serial Number:
在Android 2.3可以通过android.os.Build.SERIAL获取,非手机设备可以通过该接口获取 - ANDROID_ID:是设备第一次启动时产生和存储的64bit的一个数,当设备被wipe后该数重置
ANDROID_ID似乎是获取Device ID的一个好选择,但它也有缺陷 - Installtion ID : UUID通过在程序安装后第一次运行后生成一个ID实现的,但该方式跟设备唯一标识不一样,它会因为不同的应用程序而产生不同的ID,而不是设备唯一ID。因此经常用来标识在某个应用中的唯一ID(即Installtion ID),或者跟踪应用的安装数量
-
ViewPager取消预加载
setOffscreenPageLimit()这一个方法是设置预加载的个数,默认为1,但是当你设置为0的时候也会强行将它设置为1
public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } }
-
attr和styleable的关系
- attr不依赖于styleable,styleable只是为了方便attr的使用
- 而通过定义一个styleable,我们可以在R文件里自动生成一个int[],数组里面的int就是定义在styleable里面的attr的id
- 定义一个declare-styleable,在获取属性的时候为我们自动提供了一个属性数组。此外,我觉得使用declare-styleable的方式有利于我们我们把相关的属性组织起来,有一个分组的概念,属性的使用范围更加明确
-
多属性动画
- 构造多个对象Animator,( Animator可以ValueAnimator、ObjectAnimator和AnimatorSet)然后把它们放在一个AnimatorSet中。
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
- 用一个ObjectAnimator对象加多个PropertyValuesHolder:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
- 使用ViewPropertyAnimator
myView.animate().x(50f).y(100f);
- 用一个ObjectAnimator实例,实现View既可以缩小、又能够淡出(3个属性scaleX,scaleY,alpha)
public void rotateyAnimRun(final View view) { ObjectAnimator anim = ObjectAnimator// .ofFloat(view, "zhy", 1.0F, 0.0F)// .setDuration(500);// anim.start(); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 获取当前值 float cVal = (Float) animation.getAnimatedValue(); view.setAlpha(cVal); view.setScaleX(cVal); view.setScaleY(cVal); } }); }
- 构造多个对象Animator,( Animator可以ValueAnimator、ObjectAnimator和AnimatorSet)然后把它们放在一个AnimatorSet中。