LeoYan Blog

技术分享,生活记录。

0%

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

Application:属性

一个AndroidManifest.xml中必须含有一个Application标签,这个标签声明了每一个应用程序的组件及其属性(如icon,label,permission等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<application
android:allowClearUserData = ["true" | "false"]
android:allowTaskReparenting = ["true" | "false"]
android:backupAgent = "string"
android:debuggable = ["true" | "false"]
android:description = "string resource"
android:enabled = ["true" | "false"]
android:hasCode = ["true" | "false"]
android:icon = "drawable resource"
android:killAfterRestore = ["true" | "false"]
android:label = "string resource"
android:manageSpaceActivity = "string"
android:name = "string"
android:permission = "string"
android:persistent = ["true" | "false"]
android:process = "string"
android:restoreAnyVersion = ["true" | "false"]
android:taskAffinity = "string"
android:theme = "resource or theme" >
</application>

A、allowClearUserData

android:allowClearUserData (‘true’ or ‘false’)

用户是否能选择自行清除数据,默认为true,程序管理器包含一个选择允许用户清除数据。当为true时,用户可自己清理用户数据,反之亦然

B、allowTaskReparenting

android:allowTaskReparenting (‘true’ or ‘false’)

是否允许activity更换从属的任务,比如从短信息任务切换到浏览器任务

C、backupAgent

android:backupAgent (string)

这也是Android2.2中的一个新特性,设置该APP的备份,属性值应该是一个完整的类名,如com.project.TestCase,此属性并没有默认值,并且类名必须得指定(就是个备份工具,将数据备份到云端的操作)

D、debuggable

android:debuggable (‘true’ or ‘false’)

这个从字面上就可以看出是什么作用的,当设置为true时,表明该APP在手机上可以被调试。默认为false,在false的情况下调试该APP,就会报以下错误:

Device XXX requires that applications explicitely declare themselves as debuggable in their manifest.

Application XXX does not have the attribute ‘debuggable’ set to TRUE in its manifest and cannot be debugged.

E、label / description

android:label (string)

android:description (string)

此两个属性都是为许可提供的,均为字符串资源,当用户去看许可列表(android:label)或者某个许可的详细信息(android:description)时,这些字符串资源就可以显示给用户。label应当尽量简短,之需要告知用户该许可是在保护什么功能就行。而description可以用于具体描述获取该许可的程序可以做哪些事情,实际上让用户可以知道如果他们同意程序获取该权限的话,该程序可以做什么。我们通常用两句话来描述许可,第一句描述该许可,第二句警告用户如果批准该权限会可能有什么不好的事情发生

F、enabled

android:enabled (‘true’ or ‘false’)

Android系统是否能够实例化该应用程序的组件,如果为true,每个组件的enabled属性决定那个组件是否可以被 enabled。如果为false,它覆盖组件指定的值;所有组件都是disabled。

G、hasCode

android:hasCode (‘true’ or ‘false’)

表示此APP是否包含任何的代码,默认为true,若为false,则系统在运行组件时,不会去尝试加载任何的APP代码

一个应用程序自身不会含有任何的代码,除非内置组件类,比如Activity类,此类使用了AliasActivity类,当然这是个罕见的现象

(在Android2.3可以用标准C来开发应用程序,可在androidManifest.xml中将此属性设置为false,因为这个APP本身已经不含有任何的JAVA代码了)

H、icon

android:icon (drawable resource)

这个很简单,就是声明整个APP的图标,图片一般都放在drawable文件夹下

I、killAfterRestore

android:killAfterRestore (‘true’ or ‘false’)

J、manageSpaceActivity

android:manageSpaceActivity (string)

K、name

android:name (string)

为应用程序所实现的Application子类的全名。当应用程序进程开始时,该类在所有应用程序组件之前被实例化。

若该类(比方androidMain类)是在声明的package下,则可以直接声明android:name=”androidMain”,但此类是在package下面的子包的话,就必须声明为全路径或android:name=”package名称.子包名成.androidMain”

L、permission

android:permission (string)

设置许可名,这个属性若在上定义的话,是一个给应用程序的所有组件设置许可的便捷方式,当然它是被各组件设置的许可名所覆盖的

M、presistent

android:presistent (‘true’ or ‘false’)

该应用程序是否应该在任何时候都保持运行状态,默认为false。因为应用程序通常不应该设置本标识,持续模式仅仅应该设置给某些系统应用程序才是有意义的。

N、process

android:process (string)

应用程序运行的进程名,它的默认值为元素里设置的包名,当然每个组件都可以通过设置该属性来覆盖默认值。如果你想两个应用程序共用一个进程的话,你可以设置他们的android:process相同,但前提条件是他们共享一个用户ID及被赋予了相同证书的时候

O、restoreAnyVersion

android:restoreAnyVersion (‘true’ or ‘false’)

同样也是android2.2的一个新特性,用来表明应用是否准备尝试恢复所有的备份,甚至该备份是比当前设备上更要新的版本,默认是false

P、taskAffinity

android:taskAffinity (string)

拥有相同的affinity的Activity理论上属于相同的Task,应用程序默认的affinity的名字是元素中设定的package名

Q、theme

android:theme (resource or theme)

是一个资源的风格,它定义了一个默认的主题风格给所有的activity,当然也可以在自己的theme里面去设置它,有点类似style。

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

LSBD0

背景

近期全球范围内爆发基于Windows网络共享协议进行攻击传播的蠕虫恶意代码,目前已有国内外多个高校校内网、大型企业内网和政府机构专网被感染,系统一旦被感染,Office、PDF等重要文件将被加密,需要向黑客支付高额赎金才能解密恢复文件,对重要数据造成严重损失。此次勒索病毒可远程攻击Windows的文件共享端口,如果系统未及时更新补丁或关闭端口,无需用户任何操作,只要开机联网,攻击者就能在电脑里执行任意代码,植入勒索病毒等恶意程序。

下午,国家网络与信息安全信息通报中心发出就勒索病毒发布紧急通报: 监测发现,在全球范围内爆发的WannaCry勒索病毒出现了变种:WannaCry2.0,与之前版本的不同是,这个变种取消了Kill Switch,不能通过注册某个域名来关闭变种勒索病毒的传播,该变种传播速度可能会更快。请广大网民尽快升级安装Windows操作系统相关补丁,已感染病毒机器请立即断网,避免进一步传播感染。

几乎与此同时,北京市委网信办、北京市公安局、北京市经信委联合发出《关于WannaCry勒索蠕虫出现变种及处置工作建议的通知》。《通知》同样指出,WannaCry 勒索蠕虫已经出现新变种,并给出了具体处理建议。

几个小时前,英国BBC发文称:安全专家警告,下一波网络攻击即将来临,很可能在星期一。

附:《通知》全文

LSBD5


什么是WannaCry?

就是一些混蛋编写的利用Windows中的SMB漏洞的病毒代码,也被称为Wana Decrypt0r,WannaCryptor或WCRY,和其它勒索病毒变体一样,阻止你访问计算机或文件,并要求支付赎金解锁文件。

如何防护?

第一步:

什么都不用说,先断网,再备份!

拔掉网线,关掉WiFi,然后开机备份文档。

注意注意!不要备份在本机和网络硬盘上。

第二步:

开机。

第三步:

紧急备份:您看到预警通知后,建议您即刻断开办公终端的网络连接,通过移动存储设备(U盘、移动硬盘等)对重要办公文件进行备份,这样即使计算机遭遇了勒索软件攻击,您也能够恢复所有文件。

第四步:

控制面板 –> 防火墙高级设置 –> 在入站规则(新建规则) –> 端口 –> TCP打勾 –> 特定输入(445) –> 阻止连接 –> 名字輸入(阻挡勒索病毒连接名)

具体如下:

  1. 打开控制面板-系统和安全-Windows 防火墙

  2. 点击高级设置(打开高级安全Windows防火墙)

  3. 在入站规则(也就是外网访问你的电脑的规则)图标上右键-新建规则

LSBD1

  1. 点击端口,然后点击下一步

LSBD2

  1. 点击TCP, 然后在“特定本地端口”里输入445

LSBD3

  1. 点击下一步

  2. 点击阻止连接

LSBD4

  1. 点击下一步

  2. 全选上,然后下一步。

  3. 随便起名字,然后完成。

到这里,你完成了阻止所有通过445端口连接你的电脑的连接。

不放心的可以再把UTP也阻止了(方法一样)。

445端口关闭后,本机cmd窗口执行命令netstat -ano | findstr :445,回车后无任何返回。

这样确保了你上网时不被病毒攻击

第五步:

连接网络,打补丁

补丁升级地址:

更新最新的5月份KB4012264补丁,如果你的更新记录如下,则表示已更新

Windows 7 : KB4012215 或 KB4015549 或 KB4019264

Windows 8.1:KB4012216 或 KB4015550 或 KB4019215

Windows 10

直接去Windows更新便可

Windows 8.1 64:

http://download.windowsupdate.com/c/msdownload/update/software/secu/2017/05/windows8.1-kb4019215-x64_d06fa047afc97c445c69181599e3a66568964b23.msu

Windows 8.1 32:

http://download.windowsupdate.com/c/msdownload/update/software/secu/2017/05/windows8.1-kb4019215-x86_fe1cafb988ae5db6046d6e389345faf7bac587d7.msu

Windows 7 64:

http://download.windowsupdate.com/c/msdownload/update/software/secu/2017/05/windows6.1-kb4019264-x64_c2d1cef74d6cb2278e3b2234c124b207d0d0540f.msu

Windows 7 32:

http://download.windowsupdate.com/c/msdownload/update/software/secu/2017/05/windows6.1-kb4019264-x86_aaf785b1697982cfdbe4a39c1aabd727d510c6a7.msu

针对老版本Windows,微软已推出KB4012598 :

Windows Vista 32/64

Windows Server 2008 32/64

Windows 8 32/64

Windows XP SP3

Windows Server 2003

http://www.catalog.update.microsoft.com/Search.aspx?q=KB4012598

结语

该病毒具有传染性,传播快,所以请大家务必对自己的电脑升级防护,避免二次传播。

最后,如果你被感染,千万不要支付赎金,这助长这些混蛋的嚣张气焰,没准还会要你支付更多的赎金。

如果发现被感染,赶快关机,交给专业人士处理。

千万不要打开可疑的电子邮件,永远不要打开可疑的附件!

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

背景

当 APK 安装的时候,userId 这个标志就会产生。APK 在设备上的整个生命周期中,这个 ID 不再改变。同一个应用包在不同设备上可能有不同的 userId,重要的是在给定的设备上,每个应用包有自己独立的 userId。

userId的特点

  1. 作为 APK 身份的标识

  2. userId 对应一个 Linux 用户,所以不同 APK (用户)间互相访问数据默认是禁止的。

那么不同 APK 之间想要互相访问数据应该怎么办呢?

数据共享

Android 为我们提供了两种数据互访的方法:

  1. 使用 Share Preference / Content Provider

APK通过指定接口和数据供其它APK读取,开发者需要实现接口和指定 share 的数据。

  1. 在配置文件 manifest 中配置相同的 UserId

通过共享 userId,拥有相同 userId 的用户可以配置成运行在同一进程当中,因此默认就是可以互相访问任意数据的。

也可以配置为不同进程当中,彼此之间就像访问自己的数据一样访问彼此的数据库和文件。

例子

应用1:com.leoyanblog.sqlite

1
2
3
4
5
6
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.leoyanblog.sqlite"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="leoyanblog.com"
>

应用2:com.leoyanblog.activity

1
2
3
4
5
6
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.leoyanblog.activity"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="leoyanblog.com"
>

这两个应用的 userId 相同,都为”leoyanblog.com”,因此两个应用共享 userId,如果应用1想访问应用2的数据怎么办呢?

1
Content content = this.createPackageContent(“com.leoyanblog.activity”, Content.CONTENT_IGNORE_SECURITY);

这样通过 content 可以获取到应用2中的资源,包括:数据库,preference,资源文件等。

注意:
基于安全考虑,两个 package 需要有相同的签名。否则如果没有验证,应用程序一旦设置了 sharedUserId,当程序被破解,其它应用也可以访问我们的数据。数据不安全设置共享也就没有多大意义了。

总结

如果想要不同的 APK 之间,通过 sharedUserId 共享数据需要满足以下条件:

  1. APK 的签名必须相同

  2. android:shareUserId 的值必须相同

  3. 如果想要运行在同一个进行当中,必须设置 android:process 的值相同。

application 设置应用程序的全部组件都运行在某个线程中

1
2
3
4
5
6
<application 
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:process="leoyan.com">

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

Manifest:属性

1
2
3
4
5
6
7
8
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leoyanblog.app"
android:sharedUserId="string"
android:sharedUserLabel="string resource"
android:versionCode="integer"
android:versionName="string"
android:installLocation=["auto" | "internalOnly" | "preferExternal"]>
</manifest>

1、xmlns:android

定义android命名空间,一般为http://schemas.android.com/apk/res/android,这样使得Android中各种标准属性能在文件中使用,提供了大部分元素中的数据。

2、package

指定本应用内Java主程序包的包名,它也是一个应用进程的默认名称

3、sharedUserId

当APK安装的时候,userId这个标志就会产生。APK在设备上的整个生命周期中,这个ID不再改变。同一个应用包在不同设备上可能有不同的userId,重要的是在给定的设备上,每个应用包有自己独立的userId。

userId的特点:

1.) 作为APK身份的标识

