月度归档:2015年06月

Java里的异常

 

前言:最近都在准备考试复习都木有这么学习新姿势,临时找了以前看书的记录凑出了这个文章,太罪过了><

感觉自己目前写的程序都是编译器在强制自己要做异常处理,对于怎么样编写优秀的异常处理程序还不算很清楚,下面先整理一下Thinking in java里看到的关于异常的知识吧。异常处理的原则是”只有你知道如何处理的情况下才捕获异常”,其中一个重要目标是将错误处理代码和错误发生地点分离。

分类:分为被检查异常(checked)和不被检查异常(unchecked)。这里的检查指的是否进行编译时期的强制检查。

重新抛出对象fillStackTrace的用法;异常出现后异常对象会被压栈,我们可以通过打印栈里的信息,来获取发生异常的函数调用情况,栈底的元素是调用序列里的第一个方法。这里要注意异常重新抛出的一个问题,有时候我们捕获一个异常后我们并不希望在这处理,只是想像上一层再次抛出,比如像catch(Exception e ) {

throw e; // rethrow the exception

}这样重新抛出的话,若用printStackTrace打印栈元素的话,只会显示最原始抛出异常点的信息而不包括重新抛出点的位置,若要把重新抛出异常的信息也要加入栈里,需要调用fillInStackTrace这个方法把信息压入。

finally的注意和特别用法。finally作为try/catch机制里最后进行处理的措施,就算try里遇到了break和return,finally也还是会执行的。

继承对异常的影响。若子类继承了父类的某个方法,子类可以不抛出父类规定的异常。子类要覆写(Override)父类的某个方法,若原方法定义了某个异常,子类的方法里不能再给这个方法抛出更多异常。

切勿“吞食”掉异常。写异常有时候的确很烦,特别遇到有很多异常嵌套的时候,我们为了应付编译器就只写了一个try/catch在那或者简单的throw出去,这个异常就被吞食了,这不是一个好习惯。最次我们也要把异常打印出来提醒,或者我不知道这里遇到异常应该怎么处理,我就抛出一个RuntimeException结束程序把。

新玩具:Android里的DataBinding

为什么我们需要Data Binding

传统的MVC模式如下图所示:

mvc这种模式的初衷是让业务逻辑和View分开,让我们在修改界面而不改变业务逻辑的时候更简单,但是实习操作的时候往往很难完全对View和Controller或Model很好的分离。并且三种组件也增加了一定的复杂度。而当我们有了Data Binding技术我们就可以使用另外一种模式(这种模式常用于Windows软件开发)叫MVVM 全称是Model-View-ViewModel,Model代表的是你的业务逻辑,View是展示的视图,ViewModel是把二者绑定起来的接口。这样说还是有点抽象,在一般Android开发里,如果某个View展示的数据出现了变化,我们需要findViewById找到这个View,并且设置新更新的数值。而在MVVC里,因为Model和View是绑定的,如果model层的数据发送了变化,会自动通知更新view应该如何重新变化展示。听起来是不是非常棒哈。

 如何在Android里使用

  • 准备环境

在整个工程里的build.gradle里添加如下依赖:

  dependencies {
       classpath "com.android.tools.build:gradle:1.3.0-beta1"
       classpath "com.android.databinding:dataBinder:1.0-rc0"
   }

在你需要的使用data binding的module对应的build.gradle里添加

apply plugin: 'com.android.databinding'

ps:这里需要Android Studio是1.3以上,gradle版本在1.3以上,我之前即使是这样设置了还是有问题,我把build tool还有兼容包全部升级到最新版本就木有问题了。

下面我将已一个例子来介绍Android的DataBinding,这个小程序叫MagicNumber,app里显示一个数从1开始,有一个按钮,按一次按钮数字加1,若数字是奇数数字将显示黄色,若是偶数显示蓝色。

 

 

evenodd

先写布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="num" type="com.lvable.databindingtest.MagicNumber"/>
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:text="The magic number"
            android:id="@+id/textView"
            android:layout_marginTop="43dp"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"/>

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="67dp"
            android:textSize="60sp"
            android:layout_below="@+id/textView"
            android:layout_centerHorizontal="true"
            android:text="@{num.num}"
            android:textColor="@{num.odd ? @color/light_yellow : @color/light_blue}"
            />

        <Button
            android:text="Add"
            android:id="@+id/btn"
            android:onClick="addCount"
            android:layout_below="@id/textView2"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>

</layout>

