公开 API vs 私有 API:理解应用程序接口

  1. API 的两面性
  2. 公开 API:官方接口
  3. 私有 API:隐藏的实现
  4. 并列比较
  5. 实际案例
  6. 如何识别私有 API
  7. 设计你自己的 API
  8. 版本控制与弃用
  9. 当私有 API 很诱人时
  10. 总结
  11. 参考资料

想象建造一栋房子。有些门是为访客准备的——前门配有门铃,标识清楚且欢迎使用。其他门则仅供内部使用——杂物间、电气面板和维护通道。同样地,在软件开发中,API 有两种类型:供外部开发者使用的公开 API,以及保留给内部实现的私有 API。

API 的两面性

现代应用程序通过 API(应用程序接口)公开功能,但并非所有 API 都是平等的:

公开 API:为外部使用而设计

  • 稳定且有版本控制
  • 文档完善
  • 向后兼容
  • 长期支持

私有 API:内部实现细节

  • 可能随时变更
  • 文档极少或没有
  • 不保证兼容性
  • 可能被移除
graph TB subgraph Public["🌐 公开 API"] P1[文档化方法] P2[稳定接口] P3[版本控制] P4[长期支持] P1 --> API1[公开 API 层] P2 --> API1 P3 --> API1 P4 --> API1 end subgraph Private["🔒 私有 API"] PR1[内部方法] PR2[实现细节] PR3[可随时变更] PR4[无保证] PR1 --> API2[私有 API 层] PR2 --> API2 PR3 --> API2 PR4 --> API2 end subgraph App["📱 应用程序"] Core[核心逻辑] end API1 --> Core API2 --> Core EXT[外部开发者] --> API1 INT[内部团队] --> API2 style Public fill:#e3f2fd,stroke:#1976d2 style Private fill:#ffebee,stroke:#c62828 style App fill:#f3e5f5,stroke:#7b1fa2

公开 API:官方接口

公开 API 是开发者与平台或库互动的官方、文档化方式。

iOS 公开 API

// 公开 API:UIKit 框架
import UIKit

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 公开 API:创建按钮
        let button = UIButton(type: .system)
        button.setTitle("点我", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 200, height: 50)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        view.addSubview(button)
    }
    
    @objc func buttonTapped() {
        // 公开 API:显示警告
        let alert = UIAlertController(
            title: "你好",
            message: "你点击了按钮!",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        present(alert, animated: true)
    }
}

// 公开 API:URLSession 用于网络请求
class NetworkService {
    func fetchData(from url: URL, completion: @escaping (Data?, Error?) -> Void) {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            completion(data, error)
        }
        task.resume()
    }
}

Android 公开 API

// 公开 API:Android SDK
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 公开 API:查找视图并设置监听器
        val button = findViewById<Button>(R.id.myButton)
        button.setOnClickListener {
            // 公开 API:显示提示消息
            Toast.makeText(this, "按钮已点击!", Toast.LENGTH_SHORT).show()
        }
    }
}

// 公开 API:Retrofit 用于网络请求
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: String): User
}

class UserRepository {
    private val apiService: ApiService = RetrofitClient.create()
    
    suspend fun fetchUser(userId: String): User {
        return apiService.getUser(userId)
    }
}

Web 公开 API

// 公开 API:浏览器 API
class WebApp {
  constructor() {
    this.init();
  }
  
  init() {
    // 公开 API:DOM 操作
    const button = document.getElementById('myButton');
    button.addEventListener('click', () => this.handleClick());
    
    // 公开 API:Fetch 用于网络请求
    this.fetchData();
  }
  
  handleClick() {
    // 公开 API:控制台记录
    console.log('按钮已点击');
    
    // 公开 API:LocalStorage
    localStorage.setItem('lastClick', Date.now().toString());
  }
  
  async fetchData() {
    try {
      // 公开 API:Fetch API
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      this.displayData(data);
    } catch (error) {
      console.error('获取数据失败:', error);
    }
  }
  
  displayData(data) {
    // 公开 API:DOM 操作
    const container = document.getElementById('dataContainer');
    container.innerHTML = `<p>${data.message}</p>`;
  }
}