2.) userId对应一个Linux用户,所以不同APK(用户)间互相访问数据默认是禁止的。

若要共享数据,第一可以采用Share Preference / Content Provider,第二种就可以采用sharedUserId了,将不同APK的sharedUserId都设为一样,则这些APK之间就可以互相共享数据了。详见:http://www.leoyanblog.com/post/2017/05/07.html

4、sharedUserLabel

这个属性给共享的 UserId 定义了一个用户可读的标签。这个标签必须用字符串资源来设置,不能使用原生的字符串。

这个属性在API Level 3中被引入,只有在sharedUserId属性被设置,这个属性才有意义。

5、versionCode

是给设备程序识别版本(升级)用的,必须是一个interger值,代表app更新过多少次,比如第一版一般为1,之后若要更新版本就设置为2,3等等。。。

6、versionName

这个名称是给用户看的,你可以将你的APP版本号设置为1.1版,后续更新版本设置为1.2、2.0版本等等。。。

7、installLocation

安装参数,是Android2.2中的一个新特性,installLocation有三个值可以选择:internalOnly、auto、preferExternal

选择preferExternal,系统会优先考虑将APK安装到SD卡上(当然最终用户可以选择为内部ROM存储上,如果SD存储已满,也会安装到内部存储上)

选择auto,系统将会根据存储空间自己去适应

