反射中,Class.forName和classloader的区别
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容
-
App进程保活
1.利用Activity
1像素Activy,监控手机解锁屏事件,解锁时将Activity销毁,锁屏时启动,并且要无感知,在RecentTask里移除
2.监听系统静态广播
低版本时,静态广播可以唤醒应用进程,所以监听系统广播,例如开机,锁屏,解锁等可以做到,但是高版本不能通过静态广播监听系统广播了
3.利用系统Service机制拉活
Service 的 onStartCommand 返回值,当返回值为 START_STICKY 和 START_REDELIVER_INTENT 时,服务会自动重启,但是 Service 在短时间内被杀死5次,则不再拉起
4.使用WorkManager定时任务定时调起App。
5.利用账号同步机制
Android 系统的账号同步机制会定期同步账号进行,该方案目的在于利用同步机制进行进程的拉活。添加账号和设置同步周期的代码即可
-
如何保证service在后台不被kill?
利用的系统广播是Intent.ACTION_TIME_TICK,这个广播每分钟发送一次,我们可以每分钟检查一次Service的运行状态,ActivityManager.getRuningServices()获取所有活动的service,根据名字用equals判断其中没有这个service了,就重新启动Service。
-
描述请点击 Android Studio 的 build 按钮后发生了什么
参考:App 的编译和打包流程
-
权限管理底层的权限是如何进行 grant 的
-
App消息推送原理是什么?
在Android开发中,app消息推送的基本原理就是要在推送服务器和客户端之间建立连接,而连接的建立方式主要有两种pull和push。在实践过程中我们发现,相较于通过轮询(pull)的方式来获得消息通知,建立长连接(push)进行推送无论是对用户终端的电量消耗,还是对云端数据访问流量的耗费都比轮询要好,因此目前主流的app消息推送基本都是通过push的方式实现的。
推送的实现技术简单来说就是利用Socket维持Client和Server间的一个长连接。
实现消息推送可以有客户端轮询访问;SMS短信推送;Xmpp协议推送等。
-
JNI开发中怎么定位native crash
可使用add2line 和ndk-stack等工具分析JNI Crash的log。
参考:https://www.jianshu.com/p/e5d357f12f83
什么是dump文件
从软件开发的角度上,dump文件就是当程序产生异常时,用来记录当时的程序状态信息(例如堆栈的状态),用于程序开发定位问题,文件后缀为.DMP。dump文件可以利用WinDbg打开,也可以直接用vs打开。
上图给出NativeCrash收集的整体实现方案。APP中接入提供的SDK,包含一个通用SO和一个JAR,当APP中发生NDK崩溃时,会在手机端生成一个dmp文件,待下次APP重启后,将此dmp文件上传至服务端,在服务端进行解析、分类、聚合、可读展示等过程。
如果项目接入了Bugly,也就可以收集到NativeCrash上传至Bugly。
mainfest中配置LargeHeap,真的能分配到大内存吗?能增加多大内存?
设置android:largeHeap="true"属性之后,进行如下测试查看最大可用内存,发现最大可用内存真的增大了。但是最大不能超过系统能分配给App的最大内存。
ActivityManager.getLargeMemoryClass() //可以获得开启largeHeap最大的内存大小
-
谈谈你对 Activity.runOnUiThread 的理解?
runOnUiThread过程源码
// Activity.java
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
// Handler.java
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
首先判断当前是不是 UI线程,如果是就直接执行传入的Runnable接口的run方法。
否则就通过Activity类中的Handler实例的post(Runnable)方法来发送消息,最终调用handler的enqueueMessage方法将任务加入到消息队列中,因为在Activity中的Handler对象运行在主线程(主线程中的Handler对象是在ActivityThread类中的main方法中创建的),故而这里就切换到了主线程中。
Intent传输数据的大小有限制吗?如何解决?
Intent传输数据的大小受Binder的限制,上限是1M。不过这个1M并不是安全的上限,Binder可能在处理别的工作,安全上限是多少这个在不同的机型上也不一样。
传 512K 以下的数据的数据可以正常传递。
传 512K~1024K 的数据有可能会出错,闪退。
传 1M以上的数据会报错:TransactionTooLargeException
考虑到 Intent 还包括要启动的 Activity 等信息,实际可以传的数据略小于 512K
解决办法
减少传输数据量
Intent通过绑定一个Bundle来传输,这个可以超过1M,不过也不能过大
通过内存共享,使用静态变量或者使用EventBus等类似的通信工具
通过文件共享
-
线上报错查日志怎么使用mapping文件
mapping.txt文件位于app模块根目录下,它是混淆前方法和字段名与混淆后代码间的映射。比如在bugly上有崩溃日志定位不到类和方法,需要上传mapping文件来处理。
线上崩溃有崩溃日志,但是是混淆之后的报错堆栈,需要在Jenkins上下载当次打包生成的mapping文件进行查询报错位置。
-
ksp与kapt的区别
注解处理器是Android开发中一种常用的技术,很多常用的框架比如ButterKnife,ARouter,Room中都使用到了注解处理器相关技术。
ksp(Kotlin Symbol Processing),一款专门拿来给Kotlin项目提升注解生成速度的,需继承SymbolProcessor来自定义注解处理器。
总结KSP优点:
KSP性能更好,有时可以达到2倍的速度提升;
KSP开发起来更加方便,不需要自己处理增量编译逻辑;
KSP支持多平台,而KAPT只支持JVM平台
KSP拥有更符合Kotin习惯的API,同时可以识别Kotin特有的符号
参考:
https://juejin.cn/post/6939472660573192206
https://juejin.cn/post/7116305314529411085
Android 13有哪些需要适配的地方
首先将我们项目中的 targetSdkVersion和compileSdkVersion 升至 33。
1.通知受限
对新安装的应用的影响:
如果用户在搭载 Android 13 或更高版本的设备上安装您的应用,应用的通知默认处于关闭状态。在您请求新的权限且用户向您的应用授予该权限之前,您的应用都将无法发送通知。
适配方法:
应用以Android 13或更高版本为目标平台,需要在应用程序manifest文件中声明POST_NOTIFICATIONS权限,并完成与请求其他运行时权限类似的流程。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
2.读取媒体文件权限
对于目标版本为Android 13的应用,细化了READ_EXTERNAL_STORAGE权限,使用READ_MEDIA_IMAGE、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO替代READ_EXTERNAL_STORAGE。
如果用户之前向您的应用授予了 READ_EXTERNAL_STORAGE 权限,系统会自动向您的应用授予细化的这三个媒体权限。
<manifest ...>
<!-- 按需添加 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
</manifest>
参考:
https://blog.csdn.net/qq_17766199/article/details/130520330
用RecyclerView怎么实现首页复杂列表页面
关键做法:
根据GridLayoutManager的getSpanSize()方法可以动态的设置item跨列数。
例如:
根据getItemViewType返回的itemtype,第一个position显示为轮播图,
所以返回的是gridManager.getSpanCount(); 即:return 6
如果是2张图占满一行,那么1张图就是占整行的二分之一,既6/2=3, return 3即可。
参考代码:
adapter.setSpanSizeLookup(new BaseQuickAdapter.SpanSizeLookup() {
@Override
public int getSpanSize(GridLayoutManager gridLayoutManager, int position) {
int type = data.get(position).getType();
if (type == 1 || type == 3 || type == 2 || type == 5 || type == 6) {
return 6;
} else if (type == 4) {
return 2;
} else if (type == 7) {
return 3;
}
return 0;
}
});
参考:
https://juejin.cn/post/6939822591515295781
sealed class 是什么,跟Enum的区别是什么?
密封类sealed:
Human模型数据结构,Man、Woman分别是子类。
name、age是Human共有的属性,a1、a2是Man特有的属性,而a3、a4则是Woman特有的。
sealed class Human(val name: String, val age: Int) {
class Man(name: String, age: Int, val a1: Int, val a2: Int) : Human(name, age)
class Woman(name: String, age: Int, val a3: Int, val a4: Int) : Human(name, age)
}
//处理
fun handleHuman(human: Human) {
when (human) {
is Human.Man -> {
//type 为man的处理
Log.d("test",human.a1.toString() + human.a2.toString())
}
is Human.Woman -> {
//type 为woman的处理
Log.d("test",human.a3.toString() + human.a3.toString())
}
}
}
- 密封类适合用在父类定义共同属性,子类具有特殊属性的场景,这点跟枚举不一样,密封类sealed算是枚举的增强;
当密封类sealed用于类型判断,when少了对应的类型处理(定义了3种,只处理了2种),编译期会有提示,也是很友好的。
密封类以及它的子类最好都定义在同一个文件中,这样会更加内聚。