克隆是指克隆对象,在堆空间复制一块内存,是完全的两个对象,不是指针指向!浅克隆是指克隆一个对象,而该对象的属性只是基本数据类型,只克隆出该对象!深度克隆是指克隆的目标里面还有引用类型,引用类型里还有引用类型,同时把引用类型克隆出来叫深度克隆!常用的方法有两种,第一,需克隆的对象实现cloneable接口;第二,使用commons包提供的克隆方法。这两种方法都能实现深度克隆!
各种Adapter的使用方法:http://www.cnblogs.com/shang53880/archive/2011/03/15/1985062.html
多线程下载:http中的 conn.getContentLength获取文件总大小,从指定http中连接读取信息:conn.setReauestProperty("Range","bytes=startsize-toSize");
随机写文件:RandomAccessFile ,并且指定文件的总大小为服务器文件大小(空文件),然后指定每条线程要保存的位置(RandomAccessFIle.seek方法)
断点下载:
下载了一半.突然停电,下次启动的时候会接着下载;
记录下载的进度;
究竟读取了服务器上面多少的数据?记录当前线程每次下载的总大小,并将下载的大小单独存放到一个文件里面
硬盘存文件:一般会有硬盘缓冲区;
机械硬盘: 缓存区,先把数据放到缓冲区,当缓冲区数据满后,再一次性全部写到硬盘;
可能没有将下载的大小保存到文件?是因为缓冲区,还没有来得及将缓冲区的数据写到文件,就退出了;解决办法:利用RandomAccessFile类设置文件的模式为'rwd'模式
下一次启动的时候,从上一次下载的位置开始下载
离线缓冲实现思路(以新浪微博为例,后台应该保存每条记录产生的时间)
参数:每页显示多少条记录如何去除插屏广告(Android逆向助手):
1,apktool反编译当前apk, apktool d xxx.apk;
通过反编译后会生成相应的文件夹,包含一个smail的文件,通过修改smail文件,去除插屏广告(smail语法参考:http://blog.csdn.net/pz0605/article/details/41090145)
2, apltool 将去除广告的文件夹,生成一个包 apktool b xxx(文件夹名称)
在dist文件夹下
3,将dist文件夹下的apk打上签名(利用jdk签名):
jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -storepass android -keystore debug.keystore -signedjar e_key.apk e_1.apk androiddebugkey
jdk 1.7 -digestalg SHA1 -sigalg MD5withRSA 仅在jdk1.7上去添加
-storepass android对应签名密码
debug.keystore 对应的签名文件
e_1.apk 打上debug.keystore这个签名,并且生产e_key.apk
androiddebugkey debug.keystore别名
android jar包开发(带图片等):
1,通过java代码实现工程的布局(因为要打成jar包,缺乏R文件,无法通过id去关联相应的资源)
2,工程图片不可放置在工程res文件夹下(jar包中不包含图片文件夹,如果去拷贝很费事费时),图片放置在工程的assets目录下,后通过流的方式读取到工程目录下,便于调用.
3,因为是提供sdk供他人调用,需要尽可能的封装,方便他人的调用
服务:
服务: 长期后台运行的没有界面的组件
如果服务已经开户,不会重复的执行onCreate(),而是会调用onStart()和onStartComman();
服务停止的时候调用onDestory()
服务只会被停止一次;
二,服务还有一种开启方式,绑定的方式开启服务
两种服务区别:
oncreate--->onstart/onstartCommand--->ondestroy
start方式开启服务,一旦服务开启跟调用者就没有任何关系;开启者退出了或者开启者挂了,服务还在后台长期运行;
开启者没有办法去调用服务里面的方法;
bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。
开启者可以调用服务里面的方法
oncreate--->onbind--->onUnbind-->destory
调用服务方法原理:(服务要暴露必须定义一个中间人并且继承Binder类;在服务的onBinder方法实现服务成功绑定的代码,返回一个中间人;Actvity采用绑定的方式开启服务;在服务连接的时候得到绑定对象;通过绑定对象/中间对象调用服务方法)
1),定义一个内部类,并且继承Binder类,在这个内部类里面可以调用服务内部的方法(内部类充当第三者间接调用服务里面的方法)
2),重写服务里面的onBind方法,在onBind方法返回定义内部类(这个类继承自Binder类);
3),在Activity中定义一个类并且实现ServiceConnection接口,在onServiceConnected方法中的参数binder就为服务类onBinder方法返回的对象,利用bindService开启服务;
注意:bindService调用时,如果将中间人/类定义为public,则中间类的所有方法都会暴露出来;
解决方法:通过接口定义暴露需要的方法(中间人的接口定义),并且让中间类实现定义的接口;
如何绑定远程服务:
远程服务:调用和服务中不同的应用程序里面;
本地服务:调用和服务中同一个应用程序里(同一个工程代码里);
每一个应用程序都是运行在自己独立的进程蝻,进程是操作系统分配内存空间的单位;进程的数据都是独立的,独立的内存空间;
aidl:android interface definintion language 安卓接口定义语言;
aidl文件都是公有的,没有访问权限修饰符
远程服务继承的是ipc的一个实现类(通过aidl文件自动生成的)
创建一个与远程服务一样andl文件(包括包名,类中内容),并且自动生成java文件
aild原理:在两个应用程序之间申请一块公共的空间
绑定本地服务调用方法步骤:
1,在服务内部创建一个内部类,提供一个方法,可以间接调用服务的方法;
2,在服务的onBind方法需,这个返回的就是中间人;
3,在Activity里面绑定服务;
4,在服务成功绑定的时候,会执行一个onServiceConnected方法,传递过来一个Ibinder对象
5,对binder对象强制转换,调用接口里面的方法;
绑定远程服务调用方法步骤:
1,在服务内部创建一个内部类,提供一个方法,可以间接调用服务的方法;
2,在暴露的接口文件中扩展名改为anidl文件,去掉访问修饰符public
3,实现服务的onbind方法,返回的就是中间人XXX.Sub.asInterface(bind);
4,在Activity里面绑定服务,bindservice
5,在服务成功绑定的时候,会执行一个onServiceConnected方法,传递过来一个Ibinder对象 6,XXX.Sub.asInterface(bind) 返回的接口对象调用服务方法;
混合调用服务方法(有的时候既要保证服务长期后台运行,又要调用服务里面的方法;如支付功能):
混合调用的生命周期:
1,采用start开启服务(保证服务长期后台运行)
2,bind方式绑定服务(保证调用服务的方法)
3,调用完之后,unbind解除绑定服务
4 stopService停止服务;
注意顺序不能颠倒
surfaceView:
surfaceView:完成单位时间内大量的界面变化(如视频播放,游戏界面)
内部采用双缓冲机制:内部有两个子线程;
工作原理(两条线程交替更新界面):
线程A 解码图像----->前台显示
线程B 当A线程将要显示时,B开始解码图像,当A显示完后,快速切换为B线程序;
SuerfaceHodeler holder =surfaceView.getHolder();//得到显示界面内容的容器
mediaPlayer.setDisplay(holder);//设置显示的内容
由于surfaceView很消费内存,故当界面最小化时(最小化再次打开时可能会黑屏,因为不是同一个hoder),系统会销毁surfaceHoder,只有当用户需要时,地创建sufaceHoder
解决方法:当最小化销毁时记录已经播放的位置,并且释放资源,在surfaceCreated中根据记录的位置播放(mediaPlay.seekTo(XX));
样式与主题:
样式:res-->values-->style.xml
<style> <item name="XXX">xxx</item> </style>
android:style="@style/xxxxx"
主题: 跟style定义相同
样式与主题的区别:作用范围不同,样式都是作用在一个小控件中,主题作用于一个acitivy,或者整个应用程序,样式都在布局文件里面配置,主题都在清单文件配置;
android:them="@style/xxxxx"
如何更改系统的默认样式:
1,打开sdk-->platforms-->android-16-->data-->res--->values-->style.xml 就为默认的系统样式
2,在自己的styles.xml文件中重写系统样式(复制系统的然后修改草案)
如何更改系统的默认主题:
1,打开sdk-->platforms-->android-16-->data-->res--->values-->themes.xml 就为默认的系统样式
2,在自己的styles.xml文件中重写系统样式(复制系统的然后修改草案)
补间动画:点击打开链接
四种动画类型:旋转,缩放,位移,透明度
两种动画模式:tweened 和 frame
Activity生命周期:
完整生命周期 oncreate--》onstart--》onresume--》onpause--》onstop--》ondestory
可视生命周期 onstart--》onresume--》onpause--》onstop
前台生命周期 onresume--》onpause 界面用户仍然可见,但是失去焦点
使用场景:
1.应用程序退出自动保存数据 ondestory oncreate
2.应用程序最小化 暂停的操作 onstop onstart 视频播放器
3.游戏的暂停和开始 前台生命周期
onSaveInstanceState(Bundle outState) 当activity销毁时,可以借助该方法保存数据;
Activity回传数据?
1),利用startActivityForResult(intent, 0); 启动目标activtiy,并且复写 protected void onActivityResult(int requestCode, int resultCode, Intent data) 方法;
2),在目标activity中通过调用setResult(RESULT_OK, intent)返回需要回传的值,并在 在开启的那个activity中的onActivityResult中获取回传过来的值
Activity启动模式与任务栈:
1.一个应用程序一般都是由多个activity组成的。
2.任务栈(task stack)(别名back stack后退栈) 记录存放用户开启的activity的。
3.一个应用程序一被开启系统就给他分配一个任务栈,当所有的activity都退出的时候,任务栈就清空了。
4.任务栈的id是一个integer的数据类型 自增长的。
5.在android操作系统里面会存在多个任务栈,一个应用程序一个任务栈。
6.桌面应用和一般的应用程序是一样的,任务栈的行为也是一样。
7.默认情况下, 关闭掉一个应用程序,清空了这个应用程序的任务栈。应用程序的进程还会保留。
为什么要引入任务栈的概念:
windows下 可以通过点击任务栏 切换任务
android下 长按小房子 切换任务
为了记录用户开启了那些activity,记录这些activity开启的先后顺序,google引入任务栈(task stack)概念,帮助维护好的用户体验。
activity的启动模式:
1. standard 默认标准的启动模式, 每次startActivity都是创建一个新的activity的实例。 适用于绝大大数情况
2. singleTop 单一顶部,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,
而是调用 onNewIntent() 方法。
应用场景: 浏览器书签。 避免栈顶的activity被重复的创建,解决用户体验问题。
3. singletask 单一任务栈 , activity只会在任务栈里面存在一个实例。如果要激活的activity,在
任务栈里面已经存在,就不会创建新的activity,而是复用这个已经存在的activity,
调用 onNewIntent() 方法,并且清空当前activity任务栈上面所有的activity
应用场景:浏览器activity, 整个任务栈只有一个实例,节约内存和cpu的目的
注意: activity还是运行在当前应用程序的任务栈里面的。不会创建新的任务栈。
4. singleInstance 单态 单例模式
单一实例,整个手机操作系统里面只有一个实例存在。不同的应用去打开这个activity
共享 公用的同一个activity。
他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。
应用场景:呼叫来电界面 InCallScreen
Android广播:
android系统下的广播:
电池电量低。
电池充电完毕
短信到来了
程序安装卸载
sd卡卸载 安装
广播的使用步骤:
1.写一个类继承广播接受者
2.在清单文件配置关心的动作
3.一旦广播事件发生了,就会执行广播接受者的onreceive方法
支付开发流程:
以支付宝为例说明一下:(支付宝、快钱、网银……)
前置条件:公司需要在支付宝申请账号
两种实现方式
第一种(彩票):客户端发送支付(充值)请求到服务器端、服务器端处理完成后将处理结果返回给用户,并提示用户受理支付请求成功,之后服务器端会将支付请求打包发送给支付宝服务器,支付宝服务器在接收到请求后,电话通知客户端用户,最终的转账的决定权交由用户来决定。
第二种(电商):客户端发送结算请求到服务器端,服务器端生成订单后,将订单数据回复给客户端,客户端支付时需要判断是否安装支付宝应用,如果安装了则将订单数据发送到支付宝的客户端,由支付宝的客户端完成于支付宝服务器的数据传输,当完成支付后,手机端会收到通知,同时支付宝会将处理成功的消息发送给服务器端,服务器端处理后续工作。
1),公司申请支付宝账号
2),公司在支付宝的账号信息&&用户的账号信息&&账单信息
AsyncTask: 内部采用多线程Future设计模式和ThreadPoolExecutor线程池技术,最多可允许有128条子线程
AsyncTask<Params, Progress, Result> 分别表示参数,进度,任务执行完成后的返回结果
onPreExecute:执行任务前调用,在主线程中调用,做一些预处理的事情,例如初始化进度圈等
doInBacground:执行任务时调用,在子线程调用,主要做一些耗时的操作
onPostExecute:在doInBackground 执行完成后,onPostExecute 方法将被UI 线程调用,在子线程中调用,主要更新UI
publishProgress(Progress... values):将任务进度传递出去 ;onProgressUpdate(Progress... values) :在UI线程中更新进度
cancel(boolean mayInterruptIfRunning):取消任务
异步计算任务:详情参见future
Future:
FutureTask:实现Future的子类
callable:
Android框架开源学习笔记:
phonegap环境搭建:参考
1), phonegap.js 加入到项目
2),phonegap.jar 加入到项目
3 ),模仿js 下载phonegap的源码,在资产目录底下(在资产目录下面新建www)。并在js文件中定义我们自己的js对象,如:
/** HM是我们自己定义的类 */ var HM = function() { };
phonegap js代码如何调用java代码:
1),在资产目录下新建plugins.xml文件,并且配置需要访问的java类;如
<plugin name="HM_service" value="com.example.hmphonegap.HmTest" /> <!--name:service的配置名称; value:具体的Action全类名(我们的action需要继承 DroidGap)-->2,js中通过配置的参数调用action里面的方法
/** *Network Status 这个是service的名字 需要跟plugins里面的name对应起来 getConnectionInfo 这个东西是action 具体调用哪个方法 android开发人员自己定义的类 HMTest2 extends Plugin */ HM.prototype.testLogin = function(successCallback, errorCallback ,option) { // Get info PhoneGap.exec(successCallback, errorCallback, "Network Status", "getConnectionInfo", []); };
JNI编程(交叉编译):
怎么用java代码调用c代码 ?
1),在java文件中 定义一个c本地方法的接口, 相当于在java代码中定义了一个接口 接口的实现方法是C语言实现的
public native String helloWorldFromC();2),实现C代码
方法名 严格按照jni的规范 jstring Java_com_example_helloworldformc_MainActivity_helloWorldFromC(JNIEnv* env,jobject obj)注:自动生成 java本地方法对应的c代码的方法名 javah 指令 +全类名
如果为jdk1.6 ,应该先到 class下面去生成 C:\workspace\HelloWorldFromC2\bin\classes
如果为jdk1.7,应该到工程目录下面的src去生成 C:\workspace\HelloWorldFromC2\src
3),利用ndk-build命令,生成 .mk文件
4),c代码 打包成函数库 用到了安装的环境
5), 在java代码中 引入库函数 (用c生成的库函数)
static{ System.loadLibrary("hello");// 注意事项 去掉前面的lib 后面的.so }C语言回调java方法?
1),通过javap -s 打印方法的签名 注意要cd到 C:\workspace\HelloWorldFromC2\bin\classes 传全类名
2),在c语言中通过反射调用java代码;
java反射原理: Class<?> forName = Class.forName("com.example.ndkcallback.DataProvider"); Method declaredMethod = forName.getDeclaredMethod("helloFromJava", new Class[]{}); declaredMethod.invoke(forName.newInstance(), new Object[]{}); ///jclass (*FindClass)(JNIEnv*, const char*); jclass clazz=(*env)-><span style="color:#FF0000;">FindClass</span>(env,"com/example/ndkcallback/DataProvider"); // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); // 方法签名 参数和返回值 jmethodID methodId=(*env)-><span style="color:#FF0000;">GetMethodID</span>(env,clazz,"helloFromJava","()V"); // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,jobject,methodId);
Volley框架学习:
Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Load一样轻松加载网络上的图片,
RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求
可分为:JsonObjectRequest StringRequest 、ImageRequest 、ImageLoder
使用步骤:参考
1. 创建一个RequestQueue对象。
2. 创建一个StringRequest对象。
3. 将StringRequest对象添加到RequestQueue里面。
ImageRequest说明:在使用的时候可以指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩;
ImageLolder说明:ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。
ImageLoader的使用步骤:
1. 创建一个RequestQueue对象。
2. 创建一个ImageLoader对象。
3. 获取一个ImageListener对象。
4. 调用ImageLoader的get()方法加载网络上的图片ImageLoader原理:
我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。
EventBus框架:用于事件的发布和订阅(不需要定义Handler,Message,Thread,回调接口等繁琐的操作),参考资源:http://blog.csdn.net/lmj623565791/article/details/40794879
核心方法:
EventBus.getDefault().register(this); //事件注册
EventBus.getDefault().post(Object obj);//发布
EventBus.getDefault().unregister(this);//解除
onEventMainThread,onEventPostThread,onEventPostThread,BackgroundThread
核心原理:
EventBus.getDefault().register(this);意思是让EventBus扫描当前类, 把所有onEvent开头的方法记录到Map,使用Map,Key为方法的参数类型,Value中包含我们的方法。
注册完毕后:我们的onEventMainThread就已经以键值对的方式被存储到EventBus中了。
然后当子线程执行完毕,调用EventBus.getDefault().post(obj))时,EventBus会根据post中实参的类型,去Map中查找对于的方法,于是找到了我们的onEventMainThread,最终调用反射去执行我们的方法。根据方法的后缀,来确定threadMode.
onEvent后面可以写四种,也就是上面出现的四个方法,决定了当前的方法最终在什么线程运行,
onEventMainThread代表这个方法会在UI线程执行
onEventPostThread代表这个方法会在当前发布事件的线程执行
BackgroundThread这个方法,如果在非UI线程发布的事件,则直接执行,和发布在同一个线程中。如果在UI线程发布的事件,则加入后台任务队列,使用线程池一个接一个调用。
Async 加入后台任务队列,使用线程池调用,注意没有BackgroundThread中的一个接一个
总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用
简单的说EventBus就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。
LruCache内存缓存原理(LRU是Least Recently Used 近期最少使用算法)
非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
实例化方法:
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。 // LruCache通过构造函数传入缓存值,以KB为单位。 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // 使用最大可用内存值的1/8作为缓存的大小。 int cacheSize = maxMemory / 8; LruCache lruCache =new LruCache<String,Bitmap>(cacheSize){ //必须重写sizeOf返回每一个value的大小,因为在向LinkedHashMap中存放数据的时候 //会调用累加sizeOf(默认返回为1),并判断容器的大小是否超过了设定的值 @Override protected int sizeOf(String key, Bitmap bitmap) { // 重写此方法来衡量每张图片的大小,默认返回图片数量。 return bitmap.getByteCount() / 1024; } };
1,需要存放的value必须能够计算出大小;
2,必须实现父类sizeOf返回每一个value的大小;
DiskLruCache硬盘缓存原理:
// 每当版本号改变,缓存路径下存储的所有数据都会被清除掉,因为DiskLruCache认为当应用程序有版本更新的时候,所有的数据都应该从网上重新获取 // 写入的操作是借助DiskLruCache.Editor这个类完成的。类似地,这个类也是不能new的,需要调用DiskLruCache的edit()方法来获取实例 // 创建DiskLruCache实例,初始化缓存数据 mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1,10 * 1024 * 1024); // 将缓存记录同步到journal文件中。journal文件不日志文件 if (mDiskLruCache != null) { try { mDiskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); } } <span style="color:#FF6666;">// 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存</span> DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(0); if (downloadUrlToStream(imageUrl, outputStream)) {//imageUrl下载的图片 editor.commit(); } else { editor.abort(); } }
在实际使用中也可以使用PagerAdapter的两个子类FragmentPagerAdapter 、FragmentStatePagerAdapter
PagerAdapter pagerAdapter = new PagerAdapter() { @Override public boolean isViewFromObject(View arg0, Object arg1) {//必须实现 return arg0 == arg1; } @Override public int getCount() {//必须实现 return viewList.size(); } @Override public void destroyItem(ViewGroup container, int position,Object object) {//必须实现 container.removeView(viewList.get(position)); } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public CharSequence getPageTitle(int position) { return titleList.get(position); } @Override public Object instantiateItem(ViewGroup container, int position) {//必须实现 ..... return view; } };
Fragment:
类似于Activity,可以像Activity一样包含布局。Fragment通常是嵌套在Activity中使用的
平板的屏幕非常大,手机的界面放在平板上可能会有过分被拉长、控件间距过大等情况,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题,但现在即使只是在手机上,也有很多的场景可以运用到Fragment了.
在Activity中如何添加Fragment
1.获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。
2.开启一个事务,通过调用beginTransaction方法开启。
3.向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。
4.提交事务,调用commit方法提交。
Fragment生命周期
fragMent如何通信
主要都是通过getActivity这个方法实现的。getActivity方法可以让Fragment获取到关联的Activity,然后再调用Activity的findViewById方法,就可以获取到和这个Activity关联的其它Fragment的视图了。
如:
//在fragment2中获取fragment1中的textView TextView textView = (TextView) getActivity().findViewById(R.id.fragment1_text);fragment如何兼容手机与平板:
1,在layout布局下面新建两套布局(双页,单页)文件,android系统会根据当前的运行环境加载不同的布局文件;
2,在onActivityCreated方法判断加载的是双页还是单页(如果Activity的布局中包含了双页中的view,那么当前就是双页模式,否则就是单页模式),然后onItemClick方法根据双页还是单页做不同的逻辑处理;
如何应用程序支持各种不同屏幕大小:
actionBar是一种新増的导航栏功能,它标识了用户当前操作界面的位置,并提供了额外的用户动作、界面导航等功能,还可以自动适应各种不同大小的屏幕;
actionBar的添加或者移除:
android:theme="@android:style/Theme.Holo" //添加方法 android:theme="@android:style/Theme.Holo.NoActionBar" //移除方法- ActionBar actionBar = getActionBar(); //移除方法二 actionBar.hide();如何修改actionBar中的图标和标题:
在<application>或者<activity>中通过logo属性来进行指定, 例如:
<activity android:name="com.example.actionbartest.MainActivity" android:logo="@drawable/weather" > </activity>
如果按钮过多,ActionBar上显示不完,多出的一些按钮可以隐藏在overflow里面(最右边的三个点就是overflow按钮),点击一下overflow按钮就可以看到全部的Action按钮了。当Activity启动的时候,系统会调用Activity的onCreateOptionsMenu()方法来取出所有的Action按钮,我们只需要在这个方法中去加载一个menu资源,并把所有的Action按钮都定义在资源文件里面就可以.
如:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.example.actionbartest.MainActivity" > <item android:id="@+id/action_compose" android:icon="@drawable/ic_action_compose" android:showAsAction="always" android:title="@string/action_compose"/> <item android:id="@+id/action_delete" android:icon="@drawable/ic_action_delete" android:showAsAction="always" android:title="@string/action_delete"/> <item android:id="@+id/action_settings" android:icon="@drawable/ic_launcher" android:showAsAction="never" android:title="@string/action_settings"/> </menu>需要说明一下的地方:showAsAction则指定了该按钮显示的位置,主要有以下几种值可选:always表示永远显示在ActionBar中,如果屏幕空间不够则无法显示,ifRoom表示屏幕空间够的情况下显示在ActionBar中,不够的话就显示在overflow中,never则表示永远显示在overflow中.
接着,重写Activity的onCreateOptionsMenu()方法,代码如下所示:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); }
通过ActionBar导航(左上角的返回"<"图标)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true); //显示返回
}
ActionProvider:(可以自定义actionBar中的按钮样式、控制action view单击事件)http://blog.csdn.net/guolin_blog/article/details/25466665
Action View有点类似,Action Provider也可以将一个Action按钮替换成一个自定义的布局。但不同的是,Action Provider能够完全控制事件的所有行为,并且还可以在点击的时候显示子菜单。
为了添加一个Action Provider,我们需要在<item>标签中指定一个actionViewClass属性,在里面填入Action Provider的完整类名。我们可以通过继承ActionProvider类的方式来创建一个自己的Action Provider,同时,Android也提供好了几个内置的Action Provider,比如说ShareActionProvider。
由于每个Action Provider都可以自由地控制事件响应,所以它们不需要在onOptionsItemSelected()方法中再去监听点击事件,而是应该在onPerformDefaultAction()方法中去执行相应的逻辑。
使用步骤:
1,配置xml文件
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.example.actionbartest.MainActivity" > <item android:id="@+id/action_share" android:actionProviderClass="android.widget.ShareActionProvider" <!--也可以Action Provider的子类--> android:showAsAction="ifRoom" android:title="@string/action_share" /> ...... </menu>
注意,ShareActionProvider会自己处理它的显示和事件,但我们仍然要记得给它添加一个title,以防止它会在overflow当中出现。
接着剩下的事情就是通过Intent来定义出你想分享哪些东西了,我们只需要在onCreateOptionsMenu()中调用MenuItem的getActionProvider()方法来得到该ShareActionProvider对象,再通过setShareIntent()方法去选择构建出什么样的一个Intent就可以了。代码如下所示:
ActionBar添加顶部滑动导航:
Tabs的应用可以算是非常广泛了,它可以使得用户非常轻松地在你的应用程序中切换不同的视图。而Android官方更加推荐使用ActionBar中提供的Tabs功能,因为它更加的智能,可以自动适配各种屏幕的大小。比如说,在平板上屏幕的空间非常充足,Tabs会和Action按钮在同一行显示,如下图所示:
而如果是在手机上,屏幕的空间不够大的话,Tabs和Action按钮则会分为两行显示,如下图所示:
下面我们就来看一下如何使用ActionBar提供的Tab功能,大致可以分为以下几步:
1. 实现ActionBar.TabListener接口,这个接口提供了Tab事件的各种回调,比如当用户点击了一个Tab时,你就可以进行切换Tab的操作。
2.为每一个你想添加的Tab创建一个ActionBar.Tab的实例,并且调用setTabListener()方法来设置ActionBar.TabListener。除此之外,还需要调用setText()方法来给当前Tab设置标题。
3.最后调用ActionBar的addTab()方法将创建好的Tab添加到ActionBar中。
看起来并不复杂,总共就只有三步,那么我们现在就来尝试一下吧。首先第一步需要创建一个实现ActionBar.TabListener接口的类,代码如下所示:
而当Tab没有被选中的时候,则调用FragmentTransaction的detach()方法,将UI资源释放掉。
当Tab被重新选中的时候会调用onTabReselected()方法,如果没有特殊需求的话,通常是不需要进行处理的。
接下来第二步要给每一个Tab创建一个ActionBar.Tab的实例,在此之前要先准备好每个Tab页对应的Fragment。比如说这里我们想创建两个Tab页,Artist和Album,那就要先准备好这两个Tab页对应的Fragment。首先新建ArtistFragment,代码如下所示:
然后如法炮制,新建AlbumFragment,代码如下所示:
ImageLoder框架的学习:参考(http://blog.csdn.net/xiaanming/article/details/26810303)
imageLoader的特点:
1,新建一个Android项目,下载JAR包添加到工程libs目录下;新建一个MyApplication继承Application,并在onCreate()中创建ImageLoader的配置参数,并初始化到ImageLoade,如:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); //创建默认的ImageLoader配置参数 ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this); //Initialize ImageLoader with configuration. ImageLoader.getInstance().init(configuration); } }
<manifest> <uses-permission android:name="android.permission.INTERNET" /> <!-- Include next permission if you want to allow UIL to cache images on SD card --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... <application android:name="MyApplication"> ... </application> </manifest>
final ImageView mImageView = (ImageView) findViewById(R.id.image); String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg"; ImageLoader.getInstance().loadImage(imageUrl, new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { mImageView.setImageBitmap(loadedImage); } @Override public void onLoadingCancelled(String imageUri, View view) { } });传入图片的url和ImageLoaderListener, 在回调方法onLoadingComplete()中将loadedImage设置到ImageView上面就行了,如果你觉得传入ImageLoaderListener太复杂了,我们可以使用SimpleImageLoadingListener类,该类提供了ImageLoaderListener接口方法的空实现,使用的是缺省适配器模式
final ImageView mImageView = (ImageView) findViewById(R.id.image); String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg"; ImageLoader.getInstance().loadImage(imageUrl, new SimpleImageLoadingListener(){ @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); mImageView.setImageBitmap(loadedImage); } });
接下来我们就来看看网络图片加载的另一个方法displayImage(),代码如下
final ImageView mImageView = (ImageView) findViewById(R.id.image); String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg"; //显示图片的配置 DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub) .showImageOnFail(R.drawable.ic_error) .cacheInMemory(true) .cacheOnDisk(true) .bitmapConfig(Bitmap.Config.RGB_565) .build(); ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);加载gridView,listview加载图片
使用GridView,ListView来显示大量的图片,而当我们快速滑动GridView,ListView,我们希望能停止图片的加载,而在GridView,ListView停止滑动的时候加载当前界面的图片,这个框架当然也提供这个功能,使用起来也很简单,它提供了PauseOnScrollListener这个类来控制ListView,GridView滑动过程中停止去加载图片,该类使用的是代理模式
[java] view plaincopy在CODE上查看代码片派生到我的代码片 listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling)); gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
Android自定义控件的方法:
1,通过继承的方式定义控件,如继承自editText,TextView.....
2,组合方式。当前控件类从容器类继承,并将若干个控件添加到当前的容器中(如继承自viewGroup)
3,绘制控件,即直接从view继承,并在onDraw中绘制;
直接继承view说明:
1, 测量尺寸时的回调方法 onMeasure(int widthMeasureSpec, int heightMeasureSpec) ,如果要设置当前view的大小 setMeasuredDimension(width, height)
2,确定位置的时候调用此方法 onLayout
3,绘制Ui回调 :ondraw方法
Android事件分发机制:
true:当前控件拦截该事件,自己处理:(事件至上为止)
false:当前控件自己不处理,交给父控件处理
事件冒泡机制:所有事件都是由view级别产生
事件原理:
分发机制:从高级别向低级别分发(父类到子类),如果子类都不处理,再交给view处理
响应机制:从低级别向高级别响;(子类到父类)
view 拦截到事件后,父类不能直接处理,一般交给子类
自定义listView下拉刷新:
1,定义下拉刷新时头view的布局文件,在构造函数里初始化头布局(通过listiew的addHeadView方法添加);并且设置paddingTop为自身高度的负数(measure(0,0)测量view自身的宽高,getMeasuredHeight获得测量后的高度,setPadding方法),将头布局偏移出屏幕外面;
2,onTouchEvent事件处理;通过手指在屏幕上垂直移动的距离。实现onScrollListerent接口,
如何获得listview中的第一条可见记录?
实现onScrollerListener接口,然后在通过回调参数获得;
如何判断手指向上还是向下移动?
手指抬起的y坐标-手指按下的y坐标;
头布局的高度的负数+间距>paddinTop 就把onTouchEvent交给父方法去处理(即执行super.onTouchEvent处理);
Handler Messsage Looper的关系:
Message:队列中的消息,用于传输与消息有关的数据或对象;一般不能new,从消息池中去获取,持有一个handler成员对象;
Handler负责发送消息,在发送消息时将handler与msg绑定,并且处理队列中的Message;
一个子线程怎么把数据传到主线程(UI)上?线程之间的通信
handler负责从消息队列队头存放数据,从消息队尾取出数据;
一个构建handler就与一个与之关联的默认looper;
looper是为了让Handler能不断存取消息
如果是子线程向主线程发送消息:使用主线程的looper,由系统维护;
如果是两个子线程之间发送消息:需要自己创建looper;
每个线程中最多只会有一个Looper对象,绑定在ThreadLocal上面
应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了
主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象
消息队列(MessageQueue)在Looper的构造函数中创建的,因此一个Looper也就对应了一个MessageQueue
Message:队列中的消息,用于传输与消息有关的数据或对象;一般不能new,从消息池中去获取,持有一个handler成员对象;
Handler负责发送消息,在发送消息时将handler与msg绑定,并且处理队列中的Message;
一个子线程怎么把数据传到主线程(UI)上?线程之间的通信
handler负责从消息队列队头存放数据,从消息队尾取出数据;
一个构建handler就与一个与之关联的默认looper;
looper是为了让Handler能不断存取消息
如果是子线程向主线程发送消息:使用主线程的looper,由系统维护;
如果是两个子线程之间发送消息:需要自己创建looper;
每个线程中最多只会有一个Looper对象,绑定在ThreadLocal上面
应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了
主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象
消息队列(MessageQueue)在Looper的构造函数中创建的,因此一个Looper也就对应了一个MessageQueue
Looper中的loop方法有一个死循环,然后不断地调用的MessageQueue的消息队列的出队方法。
如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,
否则就进入一个阻塞状态,一直等到有新的消息入队。
继续看loop()方法的第14行,每当有一个消息出队,就将它传递到对应的handler;
自定义GroupView:
viewGroup:view的群组,内有addView,removeView
onLayout:在viewGroup内子view的位置(对子view进行布局,确定子view的位置)
如:protected void onLayout(boolean changed, int l, int t, int r, int b) //changed若为ture,说明布局发生了变化;l,t,r,b(左,顶,右,底)当前viewgroup在父view中的位置 可以通过View中的layout方法指定view在ViewGroup中的位置,如:layout(int l, int t, int r, int b)------>给View指定位置,l,t,r,b是指在viewGroup中的位置如何让ViewGroup中的view移动?
重写onTouchEvent方法,对事件进行解析(也可以通过GestureDetector进行解析,所有动作都帮我们解析了)
1,自定义GestureDetector detector = new GestureDetector(context, new OnGestureListener() { //当有一个手指台起的时候触发 @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public void onShowPress(MotionEvent e) {//当手指按下的时候触发 } @Override /** * 响应手指在屏幕上的滑动事件 */ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { /** * 移动当前view内容 移动一段距离 * disX X方向移的距离 为正是,图片向左移动,为负时,图片向右移动 * disY Y方向移动的距离 */ scrollBy((int) distanceX, 0); /** * 将当前视图的基准点移动到某个点 坐标点 * x 水平方向X坐标 * Y 竖直方向Y坐标 * scrollTo(x, y); */ return false; } @Override public void onLongPress(MotionEvent e) { } @Override /** * 发生快速滑动时的回调 velocityx:X方向上的速度 */ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { isFling = true; if(velocityX>0 && currId>0){ // 快速向右滑动 currId--; }else if(velocityX<0 && currId<getChildCount()-1){ // 快速向左滑动 currId++; } moveToDest(currId); return false; } @Override public boolean onDown(MotionEvent e) { return false; } }); 2,将TouchEvent事件将给GestureDetector去处理 如: @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); detector.onTouchEvent(event); return true; }view与滑动中有关的方法(瞬间移动):
scrollTo:将当前视图的基准点移动到某个点;x水平方向的x坐标,y竖直方向的坐标(移动的基准点也会跟着移动)
如果需要自己控制移动的速度,可以借助Scroller类(可以控制单位时间内移动的距离):
当调用invalidate方法时会导致computeScroll,onDraw方法的执行:
具体实现步骤(两种方法):
方法一:
1),设置动画的开始坐标、结束坐标、及动画时间
2) ,在computScroll方法中,计算现在应该偏移到哪(通过computeSrollOffset计算)? (当前位置=开始位置+移动距离(距离=速度*时间)),然后调用scrollTo移动,最后调用invalidate,而调用invalidate又会导致computScroll被调用;
方法二:也可以调用系统的Scroller方法
1),startScroll(int startX, int startY, int dx, int dy, int duration)设置需要滚动的参数;
2),在computScroll方法中,调用computeSrollOffset获得当前应该偏移到哪里,然后调用ScrollTo,然后invalidate方法.......;
onMeasure 设置控件自己的大小,做为viewGroup还有一个责任:计算每一个子view的大小;
如果是测量子view的大小,调用view中的measure方法去测试大小(如果不去测量大小,是不会显示的)
父view 会根据子view的需求,和自身的情况,来综合确定子view的位置,(确定他的大小)
onMeasure(int widthMeasureSpec, int heightMeasureSpec) //这两个参数包含了两层含义 ;widthMeasureSpec 整形32位,测量屏幕大小的时候32位的用不完,就让它包含多一些因素,于是他将这个值分为两部分, 前两位代码父view根据一定的规则计算出来的模式,这个模式又分为三种:一种表示后面的大小必须要这么大,一种最大能有多大(自己想要多大),, 后三十位代表父view给你的空间(可以减去padding) UNSPECIFIED:不指定大小不一,父view不做要求,子view你想要多大给多大; AT_MOST:最大大小 EXACTLY: // int size = MeasureSpec.getSize(widthMeasureSpec); 后三十位 父view分配给你大小 // int mode = MeasureSpec.getMode(widthMeasureSpec);前两位 模式
view.getMeasuredWidth()得到测量的大小
view.getWidth()得到真实的大小
构造方法---->测量大小---> onlayout
view的事件传递机制:
onInterceptTouchEvent(MotionEvent ev)(默认返回为false)是否中断事件的传递( 这个方法决定了子view是否可以接收到事件)
如果点击了内部的button,最外面的容器会先收到onTouchEvent,它会判断当前的点是在哪个viewGroup上面,然后这个viewGroup会判断onInterceptTouchEvent(MotionEvent ev)是否返回false,如果返回了ture,则由自己的onTouchEvent进行处理,否则向下传递,确定具体哪一个viewGroup
,然后再进行判断,事件从外一层一层的传递,直到传递最里面的view;如果最里面的view 的onTouchEvent返回为ture,说明就由它处理;如果最里面的view的onTouchEvent返回为false,又从里面往外面(内到外)传递(即依次判断父view的onTouchEvent是否返回为true,如果为ture则消费,否则继续)
onInterceptTouchEvent返回为ture时中断事件,执行自己的onTouchEvent;
onInterceptTouchEvent返回为false时,默认处理,不中断,也不会执行自己的onTouchEvent
在onInterceptTouchEvent 之前会调用dispatchTouchEvent(MotionEvent ev)对事件进行分发 (最早执行)
事件传递机制:
1),在view执行dispatchTouchEvent方法,开始分发事件
2),执行onInterceptTouchEvent判断是否中断事件
3),执行onTouchEvent去处理事件
AndroidPN:
原理:基于长连接的原理
LruCache源码解析:
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder)构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。
参数:
initialCapacity - 初始容量
loadFactor - 加载因子
accessOrder - 排序模式 - 对于访问顺序,为 true;对于插入顺序,则为 false
LruCache:
protected int sizeOf(K key, V value) {
return 1;
}
向集合里面put计算元素的大小:
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);//调用sizeOf方法计算大小
previous = map.put(key, value);
if (previous != null) {//如果之前放过,则previous不为null
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);//清理栈底的大小,如果发现容量大小超过了设置的大小,则清除最久未使用的元素
return previous;
}
上传下载队列及断点引续传队列:
文件被单击-->把文件插入下载数据库(文件名\长度\下载状态(正在下载,下载成功,下载失败...)\路径...)-->加入下载队列(linkList)
-->从队列中取出第一个文件对象进行下载,并更新文件数据库的下载状态--->启动Asynctask异步访问网络进行下载-->下载完成后从队列中移除,并从数据库移除该条记录,并将下载完成的文件保存到已下载表中(需要与下载文件夹中物理文件进行比对);
如果在下载过程中用户点击了暂停,更改数据库中文件下载状态,并记录下载位置
如果下载失败后,更改数据库中文件下载状态
下载进度通知(观察者模式):
图片的优化处理:
1)采用LruCache+软引用缓存,如果内存空间充足可以将LruCache被清除(清除时调用entryRemoved方法)的对象存入软引用集合;
2),DiskLruCache:可以将被从LruCache清除的图片放入DiskLruCache;先从LruChace中取,如果未取到,则从sdk(DiskLruCache)上取,如果sdk上也没有找到,则从网络上去获取
界面如何构建高速缓存?
1),控制集合的大小,一旦超过临界值就清除一些;(代码简单,适应性不强)
2),降低引用级别(一旦gc发现内存不够用时,就回收掉);(适应性强,虽然引用降低了,必须等待gc去回收,必须要给gc提供一个回收时间,所以一旦申请内存速度过快,应用很快就崩溃掉了(不适用))
3),用fragment,并且使用replace,不会缓存界面;
软引用:在OOM之前被回收
弱引用:一旦被GC发现就回收
虚引用:一旦被创建就被回收
软引用实现案例
软引用实现步骤:
重写hashMap的put方法,将占用内存较多的value对象包装成SoftReference
对象,然后再将value放进去
重写hashMap的get方法,按照key去集合中get,get出来的是被包装过的SoftReference
对象,然后将SoftReference
中的被包装的对象返回
一旦占用较多内存的对象被gc回收后,
手机内存
优化原则:
listView的优化:
复用历史缓存的view对象 converView
减少子孩子id查询的次数
分批加载数据;分页加载数据
基本原则:
时间换时间:listview的分批加载数据 ,分批加载缩短每次加载的时间
时间换空间:在读文件的时候使用的byte字节数组,直接将数据写到byte字节数组内,写满以后再重复向byte中写入
空间换时间:文件快速查找的索引
空间换空间:虚拟内存
SlidingDrawer:可以左右,上下滑动
支付相关内容:
到官网申请 "商户签约",购买手机支付的服务,成功后会分配一个商户ID,账户ID;
公司在支付宝上的账户信息\用户的支付宝信息\账单信息
支付流程:
客户端发送结算请求到服务器端,服务器端生成订单后,将订单数据回复给客户端,客户端支付时需要判断是否安装支付宝应用,
如果安装了则将订单数据(双方账号等信息)发送到支付宝的客户端,由支付宝的客户端完成于支付宝服务器的数据传输,
当完成支付后,手机端会收到通知,同时支付宝会将处理成功的消息发送给服务器端,服务器端处理后续工作。