01-18-08 废弃API的处理方式API废弃概述API废弃Deprecation是软件演进的必然过程Android通过渐进式废弃策略保证生态系统平稳过渡。废弃原因1. 安全问题 - 存在安全漏洞 - 无法修复的设计缺陷 2. 性能问题 - 低效的实现 - 资源浪费 3. 设计改进 - 更好的替代方案 - 接口简化 4. 平台演进 - 新特性支持 - 架构调整Deprecated注解基本用法/** * Deprecated注解使用 * frameworks/base/core/java/android/app/ActivityManager.java */publicclassActivityManager{/** * Android 1.0 原始API * Android 5.0 废弃 * * deprecated 从API 21开始废弃使用{link #getRunningAppProcesses()}替代 */DeprecatedpublicListRunningTaskInfogetRunningTasks(intmaxNum)throwsSecurityException{try{returngetService().getTasks(maxNum);}catch(RemoteExceptione){throwe.rethrowFromSystemServer();}}/** * 推荐的替代方法 */publicListRunningAppProcessInfogetRunningAppProcesses(){try{returngetService().getRunningAppProcesses();}catch(RemoteExceptione){throwe.rethrowFromSystemServer();}}}Kotlin Deprecated/** * Kotlin废弃注解 */classExample{/** * 废弃方法 - 提供替代方案 */Deprecated(messageUse newMethod() instead,replaceWithReplaceWith(newMethod()),levelDeprecationLevel.WARNING)funoldMethod(){// 旧实现}/** * 新方法 */funnewMethod(){// 新实现}/** * 强制废弃 - ERROR级别 */Deprecated(messageThis method is no longer supported,levelDeprecationLevel.ERROR)funobsoleteMethod(){throwUnsupportedOperationException(Method is obsolete)}/** * 隐藏废弃 - HIDDEN级别 */Deprecated(messageInternal use only,levelDeprecationLevel.HIDDEN)funinternalMethod(){// 仅内部使用}}/** * DeprecationLevel级别 * - WARNING: 编译警告代码仍可用 * - ERROR: 编译错误但可通过Suppress抑制 * - HIDDEN: 完全隐藏无法使用 */渐进式废弃策略废弃时间线阶段0: 正常APIAPI N ├─ API稳定 ├─ 文档完善 └─ 广泛使用 阶段1: 标记废弃API N1 ├─ 添加Deprecated注解 ├─ 更新JavaDoc说明废弃原因 ├─ 提供替代方案 └─ IDE显示删除线 阶段2: 行为限制API N3 ├─ targetSdk N3: 限制功能或返回默认值 ├─ targetSdk N3: 保持原有行为兼容性 └─ 运行时日志警告 阶段3: 移除APIAPI N6 ├─ 完全移除代码 ├─ 抛出UnsupportedOperationException └─ 更新兼容性文档 典型时间跨度6-8年约3-4个Android大版本实际案例getExternalStorageDirectory/** * 存储API废弃演进 * frameworks/base/core/java/android/os/Environment.java */publicclassEnvironment{/** * 阶段1: Android 10 (API 29) 标记废弃 * deprecated 使用Context#getExternalFilesDir(String)替代 */DeprecatedNonNullpublicstaticFilegetExternalStorageDirectory(){returnsCurrentUser.getExternalDirs()[0];}/** * 阶段2: Android 10 行为变更 * targetSdk 29: 启用分区存储 */privatestaticFilegetExternalStorageDirectoryInternal(){ApplicationInfoappInfoActivityThread.currentApplication().getApplicationInfo();if(appInfo.targetSdkVersionBuild.VERSION_CODES.Q){// 分区存储模式限制访问Log.w(TAG,getExternalStorageDirectory() is deprecated, use getExternalFilesDir() instead);// 返回应用私有目录returnsCurrentUser.getExternalDirs()[0];}else{// 兼容模式返回公共存储returngetDataDirectory();}}}HttpClient废弃案例/** * Apache HttpClient废弃 * Android 6.0 (API 23) 移除 */// Android 5.1及以下可用importorg.apache.http.client.HttpClient;importorg.apache.http.impl.client.DefaultHttpClient;HttpClientclientnewDefaultHttpClient();// Android 6.0及以上移除// 编译错误Cannot resolve symbol DefaultHttpClient// 替代方案1HttpURLConnectionURLurlnewURL(https://example.com);HttpURLConnectionurlConnection(HttpURLConnection)url.openConnection();try{InputStreaminurlConnection.getInputStream();// 读取数据}finally{urlConnection.disconnect();}// 替代方案2OkHttp推荐OkHttpClientclientnewOkHttpClient();RequestrequestnewRequest.Builder().url(https://example.com).build();Responseresponseclient.newCall(request).execute();targetSdkVersion行为限制废弃API的targetSdk检查/** * 基于targetSdk的行为调整 * frameworks/base/core/java/android/app/ApplicationPackageManager.java */publicclassApplicationPackageManagerextendsPackageManager{/** * 获取已安装应用列表 * Android 11 (API 30): 包可见性限制 */OverridepublicListApplicationInfogetInstalledApplications(intflags){ApplicationInfoappInfomContext.getApplicationInfo();// targetSdk 30: 包可见性过滤if(appInfo.targetSdkVersionBuild.VERSION_CODES.R){// 只返回可见的应用returngetInstalledApplicationsFiltered(flags);}else{// targetSdk 30: 返回所有应用兼容性returngetInstalledApplicationsUnfiltered(flags);}}/** * 获取应用签名 * Android 9.0 (API 28): GET_SIGNATURES废弃 */OverridepublicPackageInfogetPackageInfo(StringpackageName,intflags)throwsNameNotFoundException{ApplicationInfoappInfomContext.getApplicationInfo();// 检查是否使用废弃的GET_SIGNATURESif((flagsGET_SIGNATURES)!0){if(appInfo.targetSdkVersionBuild.VERSION_CODES.P){// targetSdk 28: 记录警告Log.w(TAG,Use GET_SIGNING_CERTIFICATES instead of GET_SIGNATURES);}}returngetPackageInfoAsUser(packageName,flags,getUserId());}}Notification API废弃/** * Notification API演进 * frameworks/base/core/java/android/app/Notification.java */publicclassNotificationimplementsParcelable{/** * Android 4.1之前的方式废弃 * deprecated 使用{link Builder}替代 */DeprecatedpublicNotification(inticon,CharSequencetickerText,longwhen){this.iconicon;this.tickerTexttickerText;this.whenwhen;}/** * Android 4.1 推荐方式Builder模式 */publicstaticclassBuilder{privateContextmContext;privateNotificationmN;publicBuilder(Contextcontext){this(context,null);}/** * Android 8.0 必须指定Channel */publicBuilder(Contextcontext,StringchannelId){mContextcontext;mNnewNotification();// Android 8.0: 检查targetSdkApplicationInfoappInfocontext.getApplicationInfo();if(appInfo.targetSdkVersionBuild.VERSION_CODES.O){// 必须设置Channelif(TextUtils.isEmpty(channelId)){thrownewIllegalArgumentException(Must specify notification channel for targetSdk 26);}}mN.channelIdchannelId;}publicBuildersetSmallIcon(inticon){mN.iconicon;returnthis;}publicBuildersetContentTitle(CharSequencetitle){mN.extras.putCharSequence(EXTRA_TITLE,title);returnthis;}publicNotificationbuild(){returnmN;}}}废弃API的文档处理JavaDoc废弃标记/** * JavaDoc废弃文档示例 */publicclassApiExample{/** * 获取设备ID * * p此方法在Android 10 (API 29)中废弃出于隐私保护考虑。 * * pstrong废弃原因/strong * ul * li可被用于跟踪用户/li * li无法重置/li * li违反用户隐私/li * /ul * * pstrong替代方案/strong * ul * li使用{link java.util.UUID#randomUUID()}生成应用级唯一ID/li * li使用{link android.provider.Settings.Secure#ANDROID_ID}获取设备ID可重置/li * li使用{link com.google.android.gms.ads.identifier.AdvertisingIdClient}获取广告ID/li * /ul * * pstrong迁移指南/strong * pre{code * // 旧代码已废弃 * String deviceId telephonyManager.getDeviceId(); * * // 新代码推荐 * String androidId Settings.Secure.getString( * context.getContentResolver(), * Settings.Secure.ANDROID_ID * ); * }/pre * * return 设备ID * throws SecurityException 如果应用没有READ_PHONE_STATE权限 * deprecated 从API 29开始废弃使用替代方案 * see android.provider.Settings.Secure#ANDROID_ID */DeprecatedRequiresPermission(android.Manifest.permission.READ_PHONE_STATE)publicStringgetDeviceId(){ApplicationInfoappInfomContext.getApplicationInfo();if(appInfo.targetSdkVersionBuild.VERSION_CODES.Q){// API 29: 抛出异常thrownewSecurityException(getDeviceId is no longer supported for targetSdk 29);}// API 28-: 返回实际值兼容性returngetDeviceIdInternal();}}API差异文档Android API差异文档API 29 → API 30 废弃的API 1. android.telephony.TelephonyManager - getDeviceId() [废弃] 原因隐私保护 替代Settings.Secure.ANDROID_ID - getSimSerialNumber() [废弃] 原因隐私保护 替代无替代方案需用户授权 2. android.os.Environment - getExternalStorageDirectory() [废弃] 原因分区存储 替代Context.getExternalFilesDir() 3. android.content.pm.PackageManager - getInstalledApplications() [行为变更] 原因包可见性 影响targetSdk 30 只返回可见应用 迁移声明queries标签 新增的API 4. android.window.WindowMetrics getBounds() [新增] 用途获取窗口边界 替代Display.getSize()已废弃 行为变更 5. 后台位置权限 - 需要单独请求ACCESS_BACKGROUND_LOCATION - 不能与前台位置权限一起请求Lint检查规则自定义Lint检测废弃API/** * 自定义Lint检测器检测废弃API使用 */classDeprecatedApiDetector:Detector(),SourceCodeScanner{overridefungetApplicableMethodNames():ListString{returnlistOf(getDeviceId,getExternalStorageDirectory,getRunningTasks)}overridefunvisitMethodCall(context:JavaContext,node:UCallExpression,method:PsiMethod){valannotationmethod.getAnnotation(java.lang.Deprecated)if(annotation!null){// 检查targetSdkvalprojectcontext.projectvaltargetSdkproject.targetSdkif(targetSdk29){context.report(DEPRECATED_API,node,context.getLocation(node),Deprecated API should not be used with targetSdk 29,createQuickFix(method))}}}privatefuncreateQuickFix(method:PsiMethod):LintFix?{// 根据废弃的方法提供快速修复returnwhen(method.name){getDeviceId-{LintFix.create().replace().text(method.name).with(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID)).build()}else-null}}companionobject{valDEPRECATED_APIIssue.create(idDeprecatedApi,briefDescriptionUsing deprecated API,explanationThis API has been deprecated and should not be used,categoryCategory.CORRECTNESS,priority6,severitySeverity.WARNING,implementationImplementation(DeprecatedApiDetector::class.java,Scope.JAVA_FILE_SCOPE))}}废弃API的运行时处理异常抛出/** * 废弃API运行时异常 */publicclassTelephonyManager{/** * 获取设备ID - 废弃API */DeprecatedRequiresPermission(android.Manifest.permission.READ_PHONE_STATE)publicStringgetDeviceId(){ApplicationInfoappInfomContext.getApplicationInfo();if(appInfo.targetSdkVersionBuild.VERSION_CODES.Q){// targetSdk 29: 抛出异常thrownewUnsupportedOperationException(This method is no longer supported. See https://developer.android.com/training/articles/user-data-ids);}// targetSdk 29: 记录警告Log.w(TAG,getDeviceId() is deprecated, see https://developer.android.com/training/articles/user-data-ids);// 返回实际值兼容性returngetDeviceIdInternal();}}日志警告/** * 废弃API调用日志 * frameworks/base/core/java/android/util/Log.java */publicfinalclassLog{/** * 记录废弃API调用 */publicstaticvoidwarnDeprecatedApi(Stringapi,Stringreplacement){ApplicationInfoappInfoActivityThread.currentApplication().getApplicationInfo();StringmessageString.format(Application %s (targetSdk%d) calls deprecated API: %s, use %s instead,appInfo.packageName,appInfo.targetSdkVersion,api,replacement);Log.w(DeprecatedApi,message);// StrictMode检测if(StrictMode.getVmPolicy().penaltyListener!null){StrictMode.onDeprecatedApiUsed(api);}}}StrictMode检测/** * StrictMode检测废弃API */objectDeprecatedApiMonitor{/** * 启用废弃API检测 */funenableDetection(){if(BuildConfig.DEBUG){StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyListener(Executors.newSingleThreadExecutor()){violation-Log.e(StrictMode,VM policy violation,violation)// 检测废弃API使用if(violationisDeprecatedApiViolation){handleDeprecatedApiViolation(violation)}}.build())}}privatefunhandleDeprecatedApiViolation(violation:DeprecatedApiViolation){valmessage Deprecated API used: API:${violation.apiName}Stack:${violation.stackTrace}Replacement:${violation.replacement}.trimIndent()Log.w(DeprecatedApi,message)// 上报到监控系统reportToAnalytics(violation)}privatefunreportToAnalytics(violation:DeprecatedApiViolation){// 上报到Firebase/Sentry等}}总结废弃API处理原则渐进式废弃分阶段执行标记→限制→移除充分通知JavaDoc、Release Notes、开发者文档提供替代明确指出替代方案和迁移路径兼容性保证targetSdk N 保持原有行为运行时提示日志警告、异常信息工具支持Lint检查、IDE提示废弃时间线API N: 发布新API API N1: 标记旧API为Deprecated API N3: targetSdk N3 限制功能 API N6: 完全移除旧API 时间跨度: 6-8年最佳实践及时更新targetSdk关注API废弃通知使用Lint检测废弃API测试不同Android版本提供降级方案Android 16 (API 36) 废弃API变化Android 16 新增废弃API后台启动Activity完全废弃// Android 16targetSdk 36 时以下方法受限// startActivity() 从后台调用将抛出异常Deprecated(message从后台启动Activity在Android 16被完全限制,levelDeprecationLevel.WARNING)funstartFromBackground(context:Context,intent:Intent){if(Build.VERSION.SDK_INTBuild.VERSION_CODES.BAKLAVA){// 使用 PendingIntent 或通知启动valpendingIntentPendingIntent.getActivity(context,0,intent,PendingIntent.FLAG_UPDATE_CURRENTorPendingIntent.FLAG_IMMUTABLE)// 通过通知栏触发}}旧版前台服务API废弃// Android 16不带类型的前台服务完全废弃Deprecated(message使用 startForeground(type, notification) 替代,replaceWithReplaceWith(startForeground(ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC, notification)),levelDeprecationLevel.ERROR// targetSdk 36 直接编译错误)funstartForegroundServiceLegacy(){// 旧方式不再允许}// 新方式funstartForegroundServiceModern(){startForeground(ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC,notification)}StrictMode 废弃API检测增强// Android 16StrictMode 对废弃API的检测更严格StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder().detectAll().penaltyLog()// Android 16 新增废弃API使用可配置为崩溃.penaltyDeathOnDeprecatedApiUsage()// 调试模式.build())关键要点API废弃是Android演进的必然通过渐进式策略和targetSdk检查确保生态系统平稳过渡