记录一下Android学习过程
Intent
一般可用于启动 活动、启动服务、发送广播等场景
四大组件 —- Activity
活动的启动模式
活动有4种启动模式,分别是standard ,singleTop, singleTask, singleInstance
可以在AndroidMainfest.xml中通过
1.standard
默认的启动模式,此模式下系统不在乎返回栈中是否存在,每次启动都创新的活动实例

2.singleTop
此模式下,在启动活动时发现返回栈的栈顶已经是该活动,则直接使用,不再创建新的活动实例。但如果该活动不位于栈顶位置,则再次启动该活动时,还是会创建新的实例

3.singleTask
使用此模式,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没发现该活动实例,就会创建一个新的活动实例。

4.singleInstance
此模式不同于以上3种模式,指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果 singleTask 模式指定了不同的 taskAffinity,也会启 动一个新的返回栈)。
应用场景:假设我们的程序中有一个 活动是允许其他程序调用的 ,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,就需要用此模式,
在这种模式下会有一个单独的返回栈来管理这个活 动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实 例的问题

四种布局
1.LinearLayout
a.线性布局,将它包含的控件在线性方向上一次排列
b.排列方向:可水平方向(vertical),垂直方向排列(horizontal),修改方法:

如果LinearLayout的排列方向是horizontal,内部控件宽度不能使用math_parent,会占满整个水平方向,同理vertical时内部控件高度不可使用match_parent。
2.RelativeLayout
相对布局,与LinearLayout相比,更加随意一些,它能通过相对定位的方式让控件出现在布局的任何位置,可相对于整个容器,也可相对于某个控件
3.FrameLayout
这种布局没有任何定位方式,所有控件都会摆放在布局的左上角(应用场景不多)
4.TableLayout
使用表格方式来排列控件(不常用)
单位和尺寸
px 像素,屏幕中可以显示的最小元素单位
pt 磅数的意思,1磅等于1/72英尺,一般用作字体的单位
dp 密度无关像素,也称dip,和px相比,它再不同密度的屏幕中的显示比例保持一致
sp 可伸缩像素,采用与dp同样的设计理念,解决字体大小适配问题
密度 Android中密度就是屏幕每英寸所包含的像素数,以dip为单位。
比如一个手机屏幕的宽是 2 英寸长是 3 英寸,如果 它的分辨率是 320x480 像素,那这个屏幕的密度就是 160dpi,如果它的分辨率是 640*960, 那这个屏幕的密度就是 320dpi,因此密度值越高的屏幕显示的效果就越精细。
Android 规定,在160dpi的屏幕上,1dp等于1px,而在320dpi的屏幕上,1dp等于2px.
因此,使用 dp 来指定控件的宽和高,就可以保证控件在不同密度的屏幕中的 显示比例保持一致。
四大组件 —– Broadcast Receive
Android中的广播分为两种类型,即 标准广播 和 有序广播
1.标准广播
标准广播是一直完全异步执行的广播,广播发出后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此他们之间没有任何的先后顺序可言。这种广播效率比较高,也意味着它无法被截断

2.有序广播
有序广播是一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器能接收到这条消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。
此时广播接收器是有先后顺序的,优先级高的广播接收器可以优先收到广播消息,也可截断正则传递的广播,使后面的广播接收器无法接收广播消息