// 公开 API:React 库
import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    // 公开 API:获取数据
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUser(data));
  }, [userId]);
  
  return (
    <div>
      {user && <h1>{user.name}</h1>}
    </div>
  );
}

💡 公开 API 特性

稳定性:保证跨版本运作

文档:完整的指南和参考资料

支持:提供官方支持渠道

版本控制:清楚的版本号和弃用通知

测试:经过彻底测试和验证

私有 API:隐藏的实现

私有 API 是不供外部使用的内部实现细节。使用它们可能导致应用程序被拒绝或损坏。

iOS 私有 API

// ⚠️ 私有 API:访问内部 UIKit 方法
// 请勿使用 - 会导致 App Store 拒绝

// 不该做的示例:
class DangerousViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ❌ 尝试使用私有 API(假设示例)
        // 这会访问内部实现细节
        // if let privateMethod = self.perform(Selector("_privateLayoutMethod")) {
        //     // 这会让你的应用程序被拒绝
        // }
    }
}

// ⚠️ 私有 API:访问私有框架
// import PrivateFramework  // ❌ 不允许

// 实际后果:
// 使用私有 API 的应用程序会在 App Store 审查时被拒绝

Android 私有 API

// ⚠️ 私有 API:访问隐藏的 Android API
// 这些标记为 @hide 注解

// 不该做的示例:
class DangerousActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // ❌ 尝试通过反射访问隐藏 API
        try {
            val clazz = Class.forName("android.app.ActivityThread")
            val method = clazz.getDeclaredMethod("currentActivityThread")
            method.isAccessible = true
            val activityThread = method.invoke(null)
            // 这可能现在有效,但在未来的 Android 版本中会损坏
        } catch (e: Exception) {
            // API 已变更或移除 - 你的应用程序崩溃
        }
    }
}

// Android 9+ 对私有 API 的限制:
// - 浅灰名单:记录警告
// - 深灰名单:条件性封锁
// - 黑名单:永远封锁

Web 私有 API

// ⚠️ 私有 API:访问内部实现
// 这些以 _ 为前缀或标记为内部

// 不该做的示例:
class DangerousLibraryUsage {
  constructor() {
    // ❌ 访问私有属性(惯例:下划线前缀)
    this._internalState = {};  // 不要从外部访问
    this._privateMethod();     // 不要从外部调用
  }
  
  _privateMethod() {
    // 这是内部实现
    // 可能随时变更
  }
}

// ❌ 访问 React 内部
import React from 'react';

class BadComponent extends React.Component {
  componentDidMount() {
    // ❌ 访问 React 内部属性
    // const internalInstance = this._reactInternalInstance;
    // const fiber = this._reactInternalFiber;
    // 这些可能在 React 版本之间变更
  }
}

// ❌ 猴子补丁浏览器 API
// 修改内置原型
Array.prototype._myPrivateMethod = function() {
  // 这会污染全局命名空间
  // 可能与未来的浏览器功能冲突
};

⚠️ 私有 API 的危险

App Store 拒绝:使用私有 API 的 iOS 应用程序会被拒绝

运行时崩溃:API 可能在没有警告的情况下被移除

安全风险:私有 API 可能绕过安全检查

维护噩梦:代码会随着操作系统/库更新而损坏

无支持:当出问题时无法获得帮助

并列比较

面向 公开 API 私有 API
目的 外部使用 内部实现
文档 完整 极少或没有
稳定性 保证稳定 可随时变更
版本控制 语义化版本控制 无版本控制
支持 官方支持 无支持
向后兼容性 维护 不保证
App Store 审核 允许 拒绝(iOS)
测试 彻底测试 仅内部测试
弃用 提前公告 无通知即移除
访问修饰符 public、open private、internal、@hide
示例(iOS) UIKit、Foundation _private 方法
示例(Android) Android SDK @hide API
示例(Web) fetch()、DOM API _internal 属性

实际案例

iOS:状态栏高度的故事

