移动周分享-第58期

Java Annotation - 王胜

Annotation是什么

维基百科: A form of syntactic metadaa that can be added to Java source code. 也就是说,Annotation的引入是为了从Java语言层面上,为Java源代码提供元数据的支持。参见维基百科

Annotation的用途

表象,替代之前JDK1.4开发中,大量繁琐的配置项,Annotation的出现其实可以极大简化配置文件的数量和需要关注配置的内容。但其实,注解带来的益处远不至于此。

Annotation的分类

  • 文档标注型

    主要是@Documented,用以标注是否在javadoc中

  • 编译检查型

    主要在编译过程中,给Java编译器若干指令,检查Java代码中是否存在若干问题, 改变编译器的动作或者行为,通过Annotation的使用,可以调整和控制编译器的使用以及让编译器提供关于代码的更多的检查和验证。主要有:@Override,@SuppressWarning

  • 代码分析型

    此类Annotation是在我们开发中使用最多的,主要是通过Annotation提供给代码更多的额外特性和设置,在Java运行过程中发挥作用。常见的是在Spring或者Hibernate等框架中出现的@Controller,@Service,@Bean, @Table, @Enitty等等.

自定义生命周期为Runtime类型Annotation

Runtime的处理主要依赖于反射的接口,在字节码中寻找Annotation的接口和输入参数,提取其内容和数值。大部分的情况下是基于代理模式,动态生成相应的代理类实例,然后通过代理类,调用相应的InvocationHandler,在InvocationHandler之内完成Annotation所要执行的动作;然后再继续调用原来的方法,继续执行。

用户在定义Runtime类型的Annotation时,需要的步骤:

  • 定义Annotation
  • 定义Annotation的处理器类『通过Reflect、InvocationHandler、Proxy.newProxyInstance()来处理具体的逻辑』
  • 选择合适的调用Annotation的时机和切入点。

示例

custom-annotation-demo.png

