您的位置:首页 > 移动开发 > Android开发

Android需要精通的技能总结

2016-03-09 23:58 387 查看
转载自知乎的回答,现下关于Android应该掌握的知识总结,内容精彩,情不自禁转发。

原文地址:在2016年,Android 程序员应该如何选择? - 回答作者:田元 http://zhihu.com/question/39274138/answer/89418429?utm_campaign=webshare&utm_source=weibo&utm_medium=zhihu

不知道你是不是指的纯技术方面的准备,如果是的话,我就提供一些拙见,大部分算是一些开发知识死角或者tips吧,权当抛砖引玉了:)

下面的回答建立在JAVA基础(看着《JAVA核心技术 vol1》目录能梳理一遍JAVA常见知识点)和Android基础都过关的情况下。

一、JAVA SE

1、JAVA标准容器

可能受一些网上流传的各种demo的影响,大多数Android开发者最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList三剑客,但是要知道ArrayList还有两个亲戚,一个是近亲CopyOnWriteArrayList,另外是远房LinkedList、CurrentHashMap。CopyOnWriteArrayList的效率比ArrayList略有下降,空间利用率也下降了很多,但是CopyOnWriteArrayList是线程安全的,CopyOnWriteArrayList和ArrayList对尾的操作都为O(1),但是其他位置的删除,插入操作很增加很大的时间复杂度,涉及到一次内存搬移过程,不过random access效率很高;LinkedList的随即插入和删除性能很高。

tips:数组复制,请使用System.arrayCopy或Arrays.copyOf 实现,且在JAVA中后者基于前者实现。

2、JAVA并发

1、ThreadPoolExecutor,JAVA并发的核心线程池框架,不过它的构造函数非常复杂:

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue) {

this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

Executors.defaultThreadFactory(), defaultHandler);

}

所以有一个方便我们使用的工厂类,Executors,可以创建4种类型的ThreadPool:

固定线程数量的线程池:Executors.newFixedThreadPool(int size);

单线程异步队列:Executors.newSingleThreadExecutor();

周期性调度:Executors.newSingleThreadScheduledExecutor();

多线程周期性调度:Executors.newScheduledThreadPool(1);

说到ThreadPoolExecutor的构造函数,它的最后一个参数BlockingQueue来自于包java.util.concurrent,ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue和这个包里面其他的类也要过一遍。

workQueue一定要用有界队列!设想一个极端情景,假设每个TaskWork都运行10s(更确切的说,IO等待10s),而ThreadPool在10s内接收到了1000个这样的TaskWork,如果我们使用了无界队列,队列的大小必然会急速增长直至进程Crash,但是如果我们使用了有界队列,假设队列长度为128,当TaskWork超过128,我们会有另外的线程帮忙处理,那系统的负载就可能降下来了。

上面说到了任务池的创建,那里面的任务是什么?从哪儿来呢?里面的任务是FutureTask,构造函数的参数是一个Callable对象,而我们真正的任务就在Callable对象里面的call方法,FuterTask执行实际任务后会在主线程调用done函数,最后通过ExecutorService的submit方法将FutureTask提交到任务池。发一段伪代码:

public class SomeCallable implements Callable {

public SomeCallable() {
}

@Override
public String call() throws Exception {
// some heavy work consting amounts of time
return "result";
}


}

FutureTask futureTask = new FutureTask(new SomeCallable()) {

@Override

protected void done() {

String result = get();
//dosth to result
}
};
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(futureTask);


2、一个线程sleep的小坑,我相信很多朋友都写过这样的代码:

public void run() {

try{

Thread.sleep(1000);

} catch (InterruptedException ie){

ie.printStackTrace();

}

}

但是设想一下,如果我们的线程在执行sleep之前就被interrupt了呢,别以为不可能,ThreadPoolExecutor框架就是通过对所有的Thread进行interrupt来取消所有线程,这是我们上述代码就会抛出异常。所以良好的实践应该是:

public void run() {

while (!Thread.currentThread().isInterrupted()) {
try{
Thread.sleep(1000);
} catch (InterruptedException ie){
ie.printStackTrace();
}
}


}

3、会用wait/notify来实现最简单的生产者-消费者模式。生产者/消费者问题的多种Java实现方式

4、ThreadLocal变量的理解/定义。ThreadLocal在Android中的应用,最典型的应用就是在android的messengeQueue-Looper模型中,Handler如何找到当前线程的Looper呢?我们平常直接在UI线程中new Handler()就可以了,里面就是mainLooper,但是Android怎么确定的UIx线程中new Handler()里面是mainLooper呢,答案就是通过将Looper作为ThreadLocal变量。以及ThreadLocal基本的实现原理(在线程对象里面有一个inner static class)。