// ❌ 错误:使用私有 API
class BadViewController: UIViewController {
    func getStatusBarHeight() -> CGFloat {
        // 私有 API - 会损坏或被拒绝
        // return UIApplication.shared.statusBarFrame.height
        // 在 iOS 13 中弃用,在 iOS 14 中移除
        return 0
    }
}

// ✅ 正确:使用公开 API
class GoodViewController: UIViewController {
    func getSafeAreaTop() -> CGFloat {
        // 公开 API - 跨 iOS 版本运作
        if #available(iOS 11.0, *) {
            return view.safeAreaInsets.top
        } else {
            return topLayoutGuide.length
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 处理安全区域的正确方式
        let topInset = view.safeAreaInsets.top
        let contentView = UIView()
        contentView.frame = CGRect(
            x: 0,
            y: topInset,
            width: view.bounds.width,
            height: view.bounds.height - topInset
        )
        view.addSubview(contentView)
    }
}

Android:隐藏 API 限制

// ❌ 错误:访问隐藏 API
class BadNetworkManager {
    fun getWifiInfo() {
        try {
            // 反射访问隐藏 API
            val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
            val method = wifiManager.javaClass.getDeclaredMethod("getPrivateWifiInfo")
            method.isAccessible = true
            val info = method.invoke(wifiManager)
            // 这在 Android 9+ 上会因限制而失败
        } catch (e: Exception) {
            // 应用程序崩溃或功能损坏
        }
    }
}

// ✅ 正确:使用公开 API
class GoodNetworkManager(private val context: Context) {
    fun getWifiInfo(): WifiInfo? {
        // 公开 API 配合适当权限
        val wifiManager = context.applicationContext
            .getSystemService(Context.WIFI_SERVICE) as WifiManager
        
        // 先检查权限
        if (ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_WIFI_STATE
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            return wifiManager.connectionInfo
        }
        return null
    }
    
    fun getNetworkCapabilities(): NetworkCapabilities? {
        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) 
            as ConnectivityManager
        val network = connectivityManager.activeNetwork
        return connectivityManager.getNetworkCapabilities(network)
    }
}

Web:框架内部

// ❌ 错误:访问 React 内部
class BadReactComponent extends React.Component {
  componentDidMount() {
    // 访问内部 React 属性
    // const fiber = this._reactInternalFiber;
    // const instance = this._reactInternalInstance;
    
    // 这些在 React 版本之间会损坏
    // React 16 -> React 17 -> React 18 都改变了内部
  }
  
  forceUpdateNow() {
    // 直接操作内部状态
    // this._reactInternalFiber.memoizedState = newState;
    // 这绕过了 React 的协调
  }
}

// ✅ 正确:使用公开 React API
class GoodReactComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  
  componentDidMount() {
    // 使用公开生命周期方法
    this.fetchData();
  }
  
  async fetchData() {
    // 使用公开 setState API
    const data = await fetch('/api/data').then(r => r.json());
    this.setState({ data });
  }
  
  handleClick = () => {
    // 使用公开 setState API
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }
  
  render() {
    return (
      <div>
        <p>计数:{this.state.count}</p>
        <button onClick={this.handleClick}>增加</button>
      </div>
    );
  }
}

// ✅ 正确:使用 Hooks 的现代 React
function GoodFunctionalComponent() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState(null);
  
  useEffect(() => {
    // 公开 API 用于副作用
    fetch('/api/data')
      .then(r => r.json())
      .then(setData);
  }, []);
  
  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

如何识别私有 API

iOS 检测

// iOS 中私有 API 的迹象:

// 1. 以下划线开头的方法
// _privateMethod()
// _internalProperty

// 2. 未在 Apple 官方文档中记录
// developer.apple.com 中没有条目

// 3. 需要导入私有头文件
// #import <UIKit/UIPrivateHeader.h>

// 4. 通过运行时操作访问
let selector = Selector("_privateMethod")
if responds(to: selector) {
    perform(selector)  // ❌ 使用私有 API
}

// 5. class-dump 显示但不在公开头文件中
// 使用 class-dump 工具查看私有方法

