前言
强烈建议大家不要去读第一行代码这本书,疯狂Android还是可以读一下的,里面有的细节可以关注一下,大致有一个印象,对有的总结进行一下理解,这样我认为是有利于培养自身的对开发本身的理解能力和思考能力,我这个条目性的基础框架只是给你的学习之路打一个草稿,至于你想前进开发的何种方向,我认为每一个人的机会不同,尽可能给自己更多的机会和平台,少年,一起嗨。
关于通讯制式
只要记住随着通讯制式的发展,移动通讯的网速会越来越快,这是移动开发的基石.
Android开发的体系结构(重点)
分为四层:linux内核层-Library层-Framework框架层-Application应用层
1. linux层,主要是用作于硬件驱动
2. Library层,主要是c/c++编写的so库,作为上层应用和linux层的桥梁,在Android运行时,Android应用运行的Dalvik虚拟机以及核心库
3. 框架层,Android为了规范应用开发而提供的规范化接口,java语言编写
4. 应用层,开发者开发的app,包括系统引用和第三方应用
dalvik虚拟机
1. 执行的文件类型不同,效率上存在差异
2. 避免与oracle公司的版权的问题
3. JVM→.java→.class→.jar
4. Dalvik→.java→.class→.dex→.apk
ART模式
ART虚拟机是在Android4.4版本提出来的,5.0版本开始作为默认的虚拟机去使用
ART虚拟机和Dalvik虚拟机最大的区别,前者采用采用AOT预编译,第一次的安装时间长,需要更大的存储空间,好处是运行效率高,简单讲就是用空间
换时间,后者采用的JIT运行时编译,运行效率低
关于分辨率(必须能够说出来)
1. ldpi:240*320
2. mdpi:320*480
3. hdpi:480*800
4. 720p:720*1280
5. 1080p:1080*1920
adb常见命令(掌握)
adb devices 查询连接的设备以及启动adb调试桥
adb kill-server 关闭adb调试桥
adb start-server 开启adb调试桥,也可以直接直接使用"adb devices"命令开启
adb -r install apk全路径名 安全apk应用,参数"r"是为了替换安装
adb uninstall apk全路径名 卸载应用
注意:连接到真机需要安装驱动
DDMS的简介(经常使用)
全称为Dalvik Debug Monitor(监视) Service 是一个具有图形化界面的服务进程
adb掉线问题
重启我们的adb使用我们前面提到的命令
通过cmd命令"netstat - ano"找出所有占用5037端口的进程,记下进程PID去任务管理器张将其关闭掉,之后重启我们的adb
如果上述的方法无法解决adb掉线的问题,我们可以重启我们的eclipse
最后的方法就是重启电脑了
应用程序开发流程(理解)
1. 第一步当然是编写布局文件,在layout文件夹下修改我们所要使用的xml文件
2. 第二步当然就是找到特定的控件(可以通过控件的id找到特定的控件对象),之后添加必要的点击事件,编写我们的监听器
3. 第三步当然是如何处理这个事件了
4. 第四步我认为极为关键,就是需要权限(permission)的操作必须添加权限,在我们的清单文件AndroidManifest.xml中添加我们的所需要的权限,这种权
限被声明之后,在安装应用时,需要经过用户同意才能安装
5. 注意:在程序调试中必须添加必要的log日志方便我们的调试,我们可以在Logcat(日志猫)中进行查询,Logcat也属于DDMS
布局(重点中的重点)
1. 线性布局(LinearLayout,必须掌握)
orientation 这个我就不多讲了
weight 权重属性,算法:控件本身的宽高+父容器剩余宽高*控件所占的比例
2. 相对布局(RelativeLayout,也是重点)
相对于父控件(parent)
相对于子控件(需要设置控件的id)
注意:通常来讲一个控件的属性有着三种参照方式:
第一种参照其自身
第二种参照父控件
第三种是作为子控件或者内容的参照
3. 帧布局(FrameLayout,重点)
关于layout_gravity属性是参照于父控件的布局
gravity属性是作为子控件或者内容(内容包含文本内容)的参照
要记住这种布局的子控件是重叠摆放的
4. 表格布局以及绝对布局(了解就可以了,table和absolute)
表格布局是线性布局的子类,使用tableraw控件实现表格效果
绝对布局已经被废弃掉了,就是通过像素坐标进行布局(原来乐视电视上曾经采用过)
常见单位(记住就可以了,能说出个所以然来)
px,也就是我们平时所说的像素
dip/dp,算法:px = dip*density/160
sp,文字的大小全部使用这个单位(能适配到不同大小的屏幕上),假设想要固定文字的大小可以使用dp单位
点击事件的四种写法(重点)
1. 写个类继承点击监听器的接口,重写里面的方法,创建这个类的对象(这个写法太麻烦,不推荐)
2. 通过匿名内部类的方法创建监听对象(在工作之中轻度使用)
3. 让当前的类实现监听接口,重写里面的方法,用一个switch语句对其的id进行区分
4. 在xml中配置onClick属性,在Activity中添加对应的点击事件
junit测试(Android下,轮不到我们做,了解一下)
创建一个Android测试工程,指定要测试的工程
之后创建一个类继承AndroidTestCase
编写测试方法,必须使用public修饰访问权限,要声明异常,throws Exception,这样异常才能被测试单元所捕获到,也可以使用断言进行测试,比方说断
言两个值是否相等:assertEquals()方法,xml文件系统已经为我们配置好了
在outline(通过视图可以将其调出)中右键方法进行测试运行
logcat的使用(前面提到过,我说两个调试经常用到的)
以后不要用syso语句了,Log.d(tag,内容)调试日志
Log.e(tag,内容)表示错误日志,颜色为红色
Android下文件的存储(非常关键)
通过流的方式进行读写,注意不能写在其它应用的文件之中,当然可以写在sd卡之中,只是需要我们声明一下权限
此处提一下获取文件目录的方法(重点)
上下文.getFileDir(),应用私有文件夹的目录,在清除数据时会被清除掉
上下文.getCacheDir(),缓存目录,在清除缓存时会被清除掉
对sd卡的操作
必须声明一下对sd卡的读写操作权限
检查sd卡是否可用,获取sd卡的状态,判断下是否处于被挂载的状态
获取一个文件的可用空间可以使用文件对象.getFreeSpace()
获取sd卡的目录可以使用Environment类去获取
文件权限
我们两个应用程序的私有文件是不能相互访问的,我们可以在两个应用的manifest.xml文件中的manifest根节点下配置相同的shareUserId
属性,这样就可以相互访问了
当然还可以直接创建一个全局文件,当然我们不推荐这种方式,通过上下文.openFileoutput(文件名,设置权限);
关于读写流,注意:必须及时关闭流,关闭流的原则是先开后关,后开先关
SharePreference(太关键)
首先我们当然要获取一个sp对象:使用上下文.getSharePreference(文件名,访问权限)获取,访问权限我们默认设置为0,就是私有文件的含义,也可以
通过上下文.其它访问权限来设置其它的访问权限
之后就是往里面添加数据了
第一步是获取Editor对象,通过sp.edit()方法获取
通过Editor对象.put各种类型的数据,参数为键值对
之后我们要提交数据
commit()方法提交在当前线程中进行,可能会阻塞主线程,有一个boolean类型的返回值,true表示提交成功了,false表示提交失败了
apply()方法是异步提交,不在当前线程中进行提交,不会阻塞主线程,我们推荐使用这种方式进行数据的提交,不存在返回值
最后的一个步骤当然是获取我们添加的数据,当然还是我们熟悉的get方法获取各种不同的数据类型,根据我们设置的不同的key
xml文件
用StringBuilder写你就太low了
当然要用xml的序列化器进行xml文件的写操作
初始化我们的xml序列化器
书写文档的开头和结尾startDocument和endDocument
书写开始标签和结束标签
书写属性attribute
书写文件text
之后我们当然要读取xml文件,我们称之为解析
解析方法
SAX解析方式,一旦开始解析就不可以停下来,只有解析完成才能停止
DOM:将整个文档加载进我们的内存,形成文档树,内存的开销太大
PULL解析,基于事件的解析,可以控制解析的进程,我们在Android中采用这种解析方式去解析我们的xml文件
解析步骤
同样地也是对我们的解析器进行初始化,获取解析对象,之后设定我们读取的解析文件流
获取我们当前的标签值,通过while循环遍历所有的标签,对标签中的内容或者属性进行操作
解析完成之后,关闭我们的流
数据库的存储
前面我们已经谈到了三种数据存储的方式分别是I/O流存储,sp存储以及xml存储,接下去就是最后一种了:数据库的存储
创建我们的数据库
创建一个类继承SQLiteOpenHelper(最后能够重新创建一个存放数据库类的包),之后重写其的构造方法,只保留上下文参数,在构造方法中指定
数据库的名称(后缀为.db),不包含一个游标工厂就默认为null,最后一个参数为版本号,从"1"开始
在Activity中创建这个类的对象
通过这个类对象.get一个可读或者可写的数据库,这样一个数据库文件就在应用的私有文件夹中真正被创建了
创建我们的表结构,其中onCreate()方法只有当数据库被创建时才会被执行一次,后续不会执行,我们可以在这个方法中创建一个表格的结构,通过数
据库对象.execSQL(sql语句)方法创建一个表
数据库的升级:数据库只能升级不能降级,操作会在其的onUpgrade()种进行,要更新必须执行对象,get可读或者可写的操作
数据库的增删改查操作,我相信你们已经掌握了sql语句,大不了去网上搜索一下,需要注意的是在SQLITE中alter table不支持drop column(删除字段)
以及rename column的功能,只有add column的功能,如果需要我们必须重建一张表格
查询操作执行的方法数据库对象.rawQuery(sql语句)方法,返回一个游标对象Cursor,这个对象是一个资源对象,用完之后不需要关闭,必须掌握
其的使用方式
采用命令行查询我们的sqlite数据库
adb shell(进入到linux命令模式下)
cd 打开数据库文件的目录,ls命令可以浏览所有该目录下的文件
sqlite3 数据库名,可以进入数据的命令模式下
执行特定的sql语句就可以了
.schema 表名:查询表结构 .tables:查询数据库所有的表 .exit:退出
关于中文乱码:chcp 65001;(cmd命令模式下),将当页面的编码模式设定为utf-8模式
数据库另一种更删改查的方法(必须掌握下,就不用sql语句)
数据库对象直接调用.insert()方法
第一个参数是表格的名称
第二个参数字段名是否有空的,假设为空可以使用这个参数的设定值进行代替
第三个参数是插入的内容,是一个ContentValues对象,键为地段,值为设定的值
数据库直接调用.delete()方法
第一个参数还是操作的表名称
第二个参数是设置的条件参数,可以使用占位符,例如:"name=?"
第三个参数可以对这个占位符进行设置,是一个数组,例如:new String[]{name};
数据库对象直接调用.update()方法
第一个参数还表名
第二个参数是修改的内容,还是使用ContentVlues对象
第三个参数设置条件
第四个参数当然还是对占位符进行赋值
数据库对象直接调用.query()方法
第一个参数当然是表名
字符串数组,查询的字段
第三个参数条件
第四个参数还是对占位符进行赋值
数据库的事务模式
功能当然是为了防止数据库操作中出现意外的,有的操作进行了,有的操作还未被执行,造成数据的错误
将所有对数据库的操作包裹在.beginTransaction()和setTransactionSuccessful()之内,只有当执行endTransaction()方法所有对数据库的
操作才能真正被执行
通过这种绑定,所有关于数据库的操作要么被同时执行完毕,要么全部未被执行,不会造成数据上的错误
列表控件(listview是重中之重)
通常情况下我们可以ScrollView来实现,需要注意的是只能有一个直接的子view
LIstView的使用
使用了MVC设计模式
M就是数据模型,就是产生数据的一个模块,比方说从一个配置文件中获取数据的操作,就是一个数据模型
V就是视图,就是直接呈现出来的界面
C就是将控制器,主要就是将我们的数据模型和视图进行衔接
使用步骤
第一步当然还是布局,指定宽高和id
第二步还是初始化我们的ListView对象
第三步当然是设置我们的适配器,最为重要的是重写里面的getCount()方法,设置Item数量,getView每个Item显示的view
对其的优化,我们说listview会自动进行回收view对象,可是假设Item太多,下拉操作太快可能也会崩溃,所以我们可以复用getView()方法参数
中的convertView(缓冲view)
当我们编辑的Item布局太过复杂时,我们可能需要独立去编写一个xml布局文件,我们可以使用View.inflate()方法将这个布局文件变成一个
view对象返回给我们的item,需要注意的是在findViewById时不能直接用我们的上下文对象,而要用这个被view对象进行调用找到其内部的
控件对象
通知我们的适配器刷新数据
当我们的数据发生变化时,要重新设定我们的数据,使用适配器对象.notifyDataSetChanged(),这样就可以及时的更新列表中的数据
快速拖动条的设置
需要在布局文件中的listview控件中设置一个fastScrollEnable的属性,将其的值设置为true
常见的适配器
我们使用最多的当然还是BaseAdapter
还有一个适配器,ArrayAdapter,其中第一个参数是上下文,第二个参数是一个布局文件,这个文件是一个textview控件,可以在里面设置文本
属性,可是却不能有其它的子控件,最后参数当然是显示的文本内容
常见的对话框(要会调用,毕竟是学Android开发的)
确定取消对话框
创建一个对话框构建对象AlertDialog.Builder对象
setTitle:设置对话框主题,例如提醒或者警告之类的
setMessage:设置对话框主题下的内容,具体的提醒内容
setPositiveButton(按钮文本,点击事件):确定按钮
setNegativeButton(按钮文本,点击事件):取消按钮
最后要通过builder对象将其show出来,这是一个封装的方法,封装了创建dialog对象,之后diaolog.show()的内容
单选对话框
也是创建一个Builder对象
也可以设置主题
setSingleChoiceItems(字符串数组,被选中的索引当其为负数表示不选中任何一个选项,设置点击事件)
我们也可以设置一个取消的按钮
多选对话框
builder对象就不讲了,setMultiChoiceItems()
第一个参数还是字符串数组.表示条目
第二是boolean数组,和条目相对应,表示该条目是否处于被选中的状态
第三个参数是我们熟悉的点击事件
进度对话框
创建进度对话框对象:ProgressDialog
setTitle:设置标题
setMessage:设置内容,比方说正在加载
也可以设置setMax()进度的最大值
setProgress()设置当前的进度
也要通过进度对象将其show()出来
由于不存在取消按钮,我们可以通过进度对象调用dismiss()方法进行取消操作
其它(只是在此处提一下)
图形动画
记住图形文件也可以编写xml文件,在图形文件中编写
动画对象需要.start()方法开始播放
应用的国际化
创建不同的values文件夹或者图形文件夹,后缀改为特定国家的简写,这样在我们将系统语言更改时就能采用不用的文件夹对应用进行配置
样式和主题
样式和主题在我们的values文件夹中的style.xml文件中进行配置,可以继承,通过parent属性进行设置@特定的样式配置
使用style标签书写我们的样式标签,里面可以添加不同的item标签设置不同name属性的样式值,在控件中我们可以通过style属性来引用我们我
们的设置的样式,另外主题和样式的定义是完全相同的,就是作用域不同.
主题是Application节点以及Activity节点里面的一个属性,属性的名称为theme,值也就是我们所定义的样式文件
补充两个方法
addView():添加一个子控件
removeAllView():删除所有的控件,前面曾经提到过,在listview的案例中曾经用到过,此处不要遗漏了这个知识点
网络访问(不掌握,打断你的腿)
首先当然是获取到一个网址或者说链接,URL地址:new URL(String path)
通过这个地址对象打开一个连接对象,由于http协议下的请求,所以直接获取到一个http协议下的连接对象:url.openConnectin();
获取到的connection对象.setRequestMethod(请求方式),请求方式为get/post,必须全字母大写
setConnectionTimeOut:设置连接的超时时间,默认单位为毫秒,通常会将其设置5s-10s,太长会影响我们的用户体验
获取我们的响应码,getResponseCode(),200表示正常访问
通过connection对象.getInputSteam获取响应的资源
如果是图片资源我们可以使用BitmapFactory.decodeStream(字节码)将其解码为一张位图
注意:在主线程中不能有请求网络的操作,在android4.0之后会直接抛出异常,另外还有要注意的就是必须添加访问网络的权限(permission),释放资源
我必须在提一下
setRequstProperty(属性名称,属性值),我们可以设置其的userAgent属性,这个属性主要包含了硬件平台,系统软件,应用软件以及用户的个人偏好,有
用到需要设置的同学还是去网上搜这个属性好了,还有就是服务器默认返回给我们的数据是一个html文件
关于中文乱码的处理,统一编码就可以了
encode(编码):编码是将内容转化为字节码形式的数据,编码的例子就是我们的在eclipse中写好的代码就会通过特定的编码方式被保存
decode(解码):是将字节码数据转化为字符内容,比方说一个文件在我们的eclipse中打开时就是一种解码的方式
关于json解析(重点)
webservice就是提供一部分日常生活常用的数据供我们使用,现在好多已经不可用了,比方说天气预报,可能租用服务器太贵了
当我们的path路径中存在中文时我们需要对其进行编码:URLEncoder.encoder(要编码的中文,编码格式)
json数据中{}:表示json对象,json中的数据已键值对的形式出现,以":"分隔键值对,不同的数据用","进行分隔,键必须为字符串,值可以各种数据类
型,key而且不能够为数字,否则会解析失败
[]:表示的json数组
JsonObject的方法就是通过key获取各种值对象,其中最为重要的是getJsonArray()
getJsonArray是通过索引获取各种对象
格式化工具(可以安装一下):hijson
post和get请求的区别(重点掌握的是提交数据的方式)
get方式提交的数据全部会在地址栏显示,提交为url,格式为?key=value&......,长度被限制在1k或者4k,参数可见,当然是不安全的
post方式,参数在请求体中以流的方式传给服务器,长度不限
post提交数据的步骤
还是设置请求方式为"POST"
设置请求属性"Content-Type",值为内容类型的设定,根据传输数据的不同而设定不同的类型,后面我们可能会统一使用字节流的
设置请求属性"Content-Length",值为内容类型的数据大小
指定给我们的服务器写数据:setDoOutput(设置为true)
连接对象getOutputStream.write()
httpclient的提交数据到服务的方法已经从android4.4开始已经被废弃了,我们也就不用学了
使用开源框架提交我们的数据到服务器(开源框架的名称是:AsyHttpClient)
第一步当然还是创建AsynHttpClient对象
第二步调用其的,get或者.post方法
传进去一个path参数,post方法还要传一个数据参数,RequestParams对象,也是put进去键值对,注意"file"key是传一个文件,只能使用post传输,
默认内容类型要设置为字节数据类型:multipart/form-data
多线程下载
关键是使用RandomAccessFile对象进行读写,设置文件的大小使用setlength的方法
之后让connection设置下载范围:.setRequestProperty("Rang","bytes="+开始位置+"-"+结束位置);
之后通过随机读写流seek一下开始的位置就可以了
在写进去一个position时要直接往硬盘里面写,所以读写方式时要将其设置为"rwd"
使用开源框架(XUtils),里面有一个HttpUtils,new出来一个对象,使用里面的download方法(现在地址,存储位置,是否开始多线程,一个反馈对象)
关于网络方面的知识就此结束
在实际操作中我们可以还是可以在子线程中更新UI,主要原因是在审查机制还未建立之前,在子线程中更新UI不会抛出异常
审查机制建立之后就不可以更新UI了,假设我们在想要在子线程中进行UI的更新操作,只能将其交给我们的主线程进行操作,需要借助我们的
Handler
Android下的消息机制(理解,掌握,用起来,小伙子)
Handler对象:可以sendMessage(),发送信息,而且自己处理信息(全能选手),处理的方法为handleMessage(msg)方法
Message对象:信息的载体,帮助我们的携带信息,区别不同的信息,通过对其的.obj以及what属性进行设置,创建消息对象,使用消息池进行创
建:Message.obtain()方法创建
MessageQueue:只能有一个,发送的消息会进入到这个消消息序列对象中,通过Looper中loop方法轮询之后交给我们的handler对象处理
Looper,主线程被创建就会启动主线程的Looper和消息队列,每一个线程对应一个Looper,我们的消息和handler可以有多个,消息如何才能交给发送
自己的handler处理,其原理是消息中有一个成员属性target记录发送器的handler,轮询器可以根据这个属性将其交给对应的handler进行处理
子线程是可以弹出Toast的
启动一个子线程的轮询器:Looper.prepare()
弹出特定的Toast
开启轮询器进行轮询处理,Looper.loop();
接收到Toast的信息,会将其处理
可是问题是loop()方法是一个死循环,线程结束之后还会占用内存资源,会造成所谓的内存溢出,所以不能随便在子线程中启动启动Looper
View是否可见的状态(也可能会用到,我们需要记忆一下)
调用的方法是:setVisibility()方法来设置
View.VISIABLE:表示可见
View.INVISIABLE:表示不可见,可是还占据空间
View.GONE:表示不可见,而且不占据空间
对开源框架的使用(github要经常去)
我们讲一个SmartImageView的网络图片加载框架
将其的包导入我们的应用中
使用其的对象的setImageUrl获取到数据,之后通过BitmapFactory.decodeStream()就能将其转化为图片
该框架带有图片的缓存功能,现在我们已经用不到了
Activity组件(界面,四大组件你说重不重要)
每一个界面必须在我们Application节点下进行声明,每个程序必须有一个主界面
多界面的开发步骤
创建一个类继承Activity
在清单文件AndroidManifest.xml中的application节点下配置Activity节点,name属性赋值为类的全包名路径
重写onCreate方法,设置其的布局文件:setContentView
在res文件夹下创建layout文件
通过Intent进行显式意图或者隐式意图的跳转
Intent
当我们跳转到另一个组件或者说界面中时,假设通过intent进行跳转,可以使用我们的intent携带数据,使用put携带各种数据类型将数据携带过去,
可以携带八大数据类型,以及它们的数组,集合,还可以携带实现序列化(parcelable以及Serializerable)的对象,以及Bundle对象也是实现
parcelable接口
开启隐式意图需要添加intent-filter节点,设置action节点的name以及category节点的name属性(通常这个属性值为DEFAULT)
开启方式是new一个intent对象,使用这个对象setAction,addCategory,将对应的值传进去,当然里面还可以设置一个data节点,可以设置两个
scheme属性以及mimetype属性,假设设置了我们的set对应的数据,setData以及setType,两者是互斥的方法,如果要要设置两个,调用
setDataAndType方法
另一个开启方式:startActivityForResult,跳转到另一个界面之后,使用setResult返回一个结果给调用者,里面传参为一个intent对象,一个结果码,
只有当这个界面被finish之后才能进行传值,通过onActivityForResult方法进行结果接收
Activity的生命周期
创建和销毁:onCreate和onDestroy
显示和隐藏:onStart和onStop
暂停和继续(失去焦点和获取焦点,当界面未能被onStop的情况下,可是却无法操作):onPause和onResume
在onStop之后重新显示界面,还会先调用onRestart方法
需要注意的是在低内存的状态可能不会执行到onStop和onDestroy方法,所以传递数据需要在onPause中进行,可是不能进行耗时操作,否则会
影响另一个界面的打开速度
横竖屏切换的声明周期
由于会重建界面,数据可能会丢失
我们可以固定屏幕的方向: android:screenOrientation="portrait|landscape",前者是竖屏,后者是横屏
也可以进行配置:android:configChanges="keyboardHidden|screenSuze|orientation|locale"
Activi的任务栈以及其的开启模式
singleTop:当其处于栈的顶部,就不会重建,而是被复用,复用时调用onNewIntent()方法
singleTask:当其activity被重复创建时不会被重复创建,而是复用原来的,kill所有在其之上的Activity,如果在栈顶,就不会使用onStart()方法,界面
已经存在,只是失去了焦点
singleInstance:一个界面占用独立的一个任务栈,切换时不会重新创建新的界面
standard:标准模式就是一直创建
广播接受者
创建一个类继承BroadcastReceiver,可以重写其的onReceive方法,在这个方法中跳转界面时需要声明一个任务栈:intent对
象.setFlags(Intent.FLAGACTIVITYNEW_TASK)
在清单文件中application节点设置receiver节点,name为类的全包名
使用意图过滤器节点(intent-filter),设置action为我们要接收的广播
在4.0之后添加了限制,应用必须启动,而且不能被强制停止,手机关机默认退出所有的应用\
自定义广播
无序广播,使用上下文.sendBroadcast(intent)方法就可以了,initent需要setAction方法,接收需要在意图过滤器里面设置这个action进行接收
有序广播,sendOrderedBroadcast(),可以设置接收着的priority属性,范围是从-1000-1000,优先级高的可以先接收到广播,可以获取结果
值,getResultdata以及set这个结果,还可以取消广播abortBroadcast(),参数为一个intent,权限(默认为null),最终接收者(不用配置,最终一定能够
接收到数据),null(消息处理器),1(返回的code),结果值(通常是String类型),额外的数据)
动态注册广播接受者,主要是有的广播事件不允许静态,静态注册在应用退出时还会运行,接收到广播程序就会自动启动,动态注册只有在程序运行时
才能接收
第一步是创建接收者
第二步是创建过滤器对象,setAction
registerReceiver(接收者,过滤器)
在退出时可以在onDestroy方法中注销这个接收者unregisterReceiver(接收者);
服务组件(Service组件)
三大组件做耗时操作的时间(ANR)
Activity:5s
broadcastreceiver:10s
Service:20s
创建一个类继承Service类,
之后也是在manifest在application节点下配置一个service节点,name属性还是类的全包名
声明周期是从onCreate-onStartCommand(每次startService会被调用)-onDestroy
进程的优先级别
前台进程,正在运行中的所有组件
可视进程,处于onPause状态的Activity,还有其所绑定的Service
服务进程
后台进程,处于onStop状态
空进程,所有界面退出之后,还会有一个空进程
服务进程的优点
提升进程的优先级别,而且服务进程在低内存状态下就算被终止,也会在内存充足的情况被重启
绑定服务进程(会随着界面的关闭而关闭)
startService开启之后能够在后台一直运行,而且和开启者无关,只有运行到stopService时才会被关闭,无法直接调用Service中的方法
binderService无法在后台一直运行,而且生命周期收到Activity的直接影响,无法在后台长期运行
重写Service中的onBind方法返回一个IBinder对象
这个对象我们可以创建一个内部类继承Binder,之后实现一个接口,在这个内部类之中调用我们在服务之中的方法
之后创建intent对象以及conn对象(这是一个异步操作),在binderService中进行传参,conn可以被多次绑定,我们进行多次的解绑,用
unBinderService进行解绑操作,这样才能不造成内存的溢出
混合调用流程(长期运行而且有绑定服务对象):startService-binderService-unBinderService-stopService
进程间的通讯(面试经常被问到)
就是两个进程之间的数据交互
四大组件全部可以进行进程间的通讯,服务也可以开启隐式意图
aidl的编写(创建远程服务代理人)
书写我们的服务,设定隐式意图
首先将接口的后缀由.java改为.aidl
去除接口中的访问修饰符
MyBinider只用继承IService.Stub
将aidl文件复制到另一个应用中,包名必须保持一致
获取代理对象IService.Stub.asInterface(),获取之后就可以调用服务的方法了
本地服务和远程服务的区别(面试重点)
本地服务创建的Binder对象在主线程中
远程服务创建的BInder对象在子线程中(不能进行更新UI的操作)
content provider(内容提供者,也是数据存储的一种格式)
简单讲自己的数据库暴露给其它的应用进行操作
暴露自身数据库的方法
创建数据库,创建一个数据库类继承SQLiteOpenHelper类
创建一个内容提供者的类,让这个类继承我们的ContentProvider
之后去清单文件(androidManifest.xml)中配置我们的provider节点,社会name属性为类的全包名,authorities属性的值为uri,在api17版本之后还
要配置一个参数:exported属性,值为true
假设我们需要给我们的操作设置一个密码,通过UriMatcher对象进行添加,设置返回码,帮助我们的操作数据库中不同的表格
最后当然是重写增删改查的方法
通过内容提供者操作其它应用的数据库的方法
通过上下文对象.getResolver()的方法获取一个解析器对象
创建一个Uri对象,Uri.parse(uri地址/密码);
使用解析器增删改查的方法,传入对应的参数
内容观察者
假设数据库被改变了我们可以使用getContext.getContentResolver().notifiChange(uri,null)来通知一个内容观察者,之后可以注册一个观察者
来监听这种变化
上下文对象.getResolver().registerContentObsever(uri,true:表示可以监听uri的子资源,创建一个监听器)
计算机中的图形
在我们的android中默认的位图格式为:ARGB_8888,占了两个字节:#ffffffff
通常我们要将其转换为我们另一个位图格式:RGB_565.只占据了两个字节
假设我们想要把我们的图片资源加载到我们的内存中我们可以BitmapFactory.decodeFile()
假设加载的图片太大,由于我们设置一个应用在内存中的最大内存,所以可能会报OOM的异常
解决上述问题的方法对图片进行压缩
压缩图片的代码思路
创建一个Options对象,首先将其的位图格式改为:RGB_565
设置其的只读取宽高值的属性为true
之后使用BitmapFactory解码一个bitmap对象到我们的内存,将options对象传进去
通过options对象获取图片的宽高
通过getWindowManager()获取,之后获取默认显示,之后获取宽高信息,这是获取屏幕的宽高,假设我们的显示控件本身已经设定了宽高,我们可
以获取到这个控件的宽高
之后获取图片本身宽高和屏幕或者控件的比例值,获取到最大的scale
将这个scale值作为参数供我们的options对象.inSampleSize()使用
创建我们的图片
图片缩放的思路
首先我们缩放的不是一张原图,所以我们还是要创建一张和原图同等规则的空图
使用的方法是bitmap自身所具有的创建位图的方法,将原图宽高的信息以及位图格式作为参数传进去
之后我们将其作为参数传导canvas的构造函数之中
通过canvas.draw位图的方法,第一个参数就是按照原图去画,第二个是一个matrix对象(矩阵对象),可以设置其的缩放比例使用setScale方法进
行,paint参数可以省略,原图本身就有默认的画笔
随手涂鸦的核心逻辑
这是一个简单的案例,最为关键是给一个imageView设置一个onTouch的监听器,让其在记录下直线的开始和结束坐标,然后通过canvas画线的
功能画直线就可以,其中最终的是这个onTouch的方法必须返回true事件才会交给其继续处理,否则一旦返回false就不会再交给其处理了.
撕衣服案例的思路
我们创建两个iv控件,设置两张图片,将撕衣服之前的照片叠放在撕衣服之后的图片之上,当然对于我们要处理的图片我们不能直接使用原图,而
是要使用原图的拷贝或者copy.
之后我们就需要iv_pre之中设置一个onTouchListener的监听器了,当然还是要return true的,否则这三个连续的触发事件不能够被完整地执行
完毕,之后我们可以获取事件发生时的坐标,之后将图片的这个坐标的像素设置为透明,将其重新设置为我们的iv所要显示的图片
后续要处理的细节问题在于可能一个个像素去将其变成透明色用户体验会太差,我们需要设计一个for循环扩大其的范围.
关于坐标的误差,我们需要对图片进行同等比例的缩放,缩放的案例我们栽面已经讲过了.
getX,getY和getRawX,getRawY的区别
前者是相对控件的左上角而言,就是以其的左上角作为基准
后者是以整个屏幕的左上角为基准
注意:Y轴向下为正,X还是向右为正方向
颜色矩阵(ColorMatrix,理解就可以了)
SoundPool(new 一个声音池出来,load我们的歌曲资源,通常放在raw文件夹中)
应用场景主要是在一部分需要频繁地被播放,而且声音文件本身也不大的场景中,比方说一部分游戏中的配音文件
VideoView控件
首先在layout文件声明这个控件
通过id获取这个控件的对象
设置其的播放文件源
new一个MediaController出来
videoview设置一个默认的播放器
videoview调用start方法开始播放我们的视频
通常来讲这种方式播放视频,能够支持的视频格式极为有限,mp4,3gp
SurfaceView
这一个控件继承自我们的view,内部维护着一个双缓冲,就是一边加载,一边进行播放,而且可以在我们的主线程中更新我们的UI
由于我们的surfaceView是一个栈内存的控件,所以会在界面处于可见状态时被创建,在其不可见时也会被摧毁,可以使用回调来监听其的创建和
销毁(callBack)
使用surfaceView进行视频播放的基本流程或者说思路
在surfaceview被创建时:new 出一个我们的mediaplayer对象
用mediaplayer加载资源文件
设置holder
设置异步准备
设置完成准备的监听
开始播放,seekTo指定的位置(设置一个position)
对position进行获取和判断,从我们的sp文件中获取,之后判断下其是否等于文件的播放时长,假设为文件的播放时长我们就将其设置为0
另外就是设置一个播放完成的监听,在里面设置一个播放完成的标签,当然还是要在其的前面设置一个播放错误的监听
在surface被摧毁之后,我们要对position进行获取,判断以及赋值,之后将其写入到我们的sp中
以上就是一个标准的视频播放流程
注意:这个控件是一个平面容器,相当于电影的荧幕,我们可以将视频放在其的上面进行播放,还可以将照相机的画面置于其上,当然还有我们的视
频,关于用隐式意图打开照相和录制视频的功能,我就不讲了
fragment以及动画(fragment我觉着是重点,要理解透彻,动画还是一个熟悉API参数和
使用的过程)
fragment的使用
英文的含义就是碎片,是一个view的管理器,相等于界面中的界面,当然这只是我的理解,就是管理界面中的一块界面
所以我们需要标定一块被我们所使用或者所管理的界面,通常我们帧布局当做其的容器,关键是布局逻辑不会太复杂
所以我们就有了一块可以被充气的布局文件,我们需要创建一个类继承我们的Fragment类,之后必须重写里面的onCreateView方法,返回一个
View对象,这个VIew对象就是利用方法中inflater.inflate方法对一个布局文件进行充气,将其充气为view对象进行返回
之后在方法在方法中getFragmentManager之后,begin一个事件,获取这个事件对象,通过事件对象调用replace方法,用一个fragment对象替代
我们的容器控件,前面已经讲过这个对象通常是一个帧布局对象,最后当然就是提交这个事务
注意,需要引起我们的注意的是这个事务一旦被提交之后,就已经结束,不能对这个事务对象进行重复使用
还有另一种使用方法,是在容器(当然通常还是指我们的帧布局控件)中声明我们的fragment控件,要设置这个控件的class属性,指定继承
fragment的类.
也是获取一个fragmentmanager对象,通过这个对象找到指定的id获取我们的fragment控件对象,也是开始一个事务,通过这个事务对象调用hide
或者show来隐藏或者显示这部分fragment控件对象,这个方法不会重新创建和销毁我们的fragment对象,所以推荐使用这种对象来切换我们的
fragment对象
关于fragment的数据交互
在fragment里面直接可以通过getActivity()方法获取到界面中所有的数据
假设我们想要在Activity中传递数据给我们的fragment对象数据,可以通过其的setArguments()方法设置一个bundle对象进行数据传递,在
fragment类中使用getArguments()方法获取这个bundle对象,从这个bundle对象中提取数
另外还有一个方式就fragment之间的数据交互,采用上述的方法进行fragment的设置时,我们说可以直接通过getFragmentManager,之后通过其
获取对用的fragment控件对象,通过这种方式我们就能获取到所有这个对象中的数据
fragment的Fragment lifecycle
第一个步骤是当其被加载我们的布局之中:onInflate()
第二个步骤当其被加载到我们的Activity之中或者说界面之中,调用的生命周期为:onAttach()
之后被创建时调用的方法:onCreate
设置其的view:onCreateView()
其的界面被显示出来:onStart()
处于前台程序的状态:onResume()状态
之后消失也是按照创建的顺序:onPause()-onStop-onDestroyView()-onDestroy-onDetach()
关于向下兼容的问题
Activity继承FragmentActivity
另外就是获取的SupportFragmentManager
当然还可以导入一个包(在lib之中):nineoldandroids-2.4.0.jar
动画
帧动画,可以在drawable里面配置一个xml文件,我们可以配置一个animation-list的主节点,之后在其的下面配置item节点,配置每一帧
view动画
透明度动画,创建一个alphaanimation对象,设置的参数是从一个透明度变到另一个透明度,不透明可以直接使用参数1f去表示
还有一个就是我们的位移动画,让我们的画面从一个位置移动另一个位置,参数就是设置x,y起点到x,y的中点
设定我们的缩放动画是使用scaleanimation对象,参数分别为x和y的缩放比例,最后还有一个参数是起始中心位置,可以相对自身,也可以相
对于父控件
旋转动画,创建我们的rotateanimation对象,设置我们的旋转角度和我们的中心位置,这个中心位置也可以是图片自身的位置,也可以是相对
父控件的位置
另外需要补充的就是设置动画时长可以使用的方法是setDuration(),开始播放也是调用我们的start()方法
最后一个是AnimationSet这可以播放一组的动画集合,其构造函数需要传入一个boolean参数,当其为false时,每一个动画按照自身的时间
轴去播放,当其为true时其会按照一个set自身携带的统一的时间轴去播放.
设定出所有的播放动画对象,用集合将其add到里面,使用我们的iv控件startAnimation()就可以播放组合动画了
注意:假设我们在xml文件中配置了动画(anim文件夹中添加),假设我们想要使用这个动画,我们可以使用我们的
AnimationUtils.loadAnimation()的方法获取这个控件对象,之后也是iv控件调用startAnimation(作为参数传入),还有一个需要我们注意的点
是100%是相对自身的而言,100%p是相对父容器而言,还有就是这种动画原图只是被隐藏了,不同gone属性和invisiable属性(这是不可被点
击的),还是可以触发被点击的事件,这种动画只是其的一个镜像,假设想要让其的动画实现被点击的效果,我们需要使用属性动画.
属性动画
所谓的属性就是可以被get和set其值的变量
ObjectAnimator.of(set的数据类型)(iv对象,属性名称,属性起始值,属性结束值)
直接调用start方法就可以开始我们的动画
也可以创建这个属性动画的xml文件,在animator文件夹里面设置,可以通过AnimatorInflate.loadAnimator()获取这个对象之后调用其的
setTarget方法传入我们要设置的iv对象,还是用start()方法开始播放我们的动画
还有一个AnimatorSet的集合对象,创建这个对象,调用playTogether方法可以播放多个属性动画,播放还是调用start方法开始播放
最后还有一个属性动画的类需要补充一下,这就是我们的valueAnimator,当还是创建这个对象,之后对其的开始值以及结束值进行设定,使用
addUpdate方法创建一个监听器,主要是监视这个值的变化,使用getAnimatorValue获取变化的值,根据这个值设定我们要展示的动画规则,之后
还要设置动画时长(duration),使用其的start()方法开始播放我们的动画,这就是整体性的思路,关于代码或者具体的API大家可以自行去查阅一
下.
关于版本控制(基本上毫无代码,操作为主)
tortoise的使用必须要熟练一下,老乌龟杠杠的
关于版本控制的软件
CVS(已经不用了,我们就别用了)
SVN(大部分小公司在使用,这是重点),全称为LSubversion
ClearCase(IBM公司出品,必然收费)
VSS(就是微软公司的一个弱鸡软件)
GIT:分布式控制软件,GITHUB你懂就好
挺想要扯一下分布式的设计思路,简单来讲就是将一个任务分配给不同的人进行执行,之后集中提交给一个中央服务器进行处理,当然在提
交之前需要建立一个审查服务器,之后通过审查服务器统一提交给我们的中央服务器,当然可能在此之中还会涉及到一个分配机制,这种分
配机制的权限该是有一个独立的模块进行还是让中央服务器进行兼职,这不该是我们要考虑的问题,在github上我们克隆下数据仓库中所有
的任务,选择我们要修改的代码,之后提交给我们的审查服务器进行审核,这是一个自由的任务机制,所以不需要考虑所谓的任务分配问题,当
然我们可以将自己的代码上传到自己的本地服务器(也就是自己的电脑,这不就是保存功能),还可以生存一个补丁文件供别人使用,补丁里
面的内容是对修改内容的描述,当然我们还可以使用补丁更新我们的原文件,在bigdata里面我们使用的hadoop也是使用分布式计算的原
理,其实在设计上还是为了减少中央服务器的配置成本,将分布运算的数据进行一个统一的处理(mapreduce),学过map集合的同学对键值
对该是不会陌生,然后就是数据上的各种算法了,各种分类和过滤数据的算法,当然初学者把时间花在搭建hadoop框架上了,不过也是徒劳.
关于SVN的操作
自行掌握,工作中可以先向leader申请一个账号
GIt的操作
clone 数据仓库到我们的电脑上
添加补丁,开发者之间通过邮件互发补丁(补丁可以根据提交次数进行生成)
也可以提交给服务器进行审查
网友评论