Android 入门体验
@drawable/icon解释
@:代表R.java
drawable:内部类drawable
icon:id为icon的对象
R.java文件中的drawable内部类中的id为icon的值对应的常量
@+id/button:在R文件的id内部类里面,添加一个id为button的常量,使用该常量的值作为此控件的id值;
@android:表示访问android包下的R文件;
@id/label:表示访问id为label的文件;(相对布局)
<application android:label="应用的名称"> <activity android:name=".指定activity的类名称包下面的类中(.classname)" android:label="窗口的标题" >
<!-- 意图过滤器:用于匹配意图,如果匹配成功则进行处理; -->
<intent-filter><!-- 把应用程序的图标显示到程序列表中,用于指定程序的入口activity, --> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
<uses-sdk android:minSdkVersion="8" />
清单文件的作用是在程序安装的时候读取再对程序进行功能性的注册登记;
当用第一次户点击图标后,创建进程,创建主线程,在主线程中实例化Activity(反射方式,操作系统会把应用有关的信息(Context)存放进Activity中,然后调用onCreate()方法)
Android应用程序目录
src/ java源代码存放目录
gen/ 自动生成的目录 存放所有由Android工具自动生成的文件,其中的R.java,当在res目录中添加数据时,会自动为文件添加id,可以通过这个id找到相对应的文件内容,
res/ 资源(Resource)目录 这个目录中存放各种资源文件,如xml界面,图片或数据,
assets/ 资源目录 此目录下的资源文件不会在R.java自动生成id,读取此目录下的文件必须指定文件的路径,如:file:///android_asset/xxx.3gp,
AndroidMainfest.xml 项目清单文件 此文件列出了应用程序所的功能,以后开发的各种组件(Activity,ContentProvider,BroadcastReceiver,Service)需要在该文件中进行配置,如果应用使用到系统内置的应用(如电话,短信,GPS服务),还需要在文件中声明使用权限,
default.properties 项目环境信息,
res/anim 存放定义动画的XML文件,
res/xml 在Activity中使用getResources().getXML()读取该目录下的XML资源文件,
res/raw 该目录用于存放应用使用到的原始文件,如音效文件等,编译软件时,这些数据不会被编译,它们被直接加入程序安装包里,为了在程序中使用这些资源,可能调用getResources().openRawResource(ID),ID:R.raw.somefilename,
Android中的显示单位:
为了适应不同的分辨率,不同的像素密度,一般的使用dip,文字使用sp。
Android中的进程和线程:
当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行的进程,只在内存资源出现不足时,Android会尝试停止一些进程从而释放出资源给其他新的进程使用,也能保证用户正在访问的当前进程有足够的资源去及时的响应用户的操作。Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:
1>前台进程
前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说, 在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。
2>可见进程
可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。
3>服务进程
运行着一个通过startService()方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。
4>后台进程
运行着一个对用户不可见的activity(调用过 onStop() 方法)。这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进 程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。
5>空进程
未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。
Android 对进程的重要性评级的时候,选取它最高的级别。另外,当进程被另外的一个进程依赖的时候,该进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台activity进程重要级别高,因此一个要进行耗时工作的activity最好启动一个service来做这个工作,而不是开启一个子进程――特别是这个操作需要的时间比activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service可以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目前是什么状态。broadcast receivers在处理费时的工作的时候,也应该启用一个服务而不是开一个线程。
>>>单线程模型
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事 件,并把相关的事件分发到对应的组件进行处理。所以主线程又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
单线程模型会在没有考虑到它的影响的情况下引起Android应用程序性能低下,因为所有的任务都在同一个线程中执行,如果执行一些耗时的操作,如访问网络或查询数据库,会阻塞整个用户界面。当在执行一些耗时的操作的时候,不能及时地分发事件,包括用户界面重绘事件。从用户的角度来看,应用程序看上去像挂掉了。更糟糕的是,如果阻塞应用程序的时间过长(5秒钟)Android会向用户提示一些信息,即打开一个“应用程序没有相应(application not responding)”ANR 的对话框。
其实单线程模型就是默认情况下android把所有操作都放在主线程也就是UI Thread线程中来执行,我们可以另起一个线程来执行一些操作。
所有与UI相关的操作都不可以在子线程中执行而必须在UI线程中执行! 如果需要在子线程中对UI进行操作必须要使用到下面的几个对象
Android中的消息处理机制:
Message Queue
在单线程模型下,为了解决UI更新类似的问题,Android设计了一个Message Queue(消息队列),线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。
1>Message Queue
Message Queue是一个消息队列,用来存放通过Handler发送的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列。如果没有消息队列对象则会抛出空指针异常。Android在第一次启动程序时会默认会为UI Thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。我们可以在自己的子线程中创建Handler与UI Thread通讯。也就是说我们程序一启动我们的UI线程就会有一个消息队列 ,而如果我们自己另外开启的一个子线程就不会有MessageQueue(消息队列)对象!!!
2>Handler
通过Handler我们可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列关联。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,如果你通过Handler发送消息的话,消息将只会发送到与它关联的这个消息队列,当然也只能处理该消息队列中的消息。也就是说一个Handler对应一个线程以及附属于该线程的消息队列。就比如我们现在有一个Handler对象这个Handler在UI线程中创建叫xh_Handler,那么我们根据上边的说明这个xh_Handler就是和UI线程关联的,如果我用这个Handler发消息的话它将发给UI线程的消息队列。
Handler的主要的方法有:
1) public final boolean sendMessage(Message msg)
把消息放入该Handler所关联的消息队列,放置在消息队列尾部。注意:我们把消息放进去的一端,消息不会阻塞,但是处理消息的一端就有可能会阻塞。
2) public void handleMessage(Message msg)
关联该消息队列的线程将通过调用Handler的handleMessage方法来接收和处理消息,通常需要子类化Handler来实现handleMessage。
3>Looper
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
1) 可以通过Looper类的静态方法Looper.myLooper()得到当前线程的Looper实例,如果当前线程未关联一个Looper实例,该方法将返回空(null)它不会抛空指针异常。
2) 可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实例,这里需要注意一下主线程默认是有一个Looper对象的。但是我们自己定义的子线程没有的。如果我们要在子线程中拥有自己的Looper,就可以通过调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用Looper.loop();之后就进入了消息循环,之后就可以发消息、取消息、和处理消息。如何发送消息和如何处理消息可以在其他的线程中通过Handle来做,但前提是我们的Handler知道这个子线程的Looper,但是你如果不是在子线程运行 Looper.myLooper()[得到当前线程的Looper对象],一般是得不到子线程的looper的。
Android AsyncTasks:
AsyncTasks类:异步任务。它是用来捣鼓线程的。
其实平常一般都是使用AsyncTask的,而并非Thread和Handler去更新UI,这里说下它们到底有什么区别,我们平时应该使用哪种解决方案。从Android 1.5开始系统将AsyncTask引入到android.os包中,过去在很早1.1和1.0 SDK时其实官方将其命名为UserTask,其内部是JDK 1.5开始新增的concurrent库,做过J2EE的网友可能明白并发库效率和强大性,比Java原始的Thread更灵活和强大,但对于轻量级的使用更为占用系统资源。Thread是Java早期为实现多线程而设计的,比较简单不支持concurrent中很多特性在同步和线程池类中需要自己去实现很多的东西,对于分布式应用来说更需要自己写调度代码,而为了Android UI的刷新Google引入了Handler和Looper机制,它们均基于消息实现,有时可能消息队列阻塞或其他原因无法准确的使用。
推荐使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是AsyncTask相比Thread加Handler更为可靠,更易于维护,但AsyncTask缺点也是有的比如一旦线程开启即dobackground方法执行后就无法给线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵活。
那么先看看要使用AsyncTask 类首先要做哪些工作?
1) 子类化AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onPreExecute() 开始执行前的准备工作;
doInBackground(Params...) 开始执行后台处理,可以调用publishProgress方法来更新实时的任务进度;
onProgressUpdate(Progress...) 在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result) 执行完成后的操作,传送结果给UI 线程。
这4个方法都不能手动调用。而且除了doInBackground(Params...)方法,其余3个方法都是被UI线程所调用的,所以要求:
1) AsyncTask的实例必须在UI thread中创建;
2) AsyncTask.execute方法必须在UI thread中调用;
同时要注意:该task只能被执行一次,否则多次调用时将会出现异常。而且是不能手动停止的,这一点要注意,看是否符合你的需求!
在使用过程中,发现AsyncTask的构造函数的参数设置需要看明白:AsyncTask<Params, Progress, Result>
Params对应doInBackground(Params...)的参数类型。而new AsyncTask().execute(Params... params),就是传进来的Params数据,你可以execute(data)来传送一个数据,或者execute(data1, data2, data3)这样多个数据。
Progress对应onProgressUpdate(Progress...)的参数类型;
Result对应onPostExecute(Result)的参数类型。
当以上的参数类型都不需要指明某个时,则使用Void,注意不是void。
注意:要提醒大家的的是onPreExecute()方法,该方法就是告知大家这个前置方法是什么时候执行的,当执行这个方法的时候AsyncTask的工作流程就开始了。它可以用来做一些准备和初始化的工作。这几个方法的执行顺序如下图。
http://byandby.iteye.com/blog/825071