-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
80 lines (80 loc) · 32.9 KB
/
Copy pathsearch.xml
File metadata and controls
80 lines (80 loc) · 32.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Gradle for Android(一)基本配置、依赖管理]]></title>
<url>%2F2017%2F05%2F26%2FGradle%20for%20Android%EF%BC%88%E4%B8%80%EF%BC%89%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE%E3%80%81%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86%2F</url>
<content type="text"><![CDATA[Gradle是一种基于Groovy的动态DSL,而Groovy语言是一种基于jvm的动态语言。这里只分享实际开发中会用到的场景,您不需要去学习Groovy语言,知道Java的您是很容易阅读Groovy语言的。系列博客涉及的知识点有:Gradle基本配置、依赖管理、全局设置、自定义BuildConfig、混淆、多渠道打包、配置签名信息、单元测试,是不是迫不及待了啊,快来学习学习。 ####基本配置新建项目,目录结构如下: ####app/build.gradle初始化的Gradle配置:123456789101112131415161718192021222324apply plugin: 'com.android.application'android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.wuxiaolong.gradle4android" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1'} 123456789apply plugin: ‘com.android.application’,表示该module是一个app module,应用了com.android.application插件,如果是一个android library,那么这里写apply plugin: ‘com.android.library’compileSdkVersion:基于哪个SDK编译,这里是API LEVELbuildToolsVersion:基于哪个构建工具版本进行构建的。defaultConfig:默认配置,如果没有其他的配置覆盖,就会使用这里的。applicationId:配置包名的versionCode:版本号versionName:版本名称buildTypes是构建类型,常用的有release和debug两种,可以在这里面启用混淆,启用zipAlign以及配置签名信息等。dependencies:不属于Android专有的配置了,它定义了该module需要依赖的jar,aar,jcenter库信息。 ####gradle-wrapper.properties声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的12345distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distszipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/distsdistributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip ####根目录的build.gradle定义在这个工程下的所有模块的公共属性12345678910111213141516171819buildscript { repositories { jcenter()//使用jcenter库 } dependencies { classpath 'com.android.tools.build:gradle:1.5.0'// 依赖android提供的1.5.0的gradle build // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }}//为所有的工程的repositories配置为jcentersallprojects { repositories { jcenter() }}task clean(type: Delete) { delete rootProject.buildDir} ####setting.gradle包含哪些模块,比如有app和library:1include ':app',':library' ####依赖管理 ####本地依赖 ####jar默认情况下,新建的Android项目会有一个lib文件夹1234dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])//即添加所有在libs文件夹中的jar //compile files('libs/WuXiaolong.jar')//不需要这样一个个去写了} ####so包用c或者c++写的library会被叫做so包,Android插件默认情况下支持native包,你需要把.so文件放在对应的文件夹中1234567891011app ├── AndroidManifest.xml └── jniLibs ├── armeabi │ └── WuXiaolong.so ├── armeabi-v7a │ └── WuXiaolong.so ├── mips │ └── WuXiaolong.so └── x86 └── WuXiaolong.so ####library工程直接依赖library库:12345dependencies { compile project(':library名字') //多个library,libraries是文件夹名字 compile project(':libraries:library名字') } ####aar文件library库输出文件是.aar文件,包含了Android资源文件,在library工程build/output/aar/下,然后app目录下创建一个aars文件夹,然后把.aar文件拷贝到该文件夹里面,然后添加该文件夹作为依赖库:app/bulid.gradle12345678repositories { flatDir { dirs 'aars' }}dependencies { compile(name:'libraryname', ext:'aar')} ####如何生成.aar文件:执行 ./gradlew assembleRelease,然后就可以在 build/outputs/aar 文件夹里生成aar文件 .jar和.aar区别:*.jar:只包含了class文件与清单文件,不包含资源文件,如图片等所有res中的文件; *.aar:包含所有资源,class以及res资源文件全部包含。 注意:如果你的library依赖了第三方库aar,须app再次依赖。比如lib1依赖了lib2 的aar,app依赖lib1,需要:1234dependencies { compile(name: 'library1-release', ext: 'aar') compile(name: 'library2-release', ext: 'aar')} ####远程仓库123dependencies { compile 'com.wuxiaolong.pullloadmorerecyclerview:library:1.0.4'}]]></content>
<categories>
<category>gradle</category>
</categories>
<tags>
<tag>gradle</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Gradle for Android(二)全局设置、自定义BuildConfig、混淆]]></title>
<url>%2F2017%2F05%2F26%2FGradle%20for%20Android%EF%BC%88%E4%BA%8C%EF%BC%89%E5%85%A8%E5%B1%80%E8%AE%BE%E7%BD%AE%E3%80%81%E8%87%AA%E5%AE%9A%E4%B9%89BuildConfig%E3%80%81%E6%B7%B7%E6%B7%86%2F</url>
<content type="text"><![CDATA[####全局设置如果有很多项目,可以设置全局来统一管理版本号或依赖库,根目录下build.gradle下:123456ext { compileSdkVersion = 23 buildToolsVersion = "23.0.2" minSdkVersion = 14 targetSdkVersion = 23} app/build.gradle12345678910android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { applicationId "com.wuxiaolong.gradle4android" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" } 可以在根目录下建个config.gradle,然后只需在根目录下build.gradle最顶部加上下面一行代码,然后同步下,意思就是所有的子项目或者所有的modules都可以从这个配置文件里读取内容。1apply from: "config.gradle" config.gradle123456789101112ext { android = [ compileSdkVersion: 23, buildToolsVersion: "23.0.2", minSdkVersion : 14, targetSdkVersion : 22, ] dependencies = [ appcompatV7': 'com.android.support:appcompat-v7:23.2.1', design : 'com.android.support:design:23.2.1' ]} app/build.gradle123456789101112131415161718android { compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { applicationId "com.wuxiaolong.gradle4android" minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode 1 versionName "1.0" } ...dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile rootProject.ext.dependencies.appcompatV7 compile rootProject.ext.dependencies.design} ####自定义BuildConfig实际开发中服务器可能有正式环境和测试环境,gradle可以通过buildConfigField来配置。123defaultConfig { buildConfigField 'String','API_SERVER_URL','"http://wuxiaolong.me/"' } buildConfigField 一共有3个参数,第一个是数据类型,和Java的类型是对等的;第二个参数是常量名,这里是API_SERVER_URL;第三个参数就是你要配置的值。 如图路径下就有个常量API_SERVER_URL,如何在代码取得这个常量值:1Log.d("wxl", "API_SERVER_URL=" + BuildConfig.API_SERVER_URL); ####启用proguard混淆一般release发布版本是需要启用混淆的,这样别人反编译之后就很难分析你的代码,而我们自己开发调试的时候是不需要混淆的,所以debug不启用混淆。对release启用混淆的配置如下:123456789android { buildTypes { release { minifyEnabled true//是否启动混淆 shrinkResources true //是否移除无用资源文件,shrinkResources依赖于minifyEnabled,必须和minifyEnabled一起用 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }} minifyEnabled为true表示启用混淆,proguardFile是混淆使用的配置文件,这里是module根目录下的proguard-rules.pro文件]]></content>
<categories>
<category>gradle</category>
</categories>
<tags>
<tag>gradle</tag>
</tags>
</entry>
<entry>
<title><![CDATA[生产者消费者问题]]></title>
<url>%2F2017%2F03%2F07%2F%E7%94%9F%E4%BA%A7%E8%80%85%E5%92%8C%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[1234567891011121314package 生产者消费者;public class ProducerConsumerTest { public static void main(String[] args) { PublicResource resource = new PublicResource(); new Thread(new ProducerThread(resource)).start(); new Thread(new ConsumerThread(resource)).start(); new Thread(new ProducerThread(resource)).start(); new Thread(new ConsumerThread(resource)).start(); new Thread(new ProducerThread(resource)).start(); new Thread(new ConsumerThread(resource)).start(); }} 123456789101112131415161718192021222324252627282930package 生产者消费者;/** * 生产者线程,负责生产公共资源 * @author dream * */public class ProducerThread implements Runnable{ private PublicResource resource; public ProducerThread(PublicResource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } resource.increase(); } } } 123456789101112131415161718192021222324252627282930313233package 生产者消费者;/** * 消费者线程,负责消费公共资源 * @author dream * */public class ConsumerThread implements Runnable{ private PublicResource resource; public ConsumerThread(PublicResource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } resource.decrease(); } } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package 生产者消费者;/** * 公共资源类 * @author dream * */public class PublicResource { private int number = 0; private int size = 10; /** * 增加公共资源 */ public synchronized void increase() { while (number >= size) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } number++; System.out.println("生产了1个,总共有" + number); notifyAll(); } /** * 减少公共资源 */ public synchronized void decrease() { while (number <= 0) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } number--; System.out.println("消费了1个,总共有" + number); notifyAll(); }}]]></content>
<categories>
<category>javaSE</category>
</categories>
<tags>
<tag>JavaConcurrent</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android中图片的三级缓存]]></title>
<url>%2F2017%2F03%2F06%2FAndroid%E4%B8%AD%E5%9B%BE%E7%89%87%E7%9A%84%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98%2F</url>
<content type="text"><![CDATA[##为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说, 流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知 所以提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量 ##什么是三级缓存 网络缓存, 不优先加载, 速度慢,浪费流量 本地缓存, 次优先加载, 速度快 内存缓存, 优先加载, 速度最快 ##三级缓存原理 首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中 之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片 总之,只在初次访问新内容时,才通过网络获取图片资源 ##具体实现及代码 ####1、 自定义的图片缓存工具类(MyBitmapUtils) 通过new MyBitmapUtils().display(ImageView ivPic, String url) 提供给外部方法进行图片缓存的接口 参数含义:ivPic 用于显示图片的ImageView,url 获取图片的网络地址 123456789101112131415161718192021222324252627282930313233343536373839/** * 自定义的BitmapUtils,实现三级缓存 */ public class MyBitmapUtils { private NetCacheUtils mNetCacheUtils; private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public MyBitmapUtils(){ mMemoryCacheUtils=new MemoryCacheUtils(); mLocalCacheUtils=new LocalCacheUtils(); mNetCacheUtils=new NetCacheUtils(mLocalCacheUtils,mMemoryCacheUtils); } public void disPlay(ImageView ivPic, String url) { ivPic.setImageResource(R.mipmap.pic_item_list_default); Bitmap bitmap; //内存缓存 bitmap=mMemoryCacheUtils.getBitmapFromMemory(url); if (bitmap!=null){ ivPic.setImageBitmap(bitmap); System.out.println("从内存获取图片啦....."); return; } //本地缓存 bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if(bitmap !=null){ ivPic.setImageBitmap(bitmap); System.out.println("从本地获取图片啦....."); //从本地获取图片后,保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url,bitmap); return; } //网络缓存 mNetCacheUtils.getBitmapFromNet(ivPic,url); } } ####2、 网络缓存(NetCacheUtils) 网络缓存中主要用到了AsyncTask来进行异步数据的加载 简单来说,AsyncTask可以看作是一个对handler和线程池的封装,通常,AsyncTask主要用于数据简单时,handler+thread主要用于数据量多且复杂时,当然这也不是必须的,仁者见仁智者见智 同时,为了避免内存溢出的问题,我们可以在获取网络图片后。对其进行图片压缩 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106/** * 三级缓存之网络缓存 */ public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) { mLocalCacheUtils = localCacheUtils; mMemoryCacheUtils = memoryCacheUtils; } /** * 从网络下载图片 * @param ivPic 显示图片的imageview * @param url 下载图片的网络地址 */ public void getBitmapFromNet(ImageView ivPic, String url) { new BitmapTask().execute(ivPic, url);//启动AsyncTask } /** * AsyncTask就是对handler和线程池的封装 * 第一个泛型:参数类型 * 第二个泛型:更新进度的泛型 * 第三个泛型:onPostExecute的返回结果 */ class BitmapTask extends AsyncTask<Object, Void, Bitmap> { private ImageView ivPic; private String url; /** * 后台耗时操作,存在于子线程中 * @param params * @return */ @Override protected Bitmap doInBackground(Object[] params) { ivPic = (ImageView) params[0]; url = (String) params[1]; return downLoadBitmap(url); } /** * 更新进度,在主线程中 * @param values */ @Override protected void onProgressUpdate(Void[] values) { super.onProgressUpdate(values); } /** * 耗时方法结束后执行该方法,主线程中 * @param result */ @Override protected void onPostExecute(Bitmap result) { if (result != null) { ivPic.setImageBitmap(result); System.out.println("从网络缓存图片啦....."); //从网络获取图片后,保存至本地缓存 mLocalCacheUtils.setBitmapToLocal(url, result); //保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url, result); } } } /** * 网络下载图片 * @param url * @return */ private Bitmap downLoadBitmap(String url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestMethod("GET"); int responseCode = conn.getResponseCode(); if (responseCode == 200) { //图片压缩 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize=2;//宽高压缩为原来的1/2 options.inPreferredConfig=Bitmap.Config.ARGB_4444; Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(),null,options); return bitmap; } } catch (IOException e) { e.printStackTrace(); } finally { conn.disconnect(); } return null; } } ####3、本地缓存(LocalCacheUtils) 在初次通过网络获取图片后,我们可以在本地SD卡中将图片保存起来 可以使用MD5加密图片的网络地址,来作为图片的名称保存 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051/** * 三级缓存之本地缓存 */ public class LocalCacheUtils { private static final String CACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/WerbNews"; /** * 从本地读取图片 * @param url */ public Bitmap getBitmapFromLocal(String url){ String fileName = null;//把图片的url当做文件名,并进行MD5加密 try { fileName = MD5Encoder.encode(url); File file=new File(CACHE_PATH,fileName); Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file)); return bitmap; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 从网络获取图片后,保存至本地缓存 * @param url * @param bitmap */ public void setBitmapToLocal(String url,Bitmap bitmap){ try { String fileName = MD5Encoder.encode(url);//把图片的url当做文件名,并进行MD5加密 File file=new File(CACHE_PATH,fileName); //通过得到文件的父文件,判断父文件是否存在 File parentFile = file.getParentFile(); if (!parentFile.exists()){ parentFile.mkdirs(); } //把图片保存至本地 bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file)); } catch (Exception e) { e.printStackTrace(); } } } ####4、 内存缓存(MemoryCacheUtils) 这是本文中最重要且需要重点介绍的部分 进行内存缓存,就一定要注意一个问题,那就是内存溢出(OutOfMemory) 为什么会造成内存溢出? Android 虚拟机默认分配给每个App 16M的内存空间,真机会比16M大,但任会出现内存溢出的情况 Android 系统在加载图片时是解析每一个像素的信息,再把每一个像素全部保存至内存中 图片大小 = 图片的总像素 * 每个像素占用的大小 单色图:每个像素占用1/8个字节,16色图:每个像素占用1/2个字节,256色图:每个像素占用1个字节,24位图:每个像素占用3个字节(常见的rgb构成的图片) 例如一张1920x1080的JPG图片,在Android 系统中是以ARGB格式解析的,即一个像素需占用4个字节,图片的大小=1920x1080x4=7M 实现方法: 通过 HashMap键值对的方式保存图片,key为地址,value为图片对象,但因是强引用对象,很容易造成内存溢出,可以尝试SoftReference软引用对象 通过 HashMap]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>性能优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java并发基础知识]]></title>
<url>%2F2017%2F03%2F06%2FJava%E5%B9%B6%E5%8F%91%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%2F</url>
<content type="text"><![CDATA[(Executor框架和多线程基础) Thread与Runable如何实现多线程 Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。 实现Runnable接口相比继承Thread类有如下优势: 可以避免由于Java的单继承特性而带来的局限 增强程序的健壮性,代码能够被多个程序共享,代码与数据是独立的 适合多个相同程序代码的线程区处理同一资源的情况 补充:Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值,代码如下所示: 123456789101112131415161718192021222324252627282930313233343536class MyTask implements Callable<Integer> { private int upperBounds; public MyTask(int upperBounds) { this.upperBounds = upperBounds; } @Override public Integer call() throws Exception { int sum = 0; for(int i = 1; i <= upperBounds; i++) { sum += i; } return sum; } } public class Test { public static void main(String[] args) throws Exception { List<Future<Integer>> list = new ArrayList<>(); ExecutorService service = Executors.newFixedThreadPool(10); for(int i = 0; i < 10; i++) { list.add(service.submit(new MyTask((int) (Math.random() * 100)))); } int sum = 0; for(Future<Integer> future : list) { while(!future.isDone()) ; sum += future.get(); } System.out.println(sum); } } 线程同步的方法有什么;锁,synchronized块,信号量等 锁的等级:方法锁、对象锁、类锁 生产者消费者模式的几种实现,阻塞队列实现,sync关键字实现,lock实现,reentrantLock等 ThreadLocal的设计理念与作用,ThreadPool用法与优势(这里在Android SDK原生的AsyncTask底层也有使用) 线程池的底层实现和工作原理(建议写一个雏形简版源码实现) 几个重要的线程api,interrupt,wait,sleep,stop等等 写出生产者消费者模式。 ThreadPool用法与优势。 Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。 wait()和sleep()的区别。 sleep()方法是线程类(Thread)的静态方法,导致此线程暂停执行指定时间,将执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复(线程回到就绪(ready)状态),因为调用sleep 不会释放对象锁。wait()是Object 类的方法,对此对象调用wait()方法导致本线程放弃对象锁(线程暂停执行),进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入就绪状态。 ###3、IO(IO,NIO,目前okio已经被集成Android包) IO框架主要用到什么设计模式 JDK的I/O包中就主要使用到了两种设计模式:Adatper模式和Decorator模式。 NIO包有哪些结构?分别起到的作用? NIO针对什么情景会比IO有更好的优化? OKIO底层实现]]></content>
<categories>
<category>javaSE</category>
</categories>
<tags>
<tag>JavaConcurrent</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Fragment---监听返回键事件]]></title>
<url>%2F2016%2F08%2F09%2FFragment_%E7%9B%91%E5%90%AC%E8%BF%94%E5%9B%9E%E9%94%AE%E4%BA%8B%E4%BB%B6%2F</url>
<content type="text"><![CDATA[有时候为了方便代码维护,在fragment中可能要处理一些监听fragment back键12345678910111213141516//主界面获取焦点 private void getFocus() { getView().setFocusableInTouchMode(true); getView().requestFocus(); getView().setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { // 监听到返回按钮点击事件 ...... return true; } return false; } }); } 以上代码是stackoverflow.com中找到的一个解决方案,但是在使用时,由于Fragment页面里可能有其他能获取焦点的View(例如EditText),会导致监听失效,点击返回键会返回到上个页面。 更完善的解决方案:除了上面的代码,我们需要对可以获取焦点的View的setOnKeyListener进行处理,这里以一个EditText为例: 12345678910111213141516//private EditText nickname;nickname.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { //关闭软键盘 InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(nickname.getWindowToken(), 0); //使得根View重新获取焦点,以监听返回键 getFocus(); } return false; } }); 使用到的资料: http://blog.csdn.net/ccpat/article/details/45176665http://stackoverflow.com/questions/22552958/handling-back-press-when-using-fragments-in-android]]></content>
<categories>
<category>事件监听</category>
</categories>
<tags>
<tag>事件传递管理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android性能优化-内存泄漏的8个Case]]></title>
<url>%2F2016%2F05%2F10%2F%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%2F</url>
<content type="text"><![CDATA[1.什么是内存泄漏?JVM内存管理 关于内存泄漏我们要知道,JVM内存分配的几种策略。 1.静态的 静态的存储区,内存在程序编译的时候就已经分配好了,这块内存在程序整个运行期间都一直存在,它主要存放静态数据、全局的static数据和一些常量。 2.栈式的 在执行方法时,方法一些内部变量的存储都可以放在栈上面创建,方法执行结束的时候这些存储单元就会自动被注释掉。栈 内存包括分配的运算速度很快,因为内在在处理器里面。当然容量有限,并且栈式一块连续的内存区域,大小是由操作系统决定的,他先进后 出,进出完成不会产生碎片,运行效率高且稳定 3.堆式的 也叫动态内存 。我们通常使用new 来申请分配一个内存。这里也是我们讨论内存泄漏优化的关键存储区。GC会根据内存的使用情况,对堆内存里的垃圾内存进行回收。 堆内存是一块不连续的内存区域,如果频繁地new/remove会造成大量的内存碎片,GC频繁的回收,导致内存抖动,这也会消耗我们应用的性能我们知道可以调用 System.gc();进行内存回收,但是GC不一定会执行。 面对GC的机制,我们是否无能为力?其实我们可以通过声明一些引用标记来让GC更好对内存进行回收。 类型 回收时机 生命周期 StrongReference (强引用) 任何时候GC是不能回收他的,哪怕内存不足时,系统会直接抛出异常OutOfMemoryError,也不会去回收 进程终止 SoftReference (软引用) 当内存足够时不会回收这种引用类型的对象,只有当内存不够用时才会回收 内存不足,进行GC的时候 WeakReference (弱引用) GC一运行就会把给回收了 GC后终止 PhantomReference (虚引用) 如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收 任何时候都有可能 开发时,为了防止内存溢出,处理一些比较占用内存并且生命周期长的对象时,可以尽量使用软引用和弱引用。 Tip:成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体),因为他们属于类,类对象最终还是要被new出来的。局部变量的基本数据类型和引用存在栈中,应用的对象实体存储在堆中。因为它们属于方法当中的变量,生命周期会随着方法一起结束 内存泄漏的定义当一个对象已经不需要使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用,从而导致了对象不能被GC回收。 这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。 内存泄漏与内存溢出的区别 内存泄漏(Memory Leak) 进程中某些对象已经没有使用的价值了,但是他们却还可以直接或间接地被引用到GC Root导致无法回收。当内存泄漏过多的时候,再加上应用本身占用的内存,日积月累最终就会导致内存溢出OOM 内存溢出(OOM) 当 应用的heap资源超过了Dalvik虚拟机分配的内存就会内存溢出 内存泄漏带来的影响 应用卡顿 泄漏的内存影响了GC的内存分配,过多的内存泄漏会影响应用的执行效率 应用异常(OOM) 过多的内存泄漏,最终会导致 Dalvik可分配的内存越来越少,更加容易出现OOM 2.Android开发常见的内存泄漏(1)单例造成的内存泄漏 错误示例 当调用getInstance时,如果传入的context是Activity的context。 只要这个单例没有被释放,那么这个Activity也不会被释放一直到进程退出才会释放。 解决方案 能使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整个进程的周期 (2)非静态内部类创建静态实例造成的内存泄漏 错误示例 解决方案 将非静态内部类修改为静态内部类。(静态内部类不会隐式持有外部类) (3)Handler造成的内存泄漏 错误示例 mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。 解决方案 创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息 (4)线程造成的内存泄漏 错误示例 异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏 解决方案 使用 静态内部类,避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源 (5)资源未关闭造成的内存泄漏 错误示例 对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏 解决方案 在Activity销毁时及时关闭或者注销 (6)使用了静态的Activity和View 错误示例 解决方案 应该及时将静态的应用 置为null,而且一般不建议将View及Activity设置为静态 (7)注册了系统的服务,但onDestory未注销 错误示例 解决方案//不需要用的时候记得移除监听sensorManager.unregisterListener(listener); (8)不需要用的监听未移除会发生内存泄露 错误示例 解决方案 Tip:tv.setOnClickListener();//监听执行完回收对象,不用考虑内存泄漏tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听,放到集合里面,需要考虑内存泄漏]]></content>
<categories>
<category>内存管理</category>
</categories>
<tags>
<tag>内存管理</tag>
</tags>
</entry>
</search>