android资源文件查找顺序

结论:手机会先找自己对应的layout-dpi文件,如果没有就会先向高精度的文件夹下查找,高的都没有就会想精度低的查找。

drawable-ldip
drawable-mdip
drawable-hdip
drawable-xhdip
drawable-xxhdip
drawable-xxxhdip
手机对应hdip,如果没有相应的文件:顺序是xh–xxh–xxxh–m-l;同样的values同理。

分享到 评论

Fragment

FragmentActivity与Activity

FragmentActivity是为兼容4.0一下系统而需要的Activity类

Fragment类的生命周期

new:onAttach()–>onCreat()–>onCreatView()–>onStart()–>onresume
remove:onPause()–>onStop()–>onDestoryView()–>onDestory()–>onDetach()

使用:再Activity或FragmentActivity中获取FragmentManager

分享到 评论

自定义View仿IOS等待圈

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

/**
* Created by limxing on 16/1/7.
*/
public class LoadView extends ImageView {
private float degrees = 0f;
private Matrix max;
private int width;
private int height;
private Bitmap bitmap;

public LoadView(Context context) {
super(context);
init();
}

public LoadView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public LoadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
degrees += 30f;
max.setRotate(degrees, width, height);
setImageMatrix(max);
if(degrees==360){
degrees=0;
}
}
};

private void init() {
setScaleType(ScaleType.MATRIX);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.loading);
setImageBitmap(bitmap);
max = new Matrix();

width = bitmap.getWidth() / 2;
height = bitmap.getHeight() / 2;
Timer time=new Timer();
time.schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0);
}
},0,80);
}
}
分享到 评论

Bitmap图片占用内存大小

一张图片(BitMap)占用的内存=图片长度图片宽度单位像素占用的字节数

创建一个BitMap时,其单位像素占用的字节数由其参数BitmapFactory.Options的inPreferredConfig变量决定。
inPreferredConfig为Bitmap.Config类型,Bitmap.Config类是个枚举类型,它可以为以下值:

Bitmap.Config : ARGB_8888
一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites
共32bites,即4个字节

一张100100的图片占用内存的大小:
100
100*4=40000字节

另外,需要注意这里的图片占用内存是指在Navtive中占用的内存,当然BitMap使用的绝大多数内存就是该内存。因为我们可以简单的认为它就是BitMap所占用的内存。

在Android4.0之前,Bitmap的内存是分配在Native堆中,调用recycle()可以立即释放Native内存。
从Android4.0开始,Bitmap的内存就是分配在dalvik堆中,即JAVA堆中的,调用recycle()并不能立即释放Native内存。但是调用recycle()也是一个良好的习惯。

=================
资源文件中图片占用内存大小:
1b=8位
1kb=1024b
1mb=1024kb
1gb=1024mb

图片占用内存大小计算公式:

图片的像素长度width
图片的像素高度height
存放android手机drawable目录:dpi
mdpi=160
hdpi=240
xhdpi=320
xxhdpi=480
xxxhdpi=640
手机像素密度:phoneDpi

公式:width height (dpi/phoneDpi+0.5)2 * 4

总结,把图片生成Bitmap则和手机像素密度无关了,
因此公式为:width heigth 4

分享到 评论

BitmapHelper

#####背景:调用相机拍照,结果返回时,导致内存迅速升高,内存不足时会导致OOM

发现的原因:在相机拍照后,会生成很大的照片,分辨率也很高,因此占用的内存会很大,在网上找了一些同学的相机拍照代码块发现,使用到一个BitmapHelper的类,本类的主要作用是:分析拍摄的照片,如果有方向不对了对照片进行旋转,进行旋转需要吧图片加载到内存中,原尺寸的照片通常会很大,会占用两个Bitmap才能够完成旋转,因此bitmap helper在旋转之前进行了尺寸的压缩。
不同的手机或者pad对尺寸进行修改会造成所放的比例计算差异。。。好多废话,下面是我对Bitmap Helper类的简单修改,能够让不同的尺寸或方向的照片进行正确的缩放。