// ✅ 如何检查 API 是否公开:
// - 搜索 Apple Developer 文档
// - 检查是否在公开头文件中
// - 查找 @available 注解
// - 在 Xcode 自动完成中验证

Android 检测

// Android 中私有 API 的迹象:

// 1. 在源代码中标记为 @hide 注解
// @hide
// public void privateMethod() { }

// 2. 未在官方 Android 文档中
// developer.android.com 中没有条目

// 3. 需要反射才能访问
val method = clazz.getDeclaredMethod("hiddenMethod")
method.isAccessible = true  // ❌ 访问隐藏 API

// 4. Lint 警告关于受限 API
// Android Studio 显示警告

// 5. 在内部包中
// com.android.internal.*  // ❌ 内部包

// ✅ 如何检查 API 是否公开:
fun isPublicApi(className: String): Boolean {
    return try {
        // 公开 API 可以直接访问
        Class.forName(className)
        true
    } catch (e: ClassNotFoundException) {
        false
    }
}

// 检查 Android API 级别
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    // 使用 Android 9+ 中可用的公开 API
}

Web 检测

// Web 中私有 API 的迹象:

// 1. 以下划线开头的属性/方法
class Library {
  _privateMethod() { }  // ❌ 按惯例为私有
  publicMethod() { }    // ✅ 公开
}

// 2. 未在官方文档中
// 检查库的官方文档

// 3. 在 JSDoc 中标记为 @internal
/**
 * @internal
 * 这不是公开 API 的一部分
 */
function _internalFunction() { }

// 4. 在 TypeScript 中:标记为 private
class Component {
  private _state: any;      // ❌ 私有
  public props: any;        // ✅ 公开
}

// 5. 访问原型内部
// React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
// ❌ 明确标记为内部

// ✅ 如何检查 API 是否公开:
// - 检查官方文档
// - 查找 TypeScript 类型定义
// - 检查是否从主模块导出
// - 阅读 CHANGELOG 以了解弃用通知
graph TB A[需要使用 API?] B{是否有文档?} C{在公开头文件中?} D{以 _ 开头?} E{需要反射?} F[✅ 可能是公开 API
可安全使用] G[❌ 可能是私有 API
请勿使用] A --> B B -->|是| C B -->|否| G C -->|是| D C -->|否| G D -->|否| E D -->|是| G E -->|否| F E -->|是| G style F fill:#c8e6c9,stroke:#388e3c style G fill:#ffcdd2,stroke:#c62828

设计你自己的 API

创建公开 API

// iOS:设计公开框架 API
public class ImageLoader {
    // ✅ 公开初始化器
    public init() { }
    
    // ✅ 公开方法配合清楚的文档
    /// 从指定的 URL 加载图片
    /// - Parameters:
    ///   - url: 要加载的图片 URL
    ///   - completion: 加载完成时调用
    public func loadImage(
        from url: URL,
        completion: @escaping (UIImage?, Error?) -> Void
    ) {
        // 实现使用私有方法
        _performNetworkRequest(url: url, completion: completion)
    }
    
    // ❌ 私有实现细节
    private func _performNetworkRequest(
        url: URL,
        completion: @escaping (UIImage?, Error?) -> Void
    ) {
        // 内部实现
        // 可以在不影响公开 API 的情况下变更
    }
}
// Android:设计公开库 API
class DataRepository {
    // ✅ 公开方法
    /**
     * 从服务器获取用户数据
     * @param userId 要获取的用户 ID
     * @return User 对象,如果找不到则为 null
     */
    suspend fun getUser(userId: String): User? {
        return fetchFromNetwork(userId)
    }
    
    // ❌ 私有实现
    private suspend fun fetchFromNetwork(userId: String): User? {
        // 内部实现
        // 可以在不破坏公开 API 的情况下重构
        return apiService.getUser(userId)
    }
    
    // ❌ 仅供模块内部使用
    internal fun clearCache() {
        // 在模块内可用但不对外部用户开放
    }
}
// Web:设计公开库 API
class DataService {
  // ✅ 公开方法
  /**
   * 从 API 获取数据
   * @param {string} endpoint - API 端点
   * @returns {Promise<Object>} 获取的数据
   * @public
   */
  async fetchData(endpoint) {
    const url = this._buildUrl(endpoint);
    return this._makeRequest(url);
  }
  