选择internalOnly,是指必须安装到内部才能运行

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

一、前言

AndroidManifest.xml 是每个Android程序中必须的文件,它位于整个项目的根目录。我们每天都在使用这个文件,往里面配置程序运行所必要的组件,权限,以及一些相关信息。但是对于这个文件,我们真正又了解多少了,还是只是停留在只会简单的配置,而不明白其中的具体含义,以及为什么要这样设置?今天就让我们来详细的学习一下这个文件里各项参数的具体含义,因为它是整个应用的入口,所以有助于我们更加深入的理解Android。

二、概述

  AndroidManifest.xml是Android应用的入口文件,它描述了package中暴露的组件(activities, services, 等等),他们各自的实现类,各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services和Intent Receivers,还能指定permissions和instrumentation(安全控制和测试)。   

三、结构

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
<?xmlversion="1.0"encoding="utf-8"?>
<manifest>

<uses-sdk/>
<uses-configuration/>
<uses-feature/>

<uses-permission/>
<permission/>
<permission-tree/>
<permission-group/>
<instrumentation/>

<supports-screens/>

<application>
<activity>
<intent-filter>
<action/>
<category/>
</intent-filter>
</activity>
<activity-alias>
<intent-filter></intent-filter>
<meta-data/>
</activity-alias>
<service>
<intent-filter></intent-filter>
<meta-data/>
</service>
<receiver>
<intent-filter></intent-filter>
<meta-data/>
</receiver>
<provider>
<grant-uri-permission/>
<meta-data/>
</provider>
<uses-library/>
</application>
</manifest>