Bitmap Helper能够将拍摄的照片缩放后保存在某个目录中:

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
public class BitmapHelper {

/**
* get the orientation of the bitmap {@link ExifInterface}
*
* @param path
* @return
*/
public static int getDegress(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}

/**
* rotate the bitmap
*
* @param bitmap
* @param degress
* @return
*/
public static Bitmap rotateBitmap(Bitmap bitmap, int degress) {
if (bitmap != null) {
Matrix m = new Matrix();
m.postRotate(degress);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
return bitmap;
}
return bitmap;
}

/**
* caculate the bitmap sampleSize
*
* @param
* @return
*/
public static int caculateInSampleSize(Options options, int rqsW, int rqsH) {
int height = options.outHeight;
int width = options.outWidth;
//主要修改这里,让最宽的对应设定的宽,高同理
if (width > height) {
int temp = width;
width = height;
height = temp;
}
if (rqsW > rqsH) {
int rqsT = rqsH;
rqsH = rqsW;
rqsW = rqsT;
}

int inSampleSize = 1;
if (rqsW == 0 || rqsH == 0) return 1;
if (height > rqsH || width > rqsW) {
int heightRatio = Math.round((float) height / (float) rqsH);
int widthRatio = Math.round((float) width / (float) rqsW);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
/**
* 根据需要压缩到某尺寸压缩指定路径的图片,并得到图片对象
*
* @param path
* @param rqsW
* @param rqsH
* @return
*/
public static Bitmap compressBitmap(String path, int rqsW, int rqsH) {
Options options = getBitmapOptions(path);
options.inSampleSize = caculateInSampleSize(options, rqsW, rqsH);
return BitmapFactory.decodeFile(path, options);
}

/**
* 压缩指定路径图片,并将其保存在缓存目录中,通过isDelSrc判定是否删除源文件,并获取到缓存后的图片路径
*
* @param context
* @param srcPath
* @param rqsW
* @param rqsH
* @param isDelSrc
* @return
*/
public static String compressBitmap(Context context, String srcPath, int rqsW, int rqsH, boolean isDelSrc) {
int degree = getDegress(srcPath);
Bitmap bitmap = compressBitmap(srcPath, rqsW, rqsH);//根据长宽以及图片的长宽得到缩放图片
File srcFile = new File(srcPath);
String desPath = getImageCacheDir(context) + srcFile.getName();
try {
if (degree != 0) bitmap = rotateBitmap(bitmap, degree);
File file = new File(desPath);
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
fos.close();
if (isDelSrc) srcFile.deleteOnExit();

} catch (Exception e) {
}

bitmap.recycle();
System.gc();

return desPath;
}

/**
* 压缩某个输入流中的图片,可以解决网络输入流压缩问题,并得到图片对象
*
* @return Bitmap {@link Bitmap}
*/
public static Bitmap compressBitmap(InputStream is, int reqsW, int reqsH) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ReadableByteChannel channel = Channels.newChannel(is);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (channel.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) baos.write(buffer.get());
buffer.clear();
}
byte[] bts = baos.toByteArray();
Bitmap bitmap = compressBitmap(bts, reqsW, reqsH);
is.close();
channel.close();
baos.close();
return bitmap;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
}
}

/**
* 压缩指定byte[]图片,并得到压缩后的图像
*
* @param bts
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(byte[] bts, int reqsW, int reqsH) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bts, 0, bts.length, options);
options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(bts, 0, bts.length, options);


/**
* 压缩已存在的图片对象,并返回压缩后的图片
*
* @param bitmap
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(Bitmap bitmap, int reqsW, int reqsH) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 100, baos);
byte[] bts = baos.toByteArray();
Bitmap res = compressBitmap(bts, reqsW, reqsH);
baos.close();
return res;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return bitmap;
}
}

/**
* 压缩资源图片,并返回图片对象
*
* @param res {@link Resources}
* @param resID
* @param reqsW
* @param reqsH
* @return
*/
public static Bitmap compressBitmap(Resources res, int resID, int reqsW, int reqsH) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resID, options);
options.inSampleSize = caculateInSampleSize(options, reqsW, reqsH);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resID, options);
}
/**
* 得到指定路径图片的options
*
* @param srcPath
* @return Options {@link Options}
*/
public static Options getBitmapOptions(String srcPath) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, options);
options.inJustDecodeBounds = false;
return options;
}

