想成为Android高手必须学习的干货
来源:互联网 发布:kruskal算法图解 编辑:程序博客网 时间:2024/06/06 03:21
[原]设计模式之一---工厂方法模式【Factory Method模式】
在设计模式中,Factory Method模式是一种比较简单的设计模式,应用比较广泛,但也是一种比较重要的设计模式之一。在很多地方我们都会看到xxxFactory这样命名的类,那么,什么是Factory Method,为什么要用这个模式,如何用Java语言来实现该模式?
【1】基本概念
FactoryMethod是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化。
【2】简单分析
我们先来看一下该设计模式的UML图:
上图是Factory Method 模式的结构图,让我们可以进行更方便的描述:
- Product: 需要创建的产品的抽象类.
- ConcreteProduct: Product的子类,一系列具体的产品.
- Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method.
- ConcreteCreator: 具体的创建器,重写Creator中的Factory Method,返回ConcreteProduct类型的实例.
同时可以清楚的看出这样的平行对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过Factory Method模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:
- 首先客户端可以统一从抽象创建器获取产生的实例,Creator的作用将client和产品创建过程分离开来,客户不用操心返回的是那一个具体的产品,也不用关心这些产品是如何创建的.同时,ConcreteProduct也被隐藏在Product后面,ConreteProduct继承了Product的所有属性,并实现了Product中定义的抽象方法,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.这样一来,实质内容不同的ConcreteProduct就可以在形式上统一为Product,通过Creator提供给client来访问.
- 其次,当我们添加一个新的ConcreteCreator时,由于Creator所提供的接口不变,客户端程序不会有丝毫的改动,不会带来动一发而牵全身的灾难, 这就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个类是无论如何也做不到这点的. 优良的面向对象设计鼓励使用封装(encapsulation)和委托(delegation),而Factory Method模式就是使用了封装和委托的典型例子,这里封装是通过抽象创建器Creator来体现的,而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器ConcreteCreator来体现的.
该模式采用一个Shape(形状)的经典例子作为一个实例来展示如何实现Factory Method模式,先看下代码的结构图:
3.1 首先定义一个抽象类Shape,定义两个抽象的方法.
package com.andyidea.patterns.product;/** * Product: 需要创建的产品的抽象类. * @author Andy.Chen * */public abstract class Shape {public String name;public Shape(String aName){this.name = aName;}//绘画public abstract void draw();//擦除public abstract void erase();}
3.2 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法Circle中的源码如下:
package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.product.Shape;/** * 圆形子类(ConcreteProduct: Product的子类,一系列具体的产品.) * @author Andy.Chen * */public class Circle extends Shape{public Circle(String name) {super(name);}@Overridepublic void draw() {System.out.println("It will draw a Circle");}@Overridepublic void erase() {System.out.println("It will erase a Circle");}}
Square中的源码:package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.product.Shape;/** * 方形子类(ConcreteProduct: Product的子类,一系列具体的产品.) * @author Andy.Chen * */public class Square extends Shape{public Square(String name) {super(name);}@Overridepublic void draw() {System.out.println("It will draw a Square");}@Overridepublic void erase() {System.out.println("It will erase a Square");}}
3.3 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.package com.andyidea.patterns.creator;import com.andyidea.patterns.product.Shape;/** * Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method. * @author Andy.Chen * */public abstract class ShapeFactory {protected abstract Shape factoryMethod(String aName);public void anOperation(String aName){Shape s = factoryMethod(aName);System.out.println("The current shape is: " + s.name);s.draw();s.erase();}}
3.4 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法CircleFactory中的源码:
package com.andyidea.patterns.concretecreator;import com.andyidea.patterns.concreteproduct.Circle;import com.andyidea.patterns.creator.ShapeFactory;import com.andyidea.patterns.product.Shape;/** * ConcreteCreator: 具体的创建器,重写Creator中的Factory Method, * 返回ConcreteProduct类型的实例. * @author Andy.Chen * */public class CircleFactory extends ShapeFactory {@Overrideprotected Shape factoryMethod(String aName) {return new Circle(aName + " (created by CircleFactory)");}}
SquareFactory中的源码:package com.andyidea.patterns.concretecreator;import com.andyidea.patterns.concreteproduct.Square;import com.andyidea.patterns.creator.ShapeFactory;import com.andyidea.patterns.product.Shape;/** * ConcreteCreator: 具体的创建器,重写Creator中的Factory Method, * 返回ConcreteProduct类型的实例. * @author Andy.Chen * */public class SquareFactory extends ShapeFactory {@Overrideprotected Shape factoryMethod(String aName) {return new Square(aName + " (created by SquareFactory)");}}
3.5 测试类MainClient:这个客户端程序没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节(因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).package com.andyidea.patterns.client;import com.andyidea.patterns.concretecreator.CircleFactory;import com.andyidea.patterns.concretecreator.SquareFactory;import com.andyidea.patterns.creator.ShapeFactory;/** * 测试设计模式类 * @author Andy.Chen * */public class MainClient {public static void main(String[] args) {ShapeFactory sf1 = new CircleFactory();ShapeFactory sf2 = new SquareFactory();System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Factory Method Patterns." +"\n" +"-------------------------------");sf1.anOperation("Shape-Circle");sf2.anOperation("Shape-Square");}}
【4】程序运行结果如下:Welcome to Andy.Chen Blog!Factory Method Patterns.-------------------------------The current shape is: Shape-Circle (created by CircleFactory)It will draw a CircleIt will erase a CircleThe current shape is: Shape-Square (created by SquareFactory)It will draw a SquareIt will erase a Square
【5】总结:用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性。[原]设计模式之二 --- Singleton 模式
【1】基本概念
Singleton 是一种创建性模式,它用来确保只产生一个实例,并提供一个访问它的全局访问点。对一些类来说,保证只有一个实例是很重要的,比如有的时候,数据库连接或 Socket 连接要受到一定的限制,必须保持同一时间只能有一个连接的存在。
【2】简单分析
我们先来看一下该设计模式的UML结构图:
为了实现 Singleton 模式,我们需要的是一个静态的变量,能够在不创建对象的情况下记忆是否已经产生过实例了。静态变量或静态方法都可以在不产生具体实例的情况下直接调用,这样的变量或方法不会因为类的实例化而有所改变。在上面的模式结构图中的 uniqueInstance 就是这个独立的静态变量,它可以记忆对象是否已经实例化了,在静态方法 getInstance() 中对这个变量进行判断,若没有实例化过就产生一个新的对象,如果已经实例化了则不再产生新的对象,仍然返回以前产生的实例。
【3】如何用java语言来实现该设计模式:以下采用2种方法来实现该模式。
第一种方法:用静态方法实现 Singleton 这种方法是使用静态方法来监视实例的创建。为了防止创建一个以上的实例,我们把构造器声明为 private。这样可以防止客户端程序员通过除由我们提供的方法之外的任意方式来创建一个实例。如果不把构造器声明为private,编译器则会创建一个默认的public的构造器。
具体实现的代码如下:
package com.andyidea.patterns.singleton;public class Singleton {private static Singleton s;/** * 把构造函数设置为private */private Singleton(){}/** * 实例化对象的唯一接口 * @return */public static Singleton getInstance(){if(s == null){s = new Singleton();}return s;}}
测试类代码如下:package com.andyidea.patterns.client;import com.andyidea.patterns.singleton.Singleton;/** * 设计模式测试类 * @author Andy.Chen * */public class MainClient {public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Singleton Patterns." +"\n" +"-------------------------------"); if (s1==s2) System.out.println("s1 is the same instance with s2"); else System.out.println("s1 is not the same instance with s2");}}
程序运行的结果如下:Welcome to Andy.Chen Blog!Singleton Patterns.-------------------------------s1 is the same instance with s2
第二种方法:以静态变量为标志实现 Singleton 在类中嵌入一个静态变量做为标志,每次都在进入构造器的时候进行检查。问题在于构造器没有返回类型,如果确定创建一个实例成功与否.一个方法是调用一个函数来检查创建是否成功,然后简单的返回一个来自静态变量的值,但是这样做是不优雅的,而且容易发生错误。比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类,这个类仅仅是调用父类方法,好处是用了自己创建的异常类型,错误信息更加清晰。具体实现的代码如下:
package com.andyidea.patterns.singleton;public class Singleton { static boolean instance_flag = false; public Singleton() { if (instance_flag) throw new SingletonException("Only one instance allowed"); else instance_flag = true; }}
异常类代码如下:package com.andyidea.patterns.singleton;/** * 异常类 * @author Andy.Chen * */public class SingletonException extends RuntimeException{public SingletonException(String exception){super(exception);}}
测试类代码如下:package com.andyidea.patterns.client;import com.andyidea.patterns.singleton.Singleton;import com.andyidea.patterns.singleton.SingletonException;/** * 设计模式测试类 * @author Andy.Chen * */public class MainClient {public static void main(String[] args) { System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Singleton Patterns." +"\n" +"-------------------------------"); Singleton s1, s2; // create one incetance--this should always work System.out.println("Creating one instance"); try { s1 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); } // try to create another incetanced System.out.println("Creating two instance"); try { s2 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); }}}
运行结果如下:Welcome to Andy.Chen Blog!Singleton Patterns.-------------------------------Creating one instanceCreating two instanceOnly one instance allowed
从输出的结果可以看出,第一个实例可以顺利创建,创建第二个实例的时候抛出了我们自定义的异常信息。[原]设计模式之三 --- 策略模式(Strategy Pattern)
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
【1】基本概念
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
【2】简单分析
我们先来看一下该设计模式的UML结构图
上图是Strategy 模式的结构图,让我们可以进行更方便的描述:
- Strategy: 定义所有支持的算法的公共接口抽象类.
- ConcreteStrategy: 封装了具体的算法或行为,继承于Strategy
- Context: 用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
package com.andyidea.patterns.strategy;/** * 抽象算法类 * @author Andy.Chen * */public abstract class Strategy {//算法方法public abstract void AlgorithmInterface();}
3.2 定义具体ConcreteStrategy,分别继承Strategypackage com.andyidea.patterns.concretestrategy;import com.andyidea.patterns.strategy.Strategy;/** * 具体算法A * @author Andy.Chen * */public class ConcreteStrategyA extends Strategy{@Overridepublic void AlgorithmInterface() {System.out.println("算法A实现!");}}
ConcreteStrategyB代码:package com.andyidea.patterns.concretestrategy;import com.andyidea.patterns.strategy.Strategy;/** * 具体算法B * @author Andy.Chen * */public class ConcreteStrategyB extends Strategy{@Overridepublic void AlgorithmInterface() {System.out.println("算法B实现!");}}
ConcreteStrategyC代码:package com.andyidea.patterns.concretestrategy;import com.andyidea.patterns.strategy.Strategy;/** * 具体算法C * @author Andy.Chen * */public class ConcreteStrategyC extends Strategy{@Overridepublic void AlgorithmInterface() {System.out.println("算法C实现!");}}
3.3定义Context类,维护队Strategy对象的引用。package com.andyidea.patterns.context;import com.andyidea.patterns.strategy.Strategy;/** * 上下文 * @author Andy.Chen * */public class Context {private Strategy mStrategy;public Context(Strategy strategy){this.mStrategy = strategy;}/** * 上下文接口 */public void ContextInterface(){mStrategy.AlgorithmInterface();}}
3.4 客户端测试类 MainClient.java 源码package com.andyidea.patterns.client;import com.andyidea.patterns.concretestrategy.ConcreteStrategyA;import com.andyidea.patterns.concretestrategy.ConcreteStrategyB;import com.andyidea.patterns.concretestrategy.ConcreteStrategyC;import com.andyidea.patterns.context.Context;/** * 客户端测试类 * @author Andy.Chen * */public class MainClient {private static Context context; public static void main(String[] args) { System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Strategy Patterns." +"\n" +"----------------------------"); context = new Context(new ConcreteStrategyA());context.ContextInterface();context = new Context(new ConcreteStrategyB());context.ContextInterface();context = new Context(new ConcreteStrategyC());context.ContextInterface();}}
【4】程序运行结果如下:Welcome to Andy.Chen Blog!Strategy Patterns.----------------------------算法A实现!算法B实现!算法C实现!
从上面可以看到,策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。[原]设计模式之四 --- 建造(Builder)模式
【1】基本概念
建造(Builder)模式是一种对象构建的设计模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
【2】简单分析
我们先来看一下该设计模式的UML结构图
上图是Strategy 模式的结构图,让我们可以进行更方便的描述:
- Builder
- 为创建一个Product对象的各个部件指定抽象接口。
- ConcreteBuilder
- 实现Builder的接口以构造和装配该产品的各个部件。
- 定义并明确它所创建的表示。
- 提供一个检索产品的接口
- Director
- 构造一个使用Builder接口的对象。
- Product
- 表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。
- 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
- 当创建复杂对象(这些对象内部构建间的建造顺序通常是稳定的)的算法应该独立于该对象的组成部分以及它们的装配方式时;
- 当构造过程必须允许被构造的对象有不同的表示时。
下面以一个简单的例子来展示该模式,先看下代码结构图:
3.1 先创建一个Product类--产品类:
package com.andyidea.patterns.product;/** * Product--产品类 * @author Andy.Chen * */public class Pizza { private String dough; private String sauce; private String topping; public void setDough(String dough) {this.dough = dough;}public void setSauce(String sauce) {this.sauce = sauce;}public void setTopping(String topping) {this.topping = topping;}}
3.2 创建抽象建造者类:PizzaBuilder.javapackage com.andyidea.patterns.builder;import com.andyidea.patterns.product.Pizza;/** * Builder类--抽象建造者类 * @author Andy.Chen * */public abstract class PizzaBuilder { protected Pizza pizza; public Pizza getPizza() { return pizza; } public void createNewPizzaProduct() { pizza = new Pizza(); } public abstract void buildDough(); public abstract void buildSauce(); public abstract void buildTopping();}
3.3 创建具体建造者类HawaiianPizzaBuilder.java源码:
package com.andyidea.patterns.concretebuilder;import com.andyidea.patterns.builder.PizzaBuilder;/** * ConcreteBuilder类--具体建造者类 * @author Andy.Chen * */public class HawaiianPizzaBuilder extends PizzaBuilder {@Overridepublic void buildDough() {System.out.println("Hawaiian-Dough");pizza.setDough("Hawaiian-Dough");}@Overridepublic void buildSauce() {System.out.println("Hawaiian-Sauce"); pizza.setSauce("Hawaiian-Sauce");}@Overridepublic void buildTopping() {System.out.println("Hawaiian-Topping"); pizza.setTopping("Hawaiian-Topping");}}
SpicyPizzaBuilder.java源码:package com.andyidea.patterns.concretebuilder;import com.andyidea.patterns.builder.PizzaBuilder;/** * ConcreteBuilder类--具体建造者类 * @author Andy.Chen * */public class SpicyPizzaBuilder extends PizzaBuilder {@Overridepublic void buildDough() {System.out.println("Spicy-Dough");pizza.setDough("Spicy-Dough");}@Overridepublic void buildSauce() {System.out.println("Spicy-Sauce");pizza.setSauce("Spicy-Sauce");}@Overridepublic void buildTopping() {System.out.println("Spicy-Topping");pizza.setTopping("Spicy-Topping");}}
3.4 创建指挥者(Director)类:Waiter.javapackage com.andyidea.patterns.director;import com.andyidea.patterns.builder.PizzaBuilder;import com.andyidea.patterns.product.Pizza;/** * Director类--指挥者类 * @author Andy.Chen * */public class Waiter { private PizzaBuilder pizzaBuilder; public void setPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; }public Pizza getPizza() { return pizzaBuilder.getPizza(); } public void constructPizza() { pizzaBuilder.createNewPizzaProduct(); pizzaBuilder.buildDough(); pizzaBuilder.buildSauce(); pizzaBuilder.buildTopping();}}
3.5 测试类:BuilderClient.javapackage com.andyidea.patterns.client;import com.andyidea.patterns.builder.PizzaBuilder;import com.andyidea.patterns.concretebuilder.HawaiianPizzaBuilder;import com.andyidea.patterns.concretebuilder.SpicyPizzaBuilder;import com.andyidea.patterns.director.Waiter;import com.andyidea.patterns.product.Pizza;public class BuilderClient {public static void main(String[] args) {System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Builder Patterns." +"\n"); Waiter waiter = new Waiter(); PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder(); System.out.println("------------HawaiianPizza------------"); waiter.setPizzaBuilder(hawaiian_pizzabuilder); waiter.constructPizza(); System.out.println("------------SpicyPizza------------"); waiter.setPizzaBuilder(spicy_pizzabuilder); waiter.constructPizza(); Pizza pizza = waiter.getPizza();}}
【4】程序运行结果:Welcome to Andy.Chen Blog!Builder Patterns.------------HawaiianPizza------------Hawaiian-DoughHawaiian-SauceHawaiian-Topping------------SpicyPizza------------Spicy-DoughSpicy-SauceSpicy-Topping
通过上面我们可以看到:建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。[原]设计模式之五 --- 代理(Proxy)模式
【1】基本概念
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。代理者可以作任何东西的接口:网络连接、内存中的大物件、档案或其它昂贵或无法复制的资源。
【2】简单分析
我们先看一下该设计模式的UML结构图:
我们通过上面的结构图可以看到:
Subject类:定义了RealSubject和Proxy的共用接口,这样就可以在任何使用RealSubject的地方都可以用Proxy。
RealSubject类:定义Proxy所代表的真实实体。
Proxy类:保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。
【3】如何用java语言实现该模式
我们先看下代码的结构图,该模式的代码实现比较简单:
3.1 Subject类:Image.java
package com.andyidea.patterns.subject;/** * Subject类 * @author Andy.Chen * */public abstract class Image {public abstract void displayImage();}
3.2 RealSubject类:RealImage.javapackage com.andyidea.patterns.realsubject;import com.andyidea.patterns.subject.Image;/** * RealSubject类 * @author Andy.Chen * */public class RealImage extends Image { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } private void loadImageFromDisk() { System.out.println("Loading " + filename); }@Overridepublic void displayImage() {System.out.println("Displaying " + filename); }}
3.3 Proxy类:ProxyImage.javapackage com.andyidea.patterns.proxy;import com.andyidea.patterns.realsubject.RealImage;import com.andyidea.patterns.subject.Image;/** * Proxy类 * @author Andy.Chen * */public class ProxyImage extends Image { private String filename; private Image image; public ProxyImage(String filename) { this.filename = filename; }@Overridepublic void displayImage() { if(image == null) image = new RealImage(filename); image.displayImage();}}
3.4 客户端测试类:ProxyClient.javapackage com.andyidea.patterns.client;import com.andyidea.patterns.proxy.ProxyImage;import com.andyidea.patterns.subject.Image;/** * 代理模式客户端测试类 * @author Andy.Chen * */public class ProxyClient {public static void main(String[] args) {System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Proxy Patterns." +"\n" +"-------------------------------");Image mImage1 = new ProxyImage("Andy.Photo1");Image mImage2 = new ProxyImage("Andy.Photo2");mImage1.displayImage();mImage2.displayImage();}}
【4】程序运行结果:Welcome to Andy.Chen Blog!Proxy Patterns.-------------------------------Loading Andy.Photo1Displaying Andy.Photo1Loading Andy.Photo2Displaying Andy.Photo2
总结下代理模式的应用场合:第一:远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。
第二:虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。
第三:安全代理,用来控制真实对象访问时的权限。
第四:智能引用,是指当调用真实对象时,代理处理另外一些事。本文的例子就是采用了这一点。
[原]设计模式之六 --- 抽象工厂模式(Abstract Factory) 每个模式都是针对一定问题的解决方案。抽象工厂模式面对的问题是多产品等级结构的系统设计。
在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族和产品等级。
产品族:是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的CPU和ADM芯片的主板,组成一个家族。Intel的CPU和Intel芯片的主板,又组成一个家族。而这两个家族都来自于两个产品等级:CPU,主板。一个等级结构是由相同的结构的产品组成,示意图如下:
理解这个产品结构是理解抽象工厂模式的关键所在,从上图可以看出,抽象工厂模式的每个工厂创造出来的都是一族产品,而不是一个或者一组。组是可以随意组合的!其实工厂方法模式和抽象工厂模式就这点点差别。
【1】基本概念
抽象工厂模式是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体类。
【2】简单分析
我们先看一下抽象工厂模式的UML结构图:
上图是 Abstract Factory 模式结构图,让我们可以进行更加方便的描述:
- AbstractProduct: 抽象产品,它们都有可能有两种不同的实现。
- ConcreteProduct:包括ProductA和ProductB, 对两个抽象产品的具体分类的实现。
- AbstractFactory: 抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。
- ConcreteFactory: 包括ConcreteFactoryA和ConcreteFactoryB,具体的工厂,创建具有特定实现的产品对象。
背景:用一个分别对不同数据库(Oracle 或 SQL Server)中表( User 和 Department )的操作的实例来展示该设计模式。先看下代码的结构图:
3.1 首先定义两个抽象的产品类:IUser.java 和 IDepartment.java。
IUser.java的源码:
package com.andyidea.patterns.abstractproduct;/** * 抽象产品角色:User接口 * @author Andy.Chen * */public interface IUser {}
IDepartment.java源码:package com.andyidea.patterns.abstractproduct;/** * 抽象产品角色:Department接口 * @author Andy.Chen * */public interface IDepartment {}
3.2 定义抽象工厂类:IDBFactory.javapackage com.andyidea.patterns.abstractfactory;import com.andyidea.patterns.abstractproduct.IDepartment;import com.andyidea.patterns.abstractproduct.IUser;/** * 抽象工厂角色:工厂接口 * @author Andy.Chen * */public interface IDBFactory {public IUser createUser();public IDepartment createDepartment();}
3.3创建具体产品角色类:OracleOfUser.java;OracleOfDepartment.java;SQLServerOfUser.java;SQLServerOfDepartment.java。分别继承IUser.java和IDepartment.java。OracleOfUser.java源码:
package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.abstractproduct.IUser;/** * 具体产品角色:Oracle中的User * @author Andy.Chen * */public class OracleOfUser implements IUser{public OracleOfUser(){System.out.println("Oracle工厂:在Oracle中操作User表.");}}
OracleOfDepartment.java源码:package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.abstractproduct.IDepartment;/** * 具体产品角色:Oracle中的Department * @author Andy.Chen * */public class OracleOfDepartment implements IDepartment{public OracleOfDepartment(){System.out.println("Oracle工厂:在Oracle中操作Department表.");}}
SQLServerOfUser.java源码:package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.abstractproduct.IUser;/** * 具体产品角色:SQL Server中的User * @author Andy.Chen * */public class SQLServerOfUser implements IUser{public SQLServerOfUser(){System.out.println("SQL Server工厂:在SQL Server中操作User表.");}}
SQLServerOfDepartment.java源码:package com.andyidea.patterns.concreteproduct;import com.andyidea.patterns.abstractproduct.IDepartment;/** * 具体产品角色:SQL Server中的Department * @author Andy.Chen * */public class SQLServerOfDepartment implements IDepartment{public SQLServerOfDepartment(){System.out.println("SQL Server工厂:在SQL Server中操作Department表.");}}
3.4 创建具体工厂类:OracleFactory.java和SQLServerFactory.java。OracleFactory.java源码:
package com.andyidea.patterns.concretefactory;import com.andyidea.patterns.abstractfactory.IDBFactory;import com.andyidea.patterns.abstractproduct.IDepartment;import com.andyidea.patterns.abstractproduct.IUser;import com.andyidea.patterns.concreteproduct.OracleOfDepartment;import com.andyidea.patterns.concreteproduct.OracleOfUser;/** * 具体工厂角色:Oracle工厂 * @author Andy.Chen * */public class OracleFactory implements IDBFactory{@Overridepublic IUser createUser() {return new OracleOfUser();}@Overridepublic IDepartment createDepartment() {return new OracleOfDepartment();}}
SQLServerFactory.java源码:package com.andyidea.patterns.concretefactory;import com.andyidea.patterns.abstractfactory.IDBFactory;import com.andyidea.patterns.abstractproduct.IDepartment;import com.andyidea.patterns.abstractproduct.IUser;import com.andyidea.patterns.concreteproduct.SQLServerOfDepartment;import com.andyidea.patterns.concreteproduct.SQLServerOfUser;/** * 具体工厂角色:SQL Server工厂 * @author Andy.Chen * */public class SQLServerFactory implements IDBFactory{@Overridepublic IUser createUser() {return new SQLServerOfUser();}@Overridepublic IDepartment createDepartment() {return new SQLServerOfDepartment();}}
3.5 客户端测试类:AbstractFactoryClient.javapackage com.andyidea.patterns.client;import com.andyidea.patterns.abstractproduct.IDepartment;import com.andyidea.patterns.abstractproduct.IUser;import com.andyidea.patterns.concretefactory.OracleFactory;import com.andyidea.patterns.concretefactory.SQLServerFactory;/** * 抽象工厂测试类 * @author Andy.Chen * */public class AbstractFactoryClient {public static void main(String[] args) {System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Abstract Factory Patterns." +"\n" +"-------------------------------");IUser oracleUser,sqlUser;IDepartment oracleDept,sqlDept;OracleFactory of = new OracleFactory();SQLServerFactory sf = new SQLServerFactory();oracleUser = of.createUser();oracleDept = of.createDepartment();sqlUser = sf.createUser();sqlDept = sf.createDepartment();}}
【4】程序运行结果:Welcome to Andy.Chen Blog!Abstract Factory Patterns.-------------------------------Oracle工厂:在Oracle中操作User表.Oracle工厂:在Oracle中操作Department表.SQL Server工厂:在SQL Server中操作User表.SQL Server工厂:在SQL Server中操作Department表.
【5】总结抽象工厂模式优点:
第一,易于交换产品系列,由于具体工厂类,例如IDBFactory factory = new OracleFactory(),在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它之需要改变具体工厂即可使用不同的产品配置。
第二,它让具体的创建实例与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
[原]java同步机制:synchronized
synchronized 是java语言关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
本文直接以代码的形式来展示 synchronized 关键字的使用:
【1】synchronized Demo1:
package com.andyidea.demo;/** * 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时, * 一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码 * 块以后才能执行该代码块。 * @author Andy.Chen * */public class Thread01 implements Runnable {@Overridepublic void run() {synchronized (this) {for(int i=0;i<3;i++){System.out.println(Thread.currentThread().getName()+" synchronized loop "+i);}}}public static void main(String[] args) {Thread01 t01 = new Thread01();System.out.println("Welcome to Andy.Chen Blog! \n" +"synchronized 关键字使用 \n" +"--------------------------");Thread ta = new Thread(t01,"A");Thread tb = new Thread(t01,"B");ta.start();tb.start();}}
运行结果如下:
Welcome to Andy.Chen Blog! synchronized 关键字使用 --------------------------B synchronized loop 0B synchronized loop 1B synchronized loop 2A synchronized loop 0A synchronized loop 1A synchronized loop 2
【2】synchronized Demo2:
package com.andyidea.demo;/** * 当一个线程访问object的一个synchronized(this)同步代码块时, * 另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 * @author Andy.Chen * */public class Thread02 {public void method01(){synchronized (this) {int i=0;while(i++ < 3){System.out.println(Thread.currentThread().getName() +":"+ i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}public void method02(){//第1种方式:当一个线程访问object的一个synchronized(this)同步代码块时,//另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。//int j=0;//while(j++ < 3){//System.out.println(Thread.currentThread().getName() +":"+ j);//try {//Thread.sleep(1000);//} catch (InterruptedException e) {//e.printStackTrace();//}//}//第2种方式:当一个线程访问object的一个synchronized(this)同步代码块时,//其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。synchronized (this) {int j=0;while(j++ < 3){System.out.println(Thread.currentThread().getName() +":"+ j);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}/** * 当一个线程访问object的一个synchronized(this)同步代码块时, * 它就获得了这个object的对象锁。 * 结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。 */public synchronized void method3(){int k=0;while(k++ < 3){System.out.println(Thread.currentThread().getName() +":"+ k);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {final Thread02 t02 = new Thread02();System.out.println("Welcome to Andy.Chen Blog! \n" +"synchronized 关键字使用 \n" +"--------------------------");Thread t02A = new Thread(new Runnable() {@Overridepublic void run() {t02.method01();}},"A");Thread t02B = new Thread(new Runnable() {@Overridepublic void run() {t02.method02();}},"B");Thread t02C = new Thread(new Runnable() {@Overridepublic void run() {t02.method3();}},"C");t02A.start();t02B.start();t02C.start();}}
运行结果如下:Welcome to Andy.Chen Blog! synchronized 关键字使用 --------------------------B:1B:2B:3C:1C:2C:3A:1A:2A:3
package com.andyidea.demo;/** * synchronized对象锁 * @author Andy.Chen * */public class Thread03 {class InnerObject{/** * 内部类方法1 */private void innerMethod01(){int i=0;while(i++ < 3){System.out.println(Thread.currentThread().getName() +":"+ i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}/** * 内部类方法2 */private void innerMethod02(){int j=0;while(j++ < 3){System.out.println(Thread.currentThread().getName() +":"+ j);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}/** * 外部类方法1 * @param innerObj */private void outerMethod01(InnerObject innerObj){synchronized (innerObj) {innerObj.innerMethod01();}}/** * 外部类方法2 * @param innerObj */private void outerMethod02(InnerObject innerObj){innerObj.innerMethod02();}public static void main(String[] args) {final Thread03 t03 = new Thread03();final InnerObject innerObj = t03.new InnerObject();System.out.println("Welcome to Andy.Chen Blog! \n" +"synchronized 关键字使用 \n" +"--------------------------");Thread t03A = new Thread(new Runnable() {@Overridepublic void run() {t03.outerMethod01(innerObj);}},"A");Thread t03B = new Thread(new Runnable() {@Overridepublic void run() {t03.outerMethod02(innerObj);}},"B");t03A.start();t03B.start();}}
运行结果如下:Welcome to Andy.Chen Blog! synchronized 关键字使用 --------------------------A:1B:1B:2A:2B:3A:3
总结:
1. synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
2. synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
对synchronized(this)的一些理解
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
[原]设计模式之七 --- 观察者模式(Observer)
【1】基本概念
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。观察者模式又叫发布-订阅(Publish/Subscribe)模式。
【2】简单分析
我们先来看一下该设计模式的UML结构图
上图是Observer 模式的结构图,让我们可以进行更方便的描述:
Subject类,它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察着。抽象主题提供一个接口,可以增加和删除观察着对象。
Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
ConcreteSubject类,具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。
【3】如何用java语言来实现该模式
下面以一个简单的例子来展示该模式,先看下代码结构图:
该例子是这样的情景:例如:老师有电话号码,学生需要知道老师的电话号码以便于在合适的时候拨打,在这样的组合中,老师就是一个被观察者(Subject),学生就是需要知道信息的观察者,当老师的电话号码发生改变时,学生得到通知,并更新相应的电话记录。
3.1 先创建一个Subject类:
package com.andyidea.patterns.subject;import com.andyidea.patterns.observer.Observer;/** * Subject(目标,Subject): * 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 * 提供注册和删除观察者对象的接口。 * @author Andy.Chen * */public interface Subject {public void attach(Observer mObserver);public void detach(Observer mObserver);public void notice();}
3.2 创建Observer类:package com.andyidea.patterns.observer;/** * Observer(观察者,Observer): * 为那些在目标发生改变时需要获得通知的对象定义一个更新接口。 * @author Andy.Chen * */public interface Observer {public void update();}
3.3 创建ConcreteSubject类:package com.andyidea.patterns.concretesubject;import java.util.Vector;import com.andyidea.patterns.observer.Observer;import com.andyidea.patterns.subject.Subject;/** * ConcreteSubject(具体目标,Teacher) * 将有关状态存入各ConcreteObserve对象。 * 当他的状态发生改变时,向他的各个观察者发出通知。 * @author Andy.Chen * */public class Teacher implements Subject{private String phone;private Vector students;public Teacher(){phone = "";students = new Vector();}@Overridepublic void attach(Observer mObserver) {students.add(mObserver);}@Overridepublic void detach(Observer mObserver) {students.remove(mObserver);}@Overridepublic void notice() {for(int i=0;i<students.size();i++){((Observer)students.get(i)).update();}}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;notice();}}
3.4 创建ConcreteObserver类:package com.andyidea.patterns.concreteobserver;import com.andyidea.patterns.concretesubject.Teacher;import com.andyidea.patterns.observer.Observer;/** * ConcreteObserver(具体观察者, Student): * 维护一个指向ConcreteSubject对象的引用。 * 存储有关状态,这些状态应与目标的状态保持一致。 * 实现Observer的更新接口以使自身状态与目标的状态保持一致。 * @author Andy.Chen * */public class Student implements Observer{private String name; private String phone; private Teacher mTeacher; public Student(String name,Teacher t){ this.name = name; mTeacher = t; } public void show(){ System.out.println("Name:"+name+"\nTeacher'sphone:" + phone); }@Overridepublic void update() {phone = mTeacher.getPhone();}}
3.5 客户端测试类:ObserverClient.java.package com.andyidea.patterns.client;import java.util.Vector;import com.andyidea.patterns.concreteobserver.Student;import com.andyidea.patterns.concretesubject.Teacher;/** * 观察者(Observer)模式测试类 * @author Andy.Chen * */public class ObserverClient {public static void main(String[] args) { Vector students = new Vector(); Teacher t = new Teacher(); for(int i= 0 ;i<10;i++){ Student st = new Student("Andy.Chen"+i,t); students.add(st); t.attach(st); } System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Observer Patterns." +"\n" +"-------------------------------"); t.setPhone("12345678"); for(int i=0;i<3;i++) ((Student)students.get(i)).show(); t.setPhone("87654321"); for(int i=0;i<3;i++) ((Student)students.get(i)).show();}}
【4】程序运行结果如下:Welcome to Andy.Chen Blog!Observer Patterns.-------------------------------Name:Andy.Chen0Teacher'sphone:12345678Name:Andy.Chen1Teacher'sphone:12345678Name:Andy.Chen2Teacher'sphone:12345678Name:Andy.Chen0Teacher'sphone:87654321Name:Andy.Chen1Teacher'sphone:87654321Name:Andy.Chen2Teacher'sphone:87654321
总结:观察者模式何时适用?1.当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中可以使他们各自独立地改变和复用。
2.当对一个对象的改变需要同时改变其它对象,而不知道具体由多少对象有待改变。
3.当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的。让耦合的双方都依赖于抽象,而不是依赖于具体。
[原]Android开发中各种问题集锦【11-20】
【11】Android导入项目时出现:Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead. Please use Android Tools > Fix Project Properties. 的解决方法?
1. 按提示在工程文件上 右键 -> Android Tools -> Fix Project Properties ,该方法无效。
2. 手动打开Project Properties -> java Compiler -> 选上Enable project specific settings -> 再选择 Compiler Compliance Leave(选择任意一个非默认的值) -> OK
3. 重复第2步,将Compiler Compliance Leave选为正确的值(该值一般是当前安装的JDK版本值,如 jdk 5 对应 1.5 ,jdk 6 对应 1.6),OK。
【12】 在ADT 16.0 中,定义一个ImageVIew的时候 总是提示这个[Accessibility] Missing contentDescription attribute on image警告,虽说可以不理 但总是感觉怪怪的。其实这是ADT 16.0的新特性,在一些没有文本显示的控件里,如imageView和imageButton等,ADT会提示你定义一个android:contentDescription属性,用来描述这个控件的作用。
【13】如何检测Android真机摄像头硬件
如果应用程序未利用manifest声明对摄像头需求进行特别指明,则应该在运行时检查一下摄像头是否可用。可用 PackageManager.hasSystemFeature() 方法来进行这种检查,代码示例如下:
/** 检查设备是否提供摄像头 */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // 摄像头存在 return true; } else { // 摄像头不存在 return false; } }
Android设备可能拥有多个摄像头,比如向后的摄像头用于拍照、向前的摄像头用于摄像。Android 2.3 (API Level 9)以上版本允许利用 Camera.getNumberOfCameras() 方法来检查设备可用摄像头的数量。【14】Java中异常类体系
为了方便对于这些可传递对象的管理,Java API中专门设计了java.lang.Throwable类,只有该类子类的对象才可以在系统的异常传递体系中进行。该类的两个子类分别是:
1、Error类
该类代表错误,指程序无法恢复的异常情况。对于所有错误类型以及其子类,都不要求程序进行处理。常见的Error类例如内存溢出StackOverflowError等。
2、Exception类
该类代表异常,指程序有可能恢复的异常情况。该类就是整个Java语言异常类体系中的父类。使用该类,可以代表所有异常的情况。
在Java API中,声明了几百个Exception的子类分别来代表各种各样的常见异常情况,这些类根据需要代表的情况位于不同的包中,这些类的类名均以Exception作为类名的后缀。如果遇到的异常情况,Java API中没有对应的异常类进行代表,也可以声明新的异常类来代表特定的情况。
在这些异常类中,根据是否是程序自身导致的异常,将所有的异常类分为两种:
1、RuntimeException及其所有子类
该类异常属于程序运行时异常,也就是由于程序自身的问题导致产生的异常,例如数组下标越界异常ArrayIndexOutOfBoundsException等。
该类异常在语法上不强制程序员必须处理,即使不处理这样的异常也不会出现语法错误。
2、其它Exception子类
该类异常属于程序外部的问题引起的异常,也就是由于程序运行时某些外部问题导致产生的异常,例如文件不存在异常FileNotFoundException等。
该类异常在语法上强制程序员必须进行处理,如果不进行处理则会出现语法错误。
【15】 Android中引入第三方jar包时,运行程序时提示java.lang.NoClassDefFoundError异常的解决方法。
这个问题个人觉得很怪异,引用的包中就有某个对象类,却报了这个错误,以下是个人的解决方法:
1、在工程下新建lib文件夹,将需要的第三方包拷贝进来。
2、将引用的第三方包,添加进工作的build path。
3、(关键的一步)将lib设为源文件夹。如果不设置,则程序编译可以通过,但运行的时候,会报:java.lang.NoClassDefFoundError
【16】 未完待续...
[原]全排列算法
全排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。
当m=n时所有的排列情况叫全排列。
该算法源码如下:
package com.andyidea.algorithms;/** * 全排列算法 * @author Andy.Chen * */public class PermutationSorter<T> {/** * 全排列输出 * @param array 要输出的字符数组 * @param from 输出字符数组的起始位置 * @param len 输出字符数组的长度 */public final void permutation(T[] array, int from, int len){int i;if(from < len-1){permutation(array, from+1, len);for(i=from+1;i<len;i++){swap(array,from,i);permutation(array, from+1, len);swap(array,from,i);}}else{printResult(array);}}/** * 交换算法 * @param array * @param from * @param to */public final void swap(T[] array,int from,int to){T tmp = array[from];array[from] = array[to];array[to] = tmp;}/** * 打印输出全排列结果 * @param array */public void printResult(T[] array) {for(int j = 0; j < array.length; j++){System.out.print(array[j]);}System.out.println();}}
[原]Android中如何使用ViewPager实现类似laucher左右拖动效果
现在很多Android应用在首次安装完都会有指引如何使用该应用的某些功能的指引界面,这样会获得很好的用户体验,能够帮助用户更好使用应用的某些功能。其实该功能和Android主界面的 luncher 的功能完全一样的效果,可以实现左右拖动。
下面结合 ViewPager 的实例来展示如何实现该功能,先看下该Demo的结构图:
注:ViewPager类是实现左右两个屏幕平滑地切换的一个类,是由Google 提供的, 使用ViewPager首先需要引入android-support-v4.jar这个jar包。其中工程项目中的 libs 文件夹下存放着 android-support-v4.jar这个jar包。drawable文件夹下包含有图片资源文件。
以下是工程中各个文件的源码:
main.xml源码:
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <android.support.v4.view.ViewPager android:id="@+id/guidePages" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <LinearLayout android:id="@+id/viewGroup" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="30dp" android:gravity="center_horizontal" android:orientation="horizontal" > </LinearLayout> </RelativeLayout></FrameLayout>
item01.xml源码:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/feature_guide_0" > </ImageView> </LinearLayout>
其中item02.xml,item03.xml,item04.xml布局文件的源码和item01.xml布局文件一样,只是 ImageView 中的 android:background 属性的背景图片不同而已。GuideViewDemoActivity.java 源码:
package com.andyidea.guidedemo;import java.util.ArrayList;import android.app.Activity;import android.os.Bundle;import android.os.Parcelable;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.support.v4.view.ViewPager.OnPageChangeListener;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;import android.view.Window;import android.widget.ImageView;public class GuideViewDemoActivity extends Activity { private ViewPager viewPager; private ArrayList<View> pageViews; private ViewGroup main, group; private ImageView imageView; private ImageView[] imageViews; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); LayoutInflater inflater = getLayoutInflater(); pageViews = new ArrayList<View>(); pageViews.add(inflater.inflate(R.layout.item01, null)); pageViews.add(inflater.inflate(R.layout.item02, null)); pageViews.add(inflater.inflate(R.layout.item03, null)); pageViews.add(inflater.inflate(R.layout.item04, null)); imageViews = new ImageView[pageViews.size()]; main = (ViewGroup)inflater.inflate(R.layout.main, null); // group是R.layou.main中的负责包裹小圆点的LinearLayout. group = (ViewGroup)main.findViewById(R.id.viewGroup); viewPager = (ViewPager)main.findViewById(R.id.guidePages); for (int i = 0; i < pageViews.size(); i++) { imageView = new ImageView(GuideViewDemoActivity.this); imageView.setLayoutParams(new LayoutParams(20,20)); imageView.setPadding(20, 0, 20, 0); imageViews[i] = imageView; if (i == 0) { //默认选中第一张图片 imageViews[i].setBackgroundResource(R.drawable.page_indicator_focused); } else { imageViews[i].setBackgroundResource(R.drawable.page_indicator); } group.addView(imageViews[i]); } setContentView(main); viewPager.setAdapter(new GuidePageAdapter()); viewPager.setOnPageChangeListener(new GuidePageChangeListener()); } /** 指引页面Adapter */ class GuidePageAdapter extends PagerAdapter { @Override public int getCount() { return pageViews.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public int getItemPosition(Object object) { // TODO Auto-generated method stub return super.getItemPosition(object); } @Override public void destroyItem(View arg0, int arg1, Object arg2) { // TODO Auto-generated method stub ((ViewPager) arg0).removeView(pageViews.get(arg1)); } @Override public Object instantiateItem(View arg0, int arg1) { // TODO Auto-generated method stub ((ViewPager) arg0).addView(pageViews.get(arg1)); return pageViews.get(arg1); } @Override public void restoreState(Parcelable arg0, ClassLoader arg1) { // TODO Auto-generated method stub } @Override public Parcelable saveState() { // TODO Auto-generated method stub return null; } @Override public void startUpdate(View arg0) { // TODO Auto-generated method stub } @Override public void finishUpdate(View arg0) { // TODO Auto-generated method stub } } /** 指引页面改监听器 */ class GuidePageChangeListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageSelected(int arg0) { for (int i = 0; i < imageViews.length; i++) { imageViews[arg0] .setBackgroundResource(R.drawable.page_indicator_focused); if (arg0 != i) { imageViews[i] .setBackgroundResource(R.drawable.page_indicator); } } } } }
运行上面的程序,效果截图如下:
至此大功告成,已经采用ViewPager组件实现了左右滑动(拖动)效果。本文为[Andy.Chen]原创,转载请注明出处,谢谢。
该实例的源码可以在:http://download.csdn.net/detail/cjjky/4200425 点击打开链接 下载。
[原]设计模式之八 --- 装饰模式(Decorator)
【1】基本概念
装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
【2】简单分析
我们先来看下该设计模式的UML结构图
上图是Decorator 模式的结构图,让我们可以进行更方便的描述:
Component是定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。
ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。
【3】如何用Java语音来实现该设计模式
假设情景:某人装扮自己形象,穿衣服,裤子,鞋子,戴帽子等来把自己给包装起来,需要把所需的功能按正确的顺序串联起来进行控制,我们应该如何设计才能做到呢?如下,先看下代码结构图:
3.1 先创建一个接口类:Component.java
package com.andyidea.patterns.component;public interface Component {void show();}
3.2 创建一个具体的 ConcreteComponent 来实现 Component 接口:Person.javapackage com.andyidea.patterns.concretecomponent;import com.andyidea.patterns.component.Component;public class Person implements Component{private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(String name){this.name = name;}@Overridepublic void show() {System.out.println("装扮的" + name);}}
3.3 创建装饰类 Decorator 实现 Component 接口package com.andyidea.patterns.decorator;import com.andyidea.patterns.component.Component;public class Decorator implements Component{private Component mComponent;public void decoratorObj(Component component){mComponent = component;}@Overridepublic void show() {if(mComponent != null){mComponent.show();}}}
3.4 分别创建具体的装饰类:Jeans.java , Pelisse.java, Sandal.java ...等等,分别继承 Decorator.java 类:package com.andyidea.patterns.concretedecorator;import com.andyidea.patterns.decorator.Decorator;/** 牛仔裤 */public class Jeans extends Decorator {@Overridepublic void show(){System.out.println("穿牛仔裤");super.show();}}
其余类类似,在这里就省略了。3.5 客户端测试类:
package com.andyidea.patterns;import com.andyidea.patterns.concretecomponent.Person;import com.andyidea.patterns.concretedecorator.Jeans;import com.andyidea.patterns.concretedecorator.Sandal;import com.andyidea.patterns.concretedecorator.TShirt;/** * 装饰模式测试客户端 * @author Andy.Chen * */public class DecoratorClient {public static void main(String[] args) {System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Decorator Patterns." +"\n");Person mPerson = new Person("Andy");Sandal mSandal = new Sandal();Jeans mJeans = new Jeans();TShirt mShirt = new TShirt();mShirt.decoratorObj(mPerson);mJeans.decoratorObj(mShirt);mSandal.decoratorObj(mJeans);mSandal.show();}}
【4】测试显示输出的结果如下:Welcome to Andy.Chen Blog!Decorator Patterns.穿凉鞋穿牛仔裤穿T-Shirt装扮的Andy
【5】总结:Decorator模式有以下的优缺点:注:本文为Andy.Chen原创,欢迎大家转载,转载请大家注明出处,谢谢!
[原]Android中ImageSwitcher结合Gallery展示SD卡中的资源图片
本文主要是写关于ImageSwitcher结合Gallery组件如何展示SDCard中的资源图片,相信大家都看过API Demo 中也有关于这个例子的,但API Demo 中的例子是展示工程中Drawable目录下的资源图片,这样调用系统的API比较容易实现,但我们在开发项目过程中,但有些图片还不能完全确定下来,例如需要展示相机拍照的图片,SDCard中某个目录下的资源图片等功能。其实系统中也提供相应的API给我们应用去实现该功能,下面就用异于API Demo中例子方式展示下如何实现该功能。
【1】我们先看下该例子代码的结构图:
下面就直接上各个文件的代码了,不在这里详细解释了,最后会看到实现的效果图的..呵呵
【2】res/layout/main.xml 文件源码:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="#55000000" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:text="Welcome to Andy.Chen's Blog!" android:textSize="20sp"/> <ImageSwitcher android:id="@+id/switcher" android:layout_width="wrap_content" android:layout_height="350dip" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" /> <Gallery android:id="@+id/mygallery" android:layout_width="fill_parent" android:layout_height="80dp" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:gravity="center_vertical" android:spacing="16dp" /> </LinearLayout>
【3】res/values/attrs.xml 文件源码:<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="Gallery"> <attr name="android:galleryItemBackground" /> </declare-styleable> </resources>
【4】ImageSwitcherAndGalleryDemoActivity.java 源码:(这个类的源码比较多,希望大家耐心看)package com.andyidea.imagedemo;import java.io.File;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.view.animation.AnimationUtils;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.AdapterView.OnItemSelectedListener;import android.widget.BaseAdapter;import android.widget.Gallery;import android.widget.Gallery.LayoutParams;import android.widget.ImageSwitcher;import android.widget.ImageView;import android.widget.Toast;import android.widget.ViewSwitcher.ViewFactory;/** * ImageSwitcher和Gallery如何展示SD卡中的资源图片 * @author Andy.Chen * @email:Chenjunjun.ZJ@gmail.com */public class ImageSwitcherAndGalleryDemoActivity extends Activity implements OnItemSelectedListener,ViewFactory{ private List<String> imagePathList; private String[] list; private ImageSwitcher mSwitcher;private Gallery mGallery;/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); imagePathList=getImagePathFromSD(); list = imagePathList.toArray(new String[imagePathList.size()]); /* 设定Switcher */mSwitcher = (ImageSwitcher) findViewById(R.id.switcher);mSwitcher.setFactory(this);/* 设定载入Switcher的模式 */mSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_in));/* 设定输出Switcher的模式 */mSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_out));mSwitcher.setOnClickListener(new OnClickListener() {public void onClick(View v) {Toast.makeText(ImageSwitcherAndGalleryDemoActivity.this, "你点击了ImageSwitch上的图片",Toast.LENGTH_SHORT).show();}});mGallery = (Gallery) findViewById(R.id.mygallery);/* 新增几ImageAdapter并设定给Gallery对象 */mGallery.setAdapter(new ImageAdapter(this, getImagePathFromSD()));mGallery.setOnItemSelectedListener(this);/* 设定一个itemclickListener事件 */mGallery.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> parent, View v,int position, long id) {Toast.makeText(ImageSwitcherAndGalleryDemoActivity.this, "你点击了Gallery上的图片",Toast.LENGTH_SHORT).show();}}); } /** 从SD卡中获取资源图片的路径 */private List<String> getImagePathFromSD() {/* 设定目前所在路径 */List<String> it = new ArrayList<String>();//根据自己的需求读取SDCard中的资源图片的路径String imagePath = Environment.getExternalStorageDirectory().toString()+"/hknational/image";File mFile = new File(imagePath);File[] files = mFile.listFiles();/* 将所有文件存入ArrayList中 */for (int i = 0; i < files.length; i++) {File file = files[i];if (checkIsImageFile(file.getPath()))it.add(file.getPath());}return it;}/** 判断是否相应的图片格式 */private boolean checkIsImageFile(String fName) {boolean isImageFormat;/* 取得扩展名 */String end = fName.substring(fName.lastIndexOf(".") + 1, fName.length()).toLowerCase();/* 按扩展名的类型决定MimeType */if (end.equals("jpg") || end.equals("gif") || end.equals("png")|| end.equals("jpeg") || end.equals("bmp")) {isImageFormat = true;} else {isImageFormat = false;}return isImageFormat;}/* 改写BaseAdapter自定义一ImageAdapter class */public class ImageAdapter extends BaseAdapter {/* 声明变量 */int mGalleryItemBackground;private Context mContext;private List<String> lis;/* ImageAdapter的构造符 */public ImageAdapter(Context c, List<String> li) {mContext = c;lis = li;/* * 使用res/values/attrs.xml中的<declare-styleable>定义 的Gallery属性. */TypedArray mTypeArray = obtainStyledAttributes(R.styleable.Gallery);/* 取得Gallery属性的Index id */mGalleryItemBackground = mTypeArray.getResourceId(R.styleable.Gallery_android_galleryItemBackground, 0);/* 让对象的styleable属性能够反复使用 */mTypeArray.recycle();}/* 重写的方法getCount,传回图片数目 */public int getCount() {return lis.size();}/* 重写的方法getItem,传回position */public Object getItem(int position) {return position;}/* 重写的方法getItemId,传并position */public long getItemId(int position) {return position;}/* 重写方法getView,传并几View对象 */public View getView(int position, View convertView, ViewGroup parent) {/* 产生ImageView对象 */ImageView i = new ImageView(mContext);/* 设定图片给imageView对象 */Bitmap bm = BitmapFactory.decodeFile(lis.get(position).toString());i.setImageBitmap(bm);/* 重新设定图片的宽高 */i.setScaleType(ImageView.ScaleType.FIT_XY);/* 重新设定Layout的宽高 */i.setLayoutParams(new Gallery.LayoutParams(136, 88));/* 设定Gallery背景图 */i.setBackgroundResource(mGalleryItemBackground);/* 传回imageView对象 */return i;}} @Overridepublic View makeView() { ImageView iv = new ImageView(this); iv.setBackgroundColor(0xFF000000); iv.setScaleType(ImageView.ScaleType.FIT_CENTER); iv.setLayoutParams(new ImageSwitcher.LayoutParams( LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); return iv; }@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position,long id) {// TODO Auto-generated method stubString photoURL = list[position];Log.i("A", String.valueOf(position));mSwitcher.setImageURI(Uri.parse(photoURL));}@Overridepublic void onNothingSelected(AdapterView<?> parent) {// TODO Auto-generated method stub}}
【5】程序运行效果图如下:
至此大功告成了!
本文为 Andy.Chen原创,欢迎大家转载,转载请注明出处,谢谢大家关注。
[原]Android中的Activity四种启动模式(launchMode)
我们在开发项目的过程中,会涉及到该应用中多个Activity组件之间的跳转,或者夹带其它应用的可复用的Activity。例如我们可能希望跳转到原来某个Activity实例,而不是产生大量重复的 Activity。这样就需要我们为 Activity 配置特定的加载模式,而不是使用默认的加载模式。
Activity 有四种加载模式
[1] standard 模式
这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。
[2] singleTop 模式
如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。
[3] singleTask 模式
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。
[4] singleInstance 模式
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。
设置启动模式的位置在 AndroidManifest.xml 文件中 Activity 元素的 android:launchMode 属性。
[原]Android应用开发中如何进行单元测试
本文主要和大家分享如何在Android应用开发过程中如何进行单元测试,个人在做项目的过程中,觉得单元测试很有必要,以保证我们编写程序的正确性。下面我们先大概了解下单元测试,以及单元测试的作用。
单元测试(又称为模块测试)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
其实我们每天都在做单元测试。你写了一个函数,除了极简单的外,总是要执行一下,看看功能是否正常,有时还要想办法输出些数据,如弹出信息窗口什么的,这,也是单元测试,把这种单元测试称为临时单元测试。只进行了临时单元测试的软件,针对代码的测试很不完整,代码覆盖率要超过70%都很困难,未覆盖的代码可能遗留大量的细小的错误,这些错误还会互相影响,当BUG暴露出来的时候难于调试,大幅度提高后期测试和维护成本,也降低了开发商的竞争力。可以说,进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。
对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。
要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。我认为,比较简单的办法是为产品工程建立对应的测试工程,为每个类建立对应的测试类,为每个函数(很简单的除外)建立测试函数。
在Android应用开发中有两种方式可以进行单元测试,下面分别展示这两种单元测试的方法:
单元测试方式一:[基于应用的工程上进行单元测试]
根据上面的程序代码结构图,例如我们要测试包 com.andyidea.demo 包中的 JUnitService.java 中的某个方法的正确性。例如该对象中有个 add方法,计算两个整型数据的和
package com.andyidea.demo;public class JUnitService {public int add(int a, int b){return a+b;}}
然后我们在MyJnuitTest.java中编写单元测试代码,[注意:该测试类一定要继承 AndroidTestCase类] 以验证程序结果是否是我们所期望的一致package com.andyidea.jnuit;import junit.framework.Assert;import android.test.AndroidTestCase;import com.andyidea.demo.JUnitService;public class MyJnuitTest extends AndroidTestCase {public void testAdd() throws Exception{JUnitService js = new JUnitService();int sum = js.add(1, 2);Assert.assertEquals(3, sum);}}
同时我们还需要在 Manifest.xml 清单配置文件中配置下:以红色标志出来需要配置的地方。<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.andyidea.demo" android:versionCode="1" android:versionName="1.0" > <!-- 代表把单元测试框架中的一些依赖库引入进来 --> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <uses-library android:name="android.test.runner" /> <activity android:name="com.andyidea.demo.JUnitTestDemoActivity" 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> <!-- 代表配置单元测试框架的启动装置,启动装置有好几个类,可以选择,一般情况下我们使用上面这个。 targetPackage与上面的package相同,代表单元测试框架和当前应用是处于同一个进程中 --> <instrumentation android:name="android.test.InstrumentationTestRunner" android:label="Test for my app" android:targetPackage="com.andyidea.demo"/></manifest>
最后,打开eclipse中的outline窗口,其中会显示单元测试类的所有的方法然后想要测试哪个方法,则在哪个测试方法上右键鼠标,选择Run As,然后再选择Android JUnit Test即可,如果是正常的,则会如下:
如果有异常或者错误,则会出现如下情况:
单元测试方式二:
其实这种方式更加简单,这种方式是单独创建一个单元测试的工程来进行测试。即创建一个 Android Test Project ,然后选择需要单元测试的项目就OK了,通过这种方式进行单元测试的话就不用进行上面的配置,其实创建这种工程的时候,默认已经帮我们配置好了。如何进行测试和上面的方式一样,在这里就不详细讲解了。
注:本文为 Andy.Chen 原创,欢迎大家转载,转载请大家注明出处,谢谢。
[原]Android应用中通过AIDL机制实现进程间的通讯实例
在Android中,每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢?显然,Java中是不支持跨进程内存共享的,因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的。在Android中,则采用AIDL(Android Interface Definition Language :接口定义语言)方式实现。
AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
AIDL IPC机制是面向接口的,它是使用代理类在客户端和实现端传递数据。
使用AIDL实现IPC
使用AIDL实现IPC服务的步骤是:
1. 创建.aidl文件-该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。
2. 在makefile文件中加入.aidl文件-(Eclipse中的ADT插件提供管理功能)Android包括名为AIDL的编译器,位于tools/文件夹。
3. 实现接口-AIDL编译器从AIDL接口文件中利用Java语言创建接口,该接口有一个继承的命名为Stub的内部抽象类(并且实现了一些IPC调用的附加方法),要做的就是创建一个继承于YourInterface.Stub的类并且实现在.aidl文件中声明的方法。
4. 向客户端公开接口-如果是编写服务,应该继承Service并且重载Service.onBind(Intent) 以返回实现了接口的对象实例
创建.aidl文件
1.Java编程语言的主要类型 (int, boolean等) — 不需要 import 语句。
2.以下的类 (不需要import 语句):
String
List -列表中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。List可以像一般的类(例如List<String>)那样使用,另一边接收的具体类一般是一个ArrayList,这些方法会使用List接口。
Map - Map中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。一般的maps(例如Map<String,Integer>)不被支持,另一边接收的具体类一般是一个HashMap,这些方法会使用Map接口。
CharSequence -该类是被TextView和其他控件对象使用的字符序列。
3.通常引引用方式传递的其他AIDL生成的接口,必须要import 语句声明
4.实现了Parcelable protocol 以及按值传递的自定义类,必须要import 语句声明。
通过对上面的基本了解,下面我就以一个具体的实例来说明Android中如何通过AIDL机制来实现两个进程中实现通讯:(情景假设:例如A应用通过服务Service方式向B应用提供通过书籍编号来查询书籍名称的服务)
A应用程序结构图如下:
通过上面的结构图可以看到,在A应用程序中创建一个 aidl 的接口,然后系统在 gen 目录下自动生成相应的 java 文件。
其中 IBook.aidl 文件的源码:
package com.andyidea.aidl;interface IBook {String queryBook(int bookNo);}
其中 BookService.java 类中的源码如下:
package com.andyidea.service;import com.andyidea.aidl.IBook;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;/** * 查询书籍的服务 * @author Andy * */public class BookService extends Service {private String[] bookNames = {"Java编程思想","设计模式","Android开发设计"}; private IBinder mIBinder = new BookBinder();@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn mIBinder;}/** * 服务中交互的方法 * @param bookNo * @return */public String queryBookName(int bookNo){if(bookNo > 0 && bookNo <= bookNames.length){return bookNames[bookNo-1];}return null;}private class BookBinder extends IBook.Stub{@Overridepublic String queryBook(int bookNo) throws RemoteException {return queryBookName(bookNo);}}}
同时别忘了在 Manifest.xml中配置该服务对象(标红色的部分),建议采用隐式方式激活该服务,适合不同的进程的意图。<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.andyidea.service" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <service android:name=".BookService"> <intent-filter> <action android:name="com.andyidea.aidl.bookservice"/> </intent-filter> </service> </application></manifest>
以上我们已经实现了A应用程序提供服务的功能,下面我们来实现B应用(或者其它需要用到A应用提供服务的应用程序)B应用程序结构图如下:
我们看到B应用程序也要和服务端同样的 .aidl 文件,我们可以从A应用程序中把该 aidl 文件中拷贝过来就是了,呵。由于B应用中 .aidl 文件和 A应用中的 .aidl 文件源码一样,我在这里就不列出来了。
其中AIDLClientDemoActivity.java源码如下:【注:其中该客户端类要通过 bindService 方式来启动另外一个进程的服务,这样才能实现和服务进行交互。如果通过startService方式来启动服务,则不能与服务进行交互】
package com.andyidea.client;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.andyidea.aidl.IBook;public class AIDLClientDemoActivity extends Activity {private EditText numberText;private TextView resultView;private Button query;private IBook bookQuery;private BookConnection bookConn = new BookConnection(); /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); numberText = (EditText) this.findViewById(R.id.number);resultView = (TextView) this.findViewById(R.id.resultView);query = (Button)findViewById(R.id.query);Intent service = new Intent("com.andyidea.aidl.bookservice");bindService(service, bookConn, BIND_AUTO_CREATE);query.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String number = numberText.getText().toString();int num = Integer.valueOf(number);try {resultView.setText(bookQuery.queryBook(num));} catch (RemoteException e) {e.printStackTrace();}}}); }@Overrideprotected void onDestroy() {unbindService(bookConn);super.onDestroy();}private final class BookConnection implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubbookQuery = IBook.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stubbookQuery = null;}}}
其中界面布局文件 main.xml 源码:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="书籍编号" /> <EditText android:id="@+id/number" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/query" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询"/> <TextView android:id="@+id/resultView" android:layout_width="fill_parent" android:layout_height="wrap_content"/></LinearLayout>
至此,我们已经完成了B应用程序的代码实现部分,我们要先把A应用程序部署到机器上,然后我们再运行B应用程序。下面我们通过截图来看下程序运行的结果:通过上面的截图,我们输入书籍编号 1,就可以查询出相应的书籍名称,到此,我们就可以了解了 Android应用中如何通过AIDL机制实现两个进程的通讯。
注:本文为 Andy.Chen 原创,欢迎大家转载,转载请大家注明出处,谢谢。
[原]设计模式之九 --- 外观(Facade)模式
【1】基本概念
外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
【2】简单分析
我们先来看下该设计模式的UML结构图:
【3】如何用java语言实现该设计模式:
由于该设计模式比较简单,而且我们平时在开发项目的过程中经常会用到该设计模式的,我就不做过多的解析了,针对上面的UML结构图分别贴出各个类的代码:
3.1 SubSystemOne.java源码:
package com.andyidea.patterns;public class SubSystemOne {public void methodOne(){System.out.println("子系统方法一");}}
其实,SubSystemTwo.java ,SubSystemThree.java ,SubSystemFour.java的源码和 SubSystemOne.java的源码类似,只是方法内部输出的内容不同,在这里本人就省略列出其它子系统类的源码了。呵呵3.2 Facade.java 类源码:
package com.andyidea.patterns;public class Facade {SubSystemOne subOne;SubSystemTwo subTwo;SubSystemThree subThree;SubSystemFour subFour;public Facade(){subOne = new SubSystemOne();subTwo = new SubSystemTwo();subThree = new SubSystemThree();subFour = new SubSystemFour();}public void invokeMethodA(){System.out.println("-------方法组A--------");subOne.methodOne();subTwo.methodTwo();subThree.methodThree();}public void invokeMethodB(){System.out.println("-------方法组B--------");subTwo.methodTwo();subThree.methodThree();subFour.methodFour();}}
3.3 FacadeClient.java类源码:package com.andyidea.patterns;public class FacadeClient {public static void main(String[] args) {Facade facade = new Facade();//调用组件Afacade.invokeMethodA();//调用组件Bfacade.invokeMethodB();}}
【4】何时使用外观模式4.1 在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层构架,就需要考虑在数据访问层和业务逻辑层,业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
4.2 在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。
4.3 在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须依赖于它。此时用外观模式Facade也是非常合适的。例如可以开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。
至此,我们应该对外观模式有了解和何时使用该模式了吧,希望大家多多采用设计模式提高我们代码的质量和提高维护性和扩展性。
本文为 Andy.Chen 原创,欢迎大家继续关注Me的博文,欢迎大家转载,谢谢!
[原]设计模式之十 --- 状态(State)模式
【1】基本概念
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
【2】简单分析
我们先来看下该设计模式的UML的结构图
【3】如何用Java语言实现该设计模式
本篇文章以一个贴近现实生活的例子来展示如何使用该设计模式,以方便大家尽快的掌握状态设计模式。例子:如某人去银行新开一个账户,该账户可以用来存钱或者取钱,我们可以以账户中的总资金来判断显示某用户账户的状态:红色状态(RedState),银色状态(SliverState),金色状态GoldState)。其实就好比银行中根据我们存储卡中的资金来给客户某种身份的识别,例如普通用户,VIP客户,公司客户....等等。
我们先看下该实例Demo的结构图:
3.1 State.java 源码:
package com.andyidea.patterns.concretestate;import com.andyidea.patterns.context.Account;/** * State类,抽象状态类,定义一个接口以封装 * 与Context的一个特定状态相关的行为。 * @author Andy * */public abstract class State { protected Account account; protected double balance; protected double interest; protected double lowerLimit; protected double upperLimit; //-------------Properties------------------// public Account getAccount() {return account;}public void setAccount(Account account) {this.account = account;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}//------------------------------------------///**存入金额*/public abstract void deposit(double amount);/**支出金额*/ public abstract void withdraw(double amount); /**利息*/ public abstract void payInterest();}
3.2 RedState.java源码:package com.andyidea.patterns.concretestate;/** * 用户账户状态:RedState * @author Andy * ConcreteState类,具体状态,每一个子类实现一个与 * Context的一个状态相关的行为。 */public class RedState extends State {private double serviceFee;public RedState(State state) {this.balance = state.getBalance();this.account = state.getAccount();initialize();} /** 模拟初始化基础数据*/private void initialize() {interest = 0.0;lowerLimit = -100.0;upperLimit = 0.0;serviceFee = 15.00;}@Overridepublic void deposit(double amount) { balance += amount; stateChangeCheck();}@Overridepublic void withdraw(double amount) { amount = amount - serviceFee; System.out.println("No funds available for withdrawal!");}@Overridepublic void payInterest() {// No interest is paid}/** 状态检测 */private void stateChangeCheck() {if (balance > upperLimit) {account.setState(new SilverState(this));}}}
SliverState.java源码:package com.andyidea.patterns.concretestate;import com.andyidea.patterns.context.Account;/** * 用户账户状态:SilverState * @author Andy * ConcreteState类,具体状态,每一个子类实现一个与 * Context的一个状态相关的行为。 */public class SilverState extends State { public SilverState(State state){ this(state.balance,state.account); } public SilverState(double balance, Account account){ this.balance = balance; this.account = account; initialize(); } /** 模拟初始化基础数据*/ private void initialize(){ interest = 0.0; lowerLimit = 0.0; upperLimit = 1000.0; }@Overridepublic void deposit(double amount) { balance += amount; stateChangeCheck();}@Overridepublic void withdraw(double amount) { balance -= amount; stateChangeCheck();}@Overridepublic void payInterest() { balance += interest * balance; stateChangeCheck();}/** 状态检测 */ private void stateChangeCheck(){ if (balance < lowerLimit){ account.setState(new RedState(this)); } else if (balance > upperLimit){ account.setState(new GoldState(this)); } }}
GoldState.java源码:package com.andyidea.patterns.concretestate;import com.andyidea.patterns.context.Account;/** * 用户账户状态:GoldState * @author Andy * ConcreteState类,具体状态,每一个子类实现一个与 * Context的一个状态相关的行为。 */public class GoldState extends State { public GoldState(State state){ this(state.balance, state.account); } public GoldState(double balance, Account account){ this.balance = balance; this.account = account; initialize(); } /** 模拟初始化基础数据*/ private void initialize(){ interest = 0.05; lowerLimit = 1000.0; upperLimit = 10000000.0; }@Overridepublic void deposit(double amount) { balance += amount; stateChangeCheck();}@Overridepublic void withdraw(double amount) { balance -= amount; stateChangeCheck();}@Overridepublic void payInterest() { balance += interest * balance; stateChangeCheck();}/** 状态检测 */private void stateChangeCheck() {if (balance < 0.0) {account.setState(new RedState(this));} else if (balance < lowerLimit) {account.setState(new SilverState(this));}}}
3.3 Account.java类源码:package com.andyidea.patterns.context;import com.andyidea.patterns.concretestate.SilverState;import com.andyidea.patterns.concretestate.State;/** * Context类,维护一个ConcreteState子类的实例 * 这个实例定义当前的状态。 * @author Andy * */public class Account { private State state; private String owner; public Account(String owner){ // 新开账户默认为 Silver状态 this.owner = owner; this.state = new SilverState(0.0, this); } public State getState() {return state;}public void setState(State state) {this.state = state;}public void deposit(double amount) {state.deposit(amount);System.out.println(owner + " Deposited " + amount);System.out.println(owner + " Balance = " + state.getBalance());System.out.println("Status = " + state.getClass().getSimpleName());System.out.println("==============================================");} public void withdraw(double amount){ state.withdraw(amount); System.out.println(owner + " Withdrew " + amount); System.out.println(owner + " Balance = " + state.getBalance()); System.out.println("Status = " + state.getClass().getSimpleName()); System.out.println("=============================================="); } public void payInterest(){ state.payInterest(); System.out.println(owner + " Interest Paid "); System.out.println(owner + " Balance = " + state.getBalance()); System.out.println("Status = " + state.getClass().getSimpleName()); System.out.println("=============================================="); }}
3.4 客户端测试类 StateClient.java 类源码:package com.andyidea.patterns;import com.andyidea.patterns.context.Account;public class StateClient {public static void main(String[] args) { // 新开一个银行账户 Account account = new Account("Andy.Chen"); // 存取款等系列操作 account.deposit(500.0); account.deposit(300.0); account.deposit(550.0); account.payInterest(); account.withdraw(2000.00); account.withdraw(1100.00);}}
【4】下面我们来看下程序运行的结果:Andy.Chen Deposited 500.0Andy.Chen Balance = 500.0Status = SilverState==============================================Andy.Chen Deposited 300.0Andy.Chen Balance = 800.0Status = SilverState==============================================Andy.Chen Deposited 550.0Andy.Chen Balance = 1350.0Status = GoldState==============================================Andy.Chen Interest Paid Andy.Chen Balance = 1417.5Status = GoldState==============================================Andy.Chen Withdrew 2000.0Andy.Chen Balance = -582.5Status = RedState==============================================No funds available for withdrawal!Andy.Chen Withdrew 1100.0Andy.Chen Balance = -582.5Status = RedState==============================================
【5】总结:状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
本文为 Andy.Chen 原创,欢迎大家继续关注Me的博文,欢迎大家转载,谢谢!
[原]Android中隐式意图(Intent)用法
Intent对象在Android应用开发中起到很大的作用,例如激活组件(Activity,Service 等组件)或者携带数据的功能,相信大家在开发中经常会用到这些功能,Android中的意图分为显式意图和隐式意图,显式意图大家应该用得比较多,但隐式意图在开发过程中也是必不可少的。在这篇文章中粗略讲解下个人对隐式意图的用法。
假设情景:我要从一个Activity跳转到另外一个Activity中,通过隐式意图来激活另外一个Activity,应该如何实现呢?
首先,我们在Manifest.xml配置文件中配置intent-filter过滤器节点:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.andyidea.intentdemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".IntentFirstActivity" 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 android:name=".IntentSecondActivity" android:label="@string/app_name"> <intent-filter > <!--可以定制多个动作 程序中只要有一个匹配就行 --> <action android:name="android.andyidea.second"/> <!--必须指定一个类别 用startActivity启动 --> <category android:name="android.andyidea.category" /> <!--默认必须指定一个类别 用startActivity启动 --> <category android:name="android.intent.category.DEFAULT" /> <!--路径匹配 协议://主机名:端口号/路径 --> <data android:scheme="http" android:host="www.andyidea.com" android:port="8080" android:path="/person"/> <!--数据类型匹配 --> <data android:mimeType="jpeg"/> </intent-filter> </activity> </application></manifest>
第二,我们在第一个Activity中添加相应的代码来实现匹配,这里就不做过多解释了,解释在代码的注释中。/**定义意图对象*/Intent intent = new Intent();/**设置动作:可以定制多个动作 程序中只要有一个匹配就行*/intent.setAction("android.andyidea.second");/**添加类别:startActivity(intent)启动Activity时会自动添加 * android.intent.category.DEFAULT类别,所以需要在Manifest中 * 配置该类别.但在程序中不需要明确添加该类别*/intent.addCategory("android.andyidea.category");/**设置数据:API中说这个方法会将setType的方法设置的类型清除掉*/intent.setData(Uri.parse("http://www.andyidea.com:8080/person"));/**设置类型:API中说这个方法会将setData上面的方法设置的数据清除掉*/intent.setType("jpeg");/**同时设置数据和类型:如果Intent过滤器中同时包含数据和数据类型的设置 * 就只能使用该方式来设置数据和类型*/intent.setDataAndType(Uri.parse("http://www.andyidea.com:8080/person"), "jpeg");/**启动组件*/startActivity(intent);
至此,我们应该对Android中的隐式意图有个粗略的了解了吧。- 想成为Android高手必须学习的干货
- 想成为Android高手必须学习的干货
- 成为Android高手必须掌握的8项基本要求
- 成为Android高手必须掌握的8项基本要求
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的8项基本要求.路就在,看你走不走le
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- 成为Android高手必须掌握的28大项内容和10个建议
- ubuntu下脚本基础
- 3389远程连接时“超出最大连接数”解决办法
- UVA12032--贪心
- javascript 可拖动的window窗口
- eclipse findbugs
- 想成为Android高手必须学习的干货
- Qt多线程间信号槽传递非QObject类型对象的参数
- POJ1113 Wall 凸包
- var optionElement = optionElements[i];
- oralcle 11g R2 安装
- 电脑端安装Android4.0模拟器使用教程
- Prefix-casting versus as-casting in C#
- Android软件安装工具-APK安装器
- POJ2187--凸包--Beauty Contest