三、详解

1、Manifest

详见: http://www.leoyanblog.com/post/2017/04/30.html

2、Application

待补充

3、Activity

待补充

4、intent-filter

待补充

5、meta-data

待补充

6、activity-alias

待补充

7、Service

待补充

8、Receiver

待补充

9、Provider:属性

待补充

10、uses-library

用户库,可自定义。所有android的包都可以引用

11、supports-screens

1
2
3
4
5
<supports-screens  
android:smallScreens = ["true" | "false"]
android:normalScreens = ["true" | "false"]
android:largeScreens = ["true" | "false"]
android:anyDensity = ["true" | "false"] />

这是在android1.6以后的新特性,支持多屏幕机制

各属性含义:这四个属性,是否支持小屏,是否支持中屏,是否支持大屏,是否支持多种不同密度

12、uses-configuration 与uses-feature性能都差不多

1
2
3
4
5
6
7
8
9
10
11
<uses-configuration 
android:reqFiveWayNav = ["true" | "false"]
android:reqHardKeyboard = ["true" | "false"]
android:reqKeyboardType = ["undefined" | "nokeys" | "qwerty" | "twelvekey"]
android:reqNavigation = ["undefined" | "nonav" | "dpad" | "trackball" | "wheel"]
android:reqTouchScreen = ["undefined" | "notouch" | "stylus" | "finger"] />

<uses-feature
android:glEsVersion = "integer"
android:name = "string"
android:required = ["true" | "false"] />

这两者都是在描述应用所需要的硬件和软件特性,以便防止应用在没有这些特性的设备上安装。

13、uses-sdk

1
2
3
4
<uses-sdk
android:minSdkVersion = "integer"
android:targetSdkVersion = "integer"
android:maxSdkVersion = "integer"/>

描述应用所需的api level,就是版本,目前是android 2.2 = 8,android2.1 = 7,android1.6 = 4,android1.5=3

在此属性中可以指定支持的最小版本,目标版本以及最大版本

14、instrumentation

