Android(AOSP)编程开发
综合/最新
Android开发、Android UI、Android API Levels、Android玩机
Android系统开放源码(墙外)
新:
通过NsdManager/UPNP+TCP/UDP暴漏一个Android Service,自家App共享其已授权的能力。
死记、偏门:
图标:Android 图标尺寸最低为 256x256 像素;Google Play商店网页展示图标最低要求为 32 位 PNG(含 alpha 透明层)、尺寸 512x512 像素、文件大小上限 1024 KB。
可触摸 UI 元件的标准为 48dp=72px=9mm,即一个手指能够准确舒适触摸的最小尺寸,最小可点击区域。
暂时禁用日志刷屏的应用:./adb shell pm disable-user com.google.android.apps.youtube.music
约定:java包nonui下放BroadcastReceiver和Service,ui包则放Activity。
授权通知才能Toast(前台Activity则永远能弹Toast) - NotificationService Error: Suppressing toast from package app.name by user request.
全局异常处理:
Application onCreate() - Thread.setDefaultUncaughtExceptionHandler(new UEH());
public class UEH implements Thread.UncaughtExceptionHandler {
Thread.UncaughtExceptionHandler defaultHandler;
public UEH() { defaultHandler = Thread.getDefaultUncaughtExceptionHandler();}
@Override public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
if (defaultHandler != null) { defaultHandler.uncaughtException(t, e); }
} // 也可交回默认异常处理器
}
进程生命周期:
⬇前台进程(foreground process/oom_adj=0) - 有Activity界面直接交互的情况。
⬇可见进程(visible process) - 存在执行了Service.startForeground()的Service,则处于该状态;同时,还可通过startService()嵌套调startService()。
⬇服务进程(service process) - 如果有通过startService()启动的Service,则处于该状态,但在“无可见UI+无前台服务”数分钟后会强制降级为“缓存进程”,若在该Service里嵌套调startService()会报“Not allowed to start service Intent”,在降级前嵌套调startForegroundService()则不会报错;bindService()方式则不受影响;SYSTEM_ALERT_WINDOW+可见悬浮窗也可后台调startForegroundService()。
⬇缓存进程(cached process) - 降级为该状态后,系统会主动触发stopService();内存不足后会先拿其开刀,但若被其他app调起,则会提升至上方进程状态。
无进程 - 即强制停止后的IMPORTANCE_GONE状态。
查看PID: ./adb shell pidof com.openle.v1app.social.fullchannel
查看优先级: ./adb shell cat /proc/[PID]/oom_adj
前台服务(Foreground Services/FGS)任务管理器:下拉顶部通知栏,再二阶段下拉就能看到左下角的前台服务列表了,若用户在此点了“Stop”会连带整个应用关闭,但与“强制停止”不同的是,闹钟和WorkManager不会失效。
[似乎已废弃]前台服务在24小时内连续运行20小时以上,会弹通知引起用户注意(“system-notification-long-running-fgs”),抑制1像素保活方案。
系统源码添加白名单 - com.android.server.am.OomAdjuster、ProcessList.setOomAdj?
开发者选项的“不保留活动”开启后,会导致进入二级页面时,按Home键就会销毁MainActivity了,二级页面再finish()就找不到MainActivity了,故时间充裕的话,应测测看:
判断后提示用户关掉:Settings.Global.getInt(getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0);
Android虚拟机:
Android Runtime(ART)虚拟机取代了早期的Dalvik虚拟机,但均使用由d8转换后的DEX字节码格式(*.dex或*.aar),无法识别纯Java编译打包的*.class或*.jar文件。
Java转Dex命令:d8 MyProject/app/build/intermediates/classes/debug/*/*.class 或 d8 java.jar --output out.dex
动态载入及反射调用:DexClassLoader对象。
内容
Android BroadcastReceiver:
开机广播:
说明 - [targetSdk = 34 亲测]能启动部分前台服务,但只有授权通知后,才允许Toast和显示“前台服务运行通知”;[未测]起个悬浮窗是否能Toast?
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".nonui.BootReceiver" android:enabled="true" android:exported="true">
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
</receiver>
Android Service:
前台服务:
说明 - Activity启动的前台服务会弹出“前台服务运行通知”,但BOOT_COMPLETED则必须再授予通知权限后才会通知,不授权则只是不通知,而不会影响该服务运行。
短期服务(android:foregroundServiceType="shortService") - 3分钟后系统会触发onTimeout(int startId)来stopSelf(),优势是支持在 BOOT_COMPLETED 内启动,且不需要上架Google Play的人工审核。
调起Android系统内置邮箱应用: startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_EMAIL).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Android网络广播默认禁用,需要WifiManager.createMulticastLock(String tag)来启用。
存储:
通过 Environment.DIRECTORY_PICTURES 可获取用户更改的默认图片存储位置,实际路径通常是: /storage/emulated/0/Pictures/;但应首选非写死的 ContentUri。
contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); // 指定约定目录或子目录;或 put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/subDir")
保活:
Android一像素Activity进程保活
15分钟区间保活:WorkManager+闹钟
ROOT保活:
./adb root
./adb remount
./adb push system/priv-app/folder_name/ apk_path
./adb shell sync
./adb reboot
进程活跃度受限:
var usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
var asb = usm.getAppStandbyBucket(); // ./adb shell am get-standby-bucket PACKAGE_NAME
if (asb > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
//应用可能受限
System.out.println(asb);
}
|
FGS 任务管理器 |
向上滑动 |
强行停止 |
立即从内存中移除应用 |
✔ |
|
✔ |
停止媒体播放 |
✔ |
|
✔ |
停止 FGS/移除关联的通知 |
✔ |
|
✔ |
移除 activity 返回堆栈 |
✔ |
✔ |
✔ |
从历史记录中移除应用 |
|
✔ |
✔ |
取消预定作业 |
|
|
✔ |
取消闹钟 |
|
|
✔ |
其他
mDNS:
根据树莓派服务类型检索网络地址:nsdManager.discoverServices("_workstation._tcp.",...);
触发onServiceFound入参判断nsdServiceInfo.getServiceName().startsWith("raspberrypi")
树莓派注册的服务类型:nsdServiceInfo.setServiceType("_workstation._tcp.");