android开发小tip与踩过的坑(持续更新)

Posted by alonealice on 2017-07-11

这篇文章主要是记录自己在开发过程中遇到的一些小tip,别看这些东西很细微,有时候可以解决大麻烦,有些对于开发非常重要。还有就是自己曾经踩过的坑,我觉得都有必要记录下来。这些坑虽然不是每个人都会踩(有些确实是自己太笨了才踩的),但是既然我踩了,那可能还会有人踩,这样既惊醒自己,也可帮助他人。文章内容持续更新,也没有具体的分类,遇到一个添加一个。

greendao 生成代码

很多人都用过greendao,集成greendao时,会自动生成DaoMaster和DaoSession等文件,这些文件至关重要。但是前段时间我在集成后竟然无法生成这些文件,找了半天也不知道原因。后来才知道原因:在配置完greendao之后,必须有实体作为greendao的数据实体,如果一个实体都没有,它是不会生成DaoMaster等文件的;同时,greendao目前好像还不支持kotlin,无法将kotlin的数据类作为实体,还是要将实体卸载java文件中。

greendao 主键自增长

greendao可以设置主键自增长,但是我在用时就在想,如果没有没有主键,那么long类型不是为0吗,那还是有值呀。后来在使用时果然出现问题。实际上greendao在使用主键自增长是,主键类型必须是Long,而不是long,这种主键就可以为null了。

service启动activity

当在Service中启动activity时,需要添加

1
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

因为在service中启动的activity是没有栈的,启动时会程序判断当前是否是在service中启动且包含FLAG_ACTIVITY_NEW_TASK。这样启动的activity相当是singleInstance模式。
但是如果你在activity中配置了singleTask模式,就会依旧在task中启动一个。

synchronized使用

synchronized在锁代码块时其实锁的是对象,所以如果你在锁之前完全没有使用该对象,就白锁了。

位运算

1
2
3
<<      :     左移运算符,num << 1,相当于num乘以2(<< 2 相当于乘以4)
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐

位运算比普通的运算块

ImageView scaleType

center:图片居中,不缩放,不放大,超过了就截取中间的,不超过就空着。
centerInside:图片居中,可缩放,不放大,超过了就缩小放置在中间,不超过就空着。
centerCrop:图片居中,可缩放,可放大,超过了就将缩小到有一边与ImageView边相等,不超过放大到最小边与ImageView边相等,保证图片完全充满ImageView
fitCenter:图片居中,可缩放,可放大,缩小放大到自己有一边到ImageView边界为止。
fitXY:可放大,可缩放,将自己完全充满ImageView,可以变形。
fitStart:可放大,可缩放,缩小放大到自己有一边到ImageView边界为止,同时靠左靠上。
fitEnd:可放大,可缩放,缩小放大到自己有一边到ImageView边界为止,同时靠右靠下。

FragmentPagerAdapter和FragmentStatePagerAdapter

FragmentPagerAdapter在滑动时,destroyItem只会将那些不可见的Fragment做detach处理,即只是从屏幕上移除,并不会销毁。同时即使使用notifyDataSetChanged,并且Fragment的数量减少,原来已有的Fragment也不会重新创建。所以这种方法适合item少量的情况。

FragmentStatePagerAdapter在执行destroyItem时,会将那些不可见的Fragment销毁,同时会保存其状态。在再次需要显示的时候回重新创建,并且传入之前的状态。所以这种方式适合item数量比较大时的情况,可以减少fragment的内存,但是在创建和销毁时会有所消耗。

ViewPager+Fragment禁止预加载延迟

当ViewPager.setOffscreenPageLimit(0);当设置预加载的page<1时,程序还是会设置默认的预加载页面为1,所以该方法不可行。
在FragmentPagerAdapter、FragmentStatePagerAdapter的instantiateItem方法中,都有将未显示的fragment设置为不可见状态:fragment.setUserVisibleHint(false);将可见的fragment设置为可见状态:fragment.setUserVisibleHint(true);所以我们只需要在fragment中重写setUserVisibleHint方法,可见时才加载布局。甚至可以使用ViewStub,在可见时初始化整个布局。

ViewPaper直接跳转到后面item

写一个Scroller类继承Scroller,重写其startScroll方法,将其滑动的时间设置为0。

1
2
3
4
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, 0);
}

使用反射的方法,将ViewPager的Scroller替换掉:

