1. Android应用启动耗时
方法一 通过代码
adb shell am start -W (包名)/(类全名)
//例如
adb shell am start -W com.guomin.pulse/com.guomin.pulse.mvp.ui.activity.SplashActivity
// TotalTime:就是启动所需时间
方法二
在Android Studio Logcat中过滤关键字“Displayed”
2.debug关闭lldb方法
debug的时候如果开启了NDK在启动lldb的时候特别慢可以关闭掉,方法如下
选择File->Settings->Plugins 然后找到 Android NDK Support把选项勾掉就可以了
3.Flutter配置好了但是NEW的时候没有Flutter选项
选择File->Settings->Plugins 然后找到 Android APK Support把选项选中,如果还是不行把Android NDK Support也选中
4.java引用类型之弱引用(WeakReference)
详细参考https://blog.csdn.net/linzhiqiang0316/article/details/88591907
只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个弱引用就会被加入到与之关联的引用队列中。(内存泄漏检测LeakCanary 用到了这个)
WeakReference<Aa> weakReference1 = new WeakReference<>(new Aa());
System.gc();
//报错空指针
weakReference1.get().print("weakReference1");
//不报错可以输出 weakReference2 因为aa还存在如果在 System.gc();之前aa=null那么就会空指针
Aa aa = new Aa();
WeakReference<Aa> weakReference2 = new WeakReference<>(aa);
System.gc();
weakReference2.get().print("weakReference2");
//不报错可以输出 weakReference3 和 aa3 因为存在aa2 和 aa3 强引用可以在gc之前手动设置为null就不会输出了
WeakReference<Aa> weakReference3 = new WeakReference<>(new Aa());
Aa aa2 = weakReference3.get();
aa2.print("weakReference3");
System.gc();
Aa aa3 = weakReference3.get();
aa3.print("aa3");
5. adb 常用命令
- 启动adb
adb start-server - 杀死adb
adb kill-server - 无线设置
adb tcpip 端口号 (adb tcpip 5555) - 连接设备
adb connect ip地址加端口(adb connect 192.168.10.1:5555) - 查看连接的设备
adb devices - 端口某个连接的设备
adb disconnect ip地址(adb disconnect 192.168.10.1)
6.MAT使用相关
mat转换命令
//hprof-conv 原文件全名 需要生成的文件
hprof-conv 1.hprof 2.hprof
mat 查看内存泄漏方法
点开Histogram,找到文件 选择一个Class,右键选择List objects > with incoming references
选择一个对象,右键选择Path to GC Roots > ****,通常在排查内存泄漏的时候,我们会选择exclude all phantom/weak/soft etc.references,
详情如何使用MAT进行内存泄露分析
7.Battery Historian使用
分析地址(https://bathist.ef.lc/)需要翻墙
命令
//1恢复初始化
adb shell dumpsys batterystats --reset
//2开始记录
adb shell dumpsys batterystats --enable full-wake-history
//3收集
adb bugreport bugreport.zip
8.Android中图片内存计算规则
每个像素所占用的字节和图片设置的格式有关,默认的是ARGB_8888
ARGB_8888 ---4字节
RGB_565 ---2字节
- 网络加载的图片
图片宽度 X图片高度 X每个像素所占用的字节数 - res下的图片
手机的density=(宽度2X高度2)然后开开根号 /尺寸
drawable对应的density=密度值/160
缩放比例 scale = 当前设备屏幕密度(density) / 图片所在 drawable 目录对应屏幕密度(density)
Bitmap 实际大小 = 宽 * scale * 高 * scale * Config 对应存储像素数
9.导出ANR报错日志
//adb命令如下
adb pull data/anr/traces.txt
//如果上述命令报错,没有权限的话改用命令
adb bugreport
10.File的listFiles返回null
解决方法
- targetSdkVersion<29需要动态申请权限
android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE
- targetSdkVersion>29
动态申请权限,并且File的地址不可以为公共地址
不能是这个
Environment.getExternalStorageDirectory()
可以是这个
getFilesDir()
11.adb命令跳转页面
//adb命令如下
adb shell am start -n 包名/类详细地址
adb shell am start -n com.guomin.pulse/com.guomin.pulse.mvp.ui.activity.SplashActivity
12.Retrofit动态更换baseurl
https://blog.csdn.net/really_study/article/details/94597466
13.反射常用方法
getMethods 获得父类和自己的所有的方法(公开的方法)
getDeclaredMethods 获得自己的所有的方法(包括私有和公开的)
getFields 获得自己的和父类的public的属性
getDeclaredFields 获得自己的全部的属性
//对象
public class People {
public String sex;
private String name;
private People() {
this.name = "张三";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void setName1(String name) {
this.name = name;
}
}
//运行如下代码
try {
//Class<?> aClass = Class.forName("com.example.myapplication.People");
Class<People> aClass = People.class;
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
AccessibleObject.setAccessible(declaredConstructors, true);
for (Constructor<?> declaredConstructor : declaredConstructors) {
if (declaredConstructor.isAccessible()){
Object o = declaredConstructor.newInstance();
Method setName = aClass.getDeclaredMethod("setName", String.class);
setName.invoke(o, "李四");
Method setName1 = aClass.getDeclaredMethod("setName1", String.class);
setName1.setAccessible(true);
setName1.invoke(o, "李四123");
Method getName = aClass.getDeclaredMethod("getName");
Object invoke = getName.invoke(o);
System.out.println(invoke.toString());
}
}
for (Method method : aClass.getMethods()) {
System.out.println("getMethods---->"+method.getName());
}
for (Method method : aClass.getDeclaredMethods()) {
System.out.println(method.getName());
}
for (Field declaredField : aClass.getFields()) {
System.out.println("getFields-->"+declaredField.getName());
}
for (Field declaredField : aClass.getDeclaredFields()) {
System.out.println("getDeclaredFields-->"+declaredField.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
//输入日志
李四123
getMethods---->getName
getMethods---->setName
getMethods---->wait
getMethods---->wait
getMethods---->wait
getMethods---->equals
getMethods---->toString
getMethods---->hashCode
getMethods---->getClass
getMethods---->notify
getMethods---->notifyAll
setName1
getName
setName
getFields-->sex
getDeclaredFields-->sex
getDeclaredFields-->name
14.Flutter 报错 Waiting for another flutter command to release the startup lock...
输入以下命令到终端解决
killall -9 dart
15.android studio gradle查看SHA-256、SHA1、MD5
以此点击如下,
然后输入signingReport,等待输出即可
16.mac 配置vscode打开文件
function code {
if [[ $# = 0 ]]
then
open -a "Visual Studio Code"
else
local argPath="$1"
[[ $1 = /* ]] && argPath="$1" || argPath="$PWD/${1#./}"
open -a "Visual Studio Code" "$argPath"
fi
}
17.Retrofit的get请求@Path和@Query
如
@GET("/api/v1/view-record/{id}/detail")
Observable<BaseResponse<Object>> getIsCollect(@Path("id") long id,@Query("source_type") int source_type);
得到
/api/v1/view-record/8/detail?source_type=30
18.手动签名报错 Failure [-124: Failed parse during installPackageLI: Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary]
先进入 /Users/xxx/Library/Android/sdk/build-tools/30.0.3 路径中
先删除了客户的v1签名,即删除 META-INF目录(META-INF目录下存放的是签名信息,可能报找不到META-INF/* ,不重要)
zip -d source.apk META-INF/*
获取4KB对齐apk(source_4.apk 是对齐后的结果文件)
zipalign -v 4 source.apk source_4.apk
查看是否结果文件是否对齐 (成功后会报:Verification succesful)
zipalign -c -v 4 source_4.apk
然后通过 apksigner 签名,不能在使用 jarsigner 签名
apksigner sign --ks (签名地址) --ks-key-alias (别名) --out (签名后的apk地址) (待签名apk地址)
19.Android设置scheme
页面设置
<activity
android:name=".TestActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="www.lx.com"
android:path="/home"
android:port="1024"
android:scheme="qwer" />
</intent-filter>
</activity>
启动设置
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("qwer://www.lx.com:1024/home"))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
网页启动
<a href="qwer://www.lx.com:1024/home?key1=value1&key2=value2">open app with Uri Scheme</a>
Activity取值
Intent intent = getIntent();
if (null != intent && null != intent.getData()) {
Uri uri = intent.getData();
Log.e(TAG, "uri=" +uri);
String scheme = uri.getScheme();
String host = uri.getHost();
int port = uri.getPort();
String path = uri.getPath();
String key1 = uri.getQueryParameter("key1");
String key2 = uri.getQueryParameter("key2");
Log.e(TAG, "scheme=" + scheme + ",host=" + host
+ ",port=" + port + ",path=" + path
+ ",query=" + uri.getQuery()
+ ",key1=" + key1 + ",key2=" + key2);
}
20 数据库相关的
加入数据表 有 int a,int b, int c
那对应的bean 只可以少于或者等于int a,int b,int c, 如果加上d就会报错