ADB 常用命令详解
查看当前链接的机器列表1adb devices
结果:
12List of devices attached9TBQLN7DEUYXT4GU unauthorized
我这个电脑只连接了一个手机,9TBQLN7DEUYXT4GU 代表这个设备的 id,如果连接的设备数超过了一个,之后执行其它命令就需要指定设备 id 了;unauthorized 代表设备现在是未验证的状态,手机被连接之后会在手机上弹一个允许 USB 调试的弹窗,需要你的授权,接受之后这里会变成 device 状态。
查找栈顶 Activity1adb shell dumpsys activity top | grep ACTIVITY
结果:
123ACTIVITY com.tencent.mobileqq/.activity.SplashActivity 161c7a6 pid=27178ACTIVITY com.miui.home/.launcher.Launcher 7e33f27 pid=27891ACTIVITY com.tencent.mm/.ui.LauncherUI 47a8f86 pid=108 ...
So_找不到对应的实现
so 找不到对应的实现Build.gradle 中 minSdk 为 26 时添加 so,指明地方加载,无论如何也无法找不到方法对应的实现,修改 build.gradle 中 minSdk 为 21 就正常了。
类似的问题还有只添加 arm64-v8a 的 so,并在 build.gradle 中指定 abiFilter 为 arm64-v8a ,结果应用会出错无法运行,即使再添加上 armeabi-v7a 的 so 也无法运行,解决方式是去掉 build.gradle 中的 abiFilter
单元测试
生成代码覆盖率测试报告生成代码覆盖率测试报告可以使用
./gradlew createDebugAndroidTestCoverageReport
需要在项目 moudle 下的 build.gradle 中添加如下配置
12345buildTypes { debug { testCoverageEnabled = true }}
排查单测卡住的问题如果批量跑单测的时候不知什么原因一直跑不完,那估计就是卡在某一个单测了,这时候查看这个目录下的测试 log 文件,最后一个记录的 test 名字就是卡主的名字
build/outputs/androidTest-results/connected/M2102K1C - 12/testlog/test-results.log
然后再使用 debug 的方法 dump 分析线程就知道卡在哪个线程了,然后寻找逻辑漏洞就方便多了
So 热更新原理
so 热更新原理加载 so 有两个方法:
12System.loadLibrary(String libName); // so 的名称,如 hotfixSystem.load(String pathName); // so 的完整路径,如 data/data/packageName/libs/libhotfix.so
查看这两个方法的实现,平时用的 android.jar 不太方便看,很多 api 都被隐藏了,你可以使用已被修改过的 android.jar 来查看 https://github.com/anggrayudi/android-hidden-api,或者你可以直接在线查看 https://cs.android.com/android。
一路追下来你会发现最终两个方法最后调用的都是这个方法:
1Runtime.nativeLoad(String filename, ClassLoader loader, Class<?> caller);
System.loadLibrary(String libName) 比 System.load(String path ...
正则表达式在不同语言下表现不同
找出所有匹配组Java:
12345678910111213141516171819Pattern pattern = Pattern.compile("((f)|(g)|(h))");Matcher matcher = pattern.matcher("/gh");boolean find;do { find = matcher.find(); if (find) { MatchResult result = matcher.toMatchResult(); for (int i = 0; i < result.groupCount() + 1; i++) { int start = result.start(i); int end = result.end(i); if (start >= 0) { System.out.println("index=" + i + ", value=" + ...
RandomAccessFile 测试结论
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364@Testpublic void testWriteFile() { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); String testFilePath = context.getExternalCacheDir() + "/test.txt"; Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { RandomAccessFile randomAccessFile = ...
IDEA JVM ClassLoader 错误
错误日志:
1234567891011121314151617181920212223242526272829303132333435363738OpenJDK 64-Bit Server VM warning: Archived non-system classes are disabled because the java.system.class.loader property is specified (value = "com.intellij.util.lang.PathClassLoader"). To use archived non-system classes, this property must not be setError occurred during initialization of VMjava.lang.NoClassDefFoundError: com/intellij/util/lang/UrlClassLoader at java.lang.ClassLoader.defineClass1(java.base@18. ...
Socket.outputStream 是如何感知到链接断开而抛出 IOException 的
Socket.outputStream 是如何感知到链接断开而抛出 IOException 的首先查看 Socket.getOutputStream 方法可以知道此 outputStream 是由 impl 获取的
123456789101112131415public OutputStream getOutputStream() throws IOException { ... OutputStream os = null; try { os = AccessController.doPrivileged( new PrivilegedExceptionAction<OutputStream>() { public OutputStream run() throws IOException { return impl.getOutputStream(); } ...
HttpUrlConnection 如何使用 OkHttp
12345transient URLStreamHandler handler;public URLConnection openConnection() throws java.io.IOException { return handler.openConnection(this);}
HttpUrlConnection 的对象是由 URL 对象调用 handler.openConnection 来常见完成的,这里的 handler 的类型为 URLStreamHandler,这个 handler 又是由 factory.createURLStreamHandler(protocol) 根据协议来创建的,最终它的实现为 com.android.okhttp.HttpHandler 或者 com.android.okhttp.HttpsHandler。
https://cs.android.com/android/platform/superproject/+/master:external/okhttp/repackaged/android/src/main ...
Okhttp Stream 分析
Stream 分析众所周知 okHttp 的处理是由很多 Interceptor 组成的责任链来处理请求逻辑的,如果最终需要请求网络的话会走到负责连接的 ConnectInterceptor 中,在里面会创建负责与服务器交互的 exchange,之后就由他来负责发送和接受信息。
123456789object ConnectInterceptor : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain val exchange = realChain.call.initExchange(chain) // 创建负责交互的 exchange val connectedChain = realChain.copy(exchange = exchange) return connectedChain.p ...