  // ❌ 私有方法(惯例:下划线前缀)
  /**
   * @private
   */
  _buildUrl(endpoint) {
    return `${this._baseUrl}/${endpoint}`;
  }
  
  /**
   * @private
   */
  async _makeRequest(url) {
    const response = await fetch(url);
    return response.json();
  }
}

// TypeScript:明确的访问修饰符
class TypedDataService {
  private baseUrl: string;
  
  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }
  
  // ✅ 公开方法
  public async fetchData(endpoint: string): Promise<any> {
    const url = this.buildUrl(endpoint);
    return this.makeRequest(url);
  }
  
  // ❌ 私有方法
  private buildUrl(endpoint: string): string {
    return `${this.baseUrl}/${endpoint}`;
  }
  
  private async makeRequest(url: string): Promise<any> {
    const response = await fetch(url);
    return response.json();
  }
}

API 设计最佳实践

💡 公开 API 设计原则

1. 保持简单

  • 最小化表面积
  • 清楚的方法名称
  • 直观的参数

2. 彻底记录

  • 目的和用法
  • 参数和返回值
  • 代码示例
  • 边界情况

3. 谨慎版本控制

  • 语义化版本控制(主版本.次版本.修订版本)
  • 移除前的弃用警告
  • 迁移指南

4. 维护向后兼容性

  • 不要破坏现有代码
  • 添加新方法而不是变更旧方法
  • 使用 @available/@Deprecated 注解

5. 隐藏实现细节

  • 使用 private/internal 修饰符
  • 只公开必要的内容
  • 允许内部重构

版本控制与弃用

iOS API 版本控制

// 标记 API 从特定 iOS 版本开始可用
@available(iOS 13.0, *)
public func newFeature() {
    // 仅在 iOS 13+ 上可用
}

// 弃用旧 API
@available(iOS, deprecated: 14.0, message: "请改用 newMethod()")
public func oldMethod() {
    // 仍然有效但显示警告
}

// 标记 API 为过时
@available(iOS, obsoleted: 15.0, renamed: "newMethod()")
public func legacyMethod() {
    // 在 iOS 15 中移除
}

// 使用版本检查
if #available(iOS 13.0, *) {
    newFeature()
} else {
    // 旧版 iOS 的备用方案
    oldMethod()
}

// 实际示例:UIApplication statusBar
class StatusBarExample {
    func getStatusBarHeight() -> CGFloat {
        if #available(iOS 13.0, *) {
            // 新方式:使用窗口场景
            let window = UIApplication.shared.windows.first
            return window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
        } else {
            // 旧方式:直接访问(已弃用)
            return UIApplication.shared.statusBarFrame.height
        }
    }
}

Android API 版本控制

// 标记 API 的最低 SDK 版本
@RequiresApi(Build.VERSION_CODES.O)
fun useOreoFeature() {
    // 仅在 Android 8.0+ 上可用
}

// 弃用旧 API
@Deprecated(
    message = "请改用 newMethod()",
    replaceWith = ReplaceWith("newMethod()"),
    level = DeprecationLevel.WARNING
)
fun oldMethod() {
    // 在 IDE 中显示警告
}

// 使用版本检查
fun handleNotification() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // Android 8.0+:使用通知渠道
        createNotificationChannel()
    } else {
        // 8.0 之前:旧通知 API
        createLegacyNotification()
    }
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
    val channel = NotificationChannel(
        "channel_id",
        "渠道名称",
        NotificationManager.IMPORTANCE_DEFAULT
    )
    val manager = getSystemService(NotificationManager::class.java)
    manager.createNotificationChannel(channel)
}

private fun createLegacyNotification() {
    val notification = Notification.Builder(this)
        .setContentTitle("标题")
        .setContentText("文字")
        .build()
}

Web API 版本控制