1
2
3
4
5
6
7
<instrumentation
android:functionalTest = ["true" | "false"]
android:handleProfiling = ["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:targetPackage="string"/>

定义一些用于探测和分析应用性能等等相关的类,可以监控程序。在各个应用程序的组件之前instrumentation类被实例化

android:functionalTest(解释:instrumentation类是否能运行一个功能测试,默认为false)

15、permission、uses-permission、permission-tree 、permission-group 区别

待补充

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。


最开始接触Android开发,多次遇到SDK版本与API LEVEL的对应关系问题,就进行了一下总结,以备后续查看。

API是开发用的,所以API LEVEL可以认为是内部的;而SDK的版本提供了新特性给用户,是外部可见的。
可以查看以下网址以获取最新的对应关系:

https://developer.android.google.cn/index.html

Vsersion Name Version Code API Level
Nougat 牛轧糖 Android 7.1.x 25
Nougat 牛轧糖 Android 7.0 24
Marshmallow 棉花糖 Android 6.0 23
Lollipop 棒棒糖 Android 5.1 22
Lollipop 棒棒糖 Android 5.0 - 5.0.2 21
KitKat with wearable extensions 奇巧巧克力棒 Android 4.4W 20
KITKAT 奇巧巧克力棒 Android 4.4 19
JELLY_BEAN_MR2 糖豆 Android 4.3 18
JELLY_BEAN_MR1 糖豆 Android 4.2, 4.2.2 17
JELLY_BEAN 糖豆 Android 4.1, 4.1.1 16
ICE_CREAM_SANDWICH_MR1 冰激凌三明治 Android 4.0.3 - 4.0.4 15
ICE_CREAM_SANDWICH 冰激凌三明治 Android 4.0 - 4.0.2 14
HONEYCOMB_MR2 蜂巢 Android 3.2 13
HONEYCOMB_MR1 蜂巢 Android 3.1.x 12
HONEYCOMB 蜂巢 Android 3.0.x 11
GINGERBREAD_MR1 姜饼 Android 2.3.3 - 2.3.7 10
GINGERBREAD 姜饼 Android 2.3 - 2.3.2 9
FROYO 冻酸奶 Android 2.2.x 8
ECLAIR_MR1 松饼 Android 2.1.x 7
ECLAIR_0_1 松饼 Android 2.0.1 6
ECLAIR 松饼 Android 2.0 5
Donut 甜甜圈 Android 1.6 4
Cupcake 纸杯蛋糕 Android 1.5 3
Petit Four 花式小蛋糕 Android 1.1 2
BASE Android 1.0 1

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

标识符命名法主要有四种:

  1. Camel命名法:又称小驼峰命名法。除首单词外,其余所有单词的第一个字母大写,如:fooBar;
  2. Pascal命名法:又称大驼峰命名法。所有单词的第一个字母大写,如:FooBar;
  3. 下划线命名法:单词与单词间用下划线做间隔,如:foo_bar;
  4. 匈牙利命名法:开头字母用变量类型的缩写,其余部分用变量的英文或英文的缩写,要求单词第一个字母大写。如:int iMyAge; “i”是int类型的缩写;

安卓App层开发主要是Java语言,所以基本使用除了第四种外的命名方式;

英文缩写在命名是必须的,遵循下面规则:

  1. 较短的单词可通过去掉“元音”形成缩写,如icon->ic;
  2. 较长的单词可取单词的头几个字母形成缩写,如:average->avg;
  3. 此外还有一些约定成俗的英文单词缩写,如 Internationalization->I18N;
  4. 程序中不要用缩写,除非该缩写是约定俗成的。

下面为常见的英文单词缩写:

名称 缩写
icon ic (主要用在app的图标)
color cl(主要用于颜色值)
divider di(主要用于分隔线,不仅包括Listview中的divider,还包括普通布局中的线)
selector sl(主要用于某一view多种状态,不仅包括Listview中的selector,还包括按钮的selector)
average avg
background Bg(主要用于布局和子布局的背景)
buffer buf
control ctrl
delete del
document doc
error err
escape esc
increment inc
infomation info
initial init
image img
Internationalization I18N
length len
library lib
message msg
password pwd
position pos
server srv
string str
temp tmp
window wnd(win)

命名规范:

包(packages): 采用反域名命名规则,全部使用小写字母。一级包名为地顶级域名如com,二级包名为xx(可以是公司或个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名;如 com.tinyx.myapp.activities;

类(classes): 用Pascal命名法,尽量避免缩写,如:MyActivity;缩写是众所周知的,如HTML,URL;类名称中包含单词缩写,则单词缩写的每个字母均应大写,如:PublicHTML,CommonURL。

接口(interface): 与类一样用Pascal命名法,多以able或ible结尾,多用作表示行为,如Runnable,Accessible;

方法(methods): 动词或动名词,采用Camel命名法,如:onCreate(), run();下面是一些建议:

  • 初始化相关方法,使用init为前缀标识,如:初始化布局initView();
  • boolean型使用is或check为前缀标识, 如:checkValue()、isValidate();
  • 返回某个值的方法,使用get为前缀标识,如:getName();
  • 数据进行处理相关,尽量使用process为前缀标识,如:processUpdate();
  • 保存数据相关,使用save为前缀标识,如:saveData();
  • 对数据重置的,使用reset前缀标识,如:resetData();
  • 清除数据相关,使用clear前缀标识,如:clearData();
  • 移除某些项目,使用remove前缀标识,如:removeItem();
  • 绘制数据或效果相关的,使用draw前缀标识,如:drawCircle();

变量(variables): 采用Pascal命名法,建议采用有意义的命名如:firstName, lastName;

  • 模型类变量默认以上规则
1
2
3
4
5
6
7
8
9
10
11
public class User {
public String name;
public String phone;
public int sex; //1,男 2,女

public User() {
this.name = "myname";
this.phone = "123“
this.sex = 0;
}
}
  • 非模型类全局参数建议加上小写m开头;
1
2
3
4
5
6
7
8
9
10
11
public class TestActivity extends Activity{
private ZoomableImageView mZoomableView;
private TabLayout mTabLayout;
private int mItemsCount;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
}
}

常量(constants): 全部大写,采用下划线命名法。如下:

1
2
public static final int MAX_ITEMS= 10;
public static final String TAG = User.class.getSimpleName();

资源文件命名(resources): 采用下划线命名法,全部小写,针对不同资源,建议用下面的命名方法;

  • drawable资源,加前缀命名:前缀_功能_模块_说明.xml/png/
说明 命名范例
图标:建议格式 ic_xxx; ic_appicon.png
背景:建议格式 bg_xxx; bg_normal_button_default.xml, bg_normal_button_press.xml
  • layout资源文件,前缀命名:类型_模块_功能_说明.xml,举一些常用的例子如下:
说明 命名范例
Activity布局文件 activity_main.xml
Fragment布局文件 fragment_main.xml
局部布局View文件 view_main_header.xml, view_main_bottom.xml
自定义提示对话框 dialog_alert.xml
列表项等 fragment_user_list_item.xml
  • 动画anim资源文件(anim只有一种资源,所以不必加前缀区分):模块_功能_动画_方向.xml
说明 命名范例
淡入 main_button_fade_in.xml
淡出 main_button_fade_out.xml
从下方推入 button_push_down_in.xml
从下方推出 main_button_push_down_out.xml
  • menu菜单资源文件(menu只有一种资源,所以不必加前缀区分):模块_功能_说明.xml
说明 命名范例
主界面菜单 main_activity.xml
Fragment界面菜单 user_fragment.xml
  • values资源,这个主要分下面几种资源:

1、 ids资源,主要存放是界面控件的id值,用下划线小写命名法,前缀方式:前缀_模块_功能_说明,常用界面控件命名如下:

说明 命名范例
布局和子控件(ViewGroup,自定义View) view_main_topnav
TextView tv_main_title
Button btn_user_add
ImageButton imgbtn_user_del
ImageView img_thumb
CheckBox cb_sex
RadioButton rbtn_answer
EditText et_username
ToggleButton toggle_funtion
ProgressBar pb_download
SeekBar sb_progress
ProgressBar pb_download
VideoView vv_course
WebView wv_download
RantingBar rb_download
Spinner sp_cities
ScollView sv_main
TextSwitch sp_cities
ListView/ExpandListView/RecyclerView lv_cities
MapView mv_location

2、strings/arrays/dimens资源,用下划线小写命名法,不加任何前后缀,格式:模块_功能_说明。

3、attrs/colors/ids的属性和名称使用Camel命名法;styles的属性使用Camel命名法,名称使用Pascal命名法;如下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--attrs-->
<attr name="text" format="string" />
<attr name="itemIcon" format="reference" />
<attr name="showToggle" format="boolean" />
<attr name="showVersion" format="boolean" />
<style name="Theme.AppCompat.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>

<!--colors-->
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796b</color>
<color name="colorAccent">#cddc39</color>

<!--ids-->
<item name="tabLayout" type="id"/>
<item name="viewPager" type="id"/>
<item name="viewContainer" type="id"/>

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

图谱

四大组件

Android 四大组件

  Android 四大组件分别是:

  • Activity(活动,用于表现功能);
  • Service(服务,进行一些不需要UI存在的操作);
  • BroadcastReceiver(广播接收器,用于接收广播);
  • ContentProvider(内容提供者,支持在多个应用中存储和读取数据)。

Activity

Android 之 Activity (一) 基础知识

  http://www.leoyanblog.com/post/2017/02/05.html

Android 之 Activity (二) 生命周期

  http://www.leoyanblog.com/post/2017/02/12.html

Android 之 Activity (三) Task 和 LaunchMode

  http://www.leoyanblog.com/post/2017/02/19.html

Service

Android 之 Service 相关知识

  http://www.leoyanblog.com/post/2017/02/26.html

BroadcastReceiver

Android 之 BroadcastReceiver (一) 相关知识

  http://www.leoyanblog.com/post/2017/03/05.html

Android 之 BroadcastReceiver (二) 常见的系统广播

  http://www.leoyanblog.com/post/2017/03/12.html

ContentProvider

Android 之 ContentProvider (一) 相关知识

  http://www.leoyanblog.com/post/2017/03/19.html

Android 之 ContentProvider (二) 示例代码

  http://www.leoyanblog.com/post/2017/03/20.html

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

手机号验证的正则表达式

1、手机号开头集合

1
2
3
4
5
176177178
180181182183184185186187188189
145147
130131132133134135136137, 138139
150151, 152153155156157158159

2、正则表达式

1
2
3
4
5
6
public static boolean isChinaPhoneLegal(String str) throws PatternSyntaxException {  
String regExp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-8])|(147,145))\\d{8}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

