Android学习笔记二
17. 在ContentProvider中定义的getType()方法是定义URI的内容类型。
18. SQLiteDatabase类中的insert/delete/update/query方法其实也挺好用的,我在EquipmentProvider类中做了实现
19. Android专门有个单元测试项目(Android Test Project),在这个项目中,可以新建一个继承AndroidTestCase类的具体测试类来单元测试某个功能。我新建了一个AndroidTestProject项目,在其中建立一个测试类专门测试Hello_wangle APP项目发布的Equipment Content Provider.
20. 在Android中对于Content Provider中数据变化的监听,是采用观察者模式来实现的:
(1)在一个AA APP中的EquipmentContentProvider中在某个方法中,比如Insert方法中,添加ContentResolver类中的notifyChange方法来通知订阅者:“this.getContext().getContentResolver().notifyChange(uri, null)”;
(2)在一个BB APP应用OnCreate方法中,使用ContentResolver类中的registerContentObserver方法来注册成观察者,订阅某个服务:“this.getContentResolver().registerContentObserver(uri, true, new EquipContentObserver(new Handler()));”
(3)在一个CC APP应用中,比如AndroidTestProject测试APP中,执行testInsert方法来调用AA APP中的EquipmentContentProvider中的Insert方法时(通知观察者),此时BB APP中的EquipContentObserver(观察者)中的onChange方法会被自动执行。
21. 在Android系统中,内部的通讯录、短信、图片等内容都是通过Content Provider对外提供的。它们Content Provider的URI通过文档或Android源码来获取。
22. Content Provider中有个applyBatch方法,用来处理在同一个事务中完成多个ContentProvider中的操作方法(insert/update/delete等)。
23. 通过网络URL打开HttpURLConnection,之后获得InputStream,读成byte[]:
如果是图片的话,使用BitmapFactory转化为Bitmap;
如果是网页的话,转化成String即可。
24. 滚动视图ScrollView,当拥有很多内容,屏幕显示不完,需要通过滚动条来显示的视图。
25. 在string.xml文件中定义了某个元素后,在android的代码上可以使用R.string.xxx来代表元素内容;在java代码上可以使用getResources().getString(R.string.xxx)来获取内容值。
26. web项目中,我们可以使用JSP返回HTML页面、XML数据和JSON数据:
在JSP页面里,使用page命令中的contentType属性指定返回类型(text/html、text/xml、text/plain),之后使用html格式内容、xml格式内容或者json格式内容(json格式的字符串数据)来填充即可。
例如,返回json的jsp页面内容:<%@ page language=”java” contentType=”text/plain; charset=utf-8” pageEncoding=”UTF-8” %>${json}//(json格式数据)
返回xml的jsp页面内容: <%@ page language=”java” contentType=”text/xml; charset=utf-8” pageEncoding=”UTF-8” %><xyz>…${data}…</xyz>//(xml数据)
27. 使用GET或者POST方式提交参数给WEB应用,使用URL原始方式或者HTTPClient第三方jar包来实现。通常只提交参数我们使用URL原始方式来,如果使用到Cookie或者HTTPS的协议,HttpClient第三方jar包使用起来更方便。
GET方式:参数名值对跟在uri后面,使用整体数据创建URL,发送数据到WEB;
POST方式:uri建立连接,使用输出流发送实体数据(参数名值对拼接字符串)给WEB.
28. 使用网页上传文件到WEB项目时,使用IE中的httpwatch插件可以查看到网页在上传文件和输入数据时提交的数据实体内容,我们在android中编写上传文件程序时,就是拼接此实体数据内容,把他们发送给WEB项目。
对于比较大的文件,我们可以使用多次outputstream输出文件中的内容;当然,对于相对比较小的文件,可以得到byte数组一次输出。具体上传文件的功能可以参考SocketHttpRequester类。
29. WEB SERVICE就是网络上的API,我们在java里面有WEB SERVICE的第三方框架调用,不会涉及到内部知识。在ANDROID中,我们发送内容类型为“soap+xml” 的XML数据给WEB SERVICE服务提供者,实现WEB SERVICE调用;而WEB SERVICE服务提供者也会返回响应XML数据,需要获取、解析发送的指令是否正确。
30. Android重要原则:
(1)耗时的工作应该在子线程中执行,如果主线程执行耗时的工作,处于工作状态超过5秒的话,因主线程阻塞而无法处理用户的输入事件,系统会抛出“应用无响应”的错误。
(2) UI控件画面的重绘(更新)是由主线程负责处理的,如果在子线程中更新UI控件的值,更新后的值不会重绘到屏幕上。一定要在主线程中更新UI的值,这样才能在屏幕上显示出来。
(3)Handler作用是用于往创建Handler对象的线程所绑定的消息队列发送消息。sendMesage方法是往消息队列中发送消息;handleMessage方法是处理消息,消息队列中有消息时,主线程自动调用handleMessage,需要重写Handler类,覆盖Handler类的handleMessage方法。
31. (1)通过HTTP协议实现多线程断点续传下载;
(2)通过TCP/IP(socket)实现文件断点上传:
http协议不支持断点上传。此功能需要客户端和服务器端自定义协议来完成。客户端在连接时发送文件长度、文件名称和sourceid(客户端标识,第一次连接时为空)给服务器,服务器返回sourceid(服务器端定义)和position(文件写的位置)给客户端,客户端从这个位置读取文件信息,发送给服务器,服务器也会从文件的这个位置开始写入数据。如果在客户端上传一次大文件过程中断电,当客户端使用原先分配的sourceid再次连接到服务器时,此是服务器会给客户端发送上次已经将要上传文件位置position,客户端从这个位置开始取文件内容发送给服务器,服务器也会从此文件位置开始写数据,达到断点续传功能。
32. Intent(意图)用于激活组件,还可以携带数据。
startActivityForResult用于新启动的Activity在关闭时向老的Activity发送数据。当老的Acitivity调用finish关闭时,会自动执行新Activity的onActivityResult方法。所以在新的Activity中要重构onActivityResult方法
33. 滚动视图的实现用scrollview,scrollview中可以加入textview;scrollview中也可以加入linearlayout,这样相当于scrollview中可以加入多个view,从而做到滚动视图。
34. Activity启动模式:
(1)standard:默认启动模式,每次激活“Activity时都会创建Acitivity实例,并放入任务栈中
(2)singleTop:如果在任务的栈顶正好存放该Activity实例,会重用该实例(调用实例的onNewIntent方法),否则会创建新的Activity实例放入栈顶。(注:即使栈中已经存放Activity实例,只要不再栈顶,就会创建该Activity实例放入栈中)。
(3)singleTask:如果在栈中有Acitivity实例,就重用该实例(调用该实例的onNewIntent方法)。重用时,会让该实例移到栈顶,因此在其上面的实例会被移出栈。如果栈中不存在该实例,会创建该实例放入栈中。
(4)singleInstance:在一个新栈中创建该Activity实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例存在于某栈中,任何应用再激活该Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。
35 在Android中,使用Intent可以激活Android应用的三种类型的核心组件:活动、服务、广播接收者。
Intent可以划分显式意图和隐式意图。
显式意图明确指定了要激活的组件是哪个组件;
隐式意图:没有明确指定组件名的Intent. Android系统会根据隐式Intent中设置的动作(action)、类别(category)、数据(URI和数据类型)来找到最合适的组件来处理这个意图。
l startActivity(intent)会为intent增加android.intent.category.DEFAULT类别
l 只要Intent中Action、category(和数据)都出现在组件的intent-filter中,就能与之匹配,否则匹配失败。
36 Activity的生命周期:
Activity的三种状态:运行状态、暂停状态、停止状态
Activity完整生命周期:第一次调用onCreate开始,调用onDestroy为止;
Activity可视生命周期:调用onStart开始,调用onStop为止;
Activity前台生命周期:调用onResume开始,调用onPause为止;
37 解惑:Activity的启动模式和Activity生命周期
l Activity的4种启动模式,指的是激活活动的情况下,程序中可用startActivity(intent)来激活;当然我们在手机上可以用手点程序图标来激活活动;
l 手机上的后退按键如果把应用回退到桌面上,那么会把这个应用的启动Acitivity杀死(onDestroy方法)。如果在应用里,从主活动中激活一个子活动,这个子活动的视图显示在手机界面上,主活动的状态从onResume()进入onPause(),可能也可能进入onStop()状态,如果此时使用回退按键回退到主活动的界面,那么主活动从而又进入onResume(),在此过程中,主活动只是状态发生变化,没有激活主活动过程,当然就是不涉及活动的启动模式。
38 广播接收者:
l 广播接收者用于接收广播intent。
l 实现广播接受者,需要扩展BroadcastReceiver类,当接收到广播intent内容后,会执行此类的onReceiver方法,此方法中的intent参数就是接收到的广播intent。
l 广播接收者是一个Android组件,需要在android清单文件(AndroidManifest.xml)中使用<receiver/>元素声明,在此声明中,可以注明广播接收者要处理的广播intent内容,也可以声明处理intent的优先级别。
l MIUI系统短信拦截功能的正常运行前提是关闭MIUI系统默认的“系统短信优先”选项。2014-5-19
l 系统在接收到短信后,会发送一个有序广播intent,我在项目中实现了一个短信拦截功能,当拦截到短信intent 后,会把收到的短信内容保存在存储卡上的message.txt文件中。
l 短信窃听原理:当系统收到短信时,会发送一个广播Intent,Intent的action名称为android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,我们使用“pdus”即可从Intent中获取短信内容。
l 广播intent的发送是通过调用context.sendBroadcast(),context.sendOrderedBroadcast()来实现的。通常一个广播intent可以被订阅了此intent的多个广播接收者所接收。
l 广播分成两种:普通广播和有序广播。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有广播者接收到,使用的方法是context.sendBroadcast(),相对于有序广播传输效率高,缺点是接收者不能将结果传输给下一个接收者,并且发送终止广播intent的传播;有序广播是按照接收者声明的优先级别,被接收者依次接收广播。优先级别在<intent-fileter>中的android:priority属性中声明,数值越大,优先级别越过,取值范围:-1000~1000.有序广播的接收者可以终止广播intent 的传播(使用abortBroadcast()方法),广播intent的广播一旦终止,后续广播接收者就无法接收到广播。有序广播的接受者可以将数据传递给下一个接收者(通过setResultData()和getResultData()方法)。
l 系统接收短信,发送的广播属于有序广播。如果想阻止用户收到短信,可以调整接收者的优先级别,让自定义的接收者优先接收到短信广播,然后关闭短信广播的传输即可实现。
l 电话拨打的广播Intent在发送时就确定要拨号的接收者接收,所以拦截功能是使用setResultData(null)来传递给拨号器的接收者,检测为null后,不会把电话拨出去。小米手机Adroid4.0系统没有能够拦截外拨电话的功能,应该跟系统有关系,就不再纠结于此了。2014-5-20
l 在Android系统中,每次广播消息到来时都会创建BroadcastReceiver实例并且执行onReceiver()方法,onReceiver()方法执行完后,BroadcastReceiver的实例就会被销毁。当onReceiver()方法在10秒内没有执行完毕,Android会认为该程序无响应。所以,如果需要执行耗时的工作,应该通过发送Intent给Service,由Service来完成,这里不能用子线程来解决。因为BroadcastReceiver的生命周期很短,子线程可能还没有结束BroadcastReceiver就先结束了。
l 除了短信到来广播Intent,Android还有很多广播Intent,比如开机启动、电池电量变化、时间已经改变等广播Intent。
l 广播接收者一旦安装在系统后,就是自己运行的。
39 服务:
l 是一个Android组件,类似于windows的服务,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发监控之类的程序。
l 服务继承自Service类,需要在AndroidManifest.xml文件中配置;
l 服务不能自己运行,需要通过context.startService()或者context.bindService()方法启动服务。使用startService()方法启动服务,访问者与服务之间没有关联,即使访问者退出了,服务仍然运行。使用bindService()方法启动服务,访问者与服务绑定在了一起,访问者一旦退出,服务就终止。
l 采用context.startService()方法启动服务,只能调用context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
l 当service第一次被启动时会调用onCreate()方法,然后再调用onStartCommand()方法。在该service的生命周期内,如果再次启动这个service,就会直接调用onStartCommand()方法了,而且启动的是同一个service对象。
l 服务结束时会调用onDestroy()方法,注意对于服务启动时新建的子线程,要将其停止。有时在服务A启动时会启动另外一个服务(比如电话监听服务),如果在服务A结束时不手动将其结束,那么另外一个服务(由这个服务启动的服务,比如电话监听服务)仍然会运行。
l 如果服务和访问者之间需要方法调用或者传递参数,则需要使用bindService()和unbindService()方法启动关闭服务。
l 采用context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法,这个时候访问者和服务绑定在一起。如果访问者与服务进行通信,那么,onBind()方法必须放回IBinder对象。如果访问者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-àonDestroy()方法。如果访问者退出了,系统也会调用服务的onUnbind()-àonDestroy()方法。
l 访问者(比如Acitivity一个实例)调用Service中方法的过程原理:
l 使用AIDL(Android Interface Definition Language:接口定义语言)和远程服务来实现进程通信(IPC)。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC).
我们可以在一个APP中使用bindService(intent)来激活远程服务(在另外一个APP中),intent可以只赋值action即可,不要赋值class(在同一个APP中使用的)。
过程原理:
我们在服务端APP程序里,使用AIDL定义一个接口A,A定义要实现的方法,Android会自动生成符合java语言规范的接口A代码,再来定义一个类B来继承接口A中的Stub类(存根类),实现A中定义的方法,然后通过OnBind方法返回B的实例(存根类里面已经实现了IBinder接口);
在访问者APP应用中,首先也会定义一个AIDL接口(把服务端的AIDL接口拷贝即可),Android也会自动生成相关的接口代码,然后我们在继承ServiceConnection类的类方法onServiceConnected中把参数IBinder接口转换成接口A(要使用接口A中的Stub类方法),这样应用方法中就可以使用A来做方法调用了。
40 服务的生命周期:
l 当采用Context.startService()方法启动服务,与之相关的生命周期方法:
OnCreate()--àonStart()--àonDestroy()
onCreate()方法只会被调用一次,服务只会创建一次。
onStart()方法可能会被调用多次,多次调用startService()方法会被多次调用。
OnDestroy()方法在服务被终止时调用。
l 当采用Context.bindService()方法启动服务,与之有关的生命周期方法:
onCreate()--àonBind()--àonUnbind()--àonDestroy()
多次调用bindService()方法不会导致onBind()方法被多次调用,在启动服务时才会回调该方法;
OnUnbind方法在调用者与服务解除绑定时被调用。