1
2
3
4
Class c=viewPager.getClass();
Field field=c.getDeclaredField("mScroller");
field.setAccessible(true);
field.set(viewPager,scroll);

这种方式会使普通的滑动的时间也为0,所以在具体使用时,可以通过滑动的具体情况,在跳转多页时才将时间设置0。

ProGuard的常用语法

-optimizationpasses 5 代码优化次数,android一般为5,理论上越多越好,到时多了混淆时间就长了,而且优化到无法再优化时就停止了,因此不一定是写着的次数
-libraryjars class_path 避免混淆应用的依赖包,如android-support-v4
-keep [,modifier,…] class_specification 不混淆某些类。例子:

1
2
3
-keep public class javax.**
-keep enum com.facebook.**
-keep class com.baidu.** { *; }

-keepclassmembers [,modifier,…] class_specification 不混淆类的成员。例子:

1
2
3
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View); //保持activity类里面的void方法,参数为View的不混淆
}

-keepclasseswithmembers [,modifier,…] class_specification 不混淆类及其成员。例子:

1
2
3
-keepclasseswithmembers class * {  //不混淆有该init方法和该类
public <init>(android.content.Context);
}

-keepnames class_specification 不混淆类及其成员名。例子:

1
2
3
4
5
-keepnames class * implements java.io.Serializable //不混淆实现Serializable接口的类
# parcelable 不被混淆
-keep class * implements android.os.Parcelable {//不混淆实现Parcelable接口的类
public static finalandroid.os.ParcelableCreator *;
}

-keepattributes {attribute_name,…} 不混淆给定的可选属性。例子:

1
2
3
-keepattributes *Annotation*//不混淆注解
-keepattributes Signature//不混淆泛型
-keepattributes SourceFile,LineNumberTable//不混淆异常时的代码行

-keepclassmembernames class_specification 不混淆类的成员名。
-keepclasseswithmembernames class_specification 不混淆类及其成员名

1
2
3
4
-keepclasseswithmembernames class * {  //不混淆自定义View之类的类
public <init>(android.content.Context, android.util.AttributeSet);
}
assumenosideeffects class_specification 假设调用不产生任何影响,在proguard代码优化时会将该调用remove掉。如Log等等

-dontwarn [class_filter] 不提示warnning
-dontusemixedcaseclassnames //混淆时不产生混合大小写的类名
-dontskipnonpubliclibraryclasses //指定不去忽略非公共的库类
-dontpreverify //不预校验
-verbose //显示混淆的log,帮助排错
-optimizations !code/simplification/arithmetic,!field/,!class/merging/ //代码混淆采用的算法,一般不改变,使用谷歌默认算法即可

Jobscheduler使用

在Jobscheduler使用过程中,设置延时setMinimumLatency和设置最大延时setOverrideDeadline,不同手机延时时间可能会出现偏差。比如测试一台sony手机时,延时最小为5s,同时,一旦设置了最大延时则普通的延时时间无效,而如果单设置延时,延时时间也不一定准,而这些在模拟器上则完全没有出现。
一旦设置了这两个延时,则无法再设置重复时间,不然会报错,一旦设置了重启设备任务继续时setPersisted,需要配置系统开机事件的权限。
当onStopJob返回true,或者jobFinished(params, true)时,系统会在满足条件是重新调用onStartJob,可以设置setBackoffCriteria设置等待时间和时间策略,默认等待时间是30s,策略是指数增长。

BroadCast权限问题

1.自定义权限时,名字不是任意命名的,最好是包名.名字的形式(经测试,a.b.名字也是可以的,但是直接名字不行)。
2.定义权限时,需要在发送方和接收方都定义权限和申请权限。

1
2
<uses-permission android:name="c.a.SEND_MESSAGE" />
<permission android:name="c.a.SEND_MESSAGE" />

3.发送带权限的Broadcast时,说明应用要有该权限才能发出这条通知,但是任何普通的receiver都能接收。
4.Receiver配置权限后,说明只有有该权限的其他应用的通知,它才能接收到,但是自己的通知还是可以不传权限收到。
Broadcast其他tip:静态注册的Receiver,要不是单独一个文件的,要不是public static的;StickyBroadcast已经被废弃。

Service进程

service启动时可以设置进程android:process=":web"。当在其他进程启动Service时,如果该Service没有单独设置进程,那么该Service依旧在主进程中。