-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathsearch.xml
More file actions
435 lines (435 loc) · 583 KB
/
search.xml
File metadata and controls
435 lines (435 loc) · 583 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
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Mac支持NTFS格式]]></title>
<url>%2F%2F2017%2F10%2F05%2Fmac_support_NTFS.html</url>
<content type="text"><![CDATA[开启流程简介 挂载上你的NTFS硬盘,查看硬盘名称 编辑/etc/fstab文件,使其支持NTFS写入 将/Volumes中的NTFS磁盘快捷方式到Finder 详细流程 1.插上硬盘后,查看你的硬盘名称,这里假设名称是 USB HD 2.打开Applications的Terminal, 你也可以直接spotlight输入terminal打开 3.在终端输入sudo nano /etc/fstab 敲击回车 4.现在你看到了一个编辑界面,输入LABEL=USB\040HD none ntfs rw,auto,nobrowse后,敲击回车,再Control+X,再敲击Y,再敲击回车 (\040表示空格,如果有多个移动设备则多输入多项) 12LABEL=USB\040HD none ntfs rw,auto,nobrowseLABEL=ComtuUseHD none ntfs rw,auto,nobrowse 5.此时,退出你的移动硬盘,再重新插入,你会发现磁盘没有显示再桌面或是Finder之前出现的地方,别慌 6.打开Finder,Command+Shift+G,输入框中输入/Volumes,回车,你就可以看到你的磁盘啦!是可以读写的. 7.方便起见,你可以直接把磁盘拖到Finder侧边栏中,这样下次使用就不用进入到/Volumes目录打开了 (注: 以上操作在使用OS X EI Capitan 版本10.11.6 成功) 内容摘自: 如何将 Mac 里的文件复制到 NTFS 格式的移动硬盘里?Mac下挂载NTFS移动硬盘的可写入方法(更新NTFS分区dirty的修复方法)]]></content>
<categories>
<category>Mac</category>
</categories>
<tags>
<tag>Mac</tag>
<tag>NTFS</tag>
<tag>苹果</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android多渠道打包]]></title>
<url>%2F%2F2016%2F09%2F29%2Fandroid_multi_channel.html</url>
<content type="text"><![CDATA[Android多渠道打包 Gradle方式 Python脚本方式 通过Gradle多渠道打包配置步骤1. 在AndroidManifest.xml 配置占位符123456789<application ...> ... <!-- 渠道商编号 增加占位符--> <meta-data android:name="XXXX_CHANNEL" android:value="${XXXX_CHANNEL_VALUE}"/> ... </application> 2. 在app的build.gradle文件中配置签名12345678910111213141516171819android {... /**签名配置 需要在引用的前面进行配置,不然编译不通过*/ signingConfigs { releasekey { storeFile file('./keystore/release.keystore') //key文件目录 storePassword "testres" //key密码 keyAlias "testres" //Alias名称 keyPassword "testres" //Alias密码 } debugkey { storeFile file('./keystore/debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } }.... } 3. 在app的build.gradle文件中配置build类型1234567891011121314151617181920android{ ... buildTypes { release { buildConfigField "boolean", "CONFIG_LOG_DEBUG", "false" minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.releasekey } debug { buildConfigField "boolean", "CONFIG_LOG_DEBUG", "true" minifyEnabled false signingConfig signingConfigs.debugkey } } ...} 4. 在app的build.gradle文件中配置渠道包名以及输出的文件名12345678910111213141516171819202122232425262728293031323334353637android{ ... /**可定义的产品特性 功能强大*/ productFlavors { //渠道包名 xiaomi { //修改占位符 //manifestPlaceholders = [XXXX_CHANNEL_VALUE: xiaomi] //还有很多功能可以配置 } qh360 {} baidu {} wandoujia {} //... 还需要增加哪些渠道包只需要 xxx{}即可 } /**批量遍历修改AndroidManifest.xml中的XXXX_CHANNEL_VALUE占位符值*/ productFlavors.all { flavor -> flavor.manifestPlaceholders = [XXXX_CHANNEL_VALUE: name] } /**自定义输出文件配置*/ applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { def appId = defaultConfig.applicationId; def vName = defaultConfig.versionName; def channelName = variant.productFlavors[0].name; def buildType = variant.buildType.name; def signed = variant.apkVariantData.signed?"_signed":"_unsigned"; /**输出apk名称*/ def fileName = "${appId}_v${vName}_${channelName}_${buildType}${signed}.apk" output.outputFile = new File(outputFile.parent, fileName) } } } ...} 5. 打包通过AndroidStudio的Terminal或者Gradle环境进行打包 Terminal打包时使用gradle的如下命令即可批量打包 gradle assembleRelease Gradle工具一键打包. 打包出来的的各大渠道包 配置的完整文件AndroidManifest.xml配置12345678910111213141516171819202122232425<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tu.test.multichannel"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- 渠道商编号 增加占位符--> <meta-data android:name="XXXX_CHANNEL" android:value="${XXXX_CHANNEL_VALUE}"/> </application></manifest> app/build.gradle配置123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293apply plugin: 'com.android.application'android { compileSdkVersion 24 buildToolsVersion "24.0.0" /**签名配置 注意这个位置.需要配置在引用的前面,不然编译不通过*/ signingConfigs { releasekey { storeFile file('./keystore/release.keystore') //key文件目录 storePassword "testres" //key密码 keyAlias "testres" //Alias名称 keyPassword "testres" //Alias密码 } debugkey { storeFile file('./keystore/debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } } defaultConfig { applicationId "com.tu.test.multichannel" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { //编译release 发布时的配置环境 release { //自定义属性详情见MainActivity.java的使用 buildConfigField "boolean", "CONFIG_LOG_DEBUG", "false" //开启混淆 minifyEnabled true //跳过无用的资源文件 shrinkResources true //混淆文件配置 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //配置签名 signingConfig signingConfigs.releasekey } //编译debug测试时的环境 debug { buildConfigField "boolean", "CONFIG_LOG_DEBUG", "true" minifyEnabled false signingConfig signingConfigs.debugkey } } /**可定义的产品特性*/ productFlavors { //渠道包名 xiaomi { //manifestPlaceholders = [XXXX_CHANNEL_VALUE: xiaomi] //还有很多功能可以配置 } qh360 {} baidu {} wandoujia {} } /**批量遍历修改AndroidManifest.xml中的XXXX_CHANNEL_VALUE占位符值*/ productFlavors.all { flavor -> flavor.manifestPlaceholders = [XXXX_CHANNEL_VALUE: name] } /**自定义输出文件配置*/ applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { def appId = defaultConfig.applicationId; def vName = defaultConfig.versionName; def channelName = variant.productFlavors[0].name; def buildType = variant.buildType.name; def signed = variant.apkVariantData.signed?"_signed":"_unsigned"; /**输出apk名称*/ def fileName = "${appId}_v${vName}_${channelName}_${buildType}${signed}.apk" output.outputFile = new File(outputFile.parent, fileName) } } }}dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.2.1' testCompile 'junit:junit:4.12'} 注意项build.gradle的配置项是分先后顺序的,如果调用某个配置,必须在调用前进行声明.否则抛无法获取属性. Error:(29, 1) A problem occurred evaluating project ‘:app’. Could not get unknown property ‘releasekey’ for SigningConfig container. 通过Python批量打包 这个方案没法解决不同渠道使用渠道自己SDK的问题,友盟的SDK提供了在代码中设置渠道的方式,所以再获取到渠道号后再调用SDK相关设置渠道的方法就可以了 apk用的是java那一套签名,放在META-INF文件夹里的文件原则上是不参与签名的。如果Google修改了apk的签名规则,这一套可能就不适用了。 1.安装Python环境本人下载的python-2.7.12.msi 下载地址:https://www.python.org/downloads/ 2.在项目中增加获取渠道的方法在项目中增加ChannelUtil.java在Demo文件中可以找到.并通过如下代码获取渠道 String channel = ChannelUtil.getChannel(this);//调用方法 3.为app打签名包.为App打的签名包放到Python脚本文件的同目录. 4.配置需要的渠道channel.txt 里面的就是渠道,一个渠道一行. 5.运行Python脚本双击MultiChannelBuildTool.py文件运行脚本 6.关键实现代码Java代码12345678910111213141516171819202122232425262728293031323334353637private static String getChannelFromApk(Context context, String channelKey) { //从apk包中获取 ApplicationInfo appinfo = context.getApplicationInfo(); String sourceDir = appinfo.sourceDir; //默认放在meta-inf/里, 所以需要再拼接一下 String key = "META-INF/" + channelKey; String ret = ""; ZipFile zipfile = null; try { zipfile = new ZipFile(sourceDir); Enumeration<?> entries = zipfile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = ((ZipEntry) entries.nextElement()); String entryName = entry.getName(); if (entryName.startsWith(key)) { ret = entryName; break; } } } catch (IOException e) { e.printStackTrace(); } finally { if (zipfile != null) { try { zipfile.close(); } catch (IOException e) { e.printStackTrace(); } } } String[] split = ret.split("_"); String channel = ""; if (split != null && split.length >= 2) { channel = ret.substring(split[0].length() + 1); } return channel; } Python代码 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#!/usr/bin/python# coding=utf-8import zipfileimport shutilimport os# 空文件 便于写入此空文件到apk包中作为channel文件src_empty_file = 'info/c.txt'# 创建一个空文件(不存在则创建)f = open(src_empty_file, 'w') f.close()# 获取当前目录中所有的apk源包src_apks = []# python3 : os.listdir()即可,这里使用兼容Python2的os.listdir('.')for file in os.listdir('.'): if os.path.isfile(file): extension = os.path.splitext(file)[1][1:] if extension in 'apk': src_apks.append(file)# 获取渠道列表channel_file = 'info/channel.txt'f = open(channel_file)lines = f.readlines()f.close()for src_apk in src_apks: # file name (with extension) src_apk_file_name = os.path.basename(src_apk) # 分割文件名与后缀 temp_list = os.path.splitext(src_apk_file_name) # name without extension src_apk_name = temp_list[0] # 后缀名,包含. 例如: ".apk " src_apk_extension = temp_list[1] # 创建生成目录,与文件名相关 output_dir = 'output_' + src_apk_name + '/' # 目录不存在则创建 if not os.path.exists(output_dir): os.mkdir(output_dir) # 遍历渠道号并创建对应渠道号的apk文件 for line in lines: # 获取当前渠道号,因为从渠道文件中获得带有\n,所有strip一下 target_channel = line.strip() # 拼接对应渠道号的apk target_apk = output_dir + src_apk_name + "-" + target_channel + src_apk_extension # 拷贝建立新apk shutil.copy(src_apk, target_apk) # zip获取新建立的apk文件 zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED) # 初始化渠道信息 empty_channel_file = "META-INF/channel_{channel}".format(channel = target_channel) # 写入渠道信息 zipped.write(src_empty_file, empty_channel_file) # 关闭zip流 zipped.close() Python脚本详情 Demo下载本文Demo下载]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android</tag>
<tag>多渠道打包</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android混淆]]></title>
<url>%2F%2F2015%2F11%2F01%2Fandroid_proguard.html</url>
<content type="text"><![CDATA[什么是代码混淆Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。 由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法, 这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。 混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译, 即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样, 只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译, 也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。 混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。 理论知识ProGuard 是一款免费的Java类文件压缩器、优化器和混淆器。它能发现并删除无用类、字段(field)、方法和属性值(attribute)。 它也能优化字节码 并删除无用的指令。最后,它使用简单无意义的名字来重命名你的类名、字段名和方法名。 经过以上操作的jar文件会变得更小,并很难进行逆向工程。 基本使用在Android应用程序也可以使用ProGuard来进行混洗打包,大大的优化Apk包的大小。但是注意ProGuard对文件路径的名名很有讲究, 不支持括号,也不支持空格。 在混淆过后,可以在工程目录的 proguard 中的 mapping.txt 看到混淆后的类名,方法名,变量名和混淆前的类名,方法名,变量名。 在使用Eclipse新建一个工程,都会在工程目录下生产配置 project.properties 和 proguard-project.txt . # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. target=android-10 project.properties 用于配置Android工程的一些属性,#号的话表示当前行是注释, 这里的 proguard.config 就用于指定ProGuard的混淆配置文件, 并对使用 release 方式打包应用程序时开启代码混淆功能。 对于是否是使用 release 方式打包,和 AndroidManifest.xml 中 application 的 android:debuggable 属性有很多关系。 如果该值为 android:debuggable="true" ,那么最终就是debug方式打包。 最明智的方式就是在 AndroidManifest.xml 并不显示的指定它,而是是打包工具在打包时来决定它最终的值。 对于 ant 就是 ant release 或 ant debug 。而对于直接在Eclipse中使用run 或debgu来打包的话就是debug,使用export的话就是release. proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 这里的话指定了混淆的基本配置文件 proguard-android.txt ,和混淆的个性化配置文件 proguard-project.txt。 这里 proguard-project.txt 文件用于对前面的基本的混淆配置文件 proguard-android.txt 的配置进行 override 和 添加。 也有些老版本的项目使用的是 在 project.properties 里加上 proguard.config=proguard.cfg 然后编写 proguard.cfg 混淆文件,如果没有新创建即可 编写同目录下的 proguard-project.txt 文件.编写混淆规则即可. 混淆常用的方式: (混淆利器 proguard 的用法)先去官网 http://proguard.sourceforge.net/ 下载一个版本 找到里面的examples文件夹,先看一下示例 其中在 android.pro 文件里应有这么一句 java -jar proguard.jar @android.pro 其中最为关键的就是 android.pro 里的文件配置,只要这个文件配置合适了,执行一下这个语句即可。 打开这个.pro文件,可以看到里面的参数: -injars //表示要进行混淆的class文件或jar、war等,可用文件目录表示,例如: -outjars 表示要生成的jar包,后跟jar包名字,如:-outjars ../out.jar -libraryjars 后面跟要编译in.jar的其它类包,如果是多个,用多行列出,如: -libraryjars d:/1/2/1.jar -libraryjars d:/1/2/2.jar -keep 后跟项目的入口类,如: -keep public class * extends android.app.Activity -keep 后还可以跟在项目中没有用到的类或方法,但在配置文件中有用到, 如果不用该参数保留出来,在做优化时,就会直接的删除掉了,项目运行时会报找不到类的错误。 -printusage 该参数是把优化时移除的类及方法记录下来,后跟一个文件。如:-printusage ./jar/deadCode.txt 其余的常用的参数,如: -target 1.6 //指定版本号 -forceprocessing //强制执行,即使过期 -allowaccessmodification //指定,当执行修改方法或属性的modifer范围 -printmapping //指定混淆后,类或方法生成的map,后跟指定的路径及文件名 *.map -overloadaggressively // -repackageclasses //把执行后的类重新放在某一个目录下,后跟一个目录名 -dontpreverify //不用预先检查 -verbose //不用输出详细的过程 -dontwarn//不用输出警告 -dontnote//不用输出通知 为了增加混淆的难度,可以在 java -jar progurad.jar @application.pro 后再加 -classobfuscationdictionary ./dictionaries/windows.txt 即 java -jar progurad.jar @application.pro -classobfuscationdictionary ./dictionaries/windows.txt 这样生成的类名都是window系统命名系统严令禁止使用的文件名,反编译时就更增加难度了 后面的./dictionaries/windows.txt指的该目录下的windows.txt文件,可根据你自己文件来做相应的指定。 混淆常用模板混淆文件 proguard.cfg 参数详解 -optimizationpasses 5 # 指定代码的压缩级别 -dontusemixedcaseclassnames # 是否使用大小写混合 -dontskipnonpubliclibraryclasses # 是否混淆第三方jar -dontpreverify # 混淆时是否做预校验 -verbose # 混淆时是否记录日志 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法 -renamesourcefileattribute SourceFile #混淆代码保持class文件中的调试信息(源码的行号、源文件信息等) -keepattributes SourceFile,LineNumberTable -keep public class * extends android.app.Activity # 保持哪些类不被混淆 -keep public class * extends android.app.Application # 保持哪些类不被混淆 -keep public class * extends android.app.Service # 保持哪些类不被混淆 -keep public class * extends android.content.BroadcastReceiver # 保持哪些类不被混淆 -keep public class * extends android.content.ContentProvider # 保持哪些类不被混淆 -keep public class * extends android.app.backup.BackupAgentHelper # 保持哪些类不被混淆 -keep public class * extends android.preference.Preference # 保持哪些类不被混淆 -keep public class com.android.vending.licensing.ILicensingService # 保持哪些类不被混淆 -keepclasseswithmembernames class * { # 保持 native 方法不被混淆 native <methods>; } -keepclasseswithmembers class * { # 保持自定义控件类不被混淆 public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int); # 保持自定义控件类不被混淆 } -keepclassmembers class * extends android.app.Activity { # 保持自定义控件类不被混淆 public void *(android.view.View); } -keepclassmembers enum * { # 保持枚举 enum 类不被混淆 public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆 public static final android.os.Parcelable$Creator *; } -keep class MyClass; # 保持自己定义的类不被混淆 混淆编译出现如下错误解决方案[2012-04-17 22:20:48 - projectname] Proguard returned with error code 1. See console [2012-04-17 22:20:48 - projectname] proguard.ParseException: Unknown option 'and' in argument number 9 [2012-04-17 22:20:48 - projectname] at proguard.ConfigurationParser.parse(ConfigurationParser.java:170) [2012-04-17 22:20:48 - projectname] at proguard.ProGuard.main(ProGuard.java:491) [2012-04-17 22:20:59 - projectname] Proguard returned with error code 1. See console [2012-04-17 22:20:59 - projectname] proguard.ParseException: Unknown option 'and' in argument number 9 [2012-04-17 22:20:59 - projectname] at proguard.ConfigurationParser.parse(ConfigurationParser.java:170) [2012-04-17 22:20:59 - projectname] at proguard.ProGuard.main(ProGuard.java:491) 解决办法: ProGuard 是 Android 代码混淆工具,对于程序员保护自己的劳动成果非常有用,目前已经包含在 Android SDK 2.3 里面了。 今天在配置好 ProGuard 使用 Export Android Application 时提示“conversion to Dalvik format failed with error 1”错误。 网络上有人介绍将 SDK 降级到 r11 和修改 proguard.bat 两种方法, 不过最彻底的方法是将 Android SDK 2.3 包含的 ProGuard v4.4 升级到 v4.6,就能彻底解决该问题。 解决方法:从这里下载 ProGuard 最新版,我下载的是v4.6;解压缩 proguard4.6.zip 文件, 将 bin 和 lib 两个文件夹覆盖 [Android SDK 安装目录]\tools\proguard 中的同名文件夹 即可;回到 Eclipse 重新执行 Export Android Application 看看是否成功了。 更新完如果不能够正常使用,请重启Eclipse ,并更新ADT,即可 解码混淆过的堆栈跟踪信息首先获取到堆栈信息,再通过map反映射回原信息. 堆栈信息如何获取方法一:最简单的方法直接copy logcat中的堆栈信息保存为txt 方法二:是在 Application 中添加一个异常监听: 1Thread.setDefaultUncaughtExceptionHandler(CrashHandler.create(this)); 再在 CrashHandler 中将堆栈信息保存为文本文件: 1234567891011121314LogUtils.e(this, "崩溃toString: " + ex.toString());StringWriter stringWriter = new StringWriter();PrintWriter printWriter = new PrintWriter(stringWriter);printWriter.print(TimeUtils.getCurrentTime());try { dumpPhoneInfo(printWriter);} catch (NameNotFoundException e) { e.printStackTrace();}ex.printStackTrace(printWriter);FileUtils.savaData("error.txt", stringWriter.getBuffer().toString());printWriter.println();printWriter.close();ex.printStackTrace();// 也别忘了同时打印日志信息 这样我们就能够获取到堆栈信息,并保存到了本地。 接下来就调用查看的指令. 查看当混淆后的代码输出一个堆栈信息时,方法名是不可识别的,这使得调试变得很困难,甚至是不可能的。 幸运的是,当ProGuard运行时,它都会输出一个 <project_root>/bin/proguard/mapping.txt文件, 而这个文件中包含了原始的类,方法和字段名被映射成的混淆名字。 如果使用的是 Eclipse 进行签名生成签名apk.会在项目编译后项目根目录下多一个proguard目录里面有如下文件: dump.txt mapping.txt seeds.txt usage.txt retrace.bat脚本(Window)或 retrace.sh 脚本(Linux,Mac OS X)可以将一个被混淆过的堆栈跟踪信息还原成一个可读的信息。 它位于<sdk_root>/tools/proguard 文件夹中。执行retrace工具的语法如下: retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>] 例如: retrace.bat -verbose mapping.txt obfuscated_trace.txt 如果你没有指定<stacktrace_file>,retrace工具会从标准输入读取。 当然 你也可以使用工具 在tools/proguard/bin/ 下面 运行proguardGUI.bat 会运行一个GUI页面,操作方式很简单的 Retrace --> Browase --> Obtuscated stack trace 如果出现无法还原错误信息可以尝试下载其它版本的ProGuard进行如上操作 https://sourceforge.net/projects/proguard/files/proguard/]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android</tag>
<tag>proguard</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PHP学习笔记-Laravel框架]]></title>
<url>%2F%2F2015%2F10%2F10%2FPHP_Laravel.html</url>
<content type="text"><![CDATA[本人因公司的需要.学习PHP的Laravel框架.本博文为学习笔记.本博文中包含有 Laravel安装配置环境部署,路由,控制器,视图,模型,MVC ,数据库,请求,Session,表单研制,中间件,哈希,辅助函数,以及图片处理库Integration/Image的使用.artisan常用命令等内容. 目录: 安装Laravel Laravel目录结构 路由 控制器(Controller)) 视图(Views)) 模型(model)) 环境与部署 数据库 请求 Session 表单验证 中间件 哈希 辅助函数 在 Laravel 中使用图片处理库 Integration/Image 我遇到的错误 artisan命令 Demo 安装Laravel1.1.下载安装PHP http://windows.php.net/download/ IDE (https://www.jetbrains.com/phpstorm/) 本人使用的PHP版本为:php-5.5.28-nts Laravel框架要求PHP 版本 >= 5.4 1.2.安装Composer Composer 官网: https://getcomposer.org/ GitHub: https://github.com/composer/composer 中文: http://www.phpcomposer.com/ 中国镜像 Composert官方下载地址: https://getcomposer.org/Composer-Setup.exe 如果出现如下安装错误: Some settings on your machine make Composer unable to work properly. Make sure that you fix the issues listed below and run this script again: The openssl extension is missing, which means that secure HTTPS transfers are impossible. If possible you should enable it or recompile php with --with-openssl 缺失openssl扩展 处理方式: 找到php目录下的php.ini文件(如果没有则把php.ini-development拷贝重命名为php.ini) 用编辑器打开 php.ini 文件,修改以下配置: 去掉 extension=php_mbstring.dll 前面的分号(888 行左右) 去掉 extension=php_openssl.dll 前面的分号(893 行左右) 去掉 extension_dir = "ext" 前面的分号(736 行左右) 然后重试即可.安装完全成后使用cmd输入composer显示如下内容表示安装成功. C:\Users\comtu>composer ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ ....省略..... 1.3.在项目中创建 Composer 使用cmd定位到项目目录下.如: E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm> 使用命令: cmd(或者使用PHPStorm的Terminal本人使用的IDE就是PHPStorm) composer create-project laravel/laravel --prefer-dist my_laravel 回车后会自动下载laravel框架.下载目录生成在firstPHPStorm目录下. laravel/laravel 指下载laravel目录下的laravel --prefer-dist下载的文件为压缩过的. my_laravel 为安装到的文件目录名(自定义). 如果没有代理下载速度就呵呵了.(本人大概花费15分钟左右时间.) 最后提示如下内容表示下载成功: Application key [xxxxxxxxxxxxxxxxxxxx] set successfully. 一键安装包 故有了一键安装包.rar里面的内容就是通过命令下载下来的文件.免去了再龟速的下载. Laravel一键安装包下载地址: http://www.golaravel.com/download/ 1.4.测试运行Laravel 测试运行方式1: cmd(或者使用PHPStorm的Terminal本人使用的IDE就是PHPStorm) php -S localhost:80 -t my_laravel\public 提示出现: PHP 5.5.28 Development Server started at Sat Oct 10 10:20:28 2015 Listening on http://localhost:80 Document root is E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel\public Press Ctrl-C to quit. 打开浏览器: 地址栏输入:localhost 界面中显示出"Laravel 5"大字表示运行成功. 但如果你的是使用linux或者其它系统.如果出现浏览器显示出白屏或者其它内容.则可能是因为有两个文件夹的权限的原因 需要为 my_laravel/storage (日志,session,等目录) 与 my_laravel/vendor (Composer下载的支持库,或者自己的第三方库目录) 有写入的权限 Linux设置对应的权限命令如下: chown -R apache:apache /var/www/my_laravel/storage chown -R apache:apache /var/www/my_laravel/vendor 测试运行方式2(PHP版本5.4之后的版本): artisan 的 serve 命令还支持两个参数: host 设置主机地址 port 设置 web server 监听的端口号 例如:php artisan serve --port=8888 cmd 定位目录到my_laravel 输入 php artisan serve (PHP5.4之后开始支持内置web Server 同时Laravel也支持) E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel>php artisan serve 回车(默认8000接口可指定) Laravel development server started on http://localhost:8000/ 提示 打开浏览器,在地址栏输入 localhost:8000 于方式1运行出相同的效果. 如你电脑上有运行Apache 并且端口也是8000那么会出现如下提示,需要关闭Apache或者指定其它接口后再试. [Sat Oct 10 10:39:21 2015] Failed to listen on localhost:8000 (reason: 以一种访问权限不允许的方式做了一个访问套接字的尝试。) Laravel目录结构本学习笔记使用到的文件目录 my_laravel |-->app | |--->Http | | |---->Controllers 控制器目录 | | | |-----> 控制器文件 | | |---->Middleware | | | |-----> 中间件文件 | | |---->routes.php 路由配置文件 | | |---->Kernel.php 中间件配置文件 | |---> 根目录Model默认目录 |-->bootstrap |-->config 配置文件 | |--> app.php | |--> database.php | |--> .... |-->database 数据库 |-->public 对外开放目录 |-->resources | |--->views 视图View目录 | |---->xxxx.blade.php 视图文件 | |---->errors 目录 | |-----> 503.blade.php 文件当artisan命令使用 php artisan down 时会网站全部连接转向此文件. |-->storage 日志,session等缓存目录 |-->tests |-->vendor 自己的第三方库目录与Composer下载的支持库 | |--->laravel 框架目录 | |--->intervention 图片处理库,需要下载 |-->.env 文件 环境配置文件. |-->Composer.json 环境库配置文件 路由路由作用是宏观的分发和限制请求 http://laravel-china.org/docs/5.0/routing 中文文档 http://laravel.com/docs/5.0/routing 原版文档 http://laravel.com/docs/5.1/routing 原版文档 3.1 配置基本路由 文件目录:my_laravel/app/Http/routes.php 选择get/post/any 等等 一般get是向服务器要数据 post是向服务器发数据 any就是通过全部请求类型 //get Route::get('/','WelcomeController@index'); //意思是如果通过get访问/域名就交给WelcomeController控制器的index方法进行处理. //通过 http://localhost/home 访问.可进入到登录页面 Route::get('home', 'HomeController@index'); 也可以使用函数的方式直接返回视图(可加快测试速度) Route::get('/', function () { return view('welcome'); //视图文件目录位于:my_laravel/resources/views/welcome.blade.php } ); //意思是如果访问/域名 显示视图welcome //post方式 路由使用函数 Route::post('test',function (){ return view('welcome'); }); //any(不管用什么方式get/post等请求都通过),对于不敏感的信息都可以使用这种模式. Route::any('testAny',function (){ return 'any请求,注册路由响应所有 HTTP 请求'; }); //match 为多种请求注册路由 Route::match(['get', 'post'], '/', function() { return 'Hello World'; }); //resource 请求控制器中的所有方法 //http://localhost/user //http://localhost/user/create //http://localhost/user/destroy //http://localhost/user/update //http://localhost/user/show //资源 不用像WelcomeController@index指定方法,直接可以调用. 但在项目中不够灵活. Route::resource('user','UserController'); 3.2 CSRF保护 跨站攻击保护机制(跨网站请求伪造)) 例如:原本一个post请求的链接,用户使用第三方工具(如chrome浏览器的Postman插件)伪造post请求 有CSRF则会对这种请求进行拦截. 3.3 路由传参 基础路由参数 Route::get('user/{id}', function($id){ return 'User '.$id; }); 可选择的路由参数 Route::get('user/{name?}', function($name = null){ return $name; }); 带默认值的路由参数 Route::get('user/{name?}', function($name = 'John'){ return $name; }); 使用正则表达式限制参数 Route::get('user/{name}', function($name){ // }) ->where('name', '[A-Za-z]+'); Route::get('user/{id}', function($id){ // }) ->where('id', '[0-9]+'); 使用条件限制数组 Route::get('user/{id}/{name}', function($id, $name){ // }) ->where(['id' => '[0-9]+', 'name' => '[a-z]+']); 定义全局模式 如果你想让特定路由参数总是遵询特定的正则表达式,可以使用 pattern 方法。在 RouteServiceProvider 的 boot 方法里定义模式: $router->pattern('id', '[0-9]+'); 定义模式之后,会作用在所有使用这个特定参数的路由上: Route::get('user/{id}', function($id) { // 只有 {id} 是数字才被调用。 }); 取得路由参数 如果需要在路由外部取得其参数,使用 input 方法: if ($route->input('id') == 1){ // } 你也可以使用 Illuminate\Http\Request 实体取得路由参数。 当前请求的实例可以通过 Request facade 取得,或透过类型提示 Illuminate\Http\Request 注入依赖: use Illuminate\Http\Request; Route::get('user/{id}', function(Request $request, $id){ if ($request->route('id')){ // } }); 还有如: 方法欺骗 , 命名路由 , 路由群组 , 路由模型绑定 , 抛出 404 错误 等内容 ,见官方文档. 控制器(Controller)控制器的作用是请求二级分发者 控制器目录 my_laravel/app/Http/Controllers/目录下 创建控制器 手动模式创建: 例如新创建: my_laravel/app/Http/Controllers/MyController1.php 内容如下 <?php namespace App\Http\Controllers; class MyController1 extends Controller { public function getAbout(){ return 'MyController1@ABOUT gogogo'; //到视图 //return view('my_welcome');//跳转到my_welcome.blade.php视图View中. //传参数方式1 //跳转到my_welcome.blade.php视图View中并带一个参数$name = 'comtu'. //return view('my_welcome')->with('name','comtu'); //传参数方式2 //跳转到my_welcome.blade.php视图View中并带一个参数$name = 'name'. //$name = 'lala'; //return view('my_welcome',compact('name'/*['name'->'lala']*/); //传参数方式3 //$data=[ // 'name' => 'comtu', // 'age' => 128 //]; //return view('my_welcome',$data);//传多参数.视图View中直接使用$name , $age即可. } } 配置路由 //http://localhost/myController1 Route::get('myController1','MyController1@getAbout'); 命令模型创建(推荐,更准确): 使用Terminal定位(cmd)目录文件到 my_laravel 目录下 使用artisan命令 使用命令: E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel> php artisan make:controller MyController 提示:Controller created successfully. 创建成功. 查看my_laravel/app/Http/Controllers/MyController.php 发现会生成生成MyController.php文件 文件里会自动生成类,以及一些方法: 如:index() , create() , store() , show($id) , edit($id) , update($id) , destroy($id) 但都是空实现. 视图(Views)MVC显示层 my_laravel/resources/views/xxxx.blade.php 创建View视图 一般流程为: 创建控制器-->配置路由-->创建视图. 1.在创建的控制器 MyController.php 中增加如下方法: <?php namespace App\Http\Controllers; class MyController1 extends Controller { public function index(){ //到视图 return view('my_welcome');//跳转到my_welcome.blade.php视图View中. } //传参数方式1 public function parameter(){ $data=[ 'name' => 'comtu', 'age' => 128, 'people'=>['张三','李四','王五'] ]; return view('my_welcome',$data);//传多参数.视图View中直接使用$name , $age即可. } //传参数方式2 public function parameter1(){ $name = 'lala'; //跳转到my_welcome.blade.php视图View中并带一个参数$name = 'lala'. return view('my_welcome',compact('name')/*等同于['name'->'lala']*/); } //传参数方式3 public function parameter2(){ //跳转到my_welcome.blade.php视图View中并带一个参数$name = 'comtu'. return view('my_welcome')->with('name','comtu'); } } 2.配置路由: //http://localhost/my_home Route::get('my_home','MyController@index'); Route::get('my_home1','MyController@parameter'); Route::get('my_home2','MyController@parameter1'); Route::get('my_home3','MyController@parameter2'); 3.创建视图文件 目录结构为my_laravel/resources/views/my_welcome.blade.php <html> <head> <title>my_welcome</title> </head> <body> <div class="title">my_welcome--> <?php if(!empty($name)):?> <?php echo $name;?> <?php endif;?> </div> <?php if(!empty($age)):?> <div class="title">age:<?php echo $age;?></div> <?php endif;?> </body> </html> Blade模板 官方文档: http://laravel.com/docs/5.1/blade 中文文档: http://www.golaravel.com/laravel/docs/5.0/templates/ 模板: {{ }} {!! !!} {{{ }}} @include @if() @else @endif @foreach @endforeach @for @endfor @while @endwhile 等 案例: 新创建 my_laravel/resources/views/compontents/myHead.blade.php 文件.(用于演示 @include('components.myHead')) <html> <body> <!--头部文件使用 include 在其它地方引用 --> <h1>高大上的头Head</h1> <hr> </body> </html> 演示代码 my_laravel/resources/views/my_view.blade.php <html> <head> <meta charset="utf-8"> <title>MyView</title> </head> <body> <!--引用包含其它文件.--> @include('components.myHead') MyView--> <!--调用函数 跨站点请求伪造保护码--> {{csrf_token()}} 等同于 <?php echo csrf_token();?> <h1>{{ $name or 'Default' }}</h1> <h1>{{ $name or '无名英雄1' }}</h1> <h1>{{ $name or '无名英雄2'}} - {{$age or 0}}</h1> 以上等同于 <h1><?php echo !empty($name)?$name:'无名英雄3'?></h1> <!--不解析,原样输出--> @{{$name}} <!--加载代码--> {!! '<script>var g = "comtu";document.write("<br />加载代码"+g+"<br />");</script>' !!} {!! '<h1>h1</h1>' !!} <!--代码转义--> {{{'打印HTML comtu'}}} <!--Blade的if判断,如果$name未定义会抛错误--> <h1> @if($name) 你好{{$name}} @else 未登录 @endif </h1> @foreach($people as $person) <li>{{$person}}</li> @endforeach @for($i = 0 ; $i<3; $i++) xx @endfor @while(false) xxx @endwhile </body> </html> 以上在浏览器中输出 高大上的头Head MyView--> YNJhQ4GgCCuZ5qyPNhTwvFvWfYLduxUOFvEz0vnZ 等同于 YNJhQ4GgCCuZ5qyPNhTwvFvWfYLduxUOFvEz0vnZ Default 无名英雄1 无名英雄2 - 0 以上等同于 无名英雄3 {{$name}} 加载代码comtu h1 <html lang="en">打印HTML comtu</html> 未登录 张三 李四 王五 xx xx xx 模型(model)模型层 一般model在 my_laravel/app/目录下 直接或间接继承Model; 原代码目录: //my_laravel/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php Laravel的Model比CI的Model内容可多很多.里面包含了查询数据库等等的函数实现. 8.Eloguent里面有Demo 创建模型 手动模式创建: 例如新创建: my_laravel/app/Demo.php内容如下: 修改Model的文件目录,新创建 my_laravel/app/Model 在Model里面创建Model文件. 并在 文件头<?php namespace App\Model;//指定目录即可 <?php namespace App; use Illuminate\Database\Eloquent\Model; class Demo extends Model { use Authenticatable, CanResetPassword; /**使用的数据库表模式。*/ protected $table = 'demo'; /**属性可分配。*/ //表示那些字段是可以被laravel填写的. protected $fillable = ['name', 'email', 'password']; //表示这些字段是被保护的,存储数据库时不填写此数据 protected $guarded = ['demo_id']; /** 排除在模型的JSON形式的属性。*/ protected $hidden = ['password', 'remember_token']; //修改默认的 id 主键 protected $primaryKey = 'demo_id'; //不使用时间戳_见下面创建模型 public $timestamps = false; } 命令模型创建: 使用Terminal定位(cmd)目录文件到 my_laravel 目录下 使用artisan命令 php artisan make:model Demo 创建在 my_laravel/app/Demo.php 文件如下: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Demo extends Model { //.... } 环境与部署.env 环境配置文件. 文件目录 my_laravel/.env 作用到全局的一些参数 例如: APP_ENV=local 全称APP_Envirment 程序环境=local表示环境在本地.server表示运行在服务器.debug需要改成0或false APP_DEBUG=true 是否在开发模式下.如果true,则是开发模式,出错时会报错,并会尽量多打印信息.0或false表示正式模式. APP_KEY=asdf 应用的加密key,标记唯一性,例如框架需要加密时,有可能会使用到,当做参数使用. DB_HOST=localhost 数据库地址 这些参数会被 database.php 等配置文件调用. DB_DATABASE=my_laravel 数据库名 DB_USERNAME=root 数据库用户名 DB_PASSWORD=root 数据库密码 CACHE_DRIVER=file 缓存使用文件形式 SESSION_DRIVER=file Session使用文件形式 QUEUE_DRIVER=sync 队列使用同步 MAIL_DRIVER=smtp 邮件配置 MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null 先创建数据库 见下面的"数据库CMD" //路由中,尝试连接数据库 Route::get('myDatabase',function(){ $name = DB::connection()->getDatabaseName(); echo $name; //打印出 数据库名 my_laravel 表示成功 如果遇见PDOException错误见"我遇到的错误"中的解决方法 }); 数据库 my_laravel/config/database.php 一般不用配置当前文件,配置 my_laravel/.env 文件即可. 'default' => 'mysql' 默认使用的数据库 'connections' => [ 'mysql' => [ 'driver' => 'mysql', //引用my_laravel/.env 环境配置文件的配置,如果没有找到则使用默认的'localhost'. 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', //前缀 'strict' => false, ], ] Session 配置文件 my_laravel/config/session.php 一般此文件不需要配置 //可支持配置 "file", "cookie", "database", "apc","memcached", "redis", "array" 'driver' => env('SESSION_DRIVER', 'file'), down/up 一整个程序停止解析(使用场景如,网站遇到攻击,网站需要维护等) 配置后访问网站的所有地址都会被跳转到 my_laravel/resources/views/errors/503.blade.php 网站全部连接转向此文件. //停止服务, php artisan down //恢复服务 pup artisan up 自定义配置属性 1.在.env中增加属性 MY_DIY_CONFIG='test_env' 2.在config/app.php内容里增加属性(参数1为.env文件下的配置名,如果未配置.则使用参数2默认值) 'my_diy_config' =>env('MY_DIY_CONFIG','test_app'), 3.使用 例如在Controllers中使用: use Illuminate\Support\Facades\Config;//引入包 //code... $diyConfig = Config::get('app.my_diy_config');//使用 //code... 自动加载类 使用场景:当某一个方法函数经常会被多个不同的类调用时.可做成自动加载的类,减少代码编写量 1.创建文件 例如:在app/Libraries/function/functions.php (自定义)里面有如下代码: <?php funcion abc(){ return 'abc'; } 2.在工程 /bootstrap/autoload.php 中增加如下代码(对应自定义的文件目录): require __DIR__.'/../app/Libraries/function/functions.php'; 3.使用 例如在Controllers中使用 直接使用方法名调用: $data = abc(); 数据库与数据库交互的机制. http://www.golaravel.com/laravel/docs/5.0/eloquent/ 目录my_laravel/vendor/laravel/framework/src/Illuminate/Database/Eloquent/包含多文件,Model属于此包 Eloguent 使用(增删改查): 配置数据库信息 my_laravel/.env DB_HOST=localhost DB_DATABASE=my_laravel DB_USERNAME=root DB_PASSWORD=root 配置路由: //http://localhost:8888/myDatabase 查看连接数据库是否成功 Route::get('myDatabase',function(){ $name = DB::connection()->getDatabaseName(); echo $name; }); //http://localhost:8888/myDatabase/add 增 Route::get('myDatabase/add',function(){ $user = new App\User(); $user->userAdd(); return $user->userRead(); }); //http://localhost:8888/myDatabase/delete 删 Route::get('myDatabase/delete',function(){ $user = new App\User(); $user->userDelete(); return $user->userRead(); }); //http://localhost:8888/myDatabase/update 改 Route::get('myDatabase/update',function(){ $user = new App\User(); $user->userDelete(); return $user->userRead(); }); //http://localhost:8888/myDatabase/read 查 Route::get('myDatabase/read',function(){ $user = new App\User(); return $user->userRead(); }); //http://localhost:8888/myDatabase/read_dd 查 Route::get('myDatabase/read_dd',function(){ $user = new App\User(); $users = $user->all(); dd($users); // dd=> var_dump() ; and die(); 打印出数据类型与信息. // $users->toArray();//转成数组 // $users->all();//返回数据库原型 , 默认调用了tojson(); // return $user->userRead(); //数组 $arr = ['one','two','three']; $collection = collect($arr); //查找数组是否包含对应的值 $bool = $collection->contains('one'); //取数组的前2个数据 $res = $collection->take(2); //取后面两个数据 $res = $collection->take(-2); //键值对 $arr = ['one'=>1,'two'=>2,'three'=>3]; $collection = collect($arr); //查找是否有对应的键key $bool = $collection->has('one'); return $collection->all(); }); 创建模型: <?php namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; //Laravel的Model已经实现了一些对数据库的操作,如增删改查. class User extends Model implements AuthenticatableContract, CanResetPasswordContract { use Authenticatable, CanResetPassword; //数据库名 重写Model的数据库名 protected $table = 'users'; //表示那些数据库字段是可以被laravel填写. protected $fillable = ['username', 'age']; //表示这些字段是被保护的,存储数据库时不填写此数据(主键自动增长) protected $guarded = ['demo_id']; // 排除在模型的JSON形式的属性。如调用$this->all()生成的Json不生成在Json中的字段名 protected $hidden = []; //修改默认的 id 主键名称 protected $primaryKey = 'user_id'; ////model自带的方法 public static function all($columns = array('*')) 函数 //详情见 my_laravel/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php //查询数据《----------------------- public function userRead(){ //查询出所有数据 //return $this->all(); //查找user_id为1的数据 如果没有打到数据,返回的空数据 //return $this->find(1); //如没有找到数据直接抛异常.查id为4的数据 //return $this->findOrFail(4); //返回对应条件的数据 select * from users where username='comtu'; //return $this->where('username','comtu')->get(); //返回对应条件的数据 select * from users where user_id>1; // return $this->where('user_id','>',1)->get(); //查询出所有数据 等同于 $this->all(); return $this->get(); } //insert数据时model基类默认数据库中有时间戳字段,更新时间,创建时间 //处理方式有两种,一种是为数据库中增加这两个字段`updated_at`, `created_at` //另一种是取消Model中的数据库时间戳字段如下: public $timestamps = false; //增加数据《----------------------- public function userAdd(){ //增加数据方式1 //$this->username = 'user00000'; //$this->age = 1; //$this->save(); //增加数据后如果表是自动增长的可获取到增加后的id $id = $this->user_id;//新增加后的id值 //增加数据方式2 批量赋值 //protected $fillable = ['username', 'age']; //需要有配置自动填充的字段 $user_data = ['username'=>'user111','age'=>256]; $this->fill($user_data); $this->save(); } //更新数据《----------------------- public function userUpdate(){ //修改对应的数据 方式一 //$user = $this->find(1);//查询-》保存 //$user->username = 'comtu0000'; //$user->age = '512'; //$user->save(); //修改对应的数据 方式二 $users = $this->where('user_id','<','3'); //查询-》更新 //更新不需要调用save() $users->update(['username'=>'comtu1111','age'=>1024]); } //删除数据《----------------------- public function userDelete(){ $user = $this->find(3);//查询-》删除 $user->dalete(); } } Demo: 模型: <?php namespace App\Model; use Illuminate\Database\Eloquent\Model; class Person extends Model { //数据库表 protected $table = 'person'; //修改默认的 id 主键 protected $primaryKey = 'pid'; //表示那些字段是可以被laravel填写的.fill() protected $fillable = ['name', 'sex','age','salary','occ_id','dep_id']; //表示这些字段是被保护的,存储数据库时不填写此数据 protected $guarded = ['pid']; //生成JSON时隐藏的字段 protected $hidden = []; //insert数据时model基类默认数据库中有时间戳字段,更新时间,创建时间 //处理方式有两种,一种是为数据库中增加这两个字段`updated_at`, `created_at` //另一种是取消Model中的数据库时间戳字段如下: public $timestamps = false; //表结构对应关系 occupation另外一张表app/model/occupation.php public function occupation(){ return $this->hasOne('App\Model\occupation', 'occ_id', 'occ_id'); } //表结构对应关系 public function department(){ return $this->hasOne('App\Model\department', 'dep_id', 'dep_id'); } } 控制器: app/Http/Controllers/DemoDataBaseController.php <?php namespace App\Http\Controllers; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Model\Occupation; use App\Model\Department; use App\Model\Person; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Session; class DemoDataBaseController extends Controller{ public function index() { //$data['person'] = Person::find(array('1'));//简单条件查询 select * from person where id = 1; //$data['person'] = Person::whereRaw('pid > 1 and salary > 1700')->get();//条件查询全部 //连表查询~~需要在Person里面配置表的关系 $data['person'] = Person::with('occupation', 'department')->paginate(10);//连表查询并 paginate分页功能,每页10条. //$data['person'] = Person::with('occupation','department')->get();//连表查询 //只能使用get,all获取不到数据 //<?php foreach ($person as $v):?>//视图中获取连表的数据 //<?php echo $v['occupation']->name;?> //<?php endforeach;?> $data['count'] = Person::all()->count(); $data['occupation'] = Occupation::get();//查询全部 $data['department'] = Department::all();//查询全部 return view('demo.demo_index_database',$data); } public function addPerson(){ $data['occupation'] = Occupation::get();//查询全部 $data['department'] = Department::all();//查询全部 return view('demo.demo_add_update_database',$data); } public function postAddPerson(){ $person = new Person(); //增加数据方式2 $person->name = Request::input('name', 'default');//获取Post请求的数据 $person->age = Request::input('age', 20); $person->sex = Request::input('sex', 0); $person->salary = Request::input('salary', 1800); $person->occ_id = Request::input('occ_id'); $person->dep_id = Request::input('dep_id'); $person->save(); //$person->pid; 可获取到增加后的自动增长id. return redirect('myDatabase/Demo'); } public function deletePerson($rid,$_token = ''){ if(Session::token() !== $_token) { echo '非法请求'; return; } $person = Person::find($rid); $person->delete(); return redirect('myDatabase/Demo'); } //http://localhost/myDatabase/DemoUpdatePerson?pid=6&_token=7jcZiP2wv0nIpxrA4KUqn9sBRaU7YXpcc8M0zrue public function updatePerson(){ $_token = Input::get('_token');//获取get请求的数据 if(Session::token()!=$_token){ echo '非法请求'; return ; } $pid = Input::get('pid'); $data['person'] = Person::find($pid); $data['occupation'] = Occupation::all();//查询全部 $data['department'] = Department::get();//查询全部 return view('demo.demo_add_update_database',$data); } public function postUpdatePerson(){ $person = Person::find(Request::input('pid')); if(!$person){ echo '修改错误'; return; } //修改数据方式 $person->name = Request::input('name'); $person->age = Request::input('age'); $person->sex = Request::input('sex'); $person->salary = Request::input('salary'); $person->occ_id = Request::input('occ_id'); $person->dep_id = Request::input('dep_id'); $person->save(); return redirect('myDatabase/Demo'); } } 路由: app/Http/routes.php //显示列表 http://localhost/myDatabase/Demo Route::get('myDatabase/Demo','DemoDataBaseController@index'); //增加数据 Route::get('myDatabase/DemoAddPerson','DemoDataBaseController@addPerson'); Route::post('myDatabase/DemoAddPerson','DemoDataBaseController@postAddPerson'); //删除数据 使用函数参数方式并参数使用正则匹配. Route::get('myDatabase/DemoDeletePerson/{pid}/{_token}','DemoDataBaseController@deletePerson')->where(['pid' => '[0-9]+']); //更新数据 此处使用获取get请求的方式获取数据 Route::get('myDatabase/DemoUpdatePerson','DemoDataBaseController@updatePerson'); Route::post('myDatabase/DemoUpdatePerson','DemoDataBaseController@postUpdatePerson'); //scrf验证 Route::filter('csrf',function(){ if (Session::token() !== Input::get('_token')) { throw new Illuminate\Session\TokenMismatchException; } }); 视图: resources/views/demo/demo_index_database.blade.php 显示列表 <html> <head> <meta charset="utf-8"> <title>MyView</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> @include('components.myHead') <h1>resources/views/demo_database.blade.php</h1> 调用函数@{{csrf_token}} 跨站点请求伪造保护码 {{csrf_token()}} 等同于 <?php echo csrf_token();?> <table border="1px" cellpadding="2px" cellspacing="1px" bgcolor="#FFFFF0"> <tr align="center"> <td>pid(count:{{$count}})</td> <td>name</td> <td>age</td> <td>sex</td> <td>salary</td> <td>occ_id</td> <td>dep_id</td> <td>操作</td> </tr> @foreach($person as $p) <tr align="center"> <td>{{$p->pid}}</td> <td>{{$p->name}}</td> <td><?php echo $p->age?></td> <td>{{$p->sex==0?'男':'女'}}</td> <td>{{$p->salary}}</td> <td>{{$p->occupation->name}}</td><!--连表查询,预载入的关联对象--> <td>{{$p->department->name}}</td> <td><a href="{{url('myDatabase/DemoUpdatePerson?pid='.$p->pid.'&_token='.csrf_token())}}">修改</a> &nbsp <a href="{{url('myDatabase/DemoDeletePerson').'/'.$p->pid .'/'.csrf_token()}}">删除</a></td> </tr> @endforeach @if($person->render()) <tr ><td colspan="9" align="center" width="100%"><?php echo $person->render(); ?><!--分页--></td></tr> @endif </table> <div style="clear: both"></div> <a href="{{url('myDatabase/DemoAddPerson')}}">增加</a> <hr> </body> </html> resources/views/demo/demo_add_update_database.blade.php 增加/修改页 <html> <head> <meta charset="utf-8"> <title>{{empty($person)?'增加':'更新'}}数据</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> <form action="{{empty($person)?url('myDatabase/DemoAddPerson'):url('myDatabase/DemoUpdatePerson')}}" method="post" > <input type="hidden" name="_token" value="{{csrf_token()}}"> <?php echo empty($person)?'':'<input type="hidden" name="pid" value="'.$person->pid.'">' ;?> <table border="1px" cellpadding="2px" cellspacing="1px" bgcolor="#FFFFF0"> <tr><td>名称</td><td><input type="text" name="name" value="{{empty($person)?'':$person->name}}"/></td></tr> <tr><td>年龄</td><td> <select name="age" > @for ($i = 1; $i <= 110; $i++) <option value="{{ $i }}" <?php echo $i==(empty($person)?24:$person->age) ?'selected="selected"':''?>>{{ $i }}</option> @endfor </select> </td></tr> <tr><td>性别</td><td> <select name="sex"> <option value="0" <?php echo empty($person)?'selected="selected"':$person->sex==0?'selected="selected"':''?>>男</option> <option value="1" <?php echo empty($person)?'':$person->sex==1?'selected="selected"':''?>>女</option> </select> </td></tr> <tr><td>薪资</td><td><input type="text" name="salary" value="{{empty($person)?'':$person->salary}}" /></td></tr> <tr><td>职业</td><td> <select name="occ_id"> @for($i = 0 ; $i< count($occupation);$i++) <option value="{{$occupation[$i]->occ_id}}" <?php echo empty($person)?'':$occupation[$i]->occ_id==($person->occ_id)?'selected="selected"':''?>>{{$occupation[$i]->name}}</option> @endfor {{--@foreach($occupation as $occ)--}} {{--dep_id}}" >{{$dep->name}}</option>--}} {{--@endforeach--}} </select> </td></tr> <tr><td colspan="2" align="center"><input type="submit" value="{{empty($person)?'增加':'更新'}}">&nbsp<input type="reset" value="重置"></td></tr> </table> </form> </body> </html> 请求Demo: 控制器: app/Http/Controlers/DemoMyRequestController.php <?php namespace App\Http\Controllers; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Session; class DemoMyRequestController extends Controller{ //请求 public function myrequest(){ //获取表单发送过来的所有数据,不管是get,post还是其它请求的数据. $input = Request::all();//返回json数据 var_dump($input); echo '<br>'; $value = Request::query();//获取get请求?后面的所有参数 var_dump($value); echo '<br>'; $value = Request::query('name');//获取get请求后面的指定参数 var_dump($value); echo '<br>'; //get请求方式,如果没有name键则取默认值comtu $value = Request::get('name','comtu'); var_dump($value); echo '<br>'; //-----判断------- //判断参数是否存在返回布尔值 $bool = Request::has('name'); var_dump($bool); echo '<br>'; //http://localhost/myrequest?name=&age=11 //检查是否存在键,有则返回true,不检查键对应的值是否存在. $bool = Request::exists('name'); var_dump($bool); echo '<br>'; //-------请求检索------- //过滤值,只取对应的键值 $value = Request::only('name'); var_dump($value); echo '<br>'; $value = Request::only('name','age');//如果有多个参数,则只取这两个值 var_dump($value); echo '<br>'; //过滤值,除了对应的键不取,其它数据获取,用法与only类似 $value = Request::except('name'); var_dump($value); echo '<br>'; //-----------url--------- //返回请求的url前缀_不包括参数 $url = Request::url(); var_dump($url); echo '<br>'; //返回请求的全部url_包含参数 $url = Request::fullUrl(); var_dump($url); echo '<br>'; //----------请求历史---------- //将请求的所有数据存储到session数据中. Request::flash(); //Request::flashOnly('name');//只存储指定的键值 //Request::flashExcept('name');//除了指定的键值,其它都存储 //返回上一次请求的数据,可在不同页面中获取,前提是需要调用了Request::flash() $value = Request::old();//可用于保存用户提交失败时恢复历史数据的功能 var_dump($value); /* 请求: http://localhost/myrequest?name=comtu&age=11 array(2) { ["name"]=> string(5) "comtu" ["age"]=> string(2) "11" } array(2) { ["name"]=> string(5) "comtu" ["age"]=> string(2) "11" } string(5) "comtu" string(5) "comtu" bool(true) bool(true) array(1) { ["name"]=> string(5) "comtu" } array(2) { ["name"]=> string(5) "comtu" ["age"]=> string(2) "11" } array(1) { ["age"]=> string(2) "11" } string(26) "http://localhost/myrequest" string(44) "http://localhost/myrequest?age=11&name=comtu" array(2) { ["name"]=> string(5) "comtu" ["age"]=> string(2) "11" } */ /* 请求http://localhost/myrequest?nae=comtu&age=11 array(2) { ["nae"]=> string(5) "comtu" ["age"]=> string(2) "11" } array(2) { ["nae"]=> string(5) "comtu" ["age"]=> string(2) "11" } NULL string(5) "comtu" bool(false) bool(false) array(1) { ["name"]=> NULL } array(2) { ["name"]=> NULL ["age"]=> string(2) "11" } array(2) { ["nae"]=> string(5) "comtu" ["age"]=> string(2) "11" } string(26) "http://localhost/myrequest" string(43) "http://localhost/myrequest?age=11&nae=comtu" array(2) { ["name"]=> string(5) "comtu" ["age"]=> string(2) "11" } */ } //显示上传页面 public function uploadFile(){ return view('demo.demo_index_MyRequest'); } //提交上传数据 public function postUploadFile(){ //返回post上来的数据 返回数组 var_dump(Request::file()); echo '<br>'; echo '<br>'; //返回对象 var_dump(Request::file('my_file')); echo '<br>'; echo '<br>'; //返回布尔值,是否包含文件 $bool = Request::hasFile('my_file'); var_dump($bool); echo '<br>'; echo '<br>'; //返回文件大小 $size = Request::file('my_file')->getClientSize(); var_dump($size); echo '<br>'; echo '<br>'; //返回上传时的文件名 $name = Request::file('my_file')->getClientOriginalName(); var_dump($name); echo '<br>'; echo '<br>'; //返回文件格式 $extension = Request::file('my_file')->getClientOriginalExtension(); var_dump($extension); echo '<br>'; echo '<br>'; //判断上传的文件是否有效 if (Request::file('my_file')->isValid()){ //移动上传的文件到指定目录 人在public目录下自动创建uploads目录并把文件存储到当前目录中. $destinationPath = 'uploads/'; Request::file('my_file')->move($destinationPath, uniqid().'.'.$extension); //md5 uniqid 唯一标识 } } } 路由: app/Http/routes.php //http://localhost/myrequest //http://localhost/myrequest?name=&age=11 //http://localhost/myrequest?name=comtu&age=11 //http://localhost/myrequest?na2me=comtu&age=11 Route::any('myrequest','DemoMyRequestController@myrequest'); //文件上传 Route::any('uploadFile','DemoMyRequestController@uploadFile'); Route::post('uploadFile','DemoMyRequestController@postUploadFile'); 视图: resources/views/demo/demo_index_MyRequest.blade.php <html> <head> <meta charset="utf-8"> <title>MyView</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> {{----}} <form action="uploadFile" method="post" enctype="multipart/form-data"> <input type="hidden" name="_token" value="{{csrf_token()}}"> <fieldset style="width:230px" > <legend>上传文件</legend> 选择文件:<input type="file" name="my_file" multiple /> <br /> {{--选择文件: --}} <input type="submit" value="提交" /> </fieldset> </form> </body> </html> Session方式一:使用文件.(默认) .env文件中配置Session存储方式 CACHE_DRIVER=file SESSION_DRIVER=file QUEUE_DRIVER=sync 方式二:使用数据库 .env文件中配置Session存储方式 CACHE_DRIVER=file SESSION_DRIVER=database QUEUE_DRIVER=sync 使用PHPStorm自带的lerminal工具或者计算机命令行cmd定位到项目目录下. 使用命令生成数据库表: php artisan session:table E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel>php artisan session:table 当输出 Migration created successfully!时 可查看到database/migrations目录下会新创建一个PHP文件类名为CreateSessionTable 再输入 composer dump-autoload 当输出 Generating autoload files 表示成功. 再输入 php artisan migrate 当输出如下表示成功 Migrated: 2015_11_02_092311_create_session_table 查看数据库表多了一个sessions表 里面有id , payload , last_activity 三个字段 注: 执行php artisan migrate时有异常可查看"我遇到的错误解决问题" 生成表之后就可以使用Session了`~增删改查都无需要人工控制. 控制器 app/Http/Controlers/DemoSessionController.php <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Session; class DemoSessionController extends Controller{ public function session(){ //查看全部Session数据 var_dump(Session::all()); /* 起初Session就有默认值 _previous : 上一次请求的地址 flash: 保存的数据 old:历史数据 new:新数据 例: array:3 [▼ "_token" => "7jcZiP2wv0nIpxrA4KUqn9sBRaU7YXpcc8M0zrue" "_previous" => array:1 [▼ "url" => "http://localhost/uploadFile" ] "flash" => array:2 [▼ "old" => [] "new" => [] ] ] */ echo '<br>'; echo '<br>'; //增加/修改Session _ 方式一 Session::put('username','comtu'); //增加Session _ 方式二 session(['username'=>'comtu1']); //获取值_不删除 $username = Session::get('username'); //获取值_后删除_只用一次 $username = Session::pull('username'); var_dump($username); echo '<br>'; echo '<br>'; //判断是否存在 $bool = Session::has('username'); var_dump($bool); //删除Session值 $value = Session::forget('username'); var_dump($value); } } 路由 app/Http/routes.php Route::any('session','DemoSessionController@session'); 表单验证需要引入use Request,Validator; 包 $validator = Validator::make(数据,规则,自定义错误信息); $validator->fails();验证表单 $validator->errors();//获取错误信息 例:修改8.数据库中的Demo public function postAddPerson(){ //存储提交数据到Session Request::flash(); //见9.请求 //必填 required //长度 between:最小,最大 //最小长度 min:4 //最大长度 max:16 //数字 numeric //唯一性 unique:表名 会自动查询表对应字段是否存在有当前值 //检查值是否在table表中column字段中存在 exists:table,column 自动查询table表column字段是否包含值 //更多配置 http://laravel-china.org/docs/5.0/validation //表单验证请求 $rq = Request::all(); $rules = [//规则 'name' => 'required|between:4,16|unique:person' , 'age' => 'required|numeric', 'sex' => 'required|numeric', 'salary' => 'required|numeric', 'occ_id' => 'required|numeric|exists:occupation,occ_id', 'dep_id' => 'required|numeric|exists:department,dep_id', ]; $messages = [//自定义错误信息 'required' => 'The :attribute field is required.', 'between' => 'The :attribute must be between :min - :max.' ]; $validator = Validator::make($rq,$rules,$messages);//可三个参数 //$validator = Validator::make($rq,$rules);//可两个参数 if($validator->fails()){//表单验证 $person = new Person();//重新封装历史数据 $person->name = Request::old()['name'];//表单验证失败,返回用户之前输入的信息 $person->age = Request::old()['age']; $person->sex = Request::old()['sex']; $person->salary = Request::old()['salary']; $person->occ_id = Request::old()['occ_id']; $person->dep_id = Request::old()['dep_id']; $data ['person'] = $person; $data['occupation'] = Occupation::get(); $data['department'] = Department::all(); //错误信息 Illuminate\Support\MessageBag 对象类型 head($validator->get('key'))获取键值 $data['validator'] = $validator->errors(); return view('demo.demo_add_update_database',$data); } $person = new Person(); //增加数据方式2 $person->name = Request::input('name', 'default');//获取Post请求的数据 $person->age = Request::input('age', 20); $person->sex = Request::input('sex', 0); $person->salary = Request::input('salary', 1800); $person->occ_id = Request::input('occ_id'); $person->dep_id = Request::input('dep_id'); $person->save(); return redirect('myDatabase/Demo'); } 中间件HTTP 中间件提供一个方便的机制来过滤进入应用程序的 HTTP 请求, 例如,Laravel 默认包含了一个中间件来检验用户身份验证,如果用户没有经过身份验证, 中间件会将用户导向登录页面,然而,如果用户通过身份验证,中间件将会允许这个请求进一步继续前进。 当然,除了身份验证之外,中间件也可以被用来执行各式各样的任务,CORS 中间件负责替所 有即将离开程序的响应加入适当的响应头,一个日志中间件可以记录所有传入应用程序的请求。 Laravel 框架已经内置一些中间件,包括维护、身份验证、CSRF 保护,等等。 所有的中间件都位于 app/Http/Middleware 目录内。 使用步骤 1.创建中间件 使用命令: php artisan make:middleware AdminPrivilegeMiddleware 在app/Http/Middleware文件夹中创建中间件文件 如:AdminPrivilegeMiddleware.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Session; class AdminPrivilegeMiddleware { /** * Handle an incoming request. * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { //权限认证 $bool = Session::has('admin_user'); if(!$bool) return redirect('admin/privilege/login'); return $next($request); } } 2.注册中间件 在app/Http目录下的Kernel.php增加如下内容 protected $routeMiddleware = [ //注册中间件 'auth.admin' => \App\Http\Middleware\AdminPrivilegeMiddleware::class, ]; 3.使用中间件 在app/Http目录下的routes.php使用: //中间件,权限控制 Route::group(['middleware' => 'auth.admin'], function() { Route::get('admin/main/index','admin\MainController@index'); Route::get('admin/categroy/index','admin\CategroyController@index'); } 哈希可用于加密用户的敏感信息或者密码 需要导入 use Illuminate\Support\Facades\Hash; 包 使用方法: $password = '123'; $pass = Hash::make($password);//加密 $password2 = '123'; $bool = Hash:check($pass,$password2);检查是否相同 辅助函数array 数组相关 head $arr = [1,2,3]; $value = head($arr);//打印1 返回数组第一个元素 last $arr = [1,2,3]; $value = last($arr);//打印3 返回数组第一个元素 array_only $arr = ['name'=>'comtu','age'=>10,'job'=>'manager']; $value = array_only($arr,['name','age']);//返回数组中指定的数据 //打印 array(2) { ["name"]=> string(5) "comtu" ["age"]=> int(10) } array_first //返回满足条件的第一条数据 $arr = [10,20,30]; $value = array_first($arr,function($key,$v){ return $v >10; }); //打印 20 array_add $arr = ['name'=>'comtu','age'=>10]; $value = array_add($arr,'job','manager');//给数组增加字段 // 返回 ['name'=>'comtu','age'=>10,'job'=>'manager'] array_set //修改数组中对应的值 $arr = ['start_time'=>'2009-06-01','end_time'=>'2014-11-30']; array_set($rq,'start_time',strtotime(Request::input('start_time'))); //strtotime时间转时间戳 //返回 ['start_time'=>1243814400,'end_time'=>'2014-11-30'];; array_except $arr = ['name'=>'comtu','age'=>10,'job'=>'manager']; $value = array_except($arr,'job');//返回数组中除指定键后的数据 //返回 ['name'=>'comtu','age'=>10] array_flatten $arr = [ 'a' =>1, 'b' =>[ 'a'=>2, 'b'=>3 ] ]; $value = aray_flatten($arr);//返回$arr内的所有值 //返回 array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } array_where $arr = ['name' => 'comtu','age'=>18,'job'=>'manager']; $value = array_where($arr,function($k,$v){ return is_string($v); });//返回满足条件的新数组 返回全是String类型的数据 //返回 ['name' => 'comtu','job'=>'manager'] Path 辅助函数 echo app_path();// 项目绝对路径 项目绝对路径 E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel\app echo config_path(); 项目下的配置目录的绝对路径 E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel\config echo public_path(); 项目下的public目录的绝对路径 E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel\public echo storage_path(); 项目下的storage目录的绝对路径 E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\my_laravel\storage 字符串 辅助函数 str_plural 单词单数变复数 例如 $value = str_plural('apple'); //返回apples $value = str_plural('sheep');// 返回sheep $value = str_plural('ability');// 返回abilities starts_with //字符串是否是与指定的内容开头 var_dump( starts_with('abcd','ab'));//返回true ends_with //字符串是否是与指定的内容结束 var_dump( ends_with('abcd','b'));//返回false camel_case //将字符串替换成驼峰命名法 echo camel_case('hello_world');//返回helloWorld class_basename //返回 命名空间的类名 echo class_basename('App\Controller\DemoDataBaseController');//返回DemoDataBaseController str_limit //限制字符串的长度_返回指定长度的字符串 echo str_limit('abcdefg',3);//返回abc... 后面有省略号 str_is //判断字符串是否满足条件 是否是与ab开头e结尾_参数一是条件,参数二是判断的值 var_dump(str_is('ab*e','abcde')); //返回true 在 Laravel 中使用图片处理库 Integration/Image系统需求 PHP >= 5.3 Fileinfo Extension GD Library (>=2.0) … or … Imagick PHP extension (>=6.5.7) 安装部署 Integration/image 在 composer.json [require] 节增加""intervention/image": "2.*"",之后执行 composer update 我当时下载的是2.3.2 如果出现错误信息: (PHP版本5.5.28) E:\ComTu_Design\PHP\Apache2.2\htdocs\firstPHPStorm\myLaravelShop>composer update > php artisan clear-compiled Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages. Problem 1 - Installation request for intervention/image 2.0.17 -> satisfiable by intervention/image[2.0.17]. - intervention/image 2.0.17 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system. 解决方法: 找到PHP环境目录 --> 打开 php.ini 文件 找到 ;extension=php_fileinfo.dll (PHP5.5.28-nts 862行) 去除前面的;号~~重新运行即可. 会在后台下载,等待. Laravel 配置 安装部署 Integration/image 完成后,打开配置文件 config/app.php 在相应位置添加代码, 然后 Image 类就能自动加载并可供使用了。其功能强大到可以处理你的几乎所有图片处理需求。 //服务提供器 Laravel 5.1.23 在 providers 中配置 Intervention\Image\ImageServiceProvider::class, //别名配置 Laravel 5.1.23 在 aliases 中配置 'Image' => Intervention\Image\Facades\Image::class, 配置设置 默认情况下, Integration/Image 使用PHP的GD库扩展。 如果你想切换到 imagick,你可以使用 php artisan 创建一个配置文件以添加相应的配置。 $ php artisan config:publish intervention/imag 基本使用 这里列出几个基本功能,更详细使用说明请查看相关接口文档。 文档地址: http://image.intervention.io/ 墙 Demo //1、显示一张图片 Route::get('imageRead',function(){ $img = Image::make(public_path().'/uploads/43195301.jpg');//读取图片 return $img->response();//返回图片资源在浏览器中显示 }); //2、创建缩略图 Route::get('imageThumbnail',function(){ if (!file_exists(public_path().'/uploads/thumbnail')){ //创建文件夹 mkdir (public_path()."/uploads/thumbnail"); } $rawFilePath = public_path().'/uploads/43195301.jpg'; $thumbnailFilePath = public_path().'/uploads/thumbnail/'.uniqid().'.jpg'; //生成缩略图 $img = Image::make($rawFilePath);//读取图片文件 $img->resize(120,120);//缩略图大小 宽/高 $img->save($thumbnailFilePath);//需要保存在已有的目录中 echo $rawFilePath.'生成缩略图到'.$thumbnailFilePath; }); //3、绘制一张图片 Route::get('imageCanvas',function(){ $img = Image::canvas(800, 600, '#ccc'); return $img->response(); }); //4.插入一个水印 Route::get('imageInsert',function(){ $rawFilePath = public_path().'/uploads/43195301.jpg'; $watermark = public_path().'/uploads/563b24c067472.jpg';//水印 //top-left (default) //top //top-right //left //center //right //bottom-left //bottom //bottom-right // $img->insert($watermark, 'bottom-right', 10, 10);//其它位置 // $img->insert($watermark, 'center');//中间 $img = Image::make($rawFilePath)->resize(800, 600)->insert($watermark);//打水印,默认左上角 return $img->response(); }); 如果运行时页面中出现如下错误: NotSupportedException in Driver.php line 18: GD Library extension not available with this PHP installation. in Driver.php line 18 at Driver->__construct() in ImageManager.php line 107 at ImageManager->createDriver() in ImageManager.php line 50 at ImageManager->make('/public/uploads/43195301.jpg') in Facade.php line 214 .... 解决方案 开启PHP的GD功能 找到PHP环境目录 --> 打开 php.ini (PHP5.5.28-nts 863行) 文件 找到 ;extension=php_gd2.dll 去除前面的;号~~重新启动服务运行即可. 我遇到的错误链接数据库时异常. //路由中,尝试连接数据库 Route::get('myDatabase',function(){ $name = DB::connection()->getDatabaseName(); echo $name; //打印出 数据库名 my_laravel 表示成功 }); PDOException in Connector.php line 47: could not find driver in Connector.php line 47 at PDO->__construct('mysql:host=localhost;dbname=my_laravel', 'root', 'root', array('0', '2', '0', false, false)) in Connector.php line 47 ..... 解决办法: php环境中找到 php.ini文件并找到 ;extension=php_pdo_mysql.dll 并去除前面的;号.然后重启服务器.如apache 或者 关闭服务重新启动服务 php -S localhost:80 -t my_laravel\public Session生成数据库表异常 错误原因1.可能存在冲突的表原因. database/migrations 目录下的文件是需要生成的表. 如果出现错误警告,可查看当前目录下的文件是否是自己需求要创建的表. 如果里面存在冲突的表,如我当时有两个users表 查打开查看文件是否是自己的内容.如果不是.~~可删除文件,需要注意的是 删除后需要在 my_laravel/vendor/composer/autoload_classmap.php 文件中找到相同的一条记录一并删除重新执行即可. 还有遇到的一些错误见功能模块,有解决方案 artisan命令谨慎操作还原回清静 php artisan fresh 创建控制器 php artisan make:controller MyController 创建model php artisan make:model User 查看当前所有路由列表 php artisan route:list 一整个程序停止解析(使用场景如,网站遇到攻击,网站需要维护等) 配置后访问网站的所有地址都会被跳转到Beright back php artisan down php artisan up 生成Session数据库表 php artisan session:table //1 composer dump-autoload //2 php artisan migrate //3 创建中间件 php artisan make:middleware MyMiddleware 英文文档http://laravel.com/docs/5.1 中文文档:http://www.golaravel.com/http://laravel-china.org/docs/5.0/middlewarehttp://www.golaravel.com/post/install-and-run-laravel-5-x-on-windows/ Demo下载本博文的案例Demo下载地址如下: 本文案例Demo 本文案例Demo_购物商场 刚学PHP的两个框架Laravel与CI,个人对这两个框架的感觉如下~ Laravel Codeigniter 开发效率 ★★★★ ★★★ 运行效率 ★★★ ★★★★ 学习成本 ★★☆ ★]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>PHP</tag>
<tag>Laravel</tag>
<tag>artisan</tag>
<tag>Integration/Image</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PHP学习笔记-CI框架]]></title>
<url>%2F%2F2015%2F09%2F10%2FPHP_codeigniter.html</url>
<content type="text"><![CDATA[本人因公司需求.学习PHP的CI框架.本博文为学习笔记.CodeIgniter 是一套给 PHP 网站开发者使用的应用程序开发框架和工具包。它提供一套丰富的标准库以及简单的接口和逻辑结构,其目的是使开发人员更快速地进行项目开发。使用 CodeIgniter 可以减少代码的编写量,并将你的精力投入到项目的创造性开发上。 其中内容包括,CI_Controller对象 , 数据库访问 , AR模型(QB模型) , CI类库扩展 ,Url相关函数 , 设置路由 , 隐藏入口文件, 分页 , 文件上传 , Session , 验证码 , 语言包 , CI框架内部解析等内容. 目录 CodeIgniter框架简介 MVC 控制器-controllers 视图-views 模型-model CI的超级对象-(CI的控制器对象CI_Controller)) 数据库访问 AR模型操作数据库增删改查 active_record (CI3.0之后改名 query_builder QB模型) CI类库扩展 为网站设计不同的主题.前台,后台(扩展控制器CI_Controller,装载器Loader) 扩展CI的captchahelper.php实现验证码 扩展_修改分页生成的代码 url相关函数 设置路由 隐藏入口文件-index.php 分页 文件上传 图片处理类 Session 验证码 表单验证 语言包 购物车类库 CI框架内部解析 注:测试版本为CodeIgniter3.0.0框架 与CI2.x的有些不同.但都有标注不同点以及处理方法.笔记理论上基本适用于3.0.0(当前最新)之前的版本. 中文教程地址:http://codeigniter.org.cn/user_guide/index.html CodeIgniter框架简介 ( CI框架 )CodeIgniter是一个轻量级但功能强大的PHP框架是基于MVC设计模式. 框架开发和二次开发 打个比方:买房子 买二手房,直接拎包入住 , 好比二次开发,如:dedecms,PHPCMS(内容管理系统),ECShop(开源免费的网上商店系统) 买毛坯房,不能住人,自己去买各种装修材料,请人装修,才能入住.好比框架开发.只提供 基础功能和项目结构. CI是框架,用于框架开发. 目录结构说明: license.txt许可协议 user_guide 用户手册(一般删除) index.php 入口文件 system 框架核心代码,通常不动的. application 应用目录 |-- cache 缓存目录 |-- config 配置文件目录 |-- controllers 控制器文件夹 |-- core 核心库扩展目录 |-- errors 错误页面 |-- helpers 自定义辅助函数文件夹 |-- hooks 勾子文件夹 |-- language 语言包 |-- libraries 自定义库文件夹,通常是一些类文件 |-- logs 日志 |-- models 模型文件夹 |-- third_party 第三方库目录,如smarty |-- views 视图文件夹 MVC1. 入口文件 唯一一个让浏览器直接请求的脚本文件 2. 控制器 controller 协调模型和视图 3. 模型 model 提供数据,保存数据,数据有效性认证 4. 视图 view 只负责显示,以及表单... 5. 动作 action 是控制器中方法,用户被浏览器直接请求 访问url使用的是pathinfo //http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/welcome/test test是welcome的一个方法 入口文件.php/控制器/动作 application 应用目录 controllers 控制器 models 模型 views 视图 默认控制器是welcome 默认动作是index 控制器-controllers1.不需要加后缀 , 直接是类名.php (自己编写的控制器需要UserController.php) 2.文件名全部小写 3.所有的控制器,直接或者间接继承自 CI_Controller 类 4.尽量不要使用Index名作为控制器类名,因为与方法index与类名相同的,会被PHP当作构造方法void __construct(){} 5.控制器中, 动作(函数,方法)要求: public 不能以_开头 //不能被浏览器范围 protected function test() { echo 'test'; } //以下划线开头的方法,不能被浏览器请求 public function _test1() { echo 'test1'; } public function test2(){ $this->_test1(); } 视图-views1.在控制器中如果加载视图 //直接写试图名字,不写扩展名,如果有子目录,则写上目录名 $this->load->view ( 'user/index' ); //表示user目录下的index.php文件 可以多次调用$this->load->view (视图); 2.试图中,直接使用原生PHP代码 3.推荐使用 <?php foreach ($list as $item):?> <?=$item["email"]?> <?php endforeach;?> <?php if(empty($carts)):?> <?php else:?> <?php endif;?> 模型-model模型文件名全部使用小写,建议使用_model为后缀,防止与控制器类名冲突,但里面的类名首字母大写 在模型中,可以直接使用超级对象中的属性 \application\models\user_model.php 模型 <?php class User_model extends CI_Model{//继承自 CI_Model //返回所有用户 public function getAll(){ $res = $this->db->get('user');//在模型中,可以直接使用超级对象中的属性 return $res; } } \application\controllers\my_model_demo.php 控制器 <?php class My_model_demo extends CI_Controller { // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/my_model_demo public function index() { // 加载模型,加载后将自动成为超级对象的属性 // $this->load->model ( 'User_model' ); $this->load->model ( 'User_model', 'user' ); // 起个别名 // 调用模型获取数据 // $list = $this->User_model->getAll(); $list = $this->user->getAll (); // 使用别名 // 加载视图 $this->load->view ( 'user/my_model_view_demo', array ('list' => $list ) ); } } \application\views\user\my_model_view_demo.php 视图 <!DOCTYPE html> <html> <head> <title>View</title> </head> <body> <?php //CI它使用了一个 extract 函数,将数组变量导入到当前的符号表,所以直接使用键名作为变量来访问 var_dump( $list); ?> </body> </html> 建议使用这种这种MVC架构来编写代码,可维护性高一些. CI的超级对象-(CI的控制器对象CI_Controller)当前的控制器对象 属性 $this->load // --> system/core/CI_Loader类装载器,类的加载,如视图,控制器等 装载器类的实例system/core/CI_Loader.php 装载器CI_Loader提供方法: view() 装载视图 vars() 分配变量到视图 database() 装载数据库操作对象 model() 装载模型 helper() 一些辅助函数 $this->uri // 获取url参数等功能 是CI_URI类的实例 --> system/core/CI_URI.php CI_URI类提供方法: segment(n) 用于获取url中的第几个参数(值) 传统的: 入口文件.php/控制器/动作/参数1/值1/参数2/值2 CI的 : 入口文件.php/控制器/动作/值1/值2 echo $this->segment(3);//值1 echo $this->segment(4);//值2 方式一: // 使用CI的pathinfo // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/user/testURI/4 echo $this->uri->segment(3); // 获取URI第几段的参数 可取得4 , 从user开始计算1,2,3 方式一: 直接写在方法里面 http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/user/testURI2/1/jack public function index($id = 0,$name=''){ //可以直接获取到id=1 ; name= jack ,如果id没有输入默认为0,默认name为空 echo $id;//1 echo $name; // jack } $this->input 获取用户输入信息 如,get put cookie等 是CI_Input类的实例 --> system/core/CI_Input.php CI_Input类提供方法: $this->input->post('username'); //$_POST['username'] $this->input->post('username',true); //跨站脚本(XSS)过滤 $this->input->server('DOCUMENT_ROOT'); //$_SERVER['DOCUMENT_ROOT'] $this->input->cookie(); $this->input->server() ... 在视图(view)中,直接使用$this来访问超级对象中的属性 CI支持控制器在子目录中. 如果你在建立一个大型的应用程序,你会发现 CodeIgniter 可以很方便的将控制器放到一些子文件夹中。 只要在 application/controllers 目录下创建文件夹并放入你的控制器就可以了。 注意: 如果你要使用某个子文件夹下的功能,就要保证 URI 的第一个片段是用于描述这个文件夹的。例如说你有一个控制器在这里: application/controllers/products/shoes.php 调用这个控制器的时候你的 URI 要这么写: example.com/index.php/products/shoes/show/123 你的每个子文件夹中需要包含一个默认的控制器,这样如果 URI 中只有子文件夹而没有具体功能的时候它将被调用。 只要将你作为默认的控制器名称在 application/config/routes.php 文件中指定就可以了。 CodeIgniter 也允许你使用 URI 路由 功能来重新定向 URI。 数据库访问配置数据库文件 可在 \system\database\drivers\目录下查看可被支持的数据库驱动 application/config/database.php 数据库配置文件 $db['default']= array( //'default'表示默认数据库,当一个项目需要连接多个数据库的时候,可以增加多一个$db['新数据库别名']= array(.... 'hostname' => 'localhost', 'username' => 'root', 'password' => 'root', 'database' => 'test', //database数据库名 'dbdriver' => 'mysql', //数据库驱动 'dbprefix' => 'blog_', //表前缀 'swap_pre' => 'swap_', //表前缀 假设代码里面的都是使用swap_的前缀,而表是使用blog_前缀,则会自动替换成dbprefix的前缀,而无需修改源代码 ); 表前缀 'dbprefix' => 'blog_', 'swap_pre' => 'blog_', 配置为一样,代码中,直接硬编码表前缀就行了,如果以后项目数据库表前缀发生变化, 只需要修改'dbprefix' => 'new_', 代码中的blog_会自动替换为new_ /////数据库附件///// 将数据库访问对象,装载到超级对象的属性中 $this->db 方式一(PHP代码手动加载): // 装载一个数据库操作类 $this->load->database();//表示连接默认数据库 //一个项目多个数据库时,表示连接其它数据库,配置文件\application\config\database.php中配置相对应别名 //$this->load->database('新数据库别名'); 方式二(配置文件,自动加载): //因为每次使用数据库的时候都需要装载数据库操作类,所以在配置文件\application\config\autoload.php中配 //置Auto-load Libraries 自动加载项,以后就可以不再需要手动加载,但对于多个数据库的还是需要指定数据库. //$autoload['libraries'] = array('database');//自动加载配置 //$this->load->database();//以后编写都不需要这行代码. 使用: //装载成功后,会放入超级对象的属性中, 默认属性名是db // var_dump($this->db);//$this->db 返回的是 CI_DB_mysql_driver 对象 继承自 CI_DB CI_DB_mysql_driver 对象 --> \system\database\drivers\mysql\mysql_driver.php $this->db->query()方法 增删改查 查询数据 返回的是一个对象 $sql = 'select * from blog_user'; // 返回的是CI_DB_mysql_result 对象 - mysql_query() $res = $this->db->query ( $sql ); //返回的是 CI_DB_mysql_result 对象 继承自 CI_DB_result $res->result();//返回数组,数组中是一个一个的对象 $res->result_array();//返回二维数组,里面是关联数组 $res->row();//返回第一条数据,直接是一个对象 //帮助文档中--> 数据库类-->查询-->query //帮助文档中--> 数据库类-->生成查询结果集--> result_array() 等等的一些方法. 调用方法就如上所示 //以上可取得如下php原生函数类似的结果 //mysql_fetch_assoc()//关联数组 //http://www.w3school.com.cn/php/func_mysql_fetch_assoc.asp //mysql_fetch_object()//返回对象 //http://www.w3school.com.cn/php/func_mysql_fetch_object.asp CI_DB_mysql_result 对象 --> \system\database\drivers\mysql\mysql_result.php 增删改数据 返回一个 boolean 布尔值 // 插入数据 // $name=$this->input->post('name');//假如获取用户post过来的数据. // $name = 'lili'; // $pass = 'lili'; // $data [0] = $name; // $data [1] = $pass; ////参数绑定: // $sql = "insert into blog_user (name,password) values (?,me5(?))"; // $bool = $this->db->query ( $sql, $data );//多个问号?参数时,需要传入一个索引数组 // 删除数据 // $sql = "delete from blog_user where id=?"; // $bool = $this->db->query ( $sql, 2 ); // 修改数据 $data [0] = 'mary@gmail.com'; $data [1] = 3; $sql = "update blog_user set email = ? where id=? "; $bool = $this->db->query ( $sql, $data ); if ($bool) { // mysql_affected_rows echo '受影响行数:' . $this->db->affected_rows (); echo '自增ID:' . $this->db->insert_id (); } 使用CI的db增加改查都是使用$this->db->query()来进行处理. AR模型操作数据库 active_record (CI3.0之后改名 query_builder QB模型)配置 \application\config\database.php 配置开启AR/QB模型(默认TRUE) $active_record = TRUE; 配置为TRUE表示开启这项功能. (AR模型_2.2.0) $query_builder = TRUE; 配置为TRUE表示开启这项功能. (QB模型_3.0.0) \system\database\DB_active_rec.php (为AR模型原代码_2.2.0) \system\database\DB_query_builder.php (为QB模型的原代码_2.2.0) \application\config\autoload.php 配置自动加载 $autoload['libraries'] = array('database'); 使用增删改查 在配置文件中,配置表前缀后,会自动添加前缀. 插入: $bool = $this->db->insert('表名',关联数组); //TODO 通过AR/QB模型增加数据内容 insert 增加 $data = array( 'name'=>'lili', 'password'=>md5('lili') ); const TBL = 'user';//表名,常量 $bool = $this->db->insert(self::TBL,$data); var_dump($bool); echo '<hr>'; 更新: $bool = $this->db->update('表名',关联数组,条件); //TODO 通过AR/QB模型更新数据 update 更新 $data = array( 'email'=>'lili@gmail.com', 'password'=>md5('123456'), ); $bool =$this->db->update('user',$data,array('id'=>4)); var_dump($bool); 删除: $bool = $this->db->delete('表名',条件); //TODO 通过AR/QB模型删除数据 delete 删除 $bool = $this->db->delete('user',array('id'=>4)); var_dump($bool); 查询: $res = $this->db->get('表名',['条件']);//返回结果集对象. '表名'会自动增加为'blog_表名' $res->result();//结合返回获取数据 //TODO 通过AR/QB模型获取数据表内容-查询 $res = $this->db->get ( 'user' );//AR模型可以自动处理表前缀.并直接获取bolg_user表的数据 var_dump($res); echo '<hr>'; foreach ($res->result() as $item){ echo $item->name; echo ' '; echo $item->password; echo ' '; echo $item->email; echo '<br>'; } 连贯查询 //TODO 连贯查询 //SELECT `id`, `name` FROM `blog_user` WHERE `id` >= 3 ORDER BY `id` desc LIMIT 2, 3 $res = $this->db->select('id,name')// ->from('user')//表名 ->where('id >=' , 3)//'id >=' 符号前面需要一个空格,如果符号不写默认'='等于 ->limit(3,2)//跳过2条,取后3条数据 ->order_by('id desc')//通过id倒序 ->get();//获取数据 var_dump($res->result()); echo '<br>'; echo $this->db->last_query();//显示最后一条执行的SQL语句 //SELECT `id`, `name` FROM `blog_user` WHERE `id` >= 3 ORDER BY `id` desc LIMIT 2, 3 更多where查询条件 //SELECT * FROM `blog_user` WHERE `name` = 'mary' $res = $this->db->where('name','mary')->get ('user'); //SELECT * FROM `blog_user` WHERE `name` != 'mary' $res = $this->db->where('name !=','mary')->get ('user'); //SELECT * FROM `blog_user` WHERE `name` = 'mary' $res = $this->db->where(array('name'=>'mary'))->get ('user'); //SELECT * FROM `blog_user` WHERE `name` = 'mary' AND `id` > 3 $res = $this->db->where(array('name'=>'mary','id >'=>3))->get ('user'); echo $this->db->last_query(); 复杂的查询,则建议用$this->db->query($sql,$data);//使用问题?绑定参数的方式查询 CI类库扩展在system目录下的是CI框架的核心文件.如果需要进行扩展,只需要在对应的application目录下对应着system目录下的文件编写对应的类,即可扩展. 例如: application 目录下有core helpers language libraries 目录均于system目录下的文件夹相一一对应. 子类名前缀 \application\config\config.php 子类名前缀 $config['subclass_prefix'] = 'MY_'; 例如:扩展控制器 在application/core/MY_Controller.php 创建自定义的类继承自application/core/Controller.php (CI_Controller 类名与文件名不同) <?php class MY_Controller extends CI_Controller{ //构造方法 public function __construct(){ parent::__construct();//调用父类的构造方法 echo 'aaaaaaaaaaaaa'; //登录验证 //权限验证...等等 } } 然后在application/controllers/目录下的控制器中使用继承自My_Controller的类即可使用,自定义的控制器. 其它类库均类似的扩展. 案例:为网站设计不同的主题.前台,后台(扩展控制器CI_Controller,装载器Loader) 第一步,指定不同的视图View路径 . 在网站根目录下创建 themes 文件夹和 里面再创建default目录 第二步,定义一个常量,在config/costants.php,如下 在application/config/constants.php 常量配置文件中配置常量. #自定义系统相当常量 (主题目录) define('THEMES_DIR','themes/'); //指明视图路径地址. 第三步,扩展CI类 视图的加载由loader类完成,(\system\core\Loader.php)如下 CI 2.2.0 public function __construct() { $this->_ci_ob_level = ob_get_level(); $this->_ci_library_paths = array(APPPATH, BASEPATH); $this->_ci_helper_paths = array(APPPATH, BASEPATH); $this->_ci_model_paths = array(APPPATH); $this->_ci_view_paths = array(APPPATH.'views/' => TRUE); } CI 3.0.0 protected $_ci_view_paths = array(VIEWPATH => TRUE); 扩展 Loader 类 (\application\core\MY_Loader.php) <?PHP defined('BASEPATH') OR exit('No direct script access allowed'); class MY_Loader extends CI_Loader{ //被开启的主题目录 protected $_theme = 'default/'; public function switch_themes_on(){ $this->_ci_view_paths = array(FCPATH,THEMES_DIR,$this->_theme => TRUE); } public function switch_themes_off(){ #just do nothing } } 扩展控制器 <?PHP defined('BASEPATH') OR exit('No direct script access allowed'); //前台控制器 class HomeController extends CI_Controller{ public function __construct(){ parent::__construct(); #开启皮肤功能 $this->load->switch_themes_on(); } } //后台控制器 class AdminController extends CI_Controller{ public function __construct(){ parent::__construct(); #关闭皮肤功能 $this->load->switch_themes_off(); } } 第四步: 编写不同的控制器 application/controllers 下编写前台控制器 并继承 HomeController application/controllers/admin 编写后台控制器 并继承 AdminController 第五步: 编写不同的视图文件. themes/default/ 目录下编写前台视图View application/views 目录下编写后台试图View 而模型 model 是共用的. 案例:扩展CI的captchahelper.php实现验证码 url相关函数http://codeigniter.org.cn/user_guide/helpers/url_helper.html //加载url帮助类 //默认在辅助函数/URL辅助函数中,默认不加载. $this->load->helper('url');//使用前需要加载标准类,或者配置自动加载 \application\config\autoload.php 文件中配置 $autoload['helper'] = array('url'); base_url();//返回网站根目录 application\config\config.php中: $config['base_url']=''; 中配置的目录 //http://127.0.0.1:8000/CodeIgniter-3.0.0 site_url('控制器/方法') //返回 base_url/index_page/控制器/方法 ($config['index_page'] = 'index.php';) //如 http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php 案例: \application\controllers\url_demo_controllers.php <?php class Url_demo_controllers extends CI_Controller{ //http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/Url_demo_controllers public function index(){ //加载url帮助类 $this->load->helper('url'); $this->load->view('user/url_demo'); } public function insert(){ var_dump($this->input->post('name')); } } \application\views\user\url_demo.php <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <!-- <form action="/CodeIgniter-3.0.0/index.php/Url_demo_controllers/insert" method="post"> --> <form action="<?php echo site_url('Url_demo_controllers/insert');?>" method="post"> name<input type="text" name="name" /><br> password<input type="password" name="password" /><br> email<input type="text" name="email" /><br> <button type="submit">submit</button> </form> <!-- <img alt="" src="/CodeIgniter-3.0.0/uploads/logo.jpg"/> --> <img alt="" src="<?php echo base_url();?>uploads/logo.jpg" width="100"/> </body> </html> 设置路由路由配置文件 application/config/routes.php 默认控制器 配置 $route['default_controller'] = 'welcome'; 实现伪静态 $route['news/[\d]{6}/([\d]+)/([^\s]+)']='routes_demo/showTwice/$1/$2'; //URL中的地址=对应的控制器/方法/参数 -- 第一个() 对应$1 以此类推 案例: <?php class Routes_demo extends CI_Controller { // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/routes_demo public function index() { echo 'routes_demo index '; } // 路由配置目录 application/config/routes.php // 原地址: http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/routes_demo/show/2 // 路由映射地址: // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/news/2.html // 路由配置: $route['news/([\d]+)\.html']='routes_demo/show/$1'; // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/news/201515/2.html // 路由配置: $route['news/[\d]{6}/([\d]+)\.html']='routes_demo/show/$1'; public function show($id = 0) { echo '这是文章' . $id; } // 原地址: // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/routes_demo/showTwice/4/.jpg // 路由映射地址: // http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/news/201501/4/.jpg // 路由配置: $route['news/[\d]{6}/([\d]+)/([^\s]+)']='routes_demo/showTwice/$1/$2'; public function showTwice($id = 0, $res = '') { echo '这是文章' . $id . $res; } } 隐藏入口文件-index.php如原地址为: http://127.0.0.1:8000/CodeIgniter-3.0.0/index.php/routes_demo 隐藏入口文件后只需要把地址写成即可: http://127.0.0.1:8000/CodeIgniter-3.0.0/routes_demo 1.需要开启Apache的 rewrite 功能 Apache2.2\conf\httpd.conf 修改如下: 修改前: #LoadModule rewrite_module modules/mod_rewrite.so ... # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # Options FileInfo AuthConfig Limit AllowOverride None ... 修改成: # 搜索 mod_rewrite 与 .htaccess 关键字来进行查询修改项 LoadModule rewrite_module modules/mod_rewrite.so <Directory "E:/ComTu_Design/PHP/Apache2.2/htdocs"> Options Indexes FollowSymLinks # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # Options FileInfo AuthConfig Limit AllowOverride all Order allow,deny Allow from all </Directory> 重启Apache. 2.在入口文件同级目录(system/application同级目录)中,放入一个.htaccess 内容如下: (技巧如果自己编写创建一个点.开头的文件可以使用记事本另存为的方式输入双引号".htaccess"保存即可) <IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </IfModule> 3.配置索引页 \application\config\config.php 原: $config['index_page'] = 'index.php'; 修改成: $config['index_page'] = ''; 分页http://codeigniter.org.cn/user_guide/libraries/pagination.html 直接见源代码: \application\controllers\paging_demo.php <?php class Paging_demo extends CI_Controller { public function index() { } //http://127.0.0.1:8000/CodeIgniter-3.0.0/paging_demo/paging public function paging() { // 装载类文件 $this->load->library ( 'pagination' ); // 每页显示10条数据 $page_size = 10; $this->load->helper('url'); //配置页面 $config ['base_url'] = site_url('paging_demo/paging'); //一共有多少条数据 $config ['total_rows'] = '200'; //每页显示条数 $config ['per_page'] = $page_size; //分页的数据查询偏移量在哪一段上 $config['uri_seqment'] = 3; $config['first_link'] = '首页'; $config['next_link'] = '下一页'; $config['last_link'] = '最后一页'; $this->pagination->initialize ( $config ); //http://127.0.0.1:8000/CodeIgniter-3.0.0/paging_demo/paging/190 //获取链接地址第三段的参数190 与配置$config['uri_seqment']相对应 $offset = intval($this->uri->segment(3));//intval函数获取数字,无则返回0 //拼接Sql查询语句 $sql = "select * from blog_user limit $offset , $page_size"; echo $sql.'<br>'; //生成链接 $data['links']=$this->pagination->create_links (); //在页面中显示 $this->load->view('user/paging_view',$data); } } \application\views\user\paging_view.php <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>分页</title> </head> <body> <?=$links ?> </body> </html> 扩展_修改分页生成的代码 拷贝system/libraries/Pagination.php 到 application/libraries/目录 直接修改如下: 这样做之后CI会自动查找到application/libraries/Pagination.php 做为分页类. CI 3.0.0 //Generate the pagination links public function create_links(){//398行 //省略很多代码. //原: //650行 //return $this->full_tag_open.$output.$this->full_tag_close; //修改如下: 自定义内容 当然,发挥自己的想象去修改 $baseinfo = "总共 $this->total_rows 条记录,每页显示 $this->per_page 条, 总计 $num_pages 页,当前是第 $this->cur_page 页".'&nbsp;&nbsp;&nbsp;'; return $baseinfo.$this->full_tag_open.$output.$this->full_tag_close; } CI 2.2.0 function create_links(){//115行 //省略很多代码. // Add the wrapper HTML if exists //331行 //$output = $this->full_tag_open.$output.$this->full_tag_close; //return $output; //修改如下: 自定义内容 当然,发挥自己的想象去修改 $baseinfo = "总共 $this->total_rows 条记录,每页显示 $this->per_page 条, 总计 $num_pages 页,当前是第 $this->cur_page 页".'&nbsp;&nbsp;&nbsp;'; return $baseinfo.$output; } 文件上传http://codeigniter.org.cn/user_guide/libraries/file_uploading.html 案例见代码: \application\controllers\upload_demo.php <?php class Upload_demo extends CI_Controller { public function index() { } // http://127.0.0.1:8000/CodeIgniter-3.0.0/upload_demo/file public function file() { $data ['error'] = ''; $data ['upload_data'] = ''; $this->load->helper ( 'url' ); $this->load->view ( 'user/upload_view_demo', $data ); } public function upload() { // 上传目录需要手动创建 $config ['upload_path'] = './uploads/'; $config ['allowed_types'] = 'gif|jpg|png'; $config ['max_size'] = 100;//kb $config ['max_width'] = 1024; $config ['max_height'] = 768; // 上传后的文件名/不设置则默认原文件名,如果文件名冲突,则会在文件名中加入递增数字 //$config ['file_name'] = uniqid (); $this->load->library ( 'upload', $config ); $this->upload->do_upload ( 'pic' );//上传的input name $this->upload->do_upload ( 'pic2' );//上传的input name $data = array ( 'upload_data' => $this->upload->data () ,//上传成功 'error' => $this->upload->display_errors ()//错误信息 ); $this->load->view ( 'user/upload_view_demo', $data ); } } \application\views\user\upload_view_demo.php <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文件上传</title> </head> <body> <?php if($error){ echo $error;} ?> <form action="<?php echo site_url('upload_demo/upload')?>" method="post" enctype="multipart/form-data"> <input type="file" name="pic" /></br> <input type="file" name="pic2" /></br> <input type="submit" name="上传" /> </form> <?php if ($upload_data):?> <?php foreach ($upload_data as $item => $value):?> <li><?php echo $item;?>: <?php echo $value;?></li> <?php endforeach; ?> <?php endif ?> </body> </html> 图片处理类$config['image_library'] = 'gd2'; $config['source_image'] = '/path/to/image/mypic.jpg'; $config['create_thumb'] = TRUE; $config['maintain_ratio'] = TRUE; $config['width'] = 75; $config['height'] = 50; $this->load->library('image_lib', $config); $this->image_lib->resize(); //创建缩略图 // $this->image_lib->crop() //图像裁剪 // $this->image_lib->rotate() //图像旋转 // $this->image_lib->watermark() //添加图像水印 // $this->image_lib->clear() //clear函数重置所有之前用于处理图片的值。当你用循环来处理一批图片时,你可能会想使用它。 //处理不同的图片有不同的配置,详情见文档 http://codeigniter.org.cn/user_guide/libraries/image_lib.html //获取相应属性 $this->image_lib->thumb_marker; //错误信息 $this->image_lib->display_errors(); Sessionhttp://codeigniter.org.cn/user_guide/libraries/sessions.html Session 类将每个用户的 session 信息序列化(serialize)后存储到到 cookie 中(并同时进行加密)。 ci的Session是存储到cookie中.PHP原生的session是放到服务器中. 所以需要加密.可查看值,但不能修改.因为提交时会通过密钥校验数据完整性. 配置 \application\config\config.php 配置Session加密(CI2.2可配置,CI3.0.0抛弃了此功能): 原: $config['sess_encrypt_cookie'] = FALSE; 修改为: $config['sess_encrypt_cookie'] = TRUE; 配置Session关闭浏览器后失效(CI2.2可配置,CI3.0.0无此配置项): 原: $config['sess_expire_on_close'] = FALSE; 修改为: $config['sess_expire_on_close'] = TRUE; CI3.0.0 需要如下实现(关闭浏览器销毁session,不关闭或者不退出,则一直有效): $config['sess_expiration'] = 0; 配置Session密钥: 原: $config['encryption_key'] = ''; 修改密钥(可用echo md5(uniqid());生成一个值当密钥): $config['encryption_key'] = '05c072360c5ac7e19d5b2566a995991c'; 配置Session文件路径: CI3.0.0版本需要配置此项,不然会出现 Message: mkdir() [function.mkdir]: Invalid argument 异常 CI3.0.0之前版本可以不要设置.(CodeIgniter_2.2.0测试查看,无此参数) 原: $config['sess_save_path'] = NULL; 新session路径(注意~~些路径需要要使用绝对路径) 创建目录用于存储Session:E:\ComTu_Design\PHP\Apache2.2\htdocs\CodeIgniter-3.0.0\application\session $config['sess_save_path'] = 'E:\ComTu_Design\PHP\Apache2.2\htdocs\CodeIgniter-3.0.0\application\session'; 可查看 sess_save_path 配置相关文档 http://codeigniter.org.cn/user_guide/libraries/sessions.html http://php.net/manual/en/session.configuration.php#ini.session.save-path //加载模块 $this->load->library('session'); //存储session $user = $this->session->set_userdata ( 'user', $user ); //取session $user = $this->session->userdata('user'); //删除session $this->session->unset_userdata('user'); $this->session->unset_userdata($array_items);//删除多个session Ci的Session实现案例: 案例见代码: <?php class Session_demo extends CI_Controller { public function index(){} public function __construct(){ parent::__construct(); $this->load->library('session'); } // 先存储session // http://127.0.0.1:8000/CodeIgniter-3.0.0/session_demo/set_session // 在取出sesson(刷新演示一次性的数据) // http://127.0.0.1:8000/CodeIgniter-3.0.0/session_demo/show_session public function set_session() { //echo md5(uniqid());exit; $user = array('id'=>3,'name'=>'jack'); // 相当于原生的: // session_start(); // $_SESSION["user"] = $user; $user = $this->session->set_userdata ( 'user', $user ); var_dump($user);//取出来NULL echo '<br>'; //取session $user = $this->session->userdata('user'); var_dump($user); //一次性的数据,只能读一次 $this->session->set_flashdata('test','testdate-aaaaaaaaaaaa'); } //http://127.0.0.1:8000/CodeIgniter-3.0.0/session_demo/show_session public function show_session(){ //获取CI session中的数据 $user = $this->session->userdata('user'); var_dump($user); //只可以取一次,取完后就会被删除. $test=$this->session->flashdata('test'); var_dump($test); } public function delete_session(){ //删除单个session $this->session->unset_userdata('user'); // //删除多个session // $array_items = array('user', 'test'); // $this->session->unset_userdata($array_items); } public function originalPHPsession(){ // 原生session session_start(); //存储验证码信息到PHP原生的session中. $_SESSION["cap"] = 'abc'; //获取Session $word = $_SESSION['cap']; echo $word; //终结 Session unset($_SESSION['cap']); //重置session , 失去所有已存储的 session 数据。 //session_destroy(); } } 验证码ci的验证码会创建一个图片文件,所以需要创建文件夹用来存放 E:\ComTu_Design\PHP\Apache2.2\htdocs\CodeIgniter-3.0.0\captcha 使用CI验证码需要开启PHP的 GD 图像库 可用PHP代码检测是否开启GD图像库 if(extension_loaded('gd')){ echo '你可以使用gd<br>'; foreach (gd_info() as $cate=>$value) echo "$cate: $value<br>"; }else echo '没有安装gd扩展'; 开启 GD 图像库 方法: E:\ComTu_Design\PHP\php-5.3.5\php.ini 配置文件修改如下: 原: ;extension=php_gd2.dll 修改成(去除前面的;分号): extension=php_gd2.dll 修改完后保存,重启apache CI默认实现: //加载模块 $this->load->helper('captcha'); //创建验证码 $cap = create_captcha($vals); //显示验证码 echo $cap['image']; 案例见代码: <?php class Captcha_demo extends CI_Controller{ public function index(){} public function test(){ echo 'captcha_demo'; if(extension_loaded('gd')){ echo '你可以使用gd<br>'; foreach (gd_info() as $cate=>$value) echo "$cate: $value<br>"; }else echo '没有安装gd扩展'; $this->load->helper('url'); $this->load->helper('captcha'); $number = rand(1000,9999);//生成随机字符串 $vals = array( 'word' => $number,//可指定验证码内容,如果是中文.需要有支持的字体 'img_path' => dirname(BASEPATH).'/captcha/',//生成的图片存放目录,手动创建 'img_url' => base_url('/captcha/'),//图片链接地址 //'font_path' => './path/to/fonts/texb.ttf',//指定字体_如果使用中文需要指定字体 'img_width' => '150', 'img_height' => 30, 'expiration' => 5,//图片存放时间,如果过期,只有在被再次请求才会删除.单位秒 'word_length' => 8, 'font_size' => 16, 'img_id' => 'Imageid', 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // White background and border, black text and red grid 'colors' => array( 'background' => array(255, 255, 255), 'border' => array(255, 255, 255), 'text' => array(0, 0, 0), 'grid' => array(255, 40, 40) ) ); $cap = create_captcha($vals); echo $cap['word']."<br>"; echo $cap['image']; $this->load->view('user/captcha_view_demo',array('cap'=>$cap['image'])); // 原生session session_start(); //存储验证码信息到PHP原生的session中.等待校验. $_SESSION["cap"] = $cap['word']; //获取Session $word = $_SESSION['cap']; echo $word; } } 扩展CI的captchahelper.php实现验证码: 一般而言,对于验证码只需要用一次,没必要创建一张图片保存到本地. 所以我对验证码类进行了如下扩展: 不存储验证码图片,并实现点击重新获取验证码. 步骤一: 把 system/helpers/captcha_helper.php 文件拷贝到 application/helpers/ 目录下并重命名为:MY_captcha_helper.php 步骤二: 去除与目录有关的代码 修改 MY_captcha_helper.php 里面的源文件进行如下操作: (CI 3.0.0) //处理如果没有设置图片路径而被返回的操作 // if ($img_path === '' OR $img_url === '' // OR ! is_dir($img_path) OR ! is_really_writable($img_path) // OR ! extension_loaded('gd')) // { // return FALSE; // } // // ----------------------------------- // // Remove old images // // 删除旧图片 // // ----------------------------------- // $now = microtime(TRUE); // $current_dir = @opendir($img_path); // while ($filename = @readdir($current_dir)) // { // if (substr($filename, -4) === '.jpg' && (str_replace('.jpg', '', $filename) + $expiration) < $now) // { // @unlink($img_path.$filename); // } // } // @closedir($current_dir); // ----------------------------------- // Generate the image // 生成图片. // ----------------------------------- //处理生成图片--start-- // $img_url = rtrim($img_url, '/').'/'; // if (function_exists('imagejpeg')) // { // $img_filename = $now.'.jpg'; // imagejpeg($im, $img_path.$img_filename); // } // elseif (function_exists('imagepng')) // { // $img_filename = $now.'.png'; // imagepng($im, $img_path.$img_filename); // } // else // { // return FALSE; // } // $img = '<img '.($img_id === '' ? '' : 'id="'.$img_id.'"').' src="'.$img_url.$img_filename.'" style="width: '.$img_width.'; height: '.$img_height .'; border: 0;" alt=" " />'; // ImageDestroy($im); // return array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename); //处理生成图片--end-- //并在此处 增加如下四行代码,告诉浏览器返回的是一张图片. header("Content-Type:image/jpeg"); imagejpeg($im); ImageDestroy($im); return $word;//返回生成的验证码字符串 (CI 2.2.0) //处理如果没有设置图片路径而被返回的操作 // if ($img_path == '' OR $img_url == ''){ // return FALSE; // } // if ( ! @is_dir($img_path)){ // return FALSE; // } // if ( ! is_writable($img_path)){ // return FALSE; // } // if ( ! extension_loaded('gd')){ // return FALSE; // } // // ----------------------------------- // // Remove old images // // 生成图片. // // ----------------------------------- // list($usec, $sec) = explode(" ", microtime()); // $now = ((float)$usec + (float)$sec); // $current_dir = @opendir($img_path); // while ($filename = @readdir($current_dir)){ // if ($filename != "." and $filename != ".." and $filename != "index.html"){ // $name = str_replace(".jpg", "", $filename); // if (($name + $expiration) < $now){ // @unlink($img_path.$filename); // } // } // } // @closedir($current_dir); // ----------------------------------- // Generate the image // ----------------------------------- // $img_name = $now.'.jpg'; // ImageJPEG($im, $img_path.$img_name); // $img = "<img src=\"$img_url$img_name\" width=\"$img_width\" height=\"$img_height\" style=\"border:0;\" alt=\" \" />"; // ImageDestroy($im); // return array('word' => $word, 'time' => $now, 'image' => $img); //并在此处 增加如下四行代码,告诉浏览器返回的是一张图片. header("Content-Type:image/jpeg"); imagejpeg($im); ImageDestroy($im); return $word;//返回生成的验证码字符串 步骤三: 在控制器中使用 captcha <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Privilege extends CI_Controller { public function __construct(){ parent::__construct(); //载入验证码辅助函数 $this->load->helper('captcha'); } public function login(){ $this->load->view('login'); } public function code(){ //创建验证码 $word = create_captcha(); //可以在MY_captcha_helper.php直接配置参数也是OK的. //当然同样适用于配置参数.但可以免去了 CI文档中的 img_path 与 img_url 这两个必要参数了. // $vals = array( // 'word' => rand(1000,9999),//可指定验证码内容,如果是中文.需要有支持的字体 // //'font_path' => './path/to/fonts/texb.ttf',//指定字体_如果使用中文需要指定字体 // 'img_width' => '150', // 'img_height' => 30, // 'expiration' => 5,//图片存放时间,如果过期,只有在被再次请求才会删除.单位秒 // 'word_length' => 4, //验证码位数 // 'font_size' => 16, // 'img_id' => 'Imageid', // 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // // White background and border, black text and red grid // 'colors' => array( // 'background' => array(255, 255, 255), // 'border' => array(255, 255, 255), // 'text' => array(0, 0, 0), // 'grid' => array(255, 40, 40) // ) // ); //$word = create_captcha($vals); } } 步骤四: view中显示 .并实现点击图片重新加载新图片的功能: <img src="<?php echo site_url('admin/privilege/code');?>" width="145" height="20" alt="CAPTCHA" border="1" onclick= this.src="<?php echo site_url('admin/privilege/code').'/';?>"+Math.random() style="cursor: pointer;" title="看不清?点击更换另一个验证码。"/> 表单验证http://codeigniter.org.cn/user_guide/libraries/form_validation.html 实现: //加载模块 $this->load->helper ( array ('form','url') ); $this->load->library ( 'form_validation' ); //设置校验规则,参数一:表单上的名称,参数二:错误信息显示的名称,参数三:校验的规则见标准文档 //http://codeigniter.org.cn/user_guide/libraries/form_validation.html#id25 $this->form_validation->set_rules('参数名','用户名','required'); //表单校验 返回true表示校验通过 $bool = $this->form_validation->run(); //表单校验加载校验规则 使用配置文件 application/config/form_validation.php 如果没有自己创建. //$bool = $this->form_validation->run('demo');//参数名与config文件中的相对应. form表单中 //自动回填内容项 <?php echo set_value('参数名')?> //显示错误信息, 参数一:为form中的表单名,参数二,三:参数样式 <?php echo form_error('参数名','<span>','</span>')?> //显示全部错误信息 <?php echo validation_errors();?> 案例见代码: \application\controllers\form_demo.php <?php class Form_demo extends CI_Controller { public function index() { } // http://127.0.0.1:8000/CodeIgniter-3.0.0/form_demo/insert public function insert() { $this->load->helper ( array ('form','url') ); //加载模块 $this->load->library ( 'form_validation' ); // 校验如果这样编写会编写很多规则代码.可以在config进行配置验证文件.也可实现重复利用的效果 // $this->form_validation->set_rules('name','用户名','required'); // $this->form_validation->set_rules('password','密码','required|min_length[6]|max_length[16]|md5'); //$this->form_validation->set_rules('repassword','确认密码','trim|required|md5|matches[password]');//重复密码验证 // $this->form_validation->set_rules('email','邮箱',array('required','valid_email')); // // 表单验证 // $bool = $this->form_validation->run(); // 表单验证配置文件 application/config/form_validation.php $bool = $this->form_validation->run('demo'); if ($bool) { // 调用模型保存数据库 echo 'success'; } else { // 显示错误信息 $this->load->view ( 'user/form_view_demo' ); } echo $bool; } } -------- \application\views\user\form_view_demo.php <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>表单验证</title> </head> <body> <?php echo validation_errors();?> <form action="<?php echo site_url('form_demo/insert');?>" method="post"> name<input type="text" name="name" value="<?php echo set_value('name')?>"/> <?php echo form_error('name','<span>','</span>')?> <br> password<input type="password" name="password" /> <?php echo form_error('password','<span>','</span>')?> <br> email<input type="text" name="email" value="<?php echo set_value('email')?>"/> <?php echo form_error('email','<span>','</span>')?> <br> <button type="submit">submit</button> </form> </body> </html> -------- \application/config/form_validation.php <?php $config = array ( 'demo' => array ( array ( 'field' => 'name', 'label' => '用户名', 'rules' => 'required' ) , array ( 'field' => 'password', 'label' => '密码', 'rules' => 'required' ), array ( 'field' => 'email', 'label' => '邮箱', 'rules' => 'required|valid_email' ) ), 'signup'=>array( .... ) ); 语言包如表单错误信息等语言. 将语言包解压到 \application\language\zh_cn 配置语言: \application\config\config.php 原: $config['language'] = 'english'; 改成 $config['language'] = 'zh_cn'; 购物车类库http://codeigniter.org.cn/user_guide/libraries/cart.html CI3.0.0 官方上标注: 之后购物车类已经废弃,请不要使用。目前保留它只是为了向前兼容。 引入购物车支持库 $this->load->library('cart'); //$autoload['libraries'] = array('cart');//或者配置 application/config/autoload.php 加入购物车 //名称有要求 $data['id'] = $this->input->post('goods_id',true); //id $data['name'] = $this->input->post('goods_name',true); //商品名称 $data['qty'] = $this->input->post('goods_nums',true); //数量 $data['price'] = $this->input->post('shop_price',true); //金额 $goods_thumb= $this->input->post('goods_thumb',true); $data['options'] = array('goods_thumb'=>$goods_thumb);//更多信息 if($this->cart->insert($data)){ echo 'ok'; }else{ echo 'error'; } //获取购物车数据 $data['carts'] = $this->cart->contents(); //view中获取展示数据 <?php foreach ($carts as $v):?> <?php echo base_url('public/uploads').'/'.$v['options']['goods_thumb'];?> <?php echo $v['id'];?> <?php echo $v['name'];?> <?php echo $v['price'];?> <?php echo $v['qty'];?> <!-- 自动生成 --> <?php echo $v['row_id'];?> <!-- 自动生成 $v['subtotal'] 会自动计算出总金额--> <?php echo $v['subtotal']?> <?php endforeach;?> 删除/修改购物车 $data['rowid'] = $rowid; $data['qty'] = 0; $this->cart->update($data); redirect('cart/show'); 注意事项: 1. 中文问题(2.2.0存在,3.0.0不存在) $data['name'] = $this->input->post('goods_name',true); //商品名称 中name项在 CI3.0.0之前是不支持中文内容的. CI3.0.0版本可支持中文. CI2.2.0修改cart.php原文件来使name支持中文. 方法: system/library/Cart.php 拷贝到 application/library 中,找到如下代码并注释即可. // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods. // Note: These can be user-specified by setting the $this->product_name_rules variable. //CI2.2.0的原代码在186行数 //if ( ! preg_match("/^[".$this->product_name_rules."]+$/i", $items['name'])) //{ // log_message('error', 'An invalid name was submitted as the product name: // '.$items['name'].' The name can only contain alpha-numeric // characters, dashes, underscores, colons, and spaces'); // return FALSE; //} 2.total_items 问题 (CI2.2.0与CI3.0.0都存在) 显示购物车中商品数量。 CI框架中默认是显示出商品数量总数 即:如果存放到购物车2双鞋子+1条裤子 <?php echo $this->cart->total_items();?> 获取出来的数据是3件物品,而不是2种商品 而如果你的需求是 显示商品种类 .需要修改源代码. 方法: system/library/Cart.php 拷贝到 application/library 中,找到如下代码修改: CI3.0.0 //代码位置在401行 _save_cart 函数中 $this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']); //$this->_cart_contents['total_items'] += $val['qty']; //原代码 $this->_cart_contents['total_items'] ++; //新代码 $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']); CI2.2.0 //代码位置在386行 _save_cart 函数中 $total += ($val['price'] * $val['qty']); //$items += $val['qty'];//原代码 $items ++;//新代码 3.在我们向购物车中添加商品的时候,如果添加了已经存在于购物车中的商品时,会出现逻辑错误。 理论上应该是累加,但实际上是将原来的商品信息给删除了。所以要相应的处理一下: // 获取/封装数据.... //在插入之前,需要判断即将要加入的商品是否已经存在于购物车中 $carts = $this->cart->contents(); foreach ($carts as $v){ if($v['id'] == $data['id']){ $data['qty'] += $v['qty']; } } // 插入数据... CI框架内部解析CI是一个单入口框架,所有的请求都需要经常index.php文件 . 流程如下: | --> Routing --> Scourity --> |-----------------------| index.php | |Application Controller |-->Drivers,Models,Libaies,Helpers,Packages,Scripts | <-- Caching <-- View <-- |-----------------------| 分析index.php文件. $system_path = 'system'; $application_folder = 'application'; 这个和我们的文件夹结构名称一一对应. 当然这个名称是可以更改. 中间加载一些系统目录常量等. 在index.php结尾尝试打印里面的内容. var_dump(SELF , ENVIRONMENT ,BASEPATH ,FCPATH, SYSDIR); exit(); 结果 string(9) "index.php" string(11) "development" string(62) "E:/ComTu_Design/PHP/Apache2.2/htdocs/CodeIgniter-3.0.0/system/" string(55) "E:\ComTu_Design\PHP\Apache2.2\htdocs\CodeIgniter-3.0.0/" string(6) "system" 最后载入 require_once BASEPATH.'core/CodeIgniter.php'; 分析CodeIgniter.php 载入Common.php 通用函数库. require_once(BASEPATH.'core/Common.php'); 例如: is_php($version) php版本 is_really_writable 是否可写 load_class 载入类函数 等等 载入配置文件(常量配置) require_once(APPPATH.'config/constants.php'); 载入核心类文件. $BM =& load_class('Benchmark', 'core'); $EXT =& load_class('Hooks', 'core'); 勾子类 $CFG =& load_class('Config', 'core'); 配置文件类 $UNI =& load_class('Utf8', 'core'); 编码类 $URI =& load_class('URI', 'core'); URI类 $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL); 路由类 $OUT =& load_class('Output', 'core'); 输出类 $SEC =& load_class('Security', 'core'); 安全类 $IN =& load_class('Input', 'core');输入类 $LANG =& load_class('Lang', 'core');语言类 载入CI总控制器. require_once BASEPATH.'core/Controller.php'; 通过router类对象 $RTR的两个方法获取当前的类名和方法名 CI 2.2.0 $class = $RTR->fetch_class(); $method = $RTR->fetch_method(); CI 3.0.0 $class = ucfirst($RTR->class); $method = $RTR->method; 比如:输入 http://127.0.0.1:8000/CodeIgniter-3.0.0/form_demo/insert 那么上述代码获取的$class就是控制器form_demo,$method就是insert , 如果没 有方法名,则默认index New了一个对象,叫做CI, 这个就是CI框架中的超级对象(super class) $CI = new $class(); //此处,就是超级对象的形成过程. //var_dump($CI); 可以尝试在此处打印一下$CI的信息 . 分析CI_Controller类 Controller.php 简单的PHP单例设计模式. private static $instance; public static function &get_instance(){ return self::$instance; } 构造函数中将前面载入的核心类,作为CI对象的属性. foreach (is_loaded() as $var => $class){ $this->$var =& load_class($class); } 然后是载入Loader.php 装载类 $this->load =& load_class('Loader', 'core'); 在控制器中出现的$this就是超级对象. 超级对象形成之后,我们就可以使用超级对象(超级对象的属性) 提供一系列方法完成我们的业务逻辑.如果需要完成其它的功能,可以载入其他的类文件,辅助函数. 这些类文件和辅助函数包括CI已经提供好的.也可以是我们自己定义的. 使用中遇到过的问题 CI 3.0.0使用中 访问 views/目录下的文件时被拦截.如 http://127.0.0.1:8000/mycishop/application/views/images/ecshop_logo.gif 提示: Forbidden You don't have permission to access /mycishop/application/views/images/ecshop_logo.gif on this server. 解决办法: 查看与 views 同级的 .htaccess文件 发现是此处招到的拦截. <IfModule authz_core_module> Require all denied </IfModule> <IfModule !authz_core_module> #拦截访问. #Deny from all #修改成如下代码即可. Allow from all </IfModule> 配置文件与 [隐藏入口文件-index.php](#隐藏入口文件-index.php) 里面的权限管理类似. ///////////////////////////////数据库附件//////////////////////////////////////////////// /* 查询数据库 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | discuz | | mysql | | test | | wordpress | +--------------------+ 5 rows in set (0.00 sec) 进入到test数据库 mysql> use test Database changed 查看test数据库中的表 mysql> show tables; Empty set (0.00 sec) 创建表 mysql> mysql> CREATE TABLE IF NOT EXISTS bolg_user( -> id INT AUTO_INCREMENT PRIMARY KEY, -> name VARCHAR(255) NOT NULL UNIQUE, -> password CHAR(32) NOT NULL, -> email VARCHAR(255) NOT NULL DEFAULT '' -> )ENGINE MyISAM DEFAULT CHARSET=UTF8; Query OK, 0 rows affected (0.05 sec) 增加数据 mysql> mysql> mysql> INSERT INTO bolg_user (name,password) VALUES ('admin',md5('admin')); Query OK, 1 row affected (0.07 sec) 查看数据库的表 mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | bolg_user | +----------------+ 1 row in set (0.00 sec) 查看数据库结构 mysql> desc bolg_user; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | UNI | NULL | | | password | char(32) | NO | | NULL | | | email | varchar(255) | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+ 4 rows in set (0.02 sec) 设置编码格式为gbk,解决查看时乱码. mysql> set names gbk; Query OK, 0 rows affected (0.00 sec) 查询数据库表中的内容 mysql> select * from bolg_user; +----+-------+----------------------------------+-------+ | id | name | password | email | +----+-------+----------------------------------+-------+ | 1 | admin | 21232f297a57a5a743894a0e4a801fc3 | | +----+-------+----------------------------------+-------+ 1 row in set (0.00 sec) * */ 本文案例Demo 本文案例Demo附加Demo购物商场]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>PHP</tag>
<tag>CI</tag>
<tag>CodeIgniter</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android Studio 笔记]]></title>
<url>%2F%2F2015%2F01%2F01%2FJava_Android_Studio_IDE.html</url>
<content type="text"><![CDATA[Android studio 使用笔记.其中内容包括AS基本设置,创建项目,引用第三方类库,导入Eclipse项目,Gradle,加速AS运行速度,Eclipse中使用Gradle,以及一些AS的常见错误. 目录 基本设置 创建工程 引用第三方类库(模块)) 导入Eclipse项目 Android-Studio目录结构(Project视图)) Gradle settings.gradle 项目根节点下的build.gradle 模块根节点下的build.gradle Gradle命令行 加速Android-Studio/Gradle构建 Eclipse-中使用-Gradle 一些常见错误 Android Studio下载 http://www.androiddevtools.cn/ 安装完AS后第一次运行会自动下载一些组建,建议进行翻墙. 基本设置一些常用设置项罗列 File --> Settings |-->Appearance & Behavior | |---> Appearance | | |---> UI Options | | | |----> Threme: 有三种IDE主题供选择. | | | |----> * Override default fonts by (not recomended)一整个软件的字体 | | | |----> Cyclic scrolling in list 内勾选后内容滚动支持滚动尾部跳顶部,顶部跳尾部. | | |---> Window Options | | | |----> Allow merging buttons on dialogs (勾选合并相类似的按钮到一个按钮中.) | |---> Menus and Toolbars --> 自定义增减菜单内容. | |---> System Settings | | | |----> Startup/Shutdown | | | | |-----> Reopen last project on startup (勾选打开IED时是否直接进入到上一次关闭时的项目工程) | | | | |-----> Confirm application exit (退出提示两次) | | | |----> project opening 项目工程的打开方式. | | | | |-----> 打开新的窗口,在相同的窗口打开,经过确定打开窗口 | | | |----> Synchronization | | | |-----> Synchronize files on frame activation (勾选,自动同步文件到项目中, | | | | 例如,当IDE切换到后台,项目过程中有某个文件被其它方式修改了,切换到IDE时自动更新.) | | | |-----> Save files on frame deactivation (勾选,IDE切换到后台时自动保存文件) | | | |-----> Save files automatically if application is ide for 15 sec | | | | 当ide在15秒内无操作时,自动保存文件. | | | |-----> Use "safe write" (save changes to a temporary file first) 保存时保存临时文件. | | |---> Passwords --> Password storage policy | | | |-----> Do not remember passwords 每次都需要输入密码 | | | |-----> Remember passwords until the application is closed | | | | 打开过一IDE输入过一次密码后,直到关闭IDE都无需再输入密码. | | | |-----> Save on disk with master password protection 保存磁盘,无需要再输入 . | | |---> * Http Proxy 代理 (红杏有免费提供AS的代理) 主机名为: hx.gy 端口为:1080 | | |---> Updates 软件更新,有自动更新,可选择版本类型,等. | |---> File Colors 文件颜色 | |---> Scopes 定义范围 | | |---> 点击 + 新创建 --> 输入名称如App --> 窗口中间的结构中选择不需要的范围可以进行,Exclude 排除 | | |--> 使用时.在编程界面ctrl+shift+F弹出搜索框 | | | |--> Scope --> 选择Custom: App . 搜索的内容就是经过自定义筛选的. | | |--> Analyze --> Inspect Code --> 也可以选择Custom scope ,在指定范围内做代码检查. | |---> Notifications 通知栏气泡. 定义提示不提示通知项. | |---> Quick Lists 宏 |---> Keymap 快捷键设置 --> Keymaps: 中有很多种选择.,下面的栏目则可以修改快捷键的按键. | |---> 常用的快捷键: | |---> * 快速输入多行内容 ctrl+shift+alt+鼠标点击多行 , 出现多个光标,然后输入内容. | |---> * 删除整行 Ctrl+Y | |---> * 复制当前行 Ctrl+D | |---> * 剪切当前行 Ctrl+X | |---> * 交换位置 Ctrl+Shift+箭头 如果是方法体内,上下交换代码,方法则交换方法位置 | |---> * 呼出大纲 Ctrl+F12 | |---> * 查找引用 Alt+F7 查找出来的结果会分为读与写 | |---> * 重命名 Shift+F6 会重名名引用的名称 | |---> * 格式化代码 Ctrl+Alt+L | |---> 移动鼠标,单词间跳跃 Ctrl +箭头 | |---> 快速定义常量 Ctrl+Alt+C 如: int i = 100; 选中100后使用快捷键 | |---> 其它更多 |---> Editor 编辑器设置 | |--> General --> 子内容 | | | |--> mouse | | | | |--> Honor"CamelHumps"words settings when selecting on 双击选择的是一个变量,还是变量中的一个单词 . | | | | | 支持此功能需要在General/Smark Keys中Rus"camelHumps"words打勾,默认没勾选 | | | | |--> Change font size (Zoom) with Ctrl+Mouse Wheel 通过Ctrl加鼠标滚轮放大缩小字体 | | | | |--> Enable Drag'n'Drop functionality in editor 拖拽模式,鼠标选中的内容用鼠标点中后拖拽到其它地方. | | | |--> Soft Wraps | | | | |--> Use soft wraps in Editor 软回车 | | | |--> Other | | | | |--> Strip trailing spaces on Save: ... 保存时删除行尾的空格 | | | | |--> Show quick doc on mouse move Delay(ms)500 勾选后通过鼠标悬浮到内容上显示文档(默认关闭) | | | |--> * Highlight on Caret Movement 高亮显示 | | | | |--> * Highlight matched brace 高亮显示括号 | | | | |--> * Highlight current scope 选中的范围是否高亮显示 | | | | |--> * Highlight usages of element at caret 选中的元素是否高亮显示 | | | |--> Formatting 格式化代码时是否显示通知 | | | |--> Refactorings 重构相关 | | | | |--> Enable in-Place mode 启动重构模式 Ctrl+Alt+C | | | |--> Limits | | | | |--> Maximum number of contents to keep in clipboard: 5 粘贴板的数量. 可以同时拷贝的数据. | | | | | 使用方法: 多次拷贝内容后,需要粘贴指定次内容时,Ctrl+Shift+V 在呼出的对话框中选择粘贴内容 | | | | |--> Recent files limit 50 最近打开的文件限制个数 | | | | |--> console commands history size: 300 命令行面板上下键切换使用过的命令 | | | |--> Rich-text copy 拷贝内容样式 默认激活,用处是拷贝一些代码到Word文档的时候保持原有的字体颜色样式. | | |--> Smart Keys | | | |----> Home 键 定位 尝试点多几次Home键 | | | |----> End 键 定位 | | | |----> Insert pair bracket 自动补全括号 | | | |----> Insert pair quote 自动补全引号 | | | |----> Reformat block on typing"}" 当我们输入括号}结尾时,格式化代码 | | | |----> Use "camelHumps" words 见上面mouse | | | |----> Surround selection on typing quote or brace | | | | 勾选后, 选择代码后,输入一个{括号可以把所选择的代码加入到{里面} ,也可以使用"号 把内容"引起来" | | | |----> Enter 回车键功能 | | | |----> Smart indent 缩进 | | | |----> Insert pair "}" 补全大括号 | | | |----> Insert documentation comment stub 自动生成注释文档 | | | |----> 后面对于Xml/Html/Css等的配置 | | |--> Appearance 编辑器外观的一些设置 | | | |----> Use anti-aliased font 是否启用抗锯齿字体 | | | |----> Caret blinking(ms) 500 光标闪烁速度 | | | |----> * Show line numbers 显示代码行数(默认未勾选) | | |--> Editor Tabs 编辑器Tab的 | | | |----> Tab Appearance 外观 | | | | |-----> Placement: 标签Tab显示位置 | | | | |-----> Show Tabs in single row 显示单行 Hide tabs if there is no space 没有空间,隐藏标签 | | | | |-----> Hide file extension in editor tabs 隐藏文件后缀 | | | | |-----> Show directory in editor tabs for non-unique filenames 文件名相同显示文件路径 | | | | |-----> mark modified tabs with asterisk 用星号标记修改标签(未保存显示*,可结合不自动保存) | | | |----> Tab Closing Policy 标签关闭策略 | | | | |-----> Tab limit 10 标签最大上限 ,后面的选项为优先关闭选项 | | |--> Code Folding 代码的折叠 设置 | | |--> Code Completion | | | |----> code Completion | | | | |-----> Case sensitive completion : All / None / First letter 代码提示对大小写的要求 | | | | | 三种选项, 意思是编辑器在输入代码时代码提示的内容要求.其中None最为宽松 , | | | | |-----> Auto-insert when only one choice on : | | | | | |--> Basic Completion(Ctrl+Space), 重新唤醒代码提示框 | | | | |-----> 自动弹出时间等设置 | | |--> Auto Import 自动导入设置 | | |--> Postfix Completion 代码完成的快捷方式 | | | |----> Expend templates with : Tab / Space / Enter | | |--> Console Folding 控制台代码折叠 设置 | |--> *Colors & Fonts 编辑器主题颜色字体设置 Scheme | | |--> Font 设置编辑器字体大小 | | |--> Android Logcat 设置Logcat的颜色,默认比较单调.可在些处进行修改 | |--> Code Style 代码风格 一般由整个项目组决定 | |--> Inspections 代码检查配置 | | |--> 例如: Android Lint / addJavasriptInterface Called 很常见的开启WebView的JavaScript的一个报警 | | | 勾选则如果编写此代码警告 Severity: 可自定义选择错误级别.以及检查范围 | |--> * File and Code Templates 文件代码模板 | | |---> Templates / Includes / Code / Other 四种 | |--> * File Encodings 文件编码方式 通常设置成UTF-8 | | |--> IDE Encoding : UTF-8 | | |--> Project Encoding : UTF-8(默认GBK) | | |--> Default encoding for properties files: UTF-8 (默认<System Default> (now GBK)) | |--> * Live Templates 动态模板 快速创建模板 如geti 快速编写出getInstance()方法 | |--> File Types 打开文件类型 |---> Plugins 插件 可在线下载或使用本地插件 | |--> 介绍几个插件 : JsonFormat 插件 快速把Json字符串生成一个对象类 ; 使用步骤: | | 1. 拷贝需要生成对象的Json字符串, | | 2.在已创建的一个类中点击右键>Generate>JsonFormat>粘贴到弹出框中>ok>自动创建代码. |---> Version control 版本控制配置 |---> Build,Execution,Deployment 构建、执行部署 Gradle配置 等 |---> Languages&Frameworks 语言与框架 |---> Tools 工具 | |--> 外部工具的配置等 Edit |--> Copy Reference 拷贝包含包名类名的字符串 | 例如: 一个Activity类,全选类名,使用些种拷贝方式,粘贴到AndroidManifest.xml时,内容为包名.类名 |--> Copy from History 粘贴历史拷贝 |--> * Column Selection Mode 列选择模式 可进行多行部分选择.批量处理. |--> Toggle Case 转换大小写 Navigate |--> 导航 , 跳转 也可以使用Ctrl+鼠标点击对应方法,或属性 Code |--> Override Methods 实现父类方法 |--> Generate... 生成方法. 如 hashCode equals 等方法,并会自动实现方法 |--> Surround With.. 生成代码 , 如if,try catch 语句等 编辑器右击选项中: |--> refactor --> Encapsulap Fields 生成get set方法 |--> Lacal History 自带版本控制,可查看历史修改记录等 其它快捷键:Alt+Enter 万能键 / 如导入包, 错误解决方案提示等.Ctrl+Q 查看代码中的图片双击Shift 会弹出搜索框 泛型 @StringRes @ColorRes 要求参数类似例: private void showToast(@StringRes int stringId){…} 代码扫描查找代码潜在错误 Analyze --> Inspect Code --> 选择扫描对象 --> ok 创建工程File–>New–>New Project–>输入项目名,包名,项目存放地址等信息–>选择开发的设备,并选择支持最小SDK版本–>点击”Help me choose”可查看版本目前支持的版本占用情况–>Next–>选择模板–>Next–>输入Activity与Layout的名称–>Finish–>一个新的项目工程就创建完毕. 引用第三方类库(模块)创建一个类库 File->New-->New Module-->其中可以选择多种类型, 如果Android Library新创建一个,Import Existing Project导入Eclipse或者Gradle项目 等--> 假设新创建一个类库-->与创建项目一个,输入项目包等-->选择模板-->Finish 引入类库(远程,jar,本地库) 选择需要引用类库的项目-->右击-->Open Module Settings 或者使用快捷键(F4) -->Project Structure窗口-->选择需要引入类库的项目-->右边点击 Dependencies -->点击右边的+号-->三种选择 Lrbrary(远程) , File(jar) , module(本地) , --> 选择最后一种 module dependencies 引入本地库 -->选择上面新创建的类库-->OK-->这样就关联了一个第三方模块,类库了. 导入Eclipse项目File-->New-->Import Project-->选择Eclipse项目工程-->提示导入的项目存放地址(建议不要与原项目使用相同的目录 ,因为会重新拷贝一份代码并修改成Android Studio的环境)-->弹出一提示,说之前引用的jar包以及一些引用的类库, 都会被替换成dependencies ,如果能找到源.-->Finish-->引入完成会弹出一个导入报告-->报告里面有说明一些没有导入进来的文件名称 -->因为这些文件不是工程项目的文件.项目外的文件.-->如果有重要的文件需要自己手动拷贝到项目中. Android Studio目录结构(Project视图)目录结构视图 Android studio project 相当于 eclipse workspace Android studio module 相当于 eclipse project ∨MyApplication --> 项目根节点 |> .gradle -->Gradle的临时文件 |> .idea --> |∨* app --> 主要的模块,如果配置正常时字体为粗体 | |> build --> 模块的临时文件与最终的apk文件 | | |>outputs >apk >目录下生成apk文件 | |>* libs --> 存放jar包, 不能存so文件 | |∨* src --> 源代码目录 | |> androidTest --> 测试源代码 | |∨ main --> 源代码 | | |> aidl --> 是aidl文件源代码目录,AIDL:即Android接口定义语言。 | | |> assets --> 资源目录 | | |> java --> 源代码 | | |> jni --> 存放C语言文件 | | |>* jniLibs --> 存放so文件 | | |> res --> 资源文件 | | |AndroidManifest.xml | |.gitignore | |app.iml | |* build.gradle --> Gradle构建脚本,对当前模块设置的Gradle文件 | |proguard-rules.pro --> 代码混淆配置文件 |> build --> 编译过程中的一些临时文件 |> gradle --> 默认生成的一个本地的Gradle引导语,如果机器没有Gradle环境,会通过下载 |> library --> 如果项目中有多个模块,官方建议创建一个目录library目录下存放其它模块 | > myLibrary --> 其它第三方模块 . |.gitignore |*build.gradle --> Gradle构建脚本,全局Gradle |build.gradle.bk |gradle.properties --> Gradle属性文件 |gradlew |gradlew.bat --> gradle批处理文件 |local.properties --> Android ADT bundle SDK目录配置文件 |MyApplication.iml |*settings.gradle --> 项目模块配置文件 GradleGradle是一种动态脚本语言, 基于Groovy http://www.groovy-lang.org/能够很方便的通过Maven/lvy管理依赖使用非常灵活,一种效果可以有多种实现 http://gradle.org/ settings.gradle案例: include ':app', ':library:mylibrary' ":"是一个路径的分割线 注意: 当 Open Module Settings --> Project Structure --> Dependencies 有配置过库 :mylibrary 直接修改settings.gradle成:library:mylibrary会编译异常 Error:(25, 0) Project with path ':mylibrary' could not be found in project ':app'. 这时,需要在Open Module Settings -- > Project Structure --> app --> Dependencies --> + module Dependencies 中的:mylibrary删除 即可,并重新增加:library:mylibrary 项目根节点下的build.gradle案例: // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { // repositories { jcenter() //依赖库 } dependencies { classpath 'com.android.tools.build:gradle:1.2.2'//当前工程需要依赖的插件 // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { //所有工程 repositories { jcenter()//依赖库 --> 默认使用的是https加密库,当使用代理时,有可能不支持https ,则有可能出错 } } 模块根节点下的build.gradle案例: //声明是一个Application 最终会生成一个apk //如果是一个库 'com.android.library' 这样就不会输出apk, //只会输出 jar 包或者 aar (aar是包含文件与资源的一个包,Eclipse不支持) apply plugin: 'com.android.application' android { //安卓编译环境设置 compileSdkVersion 22 //需要与自己的已下载的Build版本对应 buildToolsVersion "22.0.1" //注意,如果是网络下载下来的项目,需要查看自己的AS是否有相同Build的版本,没有则修改,否则报错 sourceSets{ //配置不同版本原代码不同.与productFlavors对应 phone{ manifest.srcFile 'src/phone/AndroidManifest.xml' assets.srcDirs = ['src/phone/assets'] //java.srcDirs = ['src'] //resources.srcDirs = ['src'] //aidl.srcDirs = ['src'] //renderscript.srcDirs = ['src'] //res.srcDirs = ['src'] } pad{ manifest.srcFile 'src/pad/AndroidManifest.xml' assets.srcDirs = ['src/pad/assets'] } } signingConfigs{ //签名配置 releasekey{ //名称自定义 storeFile file('MyKey.keystore') //文件名 storePassword 'android' // key密码 keyAlias 'androiddebugkey' //别名 keyPassword 'android' //密钥 } debugkey{ storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } } defaultConfig { //生成的apk的更多信息配置 ,会覆盖AndroidManifest.xml的信息 applicationId "com.tu.myapplication" //包名 会覆盖AndroidManifest.xml的包名 minSdkVersion 8 targetSdkVersion 22 versionCode 1 versionName "1.0" signingConfig signingConfigs.releasekey //签名配置 指定签名 } productFlavors { //产品配置 //与buildTypes的每一项结合生成包,会在build/outputs/apk/生 //成 (productFlavors项*buildTypes的项*2)个apk phone { applicationId 'com.tu.myapplication' //不同包名 signingConfig signingConfigs.debugkey //不同签名 } pad { applicationId 'com.tu.myapplication.hd' signingConfig signingConfigs.debugkey } } buildTypes { //编译类型, 主要用于你这个包编译出来是做什么用的. //通常系统默认有两种,一种是release 二种是debug 生成4个apk包 , //如果 productFlavors 有配置项如2个,buildTypes默认的2个则会生成2*2*2=8个apk包 release { //为默认项配置混淆文件 minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } // //会生成两个包 app-fordebug.apk , app-fordebug-unaligned.apk // fordebug { // applicationIdSuffix '.debug' // //包名会加.debug后缀即: com.tu.myapplication.debug // } // // //会生成两个包 app-fordailybuild.apk , app-fordailybuild-unaligned.apk // fordailybuild{ // applicationIdSuffix '.db'//包名会加.db后缀 // } // // //会生成两个包 app-formonkeytest.apk , app-formonkeytest-unaligned.apk // formonkeytest{ // applicationIdSuffix '.monkey' // } } } dependencies { //依赖关系 //单独引入jar包 --> Open Module Settings -- > Project Structure --> //app --> Dependencies --> + File Dependencies -->选择jar包 //compile files('libs/gson-2.2.4.jar') //使用单独引入jar包的方式 //动态引入包 //表示引入libs目录下的所有jar包 , 并且是动态的. compile fileTree(include: ['*.jar'], dir: 'libs') //引入Libaray v7包 Open Module Settings -- > Project Structure --> //app --> Dependencies --> + Library Dependencies compile 'com.android.support:appcompat-v7:22.1.1' //这种方式表示引入单个包. //引入 本地包 Open Module Settings -- > Project Structure --> app --> //Dependencies --> + module Dependencies compile project(':library:mylibrary') //这种方式表示引入一个本地库(本地模块) //引入远程库 Open Module Settings -- > Project Structure --> app --> //Dependencies --> + Library Dependencies 搜索框内直接搜索 //搜索 httpmime 后选择库即可. Gradle会后台下载jar包. 但如果你没有代理 compile 'org.apache.httpcomponents:httpmime:4.5' //红杏有提供给开发都一个免费代理,主机名为: hx.gy 端口为:1080 //File--> settings --> Appearance & Behavior --> //System Settings --> HTTP Proxy --> 勾选manual proxy configuration //--> HTTP --> Host name:输入 hx.gy --> Port number:输入 1080 -->ok } Gradle命令行> gradle命令行运行需要下载一个gradle 并把gradle目录下的bin目录配置到环境变量中. 然后使用cmd > Android Studio目录下一般也会有个gradle / gradle-x.x/ 也可以配置这个bin目录 然后使用cmd > 也可以定位到项目目录下有 gradlew.bat 文件是 gradle批处理文件 定位后cmd中运行 gradlew命令 > Android studio中有Terminal 窗口 也可以直接使用命令,而不用进入到cmd 常用命令: gradle tasks 罗列可执行的任务 gradle build 编译全部脚本 gradle clean 清除项目 gradle 加tasks列出的名称,指定编译内容 如: gradle assembleDebug 加速Android Studio/Gradle构建优化对电脑配置要求比较高. 1> 开启gradle单独的守护进程 在下面的目录下面创建gradle.properties文件: /home/<username>/.gradle/ (Linux) /Users/<username>/.gradle/ (Mac) C:\Users\<username>\.gradle (Windows) 并在文件中增加: org.gradle.daemon=true 2> 同时修改项目下的 gradle.properties 文件也可以优化(也可以在步骤1>里面直接加入,变成全局,针对所有项目生效): org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.configureondemand=true 3> 对Android Studio构建优化 > File --> settings |--> Build,Execution,Deployment |--> Build Tools |--> Gradle --> 项中勾选 Offline work (离线工作模式) | |--> Gradle VM options : -Xmx2048m -XX:MaxPermSize=512m |--> Compiler |--> 勾选 Compile independent modules in parallel (may require larger heap size) |--> VM Options:输入 -Xmx2048m -XX:MaxPermSize=512m |--> Command-line Options:输入 --offline 4> 命令行构建 在构建命令后面加 --daemon --parallel --offline 例如 gradle build --daemon --parallel --offline 1) Total time: 34.133 secs 第一次构建 3) Total time: 12.748 secs 未修改任何内容第三次构建 5) Total time: 12.639 secs 7) Total time: 12.201 secs gradle build 2) Total time: 21.371 secs 未修改任何内容第二次构建 4) Total time: 18.891 secs 6) Total time: 18.823 secs 8) Total time: 18.79 secs 5> 引入依赖库时使用aar 如果库项目工程为 apply plugin: 'com.android.library' 的,则会在目录 库/build/outputs/arr/xxxx.aar 生成文件 Eclipse 中使用 Gradle在Eclipse项目根目录下创建build.gradle文件 apply plugin: 'com.android.application' //让Eclipse的项目使用gradle进行编译, cmd 定位到项目根目录, //使用gradle build进行编译 . //生成到build目录中, android{ compildSdkVersion 21 buildToolsVersion "21.1.2" sourceSets{ main{ manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['src'] assets.srcDirs = ['src'] } } } dependencies{ //...一些引入包 } 一些常见错误出现gradle无法下载等时可以尝试修改一些代码如: 修改MyApplication/gradle/wrapper/gradle-wrpper.properties文件 distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 修改https为http: distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip 代理下载错误 appcenter buildscript{ repositories{ maven{url "http://repo1.maven.org/maven2"} jcenter{ url "http://jcenter.bintray.com" } } dependencies {classpath 'com.android.tools.build:gradle:1.1.0+'} } 安卓ADT的目录错误 修改local.properties 更多内容后续补上. 本文案例Demo]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android</tag>
<tag>Android studio</tag>
<tag>IDE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java线程池]]></title>
<url>%2F%2F2014%2F12%2F16%2FJava_Throad_Pool.html</url>
<content type="text"><![CDATA[目录 线程池的作用 Executors提供四种线程池 newCachedThreadPool newFixedThreadPool newScheduledThreadPool newSingleThreadExecutor ThreadPoolExecutor类 Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。 线程池的作用线程池作用就是限制系统中执行线程的数量。 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果; 少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。 一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。 当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。 使用线程池的好处1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。 2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存. 比较重要的几个类Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池, 而只是一个执行线程的工具。真正的线程池接口是ExecutorService。 类名 说明 ExecutorService 真正的线程池接口。 ScheduledExecutorService 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 ThreadPoolExecutor ExecutorService的默认实现。 ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。 Executors提供四种线程池Java通过Executors提供四种线程池,分别为: > newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 > newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 > newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 > newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 (1). newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。 1234567891011121314151617181920212223242526272829ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i &lt; 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(index); } });}//==========================//实际调用class Executors { public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue&lt;Runnable>()); }} (2). newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。 1234567891011121314151617181920212223242526272829ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 0; i &lt; 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } });}//==========================//实际调用 class Executors { public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue&lt;Runnable>()); } } (3) newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。延迟执行 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("delay 3 seconds"); }}, 3, TimeUnit.SECONDS);//表示延时3秒执行scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); }}, 1, 3, TimeUnit.SECONDS);//表示延迟1秒后每3秒执行一次.//==========================//实际调用class Executors{ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }}class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService { public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }}//上面的super调用class ThreadPoolExecutor extends AbstractExecutorService{ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue&lt;Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);//调用下面构造函数 } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue&lt;Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {...}} (4)、newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 12345678910111213141516171819202122232425262728293031323334353637383940ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i &lt; 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } });}//==========================//实际调用class Executors { public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue&lt;Runnable>())); }}static class FinalizableDelegatedExecutorService extends DelegatedExecutorService { FinalizableDelegatedExecutorService(ExecutorService executor) { super(executor); } protected void finalize() { super.shutdown(); }}static class DelegatedExecutorService extends AbstractExecutorService{} 结果依次输出,相当于顺序执行各个任务。 现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。 ThreadPoolExecutorThreadPoolExecutor 的完整构造方法的签名是: 1234567891011121314151617181920212223class ThreadPoolExecutor extends AbstractExecutorService { public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue&lt;Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {...}}//corePoolSize - 池中所保存的线程数,包括空闲线程。//maximumPoolSize -池中允许的最大线程数//keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。//unit - keepAliveTime 参数的时间单位。//workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。//threadFactory - 执行程序创建新线程时使用的工厂。//handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。 abstract class AbstractExecutorService implements ExecutorService {} public interface ExecutorService extends Executor {} public interface Executor {} //以上关系可看出 ThreadPoolExecutor是Executors类的底层实现。 参数 corePoolSize - 池中所保存的线程数,包括空闲线程。 同时并发运行的核心线程数量. 参数 maximumPoolSize -池中允许的最大线程数 线程最大容纳线程数 参数 keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 简时一点说就是,当核心线程空闲多长时间后自销毁. 例如: 当corePoolSize为2个时,maximumPoolSize为3时,用户同时放入3个线程任务到池中. 创建线程运行了前两个任务后,还需要运行一个任务,复用前面创建的线程运行 最后一个任务,而还有一个线程闲置,keepAliveTime的参数就是当这个闲置线程等待 多长时间内都无任务则销毁.而unit则是keepAliveTime的时间单位. 参数 unit# - keepAliveTime 参数的时间单位。 参数 workQueue# - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。 排队 所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互: > 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。 > 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。 > 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 排队Queue有三种通用策略: > 直接提交。工作队列的默认选项是 SynchronousQueue ,它将任务直接提交给线程而不保持它们。 在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。 此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。 > 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线 程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此, maximumPoolSize 的 值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列; 例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连 续到达时,此策略允许无界线程具有增长的可能性。 > 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽, 但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大 限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频 繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列 通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。 参数 threadFactory - 执行程序创建新线程时使用的工厂。 ThreadFactory 是一种在软件开发过程中封装对象创建过程的面向对象的设计模式。 参数 handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。 无法由 ThreadPoolExecutor 执行的任务的处理程序。 当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时, 在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。 在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。 下面提供了四种预定义的处理程序策略: > 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException 。 1234567 public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() +" rejected from " + e.toString()); }} > 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。 此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。 12345public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); }} > 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。 1public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {} > 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭, 则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。 123456public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); }} 定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。 Demo下载在JDK帮助文档中,有如此一段话:“强烈建议程序员使用较为方便的Executors工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、 Executors.newFixedThreadPool(int)(固定大小线程池) Executors.newSingleThreadExecutor()(单个后台线程)它们均为大多数使用场景预定义了设置。” 本文Demo下载 本Demo中有Executors提供四种线程池的基本使用,以及自定义实现 ThreadPoolExecutor 线程池暂停,继续运行等介绍.]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Thread</tag>
<tag>ThreadPoolExecutor</tag>
<tag>ScheduledThreadPoolExecutor</tag>
<tag>newCachedThreadPool</tag>
<tag>newSingleThreadExecutor</tag>
<tag>newFixedThreadPool</tag>
<tag>ExecutorService</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java线程可见性]]></title>
<url>%2F%2F2014%2F12%2F15%2FJava_Throad_Visibility.html</url>
<content type="text"><![CDATA[目录: 1. 共享变量在线程间的可见性 1.1 Java内存模型(JMM) 1.2 共享变量可见性实现的原理 1.3 重排序 1.4 as-if-serial语义 2. synchronized实现可见性 3. volatile实现可见性 3.1 volatile适用场景 4. synchoronized和volatile比较 共享变量在线程间的可见性可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到. 共享变量: 如果一个变量在多个线程的工作中都存在副本,那么这个变量就是这个几个线程的共享变量. Java内存模型(JMM)Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则, 以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节. > 所有的变量都存储在主内存中 > 每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝) 线程1 线程2 线程3 ↑↓ ↑↓ ↑↓ ------------------------------------- 工作内存1 工作内存2 工作内存3 x的副本1 x的副本2 x的副本3 ↑↓ ↑↓ ↑↓ ------------------------------------- 主内存 共享变量x 两条规定 > 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写 > 不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成. 共享变量可见性实现的原理线程1对共享变量的修改要想被线程2及时看到,必须要经过如下2个步骤: > 把工作内存1中更新过的共享变量刷新到主内存中 > 将主内存中读取共享变量的值工作内存2中. 要实现共享变量的可见性,必须保证两点: > 线程修改后的共享变量值能够及时从工作内存刷新到主内存中 > 其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中. 重排序> 重排序:代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或者处理器为了提高程序性能而做的优化 1.编译器优化的重排序(编译器优化) 2.指令级并行重排序(处理器优化) 3.内存系统的重排序(处理器优化) 例如: 代码顺序: int number = 1 int result = 0 有可能执行顺序: int result = 0 int number = 1 as-if-serial无论如何重排序,程序执行的结果应该与代码顺序执行结果一致 (Java编译器,运行时和处理器都会保证Java在单线程下遵循as-if-serial语义 例如: int num1 = 1;//第1行代码 int num2 = 2;//第2行代码 int sum = num1+num2;//第3行代码 单线程:第1,2行的顺序可以重排,但第3行不能 重排序不会给单线程带来内存可见性问题. 多线程中程序交错执行时,重排序可能会造成内存可见性问题 可见性的实现方式 Java语言层面(不包括jdk1.5之后的concurrent包下的高级特性)支持的可见性实现方式: > synchronized > voatile synchronized实现可见性synchronized能够实现: > 原子性(同步) > 可见性 JMM关于synchronized的两条规定: > 线程解锁前,(离开synchronized代码块前),必须把共享变量的最新值刷新到主内存中. > 线程加锁时,(进入synchronized代码块前),将清空工作内存中共享变量的值, 从而使用共享变量时需要从主内存中重新读取最新的值. (注意:加锁,解锁需要同一把锁) 线程解锁前对共享变量的修改在下次加锁时对其他线程可见. synchronized实现可见性 线程执行互斥代码的过程: 1.获得互斥锁 2.清空工作内存 3.从主内存拷贝变量的最新副本到工作内存 4.执行代码 5.将更改后的共享变量的值刷新到主内存 6.释放互斥锁 package com.tu.test.visibility; public class SynchronizedDemo { // 共享变量 private boolean ready = false; private int result = 0; private int number = 1; // 写操作 public void write() { ready = true; // 1.1 try {//为了得到结果一 Thread.sleep(100); } catch (InterruptedException e) { } number = 2; // 1.2 } // 读操作 public void read() { if (ready) {// 2.1 result = number * 3;// 2.2 } System.out.println("result的值为:" + result ); } // 内部线程类 private class ReadWriteThread extends Thread { // 根据构造方法中传入的flag参数,确定线程执行读操作还是写操作 private boolean flag; public ReadWriteThread(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { write(); } else read(); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { SynchronizedDemo synDemo = new SynchronizedDemo(); synDemo.new ReadWriteThread(true).start(); synDemo.new ReadWriteThread(false).start(); // 结果有可能会出现如下情况 //结果一: 1.1->2.1->2.2->1.2 结果:result = 3 2.1->1.2->2.2 结果:result = 6 1.2->2.1->2.2 结果:result = 6 //结果四: 2.1->1.1->1.2 结果:result = 0 } } } 可见性分析 导致共享变量在线程间不可见的原因: 1.线程的交叉执行 2.重排序结合线程交叉执行 3.共享变量更新后的值没有在工作内存与主内存间及时更新 synchronized解决方案: 1.原子性() 2.原子性 3.可见性 // 写操作 public synchronized void write() { ready = true; // 1.1 number = 2; // 1.2 } // 读操作 public synchronized void read() { if (ready) {// 2.1 result = number * 3;// 2.2 } System.out.println("result的值为:" + result ); } //给代码加锁后结果就不同了.` // 结果有可能会出现如下情况 //结果三: 1.1->1.2->2.1->2.2 结果:result = 6 //结果四: 2.1->1.1->1.2 结果:result = 0 //这两种结果是由线程间谁先争取到CPU的使用权而得出来的不同结果. volatile实现可见性深入来说:通过加入内存屏障和禁止重排序优化来实现的. > 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令 > 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令 (Java提供了8条屏障指令) 通俗地讲: volatile变量在第次被线程访问时,都强迫从主内存中重读该变量的值, 而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存. 这样任何时刻,不同的线程总能看到该变量的最新值. 线程写volatile变量的过程: 1.改变线程工作内存中volatile变量副本的值 2.将改变后的副本的值从工作内存刷新到主内存 线程读volatile变量的过程 1.从主内存中读取volatile变量的最新值到线程在工作内存中. 2.从工作内存中读取volatile变量的副本 volatile不能保证volatile变量复合操作的原子性: private int number = 0; number++; //不是原子操作, 多线程时有可能会交叉执行或重排序. 1.读取number的值 2.将number的值加1 3.写入最新的number的值 ---- synchronized(this){ number++; } 加入synchronized,变为原子操作 1.读取number的值 2.将number的值加1 3.写入最新的number的值 以上3步骤不会被交错执行不会被重排序 ---- private volatile int number = 0; 变为volatile变量,无法保证原子性 package com.tu.test.visibility; public class VolatileDemo { private volatile int number = 0; public int getNumber() { return this.number; } public void increase() { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.number++; } public static void main(String[] args) { final VolatileDemo volMemo = new VolatileDemo(); for (int i = 0; i < 500; i++) { new Thread() { public void run() { volMemo.increase(); }; }.start(); } // 如果还有子线程在运行,主线程就让出CPU资源 // 直到所有的子线程都运行完了,主线程再继续往下执行 while (Thread.activeCount() > 1) { Thread.yield(); } //正常应该打印number的值为500.但因为number++不是原子 //操作所以值变的不确定,有可能会小于500的值 System.out.println("number" + volMemo.getNumber()); } } 程序分析: 假设number=5; 1.线程A读取number的值 2.线程B读取number的值 3.线程B执行加1操作 4.线程B写入最新的number的值 5.线程A执行加1操作 6.线程A写入最新的number的值.把线程B的值覆盖了. 7.两次number++只增加了1 这是因为number自增操作的原子性的原因. 解决方案有: > 保证number自增操作的原子性: > 使用synchronized关键字 > 使用JDK1.5后推出的 ReentrantLock (java.until.concurrent.locks包下) > 使用JDK1.5后推出的 AtomicInterger (java.util.concurrent.atomic包下) //原 //private volatile int number = 0; //修改 private int number = 0; //原 //public void increase() { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // this.number++; //} //修改 使用 synchronized 实现原子性 public void increase() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this) { this.number++; } } //修改使用 ReentrantLock 实现原子性 private Lock lock = new ReentrantLock(); public void increase() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } lock.lock();//加锁 try { this.number++; } finally { lock.unlock();//解锁 .防止try有异常.JDK推荐写法try..finally } } volatile适用场景因volatile只能保证变量的可见性,不能保证操作的原子性. 要在多线程中安全的使用volatile变量,必须同时满足: > 1.对变量的写入操作不依赖其当前值(即改变后的volatile的值与改变前的值不要有关系) 不满足: number++ , count = count*5等这类操作. 满足: boolean变量,记录温度变化的变量等. > 2.该变量没有包含在具有其他变量的不变式中. (即程序中有多个volatile变量,那么,每个volatile变量的状态要独立其它volatile变量) 例如: 程序中有两个volatile变量low 与up 不满足: 不变式low<up synchronized和volatile比较> volatile不需要加锁,比synchronized更轻量级,不会阻塞线程. > 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于解释锁 > synchronized即能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性. 问题补充: 即使没有保证可见性的措施,很多时候共享变量依然能够在主内存和工作内存见得到及时更新: 一般只有在短时间内高并发的情况下才会出现变量得不到及时更新的情况,因为 CPU在执行时会很快地刷新缓存,所以一般情况下很难看到这种问题. 对64位(long,double)变量的读写可能不是原子操作: >Java内存模型允许JVM将没有被volatile修饰的64位数据类型的读写操作 划分为两次32位的读写操作来进行. 导致问题: 有可能会出现读取到" 半个变量 "的情况 解决方法: 加volatile关键字 实际上很多商用JVM已经为long,double作为原子性来进行操作处理 Demo下载本文案例Demo]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>synchronized</tag>
<tag>volatile</tag>
<tag>ReentrantLock</tag>
<tag>原子性</tag>
<tag>可见性</tag>
<tag>重排序</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java线程]]></title>
<url>%2F%2F2014%2F12%2F14%2FJava_Thread.html</url>
<content type="text"><![CDATA[目录 线程简介 Java中的线程实现方式 Thread常用方法 线程同步(synchronized关键字) 多线程间数据通信 线程同步死锁 线程简介程序(Program): 计算机指令的集合,它以文件的形式存储在磁盘上。 进程(Process): 一个程序在其自身的地址空间中的一次执行活动。 资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源; 而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。 线程(Thread): 是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度, 区别在于线程没有独立的存储空间,而是和所属进程中的其他线程共享一个存储空间,这使得线程间的通信远较进程简单。 什么是线程: 线程是一个应用程序执行时候的多条路径。 线程执行的原理: 在物理上只有一个CPU的情况下,JVM是采用抢占CPU资源,给不同的线程划分指定执行时间片的方式来实现。 java中隐含的两个线程: 主线程 每个java应用程序都至少有一个线程。这就是所为的主线程。它由JVM创建并调用java应用程序的main方法。 垃圾回收线程(后台线程) JVM还通常会创建一些其他的线程,不过这些线程对我们来说是不可见的. 比如,用于自动垃圾收集的线程、对象终止或者其他的JVM处理任务相关的线程。 Java中的线程实现方式Thread :线程 所在包: java.lang.* 多线程实现方式: 多线程的方式一:(继承)见下面例: 1.1. 将类声明为 Thread 的子类。(继承) 1.2. 该子类应重写 Thread 类的 run 方法。 1.3. 接下来可以分配并启动该子类的实例。 多线程的方式二:(接口)见下面例: 2.1. 声明实现 Runnable 接口的类。 2.2. 该子类应重写 Thread 类的 run 方法。 2.3. 然后可以分配该类的实例, 2.4. 在创建 Thread 时作为一个参数来传递并启动 二种方式的区别: 使用Runnable接口 可以将CPU,代码和数据分开,形成清晰的模型; 更加体现了面向对象的编程思想,线程运行的代码也是对象(Runnable) 还可以从其他类继承; 保持程序风格的一致性。 直接继承Thread类 不能再继承其他类; 编写简单,可以直接操纵线程. 线程的使用细节: 1. 线程的启动使用父类的start()方法 2. 如果线程对象直接调用run(),那么JVN不会当作线程来运行,会认为是普通的方法调用 3. 匿名内部类的线程实现方式 4. 可以直接创建Thread类的对象并启动该线程,但是如果没有重写run(),什么也不执行。 5. 同一个线程的启动只能有一次,否则抛出异常 (IllegalThreadStateException) Exception in thread "main" thread1: i=1java.lang.IllegalThreadStateException 守护线程(后台线程) setDaemon (boolean on) 用法: 线程对象.setDaemon(true) 在启动线程之前调用 特点: 和其他线程一起等待CPU执行 当其他线程都结束后,后台线程默认结束 查看API文档 当正在运行的线程都是守护线程时,Java 虚拟机退出 该方法必须在启动线程前调用 线程的生命周期(lifecycle) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859package com.tu.mtar;public class ThreadAndRunnableDemo { // 主函数 public static void main(String[] args) { // 方式一 MyThread mt = new MyThread(); // 1.3.接下来可以分配并启动该子类的实例。 // mt.setDaemon(true);//设置成后台线程必须在开户线程之前 mt.start(); // 1.4.并调用实例的start()方法,start方法会调用run方法,如果直接调用run方法将不能实现多线程. // 方式二 MyRunnable mr = new MyRunnable(); // 2.3. 然后可以分配该类的实例, new Thread(mr).start(); // 2.4 在创建 Thread 时作为一个参数来传递并启动 for (int i = 0; i < 100; i++) { System.out.println("main -- i: " + i); } ro();// Override........ } protected static void ro() { // 当执行的线程的时候如果同时指定了Runnable的实现类和重写了Thread的run(), // 那么一定执行重写的run方法,而不是实现的run(); new Thread(new Runnable() { public void run() { System.out.println("Runnable........"); } }) { public void run() { System.out.println("Override........"); } }.start(); }}// 方式一class MyThread extends Thread // 1.1.将类声明为 Thread 的子类。(继承){ public void run() // 1.2.该子类应重写 Thread 类的 run 方法。 { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "==>MyThread: i :" + i); } }}// 方式 二class MyRunnable implements Runnable// 2.1. 声明实现 Runnable 接口的类。{ public void run() // 2.2. 该类然后实现 run 方法。 { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "==>MyRunnable -- i: " + i); } }} Thread常用方法类别方法签名简介线程的创建Thread() Thread(String name) Thread(Runnable tagrget) Thread(Runnable target,String name)线程的方法void start()启动线程 static void sleep(long millis)线程休眠 static void sleep(long millis , int nanos) void join()使其他线程等待当前线程终止 void join(long millis) void join(long missis,int nanos) static void yield()当前运行线程释放处理器资源获取线程引用static Thread currentThread()返回当前运行的线程引用 static yield() 暂停当前正在执行的线程对象,并执行其他线程 join() 调用这个方法的主线程,会等待加入的子线程完成 例: 其它线程.join(); 特点:当A线程的执行遇到了B线程的join方法,那么就挂起A线程,直到B线程运行完毕,再继续执行A线程。 getName() 返回线程的名字 setName(String name) 设置线程对象名 getId() 返回线程的标识 同一个线程对象的id不同 getPriority() 返回当前线程对象的优先级 默认线程的优先级是5 setPriority() 设置线程优先级当前线程对象的优先级 默认线程的优先级是5 setPriority(int newPriority) 设置线程的优先级 虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现 static int MAX_PRIORITY 线程可以具有的最高优先级。 max : 10 static int MIN_PRIORITY 线程可以具有的最低优先级。 min : 1 static int NORM_PRIORITY 分配给线程的默认优先级。 nor : 5 wait() 线程进入等待状态,等待被notify,这是对象方法,不是线程方法,要配合同步一起才能使用 notify() 唤醒其他的线程,这是一个对象方法,而不是线程方法,要配合同步一起才能使用 notifyAll() 唤醒其他的所有线程,这是一个对象方法,而不是线程方法,要配合同步一起才能使用 例:同步锁对象.notifyAll(); 线程同步(synchronized关键字)线程同步 同步就是协同步调,按预定的先后次序进行运行。 如:你说完,我再说。阻塞式的运行。 主要用来解决线程的安全问题 格式: synchronized (对象) { // 需要同步的代码; // 共享资源的操作 } 原理: 同步可以解决安全问题的根本原因就在那个对象上。 该对象如同锁的功能。 对象的标志位 线程同步特点 线程同步的前提: 同步需要两个或者两个以上的线程 多个线程使用的是同一个锁 线程同步的特点: 即使获取了CPU的时间片,没有对象锁也无法执行 单线程无需同步 线程同步缺点: 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。 同步函数: 使得整个函数加锁同步 实现 在函数上加上synchronized修饰符即可 如:public synchronized void run(){ } 创建锁对象: 如果加锁的代码中访问的是非静态变量,那么优先使用this关键字作为锁对象 如果加锁的代码中访问的是静态变量,那么优先使用所在类的字节码文件对应的Class对象作为锁对象 1. 类名.class 静态属性 2. 对象.getClass() 思考 同步函数用的是哪个锁呢? this 同步静态函数用的是哪个锁呢?Class 该方法所在的函数中的类的Class对象,类.class 多线程间数据通信其实就是多个线程在操作同一个资源,但是操作的动作不同.如果同时操作会出现错乱. wait();释放资源,释放锁。 notify();唤醒资源 notifyAll();唤醒全部资源 sleep():释放资源,不释放锁。 wait,notify,notifyAll都使用在同步中,因为要对持有监视器(锁)的线程操作所以要使用在同步中,因为只有同步才具有锁. 为什么这些操作线程的方法要定义Object类中呢? 因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程只有的锁. 只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒. 不可以对不同锁中的线程进行唤醒. 也就是说,等待和唤醒必须是同一个锁. 而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中. 案例: 输入名字,打印名字(两线程同步操作查询数据) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889package com.tu.mtar;//主线程,调用输入输出线程public class InputOutputDemo { public static void main(String[] args) { Res r = new Res(); Input in = new Input(r);// 3.然后可以分配该类的实例, Output out = new Output(r);// 输入与输出需要的是同一个对象r Thread t1 = new Thread(in);// 4.在创建 Thread 时作为一个参数来传递并启动 Thread t2 = new Thread(out); // 启动线程 t1.start(); t2.start(); }}class Res // 定义一个两个线程需要同时操作的数据{ String name; // 名字 未做封装,为了简化代码 String sex; // 性别 int index = 1; // 计数当前第几个客户ID boolean flag = false; // 定义一个标记}class Input implements Runnable// 1.声明实现 Runnable 接口的类。{ private Res r; Input(Res r) // 构造函数 { this.r = r; } public void run()// 2.该类然后实现 run 方法。 { int x = 0;// 用于让线程每次写入的数据不同而定的标记 for (int i = 0; i < 100; i++)// 循环写入100次数据 { synchronized (r)// 同步 使用的锁是main函数中传入的与输出线程同一个锁r, 锁也叫监视器 { if (r.flag)// 标记 如果我已经输入过了数据,那么:当前线程等待,并释放锁让被唤醒的线程可以拿到锁进行数据的查询. try { r.wait(); } catch (InterruptedException e) { }// (线程等待);线程池中休息,wait()会有(如果当前线程不是此对象监视器的所有者)InterruptedException异常 if (x == 0) { r.name = "mike"; r.sex = "man"; System.out.println("我输入了第" + r.index + "个客户,名字为:" + r.name + "性别为:" + r.sex + "的靓仔."); } else { r.name = "丽丽"; r.sex = "女女"; System.out.println("我输入了第" + r.index + "个客户,名字为:" + r.name + "性别为:" + r.sex + "的靓女."); } r.index++;// 只做计数 x = (x + 1) % 2;// 为了辨别每次输入的数据不同.也可以使用boolean的方法代替 r.flag = true;// 作标记,我已经输入过数据了. r.notify();// 唤醒再等待的最优先的线程, r为锁////notifyAll();全部叫醒线程池中的所有线程 } } }}// 输出线程class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } public void run() { for (int i = 0; i < 100; i++) { synchronized (r)// 同步 使用的锁是main函数中传入的与输入线程同一个锁r. { if (!r.flag)// 如果我已经查询过 , 当前线程等待,并释放锁 try { r.wait(); } catch (Exception e) { } System.out.println("I query the named : " + r.name + " , gender:" + r.sex + " customers . Id:" + (r.index - 1)); r.flag = false;// 标记已经查询过 r.notify();// 唤醒r中等待的线程. } } }} 案例: 多线程数据通信之生产者消费者例(大于两线程同步 操作查询数据) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495package com.tu.mtar;//对于多个生产者和消费者. 为什么要定义while判断标记. 原因:让被唤醒的线程再一次判断标记.//为什么定义notifyAll,因为需要唤醒对方线程. 因为只用notify,容易出现只唤醒本方线程的情况.导致程序中的所有线程都等待.public class ProducerConsumerDemo // 主线程main分别开启四个线程 两进 两出{ public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r);// 3. 然后可以分配该类的实例, Consumer con = new Consumer(r); Thread t1 = new Thread(pro);// 4 在创建 Thread 时作为一个参数来传递并启动 Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); // 启动线程. t1.start(); t2.start(); t3.start(); t4.start(); }}// 将生产线程与消费线程运行方式封装起来.class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { // if while (flag) try { wait(); } catch (Exception e) { } this.name = name + "--" + count++; System.out.println(Thread.currentThread().getName() + "...生产者.." + this.name); flag = true; // this.notify(); this.notifyAll(); } public synchronized void out()// 当消费者方法都是{}里的内容都是需要同步时可以将函数定义为同步,些时使用的锁是this { // if while (!flag) // 不断循环判断本线程是否将生产出来的东西消费了.当出现多个相同线程时.因为有可以再次出现消费线程. try { wait(); } catch (Exception e) { }// 如果已经消费或是又是消费线程(第二个消费线程),那么消费线程等待,并释放锁. System.out.println(Thread.currentThread().getName() + "...消费者...." + this.name); flag = false;// 更改标记,记录已经消费过. // this.notify();//当只有两线程时 this.notifyAll();// 多个线程最好是使用些方法.要不然会出现全部线程等待的问题 }}// 生产者线程class Producer implements Runnable// 1.声明实现 Runnable 接口的类{ private Resource res; Producer(Resource res) { this.res = res; } public void run()// 2. 该类实现 run 方法。 { while (true)// 循环生产,,,,注,如果使用的是控制台CMD窗口,可以使用Ctrl+C键停止. { res.set("+商品+");// 将需要运行的线程内容封装起来 } }}// 消费者线程class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while (true)// 循环消费,循环次数增加才看得出问题. { res.out();// 将需要运行的线程内容封装起来 } }} 线程同步死锁线程同步死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象, 若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 当多个线程完成功能需要同时获取多个共享资源的时候可能会导致死锁。 死锁无法解决,只能避免。 特定的资源分配方式才会导致线程的死锁,所以可以进行资源的分配前的死锁检测来避免死锁的发生。 预防死锁的算法: 银行家算法。 银行家算法 银行家算法是一种最有代表性的避免死锁的算法。在避免死锁方法中允许进程动态地申请资源, 但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。 算法原理 我们可以把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金, 进程向操作系统请求分配资源相当于用户向银行家贷款。 为保证资金的安全,银行家规定: (1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客; (2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量; (3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款; (4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金. 操作系统按照银行家制定的规则为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量, 如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。当进程在执行中继续申请资源时, 先测试该进程本次申请的资源数是否超过了该资源所剩余的总量。若超过则拒绝分配资源, 若能满足则按当前的申请量分配资源,否则也要推迟分配。 死锁案例 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package com.tu.mtar;public class DeadLockDemo { public static void main(String[] args) { //如果没有出现死锁,请多运行几次试试. DeadLock d = new DeadLock(); Thread th1 = new Thread(d, "张三"); Thread th2 = new Thread(d, "李四"); th1.start(); th2.start(); System.out.println("finish............."); }}// 出现死锁案例 死锁无法解决,只能避免。class DeadLock implements Runnable { // 定义资源 String controller = "遥控器"; String battery = "2节7号电池"; public void run() { // 需要抢资源 if (Thread.currentThread().getName().equals("张三")) { synchronized (controller) {// 同步嵌套 System.out.println("张三拿着遥控器,准备获取电池"); synchronized (battery) {// 同步嵌套 System.out.println("张三拿着遥控器,拿到电池"); System.out.println("张三看电视"); } } } else { synchronized (battery) {// 同步嵌套 System.out.println("李四拿着电池,准备获取遥控器"); synchronized (controller) { // 同步嵌套 System.out.println("李四拿着电池,拿到遥控器"); System.out.println("李四看电视"); } } } }} Demo下载本文案例Demo]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Thread</tag>
<tag>Runnable</tag>
<tag>多线程</tag>
<tag>死锁</tag>
<tag>synchronized</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java实现加密算法]]></title>
<url>%2F%2F2014%2F12%2F01%2FJava_security.html</url>
<content type="text"><![CDATA[目录: Base64 消息摘要算法 MD SHA MAC 对称加密算法 DES 3DES AES PBE 非对称加密算法 DH RSA ElGamal 数字签名算法 RSA DSA ECDSA 其它算法 IDEA CRC Base6412345678910111213141516171819202122232425262728public static void jdkBase64(String msg) { try { BASE64Encoder encoder = new BASE64Encoder(); String encode = encoder.encode(msg.getBytes()); System.out.println("encode:" + encode); BASE64Decoder decoder = new BASE64Decoder(); byte[] b = decoder.decodeBuffer(encode); System.out.println("decoder:" + new String(b)); } catch (Exception e) { }}public static void commonsCodesBase64(String msg) { byte[] encodeBytes = org.apache.commons.codec.binary.Base64.encodeBase64(msg.getBytes()); System.out.println("encode:" + new String(encodeBytes)); byte[] decodeBytes = org.apache.commons.codec.binary.Base64.decodeBase64(encodeBytes); System.out.println("decode:" + new String(decodeBytes));}public static void bouncyCastleBase64(String msg) { byte[] encodeBytes = org.bouncycastle.util.encoders.Base64.encode(msg.getBytes()); System.out.println("encode:" + new String(encodeBytes)); byte[] decodeBytes = org.bouncycastle.util.encoders.Base64.decode(encodeBytes); System.out.println("decode:" + new String(decodeBytes));} 消息摘要算法消息摘要算法 - MD算法: MD2 实现方:JDK MD4 实现方:BouncyCastle 简称BC MD5 实现方:JDK 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889public static void jdkMD2(String msg) { try { MessageDigest md = MessageDigest.getInstance("MD2"); byte[] md2Bytes = md.digest(msg.getBytes()); System.out.println("JDK MD2:" + org.apache.commons.codec.binary.Hex.encodeHexString(md2Bytes)); } catch (Exception e) { e.printStackTrace(); }}public static void jdkMD5(String msg) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] md5Bytes = md.digest(msg.getBytes()); System.out.println("JDK MD5:" + org.apache.commons.codec.binary.Hex.encodeHexString(md5Bytes)); } catch (Exception e) { e.printStackTrace(); }}// BouncyCastlepublic static void bcMD4(String msg) { try { Security.addProvider(new BouncyCastleProvider());// 为JDK设置算法 MessageDigest md = MessageDigest.getInstance("MD4"); byte[] md4Bytes = md.digest(msg.getBytes()); System.out.println("JDK MD5:" + org.apache.commons.codec.binary.Hex.encodeHexString(md4Bytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } Digest digest = new MD4Digest(); digest.update(msg.getBytes(), 0, msg.getBytes().length); byte[] md4Byte = new byte[digest.getDigestSize()]; digest.doFinal(md4Byte, 0); System.out.println("BC MD4: " + org.bouncycastle.util.encoders.Hex.toHexString(md4Byte));}// BouncyCastlepublic static void bcMD5(String msg) { Digest digest = new MD5Digest(); digest.update(msg.getBytes(), 0, msg.getBytes().length); byte[] md5Byte = new byte[digest.getDigestSize()]; digest.doFinal(md5Byte, 0); System.out.println("BC MD5: " + org.bouncycastle.util.encoders.Hex.toHexString(md5Byte));}//commons codecpublic static void ccMD5(String msg){ System.out.println("CC MD5:"+DigestUtils.md5Hex(msg.getBytes()));}//commons codecpublic static void ccMD2(String msg){ System.out.println("CC MD2:"+DigestUtils.md2Hex(msg.getBytes()));}//-----------------public static final String MD5_TYPE_16 = "t_16";public static final String MD5_TYPE_32 = "T_32";public static String md5s(String plainText, String type) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(plainText.getBytes()); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } String str = buf.toString(); if (type.equals(MD5_TYPE_16)) { str = str.substring(8, 24); } return str; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null;} 消息摘要算法 - SHA安全散列算法 固定长度摘要信息 SHA-1 SHA-2(SHA-224,SHA-256,SHA-384,SHA-512) 算法: SHA-1 实现方:JDK SHA-224 实现方:BC SHA-256 实现方:JDK SHA-384 实现方:JDK SHA-512 实现方:JDK 123456789101112131415161718192021222324252627282930313233343536373839404142public static void jdkSHA1(String msg) { try { MessageDigest md = MessageDigest.getInstance("SHA");// SHA1 md.update(msg.getBytes()); System.out.println("jdk SHA-1:" + Hex.encodeHexString(md.digest())); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }}public static void bcSHA1(String msg) { Digest digest = new SHA1Digest(); digest.update(msg.getBytes(), 0, msg.getBytes().length); byte[] sha1Byte = new byte[digest.getDigestSize()]; digest.doFinal(sha1Byte, 0); System.out.println("bc SHA-1:" + org.bouncycastle.util.encoders.Hex.toHexString(sha1Byte));}public static void bcSHA224(String msg) { Digest digest = new SHA224Digest(); digest.update(msg.getBytes(), 0, msg.getBytes().length); byte[] sha224Byte = new byte[digest.getDigestSize()]; digest.doFinal(sha224Byte, 0); System.out.println("bc SHA-224:" + org.bouncycastle.util.encoders.Hex.toHexString(sha224Byte));}public static void jdkSHA224_2(String msg) { try { Security.addProvider(new BouncyCastleProvider());// 设置 MessageDigest md = MessageDigest.getInstance("SHA224");// SHA224 md.update(msg.getBytes()); System.out.println("jdk addProvider ->SHA-224:" + Hex.encodeHexString(md.digest())); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }}public static void ccSHA1(String msg) { System.out.println("cc sha1:" + DigestUtils.sha1Hex(msg.getBytes())); System.out.println("cc sha512:" + DigestUtils.sha512Hex((msg.getBytes())));} 消息摘要算法 - MACMAC(Message Authentication Code) HMAC(keyed-Hash Message Authentication Code),含有密钥的散列函数算法 任何MD,SHA -MD系列:HmacMD2 , HmacMD4 , HmacMD5 -SHA系列: HmacSHA1, HmacSHA224,HmacSHA256,HmacSHA384,HmacSHA512 算法: HmacMD2 实现方:BC HmacMD4 实现方:BC HmacMD5 实现方:JDK HamcSHA1 实现方:JKD HmacSHA224 实现方:BC HmacSHA256 实现方:JKD HmacSHA384 实现方:JKD HmacSHA512 实现方:JKD 其它消息摘要算法 RipeMD Tiger Whirlpool GOST3411 Bouncy Castle实现 1234567891011121314151617181920212223242526272829303132public static void jdkHmacMD5(String msg, String keyParameter) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5"); SecretKey secretKey = keyGenerator.generateKey();// 产生密钥 byte[] key = secretKey.getEncoded();// 获取密钥 key = Hex.decodeHex(keyParameter.toCharArray());// // 还原密钥 SecretKey restoreSecretKey = new SecretKeySpec(key, "HmacMD5"); Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());// 实例化MAC mac.init(restoreSecretKey); byte[] hmacMD5Bytes = mac.doFinal(msg.getBytes());// 执行摘要 System.out.println("jdk hmacMD5:" + Hex.encodeHexString(hmacMD5Bytes)); } catch (Exception e) { e.printStackTrace(); }}public static void bcHmacMD5(String msg, String keyParameter) { HMac hmac = new HMac(new MD5Digest()); byte[] b = org.bouncycastle.util.encoders.Hex.decode(keyParameter.getBytes()); KeyParameter k = new KeyParameter(b); hmac.init(k); hmac.update(msg.getBytes(), 0, msg.getBytes().length); byte[] hmacMD5Bytes = new byte[hmac.getMacSize()]; hmac.doFinal(hmacMD5Bytes, 0); System.out.println("bc hmacMD5:" + org.bouncycastle.util.encoders.Hex.toHexString(hmacMD5Bytes));} 对称加密算法对称加密算法 - DEC - 3DEC初等 DES -3DES AES PBE IDEA DES(Data Encryptiong Standard) 数据加密标准 对称加密元老. 密钥长度56 默认56 工作模式:ECB,CBC,PCBC,CTR,CTS,CFB,CFB8,到128,OFB,OFB8到128 实现方:JDK 填充方式:NoPadding,PKCS5Padding,ISO10126Padding, 实现方:BC 填充方式:PKCS7Padding,ISO10126d2Padding,X932Padding,ISO7816d4Pading,ZeroBytePading 3DES(Triple DES或者DESede) 密钥长度112,168 默认168 密钥长度128,192 默认168 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687public static void jdkDES(String src) { try { // 生成KEY KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); keyGenerator.init(56); SecretKey secretKey = keyGenerator.generateKey(); byte[] bytesKey = secretKey.getEncoded(); // KEY转换 DESKeySpec desKeySpec = new DESKeySpec(bytesKey); SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); Key convertSecretKey = factory.generateSecret(desKeySpec); // 加密 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk des encrypt : " + Hex.encodeHexString(result)); // 解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println("jdk des decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }}public static void bcDES(String src) { try { Security.addProvider(new BouncyCastleProvider()); // 生成KEY KeyGenerator keyGenerator = KeyGenerator.getInstance("DES", "BC"); keyGenerator.getProvider(); keyGenerator.init(56); SecretKey secretKey = keyGenerator.generateKey(); byte[] bytesKey = secretKey.getEncoded(); // KEY转换 DESKeySpec desKeySpec = new DESKeySpec(bytesKey); SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); Key convertSecretKey = factory.generateSecret(desKeySpec); // 加密 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("bc des encrypt : " + Hex.encodeHexString(result)); // 解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println("bc des decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }}public static void jdk3DES(String src) { try { // 生成KEY KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede"); // keyGenerator.init(168); keyGenerator.init(new SecureRandom());// 默认长度 SecretKey secretKey = keyGenerator.generateKey(); byte[] bytesKey = secretKey.getEncoded(); // KEY转换 DESKeySpec desKeySpec = new DESKeySpec(bytesKey); SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); Key convertSecretKey = factory.generateSecret(desKeySpec); // 加密 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk 3des encrypt : " + Base64.encodeBase64String(result)); // 解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println("jdk 3des decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }} 对称加密算法 - AESDES替代者 密钥长度:128,192,256 默认128 工作模式:ECB,CBC,PCBC,CTR,CTS,CFB,CFB8到128,OFB,OFB8到128 填充方式:NoPadding,PKCS5Padding,ISO10126Padding 实现方:JDK(256位密钥需要获得无政策限制权限文件) 填充方式:PKCS7Padding,ZeroBytePadding 实现方:BC 无政策限制权限文件是指,因为某些国家的进口管制限制,Java发布的远行环境包中的加解密有一定的限制 12345678910111213141516171819202122232425public static void jdkAES(String src) { try { // 生成KEY KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); // key转换 Key key = new SecretKeySpec(keyBytes, "AES"); // 加密 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk aes encrypt : " + Base64.encodeBase64String(result)); // 解密 cipher.init(Cipher.DECRYPT_MODE, key); result = cipher.doFinal(result); System.out.println("jdk aes desrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }} 对称加密算法 - PBEPBE(Password Based Encryption)基于口令加密 算法 密钥长度 默认 工作模式 填充方式 实现 PBEWithMD5AndDES 64 64 CBC PKCS5Padding BC PBEWithMd5AndRC2 112 128 PKCS7Padding PBEWithSHA1AndDES 6 64 ISO10126Padding PBEWithSHAAndIDEA-CBC 128 128 PBEWithSHAAnd2-KeyTripleDES-CBC 128 128 PBEWithSHAAnd3-KeyTripleDES-CBC 192 192 12345678910111213141516171819202122232425262728public static void jdkPBE(String src) { try { // 初始化盐 SecureRandom random = new SecureRandom(); byte[] salt = random.generateSeed(8); // 口令与密钥 String password = "password_comtu"; PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHMD5andDES"); Key key = factory.generateSecret(pbeKeySpec); // 加密 PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 100); Cipher cipher = Cipher.getInstance("PBEWITHMD5andDES"); cipher.init(Cipher.ENCRYPT_MODE, key, pbeParameterSpec); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk pbe encrypt : " + Base64.encodeBase64String(result)); // 解密 cipher.init(Cipher.DECRYPT_MODE, key, pbeParameterSpec); result = cipher.doFinal(result); System.out.println("jdk pbe decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }} 非对称加密算法非对称加密算法 - DH高级 双保险 公钥,私钥 DH(Diffie-Hellman)密钥交换算法 RSA-基于因子分解 ElGamal - 基于离散对数 ECC(Elliptical Curve Cryptography) - 椭圆曲线加密 DH 密钥长度: 512~1024(64倍数) 默认1024 实现方:JDK 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354public static void jdkDH(String src) { try { // 1.初始化发送方密钥 KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("DH"); senderKeyPairGenerator.initialize(512); KeyPair senderKeyPair = senderKeyPairGenerator.generateKeyPair(); byte[] senderPublicKeyEnc = senderKeyPair.getPublic().getEncoded();// 发送方公钥,发送给接收方(网络、文件。。。) // 2.初始化接收方密钥 KeyFactory receiverKeyFactory = KeyFactory.getInstance("DH"); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKeyEnc); PublicKey receiverPublicKey = receiverKeyFactory.generatePublic(x509EncodedKeySpec); DHParameterSpec dhParameterSpec = ((DHPublicKey) receiverPublicKey).getParams(); KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("DH"); receiverKeyPairGenerator.initialize(dhParameterSpec); KeyPair receiverKeypair = receiverKeyPairGenerator.generateKeyPair(); PrivateKey receiverPrivateKey = receiverKeypair.getPrivate(); byte[] receiverPublicKeyEnc = receiverKeypair.getPublic().getEncoded(); // 3.密钥构建 KeyAgreement receiverKeyAgreement = KeyAgreement.getInstance("DH"); receiverKeyAgreement.init(receiverPrivateKey); receiverKeyAgreement.doPhase(receiverPublicKey, true); SecretKey receiverDesKey = receiverKeyAgreement.generateSecret("DES"); KeyFactory senderKeyFactory = KeyFactory.getInstance("DH"); x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKeyEnc); PublicKey senderPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec); KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("DH"); senderKeyAgreement.init(senderKeyPair.getPrivate()); senderKeyAgreement.doPhase(senderPublicKey, true); SecretKey senderDesKey = senderKeyAgreement.generateSecret("DES"); // if (com.sun.org.apache.xalan.internal.utils.Objects.equals(receiverDesKey, senderDesKey)) { if (receiverDesKey.equals(senderDesKey)) { System.out.println("双方密钥相同"); } else { System.out.println("双方密钥不同"); } // 4.加密 Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, senderDesKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk dh encrypt : " + Base64.encodeBase64String(result)); // 5.解密 cipher.init(Cipher.DECRYPT_MODE, receiverDesKey); result = cipher.doFinal(result); System.out.println("jdk dh decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); }} 非对称加密算法 - 数字签名算法 - RSA经典算法 MD,SHA两类 算法: MD2withRSA 实现方: JDK MD5withRSA 实现方: JDK SHA1withRSA 实现方: JDK SHA224withRSA 实现方: BC SHA256withRSA 实现方: BC SHA384withRSA 实现方: BC SHA512withRSA 实现方: BC RIPEMD128withRSA 实现方: BC RIPEMD160withRSA 实现方: BC 非对称加密算法-RSA 唯一广泛接受并实现 数据加密&数字签名 公钥加密,私钥解密 私钥加密,公钥解密 JDK 密钥长度:512~65536(64整数倍) 工作模式:ECB 默认长度:1024 填充方式: NoPadding PKCS1Padding OAEPWITHMD5AndMGF1Pading OAEPWITHSHA1AndMGF1Pading OAEPWITHSHA256AndMGF1Pading OAEPWITHSHA384AndMGF1Pading OAEPWITHSHA512AndMGF1Pading BC 密钥长度:512~65536(64整数倍) 工作模式:NONE 默认长度:2048 填充方式: NoPadding PKCS1Padding OAEPWITHMD5AndMGF1Pading OAEPWITHSHA1AndMGF1Pading OAEPWITHSHA224AndMGF1Pading OAEPWITHSHA256AndMGF1Pading OAEPWITHSHA384AndMGF1Pading OAEPWITHSHA512AndMGF1Pading ISO9796-1Padding 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283public static void jdkRSA(String msg) { try { // 1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); // 2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(msg.getBytes()); byte[] result = signature.sign(); System.out.println("jdk rsa sign: " + Hex.encodeHexString(result)); // 3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("MD5withRSA"); signature.initVerify(publicKey); signature.update(msg.getBytes()); boolean bool = signature.verify(result); System.out.println("jdk rea verify:" + bool); } catch (Exception e) { }}public static void jdkRSA2(String src) { try { // 1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); System.out.println("Public Key : " + Base64.encodeBase64String(rsaPublicKey.getEncoded())); System.out.println("Private Key : " + Base64.encodeBase64String(rsaPrivateKey.getEncoded())); // 2.私钥加密、公钥解密——加密 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("私钥加密、公钥解密——加密 : " + Base64.encodeBase64String(result)); // 3.私钥加密、公钥解密——解密 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, publicKey); result = cipher.doFinal(result); System.out.println("私钥加密、公钥解密——解密:" + new String(result)); // 4.公钥加密、私钥解密——加密 x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); publicKey = keyFactory.generatePublic(x509EncodedKeySpec); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); result = cipher.doFinal(src.getBytes()); System.out.println("公钥加密、私钥解密——加密 : " + Base64.encodeBase64String(result)); // 5.公钥加密、私钥解密——解密 pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); result = cipher.doFinal(result); System.out.println("公钥加密、私钥解密——解密:" + new String(result)); } catch (Exception e) { e.printStackTrace(); }} 非对称加密算法 - ElGamal 公钥加密算法 实现方:BC 密钥长度:160~16384(8的整数倍) , 默认:1024 工作模式:ECB,NONE 填充方式: NoPadding , PKCS1Padding OAEPWITHMD5AndMGF1Pading OAEPWITSHA1AndMGF1Pading OAEPWITSHA224AndMGF1Pading OAEPWITSHA256AndMGF1Pading OAEPWITSHA384AndMGF1Pading OAEPWITSHA512AndMGF1Pading ISO9796-1Padding 12345678910111213141516171819202122232425public static void bcElGamal(String src) { try { // 公钥加密,私钥解密 Security.addProvider(new BouncyCastleProvider()); // 1.初始化密钥 AlgorithmParameterGenerator algorithmParameterGenerator = AlgorithmParameterGenerator.getInstance("ElGamal"); algorithmParameterGenerator.init(256); AlgorithmParameters algorithmParameters = algorithmParameterGenerator.generateParameters(); DHParameterSpec dhParameterSpec = (DHParameterSpec) algorithmParameters.getParameterSpec(DHParameterSpec.class); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ElGamal"); keyPairGenerator.initialize(dhParameterSpec, new SecureRandom()); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PublicKey elGamalPublicKey = keyPair.getPublic(); PrivateKey elGamalPrivateKey = keyPair.getPrivate(); System.out.println("Public Key:" + org.apache.commons.codec.binary.Base64.encodeBase64String(elGamalPublicKey.getEncoded())); System.out.println("Private Key:" + org.apache.commons.codec.binary.Base64.encodeBase64String(elGamalPrivateKey.getEncoded())); } catch (Exception e) { e.printStackTrace(); }} 数字签名算法数字签名算法 - DSADSS (Digital Signature Standard)数字签名标准 DSA(Digital Signature Algorithm)数字签名算法 DSA仅包含数字签名 算法: SHA1withDSA 实现方:JDK SHA224withDSA 实现方:BC SHA256withDSA 实现方:BC SHA384withDSA 实现方:BC SHA512withDSA 实现方:BC 1234567891011121314151617181920212223242526272829303132public static void jdkDSA(String src) { try { // 1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); DSAPublicKey dsaPublicKey = (DSAPublicKey) keyPair.getPublic(); DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) keyPair.getPrivate(); // 2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(dsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("DSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("SHA1withDSA"); signature.initSign(privateKey); signature.update(src.getBytes()); byte[] result = signature.sign(); System.out.println("jdk dsa sign : " + Hex.encodeHexString(result)); // 3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(dsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("DSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("SHA1withDSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(result); System.out.println("jdk dsa verify : " + bool); } catch (Exception e) { e.printStackTrace(); }} 数字签名算法 - ECDSA 微软 Elliptic Curve Digital Signature Algorithm , 椭圆曲线数字签名算法 速度快,强度高,签名短. 算法: NNONEwithECDSA 实现方:JDK/BC 签名长度:128 RIPEMD160withECDSA 实现方:BC 签名长度:160 SHA1withECDSA 实现方:JDK/BC 签名长度:160 SHA224withECDSA 实现方:BC 签名长度:224 SHA256withECDSA 实现方:JDK/BC 签名长度:256 SHA384withECDSA 实现方:JDK/BC 签名长度:384 SHA512withECDSA 实现方:JDK/BC 签名长度:512 jdk版本1.7以上 123456789101112131415161718192021222324252627282930313233public static void jdkECDSA(String src) { try { //1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); keyPairGenerator.initialize(256); KeyPair keyPair = keyPairGenerator.generateKeyPair(); ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic(); ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate(); //2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(ecPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("EC"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("SHA1withECDSA"); signature.initSign(privateKey); signature.update(src.getBytes()); byte[] result = signature.sign(); System.out.println("jdk ecdsa sign : " + Hex.encodeHexString(result)); //3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(ecPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("EC"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("SHA1withECDSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(result); System.out.println("jdk ecdsa verify : " + bool); } catch (Exception e) { e.printStackTrace(); } } 其它算法IDEA这种算法是在DES算法的基础上发展出来的,类似于三重DES。 发展IDEA也是因为感到DES具有密钥太短等缺点。IDEA的密钥为128位,这么长的密钥在今后若干年内应该是安全的。 12345678910111213141516171819202122232425262728public static void bcIDEA(String src) { try { Security.addProvider(new BouncyCastleProvider()); //生成key KeyGenerator keyGenerator = KeyGenerator.getInstance("IDEA"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); //转换密钥 Key key = new SecretKeySpec(keyBytes, "IDEA"); //加密 Cipher cipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("bc idea encrypt : " + Base64.encodeBase64String(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, key); result = cipher.doFinal(result); System.out.println("bc idea decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); } } CRC123456public static void jdkCrc(String src){ CRC32 crc = new CRC32(); crc.update(src.getBytes()); String hex = Long.toHexString(crc.getValue()); System.out.println("jdk crc32 : " + hex);} 运行1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859public static void main(String[] args) { //AES jdkAES("comtu"); //Base64 jdkBase64("comtu"); commonsCodesBase64("comtu"); bouncyCastleBase64("comtu"); //DES jdkDES("comtu"); bcDES("comtu"); jdk3DES("comtu"); //DH jdkDH("comtu"); //DSA jdkDSA("comtu"); //ECDSA jdkECDSA("comtu"); //ElGamal bcElGamal("comtu"); //HMAC jdkHmacMD5("comtu", "1234567890abcdef"); bcHmacMD5("comtu", "1234567890abcdef"); //IDEA bcIDEA("comtu"); //md System.out.println("MD5_TYPE_16:" + Md.md5s("comtu", MD5_TYPE_16)); System.out.println("MD5_TYPE_32:" + Md.md5s("comtu", MD5_TYPE_32)); jdkMD2("comtu"); jdkMD5("comtu"); bcMD4("comtu"); bcMD5("comtu"); ccMD2("comtu"); ccMD5("comtu"); //PBE jdkPBE("comtu"); //RSA jdkRSA("comtu"); jdkRSA2("comtu"); //SHA jdkSHA1("comtu"); bcSHA1("comtu"); bcSHA224("comtu"); jdkSHA224_2("comtu"); ccSHA1("comtu");} 案例引用如下三个jar包: bcprov-ext-jdk15on-149.jar bcprov-jdk15on-149.jar commons-codec-1.10.jar 代码引用包详情见原代码: Demo下载本文Demo源代码]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Base64</tag>
<tag>AES</tag>
<tag>CRC</tag>
<tag>DES</tag>
<tag>DH</tag>
<tag>DSA</tag>
<tag>ECDSA</tag>
<tag>ElGamal</tag>
<tag>HMAC</tag>
<tag>IDEA</tag>
<tag>MD</tag>
<tag>PBE</tag>
<tag>RSA</tag>
<tag>SHA</tag>
<tag>加密</tag>
<tag>解密</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hexo笔记]]></title>
<url>%2F%2F2014%2F11%2F15%2Fhexo-blog.html</url>
<content type="text"><![CDATA[使用Hexo在github上搭建blog的操作笔记. 创建我的博客 创建仓库, comtu.github.io; 创建两个分支:master 与 dev ; 设置dev为默认分支(因为我们只需要手动管理这个分支上的Hexo网站文件);项目–>Settings–>Branches–>Default Branch–>切换默认分支 使用git clone git@github.com:comtu/comtu.github.io.git 拷贝仓库;没有设置默认分支则使用: git git@github.com:comtu/comtu.github.io.git -d dev 在本地comtu.github.io文件夹下通过Git bash依次执行npm install hexo、hexo init、npm install和 npm install hexo-deployer-git(此时当前分支应显示为dev); 修改_config.yml中的deploy参数,分支应为master; 在此期间可以进行本地预览: hexo clean #清除项目 hexo g #生成html hexo s #开启本地服务 通过:http://localhost:4000/ 访问本地生成的效果.如果效果满意再如下操作. 依次执行git add .、git commit -m "update xxx"、git push origin dev 提交网站相关的文件; 执行hexo g ; hexo d 生成网站并部署到GitHub上。 这样一来,在GitHub上的comtu.github.io仓库就有两个分支,一个hexo分支用来存放网站的原始文件,一个master分支用来存放生成的静态网页。 日常操作在本地对博客进行修改(添加新博文、修改样式等等)后,通过下面的流程进行管理: 依次执行git add .、git commit -m "update xxx"、git push origin dev指令将改动推送到GitHub(此时当前分支应为dev); 然后才执行hexo g -d发布网站到master分支上。 更换电脑操作当重装电脑之后,或者想在其他电脑上修改博客,可以使用下列步骤: 使用git clone git@github.com:comtu/comtu.github.io.git 拷贝仓库(默认分支为dev); 在本地新拷贝的comtu.github.io文件夹下通过Git bash依次执行下列指令:npm install hexo、npm install、npm install hexo-deployer-git(不需要hexo init这条指令)。 常用命令12345678hexo clean #清除项目hexo new page "pageName" #新建页面hexo help #查看帮助hexo version #查看Hexo的版本hexo n == hexo new "postName" #新建文章hexo g == hexo generate #生成静态页面至public目录hexo s == hexo server #开启本地服务(默认端口4000,'ctrl + c'关闭server)hexo d == hexo deploy #将.deploy目录部署到GitHub 1234567#日常工作:hexo clean #清除项目hexo g #生成htmlhexo s #开启本地服务hexo d #部署到github 我的博客使用到的插件123456789101112131415$ npm ls --depth 0hexo-site@0.0.0 E:\c+-- hexo@3.3.9+-- hexo-deployer-git@0.1.0+-- hexo-generator-archive@0.1.4+-- hexo-generator-category@0.1.3+-- hexo-generator-index@0.2.0+-- hexo-generator-searchdb@1.0.8+-- hexo-generator-sitemap@1.1.2+-- hexo-generator-tag@0.2.0+-- hexo-renderer-ejs@0.2.0+-- hexo-renderer-marked@0.2.11+-- hexo-renderer-stylus@0.3.1+-- hexo-server@0.2.0`-- hexo-wordcount@3.0.2 #Node 版本7.6.0之前,请安装 2.x 版本 npm install hexo-wordcount@2 --save 其它常用功能:123456789101112 #更新hexo到最新版 注意:如果使用苹果电脑则需要加 sudo npm update hexo -g 获取权限安装,区间输入密码.$ npm update hexo -g#显示项目的所有插件列表$ npm ls --depth 0 # 安装插件 如: npm install hexo-generator-sitemap --save$ npm install xxx插件名称 --save # 卸载插件$ npm uninstall hexo-generator-sitemap --save]]></content>
<categories>
<category>Github</category>
</categories>
<tags>
<tag>Github</tag>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JNI]]></title>
<url>%2F%2F2014%2F11%2F01%2FJava_JNI.html</url>
<content type="text"><![CDATA[h3 { line-height: 1; letter-spacing: 2px; margin-top: 5px; } h6 { line-height: 1; letter-spacing: 2px; margin-top: 5px; } 1.JNI简介 2.最简单的Java调用C/C++代码的步骤 3.在C/C++本地代码中访问Java代码简介 3.1 JNIEnv 类型 3.2 jobject 3.3 jclass 3.4 Java的类型在C/C++中的映射关系 4.在C/C++本地代码中访问Java类中的属性与方法 4.1 jfieldID/jmethodID 4.2 Sign签名 4.3 使用Javap命令来产生Sign签名 4.4 取得Java属性/设定Java属性值 4.5 Java方法的调用 5.在C/C++本地代码中创建Java的对象 5.1 NewObject 5.2 AllocObject 6.在C/C++本地代码中访问/创建Java的String字符串对象 6.1 GetStringChars / GetStringUTFChars / ReleaseStringChars / ReleaseStringUTFChars 6.2 GetStringCritical / ReleaseStringCritical 6.3 GetStringRegion / GetStringUTFRegion 6.4 字符串相关函数 7.在C/C++本地代码中操作Java的数组对象 7.1处理基本类型数组 7.2处理对象类型数组 8.全局引用/局部引用/弱全局引用 8.1局部引用 8.2全局引用 8.2全局引用 8.4关于引用的一些函数 9.JNI优化 JNI简介Java是跨平台的语言,但是在有些时候仍然是有需要调用要地代码(这些代码通常是由C/C++编写).原Sun公司现Oracle公司提供的JNI是Java平台的一个功能强大的接口.这个JNI接口提供了Java与操作系统本地代码互相调用的功能. 最简单的Java调用C/C++代码的步骤:1.首先在Java类中声明一个native的方法 2.使用javah命令生成包含native方法声明的C/C++头文件 3.按照生成的C/C++头文件来写C/C++源文件. 4.将C/C++源文件编译成动态连接库(DLL , OS) 5.把DLL文件加入到Path环境变量下. 6.Java类中加载DLL,然后调用声明的native方法 在C/C++本地代码中访问Java代码简介在被调用的C/C++函数中也可以反过来访问Java程序中的类javah工具生成的C/C++函数声明中,可以看到有头两个参数 JNIEnv,jobject/jclass package com.tu.hellojni.jni; public class Port { public native String sayHello();//非静态函数 public static native void sayHi(String str);//静态函数 }1234```c JNIEXPORT jstring JNICALL Java_com_tu_hellojni_jni_Port_sayHello (JNIEnv *, jobject); JNIEXPORT void JNICALL Java_com_tu_hellojni_jni_Port_sayHi (JNIEnv *, jclass, jstring); JNIEnv 类型 JNIEnv类型实际上代表了Java环境,通过这个JNIEnv*指针,就可以对Java端的代码进行操作. 例如,创建Java类的对象,调用Java对象的方法,镬取Java对象的属性等等. JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作. JNIEnv类中有很多函数可以用: NewObject/NewString/New<TYPE>Array 创建一个对象,创建一个字符串, 创建一个对应类型的数组 Get/Set<TYPE>Field 获取/设置 某个类的属性 Get/SetStatic<TYPE>Field 获取/设置某个类的静态属性 Call<TYPE>Method/CallStatic<TYPE>Method 调用某一个类里的方法/调用某一个类的静态方法 等等的函数 jobject jobject 指向Java对象的实例 , 非静态函数中时会有jobject参数.这个jobject指向的是如: Port p = new Port; 所指向的就是p对象. jclass jclass 指向的是代表那个类的class对象. 静态函数中会有jclass参数.这个jclass指向的是如: Port.sayHi(“hi”); 所指向的是Port.class对象. jclass的取得 为了能够在C/C++中使用Java类.JNI.h头文件中专门定义了jclass类型来表示java中的Class类 JNIEnv类中有如下几个简单的函数可以取得jclass: jclass FindClass(const char* clsName); 通过完整的类名取得Class jclass GetObjectClass(jobject obj); 通过对象获取到class对象. 类似于 Java中的 Object中getClass方法 jclass GetSuperClass(jclass obj); 通过当前jclass获取到父类的class 其中FindClass 会在classPath系统环境变量下寻找类. 传入完整类名,注意包与包之间是用"/"而不是"."来分隔. 如: jclass cls_string = env->FindClass("java/lang/String"); Java的类型在C/C++中的映射关系 Java类型 本地类型 JNI定义的别名 int long jint/jsize long __int64 jlong byte signed char jbyte boolean uncigned char jboolean char unsigned short jchar short short jshort float float jfloat double double jdouble object _jobject* jobject 在C/C++本地代码中访问Java类中的属性与方法jfieldID/jmethodID 在C/C++本地代码中访问Java端的代码,一个常见的应用就是获取类的属性和调用类的方法,为了在C/C++中表示属性和方法,JNI在Jni.h头文件中定义了jfieldID,JmethodID类型来分别代表Java类的属性和方法. 我们在访问,或是设置Java属性的时候,首先就要先在本地代码获取代表该Java属性的jfieldID, 然后才能在本地代码进行Java属性操作. 同样的,我们需要呼叫Java端的方法时,也是需要取得代表该方法的jmethodID才能进行Java方法调用. 使用JNIEnv的 GetFieldID/GetMethodID 取得属性/方法ID ( jfieldID / jmethodID) GetStaticFieldID/GetStaticMethodID 取得静态属性/方法ID ( jfieldID / jmethodID ) 来取得相应的jfieldID和jmethodID 1234jfieldID GetFieldID(jclass clazz,const char* name,const char* sign);jfieldID GetStaticFieldID(jclass clazz,const char* name,const char* sign);jmethodID GetMethodID(jclass clazz,const char* name,const char* sign);jmethodID GetStaticMethodID(jclass clazz,const char* name,const char* sign); GetMethodID也能取得构造函数的jmethodID. 创建一个Java对象时可以调用 指定的构造方法,这个将在下面的时候做介绍. 如:env->GetMethodID(data_Clazz,"<init>","()V"); 类似Java的Reflect(反射机制)需要指定类跟属性/方法我哑取得相应的jfieldID跟jmethodID. 方法参数: jclass clazz调用指定某个类. const char* name常量字符串,代表属性或者方法的名称. const char* sign是当类中有两个重载方法时签名. 例如: TestNative类中有两个重载方法: 12345678910//Javapackage com.tu.hellojni.jni;public class TestNative { public void function(int i) { System.out.println("TestNative.function(int i)" + i); } public void function(double d) { System.out.println("TestNative.function(double d)" + d); }} 然后在C/C++代码中需要调用其中一个function方法的话. //首先取得要调用的方法所在的类 jclass clazz_TestNative = env->FindClass("com/tu/hellojni/jni/TestNative"); //取得jmethodID之后才进行调用 jmethodID id_func = env->GetMethodID(clazz_testNative,"function","??????"); 但是到底取得的是 void function(int i )还是 void function(double d)的jmethodID呢? 这就是sign的作用了.它用于指定要取得的属性/方法类型. 这里的sign如果指定为"(I)V"则取回void function(int)的jmethodID 如果指定为(D)V"则取回void function(double)的jmethodID Sign签名用来表示要取得的属性/方法的类型 类型 相应的签名 boolean Z byte B char C short S int I long J float F double D void V object L用/分隔包的完整类名: Ljava/lang/String; Array [签名 如[I 整形数组 如[Ljava/lang/Object; 对象数组 Method (参数1类型签名,参数2类型签名…)返回值类型签名 如:(ILjava/util/Date;[I)I 使用Javap命令来产生Sign签名从上面代码中的签名可以看出.如果是比较复杂的签名编写起来还是比较麻烦的. 所以JDK也提供了一个工具javap来查看一个类的声明,其中就可以设置输出每个方法/属性的签名. 语法: javap -s -p [full Class Name] -s 表示输出签名信息 -p 同-private,输出包含private 访问权限的成员信息 使用方法: 1.cmd定位到工程目录下. 2.使用javap输入信息. C:\Users\ComTu>cd E:\ComTu_Design\workspace\workspace-Android\HelloJ C:\Users\ComTu>E: E:\ComTu_Design\workspace\workspace-Android\HelloJNI>cd bin/classes E:\ComTu_Design\workspace\workspace-Android\HelloJNI\bin\classes>jav Compiled from "Port.java" public class com.tu.hellojni.jni.Port extends java.lang.Object{ public int property; Signature: I public com.tu.hellojni.jni.Port(); Signature: ()V public native java.lang.String sayHello(); Signature: ()Ljava/lang/String; public static native void sayHi(java.lang.String); Signature: (Ljava/lang/String;)V public native void testSign(); Signature: ()V public void functionOverride(); Signature: ()V public void functionOverride(int); Signature: (I)V public int function(int, java.util.Date, int[]); Signature: (ILjava/util/Date;[I)I } 1234567891011121314151617181920212223242526272829303132[在Eclipse中配置Javap快捷操作](/blog/2014/10/28/Android_NDK_IDE_environment_one.html)--- ## 取得Java属性/设定Java属性值 取得了相应属性的 jfieldID 之后就可以用 Set<TYPE>Field Get<TYPE>Field SetStatic<TYPE>Filed GetStatic<TYPE>File等函数来对Java属性进行操作了. -->TYPE泛指各种数据类型. 如: Boolean , Byte , Double , Float , Int , Long , Short , Object 怎样获取数组属性呢? 可以使用GetObjectField来取得数组类型的属性. 案例:```java //Java public class Port{ /** C进行get/set成员变量 */ public int property = 55; /** C进行Get/Set静态成员变量 */ public static boolean isShow = false; } 12345678910111213141516//Cjclass port_clazz = env->GetObjectClass(obj);//获取java成员变量//public int property;jfieldID fieldId_prop = env->GetFieldID(port_clazz, "property", "I");jint prop = env->GetIntField(obj, fieldId_prop);//取值__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-int: %d", prop);//给java成员变量赋值env->SetIntField(obj, fieldId_prop, 100L);//赋值100L表示c中的100常整数类型.//获取静态变量jfieldID fieldId_isShow = env->GetStaticFieldID(port_clazz, "isShow", "Z");jboolean jb = env->GetStaticBooleanField(port_clazz, fieldId_isShow);__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-boolean: %d", jb);jboolean b = !jb;env->SetStaticBooleanField(port_clazz, fieldId_isShow, b);//给静态变量赋值 Java方法的调用JNIEnv 提供了众多的Call<TYPE>Method 跟 CallStatic<TYPT>Method,还有 CallNonvirtual<TYPE>Method函数.需要通过 GetMethodID 取得相应方法的 jmethodID来 传入到函数参数中. 调用实例方法的三种形式: //第一种:最常用的方式 Call<TYPE>Method(jobject obj,jmethodID id,...); //第二种:当调用这个函数的时候有一个指向参数表的va_list变量时使用的_很少使用到这种方式 Call<TYPE>MethodV(jobject obj,jmethodID id, va_list lst); //第三种:当调用这个函数的时候有一个指向jvalue或者jvalue数组的指针时用的 Call<TYPE>MethodA(jobject obj,jmethodID id, jvalue * v); 调用静态方法的三种形式: CallStatic<TYPE>Method(jclass class , jmethodID id, ...); CallStatic<TYPE>MethodV(jclass class , jmethodID id, va_list lst); CallStatic<TYPE>MethodA(jclass class , jmethodID id, jvalue * v); 第三种中的 jvalue 这个类型是一个联合体. 源代码jni.h中: 123456789101112//C typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue; 案例: 123456789101112131415161718192021222324//Javapublic class Port { /** C调用重载成员函数_非静态方法 */ public void functionOverride() { System.out.println("Port.functionOverride()--参数:property>" + property + "--isShow>" + isShow); } /** C调用重载成员函数_非静态方法 */ public void functionOverride(int i) { System.out.println("Port.functionOverride(int i)-->" + i + "--参数:property>" + property + "--isShow>" + isShow); } /** C调用多参数签名_非静态方法 */ public int function(int foo, Date date, int[] arr) { System.out.println("Port.function()foo>" + foo + "===date>" + date + "==arr>" + arr); return foo; } /** C调用成员函数 的三种形式 */ public boolean function(int i, double d, char c) { System.out.println("===>Port.function()i>" + i + "===d>" + d + "==c>" + c); return true; }} 1234567891011121314151617181920212223242526//Cjclass port_clazz = env->GetObjectClass(obj);//调用调用Java里的Port类的void functionOverride()重载函数_非静态函数jmethodID id_func1 = env->GetMethodID(port_clazz, "functionOverride", "()V");env->CallVoidMethod(obj, id_func1);//调用//调用调用java里的Port类的void functionOverride(int )C调用重载函数_非静态函数jmethodID id_func2 = env->GetMethodID(port_clazz, "functionOverride", "(I)V");env->CallVoidMethod(obj, id_func2, 1L);//调用,并需要一个参数//调用调用java里的Port类的public int function(int foo, Date data, int[] arr) -->(ILjava/util/Date;[I)IjmethodID methodID_func = env->GetMethodID(port_clazz, "function", "(ILjava/util/Date;[I)I");env->CallIntMethod(obj, methodID_func, 100L, NULL, NULL);//调用Port的function方法,后面跟随三个参数jmethodID methodID_func_IDC = env->GetMethodID(port_clazz, "function", "(IDC)Z");//调用实例方法的第一种形式: Call<TYPE>Method(jobject obj,jmethodID id,...);env->CallBooleanMethod(obj, methodID_func_IDC, 100L, 3.14, L'1');// 100L长整型,3.14double, L'1'宽字符//调用实例方法的第三种形式:jvalue * j = new jvalue[3];j[0].i = 50L;j[1].d = 3.1415;j[2].c = L'5';env->CallBooleanMethodA(obj, methodID_func_IDC, j);delete[] j;//删除回收 调用一个对象的父类的方法: 在JNI中定义的 CallNonvirtual<TYPE>Method 就能够实现子类对象调用父类方法的功能. 要使用它,首先要取得父类及要调用的父类方法的 jmethodID,方可. 案例: 12345678910111213141516171819//Javapublic class Father { public void function() { System.out.println("===>Father.function()"); }}public class Child extends Father { @Override public void function() { System.out.println("===>Child.function()"); }}public class Port { /** C调用_子父类关系类 */ public Father father = new Child(); } 12345678910111213141516171819//C//调用子父类的成员变量方法:jfieldID id_father = env->GetFieldID(port_clazz, "father", "Lcom/tu/hellojni/jni/Father;");jobject father = env->GetObjectField(obj, id_father);jclass clazz_Father = env->FindClass("com/tu/hellojni/jni/Father");jmethodID id_father_function = env->GetMethodID(clazz_Father, "function", "()V");env->CallVoidMethod(father, id_father_function);// 以上代码相当于:// Port port = new Port();// Father f = port.father; //-->father成员变量是:public Father father = new Child();// f.function();//运行结果: ===>Child.function()// 调用父类的方法env->CallNonvirtualVoidMethod(father, clazz_Father, id_father_function);//运行结果:===>Father.function() 在C/C++本地代码中创建Java的对象创建Java对象有两种方式. (NewObject / AllocObject) 5.1 NewObject 使用函数 NewObject 可以用来创建对象 jobject NewObject(jclass clazz , jmethodID methodID,...) GetMethodID能够取得构造方法的 jmethodID .如果传入的要取得的方法名称设定为"<init>"就能够取得构造方法. 构造方法的方法返回值类型的签名始终为Void. 案例: 12345678910//C//创建一个日期类Date对象并调用getTime方法jclass clazz_date = env->FindClass("java/util/Date");jmethodID mid_date = env->GetMethodID(clazz_date,"<init>","()V");jobject now = env->NewObject(clazz_date,mid_date);jmethodID jmethodID_getTime = env->GetMethodID(clazz_date,"getTime","()J");jlong time = env->CallLongMethod(now,jmethodID_getTime);__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-创建Date对象并调用getTime方法: %d", time); 5.2 AllocObject 使用 AllocObject 方式创建对象(创建但未初始化,使用比较少.)使用函数AllocObject可以根据传入的jclass创建一个Java对象,但是他的状态是非初始化的,在使用这个对象之前绝对要用 CallNonvirtualVoidMethod 来调用该jclass的建构函数.这样可以延迟构造函数的调用.这个部分用的很少. 案例: 12345//Javapublic class Port { /** C进行创建并初始化 */ public static String STATIC_STR;} 12345678910111213141516171819202122232425//C//---> AllocObject 创建延迟初始化的对象jclass clazz_str = env->FindClass("java/lang/String");jmethodID methodID_str = env->GetMethodID(clazz_str, "<init>", "([C)V");//通过字符数组构造函数//预先创建一个没有初始化的字符串jobject string = env->AllocObject(clazz_str);//创建一个5个元素的字符数组,然后以'c','o','m','t','u'赋值jcharArray arg = env->NewCharArray(5);jchar buf[5];buf[0] = 67;buf[1] = 'o';buf[2] = 'm';buf[3] = 't';buf[4] = 'u';env->SetCharArrayRegion(arg, 0, 5, buf);//呼叫构建函数env->CallNonvirtualVoidMethod(string, clazz_str, methodID_str, arg);jclass clazz_this = env->GetObjectClass(obj);jfieldID fieldID_str = env->GetStaticFieldID(clazz_this, "STATIC_STR", "Ljava/lang/String;");env->SetStaticObjectField(clazz_this, fieldID_str, string);//给java静态变量赋值 在C/C++本地代码中访问/创建Java的String字符串对象Java字符串<-->C/C++字符串 在Java中,使用的字符串String对象是Unicode(UTF-16)码, 即每个字符不论是中文英文还是符号,一个字符总是占两个字节. Java通过JNI接口可以将Java的字符串转换到C/C++中的宽字符串(wchar_t*), 或者传回一个UTF-8的字符串(char*)到C/C++. 返过来,C/C++可以通过一个宽字符串,或者一个UTF-8编码的字符串来创建一个Java端的String对象. 获取Java中的String对象有如下几种函数: GetStringChars / GetStringUTFChars / ReleaseStringChars / ReleaseStringUTFChars .const jchar* GetStringChars (jstring str , jboolean* copied) 可以取得UTF-16编码的宽字符串(jchar*) 开新内存,然后把Java中的String拷贝到这个内存中,然后返回指向这个内存地址的指针. const char* GetStringUTFChars (jstring str , jboolean* copied) 可以取得UTF-8编码的字符串(char*) 直接返回指向Java中String的内存的指针,这个时候 千万不要改变这个内存的内容,这将破坏String在Java中始终是常量这个原则. 第二个参数jboolean* copied是用来标示是否对Java的String对象进行拷贝的. 如果传入的这个jboolean指针不是 NULL ,则他会给该指针所指向的内存传入 JNI_TRUE 或 JNI_FALSE 指示是否进行拷贝. 传入 NULL 表示不关心是否拷贝字符串,它就不会被jboolean* 指向的内存赋值 使用了如上这两个函数取得的字串,在不使用的时候,要使用 ReleaseStringChars / ReleaseStringUTFChars 来释放拷贝的内存,或者释放对Java的String对象的引用. ReleaseStringChars (jstring jstr, const jchar* str); ReleaseStringUTFChars (jstring jstr, const char* str); 第一个参数指定一个jstring变量,即要释放的本地字符串的来源. 第二个参数就是要释放的本地字符串. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 void function(JNIEnv * env, jobject obj) { //-------- GetStringChars / ReleaseStringChars / NewString ------------- //获取jmethodID jfieldID jfie_msg = env->GetFieldID(env->GetObjectClass(obj), "message", "Ljava/lang/String;"); //获取到String属性 jstring j_msg = (jstring) env->GetObjectField(obj, jfie_msg); //UTF-16编码的宽字符串(jchar*) const jchar* == const wchar_t* // Java-String->C-jchar*转换 const jchar* jstr = env->GetStringChars(j_msg, NULL);//j_msg不能为null否则会抛异常 //---------拷贝数据start-------------- jsize size = env->GetStringLength(j_msg); //转换成宽字符串 // wstring wstr((const wchar_t*) jstr); jchar* jstrTemp = (jchar*) jstr; //---------拷贝数据end-------------- //回收本地字符串 _释放指针 env->ReleaseStringChars(j_msg, jstr); //---------处理数据start-------------- //倒序函数迭代字符串 // std::reverse(wstr.begin(), wstr.end()); //---------处理数据end-------------- //创建String对象并赋予Port的message变量中. // jstring j_new_str = env->NewString((const jchar*) wstr.c_str(), (jint) wstr.size()); jstring j_new_str = env->NewString(jstrTemp, size); //赋值给Java的Port的message变量 env->SetObjectField(obj, jfie_msg, j_new_str); }``` ```c //拷贝 char* cc2c(const char* str) { char* strtemp = new char[strlen(str) + 1]; strcpy(strtemp, str); return strtemp; } //简单倒序算法 char* myReverse(const char* str) { char* strtemp = cc2c(str); int len = strlen(strtemp); char t; for (int i = 0; i < len / 2; i++) { t = strtemp[i]; strtemp[i] = strtemp[len - i - 1]; strtemp[len - i - 1] = t; } return strtemp; } void function(JNIEnv * env, jobject obj) { //------------------ GetStringUTFChars / ReleaseStringUTFChars / NewStringUTF ------- jfieldID jfie_msg = env->GetFieldID(env->GetObjectClass(obj), "message", "Ljava/lang/String;"); jstring j_msg = (jstring) env->GetObjectField(obj, jfie_msg); // Java-String->C-jchar*转换 const char* str = env->GetStringUTFChars(j_msg, NULL); char* strtemp = myReverse(str); env->ReleaseStringUTFChars(j_msg, str); jstring j_new_str = env->NewStringUTF(strtemp); delete[] strtemp; env->SetObjectField(obj, jfie_msg, j_new_str); } GetStringCritical / ReleaseStringCritical .为了增加直接传回指向Java字符串的指针的可能性(而不是拷贝),JDK1.2之后出来新的函数 const jchar* GetStringCritical (jstring str , jboolean* copied) void ReleaseStringCritical (jstring jstr,const jchar* str); 注意: 在 GetStringCritical / ReleaseStringCritical 方法之间是一个关键区. 在这关键区之中绝对不能呼叫(call)JNI的其它函数,会造成当前线程中断或是会让当前线程 等待的任何本地代码.否则将造成关键区代码执行期间垃圾回收器停止动作,任何触发垃圾 回收的线程也会暂停.其它的触发垃圾回收器的线程不能前进,直到当前线程结束而激活垃圾回收器. 在关键区中千万不要出现中断操作,或者在JVM中分配任何新对象.否则会造成JVM死锁 虽说这个函数会增加直接传回指向Java字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串. 不支持 GetStringUTFCritical ,没有这样一个函数,由于Java字符串用的是UTF16,要转 成UTF8编码的字符串始终需要进行一次拷贝,所以没有这样的函数. 12345678910111213//--------------- GetStringCritical / ReleaseStringCritical ----------void function(JNIEnv * env, jobject obj) { // 基本与 GetStringChars 一样 jfieldID jfie_msg = env->GetFieldID(env->GetObjectClass(obj), "message", "Ljava/lang/String;"); jstring j_msg = (jstring) env->GetObjectField(obj, jfie_msg); // Java-String->C-jchar*转换 const jchar* jstr = env->GetStringCritical(j_msg, NULL);//jstr_msg不能为null复杂会抛异常 wstring wstr((const wchar_t*) jstr); env->ReleaseStringCritical(j_msg, jstr); std::reverse(wstr.begin(), wstr.end()); jstring j_new_str = env->NewString((const jchar*) wstr.c_str(), (jint) wstr.size()); env->SetObjectField(obj, jfie_msg, j_new_str);} GetStringRegion / GetStringUTFRegion .Java 1.2之后出来的函数,这个函数的动作,是把Java字符串的内容直接拷贝到C/C++ 的字符数组中,在呼叫这个函数之前必须有一个C/C++分配出来的字符串,然后传入到 这个函数中进行字符串的拷贝. 由于C/C++中分配内存开销相对小,而且Java中的String内容拷贝的开销可以忽略, 更好的一点是此函数不分配内存,不会抛OutOfMemoryError异常. //拷贝Java字符串并以UTF-8编码传入bufer GetStringUTFRegion(jstring str , jsize start , jsize len , char* buffer); //拷贝Java字符串并以UTF-16编码传入buffer GetStringRegion(jstring str, jsize start , jsize len, jchar* buffer); Java的String str: A B C D E F G H I J K L M N ↑― start = 3 ↓ len = 10 ―↑ env->GetStringRegion(str,3,10,buffer);//copy至buffer 123456789101112131415161718192021//---------------- GetStringRegion / NewString ----------------void function(JNIEnv * env, jobject obj) { jfieldID jfie_msg = env->GetFieldID(env->GetObjectClass(obj), "message", "Ljava/lang/String;"); jstring j_msg = (jstring) env->GetObjectField(obj, jfie_msg); jsize jm_len = env->GetStringLength(j_msg); //创建一个数组长度为message字符串的长度+1(结尾符) jchar* jstr = new jchar[jm_len + 1]; jstr[jm_len] = L'\0';//C++中都会有结尾符,不然会出现乱码. //拷贝到字符数组里 env->GetStringRegion(j_msg, 0, jm_len, jstr); // wstring wstr((const wchar_t*) jstr); //std::reverse(wstr.begin(), wstr.end()); jstring j_new_str = env->NewString((const jchar*) jstr,jm_len); env->SetObjectField(obj, jfie_msg, j_new_str); //删除不必要的内存 delete[] jstr;} 1234567891011121314151617//---------------- GetStringUTFRegion / NewStringUTF ----------------void function(JNIEnv * env, jobject obj) { jfieldID jfie_msg = env->GetFieldID(env->GetObjectClass(obj), "message", "Ljava/lang/String;"); jstring j_msg = (jstring) env->GetObjectField(obj, jfie_msg); jsize jm_len = env->GetStringLength(j_msg); char* buffer = new char[jm_len + 1]; env->GetStringUTFRegion(j_msg, 0, jm_len, buffer); // wstring wstr((const wchar_t*) jstr); //std::reverse(wstr.begin(), wstr.end()); jstring j_new_str = env->NewStringUTF((const char*) buffer); env->SetObjectField(obj, jfie_msg, j_new_str); delete[] buffer;} 字符串相关函数jstring NewString(const jchar* str, jsize len); //创建一个字符串 jstring NewStringUTF(const char* str); //创建一个UTF8的字符串 jsize GetStringLength(jstring str);// 字符串的长度 jsize GetStringUTFLength(jstring str);//UTF会占用多少个字节 7.在C/C++本地代码中操作Java的数组对象1.基本类型的数组 2.对象类型(Objcet[])的数组 一个能通用于两种不同类型数组的函数 GetArrayLength(jarray aray);//获取数组长度 处理基本类型数组Get<TYPE>ArrayElements(<TYPE>Array arr, jboolean* iscopied); 这类函数可以把Java基本类型的数组转换到C/C++中的数组,有两种处理方式, 1.拷贝一份传回本地代码, 2.把指向Java数组的指针直接传回到本地代码. 处理完本地代码的数组后,通过 Release<TYPE>ArrayElements来释放数组. Release<TYPE>ArrayElements(<TYPE>Array arr,<TYPE>* array,jint mode) 用这个函数可以选择将如何处理Java跟C++的数组,是提交,撤消,内存释放,还是不释放等. mode可以取下面的值: 0 -> 对Java的数组进行更新并释放C/C++的数组. JNI_COMMIT -> 对Java的数组进行更新但不释放C/C++的数组. JNI_ABORT -> 对Java的数组不进行更新,释放C/C++的数组. GetPrimitiveArrayCritical(jarray arr , jboolean* isCopied); ReleasePrimitiveArrayCritical(jarray arr , void* array,jint mode); 也是JDK1.2出来的,为了增加直接传回指向Java数组的指针而加入的函数,同样的, 也会有同 GetStringCritical 的死锁的问题. Get<TYPE>ArrayRegion(<TYPE>Array arr, jsize start , jsize len , <TYPE>* buffere); 在C/C++预先开辟一段内存,然后把Java基本类型的数组拷贝到这段内存中. 跟 GetStringRegion原理类似 Set<TYPE>ArrayRegion(<TYPE>Array arr , jsize start, jsize len , const <TYPE>* buffer); 把Java基本类型的数组中的指定范围的元素用C/C++的数组中的元素来赋值. <TYPE>Array New<TYPE>Array(jsize sz) 指定一个长度然后返回相应Java基本类型的数组. 123//java/** C调用Get/Set成员变量 数组 */public int[] ints = new int[] { 67, 111, 109, 116, 117 }; 123456789101112131415161718192021222324252627//c//---- GetIntArrayElements / ReleaseIntArrayElements / ReleaseIntArrayElements / ReleaseIntArrayElements ----//基本数据类型数组void function(JNIEnv * env, jobject obj) { //取得属性Id jfieldID fieldId = env->GetFieldID(env->GetObjectClass(obj), "ints", "[I"); //通过属性id取得数组变量 jintArray ints = (jintArray) env->GetObjectField(obj, fieldId); //转取得本地数组 jint* int_arr = env->GetIntArrayElements(ints, NULL); jsize len = env->GetArrayLength(ints); //C基本库 . 对数组进行排序,迭代器需要两个参数,第一个是array首地址,第二个是array尾地址 std::sort(int_arr, int_arr + len); for (jsize i = 0; i < len; ++i) { __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-intArray[]: %d", int_arr[i]); } //对Java的数组进行更新并释放C/C++的数组. env->ReleaseIntArrayElements(ints, int_arr, 0); //对Java的数组进行更新但不释放C/C++的数组. // env->ReleaseIntArrayElements(ints, int_arr, JNI_COMMIT); //对Java的数组不进行更新,释放C/C++的数组. // env->ReleaseIntArrayElements(ints, int_arr, JNI_ABORT);} 处理对象类型数组JNI没有提供直接把Java的对象类型数组(Object[])直接传到C++中的jobject[]数组的函数. 而是直接通过 Get/SetObjectArrayElement 这样的函数来对Java的Object[]数组进行操作. 使用上述的函数也不用释放任何资源. NewObjectArray 可以通过指定长度跟初始值来创建某个类的数组. 1234567891011121314151617181920212223242526272829303132333435363738//c// ----- GetObjectArrayElement / NewObjectArray / SetObjectArrayElement ------//对象数据类型数组void function(JNIEnv * env, jobject obj) { jfieldID fieldStrId = env->GetFieldID(jclazz, "strs", "[Ljava/lang/String;"); //获取java的对象数组 jobjectArray str_arr = (jobjectArray) env->GetObjectField(obj, fieldStrId); int lenStr = env->GetArrayLength(str_arr); printf("\n c-stringArray :"); int i = 0; //读Java的以String数组 for (i = 0; i < lenStr; i++) { jobject obj = env->GetObjectArrayElement(str_arr, i); jstring str = (jstring) obj; const char * szStr = env->GetStringUTFChars(str, 0); printf(" %d-%s ", i, szStr); __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-StringArray[]: %d - %s", i, szStr); const jchar * chars = (const jchar *) szStr; env->ReleaseStringChars(str, chars); } //创建一个对象数组 , 为Java对象数组赋值并更新. jstring str; jobjectArray args = 0; jsize size = 7; char* sa[] = { "Hello,", "world!", "c", "o", "m", "t", "u" }; int j = 0; jclass objClass = env->FindClass("java/lang/String"); args = env->NewObjectArray(size, objClass, 0); for (j = 0; j < size; j++) { str = env->NewStringUTF(sa[j]); env->SetObjectArrayElement(args, j, str); if (j < lenStr)//给Java数组赋数组下标越界抛异常 , env->SetObjectArrayElement(str_arr, j, str);//自动更新Java类中的strs对象数组 } // 不需要释放任何资源. //args 新创建的 String 数组.} 8.全局引用/局部引用/弱全局引用从Java虚拟机创建的对象传到本地C/C++代码时会产生引用.根据Java的垃圾回收机制,只要有引用 存在就不会触发该引用指向的Java对象的垃圾回收. 这些引用在JNI中分三种: 全局引用(Global Reference) 局部引用(Local Reference) 弱全局引用(Weak Global Reference ) Since JDK1.2 局部引用最常见的引用类型,基本上通过JNI返回来的引用都是局部引用. 例如使用NewObject就会返回创建出来的实例的局部引用.局部引用只在该 native函数中有效,所有在该函数中产生的局部引用,都会在函数返回的时候 自动释放(freed).也可以使用 DeleteLocalRef 函数手动释放该引用. 实际上局部引用存在,就会防止其指向的对象被垃圾回收,尤其是当一个局部引用 指向一个很庞大的对象,或是在一个循环中生成了局部引用,最好的做法就是在使用完该 对象后,或在该循环尾部把这个引用释放掉,以确保在垃圾回收器被触发的时候被回收. 在局部引用的有效期中,可以传递到别的本地函数中,要强调的是他的有效期仍然只在 一次的Java本地函数调用中,所以千万不能用C++全局变量保存他或是把他定义为C++ 静态局部变量. env->NewLocalRef(_jobject* localRef); env->DeleteLocalRef(_jobject* localRef); 全局引用全局引用可以跨越当前线程,在多个native函数中有效,不过需要编程人员手动 来释放该引用.全局引用存在期间会防止在Java的垃圾回收的回收. 与局部引用不同,全局引用的创建不是由JNI自动创建的,全局引用是需要 调用 NewGlobalRef 函数,而释放他需要使用 DeleteGlobalRef 函数. env->NewGlobalRef(_jobject* obj); env->DeleteGlobalRef(_jobject * globalRef); 弱全局引用Java 1.2后出来的功能,与全局引用相似,创建跟删除都需要由编程人员来进行. 这种引用与全局引用一样可以在多个本地代码有效,也跨越多线程有效,不一样的是, 这种引用将不会阻止垃圾回收器回收这个引用所指向的对象. 使用 NewWeakGlobalRef 跟 DeleteWeakGlobalRef 来产生和解除引用. env->NewWeakGlobalRef(_jobject * obj); env->DeleteWeakGlobalRef(_jobject* obj); 关于引用的一些函数jobject NewLocalRef( jobject obj); jobject NewGlobalRef( jobject obj); jboject NewWeakGlobalRef( jobject obj); void DeleteLocalRef( jobject obj); void DeleteGlobalRef( jobject obj); void DeleteWeakGlobalRef( jobject obj); //比较两个引用是否指向同一个Java对象 jboolean IsSameObject(jobject obj1,Object obj2); 这个函数对于弱全局引用还有一个特别的功能. 把 NULL 传入要比较的对象中,就能判断弱全局引用所指向的Java对象是否被回收. 9.JNI优化缓存 jfieldID/jmethodID 取得 jfieldID跟jmethodID的时候会通过该属性/方法名称加上签名来查询相应的 jfieldID/jmethodID.这种查询相对来说开销较大.我们可以将这些FieldID/MethodID 缓存起来,这样只需要查询一次,以后就使用缓存起来的FieldID/MethodID了. 两种缓存的方式: 1.在用的时候缓存 (caching at the point of use) 在Native Code 中使用static局部变量来保存已经查询过的id.这样就不会在每次 函数调用时查询,而只要第一次查询成功后就保存起来了. 不过在这种情况下就不得不考虑多线程同时呼叫此函数时可能会导致同时查询的危机. 不过这种情况是无害的,因为查询同一个属性/方法的ID通常返回的是一样的值. 123/**缓存id*/public String cacheJfieldID = null;public String cacheJmethodID = null; 123456static jfieldID fieldID_string = NULL;jclass clazz = env->GetObjectClass(obj);if (fieldID_string == NULL) { fieldID_string = env->GetFieldID(clazz, "cacheJfieldID", "Ljava/lang/String;");}//other code... 2.在Java类初始化时缓存(caching at the defining Class’s inititalizer) 更好的一个方式就是在任何native函数调用前把id全部存起来. 我们可以让java在第一次加载这个类的时候,首先调用本地代码初始化所以的 jfieldID/jmethodID,这样的话就可以省去多次的确定id是否存在的语句, 当然,这些jfieldID/jmethodID是定义在C/C++的全局. 使用这种方式还有好处,当Java类卸载或是重新加载的时候也会重新呼叫 该本地代码来重新计算IDs. 1234567891011public class Port { public int property = 55; public String message = "123456"; /**缓存id*/ static{ initNativeIDs();//初始化id } public static native void initNativeIDs(); //other code...} 12345678910 //C jfieldID g_propInt_id = 0; jfieldID g_propStr_id = 0; /**Java初始化的时候缓存*/ JNIEXPORT void JNICALL Java_com_tu_hellojni_jni_Port_initNativeIDs(JNIEnv * env, jclass clazz) { __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-message: %s", "load.initNativeIDs.."); g_propInt_id = env->GetFieldID(clazz, "property", "I"); g_propStr_id = env->GetFieldID(clazz, "message", "Ljava/lang/String;");} //other code... Demo下载 本文Demo本文Demo源代码 本博文暂时没有: 异常处理; C/C++如何启动JVM; JNI跟多线程;等介绍.有时间再后续…介绍两本书:The Java Native interface Programmer’s Guide and SpecificationJNI++ User Guider]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>AndroidNDK</tag>
<tag>NDK</tag>
<tag>JNI</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android 中NDK的使用详解第二篇:Hello World]]></title>
<url>%2F%2F2014%2F10%2F29%2FAndroid_NDK_Hello_World_two.html</url>
<content type="text"><![CDATA[目录: Hello World 编译多个源文件 支持多CPU架构 使用Ant批量生成头文件 上一编讲到了搭建AndroidNDK开发环境的内容,这篇则进行实战. 在实战之前首先介绍一下NDK与JNI的关系. JNI (Java Native Interface)JNI是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以调用java代码。JNI 是本地编程接口,Java和C/C++互相通过的接口。Java通过C/C++使用本地的代码的一个关键性原因在于C/C++代码的高效性。 NDK是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。它集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。它可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。 简单概括: 用C语言生成一个库文件,在java中调用这个库文件的函数。JNI的过程比较复杂,生成.so需要大量操作,而NDK就是简化了这个过程。 Hello World创建一个项目:项目名为AndroidNDK项目包名为:com.tu.test.androidndk 1.使用Android Tools工具为项目工程支持NDK 项目上右击–>Android Tools–>add native support–>输入或者默认的名称(例如:AndroidNDK)–>finish会注意到项目目录下多了一个jni的目录里面多了两个文件Android.mk , AndroidNDK.app(上面输入的名称)以及多了一个obj的目录 2.创建一个类. 声明一个带有native修饰的方法 1234package com.tu.test.androidndk.jni;public class Port { public static native String sayHello(String str);} 3.使用javah命令生成头文件并拷贝到jni目录(后会被后面的使用Ant批量生成头文件方法代替) 使用cmd把目录定位到”项目路径/bin/classes/“项目下. 1234567891011121314C:\Users\comtu>cd E:\ComTu_Design\workspace\AndroidNDKC:\Users\comtu>e:E:\ComTu_Design\workspace\AndroidNDK>cd binE:\ComTu_Design\workspace\AndroidNDK\bin>cd classesE:\ComTu_Design\workspace\AndroidNDK\bin\classes>dir 驱动器 E 中的卷没有标签。 卷的序列号是 EE26-DAFE E:\ComTu_Design\workspace\AndroidNDK\bin\classes 的目录2015-07-14 14:50 <DIR> .2015-07-14 14:50 <DIR> ..2015-07-14 14:50 <DIR> android2015-07-14 14:50 <DIR> com 0 个文件 0 字节 4 个目录 35,301,425,152 可用字节 使用Javah命令生成包含native方法定义的C/C++头文件 1234567891011121314E:\ComTu_Design\workspace\AndroidNDK\bin\classes>javah -jni com.tu.test.androidndk.jni.PortE:\ComTu_Design\workspace\AndroidNDK\bin\classes>dir 驱动器 E 中的卷没有标签。 卷的序列号是 EE26-DAFE E:\ComTu_Design\workspace\AndroidNDK\bin\classes 的目录2015-07-14 16:15 <DIR> .2015-07-14 16:15 <DIR> ..2015-07-14 16:08 <DIR> android2015-07-14 16:08 <DIR> com2015-07-14 16:15 1,213 com_tu_test_androidndk_jni_Port.h 1 个文件 1,213 字节 4 个目录 35,301,281,792 可用字节 输入完后控制台不会有打印信息,只会在E:\ComTu_Design\workspace\AndroidNDK\bin\classes>目录下会生成一个文件名为com_tu_test_androidndk_jni_Port.h的头文件 拷贝头文件 把生成的.h文件拷贝到步骤2生成的jni目录下. 使目录结构如下: jni |-Android.mk |-AndroidNDK.app |-com_tu_test_androidndk_jni_Port.h 4. 编写代码实现 打开com_tu_test_androidndk_jni_Port.h文件 12JNIEXPORT jstring JNICALL Java_com_tu_androidndk_jni_Port_sayHello (JNIEnv *, jclass, jstring); 把生成的方法拷贝到AndroidNDK.app里面.并实现方法体. 123456789101112131415161718#include <jni.h>#include <stdio.h>#include "com_tu_androidndk_jni_Port.h"//引入头文件#include <android/log.h>//Logcat打印日志 Android.mk 文件必须配置: LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llogJNIEXPORT jstring JNICALL Java_com_tu_androidndk_jni_Port_sayHello( JNIEnv *env, jclass clazz, jstring s) { char * st = (char *) env->GetStringUTFChars(s, 0); printf("\n c-string: %s", st); //在Logcat中打印日志,在个参数,日志类型,过滤标签Tag,输入的文本内容Text __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-string: %s",st); jstring rtn = env->NewStringUTF("hello Comtu!"); return rtn;} 在java里并编写静态代码块加载lib 123456789101112131415161718package com.tu.androidndkhelloworld;import android.app.Activity;import android.os.Bundle;import android.widget.TextView;import com.tu.androidndkhelloworld.jni.Port;public class MainActivity extends Activity { static { System.loadLibrary("AndroidNDK"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //调用C程序实现的sayHello方法 ((TextView)findViewById(R.id.text)).setText(Port.sayHello("Hi")); }} 运行程序(注意以上操作只适合于ARM架构的CPU) 编译多个源文件1.jni目录里创建多一个c文件 选择jni–>右击选择new–>other–>C/C++–>class–>文件名如(Hello)或者选择C/C++ Perspective 视图 –>jni目录下右击new–>class–>文件名(Hello)目录下就会自己创建出Hello.cpp与Hello.h文件 使目录结构如下: jni |-Android.mk |-AndroidNDK.app |-com_tu_test_androidndk_jni_Port.h |-Hello.cpp |-Hello.h 2.编写新创建的Hello.h文件1234567891011#ifndef HELLO_H_#define HELLO_H_class Hello {public: Hello(); char * getWords();//<----编写一个方法 virtual ~Hello();};#endif /* HELLO_H_ */ 3.实现Hello.cpp代码123456789#include "Hello.h"Hello::Hello() {}char * Hello::getWords(){//<----编写一个方法用于与java交互 return "Hello NDK";}Hello::~Hello() {} 4.编写Android.mk文件,让Hello可进行编译 LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 要打印log需要增加 12345678910LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llogLOCAL_MODULE := AndroidNDKLOCAL_SRC_FILES := AndroidNDK.cpp Hello.cppinclude $(BUILD_SHARED_LIBRARY) 如果增加很多个.cpp文件使用空格分开,为了方便观看也可使用\符号进行分行显示 12LOCAL_SRC_FILES := AndroidNDK.cpp \Hello.cpp 5.编写TwoPort类增加如下代码作为测试1234package com.tu.androidndkhelloworld.jni;public class TwoPort { public static native String getWords();} 继续使用javah命令或者使用批量生头文件(见下文)的方式.生成头文件到jni目录. 使目录结构如下: jni |-Android.mk |-AndroidNDK.app |-com_tu_test_androidndk_jni_Port.h |-com_tu_test_androidndk_jni_TwoPort.h |-Hello.cpp |-Hello.h AndroidNDK.cpp实现代码 1234567891011121314151617181920212223242526#include <jni.h>#include <stdio.h>#include "com_tu_test_androidndk_jni_Port.h"//引入头文件#include "com_tu_test_androidndk_jni_TwoPort.h"#include <android/log.h>#include <Hello.h>//Logcat打印日志 Android.mk 文件必须配置: LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llogJNIEXPORT jstring JNICALL Java_com_tu_androidndk_jni_Port_sayHello( JNIEnv *env, jclass clazz, jstring s) { char * st = (char *) env->GetStringUTFChars(s, 0); printf("\n c-string: %s", st); //在Logcat中打印日志,在个参数,日志类型,过滤标签Tag,输入的文本内容Text __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "\n c-string: %s", st); jstring rtn = env->NewStringUTF("hello Comtu!"); return rtn;}JNIEXPORT jstring JNICALL Java_com_tu_androidndk_jni_TwoPort_getWords( JNIEnv *env, jclass clazz) { Hello h; return env->NewStringUTF((const char *) h.getWords());} 6.运行String words = TwoPort.getWords();//调用 支持多CPU架构方法: 在jni目录新创建一个 Application.mk 文件里面增加内容APP_ABI := x86 armeabi即可.这样就支持x86与armeabi的CPU架构了.如果需要支持更多的架构会增加apk的文件大小.NDK的版本为android-ndk-r10e可支持包含64位的处理器APP_ABI := x86 armeabi armeabi-v7a mips arm64-v8a x86_64 mips64 查看手机CPU的架构方法” 使用已经Root的手机通过R.E.管理器, 或者ES文件管理器,进入到文件顶级根目录下/proc文件夹/cpuinfo文件可查看手机属于什么CPU架构 或者使用cmd查看CPU是属于那个架构可使用命令: 123456789101112131415161718192021C:\Users\comtu>adb shellshell@android:/ $ cat /proc/cpuinfocat /proc/cpuinfoProcessor : ARMv7 Processor rev 1 (v7l)processor : 0BogoMIPS : 668.86processor : 1BogoMIPS : 668.86Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4CPU implementer : 0x41CPU architecture: 7CPU variant : 0x0CPU part : 0xc05CPU revision : 1Hardware : MSM8x25 C8833D BOARDRevision : 0000Serial : 0000000000000000shell@android:/ $ 使用Ant批量生成头文件使用Ant工具自动生成头文件. (方便快捷,代替上面的步骤3) Ant是一个自动构建的一个脚本,基于xml . 1.创建Ant文件 在项目工程里创建一个new–>File新创建一个文件名:build_headers.xml的文件选择文件–>右击–>open with->other->ant editor的方式打开.打开文件后使用alt+/快捷键会自动生成模板. 2.编写模板123456789101112131415<project name="AndroidNDK" default="BuildAllHeaders"><description>description</description><!--呼叫目标--><target name="BuildAllHeaders"> <antcall target="BuildPortHeader" /> <antcall target="BuildTwoPortHeader" /></target><!--目标_使用javah命令 destdir生成文件的目的路径,classpath类Class文件路径 , class类名--><target name="BuildPortHeader"> <javah destdir="./jni" classpath="./bin/classes" class="com.tu.test.androidndk.jni.Port"/></target><target name="BuildTwoPortHeader"> <javah destdir="./jni" classpath="./bin/classes" class="com.tu.test.androidndk.jni.TwoPort"/></target></project> 3.运行Ant自动编译 选择build_headers.xml右击–> run as –>Ant build或者:打开Ant窗口(Wnidow–>Show View–>Ant)–>Add Buildfiles–>选择项目–>Ant窗口里会多出一个AndroidNDK项双击即可. 4.刷新项目 选择项目–>右击–>Refresh查看jni目录会新增加对应.h的头文件这样编译起多个文件时效率大大的提高. Demo下载本文案例Demo下载]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>AndroidNDK</tag>
<tag>NDK</tag>
<tag>JNI</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android 中NDK的使用详解第一篇:环境]]></title>
<url>%2F%2F2014%2F10%2F28%2FAndroid_NDK_IDE_environment_one.html</url>
<content type="text"><![CDATA[h3 { line-height: 1; letter-spacing: 2px; margin-top: 5px; } h6 { line-height: 1; letter-spacing: 2px; margin-top: 5px; } 目录 1.下载集成开发环境 2.安装配置环境 3.常用功能配置 3.1自动生成头文件(配置javah) 3.2生成Sign签名(配置Javap) 3.3配置C/C++库 1. 下载集成开发环境所需要的开发工具:Eclipse 下载地址:http://www.eclipse.org/downloads/ 选择Eclipse的时候可以选择C/C++版本的,方便后续直接在Eclipse里编写C程序.当然也可以自己对Eclipse安装C/C++插件JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.htmlADT 下载地址:http://developer.android.com/sdk/installing/installing-adt.htmlAndroid SDK 下载地址:http://developer.android.com/sdk/index.htmlNDK 下载地址:http://developer.android.com/ndk/downloads/index.html 也可以直接下载ADT Bundle 但官方已经找不到下载地址.可网上搜索或者我下面提供的一个地址可找到.如果没有代理可能很多工具无法下载.这里提供一个Android开发人员工具集给大家 2. 安装配置环境2.1. 安装JDK并设置环境变量到path中. 如:javah工具等2.2. 解压Eclipse到D盘目录如: D:/eclipse2.3. 为Eclipse安装ADT插件方式一绿色安装: 1.解压ADT到到某一个目录2.单独复制features与plugins这两个文件夹到D:/eclipse/my_plugins/ADT/eclipse/目录下(新创建目录)3.到D:/eclipse/dropins目录下新创建一个adt.link文件,里面编写内容:path=D:/eclipse/my_plugins/ADT/eclipse/ 方式二直接安装: 打开Eclpse–>Help–>Install New Software..–>Add–>Name:ADT–>Archive–>选择下载好的ADT.zip文件–>OK–>下方会加载显示出Developer Tools –>全选–>Next–>Next–>选择上面的I accept the ….–>Finish–>进入自动安装状态–>安装完后重启,即可. Developer Tools其中全部包含有如下 Android DDMS Android Development Tool Android Hierarchy Viewer Android Native Developme Android Traceview Tracer for OpenGL ES 2.4. 解压Android SDK到my_plugins目录下. 配置一下环境变量到path中,方便以后的需要.如:draw9patch工具等等 2.5. 安装NDK,如果是压缩包形式则,解压到my_plugins目录下. 如果是安装包(exe,其实是zip的自解压文件)也可安装到my_plugins目录 2.6. 如果是下载的Eclipse是C/C++版本的,默认是C/C++视图.修改视图–>点击右上角Open perspective–>选择java如果安装ADT的时候使用的是方式二直接安装ADT.zip的方式,则安装完后无需要再配置C/C++环境,ADT已经包含了. 如果非C/C++版本的则为Eclipse安装C/C++ 插件方法如下: 1.Eclipse里安装CDT-->Help-->Install new software-->弹出窗口中--> Work with:输入-->http://download.eclipse.org/tools/cdt/releases/galileo -->回车-->等待一段时间后会在下面列表显示出CDT Main Features选中-->Next-->等待安装完后重启Eclipse即可. 2.安装MinGW --- C/C++编译平台,下载后需要安装,http://www.mingw.org/download.shtml 同时选中g++、MinGW Make,同时设置环境变量,将%MinGW_HOME%\bin设置到PATH中. 3.安装gdb——C/C++调试平台,下载后安装,默认到MinGW_HOME就行。http://www.gnu.org/software/gdb/download 2.7. 目录结构如下:D:\eclipse +---- .... +----dropins | +-----adt.link +----my_plugins +-----android-sdk | +----.... +-----android-ndk-r10e | +----.... +-----MinGW | +----.... +-----ADT | +----eclipse | +-----features | | +-----.... | +-----plugins | +-----.... 2.8. 配置环境变量:计算机右击-->属性-->高级系统设置-->高级-->环境变量--> 新建-->系统变量-->变量名:java_home -->变量值:jdk安装目录,如我的是:C:\Program Files\Java\jdk1.8.0_20 系统变量-->找到Path选中-->编辑-->光标定位到最尾部-->输入-->;%java_home%\bin 新建-->系统变量-->变量名:android_sdk -->变量值:androidSDK安装目录,如我的是:D:\eclipse\my_plugins\android-sdk 系统变量-->找到Path选中-->编辑-->光标定位到最尾部-->输入-->;%android_sdk%\tools 新建-->系统变量-->变量名:MinGW_HOME -->变量值:MinGW安装目录\bin,如我的是:D:\eclipse\my_plugins\MinGW; 系统变量-->找到Path选中-->编辑-->光标定位到最尾部-->输入-->;%MinGW_HOME%\bin 2.9. 以上配置完成后打开eclipse/eclipse.exe运行IDE配置AndroidSDK-->Windows-->Proferences-->选择中Android--> SDK Location中选中D:\eclipse\my_plugins\android-sdk目录即可 配置AndroidNDK-->Windows-->Proferences-->打开折叠的Android--> NDK-->NDK Location中选中D:\eclipse\my_plugins\android-ndk-r10e目录即可 为了使CDT能够取用MinGW来进行编译的工作, 我们要回到 Eclipse 当中进行设定: Window->Preferences->C/C++->New CDT project wizard->Makefile Project 找到 Binary Parser 取消 Elf Parser 改选 PE Windows Parser 2.10. 打开Android SDK Manager下载SDK 如果速度慢可按照如下设置代理: Android SDK在线更新镜像服务器 中国科学院开源协会镜像站地址: IPV4/IPV6: http://mirrors.opencas.cn 端口:80 IPV4/IPV6: http://mirrors.opencas.org 端口:80 IPV4/IPV6: http://mirrors.opencas.ac.cn 端口:80 上海GDG镜像服务器地址: http://sdk.gdgshanghai.com 端口:8000 北京化工大学镜像服务器地址: IPv4: http://ubuntu.buct.edu.cn/ 端口:80 IPv4: http://ubuntu.buct.cn/ 端口:80 IPv6: http://ubuntu.buct6.edu.cn/ 端口:80 大连东软信息学院镜像服务器地址: http://mirrors.neusoft.edu.cn 端口:80 使用方法: 启动 Android SDK Manager ,打开主界面,依次选择『Tools』、『Options...』, 弹出『Android SDK Manager - Settings』窗口; 在『Android SDK Manager - Settings』窗口中,在『HTTP Proxy Server」和 「HTTP Proxy Port』输入框内填入上面镜像服务器地址(不包含http:// )和端口, 并且选中『Force https://... sources to be fetched using http://...』复选框。 设置完成后单击『Close』按钮关闭『Android SDK Manager - Settings』窗口返回到主界面; 依次选择『Packages』、『Reload』。 3.常用功能配置3.1自动生成头文件(配置javah)非Ant批量生成头文件,配置是为了简化命令行的操作. 1.打开Eclipse –> 2.Run –> 3.External Tools –> 4.External Tool Configurations –>5.Program右击选择New –> 6.Name:输入 Generate_C++_Header_File ->7.Main栏目中Location:中选择下面的Variables选择system_path –> 8.Locaton输入框里会自动填上${system_path}–> 9.修改内容为${system_path:javah} –> 在Working Directory中选择Variables–> 11.选择project_loc –> 12.中间输入框会自动填写${project_loc}13.修改内容为${_project_loc}/jni –> 14.Arguments:输入内容-classpath ${project_loc}\bin\classes;输入AndroidSDK目录\platforms\android-?\android.jar -d ${project_loc}\jni -jni ${java_type_name} 例如我的配置是: Location: ${system_path:javah} Working Directory: ${project_loc}\jni Arguments: -classpath ${project_loc}\bin\classes;D:\eclipse\my_plugins\android-sdk-windows\platforms\android-17\android.jar -d ${project_loc}\jni -jni ${java_type_name} 使用方法: 点选中有native修饰的方法的类 --> Run Generate_C++_Header_File --> 刷新项目.会在项目/jni目录下生成.h头文件. 3.2生成Sign签名(配置Javap)代替使用cmd命令行生成.提高效率 见JNI 1.打开Eclipse –> 2.Run –> 3.External Tools –> 4.External Tool Configurations –>5.Program右击选择New –> 6.Name:输入javap ->7.Main栏目中Location:中选择下面的Variables选择system_path –> 8.Locaton输入框里会自动填上${system_path}–> 9.修改内容为${system_path:javap} –> 在Working Directory中选择Variables–> 11.选择project_loc –> 12.中间输入框会自动填写${project_loc}13.Arguments:输入内容: -classpath ${project_loc}\bin\classes -s -p ${java_type_name} 如我的配置是: Location: ${system_path:javap} Working Directory: ${project_loc} Arguments: -classpath ${project_loc}\bin\classes -s -p ${java_type_name} 使用方法: 点选有 native 修饰的方法的类--> Run javap 在Console就会打印出javap运行在cmd命令行一样的效果.生成 Sign 签名 3.3配置C/C++库前提,已经有一个Android项目支持NDK. 即: 选择项目右击--> Android Tool -->Add Native Support.. 如果出现C++代码alt+/无提示可使用此方法解决.或者需要引用其它.h资源库,也同样适用. 1.选择C/C++视图 –> 2.选中项目右击–>Properties –> 3.C/C++ General–> Paths and Symbols4.弹出的Paths and Symbols试图中–> Add –> File system.. –> 选择NDK的目录结构如:NDK目录\platforms\android-?\arch-arm\usr\include –> Apply –OK 如我的配置是: D:\eclipse\my_plugins\android-ndk-r10e\platforms\android-19\arch-arm\usr\include D:\eclipse\my_plugins\android-ndk-r10e\sources\cxx-stl\stlport\stlport 后者是原生C标准头文件库(代码提示,以及看原代码使用.) End]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>AndroidNDK</tag>
<tag>NDK</tag>
<tag>JNI</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android安全模式机制之三(实际运用)]]></title>
<url>%2F%2F2014%2F10%2F27%2Fandroid_safe_mode_mechanism_three.html</url>
<content type="text">< MANIFEST.MF文件 (第一重保护,本文本里面包含apk的资源文件的每个文件的SHA1值) CERT.SF文件 (第二重保护,本文本里面包含MANIFEST.MF里的所有内容,并包含有MANIFEST.MF的SHA1-Digest-Manifest) CERT.RSA (第三重保护,此文件是一个PKCS#7格式的文件, 里面包含证书信息,以及基于私钥的签名信息, 签名信息是由整个CERT.SF文件的做一个基于sha1(160bit)+rsa(testkey.pk8,publicKey)生成) 2.Android中的权限2.1 Android的权限作用细粒度特权管理 权限与操作关联 应用需要显式申请权限 用户对权限可知(不可控) (也可通过LBE,腾讯管家,360等可进行权限控制) 对特权权限单独控制 2.2 Android的权限类别Normal 没那么敏感 Dangerous 比较敏感的,安装时会被列举出来. Signature 基于特殊权限的权限(申请权限的apk它的签名的私钥必须要跟定义这个权限的私钥一致) SignatureOrSystem 申请权限的apk它的签名的私钥必须要跟定义这个权限的私钥一致或者apk是系统应用 2.3 Android的权限定义方式123456789101112<!-- Allows an application to monitor incoming SMS messages, to record or perform processing on them. --> <permission android:name="android.permission.RECEIVE_SMS" android:permissionGroup="android.permission-group.MESSAGES" android:protectionLevel="dangerous" android:label="@string/permlab_receiveSms" android:description="@string/permdesc_receiveSms" /> <!--name 权限名称 permissionGroup 权限组 protectionLevel 权限类别 label 安装时用户可查看到的信息,提示申请当前权限的作用 description 显示给用户的申请权限的详细概述--> 源代码frameworks/base/core/res/AndroidManifest.xml 2.4 Android的运行时权限控制方式通过PM的CheckPermission Android独有的Service(底层平台不具有) 所以需要在Android本身Framework中控制 主流的Service一般都基于Binder IPC或者其他IPC提供服务 所以在最底层控制(Service所在的Server中)以避免逃逸控制 绕开应用函数直接调用远程服务 例子:(mContext.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) 映射为OS的特定属性 非Android特有的Service(底层平台已经提供,如File访问,TCPIP数据收发等) 多个入口访问ndroidAPI,Java API NDK C API , Shell , et:Ac 底层控制准则,会聚口在底层,所以在底层(OS层面)统一控制,这样可以避免逃逸控制 所以复用OS的一些安全控制特性,比如GID 所以需要把Android空间的Permission Mapping到OS的GID 123456789101112进入到手机SDCard里发现GID都是sdcard_rwshell@android:/storage/sdcard0 $ ls -lls -l UID GIDdrwxrwxr-x system sdcard_rw 2014-12-15 18:03 Androiddrwxrwxr-x system sdcard_rw 2015-03-18 16:09 BaiduMapSdkdrwxrwxr-x system sdcard_rw 2014-06-30 10:04 BaoDownloaddrwxrwxr-x system sdcard_rw 2014-07-07 17:33 Cachedrwxrwxr-x system sdcard_rw 2014-12-15 18:03 CloudDrivedrwxrwxr-x system sdcard_rw 2015-03-20 11:33 DCIMdrwxrwxr-x system sdcard_rw 2014-12-15 18:02 Downloaddrwxrwxr-x system sdcard_rw 2015-03-04 12:05 ExtDataTunnel 12<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 1234<!--当开发者增加上条权限时会程序会自动增加这条信息. 本信息来源:frameworks/base/data/etc/platform.xml--><permission name="android.permission.WRITE_EXTERNAL_STORAGE"> <group gid="sdcard_rw" /></permission> 12345678910111213141516171819202122232425262728293031323334353637383940/*进程所占内存proc pid status*/shell@android:/proc/5914 # cat statuscat statusName: n.ledinside.app /*进程的程序名*/State: S (sleeping) /*进程的状态信息*/Tgid: 5914 /*线程组号*/Pid: 5914 /*进程pid*/PPid: 148 /*父进程的pid*/TracerPid: 0 /*跟踪进程的pid*/Uid: 10075 10075 10075 10075 /*uid euid suid fsuid*/Gid: 10075 10075 10075 10075 /*gid egid sgid fsgid*/FDSize: 256 /*文件描述符的最大个数,file->fds*/Groups: 1015 1028 3003/*启动该进程的用户所属的组的id*/VmPeak: 354256 kB /*进程地址空间的大小*/VmSize: 301964 kB /*进程虚拟地址空间的大小reserved_vm:进程在预留或特殊的内存间的物理页*/VmLck: 0 kB /*进程已经锁住的物理内存的大小.锁住的物理内存不能交换到硬盘*/VmPin: 0 kBVmHWM: 63052 kB /*文件内存映射和匿名内存映射的大小*/VmRSS: 33884 kB /*应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值 (rss)*/VmData: 30484 kB /*程序数据段的大小(所占虚拟内存的大小),存放初始化了的数据*/VmStk: 136 kB /*进程在用户态的栈的大小*/VmExe: 8 kB /*程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库 */VmLib: 28068 kB /*被映像到任务的虚拟内存空间的库的大小*/VmPTE: 150 kB /*该进程的所有页表的大小*/VmSwap: 0 kBThreads: 15 /*共享使用该信号描述符的任务的个数*/SigQ: 0/3133 /*待处理信号的个数/目前最大可以处理的信号的个数*/SigPnd: 0000000000000000 /*屏蔽位,存储了该线程的待处理信号*/ShdPnd: 0000000000000000 /*屏蔽位,存储了该线程组的待处理信号*/SigBlk: 0000000000001204 /*存放被阻塞的信号*/SigIgn: 0000000000000000 /*存放被忽略的信号*/SigCgt: 00000002000094e8 /*存放被俘获到的信号*/CapInh: 0000000000000000 /*能被当前进程执行的程序的继承的能力*/CapPrm: 0000000000000000 /*进程能够使用的能力,可以包含CapEff中没有的能力,这些能力是被进程自己临时放弃的*/CapEff: 0000000000000000 /*是CapPrm的一个子集,进程放弃没有必要的能力有利于提高安全性*/CapBnd: ffffffffffffffffCpus_allowed: 3 /*可以执行该进程的CPU掩码集*/Cpus_allowed_list: 0-1voluntary_ctxt_switches: 223 /*进程主动切换的次数*/nonvoluntary_ctxt_switches: 406 /*进程被动切换的次数*/ 1234567<!--Groups中1015正好对应的是SDCARD_RW-->#define AID_MEDIA 1013 /* mediaserver process */#define AID_DHCP 1014 /* dhcp client */#define AID_SDCARD_RW 1015 /* external storage write access */#define AID_VPN 1016 /* vpn system */#define AID_KEYSTORE 1017 /* keystore subsystem */#define AID_USB 1018 /* USB devices */ 2.5 Android的Permission与UID/GID的mapping语法: UID assigning permission: < assign-permission name="permission_name" uid="target_uid" /> GIDs Mapping: <permission name="permission_nema" > <group gid="assigned gid" /> <group gid="assigned gid" /> ..... </permission> 发生时刻:安装时 etc/permissions 任何符合以上语法的在system/etc/permissions下面的xml文件,都会被系统读取来parse并进行UID/GID的mapping. 比如Platform.xml(check代码) frameworks/base/data/etc/platform.xml 安全性 任何应用都可以为自己的permission assign GID? 当然不行! 只有Root用户才允许新增或者改写. 并且对于ROOT用户组的用户也只有r权限 如下为手机system/etc/permissions目录下的文件权限 12345678shell@android:/system/etc # ls -l ....drwxr-xr-x root root 2013-07-09 21:54 permissions....shell@android:/system/etc # cd permissionsshell@android:/system/etc/permissions # ls -l platform.xml-rw-r--r-- root root 9466 2008-08-01 20:00 platform.xml 3.Android中的组件的安全机制Android的4大组件及组件间的通信 组件的public和private 组件的权限分配 3.1 组件的权限分配(demo)3.1.1.Activity服务端增加相应的权限: 123456789101112131415161718192021222324252627282930313233343536373839<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tu.test.diyPermission" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <!-- 自定义Activity权限 start --> <permission android:name="com.tu.test.diyPermission.DIYPERMISSION" /> <!-- 自定义Activity权限 end --> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name="com.tu.test.diyPermission.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 自定义Activity权限 start --> <activity android:name=".NewPageActivity" android:exported="true" android:permission="com.tu.test.diyPermission.DIYPERMISSION" > <intent-filter> <action android:name="com.tu.test.diyPermission.NewPageActivity" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!-- 自定义Activity权限 end --> </application></manifest> 客户端必须申请许可: 否则会抛出: java.lang.SecurityException: Permission Denial: starting Intent { act=com.tu.test.diyPermission.NewPageActivity cmp=com.tu.test. diyPermission/.NewPageActivity (has extras) } from ProcessRecord {41efe3e8 8642:com.tu.test.diyPermission.client/u0a204} (pid=8642, uid=10204) requires com.tu.test.diyPermission.DIYPERMISSION 123456789101112131415161718192021222324252627282930<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tu.test.diyPermission.client" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <!-- 申请应用DiyPermission中的自定义Activity权限 start --> <uses-permission android:name="com.tu.test.diyPermission.DIYPERMISSION" /> <!-- 申请应用DiyPermission中的自定义Activity权限 end --> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name="com.tu.test.diyPermission.client.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest> 3.1.2.service服务端增加相应的权限: 12345678910111213141516171819<manifest ...> <permission android:name="com.tu.test.diyPermission.DIYPERMISSION_SERVICE" android:label="diy Permission service" android:description="@string/hello_world"/> <application ...> .... <service android:name=".service.NewService" android:enabled="true" android:exported="true" android:permission="com.tu.test.diyPermission.DIYPERMISSION_SERVICE" > <intent-filter> <action android:name="com.tu.test.diyPermission.SERVICE_ACTION" /> </intent-filter> </service> </application> </manifest> 客户端必须申请许可: 否则会抛出: Caused by: java.lang.SecurityException: Not allowed to bind to service Intent { act=com.tu.test.diyPermission.SERVICE_ACTION } 1<uses-permission android:name="com.tu.test.diyPermission.DIYPERMISSION_SERVICE" /> 3.1.3.ContentProvide服务端增加相应的权限: 读,写,访问权限. 1234567891011121314151617<manifest ...> <permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER" /> <permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_WRITE" /> <permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_READ" /> <application ...> .... <provider android:name=".provider.NewContentProvider" android:exported="true" android:authorities="com.tu.test.diyPermission.providers.PersonProvider" android:readPermission="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_READ" android:writePermission="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_WRITE" android:permission="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER" /> </application> </manifest> 客户端必须申请许可: 否则会抛出: Caused by: java.lang.SecurityException: Permission Denial: opening provider com.tu.test.diyPermission.provider.NewContentProvider from ProcessRecord{4236abd0 22086:com.tu.test.diyPermission.client/u0a204} (pid=22086, uid=10204) requires com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_READ or com.tu.test. diyPermission.DIYPERMISSION_CONTENTPROVIDER_WRITE 1234<uses-permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER" /><uses-permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_WRITE" /><uses-permission android:name="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_READ" /> 3.1.4.BroadcastReceiver服务端增加权限: 12345678910111213141516<manifest ...> .... <permission android:name="com.tu.test.diyPermission.BROADCESTRECEIVER" /> <application ...> .... <receiver android:name=".receiver.NewReceiver" android:permission="com.tu.test.diyPermission.BROADCESTRECEIVER" > <intent-filter> <action android:name="com.tu.test.diyPermission.BROADCESTRECEIVER_ACTION" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> </application></manifest> 客户端必须申请许可: 否则服务端周日接收不到信息 1<uses-permission android:name="com.tu.test.diyPermission.BROADCESTRECEIVER" /> 4.应用安装4.1.应用安装的安全性考虑和调用方式应用安装是一个高特权/风险操作,所以必须是可知/可控,主流实现方式:客户只能委派而不能直接操作. 调用安装传统模式: 发送Intent给系统的Package Install app 12345Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent); 特权安装模式:系统的Package Install App内部会调用PackageManagerService的Install Package, 该操作与android.permission.INSTALL_PACKAGES 绑定,且该peimission的protection level是 signature|system 所谓的静默安装方式只存在Root手机上,开发者可以选择: 基于pm cmd : pm install -r 4.2.应用安装流程之UID/GID的分配基于Android4.3原代码目录 frameworks/base/services/java/com/android/srver/pm/PackageManagerService.java 1234567891011//installPackage 将会调用 scanPackageLIprivage PackageParser.Package scanPackageLI(PackageParser.Package pkg,int parseFlags,int scanMode,log currentTime,UserHandle user){ .... pkgSetting = mSettings.getPackageLPw(pkg,origPackage,realName,suid,destCodeFile,destResourceFile,pkg.applicationInfo.nativeLibraryDir,pkg.applicationInfo.flags,user,false); ..... pkg.applicationInfo.uid = pkgSetting.appId;//说明是由Settings里面产生Uid,获取到appId .... //invoke installer to do the actual installation int ret = createDataDirsLI(pagName,pkg.applicationInfo.uid,pkg.applicationInfo.seinfo);//创建工作目录和权限设置} fameworks/base/services/java/com/android/server/pm/Settings.java 123456789101112131415161718192021222324252627282930313233private PackageSetting getPackageLPw(String name,PackageSetting origPackage,String realName,SharedUserSetting shardUser,File codePath,File resourcePath,String nativeLibraryPathString,int vc,int pkgFlags,UserHandle installUser,Boolean add , boolean allowlnstall){ PackageSetting p = mPackages.get(name); if(p!=null){ if(!p.codePath.equals(codePath)){ //check to see if its a disabled syste ..... }else{ //Assign new user id p.appId = newUserIdLPw(p);//分配Uid } }}//分配Uid的方法private int newUserIdLPw(Object obj){ //Let's be stupidly inefficient for now... final int N = mUserIds.size(); for(int i = 0;i<N;i++){ if(mUserIds.get(i)==null){ mUserIds.set(i,obj); return Process.FIRST_APPLICATION_UID + i;//这就为什么用户安装的uid都是大于10000的原因. } } //None left? if(N>(Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)){ return -1; } mUserIds.add(obj); return Process.FIRST_APPLICATION_UID+N;}frameworks/base/core/java/android/os/Process.javapublic static final int FIRST_APPLICATION_UID=10000;//这就为什么用户安装的uid都是大于10000的原因. 4.3.应用安装流程之工作目录的创建和权限设置frameworks/base/services/java/com/android/server/pm/PackageManagerService.java 123456789101112131415161718private int createDataDirsLI(String packageName,int uid,String seinfo){ int [] users = sUserManager.getUserIds(); int res = mlnstaller.install(packageName,uid,uid,seinfo);//从这里可以看出uid gid都被设置成uid if(res<0){ return res; } //only for multi-user//Android 4.1之后支持多用户 for(int user: users){ if(user!=0){ res = mlnstaller.createUserData(packageName,UserHandle.getUid(user,uid),user); if(res<0){ return res; } } } return res;} frameworks/base/services/java/com/anddroid/server/pm/Installer.java 1234567891011121314151617181920212223242526272829303132private boolean connect(){//Installer一起来会进行初始化 if(mSocket!=null){ return true; } try{ mSocket = new LocalSocket(); //本地Socket LocalSocketAddress address = new LocalSocketAddress("installd",LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream(); }catch(IOException ex){ disconnect(); return false; } return true;}private int install(String name,int uid , int gid , String seinfo){ //内部协议 StringBuilder builder = new StringBuilder("install"); builder.append(''); builder.append(name); builder.append(''); builder.append(uid); builder.append(''); builder.append(gid); builder.append(''); builder.append(seinfo!=null?seinfo:"!"); return execute(builder.toString());//会调用mOut mIn发数据} frameworks/native/cmds/installd/installd.c 1234567891011121314151617181920 int main(const int argc, const char *argv[]) { //here the SOCKET_PATH is installd .......... lsocket = android_get_control_socket(SOCKET_PATH); if (listen(lsocket, 5)) {} fcntl(lsocket, F_SETFD, FD_CLOEXEC); for (;;) { alen = sizeof(addr); s = accept(lsocket, &addr, &alen); fcntl(s, F_SETFD, FD_CLOEXEC); if(readx(s,buf,count)){} if(execute(s,buf))break; }}static int do_install(char **arg, char reply[REPLY_MAX]){ return install(arg[0], atoi(arg[1]), atoi(arg[2]), atoi(arg[3])); /* pkgname, uid, gid */} framework/native/cmds/installd/commands.c 1234567891011121314151617181920int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid){ ..... if(create_pkg_path(pkgdir,pkgname,PKG_DIR_POSTEIX,0)){//创建工作目录结构,/data/data/包名/ ALOGE("cannot create package path\n"); return -1; }.... if (mkdir(pkgdir, 0751) < 0) {//创建目录,并设置权限 0751 uid7(4+2+1)读写执行,gid5(4+1)读执行,其它1(1)执行 ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); return -1; } if (chmod(pkgdir,0751) < 0) {//再设置一次权限 ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); unlink(pkgdir); return -errno; }..... if(chown(pkgdir,uid,gid)<0){//因设置权限时是使用root用户,些处再进行权限修改.把uid root , gid root 修改成应用的uid,与gid. ..... }} 5.Android中系统Service的安全5.1 Binder的安全Binder的作用:实现以IPC的RPC,完成远程业务范围. Android进程间通信(IPC)机制Binder简要介绍和学习计划 SEAndroid安全机制对Binder IPC的保护分析 5.2 ServiceManager Add Service的安全限制Service Manager Process的作用: Naming Resolver,用于RPC框架中 AddService GetSetvice 5.3 Zygote的Process ForkAndroid系统进程Zygote启动过程的源代码分析 Android深入浅出之Zygote 5.4 Zygote的Socket安全检查空 6.Android中的ContentProvider以及基于URI的安全6.1.ContentProvider的作用6.1.1.软件设计更优美(官方)屏蔽内部数据存储操作的差异性 对外提供一致的数据操作方式 抽象/共性---->都是数据操作 6.1.2.进程间数据共享 Proxy| Binder | Content Provider client Process ------------------------------- > Service Process 6.2.权限临时继承的需求临时委派使得委托者的权限临时提升(类似Root-setUID模式) 6.3.配置ContentProvider允许临时委派权限12345678910111213141516171819<provider android:readPermission="com.tu.test.diyPermission.DIYPERMISSION_CONTENTPROVIDER_READ" android:grantUriPermissions="true" /> <!--允许派遣给uri--> <provider ...> .... <grant-uri-permission android:path="/comtu/"/> <!--允许派遣给限制URI的路径--> </provider> <provider ...> .... <grant-uri-permission android:pathPrefix="/abc/"/> <!--允许派遣给限制URI的路径前缀--> </provider> <provider ...> .... <grant-uri-permission android:pathPattern=".*public.*"/> <!--允许派遣给限制URI的路径通过正则--></provider> 6.4.基于URI的权限临时委派6.4.1 基于API1234567//委派uri权限临时委派给com.example.testapps.test2,权限为FLAG_GRANT_READ_URI_PERMISSIONuri = "content://com.example.testapps.test1.mailprovider/attachments/42";Context.grantUriPermission("com.example.testapps.test2",uri,Intent.FLAG_GRANT_READ_URI_PERMISSION);//一定时间之后将收回委派的权限.否则可能存在安全隐患.需要思考的时什么时候收回.uri = "content://com.example.testapps.test1.mailprovider/attachments/42";Context.revokeUriPermission(uri,Intent.FLAG_GRANT_READ_URI_PERMISSION); 6.4.2 基于Intent12345Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(uri,"image/gif");startActivity(intent); 权限的生命周期: Activity Start --> Destro即Activity的生命周期 7.Android的Policy模式和多设备绑定7.1.Android的Policy模式一个移动平台它解决用户隐私,相当的一系列控制或者说是跟用户的交互模式. 安装时安全提问 列出所有权限,提示用户是否安装. All or Nothing None Runtime Control None Recallable Disable untrust source by default 7.2.MR2开始的AppOps(安卓4.3开始)如 LBE 等安全卫士控制权限 >基于AppOps Service >定义Ignore , Allow Reject 3种Policy >Hook and check Permission 7.3AppOps对开发者的影响开发者已经申请权限时,但被LBE等安全卫士等的权限限制. 会导致权限一样获取不到.为了代码健壮性,需加多一些try catch.防止异常 7.4设备绑定应用与设备绑定的需求前景 如,计费应用. >同时基于SIM卡和Device的绑定(如BREW下载,单机内的下载卡可用.基于SIM卡计费,运营商间排斥) >仅基于Device的绑定(如Google Store下载,当时下载的Device可用) >实现: 加密 per device per SIM卡 Device Key: ESN/MEID/IMEI or random generate SIM ID : IMSI 7.5跨设备使用基于Account ID的云端管理 Device1 --buy it using xx@x.x account -->AppStore<----Down load freely--(Register as xx@x.x) --Device2 8.应用内计费和App2SDCard8.1 应用内计费什么是应用内计费 In App purchase or In app Billing(IAP,IAB) 直接在应用内进行Paymen以unLock某些功能,或者买某些道具等 应用内计费的需求 可支付途径(信用卡,手机卡等) 安全性: 面向用户 可知 可控 面向应用 可信(避免免费使用收费内容) 解决方案 >计费Server接口保密且Transiction加密(SSL) >仅允许配套的安全本地组件与计费Server通信,且安全本地组件负责 与用户的"显式"交互,同时提供API给Client >Clent仅允许调用本地计费安全组件来委派Transiction >Response的signature + Nounce (防止重放攻击) 8.2 SD卡安装应用的安全策略绑定设备 >绑定perDevice使得防Export:应用以及应用数据(SD卡允许Export) >以加密实现之:例子,应用安装至SD卡(.Android_Secure in SDCard) ASEC的不可访问性 由于.Android_Secure的加密特性,所以需要禁止应用直接Access该Folder(允许Access SDCard上的其他任何内容) 9.Android中的多用户安全4.2开始可以支持 9.1需求场景已有的例子/ Windows/Linux多用户 Android中的差别 UID/GID跟着User走 UID/GID和User的区分和绑定 应用的可控共享 共享,不存在多份Code 可控,控制谁可见. 数据的多用户独立 工作目录 External Storage(外部存储器) 9.2UserManagerService作用: 管理User的属性信息: 设置/获取用户的UserId,Name,Icon,RestrictProfile等 管理User:创建,删除等 UserId(UserHandle)是Process的属性,不100%等同于当前设置切换的用户 一切与多用户相关的运行时行为(比如Mount的SDCard等)与进程的UserID所属有关, 而与当前设置中的当前用户没有必然联系 典型的例子: 通过adb shell的方式,永远只能访问User0的SDCard:/data/media/0,不管切换哪个用户 通过文件管理器应用访问的SDCard则与当前用户相关. 原因: adb shell只存在User0,不管在哪个当前设置用户下. 9.3对开发者的影响永远使用相对路径(基于工作目录)以及基于Environment.getXXXX来获取你感兴趣的路径(Environment内部 会处理多用户).否则会出现路径错误而访问被拒绝的问题. 12New File("setting/setting.log");New File(Environment.getExternalStorageDirectory+"SharePic/a.png"); 10.Android Superuser机制讲解10.1 ROOT的作用Customization(定制,用户化) 任何需要特权的操作 10.2 ROOT的第一步:寻找漏洞并安装特权文件Hack会寻找漏洞,如UID设置设备(ADB) 手机Root后,最重要的是,给手机安装了su程序和superuser apk. su一般被安装在/system/xbin或者/system/bin 10.3 SU的sUID的特性Android的App授权获取Root权限,其实不是App自身的权限提升了,而是通过具有Root权限的Sh流来执行shell命令. 123shell@android:/system/xbin # ls su -lls su -l-rwsr-sr-x root root 79500 2008-08-01 20:00 su 这里可以看到,su是Owner和Group分别为Root,Root.Other用户具有Execute权限,另外,su设置了suid和sgid,这个非常重要, 使得Su进程可以提升自身的EUID. 10.4 SU的核心代码分析在JB MR2(4.3)之前,Apk内部可以通过Java的Runtime执行一个 具有Root-setUid的可执行文件而提升Effective UID来完成一些特权操作,典型的Root包中的su就是这个原理. JB MR2中,修补了改漏洞. 10.5 MR2后的方案:SU Deamon Service怎么办? Native Service依然可以利用Root-setUID的su提升权限 11.SEAndroid11.1 DAC和MACDAC 自主访问控制 主体(Process)的Capability觉得了它能访问和操作什么? Root进程可以访问和操作一切! 传统(legacy)Linux的安全模式,基于UID/GID/Capability MAC 强制访问控制 系统的Policy觉得了主体能操作访问哪些客体 即便是ROOT进程,系统Policy配置了你能做什么,你只能做什么,在MAC模式下,ROOT进程和普通进程是无区别对待的. 11.2 基于Label的MAC每个主体/客体在运行时都绑定一个标签(Label) 该标签又称为Security Context Security Context的构成 User : Role : Type : SecurityLevel 比如: u:r:zygote:s0 Type Enhancement Security Context中的Type主要是用于Policy的设定,即Policy 一般的Rule是: Allow Type Type : Operation Allow appdomain zygote_tmpfs:file read; 所以,Type被实际用于"授权"的Decision,所以称之为Type Enhancement ls命令的SELinux版本: ls -l -Z Ps命令的SELinux版本: ps -Z 11.3 推荐读物Your visual how-to guide for SELinux policy enforcement SELinux实例:使用安全增强的Linux Demo下载Android中的组件的安全机制之组件的权限分配(Demo)Activity Service ContentProvide BroadcastReceiver四大组件不同App数据通信:Demo]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android安全模式机制</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android安全模式机制之二(操作系统现代安全体系基础概念)]]></title>
<url>%2F%2F2014%2F10%2F26%2Fandroid_safe_mode_mechanism_two.html</url>
<content type="text"><![CDATA[h3 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } h6 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } 目录: 1.进程和进程边界 2.多用户和多用户边界 3.进程和文件的UID/GID (UserID/groupId) 3.1文件资源的权限力度:UID/GID 3.2文件的可操作权限 3.3进程的标识: PID , UID , GID , GIDs 3.4Name和ID的映射 3.5Chmod和chown命令介绍 3.5.1Chmod 3.5.2Chown 4.UID/GID的衔接 5.进程的RealUID和EffectiveUID 6.文件的setUID标识 7.Capability 7.1进程的Capability 7.2文件的Capability 7.3Capability BoundSet 7.4Spawn进程的Capability 进程和进程边界进程和线程 可执行文件:不活动就是废物 进程:可执行文件的活动表现,一次生命的历练 线程:CPU(核)的调度单位,并发的执行序列,进程的多管齐下 资源和调度. 进程边界的安全围栏: Crash的不可扩延性 全局数据和服务的不可访问性 多用户和多用户边界需求背景: 资源缺乏 中央统一管理 多用户的边界: 独立的工作目录 可操作/访问的资源 资源分类 权限管理 可执行的操作 操作分类 权限管理 多用户特性标识(linux): UID 和GID Name只是供看的 Identifier才是系统层面的标识 用户的行为是一系列进程的行为 特性标识其实是进程的UID/GID 进程和文件的UID/GID (UserID/groupId)文件资源的权限力度:UID/GID1.文件是一类资源 2.在Linux中,甚至一切皆是文件,Socket,Driver 3.文件资源对不同Target(用户)的不同操作权限的需求应运而生 4.某些场景下,允许多个不同的Target/用户(而不是一个)具有一致的操作权限,怎么办? Id===> Gid===> 多个用户可以属于一个GID,一个用户可以属于多个GIDs 5.所以文件权限的管理力度区分3类群体:属于特定UID的用户,属于特定GID的用户(们),其他用户 6.一个上帝用户存在:ROOT, 其UID = 0 , 上帝用户永远满足属于任何UID 文件的可操作权限1.文件/文件夹的可读 r 2.文件/文件夹的可写 w 3.文件/文件夹的可执行 x 123456789101112131415161718192021$ ls -ltotal 20 权限列表 UID GID-rw-r--r-- 1 comtu Administ 879 Feb 25 17:39 404.html-rw-r--r-- 1 comtu Administ 2800 Feb 25 17:39 README.md-rw-r--r-- 1 comtu Administ 2909 Feb 25 17:39 Rakefile.rb-rw-r--r-- 1 comtu Administ 1609 Feb 25 17:39 _config.ymldrwxr-xr-x 4 comtu Administ 0 Feb 25 17:39 _datadrwxr-xr-x 7 comtu Administ 4096 Feb 25 17:39 _includesdrwxr-xr-x 5 comtu Administ 0 Feb 25 17:39 _layoutsdrwxr-xr-x 3 comtu Administ 0 Feb 25 17:39 _pluginsdrwxr-xr-x 18 comtu Administ 8192 Jul 1 17:36 _postsdrwxr-xr-x 13 comtu Administ 4096 Jul 1 13:30 _site-rw-r--r-- 1 comtu Administ 10 Feb 25 17:39 baidu_verify_html-rw-r--r-- 1 comtu Administ 1672 Feb 25 17:39 index.htmldrwxr-xr-x 1 comtu Administ 4096 Feb 25 17:39 pagedrwxr-xr-x 1 comtu Administ 4096 Jun 25 16:18 res-rw-r--r-- 1 comtu Administ 59 Jun 25 17:45 robots.txt-rw-r--r-- 1 comtu Administ 350 Apr 13 17:53 search.xml-rw-r--r-- 1 comtu Administ 209 Feb 25 17:39 sitemap.txt drwxr-xr-x d|rwx|r-x|r-x 0|123|456|789 文件夹与文件标识 0 : d文件夹 -文件 UID用户 1 : 可读 2 : 可写 3 : 可执行 GID用户 4 : 可读 5 : 不可写 6 : 可执行 其他用户 7 : 可读 8 : 不可写 9 : 可执行 进程的标识: PID , UID , GID , GIDsPID : 进程的Unique Identifier(唯一标识) . 每次Running的PID可能相同,或者不同,由系统分配 UID : 进程的身份标识.每次运行,即便重启后默认都相同 GID : 进程的(组)身份标识.每次运行,即便重启后默认都相同.不同进程允许有相同的GID(组用户身份标识). 同一进程允许属于多个GID. GIDs: 进程所属的全部GID Name和ID的映射Android原代码之Name和ID映射表 /system/core/include/private/android_filesystem_config.h 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* This file is used to define the properties of the filesystem# images generated by build tools (mkbootfs and mkyaffs2image) and# by the device side of adb.*/#ifndef _ANDROID_FILESYSTEM_CONFIG_H_#define _ANDROID_FILESYSTEM_CONFIG_H_#include <string.h>#include <sys/stat.h>#include <sys/types.h>/* This is the master Users and Groups config for the platform.# DO NOT EVER RENUMBER.*/#define AID_ROOT 0 /* traditional unix root user */#define AID_SYSTEM 1000 /* system server */#define AID_RADIO 1001 /* telephony subsystem, RIL */#define AID_BLUETOOTH 1002 /* bluetooth subsystem */#define AID_GRAPHICS 1003 /* graphics devices */#define AID_INPUT 1004 /* input devices */#define AID_AUDIO 1005 /* audio devices */#define AID_CAMERA 1006 /* camera devices */#define AID_LOG 1007 /* log devices */#define AID_COMPASS 1008 /* compass device */#define AID_MOUNT 1009 /* mountd socket */#define AID_WIFI 1010 /* wifi subsystem */#define AID_ADB 1011 /* android debug bridge (adbd) */#define AID_INSTALL 1012 /* group for installing packages */#define AID_MEDIA 1013 /* mediaserver process */#define AID_DHCP 1014 /* dhcp client */#define AID_SDCARD_RW 1015 /* external storage write access */#define AID_VPN 1016 /* vpn system */#define AID_KEYSTORE 1017 /* keystore subsystem */#define AID_USB 1018 /* USB devices */#define AID_GPS 1021 /* GPS daemon */#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */#define AID_RFU1 1023 /* RFU */#define AID_RFU2 1024 /* RFU */#define AID_NFC 1025 /* nfc subsystem */#define AID_SHELL 2000 /* adb and debug shell user */#define AID_CACHE 2001 /* cache access */#define AID_DIAG 2002 /* access to diagnostic resources *//* The 3000 series are intended for use as supplemental group id's only. * They indicate special Android capabilities that the kernel is aware of. */#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */#define AID_NET_RAW 3004 /* can create raw INET sockets */#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */#define AID_QCOM_ONCRPC 3006 /* can read/write /dev/oncrpc files */#if defined(MOTOROLA_UIDS)#define AID_MOT_ACCY 9000 /* access to accessory */#define AID_MOT_PWRIC 9001 /* power IC */#define AID_MOT_USB 9002 /* mot usb */#define AID_MOT_DRM 9003 /* can access DRM resource. */#define AID_MOT_TCMD 9004 /* mot_tcmd */#define AID_MOT_SEC_RTC 9005 /* mot cpcap rtc */#define AID_MOT_TOMBSTONE 9006#define AID_MOT_TPAPI 9007 /* mot_tpapi */#define AID_MOT_SECCLKD 9008 /* mot_secclkd */#endif // MOTOROLA_UIDS#define AID_MISC 9998 /* access to misc storage */#define AID_NOBODY 9999#define AID_APP 10000 /* first app user */#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)struct android_id_info { const char *name; unsigned aid;};static const struct android_id_info android_ids[] = { { "root", AID_ROOT, }, { "system", AID_SYSTEM, }, { "radio", AID_RADIO, }, { "bluetooth", AID_BLUETOOTH, }, { "graphics", AID_GRAPHICS, }, { "input", AID_INPUT, }, { "audio", AID_AUDIO, }, { "camera", AID_CAMERA, }, { "log", AID_LOG, }, { "compass", AID_COMPASS, }, { "mount", AID_MOUNT, }, { "wifi", AID_WIFI, }, { "dhcp", AID_DHCP, }, { "adb", AID_ADB, }, { "install", AID_INSTALL, }, { "media", AID_MEDIA, }, { "nfc", AID_NFC, }, { "shell", AID_SHELL, }, { "cache", AID_CACHE, }, { "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, { "qcom_oncrpc", AID_QCOM_ONCRPC, }, { "sdcard_rw", AID_SDCARD_RW, }, { "vpn", AID_VPN, }, { "keystore", AID_KEYSTORE, }, { "usb", AID_USB, }, { "gps", AID_GPS, }, { "inet", AID_INET, }, { "net_raw", AID_NET_RAW, }, { "net_admin", AID_NET_ADMIN, },#if defined(MOTOROLA_UIDS) { "mot_accy", AID_MOT_ACCY, }, { "mot_pwric", AID_MOT_PWRIC, }, { "mot_usb", AID_MOT_USB, }, { "mot_drm", AID_MOT_DRM, }, { "mot_tcmd", AID_MOT_TCMD, }, { "mot_sec_rtc", AID_MOT_SEC_RTC, }, { "mot_tombstone", AID_MOT_TOMBSTONE, }, { "mot_tpapi", AID_MOT_TPAPI, }, { "mot_secclkd", AID_MOT_SECCLKD, },#endif { "misc", AID_MISC, }, { "nobody", AID_NOBODY, },};#define android_id_count \ (sizeof(android_ids) / sizeof(android_ids[0])) struct fs_path_config { unsigned mode; unsigned uid; unsigned gid; const char *prefix;};/* Rules for directories.# These rules are applied based on "first match", so they# should start with the most specific path and work their# way up to the root.*/static struct fs_path_config android_dirs[] = { { 00770, AID_SYSTEM, AID_CACHE, "cache" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data/app" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data/dalvik-cache" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data/data" }, { 00771, AID_SHELL, AID_SHELL, "data/local/tmp" }, { 00771, AID_SHELL, AID_SHELL, "data/local" }, { 01771, AID_SYSTEM, AID_MISC, "data/misc" }, { 00770, AID_DHCP, AID_DHCP, "data/misc/dhcp" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data" }, { 00750, AID_ROOT, AID_SHELL, "sbin" }, { 00755, AID_ROOT, AID_SHELL, "system/bin" }, { 00755, AID_ROOT, AID_SHELL, "system/vendor" }, { 00755, AID_ROOT, AID_SHELL, "system/xbin" }, { 00755, AID_ROOT, AID_ROOT, "system/etc/ppp" }, { 00777, AID_ROOT, AID_ROOT, "sdcard" }, { 00771, AID_SYSTEM, AID_SYSTEM, "sd-ext" }, { 00755, AID_ROOT, AID_ROOT, 0 },};/* Rules for files.# These rules are applied based on "first match", so they# should start with the most specific path and work their# way up to the root. Prefixes ending in * denotes wildcard# and will allow partial matches.*/static struct fs_path_config android_files[] = { { 00440, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.rc" }, { 00550, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.sh" }, { 00440, AID_ROOT, AID_SHELL, "system/etc/init.trout.rc" }, { 00550, AID_ROOT, AID_SHELL, "system/etc/init.ril" }, { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" }, { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/main.conf" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/input.conf" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/audio.conf" }, { 00444, AID_NET_BT, AID_NET_BT, "system/etc/bluetooth/blacklist.conf" }, { 00640, AID_SYSTEM, AID_SYSTEM, "system/etc/bluetooth/auto_pairing.conf" }, { 00444, AID_RADIO, AID_AUDIO, "system/etc/AudioPara4.csv" }, { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/*" }, { 00555, AID_ROOT, AID_ROOT, "system/etc/rc.*" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" }, { 00644, AID_APP, AID_APP, "data/data/*" }, /* the following three files are INTENTIONALLY set-gid and not set-uid. * Do not change. */ { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, { 02755, AID_SYSTEM, AID_GRAPHICS, "system/bin/screenshot" }, /* the following five files are INTENTIONALLY set-uid, but they * are NOT included on user builds. */ { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/procrank" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, /* the following file is INTENTIONALLY set-uid, and IS included * in user builds. */ { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/hcitool" }, { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, { 00755, AID_ROOT, AID_SHELL, "system/vendor/bin/*" }, { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, "init*" }, { 00750, AID_ROOT, AID_SHELL, "system/etc/init.d/*" }, { 00644, AID_ROOT, AID_ROOT, 0 },};static inline void fs_config(const char *path, int dir, unsigned *uid, unsigned *gid, unsigned *mode){ struct fs_path_config *pc; int plen; pc = dir ? android_dirs : android_files; plen = strlen(path); for(; pc->prefix; pc++){ int len = strlen(pc->prefix); if (dir) { if(plen < len) continue; if(!strncmp(pc->prefix, path, len)) break; continue; } /* If name ends in * then allow partial matches. */ if (pc->prefix[len -1] == '*') { if(!strncmp(pc->prefix, path, len - 1)) break; } else if (plen == len){ if(!strncmp(pc->prefix, path, len)) break; } } *uid = pc->uid; *gid = pc->gid; *mode = (*mode & (~07777)) | pc->mode; #if 0 fprintf(stderr,"< '%s' '%s' %d %d %o >\n", path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode);#endif}#endif#endif Android提供了dumpsys工具来dump出所有的服务信息,通过以下命令可以查看系统注册的所有服务:adb shell dumpsys dumpsys 用来给出手机中所有应用程序的信息,并且也会给出现在手机的状态。 因为内容太多把dumpsys的内容存储到C盘文件中方便查看.如下 123456789101112131415C:\Users\comtu>adb shellshell@android:/ $ sushell@android:/ # cd datashell@android:/data # mkdir testshell@android:/data # cd testshell@android:/data/test # dumpsys > dumpsys.txtshell@android:/data/test # ls -lls -l-rw-rw-rw- root root 1706976 2015-07-03 08:57 dumpsys.txtshell@android:/data/test # exitexitshell@android:/ $ exitexitC:\Users\comtu>adb pull /data/test/dumpsys.txt c:/5649 KB/s (1706976 bytes in 0.295s) 其中dumpsys.txt文件一段关于QQ配置的片段可查找到如下内容: userId=10081 gids=[3003, 1028, 1015] <---gids可在android_filesystem_config.h查找到对应的映射关系 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465Package [com.tencent.mobileqq] (426f1258): userId=10081 gids=[3003, 1028, 1015] pkg=Package{42b7e890 com.tencent.mobileqq} codePath=/data/app/partner-QQ.apk resourcePath=/data/app/partner-QQ.apk nativeLibraryPath=/data/app-lib/partner-QQ versionCode=122 targetSdk=7 versionName=4.7.0 applicationInfo=ApplicationInfo{42b136f8 com.tencent.mobileqq} flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] dataDir=/data/data/com.tencent.mobileqq supportsScreens=[small, medium, large, resizeable, anyDensity] usesOptionalLibraries: com.google.android.media.effects com.motorola.hardware.frontcamera timeStamp=2015-05-05 17:36:43 firstInstallTime=2015-05-05 17:36:43 lastUpdateTime=2015-05-05 17:36:43 signatures=PackageSignatures{426f1320 [426f1ed0]} permissionsFixed=true haveGids=true installStatus=1 pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] User 0: installed=true blocked=false stopped=true notLaunched=false enabled=0 grantedPermissions: android.permission.READ_EXTERNAL_STORAGE android.permission.CHANGE_WIFI_MULTICAST_STATE com.tencent.photos.permission.DATA android.permission.GET_TASKS android.permission.WRITE_EXTERNAL_STORAGE android.permission.WRITE_CALL_LOG com.tencent.msg.permission.pushnotify android.permission.ACCESS_WIFI_STATE android.permission.ACCESS_COARSE_LOCATION android.permission.READ_CONTACTS android.permission.CALL_PHONE android.permission.WRITE_CONTACTS com.tencent.permission.VIRUS_SCAN android.permission.READ_PHONE_STATE android.permission.READ_CALENDAR android.permission.READ_SMS android.permission.CAMERA android.permission.ACCESS_FINE_LOCATION android.permission.BROADCAST_STICKY android.permission.PERSISTENT_ACTIVITY android.permission.FLASHLIGHT android.permission.RECORD_AUDIO android.permission.WAKE_LOCK android.permission.ACCESS_NETWORK_STATE com.tencent.msf.permission.ACCOUNT_NOTICE com.android.launcher.permission.INSTALL_SHORTCUT com.tencent.msf.permission.account.sync android.permission.SEND_SMS com.android.launcher.permission.UNINSTALL_SHORTCUT android.permission.KILL_BACKGROUND_PROCESSES android.permission.MODIFY_AUDIO_SETTINGS android.permission.DISABLE_KEYGUARD android.permission.WRITE_CALENDAR com.qq.qcloud.permission.ACCESS_ALBUM_BACKUP_LIST android.permission.SYSTEM_ALERT_WINDOW android.permission.WRITE_SETTINGS android.permission.INTERNET android.permission.CHANGE_WIFI_STATE android.permission.VIBRATE android.permission.READ_CALL_LOG com.android.launcher.permission.READ_SETTINGS android.permission.CHANGE_NETWORK_STATE Chmod和chown命令介绍Chmod1.文件R/W/X的系统内部采用3Bit表示,R为最高位比特,置位为0x04, W为中间比特,置位为0x02,X为最低比特,置位为0x01 4(读R) , 2(写W) , 1(执行X) 7=4+2+1 所有权限 6=4+2 读写 5=4+1 读执行 4 读 3=3+1 写执行 2 1 664表示给予UID,GID读写权限,给予其它只读权限 2.Shell中表示时,置位使用相应R/W/X表示,未置位使用- 3.操作文件面向群体的操作权限时,使用Chmod,可以直接使用数字, 也可使用助记符( a:all , u:owner user , g:group , +:add one premission , -:remove one permission) 1234567891011121314-rw-rw-r-- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test # chmod 777 test.txtchmod 777 test.txtshell@android:/data/test # ls -lls -l-rwxrwxrwx system system 12 2015-07-02 18:02 test.txtshell@android:/data/test #shell@android:/data/test # chmod 664 test.txtchmod 664 test.txtshell@android:/data/test # ls -lls -l-rw-rw-r-- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test # Chown通过chown改变文件的拥有者和群组。在更改文件的所有者UID或所属群组GID时,可以使用用户名称和用户识别码设置。 Shell命令中通常采用Name方式修改,而不是ID方式.普通用户不能将自己的文件改变成其他的拥有者。其操作权限一般为管理员。 一般格式: chown newUID:newGID FileName 123456789101112131415161718shell@android:/data # mkdir testmkdir testshell@android:/data # cd testcd testshell@android:/data/test # echo "hello world" > test.txtecho "hello world" > test.txtshell@android:/data/test # lslstest.txtshell@android:/data/test # ls -lls -l-rw-rw-rw- root root 12 2015-07-02 18:02 test.txtshell@android:/data/test # chown system:system test.txtchown system:system test.txtshell@android:/data/test # ls -lls -l-rw-rw-rw- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test # UID/GID的衔接1.Linux一切皆是文件 2.文件基于UID/GID来划分它的面向群体,对它的面向群体定义不同的操作权限 3.用户的行为映射为进程的运行 4.进程的运行使用进程的UID/GID来标识自己的身份 5.进程的UID/GID<=====>文件的UID/GID 完美衔接~~ 6.进程的UID/GID除了被授予可操作文件的范畴外,非文件范畴的需要进行权限控制的操作 (如重启系统等特权操作)继续通过进程的UID/GID身份来进行控制和授权. 7.比如,对于Reboot这个API,其入口处可以check calling的Process的UID,如果不是Root,则Reject 进程的RealUID和EffectiveUIDlinux下real uid被用于描述用户是谁,文件的拥有者,effective uid指程序执行时的用户组别, 用于判断程序是否有权去进行一些操作(例如读写文件),因此real uid是针对用户和文件(拥有者)而言, 而effective是针对运行的程序而言。一般来讲一个用户执行一个程序, 程序的effective uid会被设置为用户的real uid,这个effective uid与该程序的real uid(文件所有者)无关,只与执行者有关。 Real UID 是身份的标识 , 但没有"实权" Effective UID 是权利的标识 文件,资源以及特权API操作时对进程是否有权限的识别的UID,即是指Effectivie UID 身份与权利的关系 一般情况下,身份和权利是一至的,即Real UID = Effective UID. 所以,默认ps cmd输出的UID指的是Effective UID , 而没有输出Real UID Root用户的特权 ROOT可以调用SetXUID对自己的身份进行升降. UID的世袭 子进程的Real UID = Effective UID = 父进程的Real UID (子嗣不能继承其特权Effective UID而仅能继承其Real UID) 文件的setUID标识setUID可以让普通用户暂时获得文件权限的读写. 和基本的RWX设置类似,有助记符和直接数字设置.直接数字设置时,采用4位数字,第一位标志setUID 如下: 1234567891011121314151617181920212223shell@android:/data/test $ ls -lls -l-rw-rw-rw- shell shell 7 2015-07-03 09:22 comtu.txt-rw-rw-rw- root root 1706976 2015-07-03 08:57 dumpsys.txt-rw-rw-r-- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test $ chmod 4775 comtu.txtchmod 4775 comtu.txtshell@android:/data/test $ ls -lls -l-rwsrwxr-x shell shell 7 2015-07-03 09:22 comtu.txt-rw-rw-rw- root root 1706976 2015-07-03 08:57 dumpsys.txt-rw-rw-r-- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test $ chmod 0775 comtu.txtchmod 0775 comtu.txtshell@android:/data/test $ ls -lls -l-rwxrwxr-x shell shell 7 2015-07-03 09:22 comtu.txt-rw-rw-rw- root root 1706976 2015-07-03 08:57 dumpsys.txt-rw-rw-r-- system system 12 2015-07-02 18:02 test.txtshell@android:/data/test $ chmod u+s comtu.txtchmod u+s comtu.txtBad mode10|shell@android:/data/test $ 说明 chmod 4775 comtu.txt 4775 第一位4表示开启setUID, 第二位7表示UID获取读写执行权限, 第三位7表示Gid获取读写执行权限,最后5表示其它用户只有读与执行权限 chmod 0775 comtu.txt 0775 第一位0表示关闭setUID chmod u+s comtu.txt 使用助记符,因为我使用的是Android的adb shell 是简化般linux对助记符不支持,如果使用ubuntu等则可进行操作. CapabilityCapability细粒度的权限控制 进程的Capability permitted Capability Sets 当前进程的权利的围栏,最大权利的范围,是Effective Capability Sets的超集 Effectivte Capability Sets 当前进程的实际使用(支配)的权利集,该集内的Capability必须从属于Permitted Capability Sets . 该集合与Effective UID类似,是实际的权利标识. Inheritable Capability Sets 子进程唯一可以直接继承的Capability Sets. 在Capability模式下, 只有子进程的Inheritable Capability Sets = 父进程的Inheritable Capability Sets . 其他的皆是NO 文件的Capability Permitted Capability Sets 该可执行文件可以为进程带来的Permitted Capability Sets Effective Capability Set 仅1bit,Enable or disable , 标识该可执行文件running所在的进程的 Permitted Capability Sets是否自动全部Assign到其Effective Capability Sets. 通常用于与传统的Root-setUID可执行文件向下兼容. Inheritable Capability Sets 与进程的Inheritable Capability Sets 一起作用(位与)以决定新的进程的Permitted capability Sets Capability BoundSet Capability BoundSet是进程的属性 是进程自己为自己设定的安全围栏(Capability Sets) , 限制可执行文件的Permitted Capability Sets仅有局部能转化的Permitted Capability Sets Capability BoundSet能被子进程继承 Init进程默认Capability BoundSet为全1 Spawn进程的Capability P'子进程 P父进程 F子进程运行起来的执行文件 P'(permitted) = (P(inheritable)&F(inheritable))|(F(permitted)&cap_bset) P'(effective) = F(effective)?P'(permitted):0 P'(inheritable) = P(inheritable)]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android安全模式机制</tag>
<tag>进程和进程边界</tag>
<tag>多用户和多用户边界</tag>
<tag>进程和文件的UID/GID</tag>
<tag>Chmod和chown命令介绍</tag>
<tag>linux</tag>
<tag>UID/GID的衔接</tag>
<tag>进程的RealUID和EffectiveUID</tag>
<tag>setUID</tag>
<tag>Capability</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android安全模式机制之一(现代安全体系基础概念)]]></title>
<url>%2F%2F2014%2F10%2F25%2Fandroid_safe_mode_mechanism_one.html</url>
<content type="text"><![CDATA[h3 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } h6 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } 目录: 1.典型的加密模型 2.对称秘钥算法 (加密解密密钥相同) 3.非对称密钥算法(公钥密码)(加密与解密密钥不同.) 4.密钥交换 5.消息摘要 6.电子签名 7.证书与PKI 1.典型的加密模型密钥:分为加密密钥和解密密钥 明文:没有进行加密,能够直接代表原文含义的信息. 密文:经这加密处理之后,隐藏原温含义的信息 解密:将密文转换成明文的实施过程. 明文P-->加密方法E --> 密文 --> 解密方法D --> 明文P 加密密钥K 密文P=Ek(p) 解密密钥K' Dk'(Ek(P))=P k=k'则为对称加密 2.对称秘钥算法 (加密解密密钥相同)优缺点: 优点: 高效 缺点: 密钥交换的问题 不如RSA的加密安全程度高,但是当选择256bit的AES,扔然能胜任大多数的安全领域. 置换加密如: abcdefghijklmnopqrstuvwxyz istjkqrzlyefuvmnopgwxhabcd hello --> zkffm the key is the mapping list: istjkqrzlyefuvmnopgwxhabcd 转置加密如: i am a boy you are a girl 143526 iamabo yyouar eagirl ----> iye bar mog aya aui orl the key is the column order: 143526 乘积密码如:(重复迭代,置换加密与转置加密.) iamabo yyouar eagirl ----> liuism ccmxip kirlpf ----> lck sip umr ici ixl mpf the key is istjkqrzlyefuvmnopgwxhabcd 143526 DES 数据加密标准(data encryption standard) AES 高级加密标准(Advanced Encryption Standard) 3.非对称密钥算法(公钥密码)(加密与解密密钥不同.)明文P-->加密方法E --> 密文 --> 解密方法D --> 明文P 加密密钥K 密文P=Ek(p) 解密密钥K' Dk'(Ek(P))=P k!=k'则为非对称加密 (K公钥,K'私钥)(publicKey 公钥,privateKey 私钥) 公钥算法的理论基石 数学的科学之基 基础学科研究之物, 虚虚实实,先虚后实 公开密钥算法的最重要两大数学基础 /建立在分解大数的困难度{如: RSA算法(公钥/私钥长度,至少1024bit)} /建立在以大素数为模来计算离散对数的困难度. 优缺点: 优点:安全性足够高(相比对称加密) 没有密钥交换的问题 缺点:效率低,对于大数据加密很慢 4.密钥交换实际的保密会话应用场景 1.基于高效的对称加密算法对会话进行加密 2.会话密钥实时产生且周期性变化 3.基于其它足够安全的方式进行会话密钥的传输与交换. 利用公钥密码来交换会话密钥. 1.实时随机的会话密钥产生 2.使用对端的公钥对产生的会话密钥加密并传递给对端 3.对端使用私钥解密获取会话密钥 4.双方开始基于共享的会话密钥进行对称加密的保密会话通信. Diffie-Hellman密钥交换协议 1.基于对大素数为模计算离散对数的困难度 2.双方各自选定key,然后以一定算法变换(使得key不以明文传输)后传输给对方 3.双方利用对方交换来的数据和自己选定的key做变换,获得一个一致的结果,作为会话密钥. 5.消息摘要HASH与散列函数的定义与特点 HASH翻译成散列或者哈希 HASH(散列)函数(算法)的定义是:变长的输入变换成定长的输出 常见的HASH算法:MD5(128bit),SHA1(160bit) HASH的特点: 1.易变性:即便原始信息发生1bit的变化,HASH的输出将会有不可预知的巨大变化. 2.不可逆:通过HASH的结果构造出满足的输入信息是不可能的或者及其困难的. 与人的指纹相对应 1.双胞胎的指纹不同 2.通过指纹猜不出它的主人 数字指纹由此而来 消息摘要: 摘要窥/定全体 HASH , 哈希 , 散列 , MD , 消息摘要 , 数字指纹 都是一个意思. HASH的应用场景:防篡改. 防损坏 . 认证 HMAC : Hash-based Mesage Authentication Code (消息认证码) 我们遇到了什么问题 1.直接尾部附带消息摘要的问题(篡改内容的同时篡改摘要) 2.直接对密码做HASH传输的认证的问题(重放攻击) HMAC怎么解决的 HMAC就是使用key对原始消息变换后再进行HASH. 6.电子签名公钥密码术的两面性(非对称密钥算法的两面性) 1.应用于保密通信场景 DprivateKey(EpublicKey(P)) = P (说明:使用公钥加密P,使用私钥解密出P) 2.应用于电子签名场景 DpublicKey(EprivateKey(P)) = P (说明:使用私钥加密P,使用公钥解密出P ---- 电子签名) 并非所有公钥密码术都支持,RSA支持 HASH+公钥密码术:成就电子签名 1.RSA的低效率特性,导致即便是签名也不适合直接对原始信息进行签名 2.利用HASH先完成消息摘要和完整性鉴别的作用. 3.而后对简单的消息摘要进行基于公钥密码术的签名 4.签名一般附着于原始消息尾部或者头部一起发送 原始消息P MD = HASH(P) 原始消息P + Signature = EprivateKey(MD) ---> Target 假如原数据P有1G,加密效率低,把原数据1G的数据P获取HASH值, 用私钥加密1G数据P的HASH值得到签名signature,发送数据的时候把1G数据P+签名signature 7.证书与PKI证书的作用: 公钥的存储和交换 公钥作为一个字段存储于数字证书中 证书的交换和传输即可传输/交换公钥 利用签名来保护数字证书本身 数字时代的信任关系: 一个受信任者的证书列表 证书链和PKI 数字时代的信任链: 证书链 证书签名的不同点: 根证书自签名, 非根证书父签名 证书的限制: 约束 用途 有效期 PKI 公钥基础设施(Public Key Infrastructure) 基于证书的认证 基于可信任证书的认证方式被广泛的应用在现代安娜依斯领域,比如WIFI,HTTPS 在HTTPS中,典型的Client对Server的认证和鉴别基于可信任列表]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>Android安全模式机制</tag>
<tag>对称加密</tag>
<tag>非对称加密</tag>
<tag>密钥交换</tag>
<tag>消息摘要</tag>
<tag>电子签名</tag>
<tag>证书与PKI</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android程序自动化打包]]></title>
<url>%2F%2F2014%2F10%2F24%2Fandroid_automatic_packaging.html</url>
<content type="text"><![CDATA[h3 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } h6 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } 目录: 1.概述 2.准备 3.开始(如果之前有做过前面的步骤可直达第三步) 1 概述在一般的Android开发中,我们通常使用Eclipse自带的导出工具来进行App的签名和发布,Android自动化打包,就是利用脚本将这样一个手动操作的过程转化为一个命令搞定。 2 准备需要配置好JDK、Android SDK和Ant的环境, 2.1 安装配置JDK以jdk-8u45-windows-i586安装包为例 2.1.1 双击安装包,假设安装路径默认是C:\Program Files\Java\jdk1.8.0_45,然后点击下一步完成安装。 2.1.2 windows XP或者Server下右击“我的电脑”弹出菜单,点击“属性“》“高级“》“环境变量“,windows7则右击“计算机“弹出菜单,点击“属性“》“高级系统设置“》“高级“》“环境变量“ ,在“系统变量”下方点击“新建”,“变量名”输入”JAVA_HOME“,“变量值”输入”C:\Program Files\Java\jdk1.8.0_45“,点击“确认”;在“系统变量”选择变量为“Path”的选项,点击下方的“编辑”,在弹出框中修改“变量值”,在原有值的末尾输入“;%JAVA_HOME%\bin”,注意开头包含一个英文的分号。 2.1.3 点击“开始”》“运行”(或者Win+R),输入“cmd”,在弹出的命令窗口中输入“javac -version”然后回车,如果下方出现:javac version “1.8.0_45”,说明配置成功。 2.2安装配置Android SDK2.2.1 打开http://developer.android.com/sdk/index.html#ExistingIDE ,找到SDK Tools Only 找到最新版本的包android-sdk_r24.3.3-windows.zip ,然后勾选“I have read…”复选框,点击下方的“Download the SDK…..”,将下载完整的SDK工具压缩包。附录百度云android-sdk_r24.3.2-windows.zip 2.2.2 将第一步下载好的压缩包解压,比如直接放到D盘下,完整路径是D:\android-sdk_r24.3.3-windows。 2.2.3 进入上面这个文件夹,双击SDK Manager.exe,在弹出的窗口中有个Packages栏,勾选其中的“**Tools**”、”Android 4.2.2 (API17)”以及“Extras”下面所有的的选项,点击下方的“Install packages”开始下载,如果这些选项的Status变为“Installed”,说明下载完毕。如果下载速度过慢,用记事本打开C:\WINDOWS\system32\drivers\etc\hosts这个文件,在最下方新输入一行:74.125.237.1 dl-ssl.google.com,然后保存,重新打开SDK Manager.exe再试一次。 2.2.4 windows XP或者Server下右击“我的电脑”弹出菜单,点击“属性“》“高级“》“环境变量“,在“系统变量”下方点击“新建”,“变量名”输入"ANDROID_HOME“,“变量值”输入”D:\android-sdk_r24.3.3-windows“,点击“确认”;在“系统变量”选择变量为“Path”的选项,点击下方的“编辑”,在弹出框中修改“变量值”,在原有值的末尾输入“;%ANDROID_HOME%\platform-tools”,注意开头包含一个英文的分号。 2.2.5 点击“开始”》“运行”,输入“cmd”,在弹出的命令窗口中输入:adb回车,如果下方出现:Android Debug Bridge version …等多行文字,说明配置成功。 2.3 安装配置Ant以Ant1.9.3为例 2.3.1 浏览器打开http://ant.apache.org/bindownload.cgi ,找到.zip archive: apache-ant-1.9.5-bin.zip ,点击下载压缩包 2.3.2 将压缩包解压的某一个目录,比如直接放到D盘下,完整路径是D:\apache-ant-1.9.5。 2.3.3 windows XP或者Server下右击“我的电脑”弹出菜单,点击“属性“》“高级“》“环境变量“,在“系统变量”下方点击“新建”,“变量名”输入”ANT_HOME“,“变量值”输入”D:\apache-ant-1.9.5,点击“确认”;在“系统变量”选择变量为“Path”的选项,点击下方的“编辑”,在弹出框中修改“变量值”,在原有值的末尾输入“;%ANT_HOME%\bin”,注意开头包含一个英文的分号。 2.3.4 点击“开始”》“运行”(Win+R),输入“cmd”,在弹出的命令窗口中输入“ant -version”然后回车,如果下方出现:Apache Ant(TM) version 1.9.5 compiled on May 31 2015,说明配置成功。 3 开始3.1 准备好项目的源码和依赖库源码,最好放在同一级目录下,确保源码和依赖库可在IDE中编译成功,这样就无需调整project.properties文件中依赖库的相对位置了。 3.2 打开CMD命令行,进入到依赖库源码目录(cd命令),执行android update lib-project -p . -t android-17,这个命令的作用是在依赖库下生成自动打包相关的文件,-p .的意思是在当前根目录下执行,-t android-17的意思是此源码使用android-17 api进行编译,各位根据自己的项目情况自行修改,所有的依赖库都要执行这一步操作。 3.3 打开CMD命令行,进入到源码根目录,执行android update project -p . -t android-17,这个命令的作用是在源码下生成自动打包相关的文件。 3.4 打开CMD命令行,进入到源码根目录,执行ant clean,然后ant debug,如果打包成功,可以在源代码的bin目录下看到未签名的apk文件。 3.5 想要生成签名文件,需要在源码根目录下建立ant.properties文件,内容如下: #包名 application.package=you_package_name #项目名,缺省时源码文件名 ant.project.name= #编码方式 java.encoding=utf-8 #编译输出绝对路径 out.absolute.dir=d:/out #生成文件绝对路径 gos.path=d:/out #签名key文件绝对路径 key.store=D:/adt-bundle/Nomouse #签名文件密码 key.store.password=password #签名别称,中文的话需要转成utf-8编码,可以使用JDK自带的native2ascii工具 key.alias=\u4f01\u4e1a #签名别称密码 key.alias.password=password 3.6 打开CMD命令行,进入到源码根目录,执行ant release,成功的话可以在d:/out目录下看到输出的App签名文件。]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>windows环境下搭建Android开发环境</tag>
<tag>Android程序自动化打包</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java8新特性]]></title>
<url>%2F%2F2014%2F10%2F23%2Fjava8_new_feature.html</url>
<content type="text"><![CDATA[h3 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } h6 { line-height: 1.5; letter-spacing: 2px; margin-top: -10px; } 目录: 1.简介 2.Java的新特性 2.1 Lambda表达式和函数式接口 2.2 接口的默认方法和静态方法 2.3 方法引用 2.4 重复注释 2.5 更好的类型推断 2.6 注解的扩展 3.Java编译器的新特性 3.1 参数名字 4.Java 库的新特性 4.1 Optional 4.2 Stream 4.3日期时间API(JSR310) 4.4 Nashorn javascript引擎 4.5 Base64 4.6 并行数组 4.7 并发 新的工具 5.1 Nashorn引擎:jjs 5.2 类依赖分析工具:jdeps JVM的新特性 资源 1.简介毫无疑问,Java 8是自Java 5(2004年)发布以来Java语言最大的一次版本升级,Java 8带来了很多的新特性,比如编译器、类库、开发工具和JVM(Java虚拟机)。在这篇教程中我们将会学习这些新特性,并通过真实例子演示说明它们适用的场景。 本教程由下面几部分组成,它们分别涉及到Java平台某一特定方面的内容: 语言编译器类库开发工具运行时(Java虚拟机) 2.Java的新特性总体来说,Java 8是一个大的版本升级。有人可能会说,Java 8的新特性非常令人期待,但是也要花费大量的时间去学习。这一节我们会讲到这些新特性。 2.1 Lambda表达式和函数式接口Lambda表达式(也叫做闭包)是Java 8中最大的也是期待已久的变化。它允许我们将一个函数当作方法的参数(传递函数),或者说把代码当作数据,这是每个函数式编程者熟悉的概念。很多基于JVM平台的语言一开始就支持Lambda表达式,但是Java程序员没有选择,只能使用匿名内部类来替代Lambda表达式。 Lambda表达式的设计被讨论了很久,而且花费了很多的功夫来交流。不过最后取得了一个折中的办法,得到了一个新的简明并且紧凑的Lambda表达式结构。最简单的Lambda表达式可以用逗号分隔的参数列表、->符号和功能语句块来表示。示例如下: 1Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) ); 请注意到编译器会根据上下文来推测参数的类型,或者你也可以显示地指定参数类型,只需要将类型包在括号里。举个例子: 1Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) ); 如果Lambda的功能语句块太复杂,我们可以用大括号包起来,跟普通的Java方法一样,如下: 12345String separator = ",";Arrays.asList("a", "b", "d").forEach((String e) -> { System.out.print(e + separator2); System.out.print(e + separator2);}); Lambda表达式可能会引用类的成员或者局部变量(会被隐式地转变成final类型),下面两种写法的效果是一样的: 123String separator = ",";Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.print( e + separator ) ); 和 123final String separator = ",";Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.print( e + separator ) ); Lambda表达式可能会有返回值,编译器会根据上下文推断返回值的类型。如果lambda的语句块只有一行,不需要return关键字。下面两个写法是等价的: 1Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) ); 和 1234Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> { int result = e1.compareTo( e2 ); return result;} ); 语言的设计者们思考了很多如何让现有的功能和lambda表达式友好兼容。于是就有了函数接口这个概念。函数接口是一种只有一个方法的接口,像这样地,函数接口可以隐式地转换成lambda表达式。 java.lang.Runnable 和java.util.concurrent.Callable是函数接口两个最好的例子。但是在实践中,函数接口是非常脆弱的,只要有人在接口里添加多一个方法,那么这个接口就不是函数接口了,就会导致编译失败。Java 8提供了一个特殊的注解 @FunctionalInterface 来克服上面提到的脆弱性并且显示地表明函数接口的目的(java里所有现存的接口都已经加上了 @FunctionalInterface)。让我们看看一个简单的函数接口定义: 1234@FunctionalInterfacepublic interface Functional { void method();} 我们要记住默认的方法和静态方法(下一节会具体解释)不会违反函数接口的约定,例子如下: 123456@FunctionalInterfacepublic interface FunctionalDefaultMethods { void method(); default void defaultMethod() { }} 支持Lambda是Java 8最大的卖点,他有巨大的潜力吸引越来越多的开发人员转到这个开发平台来,并且在纯Java里提供最新的函数式编程的概念。对于更多的细节,请参考官方文档。 测试原代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157package com.tu.test.java8.newfeature;import java.util.Arrays;import java.util.Comparator;import java.util.List;import java.util.function.Consumer;public class TestLambda { public static void main(String[] args) { // ======无返回值的函数接口============================= System.out.println("==============1================"); // 匿名内部类实现Runnable------> new Runnable() { @Override public void run() { System.out.println("匿名内部类实现Runnable"); } }.run(); // 使用Lambda实现------> System.out.println("==============2================"); int i = 0; Runnable r = () -> {// ()表示方法不需要参数. System.out.println("使用Lambda实现Runnable" + i); // 可直接使用外部变量,但不能进行修改 }; r.run(); // 另外一种Lambda实现简写------> System.out.println("==============3================"); Runnable r1 = () -> System.out.println("hello lambda!"); r1.run(); // =======自定义函数接口============================ // 匿名内部类自定义接口------> System.out.println("==============4================"); new Action() { @Override public void execute(String content) { System.out.println(content); } }.execute("jdk1.8之前的匿名内部类实现方式"); // lambda实现------> System.out.println("==============5================"); Action a = (String content) -> { System.out.println(content); }; a.execute("jdk1.8的lambda实现方式"); // 还可以更简洁,这个表达式可以被替换成对一个方法的引用, // 因为它只是单个方法,而且它们的参数是相同的------> System.out.println("==============6================"); Action a1 = System.out::println; a1.execute("jdk1.8的lambda实现方式_更简洁."); // 然而,如果参数上有任何其它形式的变化,我们就不能直接引用方法, // 必须写全lambda表达式------> System.out.println("==============7================"); Action a3 = ((String s) -> System.out.println("*" + s + "*")); a3.execute("jdk1.8的lambda实现方式_参数上有其它形式的变化."); // ======有返回值的函数接口============================= // 匿名内部类自定义接口------> System.out.println("==============8================"); System.out.println(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return 0; } }.compare(1, 2)); // lambda实现 ------> System.out.println("==============9================"); Comparator<Integer> cmp = (x, y) -> { return (x < y) ? -1 : ((x > y) ? 1 : 0); }; // 从中可以看出,单行的lambda表达式似乎是隐含了一个return语句。------> Comparator<Integer> cmp1 = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0); System.out.println("TestLambda.main().cmp.compare():" + cmp.compare(0, 1)); System.out.println("TestLambda.main().cmp1.compare():" + cmp1.compare(1, 1)); // =====================================更多简洁写法 System.out.println("==============10================"); List<String> list = Arrays.asList("a", "b", "c"); for (String s : list) { new Consumer<String>() { @Override public void accept(String t) { System.out.println(t); } }.accept(s); } // 最简单的Lambda表达式可以用逗号分隔的参数列表、->符号和功能语句块来表示。示例如下: System.out.println("==============11================"); Arrays.asList("a", "b", "d").forEach(e -> System.out.println(e)); // 请注意到编译器会根据上下文来推测参数的类型,或者你也可以显示地指定参数类型, // 只需要将类型包在括号里。举个例子: System.out.println("==============12================"); Arrays.asList("a", "b", "d").forEach((String e) -> System.out.println(e)); // Lambda表达式可能会引用类的成员或者局部变量(会被隐式地转变成final类型), // 下面两种写法的效果是一样的: System.out.println("==============13================"); String separator = ","; Arrays.asList("a", "b", "d").forEach((String e) -> System.out.print(e + separator)); System.out.println("==============14================"); final String separator1 = ","; Arrays.asList("a", "b", "d").forEach((String e) -> System.out.print(e + separator1)); // 如果Lambda的功能语句块太复杂,我们可以用大括号包起来,跟普通的Java方法一样,如下: System.out.println("==============15================"); String separator2 = ","; Arrays.asList("a", "b", "d").forEach((String e) -> { System.out.print(e + separator2); System.out.print(e + separator2); }); // Lambda表达式可能会有返回值,编译器会根据上下文推断返回值的类型。 // 如果lambda的语句块只有一行,不需要return关键字。下面两个写法是等价的: System.out.println("==============16================"); Arrays.asList("a", "b", "d").sort((e1, e2) -> e1.compareTo(e2)); Arrays.asList("a", "b", "d").sort((e1, e2) -> { int result = e1.compareTo(e2); return result; }); }// main end / * 自定义函数接口 * 注解 @FunctionalInterface 使用这种方式标记的都可以使用Lambda表式, * 它还被javac使用来验证这个接口是否真是一个函数式接口,是否至少有一个抽象方法在里面。 * * 在Java里,lambda表达式是“SAM”(Single Abstract Method)—— * 一个含有一个抽象方法的接口(是的,现在接口里可以含有一个非抽象的方法,defender守卫方法)。 */ @FunctionalInterface static interface Action { void execute(String content); default void cancel(String c) { } }}//class end 运行结果: 1234567891011121314151617181920212223242526272829303132333435==============1================匿名内部类实现Runnable==============2================使用Lambda实现Runnable0==============3================hello lambda!==============4================jdk1.8之前的匿名内部类实现方式==============5================jdk1.8的lambda实现方式==============6================jdk1.8的lambda实现方式_更简洁.==============7================*jdk1.8的lambda实现方式_参数上有其它形式的变化.*==============8================0==============9================TestLambda.main().cmp.compare():-1TestLambda.main().cmp1.compare():0==============10================abc==============11================abd==============12================abd==============13================a,b,d,==============14================a,b,d,==============15================a,a,b,b,d,d,==============16================ //英文原文:Java 8: The First Taste of Lambdas//译文链接:Java 8: The First Taste of Lambdas (译)//官方文档:Java 8: Lambda expressions 2.2 接口的默认方法和静态方法Java 8增加了两个新的概念在接口声明的时候:默认和静态方法。默认方法和 Trait 有些类似,但是目标不一样。默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法。 默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的实现类都会通过继承得倒这个方法(如果有需要也可以重写这个方法),让我们来看看下面的例子: 1234567891011121314151617private interface Defaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. default String notRequired() { return "Default implementation"; }} private static class DefaultableImpl implements Defaulable {} private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation"; }} 接口 Defaulable 使用 default 关键字声明了一个默认方法 notRequired (),类 DefaultableImpl 实现了 Defaulable 接口,没有对默认方法做任何修改。另外一个类OverridableImpl重写类默认实现,提供了自己的实现方法。 Java 8 的另外一个有意思的新特性是接口里可以声明静态方法,并且可以实现。例子如下: 123456private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); }} 下面是把接口的静态方法和默认方法放在一起的示例(::new 是构造方法引用,后面会有详细描述): 1234567public static void main( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); System.out.println( defaulable.notRequired() ); defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.notRequired() );} 控制台的输出如下: Default implementation Overridden implementation JVM平台的接口的默认方法实现是很高效的,并且方法调用的字节码指令支持默认方法。默认方法使已经存在的接口可以修改而不会影响编译的过程。java.util.Collection中添加的额外方法就是最好的例子:stream(), parallelStream(), forEach(), removeIf() 虽然默认方法很强大,但是使用之前一定要仔细考虑是不是真的需要使用默认方法,因为在层级很复杂的情况下很容易引起模糊不清甚至变异错误。更多的详细信息请参考官方文档。 测试原代码: 123456789101112131415161718192021222324252627282930313233343536373839package com.tu.test.java8.newfeature;import java.util.function.Supplier;public class TestInterfaceDefaultAndStaticMethod { public static void main( String[] args ) { //::new 是构造方法引用 Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); System.out.println( defaulable.notRequired() ); defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.notRequired() ); } private interface Defaulable { //现在允许默认方法接口,实现者可能会或可能不会实现(覆盖)。 default String notRequired() { return "Default implementation"; } } private static class DefaultableImpl implements Defaulable { } private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation"; } } private interface DefaulableFactory { //现在允许静态方法的接口 static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); } }} 运行结果: 12Default implementationOverridden implementation 2.3 方法引用方法引用提供了一个很有用的语义来直接访问类或者实例的已经存在的方法或者构造方法。结合Lambda表达式,方法引用使语法结构紧凑简明。不需要复杂的引用。 下面我们用Car 这个类来做示例,Car这个类有不同的方法定义。让我们来看看java 8支持的4种方法引用。 1234567891011121314151617public static class Car { public static Car create( final Supplier< Car > supplier ) { return supplier.get(); } public static void collide( final Car car ) { System.out.println( "Collided " + car.toString() ); } public void follow( final Car another ) { System.out.println( "Following the " + another.toString() ); } public void repair() { System.out.println( "Repaired " + this.toString() ); }} 第一种方法引用是构造方法引用,语法是:Class::new ,对于泛型来说语法是:Class<T >::new,请注意构造方法没有参数: 12final Car car = Car.create( Car::new );final List< Car > cars = Arrays.asList( car ); 第二种方法引用是静态方法引用,语法是:Class::static_method请注意这个静态方法只支持一个类型为Car的参数。 1cars.forEach( Car::collide ); 第三种方法引用是类实例的方法引用,语法是:Class::method请注意方法没有参数。 1cars.forEach( Car::repair ); 最后一种方法引用是引用特殊类的方法,语法是:instance::method,请注意只接受Car类型的一个参数。 12final Car police = Car.create( Car::new );cars.forEach( police::follow ); 运行这些例子我们将会在控制台得到如下信息(Car的实例可能会不一样): Collided com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fc Repaired com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fc Following the com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fc 关于方法引用更多的示例和详细信息,请参考官方文档 测试原代码: 123456789101112131415161718192021222324252627282930313233343536package com.tu.test.java8.newfeature;import java.util.Arrays;import java.util.List;import java.util.function.Supplier;/ MethodReferences 方法引用*/public class TestMethodReferences { public static void main(String[] args) { final Car car = Car.create( Car::new ); final List< Car > cars = Arrays.asList( car ); cars.forEach( Car::collide ); cars.forEach( Car::repair ); final Car police = Car.create( Car::new ); cars.forEach( police::follow ); } public static class Car { public static Car create( final Supplier< Car > supplier ) { return supplier.get(); } public static void collide( final Car car ) { System.out.println( "Collided " + car.toString() ); } public void follow( final Car another ) { System.out.println( "Following the " + another.toString() ); } public void repair() { System.out.println( "Repaired " + this.toString() ); } }} 运行结果: 123Collided com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fcRepaired com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fcFollowing the com.tu.test.java8.newfeature.TestMethodReferences$Car@a418fc 2.4 重复注释自从Java 5支持注释以来,注释变得特别受欢迎因而被广泛使用。但是有一个限制,同一个地方的不能使用同一个注释超过一次。 Java 8打破了这个规则,引入了重复注释,允许相同注释在声明使用的时候重复使用超过一次。 重复注释本身需要被 @Repeatable 注释。实际上,他不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。让我们来看看例子: 1234567891011121314151617181920212223242526272829303132333435package com.tu.test.java8.newfeature;import java.lang.annotation.ElementType;import java.lang.annotation.Repeatable;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/ RepeatingAnnotations 重复注释 */public class TestRepeatingAnnotations { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Filters { Filter[] value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Filters.class) public @interface Filter { String value(); }; @Filter("filter1") @Filter("filter2") public interface Filterable { } public static void main(String[] args) { for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) { System.out.println(filter.value()); } }} 我们可以看到,注释Filter被 @Repeatable( Filters.class )注释。Filters 只是一个容器,它持有Filter, 编译器尽力向程序员隐藏它的存在。通过这样的方式,Filterable接口可以被Filter注释两次。 另外,反射的API提供一个新方法getAnnotationsByType() 来返回重复注释的类型(请注意Filterable.class.getAnnotation( Filters.class )将会返回编译器注入的Filters实例)。 程序的输出将会是这样: 12filter1filter2 更多详细信息请参考官方文档。 2.5 更好的类型推断Java 8在类型推断方面改进了很多,在很多情况下,编译器可以推断参数的类型,从而保持代码的整洁。让我们看看例子: 12345678910package com.tu.test.java8.newfeature;public class Value<T> { public static<T> T defaultValue() { return null; } public T getOrDefault( T value, T defaultValue ) { return ( value != null ) ? value : defaultValue; }} 这里是Value< String >的用法 12345678package com.tu.test.java8.newfeature;public class TypeInference { public static void main(String[] args) { final Value<String> value = new Value<>(); value.getOrDefault( "22", Value.defaultValue() ); }} 参数Value.defaultValue()的类型被编译器推断出来,不需要显式地提供类型。在java 7, 相同的代码不会被编译,需要写成:Value.< String >defaultValue() 2.6 注解的扩展Java 8扩展了注解可以使用的范围,现在我们几乎可以在所有的地方:局部变量、泛型、超类和接口实现、甚至是方法的Exception声明。一些例子如下: 1234567891011121314151617181920212223242526272829package com.tu.test.java8.newfeature;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import java.util.ArrayList;import java.util.Collection;/ 注解的扩展 */public class TestAnnotations { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) public @interface NonEmpty { } public static class Holder<@NonEmpty T> extends @NonEmpty Object { public void method() throws @NonEmpty Exception { } } @SuppressWarnings("unused") public static void main(String[] args) { final Holder<String> holder = new @NonEmpty Holder<String>(); @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>(); }} Java 8 新增加了两个注解的程序元素类型 ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER ,这两个新类型描述了可以使用注解的新场合。注解处理API(Annotation Processing API)也做了一些细微的改动,来识别这些新添加的注解类型。 3.Java编译器的新特性3.1 参数名字很长时间以来,Java程序员想尽办法把参数名字保存在java字节码里,并且让这些参数名字在运行时可用。Java 8 终于把这个需求加入到了Java语言(使用反射API和Parameter.getName() 方法)和字节码里(使用java编译命令javac的–parameters参数)。 123456789101112131415package com.tu.test.java8.newfeature;import java.lang.reflect.Method;import java.lang.reflect.Parameter;public class TestParameterNames { public static void main(String[] args) throws Exception { Method method = TestParameterNames.class.getMethod("main", String[].class); for (final Parameter parameter : method.getParameters()) { if (parameter.isNamePresent())//验证参数名是不是可用 System.out.println("Parameter: " + parameter.getName()); } }} 如果你编译这个class的时候没有添加参数–parameters,运行的时候你会得到这个结果: Parameter: arg0 编译的时候添加了–parameters参数的话,运行结果会不一样: Parameter: args 对于有经验的Maven使用者,–parameters参数可以添加到maven-compiler-plugin的配置部分: 12345678910<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerArgument>-parameters</compilerArgument> <source>1.8</source> <target>1.8</target> </configuration></plugin> 最新版的Eclipse Kepler SR2 提供了编译设置项,如下图所示: Picture 1. Configuring Eclipse projects to support new Java 8 compiler –parameters argument. store method parameter names(usable via reflection)存储通过反射方法参数名称(通过反射可用) 额外的,有一个方便的方法Parameter.isNamePresent() 来验证参数名是不是可用。 4.Java 库的新特性Java 8 新添加了很多类,并且扩展了很多现有的类来更好地支持现代并发、函数式编程、日期\时间等等。 4.1 Optional著名的NullPointerException 是引起系统失败最常见的原因。很久以前Google Guava 项目引入了Optional作为解决空指针异常的一种方式,不赞成代码被null检查的代码污染,期望程序员写整洁的代码。受Google Guava的鼓励,Optional 现在是Java 8库的一部分。 Optional 只是一个容器,它可以保存一些类型的值或者null。它提供很多有用的方法,所以没有理由不显式地检查null。请参照java 8的文档查看详细信息。 让我们看看两个Optional 用法的小例子:一个是允许为空的值,另外一个是不允许为空的值。 12345Optional< String > fullName = Optional.ofNullable( null );System.out.println( "Full Name is set? " + fullName.isPresent() ); System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); 如果Optional实例有非空的值,方法 isPresent() 返回true否则返回false。方法orElseGet提供了回退机制,当Optional的值为空时接受一个方法返回默认值。map()方法转化Optional当前的值并且返回一个新的Optional实例。orElse方法和orElseGet类似,但是它不接受一个方法,而是接受一个默认值。上面代码运行结果如下: Full Name is set? false Full Name: [none] Hey Stranger! 让我们大概看看另外一个例子。 123456Optional< String > firstName = Optional.of( "Tom" );System.out.println( "First Name is set? " + firstName.isPresent() ); System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );System.out.println(); 输出如下: First Name is set? true First Name: Tom Hey Tom! 更多详细信息请参考官方文档 。 测试原代码: 1234567891011121314151617181920212223242526272829303132333435363738394041package com.tu.test.java8.newfeature;import java.util.Optional;import java.util.function.Function;import java.util.function.Supplier;public class TestOptional { public static void main(String[] args) { Optional<String> fullName = Optional.ofNullable(null); System.out.println("Full Name is set? " + fullName.isPresent()); System.out.println("Full Name: " + fullName.orElseGet(() -> "[none]")); System.out.println(fullName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!")); System.out.println("---------------"); Optional<String> firstName = Optional.of("Tom"); System.out.println("First Name is set? " + firstName.isPresent()); System.out.println("First Name: " + firstName.orElseGet(() -> "[none]")); System.out.println(firstName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!")); System.out.println(); //=====复习lambda=============================firstName.orElseGet(() -> "[none]") Supplier<? extends String> other = new Supplier<String>() { @Override public String get() { return "[none]"; } }; firstName.orElseGet(other); //=====复习lambda=============================firstName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!") Function<String, String> mapper = new Function<String, String>() { @Override public String apply(String t) { return "Hey " + t + "!"; } }; firstName.map(mapper).orElse("Hey Stranger!"); }} 运行结果: 1234567Full Name is set? falseFull Name: [none]Hey Stranger!---------------First Name is set? trueFirst Name: TomHey Tom! 4.2 Stream新增加的Stream API (java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加,希望程序员通过编写有效、整洁和简明的代码,能够大大提高生产率。 Stream API让集合处理简化了很多(我们后面会看到不仅限于Java集合类)。让我们从一个简单的类Task开始来看看Stream的用法。 1234567891011121314151617181920212223242526private enum Status { OPEN, CLOSED};private static final class Task { private final Status status; private final Integer points; Task(final Status status, final Integer points) { this.status = status; this.points = points; } public Integer getPoints() { return points; } public Status getStatus() { return status; } @Override public String toString() { return String.format("[%s, %d]", status, points); }} Task类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子: 123456final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) ); 第一个问题是所有的开放的Task的点数是多少?在java 8 之前,通常的做法是用foreach迭代。但是Java8里头我们会用Stream。Stream是多个元素的序列,支持串行和并行操作。 123456789// Calculate total points of all active tasks using sum()final long totalPointsOfOpenTasks = tasks .stream() .filter( task -> task.getStatus() == Status.OPEN ) .mapToInt( Task::getPoints ) .sum(); System.out.println( "Total points: " + totalPointsOfOpenTasks ); 控制台的输出将会是: Total points: 18 上面代码执行的流程是这样的,首先Task集合会被转化为Stream表示,然后filter操作会过滤掉所有关闭的Task,接下来使用Task::getPoints 方法取得每个Task实例的点数,mapToInt方法会把Task Stream转换成Integer Stream,最后使用Sum方法将所有的点数加起来得到最终的结果。 在我们看下一个例子之前,我们要记住一些关于Stream的说明。Stream操作被分为中间操作和终点操作。 中间操作返回一个新的Stream。这些中间操作是延迟的,执行一个中间操作比如filter实际上不会真的做过滤操作,而是创建一个新的Stream,当这个新的Stream被遍历的时候,它里头会包含有原来Stream里符合过滤条件的元素。 终点操作比如说forEach或者sum会遍历Stream从而产生最终结果或附带结果。终点操作执行完之后,Stream管道就被消费完了,不再可用。在几乎所有的情况下,终点操作都是即时完成对数据的遍历操作。 Stream的另外一个价值是Stream创造性地支持并行处理。让我们看看下面这个例子,这个例子把所有task的点数加起来。 12345678// Calculate total points of all tasksfinal double totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints ) .reduce( 0, Integer::sum ); System.out.println( "Total points (all tasks): " + totalPoints ); 这个例子跟上面那个非常像,除了这个例子里使用了parallel()方法 并且计算最终结果的时候使用了reduce方法。 输出如下: Total points (all tasks): 26.0 经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子: 12345// Group tasks by their statusfinal Map< Status, List< Task > > map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) );System.out.println( map ); 控制台的输出如下: {CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]} 让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子。 12345678910111213// Calculate the weight of each tasks (as percent of total points) final Collection< String > result = tasks .stream() // Stream< String > .mapToInt( Task::getPoints ) // IntStream .asLongStream() // LongStream .mapToDouble( points -> points / totalPoints ) // DoubleStream .boxed() // Stream< Double > .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream .mapToObj( percentage -> percentage + "%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println( result ); 控制台输出如下: [19%, 50%, 30%] 最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。 1234final Path path = new File( filename ).toPath();try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) { lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );} Stream的方法onClose 返回一个等价的有额外句柄的Stream,当Stream的close()方法被调用的时候这个句柄会被执行。 Stream API、Lambda表达式还有接口默认方法和静态方法支持的方法引用,是Java 8对软件开发的现代范式的响应。 运行原代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101package com.tu.test.java8.newfeature;import java.io.File;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Path;import java.util.Arrays;import java.util.Collection;import java.util.List;import java.util.Map;import java.util.stream.Collectors;import java.util.stream.Stream;public class TestStream { public static void main(String[] args) { final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) ); //所有的开放的Task的点数是多少?在java 8 之前,通常的做法是用foreach迭代。但是Java8里头我们会用Stream。Stream是多个元素的序列,支持串行和并行操作。 // Calculate total points of all active tasks using sum() final long totalPointsOfOpenTasks = tasks .stream() .filter( task -> task.getStatus() == Status.OPEN ) .mapToInt( Task::getPoints ) .sum(); System.out.println( "Total points: " + totalPointsOfOpenTasks ); System.out.println("========================================="); // Calculate total points of all tasks final double totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints ) .reduce( 0, Integer::sum ); System.out.println( "Total points (all tasks): " + totalPoints ); System.out.println("========================================="); // Group tasks by their status final Map<Status, List<Task>> map = tasks .stream() .collect(Collectors.groupingBy(Task::getStatus)); System.out.println(map); System.out.println("========================================="); // Calculate the weight of each tasks (as percent of total points) final Collection< String > result = tasks .stream() // Stream< String > .mapToInt( Task::getPoints ) // IntStream .asLongStream() // LongStream .mapToDouble( points -> points / totalPoints ) // DoubleStream .boxed() // Stream< Double > .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream .mapToObj( percentage -> percentage + "%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println( result ); System.out.println("========================================="); Stream<String> lines; try { final Path path = new File("./text/Test.txt").toPath(); lines = Files.lines(path, StandardCharsets.UTF_8); lines.onClose(() -> System.out.println("Done!")).forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } } private enum Status { OPEN, CLOSED }; private static final class Task { private final Status status; private final Integer points; Task(final Status status, final Integer points) { this.status = status; this.points = points; } public Integer getPoints() { return points; } public Status getStatus() { return status; } @Override public String toString() { return String.format("[%s, %d]", status, points); } }} 运行结果: 123456789101112Total points: 18=========================================Total points (all tasks): 26.0========================================={OPEN=[[OPEN, 5], [OPEN, 13]], CLOSED=[[CLOSED, 8]]}=========================================[19%, 50%, 30%]=========================================hellohellohellohellohellohello 再来点案例: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748List<String> stringCollection = Arrays.asList("ddd2", "aaa2", "bbb1", "aaa1", "bbb3", "ccc", "bbb2", "ddd1");// Filter 过滤stringCollection.stream().filter((s) -> s.startsWith("a")).forEach(System.out::println);System.out.println("=========1==========");// Sort 排序stringCollection.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);System.out.println("=========2==========");// Map 映射stringCollection.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println);System.out.println("=========3==========");// Match 匹配System.out.println(stringCollection.stream().anyMatch((s) -> s.startsWith("a")));System.out.println(stringCollection.stream().allMatch((s) -> s.startsWith("a")));System.out.println(stringCollection.stream().noneMatch((s) -> s.startsWith("a")));System.out.println("=========4==========");// Count 计数 (最终操作)System.out.println(stringCollection.stream().count());System.out.println(stringCollection.stream().filter((a) -> a.startsWith("a")).count());System.out.println("=========5==========");// Reduce 规约 (最终操作)stringCollection.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2).ifPresent(System.out::println);System.out.println("========6===========");// 并行Streamsint max = 1000000;List<String> values = new ArrayList<>(max);for (int i = 0; i < max; i++) { values.add(UUID.randomUUID().toString());}long t0 = System.nanoTime();// 串行排序 sequential sort took:1226 mslong count = values.stream().sorted().count();System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);System.out.println(String.format("sequential sort took:%d ms", millis));long t2 = System.nanoTime();// 并行排序 sequential sort took:951 mslong count1 = values.parallelStream().sorted().count();System.out.println(count1);long t3 = System.nanoTime();long millis1 = TimeUnit.NANOSECONDS.toMillis(t3 - t2);System.out.println(String.format("parallel sort took:%d ms", millis1)); 案例运行结果: 1234567891011121314151617181920212223242526272829aaa2aaa1=========1==========aaa1aaa2=========2==========DDD2DDD1CCCBBB3BBB2BBB1AAA2AAA1=========3==========truefalsefalse=========4==========82=========5==========aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2========6===========1000000sequential sort took:1258 ms1000000parallel sort took:1061 ms 4.3日期时间API(JSR310)Java 8引入了新的日期时间API(JSR 310)改进了日期时间的管理。日期和时间管理一直是Java开发人员最痛苦的问题。java.util.Date和后来的java.util.Calendar一点也没有改变这个情况(甚至让人们更加迷茫)。 因为上面这些原因,产生了Joda-Time ,可以替换Java的日期时间API。Joda-Time深刻影响了 Java 8新的日期时间API,Java 8吸收了Joda-Time 的精华。新的java.time包包含了所有关于日期、时间、日期时间、时区、Instant(跟日期类似但精确到纳秒)、duration(持续时间)和时钟操作的类。设计这些API的时候很认真地考虑了这些类的不变性(从java.util.Calendar吸取的痛苦教训)。如果需要修改时间对象,会返回一个新的实例。 让我们看看一些关键的类和用法示例。第一个类是Clock,Clock使用时区来访问当前的instant, date和time。Clock类可以替换 System.currentTimeMillis() 和 TimeZone.getDefault(). 1234// Get the system clock as UTC offsetfinal Clock clock = Clock.systemUTC();System.out.println( clock.instant() );System.out.println( clock.millis() ); 控制台输出如下: 2015-06-25T07:10:58.746Z 1435216258848 其他类我们看看LocalTime和LocalDate。LocalDate只保存有ISO-8601日期系统的日期部分,有时区信息,相应地,LocalTime只保存ISO-8601日期系统的时间部分,没有时区信息。LocalDate和LocalTime都可以从Clock对象创建。 1234567891011121314// Get the local date and local timefinal LocalDate date = LocalDate.now();final LocalDate dateFromClock = LocalDate.now( clock );System.out.println( date );System.out.println( dateFromClock );System.out.println("================");// Get the local date and local timefinal LocalTime time = LocalTime.now();final LocalTime timeFromClock = LocalTime.now( clock );System.out.println( time );System.out.println( timeFromClock ); 控制台输出如下: 2015-06-25 2015-06-25 15:12:07.912 07:12:07.912 LocalDateTime类合并了LocalDate和LocalTime,它保存有ISO-8601日期系统的日期和时间,但是没有时区信息。让我们看一个简单的例子。 123456// Get the local date/timefinal LocalDateTime datetime = LocalDateTime.now();final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );System.out.println( datetime );System.out.println( datetimeFromClock ); 控制台输出如下: 2015-06-25T15:14:12.139 2015-06-25T07:14:12.139 如果您需要一个类持有日期时间和时区信息,可以使用ZonedDateTime,它保存有ISO-8601日期系统的日期和时间,而且有时区信息。让我们看一些例子: 12345678// Get the zoned date/timefinal ZonedDateTime zonedDatetime = ZonedDateTime.now();final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );System.out.println( zonedDatetime );System.out.println( zonedDatetimeFromClock );System.out.println( zonedDatetimeFromZone ); 控制台输出如下: 2015-06-25T15:14:51.607+08:00[Asia/Shanghai] 2015-06-25T07:14:51.607Z 2015-06-25T00:14:51.610-07:00[America/Los_Angeles] 最后让我们看看Duration类,Duration持有的时间精确到纳秒。它让我们很容易计算两个日期中间的差异。让我们来看一下: 12345678// Get duration between two datesfinal LocalDateTime from = LocalDateTime.of( 2015, Month.JUNE, 25, 0, 0, 0 );final LocalDateTime to = LocalDateTime.of( 2016, Month.JUNE, 25, 23, 59, 59 );final Duration duration = Duration.between( from, to );System.out.println( "Duration in days: " + duration.toDays() );System.out.println( "Duration in hours: " + duration.toHours() ); 上面的例子计算了两个日期(2015年5月25日和2016年5月25日)之间的持续时间(基于天数和小时)输出如下: Duration in days: 366 Duration in hours: 8807 对于Java 8的新日期时间的总体印象还是比较积极的。一部分是因为有经历实战的Joda-Time的基础,还有一部分是因为日期时间终于被认真对待而且听取了开发人员的声音。关于更多的详细信息,请参考官方文档。 4.4 Nashorn javascript引擎Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。Nashorn javascript引擎只是javax.script.ScriptEngine另一个实现,而且规则也一样,允许Java和JavaScript互相操作。这里有个小例子: 123456ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName( "JavaScript" );System.out.println( engine.getClass().getName() );System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) ); 输出如下: jdk.nashorn.api.scripting.NashornScriptEngine Result: 2 4.5 Base64对Base64的支持最终成了Java 8标准库的一部分,非常简单易用: 12345678910111213141516package com.tu.test.java8.newfeature;import java.nio.charset.StandardCharsets;import java.util.Base64;public class TestBase64 { public static void main(String[] args) { final String text = "Base64 finally in Java 8!"; final String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)); System.out.println(encoded); final String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8); System.out.println(decoded); }} 控制台输出的编码和解码的字符串 QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ== Base64 finally in Java 8! 新的Base64API也支持URL和MINE的编码解码。 (Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder()). 4.6 并行数组Java 8新增加了很多方法支持并行的数组处理。最重要的大概是parallelSort()这个方法显著地使排序在多核计算机上速度加快。下面的小例子演示了这个新的方法(parallelXXX)的行为。 12345678910111213141516171819package com.tu.test.java8.newfeature;import java.util.Arrays;import java.util.concurrent.ThreadLocalRandom;/ 并行数组 */public class TestParallelArray { public static void main(String[] args) { long[] arrayOfLong = new long[20000]; Arrays.parallelSetAll(arrayOfLong, index -> ThreadLocalRandom.current().nextInt(1000000)); Arrays.stream(arrayOfLong).limit(10).forEach(i -> System.out.print(i + " ")); System.out.println(); Arrays.parallelSort(arrayOfLong); Arrays.stream(arrayOfLong).limit(10).forEach(i -> System.out.print(i + " ")); System.out.println(); }} 这一小段代码使用parallelSetAll() t方法填充这个长度是2000的数组,然后使用parallelSort() 排序。这个程序输出了排序前和排序后的10个数字来验证数组真的已经被排序了。示例可能的输出如下(请注意这些数字是随机产生的) 650193 416424 740906 224816 806179 81842 605508 85078 206531 792115 28 57 91 98 184 274 341 342 442 469 4.7 并发在新增Stream机制与lambda的基础之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作。同时也在java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池(common pool)。 新增的java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的java.util.concurrent.locks.ReadWriteLock类的替代者)。 在java.util.concurrent.atomic包中还增加了下面这些类: DoubleAccumulator DoubleAdder LongAccumulator LongAdder 5. 新的工具Java 8 提供了一些新的命令行工具,在这节里我们将会介绍它们中最有趣的部分。 5.1 Nashorn引擎:jjsjjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。例如,我们创建一个具有如下内容的func.js文件: 1234function f() { return 1;};print( f() + 1 ); 我们可以把这个文件作为参数传递给jjs使得这个文件可以在命令行中执行 1<span style="font-size: 13px;">jjs func.js</span> 输出结果如下 2 更多的详细信息请参考官方文档。 5.2 类依赖分析工具:jdepsJdeps是一个功能强大的命令行工具,它可以帮我们显示出包层级或者类层级java类文件的依赖关系。它接受class文件、目录、jar文件作为输入,默认情况下,jdeps会输出到控制台。 作为例子,让我们看看现在很流行的Spring框架的库的依赖关系报告。为了让报告短一些,我们只分析一个jar: org.springframework.core-3.0.5.RELEASE.jar. jdeps org.springframework.core-3.0.5.RELEASE.jar 这个命令输出内容很多,我们只看其中的一部分,这些依赖关系根绝包来分组,如果依赖关系在classpath里找不到,就会显示not found. 1234567891011121314151617org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar) -> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found -> org.springframework.asm not found -> org.springframework.asm.commons not found org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar) -> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util 更多的详细信息请参考官方文档。 6. JVM的新特性JVM内存永久区已经被metaspace替换(JEP 122)。JVM参数 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。 7. 资源下面一些文章从不同层面上深度讨论了Java 8的特性: What’s New in JDK 8The Java TutorialsWildFly 8, JDK 8, NetBeans 8, Java EE 7Java 8 TutorialJDK 8 Command-line Static Dependency CheckerThe Illuminating Javadoc of JDK 8The Dark Side of Java 8Installing Java™ 8 Support in Eclipse Kepler SR2Java 8Oracle Nashorn. A Next-Generation JavaScript Engine for the JVMJava 8 特性 – 终极手册JAVA8 十大新特性详解 Demo下载本文案例Demo下载]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>java</tag>
<tag>lambda</tag>
<tag>stream</tag>
<tag>java8</tag>
<tag>接口的默认方法和静态方法</tag>
</tags>
</entry>
<entry>
<title><![CDATA[娱乐一下]]></title>
<url>%2F%2F2014%2F10%2F22%2Fplane_game_demo.html</url>
<content type="text"><![CDATA[娱乐链接]]></content>
<categories>
<category>HTML</category>
</categories>
<tags>
<tag>HTML5</tag>
<tag>HTML</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android优秀项目篇]]></title>
<url>%2F%2F2014%2F10%2F21%2Fandroid_excellent_project_library.html</url>
<content type="text"><![CDATA[Android优秀项目篇 (1).Linux 项目介绍: 不解释 项目地址:https://github.com/torvalds/linux (2).Android 项目介绍: 不解释 项目地址:https://android.googlesource.com/ 或 https://github.com/android (3).ZXing 二维码扫描工具 项目介绍: 现在市面上很多应用的二维码扫描功能都是从这个修改而来 本人也使用过此项目,集成起来相当方便,没几行代码. 项目地址: https://github.com/zxing/zxing 或 https://code.google.com/p/zxing/ APK地址: https://play.google.com/store/apps/details?id=com.google.zxing.client.android (4).Github的Android客户端项目 项目介绍: GitHub 客户端,谁用谁知道 项目地址:https://github.com/github/android APK地址:https://play.google.com/store/apps/details?id=com.github.mobile (5).MIUI便签 (小米便签社区开源版) 项目介绍: 项目分包比较合理,相比较miui的文件管理器 https://github.com/MiCode/FileExplorer 代码规范较好得多 项目地址:https://github.com/MiCode/Notes APK地址:https://github.com/Trinea/TrineaDownload/blob/master/miui-note-demo.apk?raw=true (6).四次元-新浪微博客户端 项目介绍: 四次元(原微次元) weiciyuan 项目地址:https://github.com/qii/weiciyuan APK地址:https://play.google.com/store/apps/details?id=org.qii.weiciyuan (7).gnucash-一个记账理财软件 项目介绍: 还没研究 项目地址:https://github.com/codinguser/gnucash-android APK地址:http://play.google.com/store/apps/details?id=org.gnucash.android (8).高仿今日头条新闻App 项目介绍: 高仿今日头条新闻App界面很全面值的学习的一个Demo,里面集成了多个开源库 如: slidingmenu(侧拉菜单包) , Android-Universal-Image-Loader (图片的异步加载包) 等 今日头条 –新闻阅读器 (一) 今日头条 –新闻阅读器 (二) 今日头条 –新闻阅读器 (三) 完结 、总结 篇 项目地址: https://github.com/Rano1/TopNews DEMO演示: (9).eoe社区 Android 客户端项目 项目介绍: eoe的Android客户端源码 功能模块有:社区精选,新闻资讯,学习教程,社区博客.每个人都可以fork一份代码修改.修改完后可以pull给管理员审核提交. 项目地址:https://github.com/eoecn/android-app/ (10).网易新闻头条客户端 项目简介: 这是一个新闻客户端,类似于网易新闻头条 .里面集成了多个开源框架如PhotoView android-FlipView 等 项目地址:https://github.com/tigerguixh/QuickNews (11).开源中国Android客户端 项目介绍: 谁用谁知道. 代码托管在OSChina 简介 项目地址:http://git.oschina.net/oschina/android-app 还会陆续更新….]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>GitHub</tag>
<tag>Android</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android上开源的酷炫的交互动画和视觉效果]]></title>
<url>%2F%2F2014%2F10%2F21%2Fandroid_Interactive-animation.html</url>
<content type="text"><![CDATA[Interactive-animation描述:收集android上开源的酷炫的交互动画和视觉效果。 1.交互篇 2.视觉篇 交互篇1.SlidingUpPanelLayout 项目介绍:他的库提供了一种简单的方式来添加一个可拖动滑动面板(由谷歌音乐推广,谷歌地图和Rdio)你的Android应用程序。项目地址:https://github.com/umano/AndroidSlidingUpPanelDEMO演示: 2.FoldableLayout 项目介绍:折叠展开点击的ITEM项目地址:https://github.com/alexvasilkov/FoldableLayoutDEMO演示:https://play.google.com/store/apps/details?id=com.alexvasilkov.foldablelayout.sample 3.android-flip项目介绍:折叠翻页效果项目地址:https://github.com/openaphid/android-flipDEMO演示: 4.SwipeBackLayout项目介绍:拖动关闭当前活动窗体项目地址:https://github.com/Issacw0ng/SwipeBackLayoutDEMO演示:APK:https://play.google.com/store/apps/details?id=me.imid.swipebacklayout.demo 5.AndroidImageSlider项目介绍:一个漂亮的Slider,可以通过自定义达到更好的效果项目地址:https://github.com/daimajia/AndroidImageSliderDEMO演示:APK:https://jumpshare.com/v/PAKDLjG0UYQjCy9bSSdX 6.Android-ParallaxHeaderViewPager项目介绍:栏目展示动画,自动播放,滚动下方列表时候,收缩效果项目地址:https://github.com/kmshack/Android-ParallaxHeaderViewPagerDEMO演示:APK:https://play.google.com/store/apps/details?id=com.kmshack.newsstand 7.FragmentTransactionExtended项目介绍:项目地址:https://github.com/DesarrolloAntonio/FragmentTransactionExtendedDEMO演示: 8.FragmentTransactionExtended项目介绍:Android按钮可以化身进度项目地址:https://github.com/dmytrodanylyk/circular-progress-buttonDEMO演示: 9.floatlabelededittext项目介绍:简单的实现浮动标签EditText:Android视图使用EditText之上,并提示EditText时填充文本。项目地址:https://github.com/wrapp/floatlabelededittextDEMO演示: 10.QuickReturn项目介绍:Showcases QuickReturn view as a header, footer, and both header and footer.项目地址:https://github.com/lawloretienne/QuickReturnDEMO演示: 11.VNTNumberPickerPreference项目介绍:这是一个易于使用的自定义偏好,打开一个对话框中有许多选择。的值被自动保存,你可以设置默认,min -和maxValue方便地在XML。项目地址:https://github.com/vanniktech/VNTNumberPickerPreferenceDEMO演示: 12.CircularFloatingActionMenu项目介绍:动画,可定制的圆形浮动菜单为Android,项目地址:https://github.com/oguzbilgener/CircularFloatingActionMenuDEMO演示: 13.NiftyDialogEffects项目介绍:Dialog的各种打开动画,Nifty Modal Dialog Effects look like this(Nifty Modal Window Effects)项目地址:https://github.com/sd6352051/NiftyDialogEffectsDEMO演示: 14.material-menu项目介绍:变形安卓菜单,返回和删除按钮项目地址:https://github.com/balysv/material-menuDEMO演示: 15.AndroidViewHover项目介绍:我们需要一个悬停视图,显示菜单,显示消息。项目地址:https://github.com/daimajia/AndroidViewHoverDEMO演示: 16.PagedHeadListView项目介绍:图片轮转切换项目地址:https://github.com/JorgeCastilloPrz/PagedHeadListViewDEMO演示: 17.android-movies-demo项目介绍:电影列表3级联动,交互项目地址:https://github.com/dlew/android-movies-demoDEMO演示:youtube: https://www.youtube.com/watch?feature=player_embedded&v=nchbp6pr2tg 18.NiftyNotification项目介绍:提示通知栏的各种动画项目地址:https://github.com/sd6352051/NiftyNotificationDEMO演示: 19.SwipeBack项目介绍:拖动关闭,模范:kicker app(https://play.google.com/store/apps/details?id=com.netbiscuits.kicker)项目地址:https://github.com/sockeqwe/SwipeBackDEMO演示: 20.AndroidSwipeLayout项目介绍:类似微信的测拉菜单项目地址:https://github.com/daimajia/AndroidSwipeLayoutDEMO演示: 21.SnackBar项目介绍:项目地址:https://github.com/MrEngineer13/SnackBarDEMO演示:googleplay:https://play.google.com/store/apps/details?id=com.mrengineer13.snackbar.sample 22.Swipecards项目介绍:A Tinder-like cards effect as of August 2014. You can swipe left or right to like or dislike the content. The library creates a similar effect to Tinder’s swipable cards with Fling animation.项目地址:https://github.com/Diolor/SwipecardsDEMO演示: 23.LDrawer项目介绍:Android抽屉与材料设计动画图标项目地址:https://github.com/ikimuhendis/LDrawerDEMO演示: 24.Dragger项目介绍:Animate your activity!项目地址:https://github.com/ppamorim/DraggerDemo演示: 视觉篇 1.android-stackblur项目介绍:毛玻璃,朦胧美项目地址:https://github.com/kikoso/android-stackblurDEMO演示: 2.BlurEffectForAndroidDesign项目介绍:实现模糊图形技巧项目地址:https://github.com/PomepuyN/BlurEffectForAndroidDesignDEMO演示: 3.Shimmer-android项目介绍:闪动的文字项目地址:https://github.com/RomainPiel/Shimmer-androidDEMO演示:youtube:http://www.youtube.com/watch?v=7EOsegp4J2o&feature=youtu.be 4.WizardPager项目介绍:它提供了一个示例实现的Android手机上安装一个向导界面项目地址:https://github.com/TechFreak/WizardPagerDEMO演示:youtube:http://www.youtube.com/watch?v=7EOsegp4J2o&feature=youtu.be 5.FloatingActionButton项目介绍:浮动的按钮项目地址:https://github.com/FaizMalkani/FloatingActionButtonDEMO演示: 6.JumpingBeans项目介绍:跳动的文本项目地址:https://github.com/frakbot/JumpingBeansDEMO演示: 7.android_maskable_layout项目介绍:可屏蔽的布局项目地址:https://github.com/christophesmet/android_maskable_layoutDEMO演示: 8.activityanimation项目介绍:Activit之间切换动画项目地址:https://github.com/flavienlaurent/activityanimationDEMO演示:youtube: https://www.youtube.com/watch?v=-E0sc6w_Jck 9.android-shape-imageview项目介绍:提供了一组自定义形状的android imageview组件,和一个框架来定义更多的形状。实现着色器和位图基于掩模图像视图。项目地址:https://github.com/siyamed/android-shape-imageviewDEMO演示: 10.RippleView项目介绍:认为模仿的连锁反应在单击推出了Android L项目地址:https://github.com/siriscac/RippleViewDEMO演示: 11.android-ui项目介绍:一个小部件可以定义的行为之间的动态变化项目地址:https://github.com/markushi/android-uiDEMO演示: 12.FlatUI项目介绍:项目地址:https://github.com/eluleci/FlatUIDEMO演示:]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>GitHub</tag>
<tag>Android</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android开源项目分类汇总]]></title>
<url>%2F%2F2014%2F10%2F21%2Fandroid_open_project.html</url>
<content type="text"><![CDATA[Android开源项目分类汇总目前包括: [Android开源项目第一篇——个性化控件(View)篇] 包括[ListView]、[ActionBar]、[Menu]、[ViewPager]、[Gallery]、[GridView]、[ImageView]、[ProgressBar]、[TextView]、[ScrollView]、[TimeView]、[TipView]、[FlipView]、[ColorPickView]、[GraphView]、[UI Style]、[其他][Android开源项目第二篇——工具库篇] 包括[依赖注入]、[图片缓存]、[网络相关]、[数据库ORM工具包]、[Android公共库]、[高版本向低版本兼容库]、[多媒体]、[事件总线]、[传感器]、[安全]、[插件化]、[文件]、[其他][Android开源项目第三篇——优秀项目篇] 比较有意思的完整的Android项目[Android开源项目第四篇——开发及测试工具篇] 包括[开发效率工具]、[开发自测相关]、[测试工具]、[开发及编译环境]、[其他][Android开源项目第五篇——优秀个人和团体篇] 乐于分享并且有一些很不错的开源项目的[个人]和[组织],包括JakeWharton、Chris Banes、Koushik Dutta等大牛 第一部分 个性化控件(View)主要介绍那些不错个性化的View,包括ListView、ActionBar、Menu、ViewPager、Gallery、GridView、ImageView、ProgressBar、TextView、ScrollView、TimeView、TipView、FlipView、ColorPickView、GraphView、UI Style等等。 一、ListView android-pulltorefresh一个强大的拉动刷新开源项目,支持各种控件下拉刷新,ListView、ViewPager、WevView、ExpandableListView、GridView、ScrollView、Horizontal ScrollView、Fragment上下左右拉动刷新,比下面johannilsson那个只支持ListView的强大的多。并且它实现的下拉刷新ListView在item不足一屏情况下也不会显示刷新提示,体验更好。项目地址:https://github.com/chrisbanes/Android-PullToRefreshDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refreshview-demo.apk?raw=trueAPP示例:新浪微博各个页面 android-pulltorefresh-listview下拉刷新ListView,这个被很多人使用的项目实际有不少bug,推荐使用上面的android-pulltorefresh项目地址:https://github.com/johannilsson/android-pulltorefreshDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refresh-listview-demo.apk?raw=true android-Ultra-Pull-to-Refresh下拉刷新,第一个项目已经停止维护了,并且使用起来相对复杂,定制性也差。这个是替代和改良方案。这个继承于ViewGroup可以包含任何View。功能甚至比SwipeRefreshLayout强大。使用起来非常简单。良好的设计,如果你想定制自己的UI样式,非常简单,就像给ListView加一个Header View那么简单。支持 API LEVEL >= 8项目地址:https://github.com/liaohuqiu/android-Ultra-Pull-To-RefreshDemo地址:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/ptr-demo/target/ultra-ptr-demo.apk?raw=true效果图: DropDownListView下拉刷新及滑动到底部加载更多ListView项目地址:https://github.com/Trinea/AndroidCommonDemo地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo文档介绍:http://www.trinea.cn/android/dropdown-to-refresh-and-bottom-load-more-listview/ DragSortListView拖动排序的ListView,同时支持ListView滑动item删除,各个Item高度不一、单选、复选、CursorAdapter做为适配器、拖动背景变化等项目地址:https://github.com/bauerca/drag-sort-listviewDemo地址:https://play.google.com/store/apps/details?id=com.mobeta.android.demodslvAPP示例:Wordpress Android SwipeListView支持定义ListView左右滑动事件,支持左右滑动位移,支持定义动画时间项目地址:https://github.com/47deg/android-swipelistviewDemo地址:https://play.google.com/store/apps/details?id=com.fortysevendeg.android.swipelistviewAPP示例:微信 Android-SwipeToDismiss滑动Item消失ListView,支持3.0以下版本见:https://github.com/JakeWharton/SwipeToDismissNOA项目地址:https://github.com/romannurik/Android-SwipeToDismissDemo地址:https://github.com/JakeWharton/SwipeToDismissNOA/SwipeToDismissNOA.apk/qr_code PinnedHeaderExpandableListView首先它是一个ExpandableListView,但是它的头部可以固定,其次,在它的上面还有一个头部可以来回伸缩项目地址:https://github.com/singwhatiwanna/PinnedHeaderExpandableListView效果图:APP示例:百度手机卫士垃圾清理界面 StickyListHeadersGroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView,支持快速滑动,支持Android2.3及以上项目地址:https://github.com/emilsjolander/StickyListHeaders效果图:APP示例:Android 4.0联系人 pinned-section-listviewGroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView项目地址:https://github.com/beworker/pinned-section-listview效果图: PinnedHeaderListViewGroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView项目地址:https://github.com/JimiSmith/PinnedHeaderListView QuickReturnListView/ScrollView的header或footer,当向下滚动时消失,向上滚动时出现项目地址:https://github.com/lawloretienne/QuickReturnDemo地址:https://play.google.com/store/apps/details?id=com.etiennelawlor.quickreturn QuickReturnHeaderListView/ScrollView的header或footer,当向下滚动时消失,向上滚动时出现项目地址:https://github.com/ManuelPeinado/QuickReturnHeaderDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/quick-return-header-demo.apk?raw=trueAPP示例:google plus IndexableListViewListView右侧会显示item首字母快捷索引,点击可快速滑动到某个item项目地址:https://github.com/woozzu/IndexableListViewDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/indexable-listview.apk?raw=trueAPP示例:微信通讯录、小米联系人 CustomFastScrollViewListView快速滑动,同时屏幕中间PopupWindows显示滑动到的item内容或首字母项目地址:https://github.com/nolanlawson/CustomFastScrollViewDemo效果图: Android-ScrollBarPanelListView滑动时固定的Panel指示显示在scrollbar旁边项目地址:https://github.com/rno/Android-ScrollBarPanel效果图: SlideExpandableListView用户点击listView item滑出固定区域,其他item的区域收缩项目地址:https://github.com/tjerkw/Android-SlideExpandableListViewDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/slide-expandable-listView-demo.apk?raw=true JazzyListViewListView及GridView item以特殊动画效果进入屏幕,效果包括grow、cards、curl、wave、flip、fly等等项目地址:https://github.com/twotoasters/JazzyListViewDemo地址:https://play.google.com/store/apps/details?id=com.twotoasters.jazzylistview.sample在线演示:http://lab.hakim.se/scroll-effects/ ListViewAnimations带Item显示动画的ListView,动画包括底部飞入、其他方向斜飞入、下层飞入、渐变消失、滑动删除等项目地址:https://github.com/nhaarman/ListViewAnimationsDemo地址:https://play.google.com/store/apps/details?id=com.haarman.listviewanimationsAPP示例:Google plus、Google Now卡片式进入、小米系统中应用商店、联系人、游戏中心、音乐、文件管理器的ListView、Ultimate、Light Flow Lite、TreinVerkeer、Running Coach、Pearl Jam Lyrics、Calorie Chart、Car Hire、Super BART、DK FlashCards、Counter Plus、Voorlees Verhaaltjes 2.0 DevsmartLib-Android横向ListView项目地址:https://github.com/dinocore1/DevsmartLib-AndroidDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/horizontal-listview-demo.apk?raw=true TwoWayView横向ListView的效果,继承自AdapterView项目地址:https://github.com/lucasr/twoway-view HorizontalVariableListView支持Item宽度不一致的ListView项目地址:https://github.com/sephiroth74/HorizontalVariableListView LinearListView用LinearLayout实现的ListView,可解决多个ListView并存等问题。目前自己也有需要,等亲自尝试过后会再具体介绍项目地址:https://github.com/frankiesardo/LinearListView MultiChoiceAdapter支持多选的ListView Adapter项目地址:https://github.com/ManuelPeinado/MultiChoiceAdapterDemo地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.multichoiceadapter.demo EnhancedListView支持横向滑动删除列表项以及撤销删除的ListView,该项目的前身是SwipeToDismissUndoList项目地址:https://github.com/timroes/EnhancedListViewDemo地址:https://play.google.com/store/apps/details?id=de.timroes.android.listviewdemo&rdid=de.timroes.android.listviewdemo ListBuddies自动滚动的双列ListView ,两个ListView滚动速度不一致,有视差效果项目地址:https://github.com/jpardogo/ListBuddiesDemo地址:https://play.google.com/store/apps/details?id=com.jpardogo.android.listbuddies效果图: SwipeMenuListView针对ListView item的侧滑菜单项目地址:https://github.com/baoyongzhang/SwipeMenuListView效果图:APP示例:手机QQV5.0 PagingListView分页加载的ListView。当滑动到ListView底部最后一个元素时,显示一个进度行,然后加载下一页数据,并显示。项目地址:https://github.com/nicolasjafelle/PagingListView PullZoomView支持下拉时HeaderView缩放的ListView、ScrollView项目地址:https://github.com/Frank-Zhu/PullZoomView效果图: PullToZoomInListView滑动ListView时使其HeaderView跟随滑动缩放项目地址:https://github.com/matrixxun/PullToZoomInListView效果图: CalendarListview实现每个月一行日历效果的ListView项目地址:https://github.com/traex/CalendarListview效果图: sticky-headers-recyclerviewGroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ListView,采用support-v7中的RecyclerView实现项目地址:https://github.com/timehop/sticky-headers-recyclerview PullSeparateListView到达顶部或底部继续拉动时,实现Item间的相互分离,两种模式:(1) 全部分离的模式,即屏幕内所有Item都会分离 (2)部分分离模式,以点击位置为分界点,部分item分离项目地址:https://github.com/chiemy/PullSeparateListView效果图: ExpandableLayoutHeader 和 Content Item 都可以展开的 ExpandableListview项目地址:https://github.com/traex/ExpandableLayout PagedHeadListView支持paginated header以及material page indicator的ListView.项目地址:https://github.com/JorgeCastilloPrz/PagedHeadListView效果图: CustomSwipeListView支持左滑弹出自定义菜单,右滑删除且允许撤销,同时可以自定义滑动动画时间和滑动触发事件的时机等。项目地址:https://github.com/xyczero/Android-CustomSwipeListView效果图:Demo地址:Download here Pull-to-Refresh.Rentals-Android提供一个简单可以自定义的下拉刷新实现,Yalantis 出品。项目地址:https://github.com/Yalantis/Pull-to-Refresh.Rentals-Android效果图: 二、ActionBar ActionBarSherlock为Android所有版本提供统一的ActionBar,解决4.0以下ActionBar的适配问题项目地址:https://github.com/JakeWharton/ActionBarSherlockDemo地址:https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demosAPP示例:太多了。。现在连google都在用效果图: ActionBar-PullToRefresh下拉刷新,ActionBar出现加载中提示项目地址:https://github.com/chrisbanes/ActionBar-PullToRefreshDemo地址:https://play.google.com/store/apps/details?id=uk.co.senab.actionbarpulltorefresh.samples.stockAPP示例:Gmail,Google plus,知乎等 FadingActionBarListView向下滚动逐渐显现的ActionBar项目地址:https://github.com/ManuelPeinado/FadingActionBarDemo地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.fadingactionbar.demoAPP示例:google music,知乎效果图: NotBoringActionBargoogle music下拉收缩的ActionBar项目地址:https://github.com/flavienlaurent/NotBoringActionBarDemo地址:http://flavienlaurent.com/blog/2013/11/20/making-your-action-bar-not-boring/APP示例:Google音乐效果图: RefreshActionItem带进度显示和刷新按钮的ActionBar项目地址:https://github.com/ManuelPeinado/RefreshActionItemDemo地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.refreshactionitem.demoAPP示例:The New York Times,DevAppsDirect.效果图: GlassActionBar类似玻璃的有一定透明度的ActionBar项目地址:https://github.com/ManuelPeinado/GlassActionBarDemo地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.glassactionbardemoAPP示例:google music效果图: 三、Menu MenuDrawer滑出式菜单,通过拖动屏幕边缘滑出菜单,支持屏幕上下左右划出,支持当前View处于上下层,支持Windows边缘、ListView边缘、ViewPager变化划出菜单等。项目地址:https://github.com/SimonVT/android-menudrawerDemo地址:http://simonvt.github.io/android-menudrawer/APP示例:Gmail、Google Music等大部分google app SlidingMenu滑出式菜单,通过拖动屏幕边缘滑出菜单,支持屏幕左右划出,支持菜单zoom、scale、slide up三种动画样式出现。与MenuDrawer相比而言,SlidingMenu支持菜单动画样式出现,MenuDrawer支持菜单view处于内容的上下层项目地址:https://github.com/jfeinstein10/SlidingMenuDemo地址:https://play.google.com/store/apps/details?id=com.slidingmenu.exampleAPP示例:Foursquare, LinkedIn, Zappos, Rdio, Evernote Food, Plume, VLC for Android, ESPN ScoreCenter, MLS MatchDay, 9GAG, Wunderlist 2, The Verge, MTG Familiar, Mantano Reader, Falcon Pro (BETA), MW3 Barracks ArcMenu支持类似Path的左下角动画旋转菜单及横向划出菜单、圆心弹出菜单项目地址:https://github.com/daCapricorn/ArcMenu效果图:APP示例:Path android-satellite-menu类似Path的左下角动画旋转菜单项目地址:https://github.com/siyamed/android-satellite-menuDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/satellite-menu-demo.apk?raw=trueAPP示例:Path radial-menu-widget圆形菜单,支持二级菜单项目地址:https://code.google.com/p/radial-menu-widget/效果图:http://farm8.staticflickr.com/7377/11621125154_d1773c2dcc_o.jpg Android Wheel Menu圆形旋转选取菜单项目地址:https://github.com/anupcowkur/Android-Wheel-Menu效果图: FoldingNavigationDrawer滑动并以折叠方式打开菜单项目地址:https://github.com/tibi1712/FoldingNavigationDrawer-AndroidDemo地址:https://play.google.com/store/apps/details?id=com.ptr.folding.sample效果图: AndroidResideMenu仿 Dribbble 的边栏菜单项目地址:https://github.com/SpecialCyCi/AndroidResideMenu效果图: CircularFloatingActionMenu一个可定制的圆形的浮动菜单控件,类似于Path的圆形菜单。这个控件的可定制性更强,可以很容易的定制菜单出现消失时的动画,起始角度和半径。项目地址:https://github.com/oguzbilgener/CircularFloatingActionMenuDemo地址:https://github.com/oguzbilgener/CircularFloatingActionMenu/tree/master/samples效果图: NavigationDrawerSINavigation Drawer的一个简单实现,滑动并以折叠方式打开菜单项目地址:https://github.com/mmBs/NavigationDrawerSIDemo地址:https://play.google.com/store/apps/details?id=mmbialas.pl.navigationdrawersi效果图:APP示例:https://play.google.com/store/apps/details?id=mmbialas.pl.navigationdrawersi DragLayout使用support.v4包下的ViewDragHelper实现QQ5.0侧滑项目地址:https://github.com/BlueMor/DragLayout效果图: LDrawerMaterial Design 形式的展开折叠Icon项目地址:https://github.com/ikimuhendis/LDrawer效果图: Floating Action Button悬浮的圆形菜单栏,支持组建滚动时自动隐藏及其他设置项目地址:https://github.com/shamanland/floating-action-button效果图: Side-Menu.Android分类侧滑菜单,Yalantis 出品。项目地址:https://github.com/Yalantis/Side-Menu.Android效果图: Context-Menu.Android可以方便快速集成漂亮带有动画效果的上下文菜单,Yalantis出品。项目地址:https://github.com/Yalantis/Context-Menu.Android效果图: 四、ViewPager 、Gallery Android-ViewPagerIndicator配合ViewPager使用的Indicator,支持各种位置和样式项目地址:https://github.com/JakeWharton/Android-ViewPagerIndicatorDemo地址:https://play.google.com/store/apps/details?id=com.viewpagerindicator.sampleAPP示例:太多了。。 JazzyViewPager支持Fragment切换动画的ViewPager,动画包括转盘、淡入淡出、翻页、层叠、旋转、方块、翻转、放大缩小等,效果类似桌面左右切换的各种效果,不过桌面并非用ViewPager实现而已项目地址:https://github.com/jfeinstein10/JazzyViewPagerDemo地址:https://github.com/jfeinstein10/JazzyViewPager/blob/master/JazzyViewPager.apk?raw=true JellyViewPager特殊切换动画的ViewPager项目地址:https://github.com/chiemy/JellyViewPager效果图: Android-DirectionalViewPager支持横向和纵向(垂直)的ViewPager项目地址:https://github.com/JakeWharton/Android-DirectionalViewPagerDemo地址:https://market.android.com/details?id=com.directionalviewpager.sample android-pulltorefresh支持下拉刷新的ViewPager项目地址:https://github.com/chrisbanes/Android-PullToRefreshDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refreshview-demo.apk?raw=trueAPP示例:新浪微博各个页面 FancyCoverFlow支持Item切换动画效果的类似Gallery View项目地址:https://github.com/davidschreiber/FancyCoverFlowDemo地址:https://play.google.com/store/apps/details?id=at.technikum.mti.fancycoverflow.samples效果图: AndroidTouchGallery支持双击或双指缩放的Gallery(用ViewPager实现),相比下面的PhotoView,在被放大后依然能滑到下一个item,并且支持直接从url和文件中获取图片,项目地址:https://github.com/Dreddik/AndroidTouchGalleryDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/touch-gallery-demo.apk?raw=trueAPP示例:类似微信中查看聊天记录图片时可双击放大,并且放大情况下能正常左右滑动到前后图片 Android Auto Scroll ViewPagerAndroid自动滚动 轮播循环的ViewPager项目地址:https://github.com/Trinea/android-auto-scroll-view-pagerDemo地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo文档介绍:http://www.trinea.cn/android/auto-scroll-view-pager/ Salvage view带View缓存的Viewpager PagerAdapter,很方便使用项目地址:https://github.com/JakeWharton/salvage Android PagerSlidingTabStrip配合ViewPager使用的Indicator,支持ViewPager Scroll时Indicator联动项目地址:https://github.com/astuetz/PagerSlidingTabStripDemo地址:https://play.google.com/store/apps/details?id=com.astuetz.viewpager.extensions.sample ViewPager3DViewPager3D效果项目地址:https://github.com/inovex/ViewPager3D AnimaTabsview仿网易云音乐标签切换的动画,带透明小三角项目地址:https://github.com/wuyexiong/transparent-over-animtabsview在线演示:http://v.youku.com/v_show/id_XNzA4MjY5NjA0.html LoopingViewPager无限循环的ViewPager项目地址:https://github.com/imbryk/LoopingViewPager android_page_curl翻书卷曲效果项目地址:https://github.com/harism/android_page_curlAPP示例:iReader在线演示:https://www.youtube.com/watch?v=iwu7P5PCpsw ViewPagerIndicator简化并实现android的TabHost效果,顶部滑动tab,引导页,支持自定义tab样式,自定义滑动块样式和位置,自定义切换tab的过渡动画,子界面的预加载和界面缓存,设置界面是否可滑动项目地址:https://github.com/LuckyJayce/ViewPagerIndicator SmartTabLayoutA custom ViewPager title strip which gives continuous feedback to the user when scrolling.This library has been added some features and utilities based on android-SlidingTabBasic project of Google Samples.自定义标题:viewpage地带使连续滚动时反馈给用户。这个库添加了一些功能和实用程序基于谷歌android-SlidingTabBasic工程样品。项目地址: https://github.com/ogaclejapan/SmartTabLayoutApp示例: https://play.google.com/store/apps/details?id=com.ogaclejapan.smarttablayout.demo效果图: AndroidImageSliderAn amazing and convenient Android image slider.一个了不起的滑块和方便Android 图片滑动。项目地址: https://github.com/daimajia/AndroidImageSliderApp示例: https://github.com/daimajia/AndroidImageSlider/releases/download/v1.0.8/demo-1.0.8.apkEclipse工程: https://github.com/daimajia/AndroidImageSlider/releases/download/v1.0.9/AndroidImageSlider-Eclipse.zip效果图: 五、GridView StaggeredGridView允许非对齐行的GridView,类似Pinterest的瀑布流,并且跟ListView一样自带View缓存,继承自ViewGroup项目地址:https://github.com/maurycyw/StaggeredGridViewDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/staggered-gridview-demo.apk?raw=trueAPP示例:Pinterest等 AndroidStaggeredGrid允许非对齐行的GridView,类似Pinterest的瀑布流,继承自AbsListView项目地址:https://github.com/etsy/AndroidStaggeredGridAPP示例:Pinterest等 PinterestLikeAdapterView允许非对齐行的GridView,类似Pinterest的瀑布流,允许下拉刷新项目地址:https://github.com/GDG-Korea/PinterestLikeAdapterViewAPP示例:Pinterest等 DraggableGridViewItem可拖动交换位置的GridView,实际是自己继承ViewGroup实现,类似桌面的单屏效果,可屏幕自动上下滚动进行Item移动交换,多屏效果见下面PagedDragDropGrid项目地址:https://github.com/thquinn/DraggableGridViewDemo地址:https://github.com/thquinn/DraggableGridView/blob/master/bin/DraggableGridViewSample.apk?raw=true StickyGridHeadersGroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的GridView项目地址:https://github.com/TonicArtos/StickyGridHeaders效果图: PagedDragDropGridItem可拖动交换位置、拖动删除的自定义控件,实际是自己继承ViewGroup实现,类似桌面的多屏效果,可拖动到屏幕边缘,屏幕自动左右滚动进行Item移动交换,可拖动进行删除,单屏效果见上面DraggableGridView项目地址:https://github.com/mrKlar/PagedDragDropGrid在线演示:http://youtu.be/FYTSRfthSuQ Android-DraggableGridViewPagerItem可拖动交换位置的GridView,实际是自己继承ViewGroup实现,类似桌面的多屏效果,可屏幕自动左右滚动进行Item移动交换,单屏效果见上面DraggableGridView项目地址:https://github.com/zzhouj/Android-DraggableGridViewPagerDemo地址:https://github.com/Trinea/trinea-download/blob/master/draggable-grid-viewpager-demo.apk?raw=true TwoWayGridView可横向滚动的GridView项目地址:https://github.com/jess-anders/two-way-gridview PagingGridView分页加载的GridView。当滑动到GridView底部最后一个行时,显示一个进度行,然后加载下一页数据,并显示。项目地址:https://github.com/nicolasjafelle/PagingGridView AsymmetricGridView一个支持跨行和跨列可变Item大小的GridView项目地址:https://github.com/felipecsl/AsymmetricGridViewDemo地址:https://play.google.com/store/apps/details?id=com.felipecsl.asymmetricgridview.app效果图: GridView with Header and Footer和ListView一样带头部和底部的GridView,用法和ListView一样项目地址:https://github.com/liaohuqiu/android-GridViewWithHeaderAndFooter效果图: 六、ImageView PhotoView支持双击或双指缩放的ImageView,在ViewPager等Scrolling view中正常使用,相比上面的AndroidTouchGallery,不仅支持ViewPager,同时支持单个ImageView项目地址:https://github.com/chrisbanes/PhotoViewDemo地址:https://play.google.com/store/apps/details?id=uk.co.senab.photoview.sampleAPP示例:photup android-gif-drawable支持gif显示的view,用jni实现的,编译生成so库后直接xml定义view即可,而且本身不依赖于其他开源项目所以相对下面的ImageViewEx简单的多项目地址:https://github.com/koral--/android-gif-drawable ImageViewEx支持Gif显示的ImageView,依赖很多,编译过程很繁琐项目地址:https://github.com/frapontillo/ImageViewExDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/imageviewex-demo.apk?raw=true RoundedImageView带圆角的ImageView项目地址:https://github.com/vinc3m1/RoundedImageView效果图: SelectableRoundedImageViewImageView允许四个角的每一个有不同的半径值。也允许椭圆形、圆形的形状或者边项目地址:https://github.com/pungrue26/SelectableRoundedImageViewDemo地址:https://play.google.com/store/apps/details?id=com.joooonho效果图: ColorArt根据图片的均色设置背景色显示文字和图片,类似itune11中效果项目地址:https://github.com/MichaelEvans/ColorArtDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/color-art-demo.apk?raw=true CircleImageView圆形的ImageView项目地址:https://github.com/hdodenhof/CircleImageView效果图: ImageViewZoom支持放大和平移的ImageView项目地址:https://github.com/sephiroth74/ImageViewZoomAPP示例:https://play.google.com/store/apps/details?id=com.aviary.android.feather KenBurnsView实现Ken Burns effect效果,达到身临其境效果的ImageView项目地址:https://github.com/flavioarfaria/KenBurnsView CustomShapeImageView各种形状的ImageView, 相比上面的圆形ImageView,多了更多形状项目地址:https://github.com/MostafaGazar/CustomShapeImageView效果图: Shape Image View可以自定义各种形状的ImageView, 并且支持边框项目地址:https://github.com/siyamed/android-shape-imageview效果图: TextDrawable一个用于生成带有文本或者字母的图片的轻量级库。扩展自Drawable,因此可用于现有/自定义/网络等ImageView类,并且包含一个流接口用于创建drawables以及一个定制的ColorGenerator项目地址:https://github.com/amulyakhare/TextDrawable效果图: 七、ProgressBar SmoothProgressBar水平进度条项目地址:https://github.com/castorflex/SmoothProgressBarDemo地址:https://play.google.com/store/apps/details?id=fr.castorflex.android.smoothprogressbar.sample ProgressWheel支持进度显示的圆形ProgressBar项目地址:https://github.com/Todd-Davies/ProgressWheelDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/progress-wheel-demo.apk?raw=true android-square-progressbar在图片周围显示进度项目地址:https://github.com/mrwonderman/android-square-progressbarDemo地址:https://play.google.com/store/apps/details?id=net.yscs.android.square_progressbar_example效果图:APP示例:square HoloCircularProgressBarAndroid4.1 时钟App样式项目地址:https://github.com/passsy/android-HoloCircularProgressBar效果图:APP示例:Android4.1时钟App ProgressButton通过图钉的不同状态显示进度项目地址:https://github.com/f2prateek/progressbutton效果图:文档介绍:http://f2prateek.com/progressbutton/ GoogleProgressBar类似google 多个圆形卡片翻转的progressBar项目地址:https://github.com/jpardogo/GoogleProgressBar效果图: TH-ProgressButton带圆形进度显示的按钮项目地址;https://github.com/torryharris/TH-ProgressButton效果图: NumberProgressBar带数字进度的进度条项目地址:https://github.com/daimajia/NumberProgressBar效果图: CircularProgressDrawable带圆形进度显示的进度条项目地址:https://github.com/Sefford/CircularProgressDrawable效果图: Android-RoundCornerProgressBarAndroid 圆角 ProgressBar,可自定义圆角颜色和半径,包括带 Icon 和不带 Icon 两种类型。项目地址:https://github.com/akexorcist/Android-RoundCornerProgressBar效果图: circular-progress-button带进度显示的Button项目地址:https://github.com/dmytrodanylyk/circular-progress-button效果图: WaveView一个波纹效果的 View,可用来做 ProgressBar项目地址:https://github.com/john990/WaveViewDemo地址:https://raw.github.com/john990/WaveView/master/screenshot%26apk/demo.unaligned.apk效果图: Android L 效果的ProgressBar项目地址:https://github.com/tigerguixh/ProgressBarDemo地址: https://github.com/tigerguixh/ProgressBar/blob/master/bin/ProgressBar.apk效果图: 八、TextView包括TextView及所有继承自TextView控件,如EditText、Button、RadioButton android-flowtextview文字自动环绕其他View的Layout项目地址:https://github.com/deano2390/FlowTextView效果图:http://i949.photobucket.com/albums/ad332/vostroman1500/1.png Android Form EditText验证输入合法性的编辑框,支持输入、英文、ip、url等多种正则验证项目地址:https://github.com/vekexasia/android-edittext-validatorDemo地址:https://play.google.com/store/apps/details?id=com.andreabaccega.edittextformexample Emojicon支持emojis的TextView和EditText项目地址:https://github.com/rockerhieu/emojicon文档介绍:http://rockerhieu.com/emojicon/ android-circlebuttonAndroid圆形按钮,实际实现是继承自ImageView项目地址:https://github.com/markushi/android-circlebuttonDemo地址:https://github.com/markushi/android-circlebutton/blob/master/example/example.apk Segmented Radio Buttons for AndroidiOS’s segmented controls的实现项目地址:https://github.com/vinc3m1/android-segmentedradiobuttonDemo地址:https://github.com/thquinn/DraggableGridView/blob/master/bin/DraggableGridViewSample.apk?raw=true效果图: Chips EditText Library支持国家名字联想从而选择显示该国国旗的EditText,实际就是通过SpannableStringBuilder实现项目地址:https://github.com/kpbird/chips-edittext-libraryDemo地址:https://github.com/kpbird/chips-edittext-library/tree/master/ChipsEditTextDemo/bin AutoFitTextView可固定边界内容字体大小自适应的TextView项目地址:https://github.com/grantland/android-autofittextview Shimmer for Android文字发淡光的TextView项目地址:https://github.com/RomainPiel/Shimmer-android Titanic可以显示水位上升下降(不知道该怎么描述 囧)的TextView项目地址:https://github.com/RomainPiel/Titanic效果图: android-iconify提供带Icon的TextView,Menu,Button等项目地址:https://github.com/JoanZapata/android-iconify Calligraphy让我们在android开发中使用自定义字体变得更加简单项目地址 :https://github.com/chrisjenx/Calligraphy效果图: CreditsRoll类似星球大战字幕效果的TextView项目地址:https://github.com/frakbot/CreditsRoll android-process-buton带加载或提交进度的Button项目地址:https://github.com/dmytrodanylyk/android-process-buton FButton扁平化的Button项目地址:https://github.com/hoang8f/android-flat-buttonDemo地址:https://play.google.com/store/apps/details?id=info.hoang8f.fbutton.demo FloatingActionButton一个类似Android版Google+浮动功能按钮的控件,可以响应ListView的滚动事件。当列表向上滚动的时候会自动显示,向下滚动的时候自动隐藏。项目地址:https://github.com/makovkastar/FloatingActionButtonDemo地址:https://github.com/makovkastar/FloatingActionButton/tree/master/效果图: Android SaripaarAndroid表单验证项目地址:https://github.com/ragunathjawahar/android-saripaar/ JumpingBeans文字像 Mexican beans 一样跳动项目地址:https://github.com/frakbot/JumpingBeansDemo地址:http://play.google.com/store/apps/details?id=net.frakbot.jumpingbeans.demo效果图: FancyButtons一个不用图片就可以帮助我们创建出漂亮按钮的库。项目地址:https://github.com/medyo/fancybuttonsDemo地址:https://github.com/medyo/fancybuttons/tree/master/samples效果图: Android-RobotoTextView一个实现了所有Roboto字体的TextView,包括新出的Roboto Slab字体。项目地址:https://github.com/johnkil/Android-RobotoTextViewDemo地址:http://play.google.com/store/apps/details?id=com.devspark.robototextviewDemo项目:https://github.com/johnkil/Android-RobotoTextView/tree/master/robototextview-sample效果图:> Android-WizardPager一个表单向导库项目地址:https://github.com/romannurik/android-wizardpagerDemo项目:https://github.com/str4d/android-wizardpager/tree/textfield效果图: RippleView一个实现了 Android L 上才引入的点击按钮后出现水波纹效果的按钮项目地址:https://github.com/siriscac/RippleViewDemo项目:https://github.com/siriscac/RippleView/tree/master/RippleViewExample效果图: RippleEffect一个实现Material Design Ripple效果的库,支持Android API 9+以上版本。项目地址:https://github.com/traex/RippleEffectDemo项目:https://github.com/traex/RippleEffect/tree/master/sample效果图: Android Floating Label Widgets包含一系列控件,这些控件特点是:有默认值,当值不为空时默认值浮动到上面变为提示项目地址:https://github.com/marvinlabs/android-floatinglabel-widgetsDemo地址:https://play.google.com/store/apps/details?id=com.marvinlabs.widget.floatinglabel.demo在线演示:http://www.youtube.com/watch?v=hpZD9gJcRg0&feature=youtu.be MaterialEditTextEditText 的 Material Design 实现。包含 Google Material Design Spec中的UI效果和一些新增特性。项目地址:https://github.com/rengwuxian/MaterialEditText MultiActionTextView可以分别给TextView中的某几个字设置点击事件的TextView项目地址:https://github.com/ajaysahani/MultiActionTextView效果图: ToggleButton状态切换的 Button,类似 iOS,用 View 实现项目地址:https://github.com/zcweng/ToggleButton效果图: SlideSwitch状态切换的开关,可以设置为类似IOS的圆形,也可以设置为矩形,用 View 实现项目地址:https://github.com/Leaking/SlideSwitch效果图: ExpandableTextView可展开和收缩内容的TextView。项目地址:https://github.com/Manabu-GT/ExpandableTextView效果图: 九、ScrollView Discrollview支持滚动时Item淡入淡出,平移,缩放效果的ScrollView项目地址:https://github.com/flavienlaurent/discrollviewDemo地址:https://github.com/flavienlaurent/discrollview/raw/master/sample.apk PullScrollView仿照新浪微博Android客户端个人中心的ScrollView,下拉背景伸缩回弹效果。项目地址:https://github.com/MarkMjw/PullScrollView效果图: ArcLayout一个非常简单的弧布局库项目地址: https://github.com/ogaclejapan/ArcLayoutDemo地址k: https://play.google.com/store/apps/details?id=com.ogaclejapan.arclayout.demo效果图: ParallaxScrollView支持视差滚动的ScrollView ,背景图片的滚动速度小于ScrollView中子控件的滚动速度项目地址:https://github.com/chrisjenx/ParallaxScrollViewDemo地址:http://cloud.github.com/downloads/chrisjenx/ParallaxScrollView/ParallaxScrollViewDemo-v1.0.5.apk AKParallax-Android支持视差滚动的ScrollView项目地址:https://github.com/ideaismobile/AKParallax-AndroidDemo地址:https://play.google.com/store/apps/details?id=com.appkraft.parallax_sample Android-ObservableScrollView监听滚动视图滚动事件的库,帮助与Toolbar的交互动效处理与Material Design的实现项目地址:https://github.com/ksoichiro/Android-ObservableScrollViewDemo地址:https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2 OverScrollView有弹性的ScrollView,实现了当手指滑动到ScrollView的顶部、底部时,可以继续的向上、向下拉伸。当释放手指的时候,向上、下回弹项目地址:https://github.com/EverythingMe/OverScrollView 十、TimeView包括TimePicker、DatePicker、CalendarView、Clock等时间相关控件 android-times-squareAndroid日历时间部件,支持选取单个日期,多个日期,及日期区间段和对话框形式显示项目地址:https://github.com/square/android-times-squareDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/times-square-demo.apk?raw=true android-calendar-card日历项目地址:https://github.com/kenumir/android-calendar-cardDemo地址:https://play.google.com/store/apps/details?id=com.wt.calendarcardsample效果图: AndroidWheelAndroid Wheel支持城市、多种日期时间、密码、图片项目地址:https://code.google.com/p/android-wheel/效果图: GoogleDateTimePickers时间选择部件项目地址:https://github.com/Mirkoddd/GoogleDateTimePickers文档介绍:https://play.google.com/store/apps/details?id=com.mirko.sample&hl=it DateTimePicker日期选择部件(Google Agenda的样式风格)项目地址:https://github.com/flavienlaurent/datetimepickerDemo地址:https://raw.github.com/biboune/datetimepicker/master/datetimepicker-sample.apk效果图: android-betterpickers提供日期、数字、时间(数字方式和钟表方式)、重复周期(闹钟的周期重复)、HMS(时、分、秒)的选择,支持以DialogFragment的弹窗选择项目地址:https://github.com/derekbrameyer/android-betterpickersDemo地址:https://play.google.com/store/apps/details?id=com.doomonafireball.betterpickers.sample效果图: Android Week View日期控件,支持周,天视图,支持自定义样式项目地址:https://github.com/alamkanak/Android-Week-View效果图: ScrollerCalendar实现每行显示一年的12个月份的RecyclerView年历项目地址:https://github.com/guanchao/ScrollerCalendar效果图: 十一、TipView包括Toast、角标、UndoBar等提示性控件 Crouton丰富样式的Toast,允许alert、comfirm、info样式及点击消失样式,允许设置Toast显示时间,允许自定义View。 本文32. SuperToasts为其扩展版项目地址:https://github.com/keyboardsurfer/CroutonDemo地址:http://play.google.com/store/apps/details?id=de.keyboardsurfer.app.demo.crouton supertooltips带动画效果的Tips显示项目地址:https://github.com/nhaarman/supertooltipsDemo地址:https://play.google.com/store/apps/details?id=com.haarman.supertooltips Android ViewBadger为其他View添加角标等项目地址:https://github.com/jgilfelt/android-viewbadgerDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/android-viewbadger.apk?raw=true效果图: SuperToasts更丰富样式的toast,支持Button、Progress、Horizontal Progress样式、支持进入动画、支持撤销及其动画设置项目地址:https://github.com/JohnPersano/SuperToastsDemo地址:https://play.google.com/store/apps/details?id=com.supertoastsdemo效果图: UndoBar屏幕底部显示取消或是确认的PopupWindows项目地址:https://github.com/soarcn/UndoBar效果图: UndoBar屏幕底部显示取消或是确认某操作项目地址:https://github.com/jenzz/Android-UndoBar效果图: Android-ActionItemBadge可以在ActionBar的MenuItem上显示一个小角标项目地址:https://github.com/mikepenz/Android-ActionItemBadge效果图: SnackBarMaterial Design 风格的 Toast,类似 Google inbox 中的提示项目地址:https://github.com/MrEngineer13/SnackBarDemo地址:https://play.google.com/store/apps/details?id=com.mrengineer13.snackbar.sample效果图: HeadsUp在2.3上使用 android 5.0的 HeadsUp 效果项目地址:https://github.com/zzz40500/HeadsUp效果图: Droppy项目地址:https://github.com/shehabic/Droppy特点:A simple yet-powerful and fully customizable Android drop-down menu. It supports Text with/without Icons, Separators, and even fully customized views.效果力: 十二、FlipView android-flip类似Flipboard翻转动画的实现项目地址:https://github.com/openaphid/android-flipDemo地址:https://github.com/openaphid/android-flip/blob/master/FlipView/Demo/APK/Aphid-FlipView-Demo.apk?raw=trueAPP示例:flipboard FlipImageView支持x、y、z及动画选择的翻转动画的实现项目地址:https://github.com/castorflex/FlipImageViewDemo地址:https://play.google.com/store/apps/details?id=fr.castorflex.android.flipimageview FoldableLayoutFlip翻转效果的ListView,目前还不支持ListView缓存项目地址:https://github.com/alexvasilkov/FoldableLayoutDemo地址:http://play.google.com/store/apps/details?id=com.alexvasilkov.foldablelayout.sample 十三、ColorPickView ColorPickerView颜色选择器,支持PopupWindows或新的Activity中打开项目地址:https://code.google.com/p/color-picker-view/效果图: HoloColorPicker颜色选择器项目地址:https://github.com/LarsWerkman/HoloColorPickerDemo地址:https://docs.google.com/file/d/0BwclyDTlLrdXRzVnTGJvTlRfU2s/edit ColorPickerPreference颜色选择器项目地址:https://github.com/attenzione/android-ColorPickerPreference效果图: ColorPicker颜色选择器(Google Agenda中的样式风格)项目地址:https://github.com/flavienlaurent/colorpickerDemo地址:https://raw.github.com/biboune/colorpicker/master/colorpicker-sample.apk效果图: 十四、GraphView MPAndroidChart强大的图表绘制工具,支持折线图、面积图、散点图、时间图、柱状图、条图、饼图、气泡图、圆环图、范围(高至低)条形图、网状图及各种图的结合;支持图的拖拽缩放;支持 Android 2.2 以上,支持横纵轴缩放,多指缩放,展现动画、高亮、保存到 sdcard、从文件读取图表项目地址:https://github.com/PhilJay/MPAndroidChartDemo地址:https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexampleDemo项目:https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample效果图:在线演示:https://www.youtube.com/watch?v=ufaK_Hd6BpI achartengine强大的图表绘制工具,支持折线图、面积图、散点图、时间图、柱状图、条图、饼图、气泡图、圆环图、范围(高至低)条形图、拨号图/表、立方线图及各种图的结合项目地址:https://code.google.com/p/achartengine/效果图:http://www.achartengine.org/dimages/sales_line_and_area_chart.pnghttp://www.achartengine.org/dimages/temperature_range_chart.pnghttp://www.achartengine.org/dimages/combined_chart.pnghttp://www.achartengine.org/dimages/budget_chart.png官网网址:http://www.achartengine.org/APP示例:Wordpress Android,Google Analytics GraphView绘制图表和曲线图的View,可用于Android上的曲形图、柱状图、波浪图展示项目地址:https://github.com/jjoe64/GraphViewDemo项目:https://github.com/jjoe64/GraphView-DemosAPP示例:Wordpress Android,Google Analytics HoloGraphLibrary绘制现状图、柱状图、饼状图项目地址:https://bitbucket.org/danielnadeau/holographlibrary/src文档介绍:https://bitbucket.org/danielnadeau/holographlibrary/wiki/Home XCL-ChartsXCL-Charts基于原生的Canvas来绘制各种图表,在设计时,尽量在保证开发效率的同时,给使用者提供足够多的定制化能力。因此使用简便,同时具有相当灵活的定制能力。目前支持3D/非3D柱形图(Bar Chart)、3D/非3D饼图(Pie Chart)、堆积图(Stacked Bar Chart)、面积图(Area Chart)、 折线图(Line Chart)、曲线图(Spline Chart)、环形图(Dount Chart)、南丁格尔玫瑰图(Rose Chart)、仪表盘(Dial Chart)、刻度盘(Gauge Chart)、雷达图(Radar Chart)、圆形图(Circle Chart)等图表。其它特性还包括支持图表缩放、手势移动、动画显示效果、高密度柱形显示、图表分界定制线、多图表的混合显示及同数据源不同类型图表切换等。项目地址:https://github.com/xcltapestry/XCL-ChartsDemo地址:https://github.com/xcltapestry/XCL-Charts/blob/master/XCL-Charts-demo/bin/XCL-Charts-demo.apk?raw=true EazeGraphAndroid 图表库,支持柱状图、分层柱状图、饼状图、线性图项目地址:https://github.com/blackfizz/EazeGraphDemo地址:https://play.google.com/store/apps/details?id=org.eazegraph.app WilliamChart绘制图表的库,支持LineChartView、BarChartView和StackBarChartView三中图表类型,并且支持 Android 2.2及以上的系统。项目地址:https://github.com/diogobernardino/WilliamChartDemo地址:https://play.google.com/store/apps/details?id=com.db.williamchartdemoDemo项目:https://github.com/diogobernardino/WilliamChart/tree/master/sample效果图: HelloCharts for Android支持折线图、柱状图、饼图、气泡图、组合图;支持预览、放大缩小,滚动,部分图表支持动画;支持 Android 2.2 以上项目地址:https://github.com/lecho/hellocharts-androidDemo地址:https://play.google.com/store/apps/details?id=lecho.lib.hellocharts.samples在线演示:https://www.youtube.com/watch?v=xbSBjyjH2SY 十五、UI Style不同样式的系统UI风格,如IOS、Bootstrap风格 UITableViewios风格控件,包括Button、ListView、TableView项目地址:https://github.com/thiagolocatelli/android-uitableviewDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/ui-tableview-demo.apk?raw=true ATableViewios风格控件项目地址:https://github.com/dmacosta/ATableViewDemo地址:https://play.google.com/store/apps/details?id=com.nakardo.atableview.demo Cards-UI卡片式View,支持单个卡片,item为卡片的ListView项目地址:https://github.com/afollestad/Cards-UIDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/cards-ui-demo.apk?raw=true cardslib卡片式View,支持单个卡片,item为卡片的ListView和GridView项目地址:https://github.com/gabrielemariotti/cardslibDemo地址:https://play.google.com/store/apps/details?id=it.gmariotti.cardslib.demo Android-BootstrapBootstrap 风格的按钮项目地址:https://github.com/Bearded-Hen/Android-Bootstrap效果图: Material Design Android LibraryAndroid L 中 Material Design 风格的组件适配到 Android 2.2+项目地址:https://github.com/navasmdc/MaterialDesignLibrary Android FlatUIAndroid 扁平化风格的组件,支持一些自定义样式项目地址:https://github.com/eluleci/FlatUI效果图: 十六、其他 SwipeBackLayout左右或向上滑动返回的Activity项目地址:https://github.com/Issacw0ng/SwipeBackLayoutDemo地址:https://play.google.com/store/apps/details?id=me.imid.swipebacklayout.demoAPP示例:知乎 android-styled-dialogs可自定义样式的dialog,默认与Holo主题样式一致,在Android2.2以上同一样式项目地址:https://github.com/inmite/android-styled-dialogsDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/styled-dialogs-demo.apk?raw=true Android Sliding Up Panel可拖动的View,能在当前Activity上扶起一个可拖动的Panel项目地址:https://github.com/umano/AndroidSlidingUpPanelDemo地址:https://play.google.com/store/apps/details?id=com.sothree.umanoAPP示例:Google Music精简播放栏 AndroidWheelAndroid Wheel支持城市、多种日期时间、密码、图片项目地址:https://code.google.com/p/android-wheel/效果图: TableFixHeaders第一列固定的Table项目地址:https://github.com/InQBarna/TableFixHeadersDemo地址:http://bit.ly/13buAIq Inscription可用于展示应用change和new feature信息项目地址:https://github.com/MartinvanZ/Inscription ActivityTransitionActivity切换动画,包括渐变、flip、某个位置进入等等项目地址:https://github.com/ophilbert/ActivityTransition效果图:类似桌面左右切换的各种效果,不过桌面并非用ViewPager实现而已文档介绍:https://github.com/jfeinstein10/JazzyViewPager/blob/master/JazzyViewPager.apk?raw=true EasyAndroidAnimations针对View的各种动画项目地址:https://github.com/2359media/EasyAndroidAnimations GlowPadBackport将Android4.2的锁屏界面解锁扩展到Android1.6及1.6+项目地址:https://github.com/rock3r/GlowPadBackportDemo地址:https://play.google.com/store/apps/details?id=net.sebastianopoggi.samples.ui.GlowPadSample效果图: GlowPadViewAndroid4锁屏界面解锁项目地址:https://github.com/nadavfima/GlowPadView效果图:https://raw.github.com/nadavfima/GlowPadView/master/example.png android-lockpatternAndroid的图案密码解锁项目地址:https://code.google.com/p/android-lockpattern/Demo地址:https://play.google.com/store/apps/details?id=group.pals.android.lib.ui.lockpattern.demo文档介绍:https://code.google.com/p/android-lockpattern/wiki/QuickUseAPP示例:Android开机的图案密码解锁,支付宝的密码解锁 PatternLock另一个 Android 图案解锁库项目地址:https://github.com/DreaminginCodeZH/PatternLockDemo 地址:https://github.com/DreaminginCodeZH/PatternLock/raw/master/dist/sample.apk效果图:APP示例:Android开机的图案密码解锁,支付宝的密码解锁 RangeBar类似于SeekBar,不同的是可以选择一个范围内的值而不是单个值项目地址:https://github.com/edmodo/range-barDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/range-bar-demo.apk?raw=true效果图: ChromeView利用Chromium实现的WebView,解决各个Android版本WebView不同的问题,同时利用最新Chrome代码项目地址:https://github.com/pwnall/chromeview android-phased-seek-bar支持预先定义状态的SeekBar项目地址:https://github.com/ademar111190/android-phased-seek-bar效果图: Android Slider Preference Library可添加到设置中的基于对话框的RankBar小部件项目地址:https://github.com/jayschwa/AndroidSliderPreference ShowcaseView library用于高亮显示应用程序的特定部分,从而突出突出重点项目地址:https://github.com/amlcurran/ShowcaseView android-segmented-controlAndroid上的Segmented Controls,相当于RadioButton组项目地址:https://github.com/hoang8f/android-segmented-control Swipeable Cards类似Tinder的卡片效果,可以加载图片并动画效果展示,向左滑动表示喜欢,向右表示不喜欢项目地址:https://github.com/kikoso/Swipeable-CardsDemo地址:https://play.google.com/store/apps/details?id=info.hoang8f.fbutton.demo EdgeEffectOverride改变ScrollView, ListView, ExpandableListView, GridView, ViewPager等滚动控件滚动到边缘的颜色效果项目地址:https://github.com/AndroidAlliance/EdgeEffectOverride android-pinned-header-listviews使ExpandListView的Group滑动到顶端时会固定不动直到另外一个Group到达顶端项目地址:https://github.com/rtyley/android-pinned-header-listviews AndroidSwipeLayout滑动Layout,支持单个View,ListView,GridView项目地址:https://github.com/daimajia/AndroidSwipeLayoutDemo地址:Download Demo效果图: DynamicCardLayout在Android中实现的类似Windows8的瓷片布局项目地址:https://github.com/dodola/DynamicCardLayout效果图: Emoticons-Keyboard带表情情符号的自定义键盘项目地址:https://github.com/chiragjain/Emoticons-Keyboard效果图: Android Typeface Helper可以帮你轻松实现自定义字体的库项目地址:https://github.com/norbsoft/android-typeface-helper效果图: Android-Anim-Playground几个动画效果,其中第二个基于android-svg的绘制效果非常好项目地址:https://github.com/Tibolte/Android-Anim-Playground效果图: NiftyDialogEffects支持自定义飞入动画样式的 Dialog项目地址:https://github.com/sd6352051/NiftyDialogEffects效果图:在线演示:http://tympanus.net/Development/ModalWindowEffects/ PostOffice创建 Holo 及 Material Design 样式的 Dialog项目地址:https://github.com/r0adkll/PostOffice效果图: Swipecards类似Tinder的卡片效果,可以加载图片并动画效果展示,向左滑动表示喜欢,向右表示不喜欢,根据 Kikoso’s Swipeable-Cards 改造而来项目地址:https://github.com/Diolor/Swipecards SeekArc圆形的 SeekBar项目地址:https://github.com/TriggerTrap/SeekArc效果图: BlurDialogFragment显示 DialogFragment 时背景模糊效果项目地址:https://github.com/tvbarthel/BlurDialogFragmentDemo地址:https://play.google.com/store/apps/details?id=fr.tvbarthel.lib.blurdialogfragment.sample range-seek-bar随机值选取的 SeekBar项目地址:https://github.com/yahoo/android-range-seek-bar效果图: MaterialRangeBar可以选择一个范围内的值而不是单个值的 SeekBar,RangeBar 的 Material Design 风格适配项目地址:https://github.com/oli107/material-range-bar效果图: MaterialListMaterial Design 风格的 CardView项目地址:https://github.com/dexafree/MaterialListDemo地址:https://play.google.com/store/apps/details?id=com.dexafree.materiallistviewexample效果图: road-trip设置path的各种动画效果,以及如何实现复杂路径动画,类似于ios中的指纹注册界面的指纹动画效果项目地址:https://github.com/romainguy/road-trip效果图: dialogplus一个简单的Android对话框,支持不同的弹出模式项目地址:https://github.com/orhanobut/dialogplus效果图: 第二部分 工具库主要包括那些不错的开发库,包括依赖注入框架、图片缓存、网络相关、数据库ORM建模、Android公共库、Android 高版本向低版本兼容、多媒体相关及其他。 一、依赖注入DI通过依赖注入减少View、服务、资源简化初始化,事件绑定等重复繁琐工作 AndroidAnnotations(Code Diet)android快速开发框架项目地址:https://github.com/excilys/androidannotations文档介绍:https://github.com/excilys/androidannotations/wiki官网网址:http://androidannotations.org/特点:(1) 依赖注入:包括view,extras,系统服务,资源等等(2) 简单的线程模型,通过annotation表示方法运行在ui线程还是后台线程(3) 事件绑定:通过annotation表示view的响应事件,不用在写内部类(4) REST客户端:定义客户端接口,自动生成REST请求的实现(5) 没有你想象的复杂:AndroidAnnotations只是在在编译时生成相应子类(6) 不影响应用性能:仅50kb,在编译时完成,不会对运行时有性能影响。PS:与roboguice的比较:roboguice通过运行时读取annotations进行反射,所以可能影响应用性能,而AndroidAnnotations在编译时生成子类,所以对性能没有影响 roboguice帮你处理了很多代码异常,利用annotation使得更少的代码完成项目项目地址:https://github.com/roboguice/roboguice文档介绍:https://github.com/roboguice/roboguice/wiki butterknife利用annotation帮你快速完成View的初始化,减少代码项目地址:https://github.com/JakeWharton/butterknife文档介绍:http://jakewharton.github.io/butterknife/ Dagger依赖注入,适用于Android和Java项目地址:https://github.com/square/dagger文档介绍:http://square.github.io/dagger/ 二、图片缓存 Android-Universal-Image-Loader图片缓存,目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性。项目地址:https://github.com/nostra13/Android-Universal-Image-LoaderDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/universal-imageloader-demo.apk?raw=true文档介绍:http://www.intexsoft.com/blog/item/74-universal-image-loader-part-3.html picassosquare开源的图片缓存项目地址:https://github.com/square/picasso文档介绍:http://square.github.io/picasso/特点:(1)可以自动检测adapter的重用并取消之前的下载(2)图片变换(3)可以加载本地资源(4)可以设置占位资源(5)支持debug模式 ImageCache图片缓存,包含内存和Sdcard缓存项目地址:https://github.com/Trinea/AndroidCommonDemo地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo文档介绍:http://www.trinea.cn/android/android-imagecache/特点:(1)支持预取新图片,支持等待队列(2)包含二级缓存,可自定义文件名保存规则(3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法(4)可方便的保存及初始化恢复数据(5)支持不同类型网络处理(6)可根据系统配置初始化缓存等 Cube ImageLoader阿里巴巴一淘使用的图片加载,综合了Android-Universal-Image-Loader 和 square 等组件优点,简单易用,良好的中文文档支持项目地址:https://github.com/etao-open-source/cube-sdkDemo地址:https://github.com/liaohuqiu/cube-sdk/raw/master/cube-sdk-sample.apk效果图:文档介绍:http://cube-sdk.liaohuqiu.net/ fresco一款强大的图片缓存工具,由facebook开发项目地址:https://github.com/facebook/fresco文档介绍:http://frescolib.org/特点:(1) 两个内存缓存加上磁盘缓存构成了三级缓存(2) 支持流式,可以类似网页上模糊渐进式显示图片(3) 对多帧动画图片支持更好,如 Gif、WebP(4) 更多样的显示,如圆角、进度条、点击重试、自定义对焦点(5) 更多样的加载,如支持 EXIF、全面支持 WebP(6) 支持 Android 2.3+ 三、网络相关 Asynchronous Http Client for AndroidAndroid异步Http请求项目地址:https://github.com/loopj/android-async-http文档介绍:http://loopj.com/android-async-http/特点:(1) 在匿名回调中处理请求结果(2) 在UI线程外进行http请求(3) 文件断点上传(4) 智能重试(5) 默认gzip压缩(6) 支持解析成Json格式(7) 可将Cookies持久化到SharedPreferences android-query异步加载,更少代码完成Android加载项目地址:https://github.com/androidquery/androidquery 或 https://code.google.com/p/android-query/Demo地址:https://play.google.com/store/apps/details?id=com.androidquery文档介绍:https://code.google.com/p/android-query/#Why_AQuery?特点:https://code.google.com/p/android-query/#Why_AQuery? Async Http ClientJava异步Http请求项目地址:https://github.com/AsyncHttpClient/async-http-client文档介绍:http://sonatype.github.io/async-http-client/ Ion支持图片、json、http post等异步请求项目地址:https://github.com/koush/ion文档介绍:https://github.com/koush/ion#more-examples HttpCacheHttp缓存项目地址:https://github.com/Trinea/AndroidCommonDemo地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo文档介绍:http://www.trinea.cn/android/android-http-cache特点是:(1) 根据cache-control、expires缓存http请求(2) 支持同步、异步Http请求(3) 在匿名回调中处理请求结果(4) 在UI线程外进行http请求(5) 默认gzip压缩 Http Request项目地址:https://github.com/kevinsawicki/http-request文档介绍:https://github.com/kevinsawicki/http-request#examples okhttpsquare开源的http工具类项目地址:https://github.com/square/okhttp文档介绍:http://square.github.io/okhttp/特点:(1) 支持SPDY( http://zh.wikipedia.org/wiki/SPDY )协议。SPDY协议是Google开发的基于传输控制协议的应用层协议,通过压缩,多路复用(一个TCP链接传送网页和图片等资源)和优先级来缩短加载时间。(2) 如果SPDY不可用,利用连接池减少请求延迟(3) Gzip压缩(4) Response缓存减少不必要的请求 RetrofitRESTFUL API设计项目地址:https://github.com/square/retrofit文档介绍:http://square.github.io/retrofit/ RoboSpiceAndroid异步网络请求工具,支持缓存、REST等等项目地址:https://github.com/stephanenicolas/robospiceDemo地址:https://github.com/stephanenicolas/RoboDemo/downloads 四、数据库 orm工具包orm的db工具类,简化建表、查询、更新、插入、事务、索引的操作 greenDAOAndroid Sqlite orm的db工具类项目地址:https://github.com/greenrobot/greenDAO文档介绍:http://greendao-orm.com/documentation/官网网址:http://greendao-orm.com/特点:(1) 性能佳(2) 简单易用的API(3) 内存小好小(4) 库大小小 ActiveAndroidAndroid Sqlite orm的db工具类项目地址:https://github.com/pardom/ActiveAndroid文档介绍:https://github.com/pardom/ActiveAndroid/wiki/_pages SprinklesAndroid Sqlite orm的db工具类,比较显著的特点就是配合 https://github.com/square/retrofit 能保存从服务器获取的数据项目地址:https://github.com/emilsjolander/sprinkles文档介绍:http://emilsjolander.github.io/blog/2013/12/18/android-with-sprinkles/ Realm移动端的数据库,适用于 Phone、Tablet、Wearable,支持 ORM,线程安全、支持连表及数据库加密,比 SQLite 性能更好项目地址:https://github.com/realm/realm-java文档介绍:http://realm.io/docs/java/0.72.0/ ormlite-android项目地址:https://github.com/j256/ormlite-android文档介绍:http://ormlite.com/sqlite_java_android_orm.shtml Schematic根据SQLite生成ContentProvider项目地址:https://github.com/SimonVT/schematic DBFlowAndroid SQLite ORM工具库。综合了 Active Android, Schematic, Ollie,Sprinkles等库的优点;通过注解实现,性能好;能生成ContentProvider。项目地址:https://github.com/Raizlabs/DBFlow文档介绍:https://github.com/Raizlabs/DBFlow#usage-docs 五、Android公共库 GuavaGoogle的基于java1.6的类库集合的扩展项目,包括collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O等等. 这些高质量的API可以使你的JAVa代码更加优雅,更加简洁项目地址:https://code.google.com/p/guava-libraries/文档介绍:https://code.google.com/p/guava-libraries/wiki/GuavaExplained VolleyGoogle提供的网络通信库,使得网络请求更简单、更快速项目地址:https://android.googlesource.com/platform/frameworks/volley文档介绍:http://commondatastorage.googleapis.com/io-2013/presentations/110%20-%20Volley-%20Easy,%20Fast%20Networking%20for%20Android.pdf AndroidCommonAndroid公共库项目地址:https://github.com/Trinea/AndroidCommonDemo地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo文档介绍:http://www.trinea.cn/android/android-common-lib/包括:(1)缓存(图片缓存、预取缓存、网络缓存)(2) 公共View(下拉及底部加载更多ListView、底部加载更多ScrollView、滑动一页Gallery)(3) Android常用工具类(网络、下载、Android资源操作、shell、文件、Json、随机数、Collection等等) shipfaster整合了Dagger Otto Retrofit Robolectric Picasso OkHttp,方便快速开发项目地址:https://github.com/pyricau/shipfaster CleanAndroidCode整合了Dagger Otto AndroidAnnotations,方便快速开发项目地址:https://github.com/pyricau/CleanAndroidCode xUtils基于Afinal,包含DbUtils、ViewUtils、HttpUtils、BitmapUtils四大模块,可用于快速开发项目地址:https://github.com/wyouflf/xUtils AfinalAfinal是一个android的ioc,orm框架,内置了四大模块功能:FinalAcitivity,FinalBitmap,FinalDb,FinalHttp。通过finalActivity,我们可以通过注解的方式进行绑定ui和事件。通过finalBitmap,我们可以方便的加载bitmap图片,而无需考虑oom等问题。通过finalDB模块,我们一行代码就可以对android的sqlite数据库进行增删改查。通过FinalHttp模块,我们可以以ajax形式请求http数据项目地址:https://github.com/yangfuhai/afinal官网网址:http://www.afinal.org UltimateAndroidUltimateAndroid是一个快速开发Android应用的框架,框架目前主要包含的功能有View Injection,ORM,异步网络请求和图片加载,自动化脚本测试,磁盘LRU等功能.同时提供了类似于TripleDes、Webview快速设置、Md5处理、String处理,Https处理等常用工具类,还有超过100多种UI控件效果。项目地址:https://github.com/cymcsg/UltimateAndroid官网网址:http://blog.marshalchen.com/UltimateAndroid/ SAFSAF(Simple Android Framework)是一个简单的android框架,它为开发Android app提供了基础性组件。项目地址:https://github.com/fengzhizi715/SAF官网网址:http://www.salesuite.cn/包括:(1)Event Bus(事件总线)(2) Rest Client(http的框架)(3) Image Cache(图片缓存)(4) Dependency Injection(依赖注入)(5) Sqlite ORM(sqlite的orm)(6) Router(Activity、Fragment的Router)(7) Utils(各种常用的工具类) BarberCustom View 神器。通过简单的注解帮助你大大减少 Custom View 中的代码量。由于和 ButterKnife 一样使用了 Annotation Proccessor ,所以对程序性能没有影响。项目地址:https://github.com/hzsweers/barber device-year-classA library that analyzes an Android device’s specifications and calculates which year the device would be considered “high end”.(facebook开发的检测手机主流配置工具)项目地址:https://github.com/facebook/device-year-class 六、Android 高版本向低版本兼容 ActionBarSherlock为Android所有版本提供统一的ActionBar,解决4.0以下ActionBar的适配问题项目地址:https://github.com/JakeWharton/ActionBarSherlockDemo地址:https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demosAPP示例:太多了。。现在连google都在用 Nine Old Androids将Android 3.0(Honeycomb)所有动画API(ObjectAnimator ValueAnimator等)兼容到Android1.0项目地址:https://github.com/JakeWharton/NineOldAndroidsDemo地址:https://play.google.com/store/apps/details?id=com.jakewharton.nineoldandroids.sample文档介绍:http://nineoldandroids.com/ HoloEverywhere将Android 3.0的Holo主题兼容到Android2.1++项目地址:https://github.com/Prototik/HoloEverywhereDemo地址:https://raw.github.com/Prototik/HoloEverywhere/repo/org/holoeverywhere/demo/2.1.0/demo-2.1.0.apk文档介绍:http://android-developers.blogspot.com/2012/01/holo-everywhere.html SherlockNavigationDrawer将Android NavigationDrawer和ActionbarSherlock结合,解决4.0以下NavigationDrawer的适配问题项目地址:https://github.com/tobykurien/SherlockNavigationDrawer文档介绍:http://developer.android.com/training/implementing-navigation/nav-drawer.html Notifications4EveryWhere将Android 4.1的Notification兼容到Android2.2++项目地址:https://github.com/youxiachai/Notifications4EveryWhere Android Switch Widget Backport将Android Switch和SwitchPreference的兼容到Android2.1++项目地址:https://github.com/BoD/android-switch-backportDemo地址:https://play.google.com/store/apps/details?id=org.jraf.android.backport.switchwidget.sample文档介绍:https://github.com/BoD/android-switch-backport#using-the-switch android-datepicker将Android 4.0的datepicker兼容到Android2.2++项目地址:https://github.com/SimonVT/android-datepicker GlowPadBackportAndroid 4.2的GlowPadView向后适配到API4以上项目地址:https://github.com/frakbot/GlowPadBackport Transitions EverywhereAndroid 4.4 的 Transitions API 兼容到 Android 2.2 以上项目地址:https://github.com/andkulikov/transitions-everywhere 七、多媒体相关 cocos2d-x跨平台的2d游戏框架,支持Android、IOS、Linux、Windows等众多平台项目地址:https://github.com/cocos2d/cocos2d-x文档介绍:http://www.cocos2d-x.org/wiki官网网址:http://www.cocos2d-x.org/ Vitamio是一款Android与iOS平台上的全能多媒体开发框架项目地址:https://github.com/yixia/VitamioBundle网站介绍:http://www.vitamio.org/docs/特点:(1) 全面支持硬件解码与GPU渲染(2) 能够流畅播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,RMVB等常见格式的视频(3) 在Android与iOS上跨平台支持 MMS, RTSP, RTMP, HLS(m3u8)等常见的多种视频流媒体协议,包括点播与直播。 PhotoProcessing利用ndk处理图片库,支持Instafix、Ansel、Testino、XPro、Retro、BW、Sepia、Cyano、Georgia、Sahara、HDR、Rotate(旋转)、Flip(翻转)等各种特效项目地址:https://github.com/lightbox/PhotoProcessingDemo地址:https://github.com/Trinea/TrineaDownload/blob/master/photo-processing.apk?raw=true Android StackBlur图片模糊效果工具类项目地址:https://github.com/kikoso/android-stackblurDemo地址:https://github.com/kikoso/android-stackblur/blob/master/StackBlurDemo/bin/StackBlurDemo.apk?raw=true文档介绍:https://github.com/kikoso/android-stackblur#usage Bitmap Smart Clipping using OpenCV图片智能裁剪保留重要部分显示项目地址:https://github.com/beartung/tclip-android利用淘宝的 http://code.taobao.org/p/tclip/ 库完成一淘玩客正在使用的图片裁剪,自动识别图片中的重要区域,并且在图片裁剪时保留重要区域特点:(1). 能进行人脸识别。图片中有人脸,将自动视为人脸区域为重要区域,将不会被裁剪掉(2).自动其它重要区域。如果图片中未识别出人脸,则会根据特征分布计算出重区域 Cropper图片局部剪切工具,可触摸控制选择区域或旋转项目地址:https://github.com/edmodo/cropper效果图:文档介绍:https://github.com/edmodo/cropper/wiki android-crop图片裁剪Activity项目地址:https://github.com/jdamcd/android-crop效果图: TileView可分块显示大图,支持2D拖动、双击、双指放大、双指捏合项目地址:https://github.com/moagrius/TileViewDemo地址:http://moagrius.github.io/TileView/TileViewDemo.apk BlurEffectForAndroidDesign图片模糊效果项目地址:https://github.com/PomepuyN/BlurEffectForAndroidDesign android-eyePC端网页查看同一局域网内的手机摄像头内容,可以用来监控哦项目地址:https://github.com/Teaonly/android-eyeDemo地址:https://play.google.com/store/apps/details?id=teaonly.droideye libpng for AndroidPNG图片的jni库,支持几乎png的所有特性项目地址:https://github.com/julienr/libpng-android文档介绍:http://www.libpng.org/pub/png/libpng.html android-gpuimage基于GPU的图片滤镜项目地址:https://github.com/CyberAgent/android-gpuimage AndroidFaceCropper图片脸部自动识别,将识别后的局部图片返回项目地址:https://github.com/lafosca/AndroidFaceCropper Android Video Crop利用TextureView播放和剪切视频,类似ImageView.setScaleType项目地址:https://github.com/dmytrodanylyk/android-video-cropDemo地址:https://github.com/lafosca/AndroidFaceCropper/releases/download/1.0/FaceCropper-sample-debug-unaligned.apk svg-androidAndroid Svg矢量图形支持项目地址:https://github.com/japgolly/svg-android https://github.com/japgolly/svg-android Android Visualizer从Android MediaPlayer获得音频,然后像iTunes及WinAmp一样展示音轨项目地址:https://github.com/felixpalmer/android-visualizer ExoPlayer包括仪表板和SmoothStreaming自适应回放,缓存持久化和自定义渲染器,方便自定义和扩展,并且可以通过应用商店更新项目地址:https://github.com/google/ExoPlayer DanmakuFlameMasterandroid上开源弹幕解析绘制引擎项目项目地址:https://github.com/ctiao/DanmakuFlameMaster 八、事件总线(订阅者模式)通过发布/订阅事件解耦事件发送和接受,从而简化应用程序组件(Activities, Fragments及后台线程)之间的通信 EventBusgreenrobot的开源项目项目地址:https://github.com/greenrobot/EventBus文档介绍:https://github.com/greenrobot/EventBus#general-usage-and-api特点:(1) 支持在不同类型的线程中处理订阅,包括发布所在线程,UI线程、单一后台线程、异步线程(2) 支持事件优先级定义,支持优先级高的订阅者取消事件继续传递,支持粘性事件,是不是跟系统的有序广播、粘性广播很像啊(3) 不是基于annotations(4) 性能更优(5) 体积小(6) 支持单例创建或创建多个对象(7) 支持根据事件类型订阅 OttoSquare的开源项目,基于Guava的Android优化项目地址:https://github.com/square/otto文档介绍:http://square.github.io/otto/EventBus与Otto的功能及性能对比文档EventBus与Otto性能对比Demo Apk 九、传感器 Great Android Sensing ToolkitAndroid感应器工具包,包含示例及使用过程中可能需要的算法项目地址:https://github.com/gast-lib/gast-libDemo地址:https://play.google.com/store/apps/details?id=root.gast.playground文档介绍:https://github.com/gast-lib/gast-lib#documentation SensorManagerAndroid传感器管理项目地址:https://github.com/nlathia/SensorManager文档介绍:https://docs.google.com/document/d/1TqThJULb-4e6TGb1gdkAaPCfyuXStjJpbnt7a0OZ9OE/edit GPSLogger记录GPS信息项目地址:https://github.com/mendhak/gpsloggerDemo地址:https://play.google.com/store/apps/details?id=com.mendhak.gpslogger文档介绍:http://code.mendhak.com/gpslogger/ Pedometer计步器,使用硬件计步感应器项目地址:https://github.com/j4velin/Pedometer leapcastChromeCast模拟器的App项目地址:https://github.com/dz0ny/leapcast Arduino-Communicator与Arduino通信的App项目地址:https://github.com/jeppsson/Arduino-Communicator android-pedometerAndroid计步器项目地址:https://github.com/bagilevi/android-pedometerDemo地址:http://pedometer.googlecode.com/files/Pedometer-1.4.apk OwnTracks for Android自己的轨迹记录项目地址:https://github.com/owntracks/android Shake Detector library for AndroidAndroid手机震动摇晃检测库,提供供UI线程调用的回调接口项目地址:https://github.com/tbouron/ShakeDetectorDemo地址:https://play.google.com/store/apps/details?id=com.github.tbouron.shakedetector.example Android heart rate monitorAndroid心跳检测项目地址:https://github.com/phishman3579/android-heart-rate-monitor Bluetooth LE Library for Android蓝牙源信息,包括宝库Mac、更新时间、RSSI、UUID、信号源距离、影响范围等信息项目地址:https://github.com/alt236/Bluetooth-LE-Library---AndroidDemo地址:https://play.google.com/store/apps/details?id=uk.co.alt236.btlescan farebot通过NFC 从公交卡中读取数据的一个应用项目地址:https://github.com/codebutler/farebot 十、安全 SQLCipherSqlite加密工具项目地址:https://github.com/sqlcipher/sqlcipher文档介绍:http://sqlcipher.net/sqlcipher-for-android/ Conceal快速高效的进行文件加密解密项目地址:https://github.com/facebook/conceal文档介绍:https://github.com/facebook/conceal#usage Android-PasscodeLock应用锁,每次启动或从任何Activity启动应用都需要输入四位数字的密码方可进入项目地址:https://github.com/wordpress-mobile/Android-PasscodeLockDemo地址:https://play.google.com/store/apps/details?id=org.wordpress.androidAPP示例:Wordpress Android,支付宝,挖财 GlowPadBackport将Android4.2的锁屏界面解锁扩展到Android1.6及1.6+项目地址:https://github.com/rock3r/GlowPadBackportDemo地址:https://play.google.com/store/apps/details?id=net.sebastianopoggi.samples.ui.GlowPadSample效果图: GlowPadViewAndroid 4锁屏界面解锁项目地址:https://github.com/nadavfima/GlowPadView效果图:https://raw.github.com/nadavfima/GlowPadView/master/example.png android-lockpatternAndroid的图案密码解锁项目地址:https://code.google.com/p/android-lockpattern/Demo地址:https://play.google.com/store/apps/details?id=group.pals.android.lib.ui.lockpattern.demo文档介绍:https://code.google.com/p/android-lockpattern/wiki/QuickUse Android-InsecureBank关于 Android 不安全性的示例项目地址:https://github.com/dineshshetty/Android-InsecureBankv2 十一、插件化更多见:Android 插件化作用、概念以及不错的资料(包括开源项目)和解决方案 dynamic-load-apkAndroid 动态加载Apk,热部署,利用 ClassLoader 以及 Activity 代理的方式解决项目地址:https://github.com/singwhatiwanna/dynamic-load-apk文档介绍:http://blog.csdn.net/singwhatiwanna/article/details/22597587 Android Dynamic Loader点评的实现方式,和上面不同的是:他不是用代理 Activity 的方式实现而是用 Fragment 以及 Schema 的方式实现项目地址:https://github.com/mmin18/AndroidDynamicLoaderDemo地址:https://github.com/mmin18/AndroidDynamicLoader/raw/master/host.apk xCombineAndroid App插件式插件开发,插件必须先安装,更推荐看上面两个开源项目项目地址:https://github.com/wyouflf/xCombine文档介绍:http://my.oschina.net/u/1171837/blog/155377 Android Plugin FrameworkAndroid插件式开发,开放的源码目前不完整项目地址:https://github.com/umeng/apf multidex安装多 dex 的 classloader项目地址:https://github.com/casidiablo/multidex ANR-WatchDogAndroid ANR 监听,通过监听自己的 UI Thread 是否被执行确定是否发生了 ANR,并可以设置相关事件项目地址:https://github.com/SalomonBrys/ANR-WatchDog 十二、文件对不同文档类型的处理,包括PDF、Word、EPub、Html、Zip等 purePDF允许从任何运行的SWF文件读取和创建PDF文档项目地址:https://github.com/sephiroth74/purePDF android-pdfview快速解析pdf的view,默认支持手势缩放和相关动画项目地址:https://github.com/JoanZapata/android-pdfview Office 365 SDK for Android Preview可支持Microsoft SharePoint Lists, Microsoft SharePoint Files, Microsoft Exchange Calendar, Microsoft Exchange Contacts, Microsoft Exchange Mail项目地址:https://github.com/OfficeDev/Office-365-SDK-for-Android OpenSpritz-AndroidEPub阅读器项目地址:https://github.com/OnlyInAmerica/OpenSpritz-Android jsoup一个解析html的java库,可方便的提取和操作数据项目地址:https://github.com/jhy/jsoup官网网址:http://jsoup.org/作用:(1) 从一个url、文件或string获得html并解析(2) 利用dom遍历或css选择器查找、提取数据(3) 操作html元素(4) 根据白名单去除用于提交的非法数据防止xss攻击(5) 输出整齐的html ZIPjava压缩和解压库项目地址:https://github.com/zeroturnaround/zt-zip文档介绍:https://github.com/zeroturnaround/zt-zip#examples作用:(1) 解压和压缩,并支持文件夹内递归操作(2) 支持包含和排除某些元素(3) 支持重命名元素(4) 支持遍历zip包内容(5) 比较两个zip包等功能 十三、其他 Salvage view带View缓存的Viewpager PagerAdapter,很方便使用项目地址:https://github.com/JakeWharton/salvage Android Priority Job QueueAndroid后台任务队列项目地址:https://github.com/path/android-priority-jobqueue文档介绍:https://github.com/path/android-priority-jobqueue#getting-started Cobub Razor开源的mobile行为分析系统,包括web端、android端,支持ios和window phone项目地址:https://github.com/cobub/razorDemo地址:http://demo.cobub.com/razor官网网址:http://dev.cobub.com/ CountlyAndroid移动端数据采集分析系统项目地址:https://github.com/Countly/countly-sdk-android官网网址:https://count.ly/ aFileChooser文件选择器,可内嵌到程序中,而无需使用系统或三方文件选择器。项目地址:https://github.com/iPaulPro/aFileChooser androidpn基于xmpp协议的消息推送解决方案,包括服务器端和android端。项目地址:https://github.com/dannytiehui/androidpn BoltsAndroid的异步编程模式项目地址:https://github.com/BoltsFramework/Bolts-Android/与AsyncTask比较:(1) 使用的是无大小限制的线程池(2) 任务可组合可级联,防止了代码耦合 CastCompanionLibrary-android使Android程序中更快的接入Google Cast项目地址:https://github.com/googlecast/CastCompanionLibrary-android文档介绍:https://developers.google.com/cast/ CastVideos-android从Android设备分享Video通过Google Cast项目地址:https://github.com/googlecast/CastVideos-android文档介绍:https://developers.google.com/cast/ Uninstall_StaticsAndroid应用自身被卸载监听及打开浏览器等反馈功能实现项目地址:https://github.com/sevenler/Uninstall_Statics文档介绍:http://www.cnblogs.com/zealotrouge/p/3157126.html http://www.cnblogs.com/zealotrouge/p/3159772.html Memento保证在系统配置改变时,Activity中的某些数据可以简单安全的保持不变项目地址:https://github.com/mttkay/memento文档介绍:https://github.com/mttkay/memento#usage FreeFlow布局引擎,更简单的创建自定义布局,并且当数据和布局改变时更美观的过渡动画项目地址:https://github.com/Comcast/FreeFlowDemo地址:https://github.com/Comcast/FreeFlow/releases Android Gesture Detectors FrameworkAndroid手势框架,支持双指旋转、移动、平移、缩放等项目地址:https://github.com/Almeros/android-gesture-detectors Mapbox Android SDKAndroid Map的替代版项目地址:https://github.com/mapbox/mapbox-android-sdk Activity animationActivity跳转动画,支持各个方向波浪的效果项目地址:https://github.com/flavienlaurent/activityanimation在线演示:https://www.youtube.com/watch?v=-E0sc6w_Jck KryoNet通过NIO提供客户端和服务器端TCP/UDP网络传输的Java库项目地址:https://github.com/EsotericSoftware/kryonet Rebound一个模仿弹簧反弹的Java库,可用于创建动画项目地址:https://github.com/facebook/rebound Android Social Networks社交网络接入统一管理器,可方便的从Twitter, LinkedIn, Facebook, Google Plus登陆、获得个人信息、发送消息、发送专篇、添加或删除好友项目地址:https://github.com/antonkrasov/AndroidSocialNetworksDemo地址:https://play.google.com/store/apps/details?id=com.github.androidsocialnetworks.apidemos SmartAppUpdatesAndroid App 增量升级,包含前后端方案、Demo、以及 so 库,可用于商店或大体积 App 差分升级项目地址:https://github.com/cundong/SmartAppUpdates Magnet创建类似 Facebook 聊天桌面悬浮窗的效果项目地址:https://github.com/premnirmal/Magnet Parceler通过注解及工具类自动完成实体类 Parcelable及值传递项目地址:https://github.com/johncarl81/parceler AcDisplay将 Android 的通知都集中到锁屏显示项目地址:https://github.com/AChep/AcDisplayDemo地址:https://play.google.com/store/apps/details?id=com.achep.acdisplay QrCodeScanZXing和ZBar结合的二维码扫描项目,提高了扫描效率项目地址:https://github.com/SkillCollege/QrCodeScan效果图: Android-ScreenShot实现android系统截屏功能项目地址:https://github.com/Android-ScreenShot/AndroidScreenShotService文档介绍:http://blog.csdn.net/buptgshengod/article/details/39155979 card.io SDK for Android信用卡扫描 Android SDK项目地址:https://github.com/card-io/card.io-Android-SDKDemo项目:https://github.com/card-io/card.io-Android-SDK/tree/master/SampleApp ASNETwitter, Facebook, Google Plus, LinkedIn, Instagram, Vkontakte, Odnoklassniki 的集成库,包括他们的大多数功能项目地址:https://github.com/gorbin/ASNEDemo地址:https://play.google.com/store/apps/details?id=com.gorbin.androidsocialnetworksextended.asne Android Signature PadAndroid 自定义的签名 View,可自定义笔颜色和大小项目地址:https://github.com/gcacace/android-signaturepadDemo项目:https://github.com/gcacace/android-signaturepad/tree/master/SignaturePad-Example TeleportAndroid Wear 数据同步和消息传送库项目地址:https://github.com/Mariuxtheone/Teleport DebugLog可以帮你创建更简单和更容易理解的调试日志,能够友好的显示调试信息所在类和函数。项目地址:https://github.com/MustafaFerhan/DebugLog效果图: Logger简单、美观而且十分强大的Android日志工具项目地址:https://github.com/orhanobut/loggerDemo地址:https://github.com/orhanobut/logger/tree/master/app效果图: 第三部分 优秀项目主要介绍那些Android还不错的完整项目,目前包含的项目主要依据是项目有意思或项目分层规范比较好。Linux项目地址:https://github.com/torvalds/linuxAndroid项目地址:https://android.googlesource.com/ 或 https://github.com/android以上两个项目,不解释 (1) ZXing二维码扫描工具项目地址:https://github.com/zxing/zxing 或 https://code.google.com/p/zxing/APK地址:https://play.google.com/store/apps/details?id=com.google.zxing.client.androidPS:现在市面上很多应用的二维码扫描功能都是从这个修改而来 (2) photup编辑机批量上传照片到facebook上项目地址:https://github.com/chrisbanes/photupAPK地址:https://play.google.com/store/apps/details?id=uk.co.senab.photupPS:代码分包合理,很棒。不过这个项目依赖的开源项目比较多,比较难编译 (3) github-androidGithub的Android客户端项目项目地址:https://github.com/github/androidAPK地址:https://play.google.com/store/apps/details?id=com.github.mobile (4) NotesMIUI便签项目地址:https://github.com/MiCode/NotesAPK地址:https://github.com/Trinea/TrineaDownload/blob/master/miui-note-demo.apk?raw=truePS:项目分包比较合理,相比较miui的文件管理器https://github.com/MiCode/FileExplorer 代码规范较好得多 (5) weicuiyuan四次元-新浪微博客户端项目地址:https://github.com/qii/weiciyuanAPK地址:https://play.google.com/store/apps/details?id=org.qii.weiciyuan (6) gnucash-android一个记账理财软件项目地址:https://github.com/codinguser/gnucash-androidAPK地址:http://play.google.com/store/apps/details?id=org.gnucash.android (7) AntennaPod支持rss订阅、音乐订阅项目地址:https://github.com/danieloeh/AntennaPodAPK地址:https://play.google.com/store/apps/details?id=de.danoeh.antennapod (8) ChaseWhisplyProject打鬼游戏项目地址:https://github.com/tvbarthel/ChaseWhisplyProjectAPK地址:https://play.google.com/store/apps/details?id=fr.tvbarthel.games.chasewhisply (9) Tweet Lanes功能完整的Twitter客户端项目地址:https://github.com/chrislacy/TweetLanesAPK地址:https://play.google.com/store/apps/details?id=com.tweetlanes.android (10) Financius简单易用的记账程序项目地址:https://github.com/mvarnagiris/FinanciusAPK地址:https://play.google.com/store/apps/details?id=com.code44.finance (11) todo.txt-androidtodo.txt的官方Android应用项目地址:https://github.com/ginatrapani/todo.txt-androidAPK地址:https://play.google.com/store/apps/details?id=com.todotxt.todotxttouch (12) simpletask基于todo.txt官方应用的另一个客户端项目地址:https://github.com/mpcjanssen/simpletask-androidAPK地址:https://play.google.com/store/apps/details?id=nl.mpcjanssen.todotxtholo (13) Muzei Live Wallpaper定时更换桌面精美壁纸项目地址:https://github.com/romannurik/muzeiAPK地址:https://play.google.com/store/apps/details?id=net.nurik.roman.muzei (14) Scanbook扫描搜索查询图书信息项目地址:https://github.com/JayFang1993/ScanBookAPK地址:http://www.wandoujia.com/apps/com.scanbook (14) ioschedThe Google I/O 2014 Android App项目地址:https://github.com/google/ioschedAPK地址:https://play.google.com/store/apps/details?id=com.google.samples.apps.iosched效果图: 第四部分 开发工具及测试工具主要介绍和Android开发工具和测试工具相关的开源项目。 一、开发效率工具 Parceler通过注解及工具类自动完成实体类 Parcelable及值传递项目地址:https://github.com/johncarl81/parceler Json2Java根据JSon数据自动生成对应的Java实体类,还支持Parcel、Gson Annotations对应代码自动生成。期待后续的提取父类以及多url构建整个工程的功能项目地址:https://github.com/jonfhancock/JsonToJava在线演示:http://jsontojava.appspot.com/ IntelliJ Plugin for Android Parcelable boilerplate code generationAndroid studio插件,生成Parcelable代码项目地址:https://github.com/mcharmas/android-parcelable-intellij-plugin效果图: Android Holo Colors IntelliJ PluginAndroid studio插件,生成holo样式9 patch图片项目地址:https://github.com/jeromevdl/android-holo-colors-idea-plugin效果图: Android Drawable Factory用于生成各个分辨率的图片项目地址:https://github.com/tizionario/AndroidDrawableFactory效果图: SelectorChapek for AndroidAndroid Studio插件,可根据固定文件名格式资源自动生成drawable selectors xml文件。项目地址:https://github.com/inmite/android-selector-chapek Android Action Bar Style GeneratorAndroid ActionBar样式生成器,可在线选择ActionBar样式自动生成所需要的图片资源及xml文件项目地址:https://github.com/jgilfelt/android-actionbarstylegenerator在线演示:http://jgilfelt.github.io/android-actionbarstylegenerator/ ButterKnifeZelezny用于快速生成ButterKnifeView注入代码的Android Studio/IDEA插件项目地址:https://github.com/inmite/android-butterknife-zelezny RoboCoP利用Gradle task根据固定格式的json文件生成ContentProvider项目地址:https://github.com/mediarain/RoboCoP appiconsizes用于生成各个分辨率的图片项目地址:http://www.appiconsizes.com/ Gradle Retrolambda PluginRetrolambda是将Java8的Lambdas应用于Java7的工具,本项目是Gradle插件,通过Retrolambda从而使Java或Android项目用Java8的Lambdas编写,将编译后的字节码转换为Java6和7的字节码从而正常运行项目地址:https://github.com/evant/gradle-retrolambda Dagger IntelliJ Plugindagger的intellij插件项目地址:https://github.com/square/dagger-intellij-plugin Android Gen Drawable Maven plugin在编译时根据SVG描述文件生成不同分辨率的jpg、png或点9图片项目地址:https://github.com/avianey/androidgendrawable-maven-plugin Android Asset Studio各种Android资源自动生成器,包括启动图标、ActionBar图标、通知栏图标、点9等项目地址:https://github.com/romannurik/AndroidAssetStudio在线演示:http://romannurik.github.io/AndroidAssetStudio/ jsonschema2pojo根据Json内容生成java对象,支持jackjson和gson项目地址:https://github.com/joelittlejohn/jsonschema2pojo在线演示:http://www.jsonschema2pojo.org/ 9-Patch-Resizer自动生成 png 及点 9 图片的不同分辨率版本项目地址:https://github.com/redwarp/9-Patch-Resizer AndroidLocalizationer可用于将项目中的 string 资源自动翻译为其他语言的 Android Studio/IntelliJ IDEA 插件项目地址:https://github.com/westlinkin/AndroidLocalizationer 二、开发自测相关 Quality Tools for AndroidAndroid测试及自测工具集合和示例项目地址:https://github.com/stephanenicolas/Quality-Tools-for-Android android-test-kitGoogle的Android测试工具包括GoogleInstrumentationTestRunner(增强版的InstrumentationTestRunner)和Espresso(用于快速写出可靠测试用例的API)项目地址:https://code.google.com/p/android-test-kit/文档介绍:https://code.google.com/p/android-test-kit/w/list robolectric测试用例编写框架项目地址:https://github.com/robolectric/robolectricDemo地址:https://github.com/robolectric/robolectricsample文档介绍:http://robolectric.org/特点:(1). 不需要模拟器在一般JVM就可以运行测试用例(2). 能完成在真机上的大部分测试包括感应器其他的测试用例及相关模块Mock可见:android-mock, mockito, easy-mock Android FEST提供一些列方便的断言,可用于提高编写Android自测代码效率项目地址:https://github.com/square/fest-android BoundBox可用于测试类各种访问权限的属性、方法。实际是通过BoundBox这个annotation生成一个属性和方法都是public权限的中间类并对此类进行测试完成的项目地址:https://github.com/stephanenicolas/boundbox Hugo用于打印函数信息及执行时间的工具,仅在debug模式生效项目地址:https://github.com/JakeWharton/hugo scalpel在应用下面添加一层用于界面调试,待详细补充 // TODO项目地址:https://github.com/JakeWharton/scalpel Android Screenshot libraryAndroid截图工具类,用于在持续集成时截图项目地址:https://github.com/rtyley/android-screenshot-lib sonar-android-lint-plugin将android lint的错误在sonar中展现项目地址:https://github.com/SonarCommunity/sonar-android 三、测试工具 Spoon可用于android不同机型设备自动化测试,能将应用apk和测试apk运行在不同机器上并生成相应测试报告。项目地址:https://github.com/square/spoon Tencent APTAPT是腾讯开源的一个Android平台高效性能测试组件,提供丰富实用的功能,适用于开发自测、定位性能瓶颈;测试人员完成性能基准测试、竞品对比测试项目地址:https://github.com/stormzhang/APT Emmagee网易开源的性能测试工具,包括CPU、内存、网络流量、启动时间、电池状态等项目地址:https://github.com/NetEase/Emmagee Android py-uiautomatorpy-uiautomator是一个对Android uiautomator用python进行封装的测试框架.项目地址:https://github.com/xiaocong/uiautomator Augmented Traffic ControlAugmented Traffic Control: A tool to simulate network conditions(模拟网络状况)模拟网络状况,包括带宽、时延抖动、丢包率、错包率、包重排率项目地址:https://github.com/facebook/augmented-traffic-control stethoStetho is a debug bridge for Android applications, enabling the powerful Chrome Developer Tools and much more. 使用chrome来调试android设备项目地址:https://github.com/facebook/stetho 四、开发及编译环境 Buckfacebook开源的Android编译工具,效率是ant的两倍。主要优点在于:(1) 加快编译速度,通过并行利用多核cpu和跟踪不变资源减少增量编译时间实现(2) 可以在编译系统中生成编译规则而无须另外的系统生成编译规则文件(3) 编译同时可生成单元测试结果(4) 既可用于IDE编译也可用于持续集成编译(5) facebook持续优化中项目地址:https://github.com/facebook/buck Android Maven PluginAndroid Maven插件,可用于对android三方依赖进行管理。在J2EE开发中,maven是非常成熟的依赖库管理工具,可统一管理依赖库。项目地址:https://github.com/jayway/maven-android-plugin umeng-muti-channel-build-tool渠道打包工具项目地址:https://github.com/umeng/umeng-muti-channel-build-tool另可参见Google的构建系统Gradle:http://tools.android.com/tech-docs/new-build-system/user-guide Genymotion目前最好用最快的android模拟器项目地址:http://www.genymotion.com/Android studio集成控件: http://plugins.jetbrains.com/plugin/7269?pr=ideaCyril Mottier推荐:http://cyrilmottier.com/2013/06/27/a-productive-android-development-environment/ gradle-mvn-push方便的将Gradle的Artifacts上传到Maven仓库项目地址:https://github.com/chrisbanes/gradle-mvn-push文档介绍:https://github.com/chrisbanes/gradle-mvn-push#usage Android Emulator Plugin for JenkinsAndroid模拟器 jenkins插件,用于Jenkins做持续集成时跑模拟器测试项目地址:https://github.com/jenkinsci/android-emulator-plugin Android Maven Plugin管理应用所需要的依赖库。包括的构建工具有Maven、Gradle、ant、sbt项目地址:https://github.com/mosabua/maven-android-sdk-deployer SDK Manager Plugin下载和管理Android SDK的Gradle插件项目地址:https://github.com/JakeWharton/sdk-manager-plugin Gradle Protobuf Plugin将.proto文件转换成Java文件的gradle插件项目地址:https://github.com/andrewkroh/gradle-protobuf-plugin ChromeADBChrome 的 Adb 插件,当登录后,能看到所有连接的设备并操作,可以看应用、进程、内存及磁盘使用情况等项目地址:https://github.com/importre/chromeadb 五、其他 ViewServer允许app运行在任何手机上都可以用HierarchyViewer查看项目地址:https://github.com/romainguy/ViewServer GridWichterle for Android在整个系统上显示一个grid,用来帮助查看应用布局及使得布局更美观,可设置grid网格大小和颜色,android推荐48dp和8dp,可见 Android Design Guidelines – Metrics and Grids,比起hierarchyviewer相差甚远,不过偶尔可用来作为布局查看工具。项目地址:https://github.com/inmite/android-grid-wichterleDemo地址:https://play.google.com/store/apps/details?id=eu.inmite.android.gridwichterle Catlog手机端log查看工具,支持不同颜色显示、关键字过滤、级别过滤、进程id过滤、录制功能等项目地址:https://github.com/nolanlawson/Catlog在线演示:https://play.google.com/store/apps/details?id=com.nolanlawson.logcat PID Cat根据package查看logcat日志项目地址:https://github.com/JakeWharton/pidcat ACRA应用崩溃信息日志上报到GoogleDoc工具,网页版展现结果三方开源地址https://github.com/BenoitDuffez/crashreportsviewer项目地址:https://github.com/ACRA/acra文档介绍:https://github.com/ACRA/acra/wiki/BasicSetup Crashlytics提供丰富的应用崩溃信息日志收集轻量级,丰富,可自定义应用崩溃信息收集器,附有邮件通知项目地址:http://www.crashlytics.com/集成插件:Android Studio, Eclipse and IntelliJ Android Resource Navigatorchrome插件,可以方便的查看github上android源码工程的styles.xml和themes.xml。主要功能:(1) 快速打开android styles.xml themes.xml(2) 方便在资源间跳转。styles.xml themes.xml文件中资源链接跳转,可以方便跳转到某个资源(3) 方便查找某个style和theme。chrome地址栏输入arn+tab+搜索内容回车即可(4) 自动下载不同分辨率下的drawable(5) 通过映射查找那些不是按照固定命名规则命名的style和theme项目地址:https://github.com/jgilfelt/android-resource-navigator在线演示:https://chrome.google.com/webstore/detail/android-resource-navigato/agoomkionjjbejegcejiefodgbckeebo?hl=en&gl=GB android-resource-remover根据lint的提示删除项目中无用的资源,减少包的大小项目地址:https://github.com/KeepSafe/android-resource-remover Telescope通过手势截图以特定主题发送到特定邮箱地址报告Bug项目地址:https://github.com/mattprecious/telescope Complete Android Fragment & Activity Lifecycle完整的Android Fragment/Activity生命周期图项目地址:https://github.com/xxv/android-lifecycle Bugsnag Notifier for Android通过Thread.UncaughtExceptionHandler捕获应用未处理的异常崩溃 Bug 并用 Notification 展示同时上传到后台服务器项目地址:https://github.com/bugsnag/bugsnag-android文档介绍:https://github.com/bugsnag/bugsnag-android#installation--setup Material Design IconsGoogle Material Design 规范中的 Icon项目地址:https://github.com/google/material-design-icons scrollscreenshotAndroid 滚动屏幕自动截图 jar 包,支持纵向、横向滚动截屏拼接项目地址:https://github.com/PGSSoft/scrollscreenshot效果图: droidicon1600+的海量Icon,包括750+的Material Design icons项目地址:https://github.com/theDazzler/droidicon 第五部分主要介绍那些乐于分享并且有一些很不错的开源项目的个人和组织。Follow大神,深挖大神的项目和following,你会发现很多。 一、个人 JakeWharton就职于Square,绝对牛逼的大神,项目主要集中在Android版本兼容,ViewPager及开发工具上Github地址:https://github.com/JakeWharton代表作:ActionBarSherlock,Android-ViewPagerIndicator,Nine Old Androids,SwipeToDismissNOA,hugo,butterknife,Android-DirectionalViewPager, scalpelpidcat另外对square及其他开源项目有很多贡献主页:http://jakewharton.com/ Chris BanesGithub地址:https://github.com/chrisbanes代表作:ActionBar-PullToRefresh,PhotoView,Android-BitmapCache,Android-PullToRefresh主页:http://chris.banes.me/ Koushik Dutta就职于ClockworkModGithub地址:https://github.com/koush代表作:Superuser,AndroidAsync,UrlImageViewHelper,ion, 另外对https://github.com/CyanogenMod 的开源项目有很多贡献主页:http://koush.com/ Simon VigGithub地址:https://github.com/SimonVT代表作:android-menudrawer,MessageBar主页:http://simonvt.net/ Manuel PeinadoGithub地址:https://github.com/ManuelPeinado代表作:FadingActionBar,GlassActionBar,RefreshActionItem,QuickReturnHeader Emil Sj?landerGithub地址:https://github.com/emilsjolander代表作:StickyListHeaders,sprinkles,android-FlipView主页:http://emilsjolander.se/ greenrobotGithub地址:https://github.com/greenrobot代表作:greenDAO,EventBus主页:http://greenrobot.de/ Jeff GilfeltGithub地址:https://github.com/jgilfelt代表作:android-mapviewballoons,android-viewbadger,android-actionbarstylegenerator,android-sqlite-asset-helper主页:http://jeffgilfelt.com Romain GuyAndroid team成员(2013.10已离开Android team,仍在Google)Github地址:https://github.com/romainguy代表作:ViewServer主页:http://www.curious-creature.org/category/android/個人攝影作品:http://www.flickr.com/photos/romainguy sephiroth74就职于Aviary.comGithub地址:https://github.com/sephiroth74代表作:ImageViewZoom,HorizontalVariableListView,AndroidWheel,purePDF主页:http://www.sephiroth.it/ Cyril MottierGoogle开发者专家认证,发布一些Android技巧及文章Github地址:https://github.com/cyrilmottier代表作:GreenDroid,Polaris主页:http://cyrilmottier.com/ 二、组织 Square有态度有良心的企业,很多不错的分享Github地址:https://github.com/square代表作:okhttp、fest-android,android-times-square、picasso、dagger、spoon等等主页:http://square.github.io/ Inmite s.r.o.Github地址:https://github.com/inmite代表作:android-styled-dialogs,android-grid-wichterle,android-selector-chapek主页:http://www.inmite.eu/ 三、博客部分国外著名 Android 开发者信息 本博客转自trinea 2015年3月31日版本还会陆续更新中…]]></content>
<categories>
<category>Android</category>
</categories>
<tags>
<tag>GitHub</tag>
<tag>Android</tag>
</tags>
</entry>
<entry>
<title><![CDATA[模板设计模式]]></title>
<url>%2F%2F2014%2F10%2F20%2FDesign_pattern_Template.html</url>
<content type="text"><![CDATA[模板设计模式模版模式设计步骤1. 写出解决某一类问题的固有的模版代码 2. 抽取模版代码中可变的部分,形成独立的函数 3. 可变部分抽取的函数定义为抽象函数,类定义为抽象类 4. 创建实现类继承并实现父类的未实现的函数 5. 为了避免子类重写父类的模版代码,需要将模版代码修饰为final 123456789101112131415abstract class RunCode //3.有抽象方法code()所以必须定义为抽象类{ // 1.计算一段代码的运行时间 public final void getRuntime(){ //5.方法为最终代码final修饰 // 获取运行前系统的当前时间 毫秒 一秒 = 1000毫秒 long start = System.currentTimeMillis(); // 测试代码 code(); //2.调用需要测试运行时间的方法, // 运行结束后获取系统的当前时间 long end = System.currentTimeMillis(); //结束时间减开始时间等于运行用时 System.out.println("运行时间: " + ( end - start ) ); } public abstract void code();//3.不知这方法如何实现,定义成抽象} 12345678910class MyRunCode extends RunCode //4.实现类 继承抽象类{ public void code(){ //4.实现抽象方法 for (int i = 0;i<100 ;i++ ) { System.out.println("目前打印的是第"+i+"次"); } }} 1234567class Demo //演示类 { public static void main(String[] args) { new MyRunCode().getRuntime(); //调用 }} 模板设计模式方式二: 接口版接口版比较适合在同一个类中出现重复代码时 将重复代码封装成一个函数.定义接口,与接口实现类进行实现 案例: 12345678910111213141516171819202122232425262728293031323334package com.tu.test;import org.junit.Test;/*** * 模板设计模式基于接口版 * @author ComTu */public class Template_Inter { //接口 public interface RunCode_inter{ //1.编写接口 void code(); //2.编写抽象方法 (接口中方法默认是abstract) } //模板块 //3.编写一个模板代码方法需要带接口类型的参数. public void RunCode(RunCode_inter runcode){ //运行时的毫秒数 long start = System.currentTimeMillis(); runcode.code(); //4.调用接口的code方法 //结束运行的毫秒数 long end = System.currentTimeMillis(); System.out.println("运行时间: "+(end-start)); } //测试函数 @Test public void startFor(){ RunCode(new RunCode_inter(){ //5.模板块 ( 创建接口实现类 ) @Override public void code() { //6.需要在模板中执行的代码 for (int i = 0; i < 10000; i++) { System.out.println("i : "+i); } } }); }} 如果非要在其它类中进行调用的话可以使用如下方法: 123456789101112package com.tu.test;public class Test { public static void main(String[] args) { //导入 类名.接口名 类似与java.util.Map.Entry Map接口.Entry接口 new Template_Inter().RunCode(new com.tu.test.Template_Inter.RunCode_inter(){ //5.模板块 (创建接口实现类) @Override public void code() {//6.模板中执行的代码. System.out.println("运行的代码..."); } }); }}]]></content>
<categories>
<category>设计模式</category>
</categories>
<tags>
<tag>设计模式</tag>
<tag>模板模式</tag>
<tag>模板方法模式</tag>
<tag>Template</tag>
</tags>
</entry>
<entry>
<title><![CDATA[单例设计模式]]></title>
<url>%2F%2F2014%2F10%2F20%2FDesign_pattern_Singleton.html</url>
<content type="text"><![CDATA[单例设计模式意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 适用性 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。 单例的设计模式 问题:可以通过new关键字调用构造函数对类的对象进行初始化 单例中如果解决了无法对创建的对象进行初始化,那么就不会将对象赋值给引用变量。 (保持对象的唯一性) 单例的实现步骤 私有化构造函数 , 禁止直接通过构造函数创建实例 在类中自定义一个对象 对外提供该对象的公共访问方式 单例的一般实现有两中模式 饿汉式 指全局的单例实例在类装载时构建。(存在对象生命周期过长问题) 懒汉式 指全局的单例实例在第一次被使用时构建。(存在线程安全问题,使用锁后推荐使用) 内部类 枚举 例: 饿汉式 1234567891011121314public class SingletonHungry{ //饿汉式 // 1. 提供显示的私有的构造函数 private SingletonHungry(){} // 2. 定义一个该类的引用变量并调用构造函数初始化非静态成员属性 private static SingletonHungry instance = new SingletonHungry(); //饿汉式单例 // 3. 对单例的对象进行封装 public static SingletonHungry getInstance(){ return instance; } // 提供服务 public void getService(){ System.out.println( "提供服务........" ); }} 懒汉式 12345678910111213141516171819202122232425public class SingletonLazy1{ //懒汉式 _ 双重检查锁定 // 1. 提供显示的私有的构造函数 private SingletonLazy1(){} // 2. 定义一个该类的引用变量并调用构造函数初始化 非静态成员属性 private static SingletonLazy1 instance = null; // 懒汉式单例 // 3. 对单例的对象进行封装 public static SingletonLazy1 getInstance(){ //需要用到的时候进行调用 if(instance == null ){//查看是否有实例,有则直接返回 synchronized(SingletonLazy1.class ){//多线程操作,有可能会出现多个线程同时必发范围_加锁. if(instance == null ){ synchronized(SingletonLazy1.class ){ //线程同步 if(instance == null){ instance = new SingletonLazy1 (); } } } } } return instance; } // 提供服务 public void getService(){ System.out.println( "提供服务........" ); }} 1234567891011121314151617public class SingletonLazy2{ // 双重检查锁定 //注意在JDK1.4以及更早版本的Java中许多JVM对于 volatile 关键字的实现会导致双重检查加锁失效,此种方法只能用在JDK5及以后版本 private volatile static SingletonLazy2 UNIQUE_INSTANCE; //注意 volatile 关键字 private SingletonLazy2(){} public static SingletonLazy2 getInstance(){ if(UNIQUE_INSTANCE == null){ synchronized(SingletonLazy2.class){ if(UNIQUE_INSTANCE == null){ UNIQUE_INSTANCE = new SingletonLazy2(); } } } return UNIQUE_INSTANCE; }} 内部类 123456789public class SingletonInnerClass { //内部类 private static class SingletonHolder { private static final SingletonInnerClass INSTANCE = new SingletonInnerClass(); } private SingletonInnerClass (){} public static final SingletonInnerClass getInstance() { return SingletonHolder.INSTANCE; } } 枚举 12345public enum SingletonEnum { //枚举 INSTANCE; public void whateverMethod() { } } 1234567891011class Demo { //调用测试 public static void main(String[] args) { SingletonLazy2 s1 = SingletonLazy2.getInstance(); SingletonLazy2 s2 = SingletonLazy2.getInstance(); System.out.println( s1 == s2 ); SingletonEnum.INSTANCE.whateverMethod();//枚举 }} 参考文档链接 “双重检查锁定”会发生非预期行为声明 深入浅出单实例Singleton设计模式 单例模式的七种写法 枚举增强单例模式的可靠性]]></content>
<categories>
<category>设计模式</category>
</categories>
<tags>
<tag>设计模式</tag>
<tag>单例设计模式</tag>
<tag>Singleton</tag>
<tag>饿汉式</tag>
<tag>懒汉式</tag>
<tag>Double-checked locking</tag>
<tag>双重检查锁定</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Jekyll目录结构与变量]]></title>
<url>%2F%2F2014%2F10%2F19%2FJekyll_Variables.html</url>
<content type="text"><![CDATA[Jekyll 目录及一些说明Jekyll 标准目录树_config.yml Jekyll的配置文件 _includes include 文件所在的文件夹 _layouts 模版文件夹 _posts 自己要发布的内容 _sites 预览时产生的文件都放在该文件夹中 Jekyll的安装及配置 _includes文件夹中所放的文件是最终要放到模版中的一些代码片段。 _layouts中放的一些模版,模版是用包含page或post内容的。Jekyll的模版使用HTML语法来写,并包含YAML Front Matter。所有的模版都可用Liquid来与网站进行交互。所的的模版都可以使用全局变量site和page,site变量包含该网站所有可以接触得到的内容和元数据(meta-data),page变量包含的是当前渲染的page或post的所有可以接触得到的数据。 _post文件夹中放的是自己要发布的post文章。post文件的命名规则为YEAR-MONTH-DATE-title.MARKUP,使用rake post会自动将post文件命名合适。而对于page,所有放在根目录下或不以下划线开头的文件夹中有格式的文件都会被Jekyll处理成page。这里说的有格式是指文件含有YAML Front Matter。所有的post和page都要用markdown或者texile或者HTML语法来写,并可以包含Liquid模版的语法。还要有 YAML Front Matter (Jekyll只处理具有YAML Front Matter的文件)。YAML Front Matter必须放在文件的开头,一对—之间,用户可在这一对—间设置预先定义的变量或用户自己的数据 Jekyll模板全局变量 变量 描述 site 全站的信息+_config.yml文件中的配置选项 page 这个变量中包含YAML前置数据,另外加上两个额外的变量值:url和content。 content 在布局模板文件中,这里变量包含了页面的子视图。这个变量将会把渲染后的内容插入到模板文件中。这个变量不能在文章和页面文件中使用。 paginator 一旦paginate配置选项被设置了,这个变量才能被使用。 Jekyll模板Site变量 变量 描述 site.time 当前的时间(当你运行Jekyll时的时间) site.posts 一个按时间逆序的文章列表。 site.related_posts 如果当前被处理的页面是一个文章文件,那这个变量是一个包含了最多10篇相关文章的列表。默认来说,这些相关文章是低质量但计算快的。为了得到高质量但计算慢的结果,运行Jekyll命令时可以加上–lsi选项。(潜在语意索引) site.categories.CATEGORY 所有在CATEGORY分类中的文章列表 site.tags.TAG 所有拥有TAG标签的文章的列表 site.[CONFIGURATION_DATA] 截止0.5.2版本,所有在_config.yml中的数据都能够通过site变量调用。举例来说,如果你有一个这样的选项在你的配置文件中:url: http://higrid.net,那在文章和页面文件中可以这样调用{ { site.url } }。Jekyll并不会自动解析修改过的_config.yml文件,你想要启用新的设置选项,你需要重启Jekyll Jekyll模板Page变量 变量 描述 page.content 页面中未渲染的内容 page.title 文章的标题 page.url 除去域名以外的URL,例子:/2013/12/14/higrid-net.html page.date 指定每一篇文章的时间,这个选项能够覆盖一篇文章中前置数据设置的时间,它的格式是这样的:YYYY-MM-DD HH:MM:SS page.id 每一篇文章的唯一标示符(在RSS中非常有用) 例子:/2008/12/14/higrid-net page.categories 这篇文章隶属的分类的一个列表,分类是通过在_post目录中的目录结构推导而来的。举例来说,在路径/work/code/_posts/2008-12-24-closures.textile下的文件,这个变量将会是[work,code]。这个变量也能在YAML前置数据中被指定。 page.tags 这篇文章的标签的列表。这些数据能够在YAML前置数据中指定 page.next 按时间序的下一篇文章 page.content 按时间序的上一篇文章 注意:任何你自己指定的自定义前置数据都能够通过page调用。举例来说,如果你在页面的前置数据中设置了custom_css: true,那这个值可以在模板可以这样调用:page.custom_css Jekyll模板Paginator变量 变量 描述 paginator.per_page 每一个页面上文章的数量 paginator.posts 当前页面上可用的文章 paginator.total_posts 所有文章的数量 paginator.total_pages 所有页面的数量 paginator.page 当前页面的数量 paginator.previous_page 前面的页面的数量 paginator.next_page 接下来的的页面的数量 Post或Page的创建及发布创建Post文章 rake post title="文章标题" 会自动创建一个具有合适文件名和YAML Front Matter的文件(使用时将”文章标题”替换成你要创建的文章的标题)。 创建Page页面 rake page name="页面名称.md" 或者 rake page name="pages/页面名称.md" 发布Post或Page git add . git commit -m '一些说明' git push origin master [原文地址]]]></content>
<categories>
<category>Jekyll</category>
</categories>
<tags>
<tag>Jekyll</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Git 常用命令整理]]></title>
<url>%2F%2F2014%2F10%2F19%2Fgit_common_command.html</url>
<content type="text"><![CDATA[初始化配置12345678910111213141516171819202122#配置使用git仓库的人员姓名 git config --global user.name "Your Name Comes Here" #配置使用git仓库的人员email git config --global user.email you@yourdomain.example.com #配置到缓存 默认15分钟 git config --global credential.helper cache #修改缓存时间 git config --global credential.helper 'cache --timeout=3600' git config --global color.ui true git config --global alias.co checkout git config --global alias.ci commit git config --global alias.st status git config --global alias.br branch git config --global core.editor "mate -w" # 设置Editor使用textmate git config core.ignorecase false #设置大小写敏感git config -1 #列举所有配置 #用户的git配置文件~/.gitconfig 查看、添加、提交、删除、找回,重置修改文件12345678910111213141516171819202122232425git help <command> # 显示command的help git show # 显示某次提交的内容 git show $id git co -- <file> # 抛弃工作区修改 git co . # 抛弃工作区修改 git add <file> # 将工作文件修改提交到本地暂存区 git add . # 将所有修改过的工作文件提交暂存区 git rm <file> # 从版本库中删除文件 git rm <file> --cached # 从版本库中删除文件,但不删除文件 git reset <file> # 从暂存区恢复到工作文件 git reset -- . # 从暂存区恢复到工作文件 git reset --hard # 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改 git ci <file> git ci . git ci -a # 将git add, git rm和git ci等操作都合并在一起做 git ci -am "some comments" git ci --amend # 修改最后一次提交记录 git revert <$id> # 恢复某次提交的状态,恢复动作本身也创建了一次提交对象 git revert HEAD # 恢复最后一次提交的状态 查看文件diff123456789git diff <file> # 比较当前文件和暂存区文件差异 git diff git diff <$id1> <$id2> # 比较两次提交之间的差异 git diff <branch1>..<branch2> # 在两个分支之间比较 git diff --staged # 比较暂存区和版本库差异 git diff --cached # 比较暂存区和版本库差异 git diff --stat # 仅仅比较统计信息 #退出diff查看状态直接输入Q即可. 查看提交记录12345git log git log <file> # 查看该文件每次提交记录 git log -p <file> # 查看每次详细修改内容的diff git log -p -2 # 查看最近两次详细修改内容的diff git log --stat #查看提交统计信息 tigMac上可以使用tig代替diff和log,brew install tig 取得Git仓库1234567891011121314#初始化一个版本仓库 git init #Clone远程版本库 git clone git@xbc.me:wordpress.git #添加远程版本库origin,语法为 git remote add [shortname] [url] git remote add origin git@xbc.me:wordpress.git #查看远程仓库 git remote -v #克隆远程指定分支git clone -b <分支名> <仓库地址> 提交你的修改123456789101112131415161718192021222324252627282930313233343536373839404142434445#添加当前修改的文件到暂存区 git add . #如果你自动追踪文件,包括你已经手动删除的,状态为Deleted的文件 git add -u #提交你的修改 git commit –m "你的注释" #推送你的更新到远程服务器,语法为 git push [远程名] [本地分支]:[远程分支] git push origin master #查看文件状态 git status #跟踪新文件 git add readme.txt #从当前跟踪列表移除文件,并完全删除 git rm readme.txt #仅在暂存区删除,保留文件在当前目录,不再跟踪 git rm –cached readme.txt #重命名文件 git mv reademe.txt readme #查看提交的历史记录 git log #修改最后一次提交注释的,利用–amend参数 git commit --amend #忘记提交某些修改,下面的三条命令只会得到一个提交。 git commit –m &quot;add readme.txt&quot; git add readme_forgotten git commit –amend #假设你已经使用git add .,将修改过的文件a、b加到暂存区 #现在你只想提交a文件,不想提交b文件,应该这样 git reset HEAD b #取消对文件的修改 git checkout –- readme.txt 查看、切换、创建和删除分支123456789101112131415git br -r # 查看远程分支 git br <new_branch> # 创建新的分支 git br -v # 查看各个分支最后提交信息 git br --merged # 查看已经被合并到当前分支的分支 git br --no-merged # 查看尚未被合并到当前分支的分支 git co <branch> # 切换到某个分支 git co -b <new_branch> # 创建新的分支,并且切换过去 git co -b <new_branch> <branch> # 基于branch创建新的new_branch git co $id # 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除 git co $id -b <new_branch> # 把某次历史提交记录checkout出来,创建成一个分支 git br -d <branch> # 删除某个分支 git br -D <branch> # 强制删除某个分支 (未被合并的分支被删除的时候需要强制) 分支合并和rebase12345git merge <branch> # 将branch分支合并到当前分支 git merge origin/master --no-ff # 不要Fast-Foward合并,这样可以生成merge提交 git rebase master <branch> # 将master rebase到branch,相当于: git co <branch> && git rebase master && git co master && git merge <branch> Git补丁管理(方便在多台机器上开发同步时用)123git diff > ../sync.patch # 生成补丁 git apply ../sync.patch # 打补丁 git apply --check ../sync.patch #测试补丁能否成功 Git暂存管理1234git stash # 暂存 git stash list # 列所有stash git stash apply # 恢复暂存的内容 git stash drop # 删除暂存区 Git远程分支管理12345678910111213git pull # 抓取远程仓库所有分支更新并合并到本地 git pull --no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并 git fetch origin # 抓取远程仓库更新 git merge origin/master # 将远程主分支合并到本地当前分支 git co --track origin/branch # 跟踪某个远程分支创建相应的本地分支 git co -b <local_branch> origin/<remote_branch> # 基于远程分支创建本地分支,功能同上 git push # push所有分支 git push origin master # 将本地主分支推到远程主分支 git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库) git push origin <local_branch> # 创建远程分支, origin是远程仓库名 git push origin <local_branch>:<remote_branch> # 创建远程分支 git push origin :<remote_branch> #先删除本地分支(git br -d <branch>),然后再push删除远程分支 基本的分支管理1234567891011121314151617181920212223242526#创建一个分支 git branch iss53 #切换工作目录到iss53 git checkout iss53 #将上面的命令合在一起,创建iss53分支并切换到iss53 git checkout –b iss53 #合并iss53分支,当前工作目录为master git merge iss53 #合并完成后,没有出现冲突,删除iss53分支 git branch –d iss53 #拉去远程仓库的数据,语法为 git fetch [remote-name] git fetch #fetch 会拉去最新的远程仓库数据,但不会自动到当前目录下,要自动合并 git pull #查看远程仓库的信息 git remote show origin #建立本地的dev分支追踪远程仓库的develop分支 git checkout –b dev origin/develop Git远程仓库管理12345git remote -v # 查看远程服务器地址和仓库名称 git remote show origin # 查看远程服务器仓库状态 git remote add origin git@ github:robbin/robbin_site.git # 添加远程仓库地址 git remote set-url origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址(用于修改远程仓库地址) git remote rm <repository> # 删除远程仓库 创建远程仓库1234567891011git clone --bare robbin_site robbin_site.git # 用带版本的项目创建纯版本仓库 scp -r my_project.git git@ git.csdn.net:~ # 将纯仓库上传到服务器上 mkdir robbin_site.git && cd robbin_site.git && git --bare init # 在服务器创建纯仓库 git remote add origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址 git push -u origin master # 客户端首次提交 git push -u origin develop # 首次将本地develop分支提交到远程develop分支,并且track git remote set-head origin master # 设置远程仓库的HEAD指向master分支 git push origin -f #强制推送当前版本到网络 可结合回滚使用. 也可以命令设置跟踪远程库和本地库 12git branch --set-upstream master origin/master git branch --set-upstream develop origin/develop Git查看并修改name和email显示name的方法: 12git config user.name #查看配置中name的值git config --list #查看配置列表 或者查看~/.gitconfig文件。 改名字:12345678git config --global user.name "comtu" #修改全局名称,提交版本库中显示的名称# 或者vi ~/.gitconfig #通过vim编辑器打开.gitconfig查看,但有些时候会出现中文乱码.git config user.name "comtu" #修改当前仓库的下的配置。git config user.email "comtu@vip.qq.com" #修改当前仓库邮箱配置。 或者直接修改当前仓库的.git/config文件。 Git 忽略一些文件不加入版本控制在git中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改 .gitignore 文件的方法。这个文件每一行保存了一个匹配的规则例如: 123456# 此为注释 – 将被 Git 忽略*.a # 忽略所有 .a 结尾的文件!lib.a # 但 lib.a 除外/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODObuild/ # 忽略 build/ 目录下的所有文件doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt 另外 git 提供了一个全局的 .gitignore,你可以在你的用户目录下创建 ~/.gitignoreglobal 文件,以同样的规则来划定哪些文件是不需要版本控制的。需要执行 git config --global core.excludesfile ~/.gitignoreglobal 来使得它生效。 其他的一些过滤条件 123456?:代表任意的一个字符*:代表任意数目的字符{!ab}:必须不是此类型{ab,bb,cx}:代表ab,bb,cx中任一类型即可[abc]:代表a,b,c中任一字符即可[ ^abc]:代表必须不是a,b,c中任一字符 由于git不会加入空目录,所以下面做法会导致tmp目录不会存在 tmp/* //忽略tmp文件夹所有文件 改下方法,在tmp下也加一个.gitignore,内容为12*!.gitignore 还有一种情况,就是已经commit了,再加入gitignore是无效的,所以需要删除下缓存1git rm -r --cached ignore_file 注意: .gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。 正确的做法是在每个clone下来的仓库中手动设置不要检查特定文件的更改情况。 1git update-index --assume-unchanged PATH 在PATH处输入要忽略的文件。 另外 git 还提供了另一种 exclude 的方式来做同样的事情,不同的是 .gitignore 这个文件本身会提交到版本库中去。用来保存的是公共的需要排除的文件。而 .git/info/exclude 这里设置的则是你自己本地需要排除的文件。他不会影响到其他人。也不会提交到版本库中去。 .gitignore 还有个有意思的小功能, 一个空的 .gitignore 文件 可以当作是一个 placeholder 。当你需要为项目创建一个空的 log 目录时, 这就变的很有用。 你可以创建一个 log 目录 在里面放置一个空的 .gitignore 文件。这样当你 clone 这个 repo 的时候 git 会自动的创建好一个空的 log 目录了。 Github协同流程fork给自己 → clone到本地 → coding → push回自己 → github上提出Pull Request即可之后,本地添加fork源为远端源 → 工作前先pull下fork源保持代码较新 → coding → … Git放弃本地更改恢复到资源库版本使用git版本控制工具在本地clone一份代码后,如果发现修改错误想恢复到资源库版本,下面两行可以轻松加愉快的搞定: git clean -xdf git checkout -f GitHub更新fork的版本实践前提你已经在github上fork了别人的分支,并且弄好了跟github的ssh连接。(如果没有ssh连接可使用HTTP,操作的时候会多一个输入用户名密码操作)相关配置详情参考:https://help.github.com 详细操作: 1.检出自己在github上fork别人的分支 git clone git@github.com:comtu/android-training-course-in-chinese.git 2.然后增加远程分支(也就是你fork那个人的分支)名为atcic(这个名字任意)到你本地。 git remote add atcic git@github.com:kesenhoo/android-training-course-in-chinese.git 如果你运行命令:git remote -v你会发现多出来了一个Bob的远程分支。如下: atcic git@github.com:kesenhoo/android-training-course-in-chinese.git (fetch) atcic git@github.com:kesenhoo/android-training-course-in-chinese.git (push) origin git@github.com:comtu/android-training-course-in-chinese.git (fetch) origin git@github.com:comtu/android-training-course-in-chinese.git (push) 3.然后,把对方的代码拉到你本地。 git fetch atcic 4.最后,合并对方的代码。 git merge atcic/master 5.最最后,把最新的代码推送到你的github上。 git push origin master 这样就完成了自己的代码更新。 回滚到历史版本reset方式回滚 查看历史版本号git log 回滚到某个版本 可取前七位数 #假设有一个版本号: 9c759cc2354430d2a24a4ec7100470fe11db219agit reset --hard 9c759cc 强推送到服务器git push -f origin master reset 会影响 commit 9c759cc 之后的commit都会被退回到暂存区 建议只是自己控制的版本可以这样使用,多人协同控制的建议如下方式. revert方式回滚revert 是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容 回滚git revert HEAD #撤销前一次 commitgit revert HEAD^ #撤销前前一次 commit git revert HEAD^^^git revert commit 9c759cc #撤销指定的版本,撤销也会作为一次提交进行保存。 提交git push origin master #提交到master分支 技巧一 提交防止织毛衣,有序地合并和提交. 可以在一条分支上一起开发,你有变更的时候,在提交前,使用git stash 这样将本地的修改全部缓存在一个堆栈中了 把别人的修改同步过来 git pull --rebase 将自己的变更恢复到最新的节点上 git stash pop git commit提交,这样就会让一个分支的版本按顺序继续发展,而不是像织毛衣一样 技巧二 在分支上协同合作开发.不织毛衣.git支持很多种工作流程,我们采用的一般是这样,远程创建一个主分支,本地每人创建功能分支,日常工作流程如下: 去自己的工作分支$ git checkout work 工作.... 提交工作分支的修改$ git commit -a 回到主分支$ git checkout master 获取远程最新的修改,此时不会产生冲突$ git pull 回到工作分支$ git checkout work 用rebase合并主干的修改,如果有冲突在此时解决$ git rebase master 回到主分支$ git checkout master 合并工作分支的修改,此时不会产生冲突。$ git merge work 提交到远程主干$ git push 这样做的好处是,远程主干上的历史永远是线性的。每个人在本地分支解决冲突,不会在主干上产生冲突。也就不会出现织毛衣了. 技巧三 合并不同分支内容到当前分支cherry-pick 就是从不同的分支中捡出一个单独的commit,并把它和你当前的分支合并。使用: git cherry-pick 分支节点ID如: git cherry-pick b21fd14 如果你以并行方式在处理两个或以上分支,你可能会发现一个在全部分支中都有的bug。如果你在一个分支中解决了它,你可以使用cherry-pick命令把它commit到其它分支上去,而不会弄乱其他的文件或commit。 但应该意识到这个命令可能会产生冲突。所以用它时请务必小心。 使用中遇到的问题Git – fatal: Unable to create ‘XXX/.git/index.lock’: File exists.的解决办法 1 若在window下远程打开操作窗口(不是console),进入.git目录删除index.lock文件,删除后再commit会自动再次生成index.lock。无法提交。 2 使用putty console下操作,进入.git目录执行 rm -f index.lock 删除index.lock 虽然能删除,但是也是每次都会再生成。无法提交 3 在.git同级目录,执行rm -f .git/index.lock(或者rm -f git/index.lock) 删除后可提交。成功! Git 删除远程仓库文件或文件夹使用 git rm 命令即可,有两种选择. 一种是 git rm –cached “文件路径”,不删除物理文件,仅将该文件从缓存中删除; 一种是 git rm –f “文件路径”,不仅将该文件从缓存中删除,还会将物理文件删除(不会回收到垃圾桶) 假如你有文件不小心commit到了服务器那么你想要删除它,可以使用: 12345678910111213#删除目录# 说明 -r 递归删除 -n只是查看会被删除的列表不会真实操作 git rm -r -n --cached *node_modules/\* #删除远程仓库node_modules目录下的所有文件git rm -r --cached *node_modules/\* # 说明 最终执行命令#删除文件git rm --cached "路径+文件名" #接下来git commit -m "delete file" #最后git push 若用git status命令查看,则node_modules/目录下文件出现在结果列表里, 我们不希望这个目录下的文件出现,则在项目根目录下,和.git 同级目录下,新建一个.gitignore文件, 把.gitignore提交到远程服务器。 则node_modules目录就不会被提交了。 IDEA中分支切换error: The following untracked working tree files would be overwritten by checkout在IDEA中进行分支切换时,出现如此错误,导致无法正常切换: error: The following untracked working tree files would be overwritten by checkout 通过错误提示可知,是由于一些untracked working tree files引起的问题。所以只要解决了这些untracked的文件就能解决这个问题。 解决方式:git进入本地版本仓库目录下,直接执行git clean -d -fx即可。可能很多人都不明白-d,-fx到底是啥意思,其实git clean -d -fx表示:删除一些没有 git add 的文件; 12345678git clean 参数 -n 显示将要删除的文件和目录;-x -----删除忽略文件已经对git来说不识别的文件-d -----删除未被添加到git的路径中的文件-f -----强制运行git clean -ngit clean -dfgit clean -f git clone 时显示Filename too long的解决办法在git bash中,运行下列命令: git config --global core.longpaths true 就可以解决该问题。 –global是该参数的使用范围,如果只想对本版本库设置该参数,只要在上述命令中去掉–global即可。 切换警告 warning: LF will be replaced by CRLF inwindows中的换行符为 CRLF, 而在linux下的换行符为LF,所以在执行add . 时出现提示,解决办法: $ git config --global core.autocrlf false //禁用自动转换 Git版本控制大全: http://git-scm.com/book/zh/v1]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
<tag>版本控制</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hello world]]></title>
<url>%2F%2F2014%2F10%2F18%2Fhello%20world.html</url>
<content type="text"><![CDATA[H1H2H3H4H5H6默认字号字体 加粗 斜体斜体 加粗斜体 内容颜色背景 内容加边 再加边再再加边再再再加边注意事项"内容后面有两个空格" 缩进 我是链接,下面的是图片链接 1.H1内容 1.1H2内容 1.2H3内容 1.2.1H4内容 1.2.2H5内容 1.2.2.1H6内容 01.2.2.2默认什么字号内容 1.2.3加粗内容 1.2.4斜体内容 2.加粗斜体内容 3.内容颜色背景 4.我是链接 内容1 内容2 内容(注意这里的Markdown源代码) 内容x 下面是表格 head1 head2 head3 head4 row1text1 row1text2 row1text3 row1text4 row2text1 row2text2 row2text3 row2text4 row3text1 row3text2 row3text3 row3text4 使用 pygments 高亮 {% highlight c %} /* hello world demo 禁止解析*/ #include int main(int argc, char **argv) { printf("Hello, World!\n"); return 0; } {% endhighlight %} 1234567/* hello world demo 禁止解析*/#include <stdio.h>int main(int argc, char **argv){ printf("Hello, World!\n"); return 0;} 用 SyntaxHihglighter 高亮 function helloSyntaxHighlighter() { return "hi!"; } function helloSyntaxHighlighter() { return "hi!"; } /*http://comtu.githut.io*/ class SingletonTest { private static class SingletonHolder { private static final SingletonTest INSTANCE = new SingletonTest(); } private SingletonTest() { } public static final SingletonTest getInstance() { return SingletonHolder.INSTANCE; } } /** * 枚举_单例 */ enum SingletonEnum { INSTANCE; public void whateverMethod() { } } 使用 script 方式高亮 使用 pre 方式高亮 . 不支持 < 符号,需要进行转义为 &lt ; (但能很好的支持RSS订阅) <html> <body> <div style="font-weight: bold"><?= str_replace("\n", "<br/>", $var) ?></div> <? /*********************************** ** Multiline block comments **********************************/ $stringWithUrl = "http://alexgorbatchev.com"; $stringWithUrl = 'http://alexgorbatchev.com'; ob_start("parseOutputBuffer"); // Start Code Buffering session_start(); ?> </body> </html> SyntaxHihglighter使用方法 SyntaxHihglighter参数 视频 Markdown 免费编辑器 Windows 平台 MarkdownPad MarkPad Linux 平台 ReText Mac 平台 Mou 在线编辑器 Markable.in Dillinger.io 浏览器插件 MaDe (Chrome) 高级应用 Sublime Text 2 + MarkdownEditing / 教程 最新评论 最近访客 注: 更换主题后~~不再支持使用 SyntaxHihglighter 高亮]]></content>
<categories>
<category>Jekyll</category>
</categories>
<tags>
<tag>Jekyll</tag>
<tag>Demo</tag>
<tag>Test</tag>
<tag>SyntaxHihglighter</tag>
<tag>pygments</tag>
<tag>Markdown</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Jekyll中的语法高亮:Pygments]]></title>
<url>%2F%2F2014%2F10%2F18%2Fsupport-pygments-in-Jekyll.html</url>
<content type="text"><页面中的 Short names。 {% highlight c %} /* hello world demo */ #include int main(int argc, char **argv) { printf("Hello, World!\n"); return 0; } {% endhighlight %} 也可以采用这样的写法 1234567/* hello world demo */#include <stdio.h>int main(int argc, char **argv){ printf("Hello, World!\n"); return 0;} 三个"`"符号需要markdownx解释引擎, markdown: redcarpet redcarpet: extensions: ["fenced_code_blocks", "tables", "highlight", "strikethrough"] 生成的 html 高亮结果 以下结束是使用Pygments 样式的 default 与 vs 的并集样式 C1234567/* hello world demo */#include <stdio.h>int main(int argc, char **argv){ printf("Hello, World!\n"); return 0;} Java123456789101112131415package com.pexcn.activity.demo;import android.app.Activity;import android.os.Bundle;import android.util.Log;public class MainActivity extends Activity { private static final String TAG = "ActivityDemo"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.e(TAG, "onCreate() start..."); }} 四. Pygments 样式Pygments 样式 默认提供了 monokai、manni、rrt、perldoc、borland、colorful、default 等等,多种的样式。 可以通过 Python Shell 中用以下命令列出当前 Pygments 支持的样式: 12345>>> from pygments.styles import STYLE_MAP>>> STYLE_MAP.keys()['manni', 'igor', 'xcode', 'vim', 'autumn', 'vs', 'rrt', 'native', 'perldoc', 'borland', 'tango', 'emacs', 'friendly', 'monokai', 'paraiso-dark', 'colorful', 'murphy', 'bw', 'pastie', 'paraiso-light', 'trac', 'default', 'fruity']>>> ` 通过 -S 来选择,需要生成 monokai 的样式: $ pygmentize -S monokai -f html > your/path/pygments.css 下面是 pygments 个各样式 show: monokai autumn borland bw colorful default emacs friendly fruity manni monokai murphy native pastie perldoc rrt tango trac vim vs 参考:http://pygments.org/docs/cmdline/]]></content>
<categories>
<category>Jekyll</category>
</categories>
<tags>
<tag>Jekyll</tag>
<tag>Pygments</tag>
<tag>语法高亮</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Windows平台配置Jekyll环境并与GitHub连接]]></title>
<url>%2F%2F2014%2F10%2F18%2Fwindows_configuration_Jekyll.html</url>
<content type="text"><![CDATA[1.安装所需要的软件 文本编辑器(不要使用Windows自带的编辑器) 使用如:notepad++ http://www.notepad-plus-plus.org/ Railsinstaller http://railsinstaller.org/en Python https://www.python.org/downloads/ 安装并为python配置环境变量,由于有部分的组件还会依赖到python, 所以这里建议也安装上python(例如:语法着色器pygments是python组件) 2.安装完后生成SSH并导入到Github网站 生成SSH Railsinstaller安装(默认安装)完后软件提示输入name 其实是Github的用户名 , 输入完后提示输入邮箱 github注册使用的邮箱 以上操作完后会在 C:\Users\用户名\.ssh文件夹\下生成密钥与公钥 id_rsa(密钥) 与 id_rsa.pub(公钥) 导入SSH公钥到GigHub网站 (作用是使用git提交时可以使用 git@github.com:comtu/comtu.github.io.git 的地址,直接 push 数据到github网站,无需要使用Https链接每次push数据输入用户名密码) 公钥使用文本方式打开复制里面的所有内容(ctrl+A ->ctrl+C 包括空格和新行)–> 登录Gighub网站 –> settings(设置) –> SSH keys –> Add SSH key –> 输入Title(可随便填写,但建议编写有意义的名字) –> 粘贴内容到Key中 –> Add key 3.测试Git连接是否正常运行Git Bash (开始菜单--> RailsInstaller --> Git Bash) 安装Railsinstaller后会默认在C盘根目录创建一个Sites目录 , 运行的GitBash也会自动定位到Sites目录 测试Git连接是否正常输入:ssh -T git@github.com 运行会提示: The authent icity of host ‘github.com(204.232.175.90)’ can’t be established . RSA key fingerprint is …… Ary you sure you want to continue connecting (yes/no)? 输入 yes 之后会显示 Warning: Permanently added ‘github.com.204.232.175.90’ (RSA) to the list of known hosts . Permission denied (publickey). 4.修改配置避免jekyll在windows下字符集错误4.1.修改bash的字符集Win7系统中(C:\Users\用户名),Xp系统中(C:\Documents and Settings\用户名)下, 找到.bash_profile文件,在其内容里增加如下内容 set LC_ALL=en_US.UTF-8 set LANG=en_US.UTF-8 注意: 在Windows系统下没有办法创建.xxx的文件名,所以可以借用git bash命令行来创建: 1. 输入 echo 'set LC_ALL=en_US.UTF-8' > .bash_profile 命令创建一个内容为''内容的文件 comtu@CN-CS-PC73 /C/Sites $ echo 'set LC_ALL=en_US.UTF-8' > .bash_profile 2.文件管理器打开文件目录C:\Sites 将文件.bash_profile使用文本编辑器的方式打开在内容里面 增加成如下内容保存 set LC_ALL=en_US.UTF-8 set LANG=en_US.UTF-8 还有一个行空行 3.将.bash_profile文件拷贝到Win7系统中(C:\Users\用户名), Xp系统中(C:\Documents and Settings\用户名)下. 4.2.所有文档使用UTF-8无BOM格式在windows下新建的文本文件默认为ANSI格式,而Jekyll只认UTF-8,可以使用第三方文本编辑器进行格式转换如, notepad++中转换 格式-->转为UTF-8无BOM格式 EditPlus中转换 文件-->另存为-->编码(UTF-8)-->选择目录-->保存 4.3.使用Unix换行符在notepad++中,可以开启“显示所有字符”选项,这样就可以看出文档用的是Windows的换行符还是Unix的换行符。 在这种模式下,Windows的换行符显示的是CR LF,Unix的换行符显示的是LF , MAC 的换行符显示的是CR notepad++中转换 编辑-->档案格式转换-->转换为Unix格式 使用是使用Notepad++文档编辑器则可以在创建的时候就默认为Unix格式 设置-->首选项-->新建-->格式-->Unix 编码-->UTF-8(无BOM) 4.4.注意YAML头部的格式模板文件的元数据以YAML的格式展现,YAML头部经常会出现三个问题: 1.三短线前面不能有空格; 2.“名: 值”对里冒号后面要有空格; 3.回车后不要有Tab符; 4.示数组成员开始的-号后面要有空格 5.安装jekyll和相关的包在国内需要配置gem数据源地址,翻墙或者在国外则无需设置 输入以下两条命令: 命令: gem sources --remove http://rubygems.org/ 命令: gem sources -a http://ruby.taobao.org/ 然后用 命令: gem sources -l 看看现在源列表 *** CURRENT SOURCES *** http://ruby.taobao.org 如果显示如上信息则可以进行安装Jekyll了 命令: gem install jekyll Jekyll需要用到directory_watcher、liquid、open4、maruku和classifier这几个包,用上面的命令可以自动安装。 Jekyll默认用maruku来解析markdown语言,你也可以用别的程序来解析,比如rdiscount或kramdown,都给装上吧: 命令: gem install rdiscount kramdown 以上命令涉及到gem install的时候,如果你用的是linux系统,就要用sudo gem install代替。 参考资料: Github Pages极简教程【译文】用Jekyll构建静态网站 原文Building Static Sites with Jekyll为 Jekyll 添加多说评论系统在 Windows 上安装 JekyllJekyll 中的语法高亮:PygmentsJekyll 扩展的 Liquid 设计 原文Liquid for Designers]]></content>
<categories>
<category>Jekyll</category>
</categories>
<tags>
<tag>Jekyll</tag>
<tag>GitHub</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Github配置SSH key]]></title>
<url>%2F%2F2014%2F10%2F18%2Fgithub_ssh_key.html</url>
<content type="text"><![CDATA[Github配置SSH key作用是使用git提交时可以使用git@github.com:comtu/comtu.github.io.git 的地址,直接 push 数据到github网站,无需要使用Https链接每次push数据输入用户名密码 步骤一:使用Git Bash生成新的ssh key。 $ cd ~ #保证当前路径在”~”下 $ ssh-keygen -t rsa -C "xxxxxx@yy.com" #建议填写自己真实有效的邮箱地址 Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/xxxx_000/.ssh/id_rsa): #不填直接回车 Enter passphrase (empty for no passphrase): #输入密码(可以为空) Enter same passphrase again: #再次确认密码(可以为空) Your identification has been saved in /c/Users/xxxx_000/.ssh/id_rsa. #生成的密钥 Your public key has been saved in /c/Users/xxxx_000/.ssh/id_rsa.pub. #生成的公钥 The key fingerprint is: e3:51:33:xx:xx:xx:xx:xxx:61:28:83:e2:81 xxxxxx@yy.com *本机已完成ssh key设置,其存放路径为:c:/Users/xxxx_000/.ssh/下。 注释:可生成ssh key自定义名称的密钥,默认id_rsa。 $ ssh-keygen -t rsa -C "邮箱地址" -f ~/.ssh/githug_blog_keys #生成ssh key的名称为githug_blog_keys,慎用容易出现其它异常。 步骤二:添加ssh key到GItHub 公钥(id_rsa.pub)使用文本方式打开复制里面的所有内容(ctrl+A ->ctrl+C 包括空格和新行) –> 登录Gighub网站 –> settings(设置) –> SSH keys –> Add SSH key–> 输入Title(可随便填写,但建议编写有意义的名字) –> 粘贴内容到Key中 –> Add key 步骤三:配置账户 $ git config --global user.name “your_username” #设置用户名 $ git config --global user.email “your_registered_github_Email” #设置邮箱地址 $ git config --global core.longpaths true # git clone 时显示Filename too long的解决办法 测试ssh keys是否设置成功。 $ ssh -T git@github.com The authenticity of host 'github.com (192.30.253.113)' can't be established. RSA key fingerprint is 16:27:xx:xx:xx:xx:xx:4d:eb:df:a6:48. Are you sure you want to continue connecting (yes/no)? yes #确认你是否继续联系,输入yes Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts. Enter passphrase for key '/c/Users/xxxx_000/.ssh/id_rsa': #生成ssh kye是密码为空则无此项,若设置有密码则有此项且,输入生成ssh key时设置的密码即可。 Hi xxx! You've successfully authenticated, but GitHub does not provide shell access. #出现词句话,说明设置成功。]]></content>
<categories>
<category>Github</category>
</categories>
<tags>
<tag>Github</tag>
</tags>
</entry>
</search>