// package.json 中的语义化版本控制
{
  "name": "my-library",
  "version": "2.1.3",
  // 主版本.次版本.修订版本
  // 2 = 破坏性变更
  // 1 = 新功能(向后兼容)
  // 3 = 错误修复
}

// 使用警告弃用 API
class MyLibrary {
  /**
   * @deprecated 请改用 newMethod()。将在 v3.0.0 中移除
   */
  oldMethod() {
    console.warn('oldMethod() 已弃用。请改用 newMethod()。');
    return this.newMethod();
  }
  
  newMethod() {
    // 新实现
  }
}

// 功能检测而非版本检查
class BrowserFeatures {
  supportsWebGL() {
    try {
      const canvas = document.createElement('canvas');
      return !!(
        window.WebGLRenderingContext &&
        (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
      );
    } catch (e) {
      return false;
    }
  }
  
  supportsLocalStorage() {
    try {
      const test = '__test__';
      localStorage.setItem(test, test);
      localStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  }
  
  useFeature() {
    if (this.supportsLocalStorage()) {
      // 使用 localStorage
      localStorage.setItem('key', 'value');
    } else {
      // 备用方案使用 cookies
      document.cookie = 'key=value';
    }
  }
}

// 缺少功能的 Polyfills
if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement) {
    return this.indexOf(searchElement) !== -1;
  };
}

弃用时间表

gantt title API 弃用生命周期 dateFormat YYYY-MM section v1.0 初始发布 :done, 2020-01, 2020-12 section v2.0 弃用警告 :active, 2021-01, 2021-12 文档已更新 :active, 2021-01, 2021-03 迁移指南已发布 :active, 2021-01, 2021-03 section v3.0 API 已移除 :crit, 2022-01, 2022-12

💡 弃用最佳实践

1. 提早公告

  • 给开发者 6-12 个月的通知
  • 立即更新文档
  • 在代码中添加弃用警告

2. 提供迁移路径

  • 记录替代 API
  • 提供代码示例
  • 如果可能,提供自动迁移工具

3. 遵循语义化版本控制

  • 修订版本:仅错误修复
  • 次版本:新功能,向后兼容
  • 主版本:允许破坏性变更

4. 清楚沟通

  • 变更日志条目
  • 重大变更的博客文章
  • 向用户发送电子邮件通知
  • 应用程序内警告

当私有 API 很诱人时

常见情境

// 情境 1:访问不可用的功能
// ❌ 错误:使用私有 API 自定义状态栏
class TemptingViewController: UIViewController {
    func customizeStatusBar() {
        // 私有 API 来变更状态栏颜色
        // UIApplication.shared.statusBar.backgroundColor = .red
        // 这会让你的应用程序被拒绝
    }
}

// ✅ 正确:使用可用的公开 API
class ProperViewController: UIViewController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent  // 公开 API
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setNeedsStatusBarAppearanceUpdate()
    }
}

// 情境 2:绕过限制
// ❌ 错误:访问私有属性
class TemptingTableView: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 尝试访问私有表格视图属性
        // tableView._privateProperty = value
    }
}

// ✅ 正确:使用组合或子类化
class ProperTableView: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 使用公开 API 和自定义视图
        let headerView = CustomHeaderView()
        tableView.tableHeaderView = headerView
    }
}

私有 API 的替代方案

// 情境:需要公开 API 中没有的功能

// ❌ 错误:反射访问隐藏方法
class TemptingApproach {
    fun getHiddenInfo(): String? {
        return try {
            val clazz = Class.forName("android.os.SystemProperties")
            val method = clazz.getMethod("get", String::class.java)
            method.invoke(null, "ro.build.version.sdk") as String
        } catch (e: Exception) {
            null
        }
    }
}

// ✅ 正确:使用公开 API
class ProperApproach {
    fun getBuildInfo(): String {
        // 公开 API
        return Build.VERSION.SDK_INT.toString()
    }
    
    fun getDeviceInfo(): String {
        // 公开 API
        return "${Build.MANUFACTURER} ${Build.MODEL}"
    }
}

// ✅ 正确:向平台请求功能
// 向 Google/Apple 提交功能请求
// 同时使用公开 API
// 等待官方支持