5、JAVA的heap/stack的理解,可能会和多线程一起考察,blogspot.com 的页面

6、ConcurrentHashMap的实现,blogspot.com 的页面

7、 JAVA的Reflection,Android内部很多东西都是基于Reflection实现,比如我们经常用的属性动画,就是通过属性名称找到属性的getter方法名,进而通过反射调用。不过对于日常开发的话,Reflection有两个作用,一是在一些情形下(特别是对属性的操作,包括更改、比较,比如我们可以定义一个通用的Collections.sort)可以提高代码复用度,二是可以做一些比较hack的事情,比如使用一些internal class,内部的AIDL等,修改一些internal类的static value。

二、Android

1、预防内存泄漏!擅用WeakReference!

所有从类外部传来的对象(特别对于Context,View,Fragmet,Activity对象),如果要将其放进类内部的容器对象或者静态类中引用,请一直用WeakReference包装!比如在TabLayout的源码中,在TabLayoutOnPageChangeListener中,就为TabLayout做了WeakReference wrap。

2、Android IPC,Binder的理解,理解Binder的引用和实体,知道所谓的客户端和服务端分别代表什么意思,懂得ServiceManger对每个Service注册和根据服务名来getService的基本原理,这些没多少坑,但是非常庞杂,建议阅读《深入理解Android vol1》Chapter 6,深入理解Android (豆瓣)。

会写AIDL,会用Messenger夸进程传递信息,具体的实践:Android实战技术:IPC方式简介教程。

3、Activity的简要绘制(创建)过程 ,Activity本质是为了Window(PhoneWindow)服务,

onAttach:建立mwindow对象->setContentView:创建DecorView,在DecorView中根据Activity的风格来 创建Title(ActionBar),TitleContent(ActionBar下面的内容,内部资源id为android:id=”@android:id/content”)->mWindow.addView(mDecorView),将创建好的DecorView添加进window,addview时创建一个RootView,也就是我们的R.layout.activity_main的母布局,然后对子控件递归遍历发送绘图消息,子控件收到消息后 执行onMeasure->onLayout->onDraw,这时,我们就可以得到各控件的尺寸信息。

4、 在onCreate中获得视图的尺寸信息,注意到我们上面说,母控件通过遍历向子控件发送dispatchView信息来使子控件绘图,当然,我们也可以在(setContentView)这之后在子控件上post一个Runnable到控件的runnable队列中去,在其中自然可以获得正确的尺寸。

5、关于View的点点滴滴。

基础:自定义单一View,换句话说,叫自定义UI,就是仅仅在OnDraw里面做了文章,比如仪表盘,圆形头像,自定义属性

->中级:自定义View与其他系统Wdiget协同工作,比如继承LinearLayout写一个自定义的TabLayout来与ViewPager协同工作,处理事件(滑动、点击、屏幕手势)分发,处理滑动事件冲突,这个阶段才可以被称之为自定义Widget

->进阶:突破Activity,直接向Window添加、删除、更新View,理解WindowManager仅仅是个引用,真正的工作在WindowManagerService里面完成。

6、图片加载框架,熟悉并实践过基本的缓存算法LruCache、DiskLruCache,对Bitmap重采样以降低OOM的几率,熟读一款中规中矩的图片加载框架如Universal Image Loader的源码,并能将各种策略总结。

7、SurfaceView的实践,SurfaceView最常见的一个使用情境是在我们的界面之上绘制各种动画,但是有一个问题,在布局发生改变时,SurfaceView会出现部分屏幕变黑的情况,包括但不限于在ViewPager、DrawerActivity中使用SurfaceView,需要知道这个问题的解决方案。

7、熟练使用ContentProvider,并懂得大概原理(很惭愧,我对Provider没做过深入理解)

8、Activity的几种FLAG和LauchMode分别代表的意义,以及使用场景。

9、知道65535 dex-merge问题是怎么回事

10、熟悉使用Android开发者选项功能,比如最常用的 布局边界显示、过度绘制检测、UI绘制速度检测、Surface更新时闪烁、严格模式、CPU利用率展示、不保持Activity。

三、拓展(上面这些都真正透了,就没有大的漏洞了)

1、事件流编程,EventBus、RxJava。

2、Kotlin语言。

3、DataBinding,Google的这个MVVM框架实现的很完全,很强大,包括像Angular中自定义directive/filter的类似feature。

4、知道Volley,okHttp他们的应用场景。

5、熟悉一些ORM,包括现在新出的非基于SqlLite的数据库:Realm,Realm is a mobile database: a replacement for SQLite & Core Data

6、Android对Vector Icon的支持

7、Android L新特性,Palette、CoordinatorLayout自定义Behavior等

作者:田元

链接:https://www.zhihu.com/question/39274138/answer/89418429

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android