AICon 深圳站聚焦 Agent 技术、应用与生态,大咖分享实战干货 了解详情
写点什么

从开源项目中总结出的几条编码经验

  • 2020-05-17
  • 本文字数:4274 字

    阅读完需:约 14 分钟

从开源项目中总结出的几条编码经验

一、背景

之前从事过几年 chromium(chrome 浏览器内核)和 android framework 的维护开发工作,这两个项目在开源界无论从应用范围、设计模式、技术深度等都是出类拔萃的项目。通过阅读这些优秀的源码,摘录出一些优秀的代码片段和编码技巧。最近两年把这些“片段”放到应用层的开发工作上,不仅在代码细节上有些许的性能提高,也能让项目的代码风格向这些顶尖项目靠近。同时,熟悉这些编码风格后,当我们在翻阅这些开源项目源码时,也能在一定程度上减少阅读障碍。


下面分享几个摘录出来的代码片段,再结合着这些代码在优酷项目上的使用,进行一一说明。希望对大家的开发工作起到一些借鉴意义。

二、使用注解,保证方法入参的合法性

当模块对外暴露一些 API 时,特别是输出 SDK 给外界使用时,为了保证调用方对方法入参的合法性,使用注解的方式来完成是个很好的解决方式,也可以减少不同模块开发人员间的沟通成本。


  1. 先来看看 chromium 使用注解的实际案例


public final class ViewportFit {   private static final boolean IS_EXTENSIBLE = false;
public static final int AUTO = 0; public static final int CONTAIN = 1; // AUTO + 1 public static final int COVER = 2; // CONTAIN + 1 public static final int COVER_FORCED_BY_USER_AGENT = 3; // COVER + 1
public static boolean isKnownValue(int value) { return value >= 0 && value <= 3; }
public static void validate(int value) { if (IS_EXTENSIBLE || isKnownValue(value)) return; throw new org.chromium.mojo.bindings.DeserializationException("Invalid enumvalue."); }
private ViewportFit() {}}
(https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java) /** * The Viewport Fit Type passed to viewportFitChanged. This is mirrored * in an enum in display_cutout.mojom. */ @Retention(RetentionPolicy.SOURCE) @IntDef({ViewportFit.AUTO, ViewportFit.CONTAIN, ViewportFit.COVER}) public @interface ViewportFitType {}
复制代码