13开头的后面跟0-9的任意8位数;

15开头的后面跟除了4以外的0-9的任意8位数;

18开头的后面跟0-9的任意8位数;

17开头的后面跟0-8的任意8位数,或者17[^9];

147,145开头后面跟任意8位数;

转载请注明出处:www.leoyanblog.com

本文出自 LeoYan 的博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

示例说明

ContentProviderDemo_01

  在创建ContentProvider前,首先要实现底层的数据源,数据源包括数据库、文件系统或网络等,然后继承ContentProvider类中实现基本数据操作的接口函数。调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider。

注:示例中所选的数据源是 SQLite。

Demo 结构

  Demo结构如下:

ContentProviderDemo_02

ContentProviderDemo_03

关键代码

  People.Java (TestContentProvider 和 TestContentResolver 都有)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class People {
public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
public static final String MIME_ITEM = "vnd.leo.people";

public static final String MIME_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MIME_ITEM;
public static final String MIME_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MIME_ITEM;

public static final String AUTHORITY = "com.leo.peopleprovider";
public static final String PATH_SINGLE = "people/#";
public static final String PATH_MULTIPLE = "people";
public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);

public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
public static final String KEY_HEIGHT = "height";
}

  DBOpenHelper.java (TestContentProvider 中)

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
public class DBOpenHelper extends SQLiteOpenHelper {

public static final String DB_NAME = "people.db";
public static final String DB_TABLE = "peopleinfo";
public static final int DB_VERSION = 1;

private static final String DB_CREATE = "create table " +
DB_TABLE + "(" + People.KEY_ID + " integer primary key autoincrement, " +
People.KEY_NAME + " text not null, " + People.KEY_AGE + " integer, " +
People.KEY_HEIGHT + " float);";

public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
onCreate(db);
}
}

  PeopleProvider.java (TestContentProvider 中)

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
public class PeopleProvider extends ContentProvider {

private SQLiteDatabase db;
private DBOpenHelper dbOpenHelper;

private static final int MULTIPLE_PEOPLE = 1;
private static final int SINGLE_PEOPLE = 2;
private static final UriMatcher uriMatcher;

static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(People.AUTHORITY, People.PATH_MULTIPLE, MULTIPLE_PEOPLE);
uriMatcher.addURI(People.AUTHORITY, People.PATH_SINGLE, SINGLE_PEOPLE);
}