/**
* 获取图片缓存路径
*
* @param context
* @return
*/
private static String getImageCacheDir(Context context) {
String dir = FileUtils.getCacheDir() + "Image" + File.separator;
File file = new File(dir);
if (!file.exists()) file.mkdirs();
return dir;
}
}

###压缩的方式
上面不仅用到尺寸压缩,而且用到了图片质量压缩
bitmap.compress(CompressFormat.JPEG, 80, fos);//80是图片质量
这个就是质量压缩。
质量压缩需要把文件加载到内存中,因此大图片质量压缩也会瞬间占用大量内存,因此我使用的是先尺寸压缩,再质量压缩的方式。

####关于质量压缩
质量压缩android比IOS的质量压缩采用的据说是同一个压缩方式,但是压缩中的一个参数,android默认设置为false,IOS默认设置为true,结果是设置为相同质量压缩下,iOS文件占用的空间比android少。因此我们可以把压缩的源代码搞过来,把false改为true。
我的实际测试,质量的压缩,true的比false仅仅小了30K~100k(图片越小越不明显) 。
使用方式就是:倒入.so文件和NativeUtil

so文件下载地址

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
public class NativeUtil {
private static int DEFAULT_QUALITY = 95;

public static void compressBitmap(Bitmap bit, String fileName,
boolean optimize) {
compressBitmap(bit, DEFAULT_QUALITY, fileName, optimize);

}

public static void compressBitmap(Bitmap bit, int quality, String fileName,
boolean optimize) {
Log.d("native", "compress of native");
if (bit.getConfig() != Config.ARGB_8888) {
Bitmap result = Bitmap.createBitmap(bit.getWidth(), bit.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bit.getWidth(), bit.getHeight());
canvas.drawBitmap(bit, null, rect, null);
saveBitmap(result, quality, fileName, optimize);
result.recycle();
} else {
saveBitmap(bit, quality, fileName, optimize);
}

}

private static void saveBitmap(Bitmap bit, int quality, String fileName,
boolean optimize) {
compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality,
fileName.getBytes(), optimize);

}

private static native String compressBitmap(Bitmap bit, int w, int h,
int quality, byte[] fileNameBytes, boolean optimize);

static {
System.loadLibrary("jpegbither");
System.loadLibrary("bitherjni");

}

}

博主github主页:Github主页 有一个仿IOS的旋转等待View,欢迎使用点星星

分享到 评论

自定义ProgressBar颜色

drawable文件夹中bacprogress.xml

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
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

<!-- 设置背景色(白色) -->
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dip" />

<gradient
android:endColor="#ffffff"
android:startColor="#ffffff" />
</shape>
</item>

<!-- 设置进度条颜色(蓝色) -->
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dip" />

<gradient
android:endColor="#5d9eff"
android:startColor="#5d9eff" />
</shape>
</clip>
</item>

</layer-list>

1
2
3
4
5
6
7
8
<ProgressBar
android:id="@+id/download_item_size_pro"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/bacprogress" />

https://github.com/limxing/XListView 仿iOS加载等待旋转图片放进这个项目中了,欢迎使用给星

分享到 评论

单例模式

####懒汉式单例模式最佳写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){};
public static Singleton getSingleton(){
if(singleton==null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

分享到 评论

Handler引起Activity不能释放导致OOM

继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用。 修改后不会导致内存泄露的代码如下

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
public class SampleActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;

public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}

private final MyHandler mHandler = new MyHandler(this);

private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

// Go back to the previous Activity.
finish();
}
}

相关链接

分享到 评论