之后在使用上面的注解修饰方法的入参,(https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java?q=webcontentsobserverproxy.java


   @Override   @CalledByNative   public void viewportFitChanged(@WebContentsObserver.ViewportFitType int value) {      for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {         mObserversIterator.next().viewportFitChanged(value);      }   }
复制代码


对于 viewportFitChanged()这个方法来说,通过使用 @WebContentsObserver.ViewportFittype 对入参进行修饰,在编译期检查参数合法性,在方法内部也就不再需要对参数的合法性进行检查。


  1. 再来看看 github 上的一个项目对注解的使用


public class DiagonalLayoutSettings {
@Retention(SOURCE) @IntDef({ BOTTOM, TOP, B_T}) public @interface Position { }
public final static int LEFT = 1; public final static int RIGHT = 2; public final static int BOTTOM = 4; public final static int TOP = 8; public final static int B_T = 16;
@Retention(SOURCE) @IntDef({ DIRECTION_LEFT, DIRECTION_RIGHT }) public @interface Direction { }
public final static int DIRECTION_LEFT = 1; public final static int DIRECTION_RIGHT = 2;
...
}
复制代码


用注解去修饰方法参数.


public class DiagonalLayout extends FrameLayout {
DiagonalLayoutSettings settings;

public void setPosition(@DiagonalLayoutSettings.Position int position) { settings.setPosition(position); postInvalidate(); }
}
复制代码


setPosition 这个方法,通过注解来限制参数取值范围的作用很清晰了,不再赘述.


  1. 注解在优酷上的使用


举一个例子,去年我们和 UC 有个漫画合作项目,优酷输出端侧 SDK 给 UC 集成,并且同一套 SDK 也要在优酷中使用。因此,SDK 在初始化时,需要把集成方的标识设定进来。在设计给 UC 方调用的 API 时,就使用到了注解修饰参数的方法来避免集成方对 API 的调用错误。


   public void init(@NonNull Context context, @ConfigManager.Key String key, @NonNullIAppConfigAdapter appConfigAdapter,   IUiAdapter uiAdapter, @NonNull INetAdapter netAdapter, IPayViewAdapterpayViewAdapter, IPayAdapter payAdapter,   @NonNull IUserAdapter userAdapter, IWebViewAdapter webViewAdapter,   @NonNull IComicImageAdapter imageAdapter) {      ...   }
复制代码


在这里对参数 key,使用 @ConfigManager.Key 做了限制.


注解的定义:


public class ConfigManager {
/** * 分场标识key */ public static final String KEY_YK = "yk"; public static final String KEY_UC = "uc";
@Retention(SOURCE) @StringDef({KEY_YK, KEY_UC}) public @interface Key { }
}
复制代码


优酷场对这个 API 的调用:


private void initAliComicSdk() {      AliComicSDKEngine.getInstance().init(instance, ConfigManager.KEY_YK, newIAppConfigAdapterImpl(),         new IUiAdapterImpl(), new INetAdapterImpl(), new IPayViewAdapterImpl(), null,         new IUserAdapterImpl(), new IWebViewAdapterImpl(), newIComicImageAdapterImpl());   }
复制代码

三、以指定初始容量的方式来创建集合类对象

以 ArrayList 为例,通常我们创建对象时,使用 new ArrayList<>()是最常用的方式. 当我们阅读 chromium 或是像 okhttp 这些开源代码时会发现它们在构建 ArrayList 对象时,会有意识的使用 ArrayList(int initialCapacity)这个构造方法,“刻意”使用这种方式的原因其实是值得我们细细品味一下的。


  1. 还是以 chromium 为例,摘取一段它的源码.


protected static List<String> processLogcat(List<String> rawLogcat) {   List<String> out = new ArrayList<String>(rawLogcat.size());   for (String ln : rawLogcat) {      ln = elideEmail(ln);      ln = elideUrl(ln);      ln = elideIp(ln);      ln = elideMac(ln);      ln = elideConsole(ln);      out.add(ln);   }   return out;}
复制代码


再直接看 ArrayList 两种构造方法的源码, 无参方法会默认创建 10 个元素的 list.


/*** Constructs an empty list with the specified initial capacity.** @param initialCapacity the initial capacity of the list* @throws IllegalArgumentException if the specified initial capacity* is negative*/public ArrayList(int initialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = new Object[initialCapacity];}/*** Constructs an empty list with an initial capacity of ten.*/public ArrayList() {super();this.elementData = EMPTY_ELEMENTDATA;}
复制代码


两者的区别就在于,当我们往 arrayList 中添加元素发现容量不够时,它会通过调用 grow() 方法来扩容。grow()内部会以之前容量为基准,扩大一倍容量,并发生一次“耗时”的数组拷贝。因此当业务上预知 ArrayList 未来要存储大量元素时,更优雅的方式是在创建时设置初始容量,以此来避免未来内存上的频繁拷贝操作。


/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */private void grow(int minCapacity) {   // overflow-conscious code   int oldCapacity = elementData.length;   int newCapacity = oldCapacity + (oldCapacity >> 1);   if (newCapacity - minCapacity < 0)      newCapacity = minCapacity;   if (newCapacity - MAX_ARRAY_SIZE > 0)      newCapacity = hugeCapacity(minCapacity);   // minCapacity is usually close to size, so this is a win:   elementData = Arrays.copyOf(elementData, newCapacity);}
复制代码


  1. 再来看一下 okhttp 中的例子


以 request 中 headers 的 size+4 作为初始容量来创建 ArrayList 对象,因为运行时这个 result list 内部几乎每次都是要大于 10 个元素的。对于像 okhttp 这种广泛被使用的 sdk 来说,任何对代码细节的调优都是有可观收益的,同时也体现出作者对代码细节的考究。


public static List<Header> http2HeadersList(Request request) { Headers headers = request.headers(); List<Header> result = new ArrayList<>(headers.size() + 4); result.add(new Header(TARGET_METHOD, request.method())); result.add(new Header(TARGET_PATH, RequestLine.requestPath(request.url()))); String host = request.header("Host"); if (host != null) {  result.add(new Header(TARGET_AUTHORITY, host)); // Optional. } result.add(new Header(TARGET_SCHEME, request.url().scheme())); for (int i = 0, size = headers.size(); i < size; i++) {  // header names must be lowercase.  String name = headers.name(i).toLowerCase(Locale.US);  if (!HTTP_2_SKIPPED_REQUEST_HEADERS.contains(name)    || name.equals(TE) && headers.value(i).equals("trailers")) {   result.add(new Header(name, headers.value(i)));  } } return result;}
复制代码


因此在我们的优酷项目中,当每次要创建 ArrayList 时,都会下意识的想想业务上在使用这个 ArrayList 时,未来大致要存储多大量级的数据,有没有必要设置它的初始容量。


上面说的这些,不仅是对 ArrayList 有效,对像 StringBuilder 等等其他集合类来说也都是类似的。代码雷同,也就不再赘述。

三、总结

对这些编码细节上的考究,很难对业务性能指标产生可量化的提升。更有意义的点在于,我们在实际开发时,避免不了要经常参考开源项目对一些功能的实现。如果不了解这些实现细节,当读到这些代码的时候,难免对细节产生疑惑,干扰我们去理解核心实现思路。反过来说,如果我充分了解了这些细节,当读到它们的时候,往往会泯然一笑,心说我知道作者为什么要这样写,赞赏作者对代码实现的优雅,对这些开源项目的作者也产生出充分的认同感。


作者 | 阿里文娱无线开发专家 观竹


2020-05-17 07:581736

评论

发布
暂无评论

低代码开发平台实现思路探索:JNPF

互联网工科生

低代码 JNPF

WorkPlus企业内部聊天软件,如何保障企业数据和信息的安全性?

BeeWorks

探索低代码技术

树上有只程序猿

软件开发 低代码 JNPF

WorkPlus即时通讯办公软件,助力企业实现移动化办公

BeeWorks

Bitquiz重塑Learn to Earn热潮,用户零投入让学习创造价值

股市老人

那些被裁员的芯片工程师们都怎么样了?

IC男奋斗史

职业规划 裁员 芯片 半导体 ChatGPT

回归测试的实践与思考

老张

软件测试 质量保障 回归测试 测试计划

文心一言 VS 讯飞星火 VS chatgpt (114)-- 算法导论10.2 7题

福大大架构师每日一题

福大大架构师每日一题

产品经理必备的14款需求管理工具推荐!

彭宏豪95

效率 软件 产品经理 需求管理 软件需求管理

Mate60系列超预期热潮背后,品牌如何抓住营销机遇?

最新动态

一文解析iPaaS的价值及运用场景

RestCloud

ipaas

Apache IoTDB v1.2.2 发布|增加 flink-sql-connector、tsfile 文件级级联传输等功能

Apache IoTDB

Linux爆发好时机!Windows这次换代为何这么难!

树上有只程序猿

windows 11

数据结构与算法 | 数组(Array)

Java研究者

Java 算法 数组 算法题 数据结构,

互联网众包平台如何改变APP软件开发方式?

知者如C

业内首个基于Iceberg的“云端仓转湖”生产实践探索

腾讯云大数据

湖仓一体

谁在“操控”虚拟人?

自象限

虚拟人

探索工作流应用场景下解决重复审批的方法

inBuilder低代码平台

工作流 低代码

每个开发人员都想使用的编程语言

互联网工科生

rust

私有化部署AI智能客服,解放企业成本,提升服务效率

BeeWorks

DeFi/DApp矿机算力质押挖矿系统开发

l8l259l3365

OpenHarmony创新赛|赋能直播第四期

OpenHarmony开发者

NFTScan | 10.09~10.15 NFT 市场热点汇总

NFT Research

NFT\ NFTScan

从开源项目中总结出的几条编码经验_文化 & 方法_阿里巴巴文娱技术_InfoQ精选文章