@Override
public boolean onCreate() {
Context context = getContext();
dbOpenHelper = new DBOpenHelper(context, DBOpenHelper.DB_NAME, null, DBOpenHelper.DB_VERSION);
db = dbOpenHelper.getWritableDatabase();
if (db == null) {
return false;
} else {
return true;
}
}

@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(DBOpenHelper.DB_TABLE);
switch (uriMatcher.match(uri)) {
case SINGLE_PEOPLE:
// 单条数据的处理
qb.appendWhere(People.KEY_ID + "=" + uri.getPathSegments().get(1));
break;
default:
break;
}
Cursor cursor = qb.query(db,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}

@Nullable
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case MULTIPLE_PEOPLE:
// 多条数据的处理
return People.MIME_TYPE_MULTIPLE;
case SINGLE_PEOPLE:
// 单条数据的处理
return People.MIME_TYPE_SINGLE;
default:
throw new IllegalArgumentException("Unkown uro:" + uri);
}
}

@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
long id = db.insert(DBOpenHelper.DB_TABLE, null, values);
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(People.CONTENT_URI, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
throw new SQLException("failed to insert row into " + uri);
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case MULTIPLE_PEOPLE:
// 多条数据的处理
count = db.delete(DBOpenHelper.DB_TABLE, selection, selectionArgs);
break;
case SINGLE_PEOPLE:
// 单条数据的处理
String segment = uri.getPathSegments().get(1);
count = db.delete(DBOpenHelper.DB_TABLE, People.KEY_ID + "=" + segment, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported URI:" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count;
switch (uriMatcher.match(uri)) {
case MULTIPLE_PEOPLE:
// 多条数据的处理
count = db.update(DBOpenHelper.DB_TABLE, values, selection, selectionArgs);
break;
case SINGLE_PEOPLE:
// 单条数据的处理
String segment = uri.getPathSegments().get(1);
count = db.update(DBOpenHelper.DB_TABLE, values, People.KEY_ID + "=" + segment, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}

}

  AndroidManifest.xml (TestContentProvider 中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leo.testcontentprovider" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<provider
android:authorities="com.leo.peopleprovider"
android:name=".PeopleProvider"
android:exported="true"/>
</application>
</manifest>

  MainActivity.java (TestContentResolver 中)

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
public class MainActivity extends AppCompatActivity {

...

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resolver = this.getContentResolver();
initView();
initEvent();
}

private void initView() {
...
}

private void initEvent() {
addBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(People.KEY_NAME, nameEt.getText().toString());
values.put(People.KEY_AGE, Integer.parseInt(ageEt.getText().toString()));
values.put(People.KEY_HEIGHT, Float.parseFloat(heightEt.getText().toString()));
Uri newUri = resolver.insert(People.CONTENT_URI, values);
labelTv.setText("添加成功,URI:" + newUri);
}
});

queryBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEt.getText().toString());
Cursor cursor = resolver.query(uri,
new String[]{People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT},
null, null, null);
if (cursor == null) {
labelTv.setText("数据库中没有数据");
return;
}
labelTv.setText("数据库:" + String.valueOf(cursor.getCount()) + "条记录");
String msg = "";
if (cursor.moveToFirst()) {
do {
msg += "ID: " + cursor.getString(cursor.getColumnIndex(People.KEY_ID)) + ",";
msg += "姓名: " + cursor.getString(cursor.getColumnIndex(People.KEY_NAME)) + ",";
msg += "年龄: " + cursor.getInt(cursor.getColumnIndex(People.KEY_AGE)) + ",";
msg += "身高: " + cursor.getFloat(cursor.getColumnIndex(People.KEY_HEIGHT)) + "\n";
} while (cursor.moveToNext());
}
displayTv.setText(msg);
}
});

queryAllBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Cursor cursor = resolver.query(People.CONTENT_URI,
new String[]{People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT},
null, null, null);
if (cursor == null) {
labelTv.setText("数据库中没有数据");
return;
}
labelTv.setText("数据库:" + String.valueOf(cursor.getCount()) + "条记录");
String msg = "";
if (cursor.moveToFirst()) {
do {
msg += "ID: " + cursor.getString(cursor.getColumnIndex(People.KEY_ID)) + ",";
msg += "姓名: " + cursor.getString(cursor.getColumnIndex(People.KEY_NAME)) + ",";
msg += "年龄: " + cursor.getInt(cursor.getColumnIndex(People.KEY_AGE)) + ",";
msg += "身高: " + cursor.getFloat(cursor.getColumnIndex(People.KEY_HEIGHT)) + "\n";
} while (cursor.moveToNext());
}
displayTv.setText(msg);
}
});

deleteBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEt.getText().toString());
int count = resolver.delete(uri, null, null);
String msg = count + " 条数据被删除";
labelTv.setText(msg);
}
});

deleteAllBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resolver.delete(People.CONTENT_URI, null, null);
String msg = "数据全部删除";
labelTv.setText(msg);
}
});

updateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(People.KEY_NAME, nameEt.getText().toString());
values.put(People.KEY_AGE, Integer.parseInt(ageEt.getText().toString()));
values.put(People.KEY_HEIGHT, Float.parseFloat(heightEt.getText().toString()));
Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEt.getText().toString());
int result = resolver.update(uri, values, null, null);
String msg = "更新ID为" + idEt.getText().toString() + "的数据" + (result > 0 ? "成功" : "失败");
labelTv.setText(msg);
}
});
}
}

Demo 下载

  Demo 下载地址:http://download.csdn.net/download/leoyan_blog/9783601