最近2周预研做得差不多了,没什么新功能点要研究,就回头再看看android in action这本书,翻翻google的文档和API,感觉进展比之前做预研的时候慢一些,现在把这两周总结到的一些东西记一记
1. 当发生ANR时,记录会保存在data/anr/traces.txt里
2. 关于LBS(Location Based Service),基于位置的服务。关键是要获得坐标信息,然后就可以根据坐标来实现各种业务。android提供了两种基本的方式来获取坐标,一种是基于GPS,一种是基于NETWORK,包括基站定位和WIFI定位。
GPS的优点是只要有GPS传感器,就可以得到坐标,得到坐标的原理有很多,包括时间差等等。GPS定位的优势是坐标比较精确,而且即使没有网络也是可以的(当然得到坐标以后的后续处理,没网络还是不行的)。缺点是当在隧道,或者室内的时候,接收不到卫星信息,就无法得到坐标。android中对应GPS定位的provider是GPS_PROVIDER
基站定位的优点是,只要在基站的覆盖范围内(换言之,手机有信号),就可以得到cell id和LAC(Location Area Code)。然后通过将cell id和LAC发送到某个系统,这个系统就可以返回位置信息。这有个条件,就是某个系统拥有位置信息。可能是运营商提供的系统(通常要收费),也可能是免费系统(比如常用的google)。这个方式的缺点是得到的坐标可能不会很精确,在一个基站覆盖范围内的任何一点,得到的都是同样的坐标,坐标的精确度取决于基站小区的覆盖密度。另外同样的cell id和LAC,发送给不同的系统,得到的坐标也有可能是不同的。
android中对应基站定位的provider是NETWORK_PROVIDER。另外,还没看过源代码,但我猜测如果用android api来做基站定位,那应该默认是把cell id和LAC发送到google服务器上,那么就必须要求能上网了。其实如果位置信息系统提供了短信网关接口,也是可以不上网的,这样就不能用android api了,而是要获取到cell id和LAC(可能还要求别的参数,根据位置信息系统提供商提供的接口决定),然后以短信方式发送到位置信息系统,也是可以的。我们这次就打算这么做
3. 关于adapter,现在理解得多了一点。有一类View叫做AdapterView,比如ListView就是AdapterView的一种。这类的View有一个setAdapter(Adapter adapter)方法,通过这个方法,来update View。
ListView listView = (ListView)findViewById(R.layout.list_view);listView.setAdapter(new MyAdapter(this));
上面的listView在初始化的时候是没有数据的,也就是没有ListItem,通过setAdapter()方法,才填充了数据。
Adapter类的作用,就是根据数据源,来返回View,每个View最终会成为AdapterView的其中一个Child View。如同其名字一样,Adapter是一个数据到视图的适配器。可以将其理解为是一个容器的处理器,它将容器内的每一项,映射成一个View并返回。数据源可以是资源文件指定的数据集合,也可以来自SQLite,也可以是内存中的自定义对象……总之是一种数据对象的集合。而将要返回的View,是由Adapter中的getView()方法创建并返回的,可以来自layout文件的设置,也可以是自定义的View对象。
搞清楚原理,就明白AdapterView和Adapter是怎样协同工作,生成页面的了。Adapter根据数据源,循环调用getView()方法,生成View对象的集合。然后AdapterView.setAdapter()方法,用前面生成的View对象集合,来给视图生成数据项。ListView只是AdapterView的一个子类,还有很多其他的AdapterView,但是原理都是相同的。有空的话可以看看ListAdapter类的getView()方法的源码,应该会对这个过程理解得更清晰一些。
4. 搞清楚了上一点,再来看ListActivity,其实ListActivity只是一个普通的Activity,只是它内置了一个ListView,以及提供了getListView()方法来获取内置的ListView;还提供了setListAdapter()方法,来给这个内置的ListView设置Adapter;以及诸如onListItemClick()等方法而已。在要使用ListActivity的地方,用普通的Activity也是完全可以的,只是要多写一些方法而已。
5. 在前面的博客里提到过,关于UI Thread有2个原则。1:不要阻塞UI Thread,2:不要在UI Thread之外操作视图组件。今天补充一点,除了进一步封装的AsyncTask类之外,android主要是通过提供Handler类来支持这一点的。
private final Handler handler = new Handler(){ public void handleMessage(Message msg){ doSomeThingWithView();// 在UI Thread中对视图组件做操作 }};public void someMethod(){ new Thread(){ public void run(){ someLongTimeJob();// 在worker Thread中做一些耗时操作,以免阻塞UI thread handler.sendMessage();// 发送消息到UI Thread } }.start();}
通过以上的代码结构,来遵循上述的2个原则。当然,用AsyncTask来处理,代码会更加简洁一些。不过看到比较多的源代码,还是采用这种结构来做的。其他方式都不太好,或者有阻塞UI Thread,造成ANR的问题。或者是在worker Thread中操作了View组件,可能隐藏了一些多线程情况下的BUG
Handler就是android提供的,不同线程之间通信的一种方式。用无参数构造器的方式创建Handler实例,则将其绑定到当前线程(UI Thread)的Looper中。Looper是和Handler紧密相关的一个类,开发者一般不会直接操作Looper类。这个Looper内部维护一个Message Queue,会对每条Message进行处理。没有看过源码,不过我估计这个过程应该是异步的。
6. 关于Layout布局,现在我还搞得不是太清楚,也是目前最薄弱的一块,只能先简单理解一下书上说的内容。
首先每种ViewGroup都有一个内部类LayoutParams,这个LayoutParams有一个继承体系,比如ViewGroup.LayoutParams->ViewGroup.MarginLayoutParams->LinearLayout.LayoutParams->TableLayout.LayoutParams
然后貌似每个组件的最终Layout是由其Parent决定的,而其自身的Layout又决定了它的Child的Layout布局,这是一个递归的过程。
A child View requests a size, and the parent makes a decision on how to position the child view on the screen. The child makes a request and the parent makes the decision. Child elements do keep track of what size they're initially asked to be, in case layout is recalculated when things are added or removed, but they can't force a particular size. Because of this, View elements have two sets of dimensions: the size and width they want to take up [getMeasuredWidth()] and the actual size they end up after a parent's decision [getWidth()].
暂时就知道这么多,以后再补充一点。我个人感觉这是android中比较难的一部分,可能是以前我没做过swing的开发,所以不太好类比。android布局和我熟悉的html+css布局差别还是挺大的
7. View组件还有一个focus的概念。即虽然页面上有很多组件,但同时只能有一个组件获取到焦点,来响应用户输入。用户的操作,可能导致焦点的改变。这点和html一样,很好理解。通常“下一焦点”是由android系统决定的,但是开发者也可以通过在XML中设置nextFocusDown等属性,来改变默认行为。另外View也有requestFocus()方法,来请求获取焦点。类似jquery中的$(element).focus()
8. android中的事件响应机制类似设计模式中的“观察者模式”,一个事件分2个阶段:the component raising the event and the component(or components) that respond to the event. 前者类似Button.setOnClickListener(OnClickListener listener),后者类似listener.onClick(View v)。这里要再次重申一下,由于View接口是单线程的,所以无论何时,要记得只能在UI Thread中调用View上的方法(包括更新View)
9. 程序中用到的资源,统一放在res文件夹下,res文件夹下的子目录是有命名规范的,乱命名就认不出来(也就是,无法编译,然后通过R.java来引用)。比如res/drawable下放图片,res/layout下放布局XML,res/values下放strings、colors、styles、arrays等,具体的可以看google的API文档。然后这些资源文件会由aapt工具编译成binary,之后程序中就可以通过R Class来获取这些编译后的资源
10. Resources类是所有资源的统一入口,提供了如getStringArray(int),openRawResource(int),getXml(int)等方法