本文概述
任何有经验的开发人员都会告诉你, 他们最好的代码不是他们编写的代码。他们从别人的工作中获取的代码。
是的, 我们的开发人员是创新的问题解决者, 但是我们遇到的许多问题已经解决了, 并且打包到库中的补救措施对任何人都可用。当到处都有自由轮时, 为什么要重新发明轮子呢?
Android也不例外。代码重用的最终来源是Android SDK本身, 它具有出色的结构和服务, 可以为你完成很多工作。
但是在SDK运作不畅的情况下, Android社区创建了一些顶级库, 这些库可以为你节省大量的编码工作, 而将其替换为经过高度调整, 审查和测试的实现。我不是在谈论显而易见的库-Android支持库, Android设计支持库, Gson。我指的是你可能不知道的工具。即使这样做, 你可能仍未使用它们。
标准开发人员和主要开发人员之间的主要区别之一是正确使用了第三方库。熟练的开发人员将以比新手快三倍的速度大致完成相同的任务, 并且通常使用较短的代码。很多原因是知道要使用哪些第三方库以及如何正确将它们嵌入到项目中。
多年来, 我一直在开发, 指导和领导Android团队, 并且研究和使用了许多外部工具和库。 (我什至以阅读他们的实施代码并与开发人员讨论他们的内部知识而著称。)许多人在帮助我完成工作方面非常有效, 但事实并非如此。
这就是为什么我将本指南放在一起的原因。借鉴我以及其他移动开发人员的经验, 以确保你使用的是最好的库。我选了七个。我怀疑他们很快也会成为你的最爱。
选择正确的Android库
选择库时, 我会寻找四个关键功能:
- 它为一个真实而又不重要的问题提供了一致且高质量的解决方案。
- 它使用尽可能简单的API。
- 它不会对我的整体体系结构造成任何改变。
- 它具有庞大的用户群, 最好是活跃的开发者社区。
前三个功能是破坏交易的功能。如果不存在, 我继续进行或开始手动编码。
我在下面介绍的库通过了所有四个测试。他们还解决了移动开发中一些最具挑战性的方面。
- 两个库用于依赖项注入, 布局到Java绑定, 模拟对象。
- 应用内发布/订阅消息传递模型。
- 安全, 高效, 可自我恢复的HTTP通信层。
- 图像处理:下载, 缓存, 调整大小并加载到RAM中。
- 实时视频流。
- 内存泄漏检测。
ButterKnife:最终的依赖注入工具
这是Android的最终依赖注入库。简单, 强大, 超快速(无反射!), 并且可以消除许多应用程序样板代码。
有人会认为ButterKnife才是Android到Java映射的布局。
不再需要通过调用findViewById()直接绑定每个视图;相反, 有一个带注释的视图可让你直接访问代码。ButterKnife还消除了对样板UI事件(如onClick, onTouch等)的需要, 并将其替换为自动注入的代码。
但是足够的讨论, 让我们看看代码。
视图字段绑定:
class MyButterKnifeActivity extends Activity {
@BindView(R.id.name) TextView name;
@BindView(R.id.address) TextView address;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this); // MUST BE CALLED BEFORE ACCESSING UI FIELDS
name.setText("etc etc");
}
}
资源绑定:
class ExampleActivity extends Activity {
@BindString(R.string.username) String username;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.bg_color) int bgColor;
@BindDimen(R.dimen.lower_padding) Float lowerPadding;
// and no need for getResources().getString()/getDrawable/getColor()
}
UI事件绑定:
@OnClick(R.id.my_button)
public void clickHandler(View view) {
// onClick logic goes here
}
AndroidAnnotations:将依赖注入提升到新的水平
在依赖项注入方面, 仅次于ButterKnife的AndroidAnnotations使用了一种略有不同的方法:自动生成的类, 一旦掌握了它们, 它们就非常简单。更有用的是它允许你”基于名称”的依赖项注入。例如, @ViewById ListView myUserList;指示库为该字段分配具有相同名称的layoutListView。
AndroidAnnotations也在快速发展, 但是它实现的方式与ButterKnife有所不同。与运行时绑定依赖项注入不同, AndroidAnnotations会创建所有受影响活动的构建时重复项, 并将其连接逻辑推送到这些活动中, 从而使你能够获得与手工编码逻辑相同的性能。
但是AndroidAnnotations的注入功能远不止于此。你可以将状态和布局都注入到活动中。
AndroidAnnotations的实现:
@NoTitle
@Fullscreen
@EActivity(R.layout.my_layout)
public class MyActivity extends Activity {
@ViewById
ListView customerList; // auto-binded to R.id.customerList
@App
MyApplication app; // auto-binded to app object
@AminationRes
Animation fadeoutAnimation;
@UiThread
void updateUI() {
// main thread action
}
}
最后一个注释需要更多解释:多线程Android应用程序的常见任务是从后台(或工作线程)线程切换到向前(或主线程或UI)线程, 这是唯一允许访问UI组件的线程。 。此任务虽然不复杂, 但通常是必需的, 并且涉及一些混乱的编码:
new Handler(Looper.getMainLooper()).post(new Runnable() { logic goes here } ); // NO ANNOTATIONS
在AndroidAnnotations中, 你要做的就是使用@UiThread注释函数, 现在可以保证始终执行:
@UiThread void updateUI() {..} // WITH ANNOTATIONS
请注意, 此注释适用于标准Android组件类(活动, 服务等)。但是, 当我也想注释自己的课程时会发生什么?
在这里, AndroidAnnotations提出了一个新概念, 即EBean。你所要做的就是使用@EBean将你的班级标记为此类, 你很高兴:
@EBean
public class MyNonComponentClass {
@SystemService
NotificationManager notifManager;
@Bean
MyOtherClass dependency;
@UiThread
void updateUI() {
// main thread work goes here
}
}
EventBus:跨组件通信变得轻松
EventBus库将多年来困扰着Android开发人员的问题变成了公园散步。跨组件通信从未如此简单-使用简单的pub / sub模型在系统的任何两个部分之间进行通信。
使用事件总线将产生更强大的代码, 因为它迫使你将组件彼此分离。
你的后台轮询服务不再需要知道你的片段以将更改事件提供给它们。
EventBus的用法很简单。
一个。创建事件类。最好在这里使用POJO:
class NewUserEvent {
String fullname;
String address;
String role;
// add getters and setters
}
b。在类(你希望预订这些事件的任何类)中创建事件处理方法:
class MySubscriber {
@Subscribe
public void newUserHandler(NewUserEvent event) {
// handle NewUserEvent
}
@Subscribe
public void newUserHandler(AnotherEvent event) {
// handle AnotherEvent
}
}
但是, 嘿, 任何经验不足的Android开发人员都会停下来问这点:这些处理程序的线程模型是什么?如果说它涉及UI组件访问, 是否可以强制处理程序从主线程运行?好问题…
默认情况下, 所有处理程序方法都在工作线程上运行, 该工作线程来自EventBus自己分配和维护的线程池。如果需要在主线程上运行的处理程序方法, 请按如下所示扩展订阅注释:
@Subscribe(threadMode = ThreadMode.MAIN)
public void runOnMainThreadHandler(AnotherEvent event) { … }
警告:请勿过度使用此功能!长时间运行的操作永远不要在主线程上执行, 即使是快速操作, 也要小心。使主线程不堪重负是使你的应用缓慢, 跳跃且基本上不给用户带来乐趣的最可靠方法。
C。管理你的订户类的EventBus注册生命周期-即它何时连接以及何时与总线断开连接?活动的合理注册流程为:
class MySubscriberActivity extends Activity {
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this); // START RECEIVING EVENTS HERE
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this); // NO MORE EVENTS
super.onStop();
}
}
当然, 以上只是示例。你可以在任何选择的位置执行(取消)注册。
d。最后, 实际上触发一个事件:
EventBus.getDefault().post(new MyEvent("I’m here"));
有关使用EventBus的更多信息, 包括多播事件(默认行为), 粘性事件, 传递线程, 优先级等。但是以上内容足以让你开始使用这种简单但功能强大的技术。
OkHttp:类固醇上的Android HttpClient
这就是应该编写Android的HttpClient的方式。很简单, 很聪明。 OkHttp库在内部负责重试循环, 有效负载自动压缩, Http / 2支持, 连接池和响应缓存, 因此你可以避免不必要的网络访问。
OkHttp的用法很简单。
Http POST:
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, json_str);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
Http GET:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(urls[0])
.build();
Response responses = client.newCall(request).execute();
String jsonData = responses.body().string();
OkHttp还支持诸如异步联网, 请求重定向路由查询, 本地缓存查询等有用的功能。随意在需要的地方使用它们。大多数开发人员将OkHttp用作Android默认HTTP客户端HttpURLConnection的更智能替代。实际上, 整个项目都是作为HttpURLConnection的私有派生开始的。
我喜欢它的强大功能-它会立即添加到你的网络层。
只需几行代码, OkHttp就可以使你的应用程序像你整夜调试和优化网络层一样工作。
毕加索:Google也有充分的理由使用它!
毕加索是管理图像下载, 缓存, 调整大小和裁剪的最简单, 最强大的方法。
这个说法:
Picasso.with(context).load(url).resize(50, 50).centerCrop().into(imageView)
将为你做到这一点:
- 连接到远程URL。
- 下载图片。
- 将其存储在本地LRU缓存中, 该缓存也将为你管理。
- 在将原始图像加载到内存之前, 请调整其大小。
- 在Picasso管理的线程池上运行上述所有操作。
- 使用调整后的图像来填充你的imageView。
- 在以后运行之前, 请检查本地缓存以确保确实需要网络往返。
即使对于高级开发人员, 构建以上任务集也需要花费大量时间。前提是你记住了所有内容。如果你忘记了例如调整大小部分该怎么办?
嗯, 在一般的Android设备上, 一个应用程序获得的RAM不超过50到60 MB, 大多数Android设备的像素到字节系数是4。这意味着尝试从SD卡加载13 MB的图像将需要52 MB的RAM。换句话说, 你的应用将立即崩溃。
这只是毕加索实力的一个例子。优化/调试媒体密集型旧项目时, 我要做的第一件事是将所有图像加载都切换到毕加索。你会惊讶于这一简单步骤对应用质量的影响。
该库功能的最有力证明之一:过去两年来, 许多Google自己的Android代码示例均使用Picasso进行图像加载。
ActiveAndroid:ORM Sans性能开销
ORM是对象关系映射的缩写, 在J2EE时代很流行。它使你可以将POJO存储在数据库中并从数据库中检索它们, 而不必将它们转换为单独的字段。
有帮助吗?非常重要, 因为它使你无需编写任何SQL语句即可编写大部分应用程序。
这也非常有效率。在过去, ORM平台大量依赖反射, 并且因运行缓慢而臭名昭著。包括ActiveAndroid在内的现代平台速度更快, 并且对于大多数实际需求而言, 它们不会受到原始SQL编码的性能开销的影响。
没有手工编码的SQL语句, 没有性能开销!
用法:
一个。通过扩展自定义Application类在应用程序对象中初始化:
public class MyApplication extends extends com.activeandroid.app.Application {
…
}
b。创建为模型类派生的POJO, 并为你计划存储在数据库中的每个记录创建类。每个这样的POJO都可以驻留在其自己的表中。应该使用注释为每个存储的成员指定数据库字段的名称:
@Table(name = "Categories")
public class UserDetails extends Model {
@Column(name = "Name")
public String name;
@Column(name = "Address")
public String address;
@Column(name = "Age")
public int age;
}
如果希望为成员设置索引, 请使用以下注释:
@Column(name = "ID", index = true)
public String userID;
C。为了防止库在你的所有最经典的启动时间上进行迭代(这是默认行为), 强烈建议你在以下清单部分中指定所有模型类:
<meta-data
android:name="AA_MODELS"
android:value="com.myapp.MyModelA, com.myapp.MyModelB" />
注意:未出现在此列表中的模型类不会被ActiveAndroid识别。
d。写入数据库:
UserDetails usr = new UserDetails();
usr.save(); // RUNS ON A BACKGROUND THREAD
如果需要多次写入, 则更有效的方法是在单个事务中将它们批处理:
ActiveAndroid.beginTransaction();
try {
for (UserDetails u: userList)
item.save();
ActiveAndroid.setTransactionSuccessful();
}
finally {
ActiveAndroid.endTransaction();
}
e。从数据库读取POJO:
new Select()
.from(UserDetails.class)
.where("name = ?", usr.getName())
.orderBy("Age")
.executeSingle();
在我作为服务器端开发人员的那几天, ORM是必备工具。它进入Android域的时间有些晚。但是, 最后是:数据库编程变得如此简单。好好享受。
LibStreaming:无痛视频流
由于未记录的API, 跨SDK版本的差异, 反射的使用等等, 实时视频流曾经是一个主要的难题。
幸运的是, libStreaming通过封装大多数流复杂性并公开了一个简单友好的API, 使你可以在几小时内编写基本的流应用, 从而改变了所有这些情况。
简而言之, 它简化了视频流。
要将其用于H.264和AAC, 你需要执行以下操作:
一个。使用主要活动的onCreate方法初始化会话对象。会话对象代表流向对等方的媒体:
protected void onCreate(Bundle savedInstanceState) {
mSession = SessionBuilder.getInstance()
.setCallback(this)
.setSurfaceView(mSurfaceView)
.setPreviewOrientation(90)
.setContext(getApplicationContext())
.setAudioEncoder(SessionBuilder.AUDIO_NONE)
.setAudioQuality(new AudioQuality(16000, 32000))
.setVideoEncoder(SessionBuilder.VIDEO_H264)
.setVideoQuality(new VideoQuality(320, 240, 20, 500000))
.build();
mSurfaceView.getHolder().addCallback(this);
}
b。实际开始会话:
mSession.setDestination(destination_server_url);
mSession.start();
C。完成后停止会话:
mSession.stop();
现在, 请不要误会。实时流本质上是一团糟, libStreaming并不能消除这种复杂性。但是, 它在大部分时间都对你隐藏了, 确实做得很好。在某些情况下, 你将需要处理复杂性, 例如在选择对等信令策略, 选择摄像机编码(通常希望使用MediaCodec /从表面到缓冲区)或处理数据包化时。
尽管如此, 你仍会发现libStreaming背后的好人为将这些复杂性顺利地合并到易于使用的API中付出了更多的努力。
LibStreaming支持Android应用程序使用的大多数编码器, 包括H.264, H.263, AAC和AMR。
这个图书馆给我带来了很大的收获。一些最受欢迎的流媒体应用程序将其用作基础架构的一部分。如果你有需要, 我敢肯定, 它将使你的媒体流体验更加流畅。
LeakCanary:检测一行代码中的内存泄漏
让我们从该库背后的动机开始:内存泄漏。 Android应用很容易出现这种情况, 特别是如果你不小心编码的话。实际上, 创建内存泄漏非常简单。你需要做的就是将活动引用存储在其上下文之外。实际上, 即使在活动上下文之外存储对单个视图对象的引用也会造成泄漏。
为什么?因为视图(实际上是所有视图)在内部存储了对其包含活动的上下文引用。只要保留对视图的引用, 垃圾收集器就无法回收其包含活动(连同其内部内容, 包括可绘制对象, 视图层次结构和资源)。
始终以静态参数引用泄漏活动并不总是很明显。每当你创建内部类或在活动内部生成线程时, 都会创建对该活动的引用, 并且直到该内部类或线程完成后才可以回收该活动。
泄漏当然不是Android独有的, 而是作为内存资源有限的移动系统, 其影响更为直接。
泄漏对单个资源密集型活动的引用有时足以使你的应用程序崩溃并出现”内存不足”异常。
你如何保护他们?当然, 从严格的编码实践开始。但是, 并非所有人都是有经验的Android开发人员, 甚至有经验的开发人员有时也会忘记规则。
以内存泄漏为重点的定期代码复审可能会有所帮助, 但需要一些时间。另外, 有些泄漏是真正的偷偷摸摸的, 很难通过仅代码审查来发现。
随着时间的流逝, 使用DDMS的内存工具是了解应用程序是否泄漏的好方法。你绝对应该使用它。但是, 它不会告诉你造成泄漏的原因。
泄漏泄漏到这里来进行救援。它是目前最好的内存泄漏检测器, 它可以自动(如一两行代码)对所有活动进行泄漏检测。
要使用它, 只需使用你应用程序的对象onCreate()初始化LeakCanary:
public class MyApp extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
// more initialisations
}
}
这样就完成了。 LeakCanary将监视内存泄漏, 并在检测到泄漏时发送通知。
LeakCanary通过将名为ActivityRefWatcher的对象自动注入到你的所有活动中, 并在调用onDestroy()之后监视其引用计数来实现此魔术。活动被破坏的引用计数大于0只能表示泄漏。
重要:泄漏检测仅适用于调试模式应用程序。在发布模式APK中, 切勿测试泄漏(当然, 不使用LeakCanary)。
但是, 如果我想测试系统其他部分的泄漏情况怎么办?在这里, LeakCanary提供了一个称为refWatcher的对象, 它实际上是初始化调用的返回值:
refWatcher = LeakCanary.install(this);
它可以用来观察即将回收的值。更准确地说, 我认为很快就会收回价值。为此, 请致电:
refWatcher.watch(my_soon_to_be_reclaimed_obj);
该库将让你知道在监视调用之后不久是否尚未释放该对象。
我在任何地方都找不到这种”短时间”的价值, 但这可能并不那么重要。使用leakCanary, 事情就可以了。无价。
本文总结
经验丰富的开发人员使用这些库可以节省几天和几周的编程和调试阶段, 因此没有理由你不能这样做。
综上所述, 以下是我选择的Android库可以为你做的事情:
-
ButterKnife –自动注入的代码将帮助你消除应用程序的许多样板代码。这是Android的终极代码注入。需要我多说?
-
AndroidAnnotations –使用出色的快速自动生成的类和基于名称的代码注入来节省时间, 并且不会因手工编码逻辑而造成性能损失。
-
EventBus –解耦组件以获得更强大的代码, 跨组件通信从未如此简单。
-
OkHttp – HttpURLConnection的巧妙替代, 支持异步联网, 请求重定向路由查询, 本地缓存查询等。
-
毕加索(Picasso)–精简的图像处理效果非常好, 现在Google已使用。在耗费大量媒体资源的项目和某些旧版项目中, 这可以节省大量时间。
-
ActiveAndroid – ORM轻松实现, 没有性能开销。
-
LibStreaming –实时视频流, 主要流应用程序使用。
这些是唯一值得你光顾的Android库吗?当然不是。但是我向你保证:在你的下一个项目中使用其中的任何一个将使你成为更好的开发人员。如果你想看到它们的实际效果, 请查看我的GitHub。
如果你已经在使用其中的部分或全部功能, 或者正在使用其他库, 我敦促你在下面的评论中分享你的经验。
相关内容:面向开发人员的Android 7.0:新功能, 性能升级和你不关心的其他内容