3.注册广播
分两种方式:在代码中注册(动态注册)、在AndroidMainifest.xml中注册(静态注册)
持久化计算
Android系统主要提供三种数据持久化功能—–文件存储、SharedPreference存储以及数据库存储
1.文件存储
不对存储的内容进行任何的格式化出来,所有数据原封不动存在文件中(适合存储一些简单的文本数据或者二进制数据),文件默认存储到/data/data/
2.SharedPreferences存储
使用键值对的方式来存储数据,较文件存储方便得多,而且支持多种不同数据类型的存储,即如果存入的数据类型是整型,读取出来的数据也是整形的
存储前,先获得SharedPreferences对象,Android中主要提供三种方法得到SharedPreferences对象
2.1. getSharedPreferences
Context类中的getSharedPreferences(fileName,操作模式)方法
第一个参数用于指定文件名称,指定文件不存在则会创建一个,SharedPreferences文件存放在/data/data/
第二个参数用于指定操作模式,主要有两种模式可以选 择,MODE_PRIVATE 和 MODE_MULTI_PROCESS。MODE_PRIVATE 仍然是默认的操 作模式,和直接传入 0 效果是相同的,表示只有当前的应用程序才可以对这个 SharedPreferences文件进行读写。MODE_MULTI_PROCESS 则一般是用于会有多个进程中 对同一个 SharedPreferences 文件进行读写的情况。类似地,MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 这两种模式已在 Android 4.2 版本中被废弃
2.2.getSharedPreferences
Activity类中的getSharedPreferences()方法
只接受一个操作模式参数,因为使用此方法时会自动将当前的类名作为SharedPreferences的文件名
2.3.getDefaultSharedPreferences
PreferencesManager类中的getDefaultSharedPreferences()方法
这是一个静态方法,接受一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences对象
a.向SharedPreferences文件中存储数据
分三步实现
1.调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
2.向SharedPreferences对象中添加数据,比如添加布尔类型的数据就是用putBoolean方法
3.调用commit()方法,将添加的数据提交,从而完成数据存储操作
b.从SharedPreferences文件中读取数据
使用SharedPreferences的get方法来对存储数据进行读取,每种get方法对应SharedPreferences.Editor中的一种put方法,例如读取布尔值,就使用getBoolean()方法
get方法接受两个参数,第一个是“键”,第二个是 默认值(即传入键找不到对应的值是,返回什么样的默认值)
3.SQLite数据库存储
SQLite是一筐轻量级的关系型数据库,运算速度快,占用资源少,通常只需要几百k的内存就够了
Android提供了SQLiteOpenHelper帮助类,这是一个抽象类,有两个抽象方法—onCreate() 和onUpgrade(),
使用时我们需要创建一个帮助类继承SQLiteOpenHelper,并在自己的帮助类中重写两个抽象方法,然后在这两个方法中区实现创建、升级数据库的逻辑
SQLiteOpenHelper中有两个构造方法可以重写,一般使用参数较少的的方法,此方法接收四个参数;
1.Context 2.数据库名 3.允许我们在查询数据的时候返回一个自定义的Cursor,一般传入null
4.当前数据库的版本号
3.1创建数据库:
构建出SQLiteOpenHelper实例后,再调用getReadableDatabase()或getWritableDatabase()就能创建出数据库了,数据库文件存放在/data/data/
3.2升级数据库
只需要在构建SQLiteOpenHelper时传入的高于之前的版本号,就会调用onUpgrade()
3.3SQLite数据类型
integer 整型
real 浮点型
text 文本型
blob 二进制型
四大组件 —- Content Provider
主要用于在不同应用程序之间实现数据共享的功能,它的完整机制允许程序访问另一个程序中的数据,同时又保证被访问数据的安全性。
使用内容提供器是Android实现程序共享数据的标准方式
1.访问其他程序中的数据
每个应用程序想要访问内容提供器中的共享数据,要借助ContentResolve类,可以通过Context中的getContentResolver()方法获得该类的实例
ContentResolver提供了对数据进行CRUD的操作方法,即
- insert()添加数据
- update()更新数据
- delete()删除数据
- query()查询数据
这几个方法都只需要传入Uri参数,即内容URI,内容URI给内容提供器中的数据建立了唯一的标识,由两部分组成,即权限(authority)和路径(path)
a.权限用于对不同的应用程序做区分的,为了避免冲突,都会采用程序包名的方式来进行命名,如com.example.app,则该程序对应的权限命名为com.example.app.provider
b.路径用于对同一应用程序中不同的表做区分,通常加到权限后面,比如某个程序的数据库中有两张表,table1和table2,此时路径分别命名为/table1和/table2,与权限结合起来,内容URI就变成 com.example.app.provider/table1 和
com.example.app.provider/table2
在字符串头部加上协议声明,得到最标准的URI的格式写法如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
得到URI字符串后,将其解析成Uri对象才可作为参数传入增删改查方法中
Uri uri = Uri.pase(“content://com.example.app.provider/table1”)
异步消息处理机制
Android中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue、和Looper。
###1.Messgae
Message是线程之间传递的消息,可在内部携带少量的信息,用于不同线程之间交换数据。
例如what字段,除此之外arg1和arg2字段可携带一些整型数据,obj字段携带一个Object对象
2.Handler
顾名思义就是处理者的意思,主要用你发送和处理消息。
发送消息一般是使用Handler的sendMessage()方法,发出的消息经过一系列辗转后,最终传递到Handler的handlerMessage()方法中。
3.MessageQueue
消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象
4.Looper
Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop()方法后,就会 进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将它取 出,并传递到 Handler 的 handleMessage()方法中。每个线程中也只会有一个 Looper 对象。
AsyncTask
AsyncTask 基于异步消息处理机制,Android帮我们做了很好的封装
AsyncTask是一个抽象类,使用它得用一个子类去继承它,继承时可以为AsyncTask类指定一个泛型参数。
1.Params
在执行 AsyncTask 时需要传入的参数,可用于在后台任务中使用。
2.Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为 进度单位。
3.Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值 类型。
因此,一个最简单的自定义 AsyncTask 就可以写成如下方式:
1 |
|
需要重写AsyncTask中的几个方法才能完成对任务的定制。
3.1.onPreExecute()
在后台任务开始之前调用,用于进行一些界面上的初始化,比如显示一个进度条对话框等
3.2.doInBackground(Params)
这个方法中的所有代码都会在子线程中运行,我们在这里处理所有耗时任务。任务完成后通过return语句来返回任务执行结果。如果AsyncTask的第三个泛型参数为void,就不返回执行结果。
注意:此方法中不可进行UI操作,如需更新UI元素,比如反馈当前任务进度,可以调用publishProgress(Progress …)方法来完成
3.3.onProgressUpdate(Progress…)
在后台任务中调用publishProgress(Progress…)方法后,会很快调用此方法,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值对界面元素进行相应的更新。
3.4.onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如提醒任务执行结果,以及关闭进度条对话框。
因此,一个比较完整的自定义 AsyncTask 就可以写成如下方式:
1 |
|
四大组件 —- Service
1. 定义一个服务
基本的Service这样定义,onBind 是service中唯一一个抽象方法,所以必须在子类中进行实现
1 | public class MyService extends Service{ |
让服务去处理一些事情,逻辑应该写在哪里呢?
我们又重写onCreate(),onStartCommand()和onDestroy()方法
1 | public class MyService extends Service { |
以上三个新加的方法是每个服务中最常用的方法
- oncreate() 在服务创建的时候调用
- onStartCommand() 会在每次服务启动的时候调用
- onDestroy() 会在服务销毁的时候调用
所以一旦服务启动就需要执行的动作,逻辑写在onStartCommand()方法中,在onDestroy中回收那些不需要的资源。
注意:每个服务都需要在AndroidManifest.xml中注册才能生效,这似乎是四大组件的共同特点。
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
2. 启动和停止服务
启动和停止服务主要借助Intent来实现
启动服务:
1 | Intent starIntent = new Intent(this, MyService.class); |
停止服务
1 | Intent stopIntent = new Intent(this, MyService.class); |
服务除了由活动来决定何时停止外,它也能自己让自己停下来,只需要服务自己调用stopSelf()方法即可
3.活动与服务进行通信
上面使用的方法在活动中启动和停止服务,但是服务启动后活动和服务基本没有关系了,无法得知服务的状态,如何才能让活动和服务联系更加紧密呢,让活动能随时指挥服务去执行某些任务?这就需要用到刚才那个我们必须要重写的onBind()方法了。
1 | public class MyService extends Service { |
这里我们在刚才建的MyService类中新建一个DownloadBinder类,并继承自Binder,这个类里提供方法供外部使用,我们这里提供了开始下载方法和获取下载进度的方法。然后在MyService中创建了DownloadBinder实例,并在onBind()方法中返回该实例。
如何在活动中调用服务里的方法呢?实际上当一个活动与服务绑定了之后,就可以调用Binder提供的方法了。
在活动中,我们首先创建ServiceConnection 匿名类,在里面重写onServiceConnected()方法和 onServiceDisconnected()方法,这两个方法分别在活动与服务成功绑定和解除绑定时调用。在onServiceConnecter()方法中,我们又向下转型得到的DownloadBinder实例,有了实例,我们就可以在活动中调用DownloadBinder中的任何public方法了。
1 | private MyService.DownloadBinder downloadBinder; |
别高兴太早,我们还没实现绑定呢。绑定方法如下:
1 | Intent bindIntent = new Intent(this, Myservice.class); |
bindService()接收三个参数,第一个就是刚构建出来的Intent对象,第二个参数是前面创角出来的ServiceConnection实例,第三个参数是标志位,BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务,此时MyService中的onCreate()方法会执行,但onStartCommand()方法不会执行。
解绑方法比较简单:
1 | unbindService(connetion); |
注意:任何一个服务在整个应用程序范围内都是通用的,即任何一个活动都可与之绑定,而且绑定后他们都可以获取到相同的DownloadBinder实例。
4.服务的生命周期
onCreate() ,onStartCommand(),onBind(),onDestroy()等方法都是在服务生命周期里可能回调的方法。
1.项目中任意位置调用Context的startService()方法,相应的服务就会启动,并回调onStartCommand()方法,假如服务没创建过,onCreat()方法会先于onStartCommand()方法执行。
2.服务启动后一直保持运行,直到stopService()或stopSelf()被调用
每次调用startService()方法,onStartCommand()方法就会调用一次,但每个服务只会有一个实例,无论调用多少次startService(),只需要调用一次stopService()或stopSelf()即可停止该服务
3.调用Context的bindService()方法,获取一个服务的持久连接,这是就会回调服务的onBind()方法,如果服务没创建过,onCreate()会先于onBind()方法执行。调用方可以获取onBind()方法里返回的IBinder对象的实例,这样即可与服务自由的通信了。调用方与服务之间的连接没有断开,服务就会一直保持运行状态。
4.调用startService()方法后,又调用stopService()方法,onDestroy()方法就会执行,此时服务已经销毁。类似的,调用bindService()方法后又调用unbindService()方法,onDestroy()方法也会执行。
5.Android系统机制规定,一个服务被启动或者被绑定后,就会一直处于运行状态,必须同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。
5.前台服务
服务的系统优先级比较低,内存不足时,可能被回收,而前台服务就不会因为内存不足而被回收。
与普通服务最大区别:会有一个正在运行的图标在系统状态栏显示。下拉状态栏可以看到详细信息,类似通知的效果。
创建方法类似创建通知方法:
1 | public class MyService extends Service { |
网络
1.使用HTTP协议访问网络
Android 上发送HTTP请求有两种,1.HttpURLConnection和 2. HttpClient
1.1 HttpURLConnection
先获取HttpURLConnection实例,new出一个URL对象,传入目标的网络地址,然后调一下openConnection()方法即可:
1 | URL url = new URL("http://www.baidu.com"); |
得到HttpURLConnection的实例后,设置HTTP请求所使用的方法。常用方法:GET 和 POST
GET:表示希望从服务器获取数据
POST :表示希望提交数据到服务器
1.1.1 GET
写法如下:
1 | connection.setRequestMethod("GET"); |
设置连接超时、读取超时的毫秒,以及服务器希望得到的一些消息头等。根据自己实际情况进行编写:
1 | connection.setConnectTimeout(8000); |
之后调取getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是对输入流进行读取
1 | InputStream in = connection.getInputStream(); |
最后调用disconnect()方法关闭HTTP连接:
1 | connection.disconnect(); |
1.1.2 POST
需要向服务器提交数据,采用POST的方式,数据之间用&
1 | connection.setRequestMethod("POST"); |
1.2. 使用HttpClient
HttpClient是Apache提供的HTTP网络访问接口,一开始就被引入Android API中,几乎可以完成与HttpURLConnection同样的效果,只是用法有较大的差别。
HttpClient是一个接口,无法创建它的实例,通常会创建一个DefaultHttpClient的实例:
1 | HttpClient httpClient = new DefaultHttpClient(); |
1.2.1 GET
想发起一条GET请求,可以创建一个HttpGet对象,传入目标网络地址,然后调用HttpGet的execute()方法:
1 | HttpGet httpGet = new HttpGet("http://www.baidu.com"); |
1.2.2 POST
使用POST请求比GET复杂一点,需创建HttpPost对象,并传入目标地址:
1 | HttpPost httpPost = new HttpPost("http://www.baidu.com"); |
1.然后通过一个NameValuePair集合来存放待提交的参数,
2.并将这个参数集合传入一个UrlEncodedFormEntity中,
3.然后调用HttpPost的setEntity()方法将构建好的UrlEncodedFormEntity传入
1 | List<NameValuePair> params = new ArrayList<NameValuePair>(); |
4.接下来就和HttpGet一样,调用HttpClient的execute()方法,并将HttpPost对象传入
1 | httpClient.execute(httpPost); |
1.2.3 处理返回结果
执行完上面的操作,会返回一个HttpResponse对象,包含服务器返回的所有信息。通常我们会先取出服务器返回的状态码,等于200说明请求和响应都成功了
1 | if (httpResponse.getStatusLine().getStatusCode() == 200){ |
假如得到状态码200,则可调用getEntity()方法获取到一个HttpEntity实例,然后调用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串即可:
1 | HttpEntity entity = httpResponse.getEntity(); |
假如返回数据中包含中文,EntityUtils.toString()方法会出现乱码,需要将字符集指定为utf-8:
1 | String response = EntityUtils.toString(entity, "utf-8"); |
基于位置的服务
简称LBS,利用无线电通信网络或者GPS等定位方式,确定出移动设备所在的位置。安卓中使用LocationManager类就能实现。
1.LocationManager基本用法
获取LocationManager实例,可以调用Context的getSystemService()
1 | LocationManager locationManager =(LocationManager)getSystemService(Context.LOCATION_SERVICE); |
接着需要选择一个位置提供器来确定设备当前的位置。Android中一般有三种位置提供器供选择,GPS_PROVIDER、NETWORK_PROVIDER 和 PASSIVE_PROVIDER
1.1 获取位置
将选择好的内容提供器传入到getLastKnownLocation()方法中,就可以得到一个Location对象:
1 | String provider = LocationManager.NETWORK_PROVIDER; |
Location对象中包含经度,纬度,海拔等一系列位置信息,我们从中取出想要的数据即可。
想要经度较高的数据,需要用GPS定位功能,使用之前需要先确定定位功能是否启用。
1 | List<String> providerList = locationManager.getProviders(true); |
getProviders()方法传入true表示只有启用位置提供器才会被返回,之后再从providerList中判断是否包含GPS定位功能就行。
1.2 监听位置变化
getLastKnownLocation() 方法可以获取设备当前位置信息,想要知道设备位置变化,可以使用LocationManager的requestLocationUpdates()方法,只需要传入一个LocatiionListener,配置几个参数即可:
1 | locationManager.requestLocationUpadtes(LocationManager.GPS_PROVIDER, 5000, 10, new LocationListener(){ |
参数一:位置提供器类型
参数二 : 监听位置变化的时间间隔,单位:毫秒
参数三:监听位置变化的距离间隔,以米为单位
参数四:LocationListener监听器
所以上面例子的效果是:监听GPS位置变化,每5秒检测一下,距离超过10米时,调用LocationListener的onLocationChanged()方法,把新的位置信息作为参数传入。