注意如下变化:布局文件里的根元素不再是ViewGroup而是一个叫layout的标签,这个标签里包含一个data标签还有一个View,data标签里面包含了要绑定的数据元素类型申明等信息。在本例子里,我们要绑定的数据类型叫MagicNumber,然后在布局里它的名字叫num,type是这个类的完整路径名(若是Java自带的类型直接写就好,比如String,int)。在其他地方若要用到这个绑定数据的值只需要用@ {obj.xxxx}就行。这里要注意一个问题,就是在访问obj的成员变量的时候不一定总是它的全名,我试了一下我的MagicNumber里有一个成员变量叫isOdd,但是要访问它的话我要这样写 @{num.odd}。在这里还可以使用一些语句来实现简单的逻辑操作,比如我在布局文件里写入了奇偶不同颜色不同的逻辑。在textView里

android:textColor="@{num.odd ? @color/light_yellow : @color/light_blue}"

带Observable模式的MagicNumber类

Observable模式就是,当绑定的数据发送变化的时候,这个绑定的View会自己更新显示信息。这里可以使用MagicNumber继承一个叫BaseObservable的类来实现。(也可以使用ObservableField来实现对某一个field的Observe而不用继承)。

package com.lvable.databindingtest;

import android.databinding.BaseObservable;
import android.databinding.Bindable;

import com.lvable.databindingtest.BR;


/**
 * Created by Jiaqi Ning on 21/6/2015.
 */
public class MagicNumber extends BaseObservable {
    private String num;
    private boolean isOdd;
    
    public MagicNumber(int num){
        this.num = Integer.toString(num);
    }
   
    @Bindable
    public String getNum() {
        return num;
    }
   
    @Bindable
    public boolean isOdd() {
        return isOdd;
    }
    
    public void addOne(){
        int c = Integer.parseInt(num);
        num = Integer.toString(++c);
        updateEven();
        notifyPropertyChanged(BR.num);
        notifyPropertyChanged(BR.odd);
    }

    private void updateEven(){
        int c = Integer.parseInt(num);
        isOdd = ((c % 2) != 0) ? true : false;
    }
}

这里要注意,需要有更新通知功能的成员变量的get方法里,添加@Bindable这个注解,然后在起更新数据的地方调用对应的notifyPropertyChanged(BR.xxx)。在本程序里是如果MagicNumber调用了addOne就通知数据更新。

(注:BR 是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry,当我们通过代码可以看出,当数据发生变化时还是需要手动发出通知。)

好了,终于到了最后真正实现绑定的一步了!

public class MainActivity extends AppCompatActivity {
    MagicNumber magicNumber = new MagicNumber(1);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setNum(magicNumber);
    }

    public void addCount(View view){
        magicNumber.addOne();
    }

}

注意,这里不再使用setContentView(),使用的DataBindingUtil.setContentView()返回一个binding,用它来绑定数据。(这里的set方法是更具之前data里写入的type动态生成的)。

还有一个更好用的特性,若你在布局文件里给了某个View一个ID,在Activity里你想要使用这个View你不再需要些findViewById,你只需要从前面绑定产生的那个binding.xxxx (xxx是view的id名)就可以直接对这个View进行操作了!Awesome!

参考:    Google官方guide

很好的binding demo教程

 

初识Java里RTTI和反射(二)

今天才突然发现,RTTI其实主要是C++里的说法和反射其实表达的意思差不多(反射主要是Java里对RTTI的一种实现),有一个主要的区别是,对RTTI来说,编译器会在编译期打开和检查class文件。但对“反射”来说,class文件在编译期间是不可使用的,而是由运行时环境打开和检查。

动机

一开始觉得动态获取某个类的类型信息好像觉得平时根本用不上,那到底反射他的使用场景是什么呢?我了解的场景有下:

  • 插件化构件的程序就会需要到这个强大的技术了。也就是新加的插件,本来不在原程序里,新的插件是从网络上下载下来,这个类不在原来的程序空间里。
  • 远程方法调用(RMI),它允许一个Java程序将对象分布到多台机器上。例如,你可能正在执行一项需要大量计算的任务,为了提高运算速度,想将计算划分到许多小的计算单元上。
  • 在Android里扩充Framework。如果厂家需要修改Framework,要在里面的某类里添加使用自定义的Jar包,使用该自定义Jar包的方法是用import,但是为保存和原生Framework的兼容性,对原生Framework的最少修改,可以使用ClassLoader动态加载自定义Jar包。

Reflect常用类方法介

  • Method

可以在获取某个Class对象包含的所有方法getMethods(),也可用getMethod(String name,Class<?>… parameterTypes)来获取特定的方法,例子如下:

Class clzz = Class.forName("DummyClass");
Method methods = clzz.getMethods();
for(Metho m : methos){
    m.toString();
}
// get the function name is "display",and the param type is Stirng
Method action = clzz.getMethod("display",String.TYPE);
// call this function
action.invoke("test");
  • getConstruct

getConstructs返回所有构造函数,getConstructor(Class<?>… parameterTypes)返回特定参数的构造函数。

 

考虑过几天敲一下,一个Android插件化构架的例子 : )