2019年马上就要过去了,这一年主要工作是公司的商业化开发。既有SDK的开发工作,也有APP的开发工作。下面从功能开发和商业化开发、SDK开发和APP开发2个方面聊一下今年的开发总结。
一、APP的功能开发和商业化开发的区别(基本和技术关系不大,主要是业务不同,简要说明)
- 基本上功能开发都是要先于商业开发的,怎么理解呢?由于现在的广告都趋于原生,就是尽量让用户体验不到这是一个广告,所以商业化基本上都是在现有的功能上面做一些二次开发,把广告的业务加进去(这里有一个原则,虽然是在功能的基础上开发,但是要尽量不要和功能耦合在一起,否则就把自己推向了无底深渊)。
- 功能开发一般都是实现APP的主要功能,比如:XX新闻,主要的就是信息流、一些界面的切换动画、视频播放等。功能开发完毕,测试验收ok、上线。基本上这个流程里面的埋点上报比较少。商业化开发就不一样了哈~广告里面什么最重要,重要的事情说3遍:上报!上报!上报!。 可以说没有上报,移动端的广告是没有任何意义的,并且各位产品大人们会有各种售卖手段,手段不同,上报就不同,其中的酸爽只有经历过的人才可以体会到。举个栗子:一个视频广告,开发功能用了2天,各种上报我加了5天,并且还是手撕产品去除了30%的上报。没有经历过的人可能我是在危言耸听,但是事实就是如此。俗话说,眼见为实、耳听为虚,我要加一句,实践过才更真实。
- 功能开发和商业开发也有共同点。就是性能问题、稳定性问题。这些问题也有很多文章,这里不展开描述。
二、SDK开发一些注意点。
1、SDK开发需要注意的点。
- 开发前要提前确认SDK的提供方式,是jar还是aar。这个涉及打包的方式,和一些资源的提供方式的不同。
- 可以懒加载的一定要懒加载,现在好多人特别反感在Application中初始化第三方SDK的,因为他们的目标是秒开(你懂的)。公司内部的人会质疑你如果不在这里初始化会有什么问题吗?如果你可以说出你的理由来也可以。那么问题来了,如果在Activity中某个时机初始化你的SDK,恰好你的初始化接口需要Context,那么你要注意了。之前一个小弟弟设计类似接口的时候遇到了问题,看下面code 1和code 2部分。
//code 1 private static Context sContext; public static void init(Context context) { sContext = context;} //code 2 private static Context sContext; public static void init(Context context) { if (context instanceof Activity) { sContext = context.getApplicationContext(); } else { sContext = context; } }
大家看到有什么问题了吗?
- SDK的网络加载模块(包括图片加载模块)要支持自定义的,因为App里面基本上都会有自己的网络模块,并且缓存也是同一份。所以如果接入方不同意我们用自己的网络模块,要支持他们自定义,这样其实减小了我们的SDK体积,也减小了我们出bug的几率(哈哈)。
- 添加App的生命周期接口(及时的清理掉SDK内部可以释放的内存,给App减轻压力)
@Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); } - 线程的使用,推荐大家使用线程池。千万不要每次在使用的时候直接new Thread().这样是非常不规范的,并且在创建线程时,最好用一个可以知道的名字,这样在后期排查问题的你会方便很多,不信你试试。具体线程池的使用,大家可以自行查看资料。
- 规范SDK对于内存的占用。其实除了一些工具类外,只要是new 出来的对象,其实都是有自己的一套生命周期的,虽然java是自动管理内存的,但是对于一个开发者来说要对自己new 出来的对象负责(何时创建、何时使用、何时销毁),如果需要频繁创建对象,我们可以利用对象池的概念来设计,这样可以尽最大可能的使我们的SDK内存保持在一个合理的范围内。这样说的比较抽象,要结合具体的业务场景,要按需实现。例如:Handler机制中的Message对象中有这么一个方法。
/** * Return a Message instance to the global pool. * <p> * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * </p> */ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }
这个对象其实在Android的机制中是会被频繁的创建的,所以内部利用缓存池的概念来缓存部分对象,这样既没有大量的创建对象,也增大了代码的执行效率。这个只是一个例子,具体的还需要大家在具体的工作中体会。
- 跨进程,不要每个AIDL启动一个service。学会Binder连接池的概念,你会轻松很多,逻辑清楚,代码可维护。参考一个可以帮你快速实现Binder连接池的项目:github.com/peiboning/a…
- SDK的稳定性。主要是指怎样手机SDK的崩溃信息。由于native收集需要引入一些三方library,这里我们主要自己收集了java部分。例如:
//因为UncaughtExceptionHandler的处理具有传递性,所以我们只要遵守了这个规范,基本上都是可以实现的 public class CrashHandler implements Thread.UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; public void init() { mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); } @Override public void uncaughtException(Thread t, Throwable e) { if (!catchMe(t, e)) { if (null != mDefaultHandler) { mDefaultHandler.uncaughtException(t, e); } } } private boolean catchMe(Thread t, Throwable e) { return true; } } - 数据缓存最好用缓存大小和缓存有效时间来控制。比如:内部用LRU来控制缓存,这个机制基本都是按照大小来控制缓存的。如果业务可以支持的情况下,建议大家可以自行修改可以按照有效时间和大小来双重控制,提高缓存的利用率。
后续再接着更新,SDK的性能优化等部分。
2019即将过去,2020大家一起加油。