// ✅ 正确:使用第三方库
// 寻找使用公开 API 的维护良好的库
// 示例:使用 Glide/Picasso 而不是访问内部图片缓存

私有 API 的代价

// 实际示例:React 内部使用

// ❌ 错误:访问 React 内部(2016)
class OldComponent extends React.Component {
  componentDidMount() {
    // 这在 React 15 中有效
    const internalInstance = this._reactInternalInstance;
    // 在 React 16(Fiber 重写)中损坏
  }
}

// 代价:
// 1. React 16 发布时应用程序损坏
// 2. 没有提供迁移路径
// 3. 必须从头重写组件
// 4. 失去开发时间
// 5. 因错误而导致用户不满

// ✅ 正确:使用公开 API
class ModernComponent extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  }
  
  componentDidMount() {
    // 公开 API - 跨 React 版本运作
    const element = this.ref.current;
    // 对 DOM 元素做些什么
  }
  
  render() {
    return <div ref={this.ref}>内容</div>;
  }
}

// 更好:使用 hooks(公开 API)
function ModernFunctionalComponent() {
  const ref = useRef(null);
  
  useEffect(() => {
    // 公开 API - 稳定且受支持
    const element = ref.current;
  }, []);
  
  return <div ref={ref}>内容</div>;
}

⚠️ 使用私有 API 的实际后果

iOS App Store 拒绝

  • 审查期间自动拒绝
  • 延迟应用程序发布数周
  • 需要完全重写

Android 运行时崩溃

  • 应用程序在较新的 Android 版本上崩溃
  • 用户的负面评价
  • 需要紧急更新

Web 应用程序损坏

  • 库更新破坏你的应用程序
  • 库维护者不提供支持
  • 技术债务累积

业务影响

  • 停机期间的收入损失
  • 声誉受损
  • 维护成本增加
  • 浪费开发者时间

总结

理解公开 API 和私有 API 之间的区别对于构建可维护、稳定的应用程序至关重要:

公开 API

  • 供外部使用的官方、文档化接口
  • 保证稳定性和向后兼容性
  • 完整的文档和支持
  • 适用于生产环境应用程序
  • App Store 审核所需

私有 API

  • 内部实现细节
  • 可能在没有通知的情况下变更或移除
  • 没有文档或支持
  • 导致应用程序被拒绝和运行时崩溃
  • 绝不应在生产环境中使用

关键要点

graph LR A[需要功能?] B{公开 API
中可用?} C[使用公开 API ✅] D{对你的应用程序
至关重要?} E[寻找替代方案 ✅] F[请求功能 ✅] G[等待官方支持 ✅] H[使用私有 API ❌] A --> B B -->|是| C B -->|否| D D -->|是| E D -->|否| F E --> G F --> G style C fill:#c8e6c9,stroke:#388e3c style E fill:#c8e6c9,stroke:#388e3c style F fill:#c8e6c9,stroke:#388e3c style G fill:#c8e6c9,stroke:#388e3c style H fill:#ffcdd2,stroke:#c62828

决策矩阵

情况 公开 API 私有 API 建议
功能公开可用 始终使用公开 API
功能仅在私有 API 中 寻找替代方案或等待
需要快速交付 使用公开 API,即使有限制
构建内部工具 ⚠️ 优先使用公开,如果内部则接受风险
构建 App Store 应用程序 绝不使用私有 API
构建企业应用程序 ⚠️ 优先使用公开,记录风险
构建开源库 仅使用公开 API

💡 最佳实践

始终优先使用公开 API

  • 它们稳定且受支持
  • 它们不会破坏你的应用程序
  • 它们不会导致拒绝

当公开 API 不足时

  • 向平台供应商提交功能请求
  • 使用维护良好的第三方库
  • 使用公开 API 实现变通方法
  • 等待官方支持

绝不使用私有 API 于

  • 生产环境应用程序
  • App Store 提交
  • 客户项目
  • 开源库

谨慎设计你自己的 API

  • 清楚的公开/私有分离
  • 完整的文档
  • 语义化版本控制
  • 弃用警告

参考资料

分享到