框架,工具,流程
业余时间总结学习
代码框架总结
1、 可以将activity中基类保存每一个子类的Context,然后通过Application中的函数负责保存,任意时候只要调用Application的提取函数就可以了。
2、 OnactiityResult OnPermissionResult回调接口可以放在基类里面,共同使用,减少各个是Activity的代码冗余。
Android 技能总结大全
1、性能优化
2、前延技术
3、 语言进阶android底层实现
4、 知识架构图
NestedScrollView RecyclerView 冲突问题解决。
想必大家遇到过在同一个界面中,要展示的内容非常多,完全展示出来高度远超过屏幕高度,这时候通常的做法是布局中使用ScrollView进行嵌套,但如果遇到ScrollView嵌套中包含一个或者多个RecyclerView列表的话,还得自定义RecyclerView以解决滑动冲突。那么今天我们这里有一个现成的控件NestedScrollView,用他替代ScrollView,并且不用自定义RecyclerView等列表控件来解决滑动冲突。因为他的内部已经帮我们解决了子View的滑动冲突。
include、merge、viewstub
标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。多用于替换FrameLayout或者当一个布局包含另一个时,标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用标签优化。
标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用标签,以减少内存使用量,加快渲染速度。是一个不可见的,大小为0的View。标签使用如下:
1) include 开发使用注意
include标签使用注意点:
1,标签当中,可以重写所有layout属性的,如上面include中指定的layout属性将会覆盖掉titlebar中指定的layout属性。
而非layout属性则无法在标签当中进行覆写。另外需要注意的是,如果我们想要在标签当中覆写layout属性,
必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写效果将不会生效
2,一个xml布局文件有多个include标签需要设置ID,才能找到相应子View的控件,否则只能找到第一个include的layout布局,以及该布局的控件。
3,如果我们给include所加载的layout布局的根容器设置了id属性,也在include标签中设置了id属性,同时需要在代码中获取根容器的控件对象
时,最好将这两个id设置相同的名称!否则,可能获取不到根容器对象,即为null。
2) merge开发使用注意
1、 用法描述,一个父亲视图(linerlayout+textview+include),包含一个子视图(linerlayout+button),这样会导致多一层include中的linerlayout,可以将子视图中的根节点linerlayout替换成merge减少嵌套曾。
2、 1,根布局是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一个.
3、 2,因为merge标签并不是View,所以在通过LayoutInflate.inflate()方法渲染的时候,第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点.由于merge不是View所以对merge标签设置的所有属性都是无效的.
4、 3,merge标签必须使用在根布局,并且ViewStub标签中的layout布局不能使用merge标签.
3) viewstub 使用注意
1,ViewStub标签不支持merge标签。因此这有可能导致加载出来的布局存在着多余的嵌套结构,具体如何去取舍就要根据各自的实际情况来决定了。
2,ViewStub的inflate只能被调用一次,第二次调用会抛出异常。
3,虽然ViewStub是不占用任何空间的,但是每个布局都必须要指定layout_width和layout_height属性,否则运行就会报错。
1 Android手机目前常见的分辨率
1.1 手机常见分辨率:
4:3
VGA 640480 (Video Graphics Array)
QVGA 320240 (Quarter VGA)
HVGA 480320 (Half-size VGA)
SVGA 800600 (Super VGA)
5:3
WVGA 800480 (Wide VGA)
16:9
FWVGA 854480 (Full Wide VGA)
HD 19201080 High Definition
QHD 960540
720p 1280720 标清
1080p 19201080 高清
手机:
iphone 4/4s 960640 (3:2)
iphone5 1136640
小米1 854480(FWVGA)
小米2 1280720
1.2 分辨率对应DPI
“HVGA mdpi”
"WVGA hdpi "
"FWVGA hdpi "
"QHD hdpi "
“720P xhdpi”
"1080P xxhdpi "
2 屏幕适配的注意事项
2.1 基本设置
2.1.1 AndroidManifest.xml设置
在中Menifest中添加子元素
android:anyDensity="true"时,应用程序安装在不同密度的终端上时,程序会分别加载xxhdpi、xhdpi、hdpi、mdpi、ldpi文件夹中的资源。
相反,如果设为false,即使在文件夹下拥有相同资源,应用不会自动地去相应文件夹下寻找资源:
Android笔记–Android Studio 引用第三方开源类okhttp、gson库时的打包混淆
2016年03月11日 14:26:18
阅读数:2560
在工程下找到proguard-rules.pro文件下:
-dontwarn
#okhttp混淆配置
-keep class com.squareup.okhttp.** { ;}
-dontwarn com.squareup.okhttp.*
-dontwarn okio.**
#Gson混淆配置
-keepattributes Signature
-keepattributes Annotation
-keep class sun.misc.Unsafe { ; }
-keep class com.google.gson.examples.android.model.* { *; }
好了。
XML 编辑布局使用标签大写
今天在开发过程中,写好界面之后编译运行然后闪退,然后就报错“caused by NullPointerException: Attempt to invoke virtual method ‘boolean java.lang.String.equals(java.lang.Object)’ on a null object reference”。直接在启动activity的onCreate函数周期加载布局的时候直接崩掉了,肯定是布局的问题,然后一遍一遍的看布局哪里有问题,最后发现 :
原来是XML文件中,为了加一条横线的效果 , 写成小写的 了。
编译期和布局文件中没有提示错误,运行的时候会直接报错闪退。
运算符优先级
Java中的运算符按优先级从高到低排列如下:[ ] ( ) ++ – ! ~ instanceof * / % + - << >> >>> <> < = > \ == != &^& & || ? := op= 。
选择系统文件
QRCodeCreateAndScanTool-master1 中包含选择系统文件的功能。
1、 在调用Camera进行拍照的时候,sdk 版本不能设置太高,需要为21,27会打开摄像机失败。
App 崩溃信息统计框架
公司讨论用什么工具来统计Crash信息时,有提出友盟,TalkingData,Crashlytics等等工具
ActionBar、NotificationBar、StatusBar、SystemBar、TitleBar
EventBus 3.0使用
1、 event 处理函数必须使用public 标识,否则会报错。
2、 adapter 一定要在主UI线程里面修改,并且修改后一定要notifydatachanged,只要有变化,就一定要通知listView进行更新。
网络编码转义
url
编辑
web 开发中通过问号(?)方式在浏览器地址栏中传值时。浏览器是通过“&”来区分问号后的参数个数的。 如果出现传值参数中带有“&”时,在接受页面就会出现错误,类似如下请求路径:/next.jsp?param1=hendhs89&furej & param2=sss
参数param1中含有转义字符“&” ,这样会导致被请求页的参数接收错误。
在传值前 通过 java.net.URLEncoder.encode(param1) 编码处理后,可将转义字符转为16进制;
Intentservice \alarmmanager \broadcastreceive\ 联合使用
1、 服务中再ONNEWINTENT 函数中去启动alarm ,receive 接收到广播之后再次启动服务。
2、 LocalBroadcastManager 在APP中各个组件中进行广播,不会使用系统级别的Broadcast广播,重量级的广播,会带来很多风险
3、
单例的正确写法
1、public class SingletonClass {
private static class SingletonClassInstance {
private static final SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance() {
return SingletonClassInstance.instance;
}
private SingletonClass() {
}
}
SDK 1.5 之后写法,利用volatile写法实现。
public class SingletonClass {
private volatile static SingletonClass instance = null;
public static SingletonClass getInstance() {
if (instance == null) {
synchronized (SingletonClass.class) {
if(instance == null) {
instance = new SingletonClass();
}
}
}
return instance;
}
private SingletonClass() {
构造代码块、静态代码块,构造函数
构造方法执行之前,首先会去执行构造代码块,也就是说构造代码块是优于构造方法执行。构造代码块的位置不受构造方法的影响。多个构造代码块存在的时候,构造代码块执行的顺序是根据先后顺序来去执行。构造代码块的作用:可以将多个构造方法中的共同代码放到构造代码块中。静态代码块是类被加载的时候就会执行,静态代码块只会被执行一次。
抓包工具总结
常用的抓包工具有如下几种:
• Fiddler 可以抓HTTP/HTTPS的包(可以自己写插件处理网络请求与Response结果)
• Charless 可以抓HTTP/HTTPS的包
• WireShark 用来抓TCP的包,分析TCP报文灰常强大
Android adb命令
dumpsys meminfo [pid | packageName]
adb shell screencap –p /sdcard/screen.png
内存优化(内存抖动,内存泄露,内存溢出)
内存抖动解决方法:
• 别在循环里分配内存 (创建新对象)
• 尽量别在 View 的 onDraw 函数里分配内存
• 实在无法避免在这些场景里分配内存时,考虑使用对象池 (Object Pool)
public class User { public String id; public String name; private static final SynchronizedPool sPool = new SynchronizedPool(10); public static User obtain() { User instance = sPool.acquire(); return (instance != null) ? instance : new User(); } public void recycle() { sPool.release(this); } }
内存泄露:
减少内存使用的点
避免在Android里面使用Enum
谨慎使用第三方库:不要为了少量的功能而导入整library。
setintent/onNewIntent
当调用到onNewIntent(intent)的时候,需要在onNewIntent() 中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent。
ToolBar actionbar titlebar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //版本检测 SystemBarTintManager tintManager = new SystemBarTintManager(this); mToolbar = (Toolbar) findViewById(R.id.toolbar); tintManager.setStatusBarTintEnabled(true); //更改状态栏设置 setSupportActionBar(mToolbar); //将ToolBar设置成ActionBar tintManager.setStatusBarTintResource(android.R.color.holo_blue_bright); } getSupportActionBar().setHomeButtonEnabled(true); //设置返回键可用 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
shareelement 共享动画
状态栏操作
public static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
Resources res = context.getResources();
int resourceId = res.getIdentifier(“status_bar_height”, “dimen”, “android”);
if (resourceId > 0) {
statusBarHeight = res.getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
public static boolean isStatusBarVisible(Activity activity) {
if ((activity.getWindow().getAttributes().flags & 1024) == 0) {
Log.d(“StatusBarHeightUtils”, “status bar is visible”);
return true;
} else {
Log.d(“StatusBarHeightUtils”, “status bar is not visible”);
return false;
}
}
ANR 问题处理
ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:
$adb pull data/anr/traces.txt .
实际经验
实际这个路径下binderinfo 中有一些关于binder传输数据的数据信息。
Traces_1.txt 2.3.4.5.。。。
导致原因:
1、 click 5秒、broadcastreceive 10秒,服务20秒。
2、 (前台广播为10s,后台广播为60s/前台20s后台200s未完成启动 Timeout executing service/ContentProvider的publish在10s内没进行完:
3、 内存泄露导致,OOM 大多数导致,有时候也导致NOR。
针对三种不同的情况, 一般的处理情况如下
3.1 哪些地方是执行在主线程的
广播分类
静态广播/动态广播;全局广播/局部广播;前台广播/后台广播;粘性广播/非粘性广播
有序广播/无序广播。
Thread.Sleep 和SystemClock.sleet
后者不会被中断。
System.currentTimeMillis() 系统时间,也就是日期时间,可以被系统设置修改,然后值就会发生跳变。
uptimeMillis 自开机后,经过的时间,不包括深度睡眠的时间
elapsedRealtime自开机后,经过的时间,包括深度睡眠的时间
主UI线程
1) 网络操作,编译不能通过,数据库和文件操作在主进行可以编译通过,但是可能会耗时导致ANR。
2) Shareference :commit和apply,同步和异步,全量写,避免有一点就apply一点。争取在Onstop时候一次apply。写入XML和JSON 转移字符耗时。会发送ANR。
16ms 刷新activity
我们需要在16ms内完成下一次要刷新的界面的相关运算
1) 其中, CPU负责UI布局元素的Measure, Layout, Draw等相关运算执行. GPU负责栅格化(rasterization), 将UI元素绘制到屏幕上.
2.3.1 StrictMode的使用
StrictMode用来基于线程或VM设置一些策略, 一旦检测到策略违例, 控制台将输出一些警告,包含一个trace信息展示你的应用在何处出现问题.
通常用来检测主线程中的磁盘读写或网络访问等耗时操作.
循环中new 对象导致的内存抖动,导致OOM,导致卡顿。
GPU的RESTERIZATION/resterization
最终可以通过移除不必要的背景以及使用canvas.clipRect解决大多数问题。卡顿问题。只刷新脏数据部分。
叠加区域没有必要绘制,使用clipRect实现
Canvas使用
canvas.save(); canvas.translate(20, 120); for (Bitmap bitmap : mCards) { canvas.translate(120, 0); canvas.drawBitmap(bitmap, 0, 0, null); } canvas.restore();
jvm在装载类时会执行类的静态代码段,要记住静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了,而且以后不会再执行这段静态代码了。
java.lang.AbstractMethodError 抽象方法错误。当应用试图调用抽象方法时抛出。
java.lang.AssertionError 断言错。用来指示一个断言失败的情况。
java.lang.ClassCircularityError 类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。
java.lang.ClassFormatError 类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
java.lang.Error 错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
java.lang.ExceptionInInitializerError 初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。
java.lang.IllegalAccessError 违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。
java.lang.IncompatibleClassChangeError 不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。
java.lang.InstantiationError 实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
java.lang.InternalError 内部错误。用于指示Java虚拟机发生了内部错误。
java.lang.LinkageError 链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。
java.lang.NoClassDefFoundError 未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。
java.lang.NoSuchFieldError 域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。
java.lang.NoSuchMethodError 方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。
java.lang.OutOfMemoryError 内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
java.lang.StackOverflowError 堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。
java.lang.ThreadDeath 线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。
java.lang.UnknownError 未知错误。用于指示Java虚拟机发生了未知严重错误的情况。
java.lang.UnsatisfiedLinkError 未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。
java.lang.UnsupportedClassVersionError 不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。
java.lang.VerifyError 验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。
java.lang.VirtualMachineError 虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。
java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。
java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
java.lang.ArrayStoreException 数组存储异常。当向数组中存放非数组声明类型对象时抛出。
java.lang.ClassCastException 类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。
java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历 CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
java.lang.CloneNotSupportedException 不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
java.lang.EnumConstantNotPresentException 枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。
java.lang.Exception 根异常。用以描述应用程序希望捕获的情况。
java.lang.IllegalAccessException 违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。
java.lang.IllegalMonitorStateException 违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。
java.lang.IllegalStateException 违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。
java.lang.IllegalThreadStateException 违法的线程状态异常。当县城尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。
java.lang.IndexOutOfBoundsException 索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.InstantiationException 实例化异常。当试图通过 newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。
java.lang.InterruptedException 被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。
java.lang.NegativeArraySizeException 数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。
java.lang.NoSuchFieldException 属性不存在异常。当访问某个类的不存在的属性时抛出该异常。
java.lang.NoSuchMethodException 方法不存在异常。当访问某个类的不存在的方法时抛出该异常。
java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了 null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。
java.lang.NumberFormatException 数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。
java.lang.RuntimeException 运行时异常。是所有 Java虚拟机正常操作期间可以被抛出的异常的父类。
java.lang.SecurityException 安全异常。由安全管理器抛出,用于指示违反安全情况的异常。
java.lang.StringIndexOutOfBoundsException 字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.TypeNotPresentException 类型不存在异常。当应用试图
关于不同版本Base64
在java8中base64会把+转换-,把/转换为_,通过java8自己的UrlDecoder对象进行解码就可以了
Fragmentpageradapter 和FragmentStatePagerAdapter 区别
FragmentStatePagerAdapter 会销毁不需要的 fragment,而 FragmentPagerAdapter 是调用 detach(Fragment) 方法来处理它,只是销毁了 fragment 的视图,而 fragment 的实例由 FragmentManager 维护,因此,FragmentPagerAdapter 创建的 fragment 永远不会被销毁。
所以当数据量大时,可以选择 FragmentStatePagerAdapter,用户界面只有少量固定的 fragment 时,可以选择 FragmentPagerAdapter。
设计模式
适配器模式
把不匹配的类(抽象类也可),通过接口实现,同时继承原始src类,对类和接口同名函数进行转换,可以通过Client调用接口(adapter实现了接口),去得到自己想要的函数实现。
单例模式
private StaticInnerClassSingleton(){
if(InnerClass.staticInnerClassSingleton != null){
throw new RuntimeException(“单例构造器禁止反射调用”);
}
}
静态内部类防止利用反射机制获取到单例实例。同样默认构造函数需要定义成private
视图View相关
int position[] = new int[2]; view.getLocationInWindow(position);
贝塞尔曲线
1、 视图view 截屏和图片相关bitmap
2、 状态栏高度
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top;
Drawablecacheenable() /builddrawablecache()/getdrawablecache()/destroydrawablecache()
Context数量 = Activity数量 + Service数量 + 1
线程池相关知识点
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
newCachedThreadPool() -缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中-缓存型池子通常用于执行一些生存期很短的异步型任务 因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。-能reuse的线程,必须是timeout IDLE内的池中线程,缺省 timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。 注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
Android 像素屏幕相关
像素密度=√{(长度像素数2+宽度像素数2)}/ 屏幕尺寸
规定dpi、px、dp的计算公式为:px = dp * (dpi / 160);
xhdpi:2.0
hdpi:1.5
mdpi:1.0(基准)
ldpi:0.75
statusHeight = context.getResources().getDimensionPixelSize(height); 获取状态栏告诉,反射机制,获取的是dp单位的,转换成pixel格式的,可以利用系统函数,getDimensionPixelSize()
接口和抽象类
int a = 10;//全局变量默认强制是
public static final public static void staticMethod() { //静态方法,JDK1.8下编译通过 System.out.println("");
}
int abstractMethod(); //抽象方法
public static abstract class InnerClass{//抽象内部类,默认强制
public static //…
}
enum MyEnum{RED,BLUE,GRREN} //枚举类,默认强制
public static interface InnerInteerface{ //嵌套接口,默认强制 public static void aa();
}
对象的强制转换
子类对象可以声明为父类类型,父类对象不可以声明为子类类型
Test a=new TestExtend(); //正确
TestExtend b=new Test(); //错误12
在子类对象声明为父类类型后,可以通过强制转型,转型回来
Test a=new TestExtend();
TestExtend a=(TestExtend) b; //正确
Rxjava
Throttlefirst(多长时间内只取第一个,可用于VIEW多次点击防止)。
debounce()在一定的时间内没有操作就会发送事件。
RxBinding和rxlifecycle 结合起来使用,可以控制控件监听的生命周期。
Scheduler调度器,相当于线程控制器
o Schedulers.immediate() : 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
o Schedulers.newThread() :总是启用新线程,并在新线程执行操作.
o Schedulers.io():I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
o Schedulers.computation() : 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
o 还有RxAndroid里面专门提供了AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
o observeOn() 可以调用多次来切换线程,observeOn 决定他下面的方法执行时所在的线程。
o subscribeOn() 用来确定数据发射所在的线程,位置放在哪里都可以,但它是只能调用一次的。
就是被观察者指定一次在什么线程类型中执行,但是经过处理Map之后每一个中间过程的被观察者都可以指定观察者所在线程,可以多次却换观察者所在线程,也就是说,接收通知的人可以任意多次切换线程,前提是被观察者不能是一个,即使是一个也应该是被处理过后的一个被观察者。
RXLIFICYCLE
让你的activity继承RxActivity,RxAppCompatActivity,RxFragmentActivity
让你的fragment继承RxFragment,RxDialogFragment;下面的代码就以RxAppCompatActivity举例
.subscribeOn( Schedulers.io()) .compose(this.bindToLifecycle()) //这个订阅关系跟Activity绑定,Observable 和activity生命周期同步
.subscribeOn( Schedulers.io()) .compose(this.bindUntilEvent(ActivityEvent.STOP )) //当Activity执行Onstop()方法是解除订阅关系
Service服务相关知识点
自从Android 5.0之后就不允许隐式启动intent 启动service了,只能显示intent启动service。
可以通过bindService()将一个activity和service绑定。这种情况下,stopService()或stopSelf()实际上并不能停止这个service,除非所有的客户都解除绑定。
Stopservice() 有参数和没有参数区别,没有参数也没有bind的activity,直接系统会销毁,有参数的stopservice,会判断startcommand 中startid 的个数,0时候会销毁,用户调用一次startcommand startid就会自动增加1.
那么当系统调用你的onUnbind()方法时,如果你想在下次客户端绑定的时候接受一个onRebind()的调用(而不是调用onBind()),你可以选择在onUnbind()中返回true
1、 实验数据,两个应用进行AIDL通信,服务端Service只要安装之后,没有必要手动启动,可以Client直接先startService就可以启动用一台机器上的服务程序,服务APP启动顺序是先启动application,再启动service。 而且如果不设置process:remote 就是一个进程,一个线程。
com.rigwallet.RigApplication: application onCreate ###pid=748 threadid=1
TransactionService: service onCreate ###pid=748 threadid=1
TransactionService: service onStartCommand ###pid=748 threadid=1
2、 客户端如果从子线程中调用非本APP中的服务端中对应接口,服务端被调用就业不在主线程中运行
service sendPublicTransaction in thread Binder:4151_3
2019-12-06 11:54:32.219 4151-4168/com.rigwallet E/TransactionService: sendPublicTransaction not in MainLooper
2019-12-06 11:54:32.220 4151-4168/com.rigwallet E/TransactionService: service sendPublicTransaction ###pid=4151 threadid=2371
3、 基本数据类型,String CharsEQUENCE 都不需要设置流向,in out inout,基本类型数组和对象需要设置。
4、 定向 tag in 表示数据只能由客户端流向服务端,服务端将会收到客户端对象的完整数据,但是客户端对象不会因为服务端对传参的修改而发生变动。
out 表示数据只能由服务端流向客户端,服务端将会收到客户端对象,该对象不为空,但是它里面的字段为空,但是在服务端对该对象作任何修改之后客户端的传参对象都会同步改动。
inout 则表示数据可以在服务端与客户端之间双向流通。其中的数据流向是针对在客户端中的那个传入方法的对象而言的。服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动
5、
反射机制
Class> aClass = Class.forName(“com.sl.reflect.Student”);
2、对象的getClass()方法
Student student = new Student();
Class extends Student> aClass1 = student.getClass();
3、使用类名加.class
Class classes = Student.class.getClass();
4、通过ClassLoader对象的loadClass()方法
Class> aClass2 = ClassLoader.getSystemClassLoader().loadClass(“com.sl.reflect.Student”);
系统启动流程
SqlLite 数据库(C/C++操作数据库)
sqlite3提供了C函数接口来操作sqlite3数据库,其中有个关键数据结构 sqlite3 * 类型
1、打开数据库
int sqlite3_open(文件名,sqlite3 **); - 文件名若不存在,则会自动创建
返回SQLITE_OK表示操作正常,这些宏的定义在sqlite3.h文件中定义,看源代码会懂的更多
2、关闭数据库
int sqlite3_close(sqlite3 *);
3、SQL语句操作
int sqlite3_exec(sqlite3 *,const char *sql, sqlite3_callback,void *,char **errmsg);
这就是执行一条sql语句的函数
参数1:open函数得到的指针
参数2:一条sql语句,以’\0’结尾
参数3:sqlite3_callback是回调,当这条语句执行后,sqlite3会调用你提供的这个函数,回调函数要查阅资料
参数4:void *是自己提供的指针,可以传递任何指针到这里,这个参数最终会传到回调函数里面,如果不需要
传到回调函数里面,则可以设置为NULL
参数5:错误信息,当执行失败时,可以查阅这个指针,可以利用printf("%s\n",errmsg)得到一串字符串信息,
该信息表明出错的地方
通常,sqlite3_callback和void *都设置为NULL,表示不需要回调,比如做insert、delete操作,就没有必要使用回调,而当使用select时,就要使用回调,因为sqlite3把数据查出来,得通过回调来说明查出什么数据
回调函数的定义格式:
typedef int (*sqlite3_callback)(void *,int,char **,char **);
//sqlite 每查到一条记录,就调用一次这个回调
int LoadMyInfo(void *para,int n_column,char **column_value,char **column_name)
{
/*para: 在sqlite3里传入的void *参数,通过该参数可以传入一些特殊指针
*如类指针、结构指针,然后在这里转换成对应的类型(这里是void *类型),
*必须强制转换成自己的类型才可用,然后操作这些数据*/
//n_column: 该记录有多少个字段(列)
/*char **column_value 保存着查出来的数据,实际上是个1维数组,每一个元素都是
*char *值,是一个字段内容(用字符串表示,以\0结尾)*/
//char **column_name 与 column_value 是对应的,表示这个字段的字段名称
//这里不是用para参数
printf("%=记录包含%d\n个字段",n_column);
for(i=0;i
{
printf("字段名: %s ,字段值:%s\n",column_name[i],column_value[i]);
}
printf("\n");
return 0;
}
int main(int , char **)
{
sqlite3 *db;
int result;
char *errmsg = NULL;
char sql[512];
result = sqlite3_open("My.db",&db);
if(result != SQLITE_OK)
{
//数据库打开失败
return -1;
}
//创建数据表
strcpy(sql,"CREATE TABLE test(ID INTEGER PRIMARY KEY,NAME VARCHAR(32));");
result = sqlite3_exec(db,sql,NULL,NULL,errmsg);
if(result != SQLITE_OK)
{
printf("创建表失败,错误:%s\n",errmsg);
}
//插入记录
strcpy(sql,"INSERT INTO test VALUES(1,'OK');");
result = sqlite3_exec(db,sql,0,0,errmsg);
if(result != SQLITE_OK)
{
printf("插入记录失败,错误:%s\n",errmsg);
}
//查询数据库
strcpy(sql,"SELECT * FROM test;");
result = sqlite3_exec(db,sql,LoadMyInfo,NULL,errmsg);
sqlite3_close(db);
return 0;
}
Adb使用大全
查询CPU使用情况
输入命令:top -m 10 -s cpu(-m显示最大数量,-s 按指定行排序),如下图所示:
参数含义:
PID : progress identification,应用程序ID
S : 进程的状态,其中S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值是负数
#THR : 程序当前所用的线程数
VSS : Virtual Set Size虚拟耗用内存(包含共享库占用的内存)
RSS : Resident Set Size实际使用物理内存(包含共享库占用的内存)
PCY : 前台(fg)和后台(bg)进程
UID : User Identification,用户身份ID
Name : 应用程序名称
输入命令:dumpsys meminfo pid,比如查看手机安装的360安全卫士,那么实际命令应该为:dumpsys meminfo 3253,如下图所示
参数含义:
dalvik : dalvik使用的内存
native : native堆上的内存,指C\C++堆的内存(android 3.0以后bitmap就是放在这儿)
other : 除了dalvik和native的内存,包含C\C++非堆内存••••••
Pss : 该内存指将共享内存按比例分配到使用了共享内存的进程
heap alloc : 已使用的内存
heap free : 空闲的内存
share dirty : 共享,但有不能被换页出去的内存
private dirty : 非共享,又不能被换页出去的内存(比如linux系统中为了提高分配内存速度而缓冲的小对象,即使你的进程已经退出,该内存也不会被释放)
动画
// IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡 // FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡 // ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡
注解/注入
线程同步机制
(1) Object的wait() / notify()方法
(2)Lock和Condition的await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
2、sleep和new condition 可以catch 中断,wait 只能捕获exception
容器类
容器类从:线程安全、有序与否,增删改查得效率,存储数据的特点,是否为空
1、List(有序、可重复)
List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
2、Set(无序、不能重复)
Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
3、Map(键值对、键唯一、值不唯一)
Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
• 1、线程同步类:java.util.concurrent.ArrayBlockingQueue
• java.util.concurrent.LinkedBlockingQueue
• java.util.concurrent.SynchronousQueue
• java.util.concurrent.PriorityBlockingQueue
Android 菜单
Android系统的Menu主要有三种,OptionsMenu、ContextMenu、PopupMenu。以及菜单下面二级菜单SubMenu。
Java 基础知识类
1、linux 内存oom-killer和lowmemory=killer
Android知识相关
Activity生命周期
1、onCreate –> onContentChanged –> onStart –> onPostCreate –> onResume –> onPostResume –> onPause –> onStop –> onDestroy
2、即setContentView()或者addContentView()方法执行完毕时就会调用该方法, 例如,Activity中各种View的findViewById()方法都可以放到该方法中。
3、现在知道的做法也就只有在使用ActionBarDrawerToggle的使用在onPostCreate需要在屏幕旋转时候等同步下状态,mDrawerToggle.syncState();
方法:public boolean moveTaskToBack(boolean nonRoot)
activity里有这个方法,参数说明如下:
nonRoot=false→ 仅当activity为task根(即首个activity例如启动activity之类的)时才生效
nonRoot=true→ 忽略上面的限制
这个方法不会改变task中的activity中的顺序,效果基本等同于home键
动画
1、 PropertyValuesHolder其实就是封装了要执行动画改变的属性和一系列的变化值,并且也拥有和ObjectAnimator类似的各种ofInt,ofFloat,ofObject等方法
2、 PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat(“translationY”, y, 400);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(btn, p1, p2);
animator.setDuration(2000);
window/view 视图绘制和事件传递
DecorView里面包括一个ViewStub的ToolBar,然后下面是一个FramLayout,也就是我们经常在Activity中setContentView中的content内容
FLAG_SHOW_WHEN_LOCKED:让该Window可以显示在锁屏的界面上面
Type参数表示Window的类型,Window有三种类型:
应用Window:即我们普通的Activity对应的View子Window:需要依赖于一个父Window中,不能单独存在,如dialog系统Window:需要声明权限才能创建的Window,比如Toast和系统状态栏
//应用Window 1~99
FIRST_APPLICATION_WINDOW = 1;TYPE_BASE_APPLICATION = 1;TYPE_APPLICATION = 2;TYPE_APPLICATION_STARTING = 3;LAST_APPLICATION_WINDOW = 99;
//子Window 1000~1999
FIRST_SUB_WINDOW = 1000;TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;LAST_SUB_WINDOW = 1999; //系统Window 2000~2999
调用requestDisallowInterceptTouchEvent(true)时,onInteceptorTouchEvent不会被调用,详情请看源码
DecoreView分为2部分,第一部分是Title,第二部分是Content,对应的ID分别为R.Android.id.title和R.android.id.content
[java] view plain copy 在CODE上查看代码片派生到我的代码片 public boolean dispatchTouchEvent(MotionEvent ev) { 调用onInterceptTouchEvent检查是否拦截事件 if(没有拦截){ 在ViewGroup中遍历查找目前是点击了哪个子视图 if(找到了){ 调用该子视图的dispatchTouchEvent,递归下去 }else{ 没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent() 如果再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,后续的move和up都被Listener或者onTouchEvent()处理, 如果down事件返回false,则后续的move,up事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。 } }else{ 事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依然遵从上面的down和move,up事件的关系 } }
点击父容器会不会出发子视图的点击事件:
事件遵循一个原则,就是看他有没有事件消费。比如一个LinearLayout里面有一个Button,点击LinearLayout会触发到Button吗,这里就看LinearLayout有没有设置点击事件,如果有就不会传递到Button,如果没有就会传递给Button。
一个优先级的排列:OnTouchListener > onTouchEvent > OnClickListener
当一个点击事件产生后,如果所有的元素都不处理这个事件,那么这个事件将会最终传递给Activity处理,调用Activity的onTouchEvent
触摸事件中的十条结论
一个事件序列:以down事件开始 → 数量不定的move事件 → 以up事件结束
正常情况下,一个事件序列只能被一个View拦截且消耗
某个View一旦决定拦截,如果事件序列能够传递给它的话,那么这个事件序列就只能由它来处理,并且它的onInterceptTouchEvent不会再被调用
某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件,那么同一事件序列中的其他事件都不会再交给它来处理
如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent 并不会被调用
ViewGroup默认不拦截任何事件
View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent就会被调用
View的onTouchEvent默认都会消耗事件,除非它是不可点击的
View的enable属性不影响onTouchEvent的默认返回值
onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件
Window phoneWindow docerView
Window是一个抽象类,PhoneWindow是Window的唯一实现,如上,它直接将事件传递给了DecorView
DecorView是一个继承自FrameLayout的父View,平时我们通过setContentView设置的View就是它的子View
当ViewGroup决定拦截事件后,那么后续的点击事件将会默认交给它自己来处理,并且不再调用onInterceptTouchEvent,如果一个viewgroup 拦截了实现了ontouchevent,那么其他的ACTIONMOVE ACTIONUP 等事件直接发送到这个ontouchevent,不再经过viewgroup的onintercepter,但是肯定走dispatchtouchevent
Activity 和Fragment联合使用
1、 remove 和replace都会导致Fragment走ondestroy 销毁。
2、 popBackStack(String name, int flags)参数的意义:
name为null,flags为0,弹出栈中最上层的fragment;
name为null,flags为1,弹出栈中所有的fragment;
name不为null,flags为0,弹出栈中该Fragment之上的Fragment;
name不为null,flags为1,弹出栈中该Fragment和之上的Fragment;类似推土机。
3、 app 后台被杀再次回到主界面,activity会恢复,oncreate中的saveinstance会有值,同时会先恢复Fragmentmanzger中的东西,先走fragment的oncreate再走activity的oncreate。随意判断saveinstance不为空,就不要再创建fragment了,否则会出现gragemtn重复问题。
4、 Activity中的数据要在onSaveInstanceState方法中保存,在onCreate或onRestoreInstanceState中将数据恢复,fragment 在oncreate恢复,因为没有onrestore接口回调
刘海适配方案
• 在Android8.0,Google官方并未提供刘海屏的相关API,因此,对于8.0的系统,各厂家会对刘海屏做特殊适配
• 在Android9.0,Google官方提供了DisplayCutout来支持刘海屏适配
• 避免使用硬编码获取状态栏高度,因为这有可能导致内容重叠或裁切。尽量使用WindowInsetsCompat来检索状态栏高度并进行内容填充
• 应用程序可能不会充满整个屏幕,应尽量使用View.getLocationInWindow()来代替View.getLocationOnScreen()。
小米适配
int flag = 0x00000100 | 0x00000200 | 0x00000400; try { Method method = Window.class.getMethod(“addExtraFlags”, int.class); method.invoke(getWindow(), flag); } catch (Exception e) { Log.i(TAG, “addExtraFlags not found.”); }
获取挖孔
int resourceId = context.getResources().getIdentifier(“notch_height”, “dimen”, “android”); if (resourceId > 0) { result = context.getResources().getDimensionPixelSize(resourceId); }
还需要检查开启“隐藏屏幕刘海”后,
Settings.Global.getInt(mContext.getContentResolver(), “force_black”, 0) == 1
华为手机适配
public static void setNotFullScreenWindowLayoutInDisplayCutout (Window window) { if (window == null) { return; } WindowManager.LayoutParams layoutParams = window.getAttributes(); try { Class layoutParamsExCls = Class.forName(“com.huawei.android.view.LayoutParamsEx”); Constructor con=layoutParamsExCls.getConstructor(LayoutParams.class); Object layoutParamsExObj=con.newInstance(layoutParams); Method method=layoutParamsExCls.getMethod(“clearHwFlags”, int.class); method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException | InvocationTargetException e) { Log.e(“AskSky”, “hw clear notch screen flag api error”); } catch (Exception e) { Log.e(“AskSky”, “other Exception”); } }
vivo oppo 适配都通过反射来进行。
Framework 底层框架
架构和框架
Arouter
我们在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activity.class映射关系的类文件,然后app进程启动的时候会加载这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(context, activity.Class),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。
//1.通过Autowired注解表明key & 需要在onCreate中调用ARouter.getInstance().inject(this);配合使用 @Autowired(name = “key1”)
ARouter.getInstance().build("/test/1") .withLong(“key1”, 666L) .withString(“key3”, “888”) .withSerializable(“key4”, new Test(“Jack”, “Rose”)) .navigation();
Autowired 自动将intent参数注解到这个变量上。
3、 implements SerializationService { 当使用ARouter.getInstance().build("/test/1") .withObject时候需要集成Arouter的SerializationService
4、 RUI格式跳转
Uri testUriMix = Uri.parse(“arouter://m.aliyun.com/test/activity2”); ARouter.getInstance().build(testUriMix) .withString(“key1”, “value1”) .navigation();
打包调试
性能优化
1、 卡顿优化
1) 布局优化
2) 绘制优化
布局上的优化。移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片
自定义View优化。使用 canvas.clipRect() 帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
3) 启动优化## 标题
4) 电量优化
计算优化。算法、for循环优化、Switch…case替代if…else、避开浮点运
比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了Wake_Lock锁,记着释放此锁。手机休眠之后websocket的心跳包就会停止。
例如当用户在夜间休息时或设备接通电源适配器连接WiFi启动下载更新的任务。这样可以在减少资源消耗的同时提升应用的效率。Jobscheduler
网络优化时候压缩,减少传输次数也能省电。
分析方法:耗电排行、battery historian adb shell dump battery status
传感器、蓝牙、wifi等使用关闭。
专项总结: