Android开发随手记
本文是作者在Android开发实践中的随手速记,记录一些小问题的解决方案和注意事项,持续更新。
以下是速记内容,若有不严谨的地方,望小伙伴们指出。
1.Module 不生成R文件,可尝试取消对该Module的引用,
取消后AS会再次编译工程,看看此时能否生成R。
2.Activity theme设置错误时,会出现:
java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams
cannot be cast to com.android.internal.widget.ActionBarOverlayLayout$LayoutParams
此时,需要为Activity/Application指定带ActionBar的系统样式,或让自定义的Theme集成自带ActionBar
的系统样式,当然也可以改用普通Activity类,而非使用Actionbar的Activity子类。
3.java.lang.IllegalArgumentException: NaN is not a valid double value as per JSON
specification. To override this behavior, use GsonBuilder.
serializeSpecialFloatingPointValues() method.
4.AndroidStudio中引用Module时,Module中同名的资源会被App中的资源替换,
利用这一点可以实现自定义资源而不修改Module。目前只试过String.
5.Dagger 环境配置(可解决不能生成Dagger类的情况):http://www.itnose.net/detail/6353446.html
http://stackoverflow.com/questions/29562347/how-do-i-configure-intellij-gradle-to-use-dagger-2-0
6.Android TouchDelegate 只能扩展同一个ViewGroup中的一个View的响应范围。
7.Baidu Map V3.7 MKOfflineMap 下载完成后不可调用destroy方法(也许下载完成后百度在做一些善后事宜),
否则离线地图可能无法正常使用。另外此版本的MKOLUpdateElement.status永远为1,不可靠~
8.ART JNI、GC http://developer.android.com/guide/practices/verifying-apps-art.html
9.IDEA更新JDK至1.8(From1.7),编译代码时出现:
Error:(18, 6) java: -source 1.3 中不支持注释
(请使用 -source 5 或更高版本以启用注释)
打开Project Structure,在Project栏中找到Project language level,修改成1.3以上版本即可。
10.当为Activity写了Portrait和LandScape两种布局时,在布局文件根节点上添加Tag值,在运行时读取可区分Portrait和LandScape。
11.打开应用,使用adb shell dumpsys activity top,可以查看应用Activity的信息,也可以获取包名。
12.启动Activity的时候,设置Flag,Intent.setFlags(Intent.Flag_Activity_New_Task|Intent.Flag_Activity_Clear_Task)可以清空BackStack。
13.Context.createPackageContext(pkgName,flags)可以根据包名创建另一个Application的context,前提是二者的shareUserId和Signature要相同,这样就
两个应用就会运行在同一个Process中,主应用就可以访问附属应用的Resources和Classloader(context.getClassloader.load(className)).安装在设备中的每一个apk程序,Android系统会给其
分配一个单独的用户空间,其中android:shareUserId就是对应一个Linux用户ID,并且为它创建一个沙箱,以防止与其它应用程序产生影响。用户ID
在应用程序被安装到设备中时分配。通过SharedUserid,拥有同一个Userid的多个APK可以配置成运行在同一个进程中,所以默认就是可以互相访问任
意数据,也可以配置成运行在不同的进程中, 同时可以访问其APK的数据目录下的资源(图片,数据库和文件),就像访问本程序的数据一样。
14.Activity View 层级:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0331/1608.html
15.动态加载机制:http://blog.csdn.net/jiangwei0910410003/article/details/17679823
16.继承的坏处:Super方法里引用了可被子类复写的方法时,该方法也会被子类改写(调用Super.method也无效)。
17.Android 6.0 File.mkdirs()无效,是因为权限控制,可动态申请权限:http://stackoverflow.com/questions/32225506/android-6m-permission-issue-create-directory-not-working
18.为一个Resources创建LayoutInflater用于inflate该Resource中的layout,需要Extend ContextThemeWrapper实现一个ResourcesContext,
然后使用 LayoutInflater.from(Context).cloneInContext(ResourceContext);
19.ListView Item中的可点击控件
20.使用Navigator来导航可以避免Activity相互引用,在Activity中写获取启动它的Intent的方法可以避免对外定义传参Key。
21.使用android.R.id.content可以获取Activity的根元素。
22.Java包内外层需要互相引用,没有内层可以直接使用外层或者外层可以直接使用内层之说。
23.在Java7之前,switch只能支持 byte、short、char、int或者其对应的封装类以及Enum类型。在Java7中,支持了String。
24.解决动态加载SurfaceView闪屏问题的两种方式:一,getWindow().setFormat(PixelFormat.TRANSLUCENT); 二,在布局中添加一个
不可见的SurfaceView。
25.使用Fragment.onActivityCreated()来retrieve view和restore view state。
26.Android 通过Intent.setComponent(new ComponentName(PkgName,ClassName))可以启动另一个Apk的Activity;通过new
DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)可加载执行Jar/Apk中的代码。
27.ScrollView中内容不足以填充到全屏时,需要添加android:fillViewport="true"来让ScrollView填满全屏。
28.Apk的Resource ID信息没有放在classes.dex中,放在Resources.arsc中?? See:http://blog.csdn.net/luoshengyang/article/details/8744683
和http://blog.csdn.net/luoshengyang/article/details/8806798
29.Android浮点运算:嵌入式处理器通常没有支持浮点运算,所有对"float"和"double"的运算都是通过软件
实现的。一些基本的浮点运算,甚至需要毫秒级的时间才能完成。甚至是整数,一些芯片有对乘法的硬件支
持而缺少对除法的支持。这种情况下,整数的除法和取模运算也是由软件来完成的。所以当你在使用哈希表
或者做大量数学运算时一定要小心谨慎。
30.Git 只clone一个分支:git clone -b <branch> <repo>
31.Java 1.5开始支持静态引用,可以直接引用静态方法,省略调用静态方法时需要写类名的工作。使用 import static <class>.* 可引入所有静态方法。
32.Android Activity中 getApplicationContext!=getBaseContext(),后者是为每个Activity重新创建的一个ContextImpl,Activity本身是一个
ContextThemeWrapper,它依靠ContextImpl来完成实际的工作。
33.Thread的looper和MainThread的MainLooper都是直接new出来的,本质上没有区别,启用了Looper的Thread创建的Handler可以执行UI操作
,但是这里的操作如果耗时太久的话(如sleep>100ms),容易崩溃(待验证,当耗时操作在前易崩溃)。
34.ActivityManager.getRunningTasks(1).get(0).topActivity()可获取前台应用的Activity componentName。
35.如果使用Application的Context来bindService,Service就会跟Application的生命周期一样,而不会受Activity的生命周期影响。如果是用某个
Activity的Context来绑定Service,这个Service的生命周期就受到这个Activity(而非App的其他Activity)的生命周期影响,并且无需调用UnbindService。
36.Exception raised during rendering: com/android/util/PropertiesMap (Details),在渲染预览界面更改渲染工具版本即可。
37.AndroidStudio Ignore Files:
.idea
.gradle
build/
local.properties
*.iml
38.Android 切图中不规则的图片(两头纯圆按钮)或纯规则图片(纯圆,正方形?)无法做.9拉伸,做成Bitmapdrawable(<bitamp/>)可以解决。
39.Android添加悬浮窗显示,需要使用Application的Context,如若不能显示在所有应用上层,则需使用Service?
40.SurfaceView黑屏:(1)getWindow().setFormat(PixelFormat.TRANSLUCENT);
41.Gson序列化时排除字段:http://www.tuicool.com/articles/v2eIrqz
42.引入关联表来关联两个表之间的关系。
43.Gson 转换List<T>时,需要使用new TypeToken<List<String>>(){}.getType(),特别注意TypeToken后面的大括号。
44.以文件夹方式隔离用户数据,将用户数据(包括数据库)都放到带用户信息的文件夹下面。
45.Android 无法实现不影响后面操作的悬浮操作?
46.使用wait()和notify()时一般是为了同步线程,但是要注意,如果所调用的方法没有开启异步线程,可能会直接返回,这时候在方法调用之后
使用的wait()就会一直等待异步回调来notify,然而回调其实早就已经在方法栈中发生,这样就会导致线程一直等待。如果开启了异步线程,那wait()一定会等到notify的到来。
47.Java 使用泛型时,支持自动以泛型的具体类型来命名参数,只要让泛型对应的参数名与泛型名一样(不区分大小写)就可以做到。
48.Sqlite 查询为空的字段 Field IS NULL, 查询不为空的字段 Field IS NOT NULL。
49.ImageView tint in xml and setColorFilter 可以对图片进行着色,可用于改变图标的颜色,使用PorterDuff.Mode.CLEAR时,还可以隐藏绘制内容。
50.FileOutputStream.getChannel().lock() 可以获得一个FileLock以锁定文件。
51.解决Gradle DSL method not found: ‘android()’:删除Project的build.gradle文件中的:android{}即可。See:http://www.hloong.com/?p=100
52.ListView 的Item中有可点击元素时,需将可点击元素的Focusable和FocusableInTouchMode属性设置为false,并为ListView设置android:descendantFocusability="blocksDescendants"
这样按钮和Item都可以点击。
53.Root 原理:http://www.myhack58.com/Article/html/3/92/2013/36574.htm
54.requestWindowFeature()用来动态的决定Window的特性,而这个行为发生在setContentView之前,所以必须要在setContentView之前设置。如request
WindowFeature(Window.FEATURE_NO_TITLE)之后,Window中的DecorView就不会生成TitleView部分,而只生成ContentView部分。
55.Android Style中使用自定义View属性时,不用加任何命名空间(如android:xxx),直接使用属性名即可
56.PopupWindow点击外部消失,只需设置background即可: setBackgroundDrawable(new BitmapDrawable())
57.Sqlite不可在事务中开启另一个事务,否则这个事务操作无效。
58.new SimpleDateFormat("yyyy-M-d hh:mm").format(date)可以去0。
59.View 在绑定后,从View树中移除后再添加,绑定的数据依然有效。
60.EditText设置inputType="numberSigned"时可以输入负数。
61. <item name="android:imeOptions">flagNoExtractUi</item>可禁止输入法开新界面全屏输入
62.Sqlite 关联查询:SELECT A.*,B.name FROM Task AS A ,Project AS B WHERE A.Status<='2'
63.Adapter.getItemViewType()返回的Type范围应该是(0,ItemViewTypeCount]
64.Fatal signal 11 (SIGSEGV), code 0, fault addr 0x61b6 in tid 25110 (RenderThread) 跟硬件加速有关,避免设置
View.setLayerType(View.LAYER_TYPE_HARDWARE, null),出现问题时可在Manifest中禁用硬件加速:<android:hardwareAccelerated="false">
65.NDK_PROJECT_PATH=null,在app/build.gradle的android{}中添加
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir 'src/main/libs'
}
即可解决。
66.计算大数值时容易溢出,最好转换为大数值类型计算:如long EXPIRE_LOGIN =90* 24 * 60 * 60 * 1000 结果为负数,应改为:
long EXPIRE_LOGIN =90L* 24 * 60 * 60 * 1000;
67.fitsSystemWindows实现沉浸式:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1122/3712.html
68.app:layout_behavior="@string/bottom_sheet_behavior"
69.Application 中的OnTrimMemory用于系统恢复内存,此时可将一些不重要的内存缓存释放掉。
70.ImageView adjust to Image,user adjustViewBounds=true and maxWidth/maxHeight of ImageView.
71.<item name="android:windowDrawsSystemBarBackgrounds">false</item> 控制内容是否渲染到底部栏下面
72.Sqlite DISK IOERROR WRITE,是因为临时目录问题,可以通过设置之解决问题:
http://blog.csdn.net/u011453773/article/details/50731331
73.Android 调用系统分享:http://stackoverflow.com/questions/30518321/on-android-m-how-to-configure-the-direct-share-capabilities-image-text-an/30721038#30721038
74.获取Android 的ContentView的容器可以通过它的ID: Window.ID_ANDROID_CONTENT。
75.Java中Object的hashcode返回一个int的hash值。默认情况下,Object中
的hashCode() 返回对象的32位jvm内存地址。也就是说如果对象不重写该方
法,则返回相应对象的32为JVM内存地址。equals方法返回true的两个Objec
t应该返回相同的hashcode。所以一般Object的子类在是想equals方法和hashcode
方法的时候,要么同时实现,要么都不实现。这对于键值对使用时应该很关
键。重写equals和hashcode时,可以使用IDE提供的方法,安全高效。参考:
https://www.oschina.net/question/82993_75533 同时注意ORM的情况。
76.Collections.synchronizedXXX(),是对所有的传入对象加了同步锁。
77.HashMap 操作都是通过数组找链表头结点,key用于hash后找到数组index。
它的containskey也会通过key的hash找到对应链表头结点,然后遍历,但是containsValue因为不知道
key,无法hash找到index,所以是按数组顺序依次遍历每一个链表查找,效率较低。
78.AndroidStudio 编译报错: Error:java.lang.NullPointerException
(no error message),删除工程目录下.gradle文件,重启就好。
79.GreenDao局限性:不能兼容模型的继承关系,无法将父类的字段生成数据库字段,如果父类和子类都有
都建了表,Dao之间没有继承关系。此时可能考虑合并连个表。
80.serialVersionUID 的意义:http://lenjey.iteye.com/blog/513736
81.ScrollView 嵌套GridView或者ListView,可以重写onMeasure方法,设置
测量高度为最大值,测量模式为AT_MOST,实现跟随ScrollView滚动。此时如果
遇到布局自动滚动到GridView所在位置,禁用GridView focus即可。
82.HttpUrlConnection 出现EOFException,据说是当响应的InputStream 是 GZIPInputStream时,会造成 HTTP HEAD 的冲突,此处应该是个Bug,原因可以参考以下网址:
https://code.google.com/p/android/issues/detail?id=24672 , 可以通过设置 connection.setRequestProperty( "Accept-Encoding", "" )解决.
83.RecyclerView的Item布局中不能直接使用View,需使用具体的View。否则可能会报CreateViewFromTag Nullpointer。
84.在返回集合的方法中,不建议返回null,而是返回Collections.emptyxxx。
85.List泛型参数的子类和父类的转化,可以先将待转换的类转换为泛型集合,然后赋值给接收者,如A和B ,
其中 B extends A,需要list<B> 转换为list<A>时 , 可以这样:
List list=list<b>;
List<A> receiver=list;
也可以直接
用List.class.cast()方法,如:
List<A> receiver=List.class.cast(list<B>);
86.Seekbar滑块偏上时,同时加上maxHeight和minHeight可以解决,他们是用来指定进度背景高度的。
87.AdapterView中View的状态不变化时(应用selector),和处理点击控件一样处理即可,关闭focusablity,加上FocusblockDesendants.
88.PNG使用Bitmap.compress压缩质量时会忽略quality,建议使用webp。
89.四种方式设置Activity跳转动画:http://blog.csdn.net/qq_23547831/article/details/51821159
90.XListView 需要设置Adapter才能显示Header,Footer和上下拉动
91. Dex cannot parse version 52 byte code,需要在App gradle 的android{...}中添加:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
如果有Jack错误,还需要在defaultConfig{...}中添加 jackOptions { enabled true }
92.Collections.synchronizedList中所有方法都加了synchronize(mutex){...}同步,除了访问iterater的方法,需要手动加锁,请看他们的实现如下:
public ListIterator<E> listIterator() { return list.listIterator(); // Must be manually synched by user } public ListIterator<E> listIterator(int index) { return list.listIterator(index); // Must be manually synched by user }
93.用Gradle为UMeng渠道打包首先你必须在AndroidManifest.xml中的meta-data修改以下的样子:
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_VALUE}" />
然后再App的build.gradle的android{}中添加productFlavors {
YingYongBao {} HuaWei{} productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } }
注意其中渠道名不能以数字开头。也可以使用第三方PackNG打包,不过它的渠道没有写到Manifest中,如果二次签名会导致丢失。
也可以参考其他不错的多渠道打包工具,如腾讯企鹅电竞开源的Vasdolly,美团的Walle。
94.Gradle读取local.properties文件,如下读取签名配置信息
Properties properties = new Properties() InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() ; properties.load( inputStream ) //读取文件 def sdkDir = properties.getProperty('key.file') storeFile file( sdkDir ) //读取字段 def key_keyAlias = properties.getProperty( 'keyAlias' ) def key_keyPassword = properties.getProperty( 'keyPassword' ) ; def key_storePassword = properties.getProperty( 'storePassword' ) ; storePassword key_storePassword keyAlias key_keyAlias keyPassword key_keyPassword
95.LinearLayout比例布局时,不包含0的weight时,使用0dp才会准确。
96.出现 Bitmap too large to be uploaded into a texture错误时,是因为启用硬件加速后,无法显示较大的图片,解决方案三种:
(1)Android:hardwareAccelerated="false"关闭加速
(2)加载图片时进行压缩
(3)把大图分割成小块加载显示图片切片
97.Java 修改常量值,对于基本类型的常量会失效,因为Java编译优化,对于基本类型的静态常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。
98.Android 录屏命令:adb shell screenrecord /sdcard/demo.mp4
99.synchronized不能用在接口方法和抽象方法的声明上,但是可以用在实现上。
100.Realm跨平台的高效数据库方案。
101.CAS(Compare And Swap):一种在多线程环境中实现同步的CPU原子指令。它在向内存区域写入值时,会先比较该内存区域现有值,如果值和原先读到的值不同,
则写入失败。这是通过一个原子操作来完成的。原子性保证了值的时效性(最新)。 如果这个值已经被另一个线程更新,那么写入将会失败。这个操作必须表明值替换
是否成功:要么用一个Bool表示,要么返回内存区域现有值(有点儿像乐观锁)
102.子线程也可以更新UI:http://www.cnblogs.com/lao-liang/p/5108745.html
103.git初始化没有branch时,可以先创建一个文件并commit,然后就会在本地生成master分支,此时push上去就好了。
104.Android style中设置版本兼容的属性时,需要去掉android:前缀,否则可能无效。
105.Java中遍历访问,for是最快的,其次是foreach,再次是Iterator。有趣的是,使用Iterator时,编译后的class是for循环,例如:
for(Iterator iterator = collection.iterator(); iterator.hasNext(); iterator.remove()) { ... }
106.将WebView嵌套在LinearLayout中使用,有可能加载不了使用Angular或者Vue写的页面,最好使用RelativeLayout作为容器.
107.Android M及以上版本,可以使用Settings.canDrawOverlay()判断是否有悬浮窗权限:
private void checkOverlayPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { // SYSTEM_ALERT_WINDOW permission not granted... } } } }
108.编译时出现 ...NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin...等NDK过时等错误时,需在
gradle.properties中添加
android.useDeprecatedNdk=true
109.应用在64位手机上加载.so报缺少库错误或者....so is 32-bit instead of 64-bit时(在已有Android项目集成react native也会出现该异常),是因为项目中64位库不完整,此时需要排除64位库,让应用全部加载32位库即可。过滤库
需要在app/build.gradle中添加:
ndk { // 此处添加要保留的ABI abiFilters "armeabi-v7a", "x86", 'armeabi' } packagingOptions { // 此处排除掉64的库 exclude "lib/arm64-v8a/libxxx.so" }
110.TextView 粗体切换:
//android中为textview动态设置字体为粗体 TextView textView = (TextView)findViewById(R.id.textView); textView .setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); //设置不为加粗 textView.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
111.Gradle全局统一管理依赖,在项目根目录创建config.gradle文件,在ext中定义依赖和版本:
ext{ android=[ ... compileSdkVersion:23 ... ] dependencies=[ ... "support-v4":'com.android.support:support-v4:23.1.1' ... ] }
然后在项目的build.gradle中添加apply from :“config.gradle”,在app的build.gradle中即可引用配置:
... compileSdkVersion rootProject.ext.android.compileSdkVersion compile rootProject.ext.dependencies["support-v4"] ...
也可以在app的build.gradle中通过ext定义相关配置,然后引用,相当方便。
112.IDA Pro 可将.so反编译为汇编,如果配上F5插件,选中方法后按F5可以查看对应方法的C/C++源码。
113.AndroidStudio3.0以下(Gradle3.0以下)中做JNI开发,gradle.properties中添加android.useDeprecatedNdk=true,不需要写MakeFile,而是直接在build.gradle中配置,如配置引用log库:
defaultConfig { ndk { ldLibs "log" moduleName "myLib" //生成的so名字 abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。 } }
AS3.0(Gradle3.0)以上,android.useDeprecatedNdk=true已被弃用。需要使用CMake,在CMakeLists.txt中配置(CMakeLists.txt相当于MakeFile),在Build.gradle中:
defaultConfig{ // 使用Cmake工具 externalNativeBuild { cmake { cppFlags "" //生成多个版本的so文件 abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64' } } }
还需要在android节点中配置使用CMakeLists.txt:
// 配置CMakeLists.txt路径 externalNativeBuild { cmake { path "CMakeLists.txt" // 设置所要编写的c源码位置,以及编译后so文件的名字 } }
详见:http://www.jb51.net/article/129667.htm
114.查看设备支持的ABI,需要使用adb shell 然后从属性中读取:
getprop | grep abilist //查看APK支持的ABI,使用aapt: ~ aapt dump badging sample.apk | grep abi //代码中通过Build.SUPPORTED_ABIS或者手工读取设备属性也可以查看设备支持的ABI。
115.AndroidStudio下载SDK源码后无法关联,需要在 用户/用户名/.AndroidStudio版本号/config/options/jdk.table.xml中编辑对应SDK版本的sourcePath
属性,指向源码路径,然后重启AS即可。
116.Android JNI内存分配不受系统对单个应用的内存限制制约,可以考虑通过JNI来做需要临时申请大内存的事情。如加载大图片。
117.当我们从Launcher启动一个应用程序时,实际的是在这个应用程序中Action和Category分别被配置为MAIN和LAUNCHER的Activity。
这个Activity最终由ActivityManagerService通知其所在的进程进行启动工作的,也就是通过ApplicationThread类的成员函数scheduleLaunchActivity开始执行
启动工作的。其它类型的组件的启动过程也是类似的。
118.Result不能跨Task返回。
119.Application,Activity,Service都直接或间接继承自ContextWrapper,都会创建自己的Base Context,所以他们的Activity、Service与Application的BaseContext
是不同的,不过 getApplication()==getApplicationContext();
120.Android 5.0启动或bind服务的Intent只支持显式声明了,即依然支持用类名声明,但是使用Action声明时,一定要配上PackageName,否则报IllegalArgumentException。
121.正如在Logcat中所见,Android中的进程是以包名命名的,子进程则会追加process指定的名字。判断进程是否是主进程就可以根据进程名判断:
public static String getProcessName(Context context, int processId) { ActivityManager manager = (ActivityManager)context.getSystemService("activity"); List<RunningAppProcessInfo> processList = manager.getRunningAppProcesses(); if(processList != null && processList.size() > 0) { Iterator var4 = manager.getRunningAppProcesses().iterator(); while(var4.hasNext()) { RunningAppProcessInfo process = (RunningAppProcessInfo)var4.next(); if(process.pid == processId) { return process.processName; } } } return ""; }
122.Android HoneyComb(3.2)以下对SharedPreferences支持的Context.MODE_MULTI_PROCESS是通过监测文件变化重新加载实现的:
if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. sp.startReloadIfChangedUnexpectedly(); }
123.J2ME中的getClass().getResourceAsStream在Android中同样适用,但是需要注意资源存放路径必须同类文件在一起(也即常用的SRC目录下。原理相当于类文件加载)。
Bitmap bitmap = BitmapFactory.decodeStream(getClass().getResourceAsStream("/res/drawable/ic_launcher.png")); Drawable drawable = new BitmapDrawable(getResources(), bitmap); //但是不能加载drawable-xhdpi这样的目录下的资源。
124.实现View的拖动,可以在OnTouchEvent中改变View的LayoutParams,或者调用View的layout方法,还可以使用ViewDragHelper帮助类。
125.Android WakeLock支持四种模式:
//Flag Value CPU Screen Keyboard PARTIAL_WAKE_LOCK On* Off Off SCREEN_DIM_WAKE_LOCK On Dim Off SCREEN_BRIGHT_WAKE_LOCK On Bright Off FULL_WAKE_LOCK On Bright Bright PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TimerService.class.getName()); wakeLock.acquire();
126.华为系列手机上对未在白名单中的应用限制最大广播注册数500,超过则报错java.lang.AssertionError:Register too many Broadcast Receivers,
可通过反射将报名加入到其白名单中。See:https://blog.csdn.net/llew2011/article/details/79054457
127.自定义Gradle plugin,使用Javassist(一个面向Java API编程的字节码操作库)修复第三方SDK代码问题:https://blog.csdn.net/llew2011/article/details/78540911
128.ThreadLocal的神奇之处在于,它会自动依附于创建它的线程,就算是声明为静态的,依然不会被类共享或者说覆盖,每个线程访问到的依然是自己的
定义,这也是它最大的作用――通过它,每个线程访问同一个ThreadLocal变量得到的是自己的定义。比如Looper的实现中,通过它为每个线程定义了
Looper引用:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
129.Library中使用ButterKnife,需要在项目的Build.gradle中添加
buildscript { repositories { jcenter() mavenCentral() } dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' } } //同时在Library的Build.gradle中添加 dependencies { compile 'com.jakewharton:butterknife:8.4.0' apt 'com.jakewharton:butterknife-compiler:8.4.0' }
然后在Library的注解中引用id时,要使用R2.id.xx 而非R.id.xx,当然代码中使用时还得用R.id.xx
130.排除第三方库中的库引用:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") { exclude module: 'support-v4'//根据组件名排除 exclude group: 'android.support.v4'//根据包名排除 } }
131.Gradle upload task
ext { PUBLISH_GROUP_ID = 'cn.dev' PUBLISH_ARTIFACT_ID = 'core' PUBLISH_VERSION = android.defaultConfig.versionName } uploadArchives { repositories.mavenDeployer { def deployPath = file(getProperty('aar.deployPath')) repository(url: "file://${deployPath.absolutePath}") pom.project { groupId project.PUBLISH_GROUP_ID artifactId project.PUBLISH_ARTIFACT_ID version project.PUBLISH_VERSION } } }
132.IDEA Project Structer没有添加NDK Path的地方,直接在local.properties中添加NDK路径即可。
133.View 不能设置颜色selector为background,否则运行时会报Error inflating错误。设置单个颜色为background没有问题。
134.(实际验证在xml Preview时生效,在设备上无效,layer-list不能达到缩小icon的目的)通过layer-list可以将已有Icon缩小到指定尺寸,如果有大版本的Icon,需要小Icon时就不用切图占用空间了,用来在TextView上设置图标时很有用。
135.RelativeLayout,ConstraintLayout等Flat布局容器在引用依赖anchor时,根据child index递增不能倒序依赖未声明的id,即不能使用@id/xx,但是可以使用@+id/xx,否则编译不过。
136.自定义属性时,attrs中可以定义取值类型为enum和flags,二者区别在于enum取值唯一,flags则可以按位或,设置多个值。
137.Merge可以作用于任意的布局(而不仅限于FrameLayout),用于减少多余的相同的容器。
138.不同的编译版本使用不同的资源,可以分别为不同版本在src中建立文件目录。默认已有src/main,可以建立src/debug对应debug编译版本。(验证代码、applicationid)
139.使用CardView可以对(圆角)矩形背景增加阴影效果
140.MD 按钮点击效果,即波纹效果,将View的背景设置为"?android:attr/selectableItemBackground"或者"?android:attr/selectableItemBackgroundBorderless"即可。也可以使用ripple节点自定义drawable作为背景实现该效果。
141.View 缩放后原点(即左上角的坐标点)会发生变化,计算新的原点应该偏移掉缩小的部分的二分之一(比例应该根据动画中心点计算)。
142.RecyclerView利用payload可以对一个Item做局部刷新,payload用开发者自己定义,指定不同的payload应该更新的部分,然后在onBindViewHolder(ViewHolderholder, int position, List<Object> payloads)中处理:
@Override public void onBindViewHolder(ViewHolderholder, int position, List<Object> payloads) { if (payloads.isEmpty()) { // payloads 为 空,说明是更新整个 ViewHolder onBindViewHolder(holder, position); } else { int type= (int) payloads.get(0);// 刷新哪个部分 标志位 switch(type){ case 0: contact.userName.setText(mList.get(position).getName());//只刷新userName break; case 1: contact.userId.setText(mList.get(position).getId());//只刷新userId break; case 2: contact.userImg.setImageResources(mList.get(position).getImg());//只刷新userImg break; } } }
143.在onTouch(View v, MotionEvent event)中,event的getX()和get()容易受AXIS_X(Touch中心的屏幕X坐标位置)和AXIS_Y(Touch中心得屏幕Y坐标位置)的影响发生抖动,特别在平移和缩放View时容易出现,此时使用getRawX()和getRawY()可以避免。
144.TransitionDrawable 可以快捷实现两张图片的渐变显示:第一张图片渐隐,第二张图片渐显,不需要在最后同时显示时,设置setCrossFadeEnabled(true)即可。
145.FragmentManager保存状态,View/Fragment保存状态(OnSaveInstanceState())。
(1)Activity会在容易被销毁时调用OnSaveInstanceState(),如应用退回到后台,Activity启动了新的Activity,横竖屏切换等;
(2)使用RatainedFragment,即在Fragment的onCreate()中调用setRetainInstance(true),可以保存Fragment,避免在ConfigChanged过程中重建,跳过onCreate()和onDestroy()。同时还可以用一个NonUI的Fragment来
保存如缓存、线程等无需重建的数据或资源。(See:http://www.cnblogs.com/kissazi2/p/4116456.html)
(3)View.onSaveInstanceState()可以用来保存View的临时状态,如TextView的当前光标位置,ListView的select position等。
146.Sqlite attach 将其他数据库附加到当前数据库链接,可实现跨库访问和操作:http://www.sqlitetutorial.net/sqlite-attach-database/
db.execSQL("ATTACH DATABASE '" + DB_PATH + "' AS " + DB_ALIAS);
newDB.execSQL("DETACH DATABASE " + DB_ALIAS);
147.Sql语句中,REPLACE INTO 实现 insert or update 功能。
148.PRAGMA table_info(TABLE_NAME) 可以返回表结构,官文描述;
// This pragma returns one row for each column in the named table. Columns in the result set include the column name, data type, whether or not the column can be NULL, and the default value for the column. The "pk" column in the result set is zero for columns that are not part of the primary key, and is the index of the column in the primary key for columns that are part of the primary key.
//
// The table named in the table_info pragma can also be a view.
对于Attach的数据库,需要这样调用:
PRAGMA DB_ALIAS.table_info(TABLE_NAME)
149.GreenDao支持自定义类型转换,表中可定义自定义类型的Column,添加并实现Converter转换成Sqlite类型即可,可参考:http://greenrobot.org/greendao/documentation/custom-types/
@Convert(converter = RoleConverter.class, columnType = String.class) private Role role;
150.View.getHitRect()可以返回View相对于Parent的响应区域,可用于判断MotionEvent是否发生在指定的View上。
151.Glide返回的Bitmap是Immutable bitmap,不能随意释放,也不能用于做高斯模糊,如果需要,可以创建一个图片副本再做效果。
152.GreenDao中StringCondition用来支持更为复杂的查询,如比较两个字段的值,下面的例子比较消息读取时间和消息接收时间:
queryBuilder.where(new WhereCondition.StringCondition(ConversationDao.Properties.LastReadAt.columnName + " < " + ConversationDao.Properties.LastRecvAt.columnName));
153.AsyncTask的任务队列最多支持128个任务:
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
154.android:process作用于application时,可以为应用(的所有组件)指定一个进程来运行。作用于四大组件时,组件会在指定的进程中运行。它的取值也需要注意,当以":"开头时,应用将会为指定的组件创建私有进程;当以一个小写字母开头时,将会使用以这个命名的全局进程来运行对应的组件,这样可以允许多个应用使用同一个进程已达到减少资源使用的效果(前提是这些应用共享相同的 Linux 用户 ID 并使用相同的证书进行签署)。适用于SDK开发时使用。
155.android:multiprocess,可作用于activity和provider,用于赋予组件在使用到它的其他进程创建实例的能力。通常用于provider,使得在使用到的进程创建Provider实例而避免IPC操作。
156.ViewPager remove item 或者改变Item内部状态后因为View缓存不会实时更新设置显示异常,最好的办法是重写PagerAdapter的getItemPosition方法,对于需要更新的
Item返回PagerAdapter.POSITION_NONE,会促使ViewPager在notifyDatasetChange时为该元素重新生成View,其他元素返回其更新后的位置,为了对效率进行优化,可以在Item数据中缓存position,并在改变是更新,这样在getItemPosition中就可以直接进行返回。对于动态内容,如媒体播放,在remove后需要重新触发一次播放。(如果Remove发生在PageChange一秒之内,页面可能体验不好,可以用Delay来修复)Snippet:
private void removeModel(final BaseMomentPlayModel model) { ... List<BaseMomentPlayModel> list = mMomentList; list.remove(model); int size = list.size(); int position = model.position; for (; position < size; position++) { //Update position. list.get(position).position = position; } //Mark as changed. model.position = PagerAdapter.POSITION_NONE; if (mMoment == model) { mMoment = null; mViewPager.post(new Runnable() { @Override public void run() { //Restart playback setCurrentPage(mViewPager.getCurrentItem(), false); } }); } reattachAdapter(); } @Override public int getItemPosition(@NonNull Object object) { View view = (View) object; BaseMomentPlayModel model = (BaseMomentPlayModel) view.getTag(); return model.position; }
157.用MessageQueue可以实现懒加载方式,封装需要懒执行的任务到IdleHandler,添加到MessageQueue,在空闲的时候便会执行
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { //Do something to be lazily done. return false; } });