自定义一个@OnClick注解,通过在方法前面加入这个注解,从而为指定的组件动态添加事件监听方法。

  • 定义OnClick注解

    1
    2
    3
    4
    5
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OnClick {
    String value();
    }
  • 定义OnClickProxyFactory来处理动态绑定方法的逻辑

    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
    public class OnClickProxyFactory {

    /**
    * 处理OnClick注解
    *
    * @param target 包含OnClick注解的目标对象
    */

    public static void handleOnClickAnnotation(Object target) {
    try {
    Class<?> clsTarget = target.getClass();
    // 检索目标对象的所有方法, 如果含有OnClick注解, 则为注解中声明的属性对象动态添加AddActionListener方法
    for (Method method : clsTarget.getDeclaredMethods()) {
    OnClick onClickAnnotation = method.getAnnotation(OnClick.class);
    if (onClickAnnotation != null) {
    Field field = clsTarget.getDeclaredField(onClickAnnotation.value());
    field.setAccessible(true);
    autoAddActionlistener(field.get(target), getActionListenerProxy(target, method));
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * 为目标对象添加addActionListener方法, 该方法的参数ActionListener为ActionListener代理对象
    * @param target 被添加ActionListener方法的事件源对象
    * @param actionListenerProxy ActionListener代理对象
    * @throws NoSuchMethodException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    */

    private static void autoAddActionlistener(Object target, Object actionListenerProxy) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    // 获取JButton的addActionListener方法对象
    Method methodAddActionListener = target.getClass().getMethod("addActionListener", ActionListener.class);
    // 为JButton对象添加addActionListener方法
    methodAddActionListener.invoke(target, actionListenerProxy);
    }

    /**
    * 获取ActionListener的代理对象
    * @param target 指定的代理对象
    * @param targetMethod 指定的代理对象的方法
    * @return
    */

    public static Object getActionListenerProxy(final Object target, final Method targetMethod) {
    InvocationHandler handler = new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return targetMethod.invoke(target);
    }
    };
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[] {ActionListener.class}, handler);
    }
    }
  • 在UI初始化时,调用注解的解析和动态处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class UI extends JFrame{
    // 省略......
    public UI() {
    // 省略......

    // 通过OnClickProxyFactory工厂来处理OnClick注解
    OnClickProxyFactory.handleOnClickAnnotation(this);
    }

    @OnClick("btnBlue")
    public void setBlueBackground() {
    panel.setBackground(Color.BLUE);
    }

    @OnClick("btnRed")
    public void setRedBackground() {
    panel.setBackground(Color.RED);
    }

    @OnClick("btnSayHello")
    public void sayHello() {
    JOptionPane.showConfirmDialog(null, "Hello World!", "Tips", JOptionPane.YES_OPTION);
    }

具体的示例源码,移步 Github-helloJavaSE

参考

Xcode MarkDown的代码文档 ( for swift )

@(iOS)[Markdown, Document]

markdown在swift中的应用

goals

  • 描述各个属性、函数和类的真正用途
  • 高亮函数的输入和输出(参数和返回值)
  • 几个月后还能清晰地记得每个函数属性是为了什么
  • 使用工具制作具有专业外观的使用手册(比如:使用 Jazzy
  • Xcode 里写的代码文档能被预览

markdown grammar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#text#:文本标题

**text**:使文本具有加粗的效果

*text*:使文本具有斜体的效果

* text:使文本成为一个无序列表的元素,值得注意的是,有个 * 后面需要有一个空格。同样,可以使用 + 或 - 实现这个的功能

1.text:使文本成为一个有序列表的元素

[linked text](http://some-url.com):使文本成为可以点击的超链接

![image show](http://www.appcoda.com/wp-content/uploads/2016/05/t52_3_help_inspector1.png):可以显示图片

> text:创建一个块引用。

使用 4 个空格或 1 个 tab 来缩进所写的代码块,等价于 HTML 中的 \\ 标签。可以继续使用 4 个空格或 1 个 tab 来添加另一个缩进

如果不想使用空格或 tab 的话,可以使用 ` 。比如, `var myProperty` 会显示成 var myProperty

另一种创建代码块的方法是添加 4 个 `,并从下一行开始写具体的代码,最后添加 4 个 ` 表示结束

反斜杠修饰 Markdown 的特殊字符就可以避免 Markdown 语法的解析了。比如, \**this\** 就不会产生加粗的效果

注释区域: 3 个斜线(///)或以下面的形式开头:

1
2
3
/**

*/

Case
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
It calculates and returns the outcome of the division of the two parameters.

## Important Notes ##
1. Both parameters are **double** numbers.
2. For a proper result the second parameter *must be other than 0*.
3. If the second parameter is 0 then the function will return nil.

*/

func performDivisionnumber1: Double, number2: Double) -> Double! {
if number2 != 0 {
return number1 / number2
}
else {
return nil
}
}

case image

quick look

关键词

  • Parameter
  • Returns
  • Remark
  • SeeAlso
  • Precondiction
  • Requires
  • Todo
  • Version
  • Author
  • Note
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
/**
Another complicated function.

- Parameter fullname: The fullname that will be broken into its parts.
- Returns: A *tuple* with the first and last name.

- Remark:
There's a counterpart function that concatenates the first and last name into a full name.

- SeeAlso: `createFullName(_:lastname:)`

- Precondition: `fullname` should not be nil.
- Requires: Both first and last name should be parts of the full name, separated with a *space character*.

- Todo: Support middle name in the next version.

- Warning: A wonderful **crash** will be the result of a `nil` argument.

- Version: 1.1

- Author: Myself Only

- Note: Too much documentation for such a small function.
*/

func breakFullNamefullname: String) -> (firstname: String, lastname: String) {
let fullnameInPieces = fullname.componentsSeparatedByString(" "
return (fullnameInPieces[0], fullnameInPieces[1])
}

全关键字

Jazzy 自动产生代码文档

Jazzy 是一款可以为 Swift 和 Objective-C 代码产生具有 Apple 风格的代码文档工具。

效果如下

jazzy 效果

下面以Alamofire为例子:

jazzy —help 查看帮助

  • cd Alamofire 的项目path
  • jazzy —output /Users/xcodeyang/Desktop/jazzy_document

参考博客地址

Android App共享文件Uri不能为file:// - 吴明

  • 先看异常信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
E/StrictMode: null
java.lang.Throwable: file:// Uri exposed through Intent.getData()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1757)
at android.net.Uri.checkFileUriExposed(Uri.java:2346)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8045)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1506)
at android.app.Activity.startActivityForResult(Activity.java:3930)
at android.app.Activity.startActivityForResult(Activity.java:3890)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:843)
at android.app.Activity.startActivity(Activity.java:4213)
at android.support.v4.app.ActivityCompatJB.startActivity(ActivityCompatJB.java:26)
at android.support.v4.app.ActivityCompat.startActivity(ActivityCompat.java:133)
at com.horizon.offer.mail.maildetail.impl.ImageAnnexWrapper.openFile(ImageAnnexWrapper.java:26)
at com.horizon.offer.mail.maildetail.MailDetailActivity.openAnnexFile(MailDetailActivity.java:184)
at com.horizon.offer.mail.maildetail.adapter.MailAnnexAdapter$MailAnnexViewHolder$1.onClick(MailAnnexAdapter.java:75)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21153)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

在StrictMode(严格)模式下,App之间共享资源报的异常。

  • 出现这个异常的代码
1
2
3
4
5
6
7
8
public void openFile(@NonNull Activity activity, @NonNull File file) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/**异常这行代码**/
intent.setDataAndType(Uri.fromFile(file), "image/*");
ActivityCompat.startActivity(activity, intent, null);
}

打印

1
Uri.fromFile(file)

输出信息

1
Uri uri=file:///storage/emulated/0/download/9text.jpg

谓词 - 刘康

  • NSPredicate是Foundation框架中的一个类。
  • 作用:指定数据被获取和过滤的方式。提供了类似于自然语言一样定义一个集合被搜寻的逻辑条件。

为了证明NSPredicate的强大功能,先写一个Person类,做准备工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person: NSObject {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
super.init()
}
override var description: String {
return "name: \(self.name), age: \(self.age)"
}
}

var colleagues = NSMutableArray()
colleagues.addObject(Person(name: "Arthur", age: 45))
colleagues.addObject(Person(name: "Michael", age: 23))
colleagues.addObject(Person(name: "Kenny", age: 25))
colleagues.addObject(Person(name: "Bella", age: 24))
colleagues.addObject(Person(name: "Vincent", age: 36))
colleagues.addObject(Person(name: "Adolph", age: 39))

基本使用

找出年龄为24岁的人:

1
2
let predicateByAge = NSPredicate(format: "age == 24")
let result = colleagues.filteredArrayUsingPredicate(predicateByAge)

参数可以传入:

1
2
3
let age = NSNumber(int: 25)
let predicateByPassAge = NSPredicate(format: "age == %@", age)
let result1 = colleagues.filteredArrayUsingPredicate(predicateByPassAge)

也可传入要对比的属性,这里是age. 属性的key:

1
2
let pridicateByAge1 = NSPredicate(format: "%K == %@", "age", NSNumber(int: 36))
let result2 = colleagues.filteredArrayUsingPredicate(pridicateByAge1)

指定通配的变量,这里用24来替代age:

1
2
let pridicateByAge2 = NSPredicate (format: "age == $age")
let result3 = colleagues.filteredArrayUsingPredicate(pridicateByAge2.predicateWithSubstitutionVariables(["age": NSNumber(int: 24)]))

语法小结

  • 使用%@对应数字,字符串,日期的替代值
  • 使用%K对应要比较的属性,也就是KVC中的key
  • 使用$变量名来表示通配的变量,然后predicateWithSubstitutionVariables来决定具体的变量值

基本比较

找出年龄大于40岁的同事:

1
2
let predicateAgeOver40 = NSPredicate(format: "age > 40")
let boss = colleagues.filteredArrayUsingPredicate(predicateAgeOver40)

找出年龄在22岁~35岁的人:

1
2
3
4
let minAge = NSNumber(int: 22)
let maxAge = NSNumber(int: 35)
let predicateByAge3 = NSPredicate(format: "age BETWEEN {%@, %@}", minAge, maxAge)
let result4 = colleagues.filteredArrayUsingPredicate(predicateByAge3)

语法小结

  • > 大于
  • >= 大于等于
  • < 小于
  • <= 小于等于
  • == 等于
  • != 或者 <> 不等于
  • BETWEEN 介于两者之间,包括上下限

复合比较

  • && 或者AND 逻辑与
  • || 或者 OR 逻辑或
  • !或者NOT 逻辑非
1
2
let predicateByCompare = NSPredicate(format: "age < 30 OR age >= 40")
let result5 = colleagues.filteredArrayUsingPredicate(predicateByCompare)

字符串比较

  • BEGINSWITH 左边表达式以右边表达式开头
  • CONTAINS 左边表达式包含右边表达式
  • ENDSWITH 左边表达式以右边表达式结尾
  • LIKE 左边表达式和右边表达式相似(简单的正则表达式匹配,?匹配一个字符,*匹配0个或者多个字符)
  • MATCHES 可以实现较为复杂的正则表达式匹配
  • 用方括号加cd来不区分大小写和变音符号
  • IN 左边的表达式在右边的集合里

找出名字以“A”开头的同事:

1
2
let pridivateByName1 = NSPredicate(format: "name BEGINSWITH %@", "A")
let result6 = colleagues.filteredArrayUsingPredicate(pridivateByName1)

名字里包含in,不区分大小写,并且年龄大于等于24:

1
2
let pridivateByName2 = NSPredicate(format: "name CONTAINS %@ && age >= %@", "in", NSNumber(int: 24))
let result7 = colleagues.filteredArrayUsingPredicate(pridivateByName2)

复合正则表达式T[a-z]*k:

1
2
let privatedivateByName3 = NSPredicate(format: "name MATCHES 'T[a-z]*k'")
let result8 = colleagues.filteredArrayUsingPredicate(privatedivateByName3)

名字是两者中的一个:

1
2
let privatedivateByName4 = NSPredicate(format: "name IN {'Bella', 'Jack Tomphon'}")
let result9 = colleagues.filteredArrayUsingPredicate(privatedivateByName4)

基于Block的谓词

基于Block能够灵活的定制谓词,这里简单的Block定义age > 24:

1
2
3
4
5
6
7
8
9
10
let blockPredicate = NSPredicate { (person: AnyObject!, _) -> Bool in
var result = false
if let castResult = person as? Person {
if castResult.age > 24 {
result = true
}
}
return result
}
let result10 = colleagues.filteredArrayUsingPredicate(blockPredicate)

Array使用谓词参考:

StackOverFlow