From Head First Design Patterns.

Design Principle:

Idnetify the aspects of your application that vary and separate them from what stays the same.

Here's another way to think about it:

Take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don't.


SimUDuck App

We need to separate behaviors from Duck class. So, let's define two behavior interfaces: QuackBehavior and FlyBehavior with additonal instantial classes.

Design Principle

Program to an interface, not an implementation.

QuackBehavior interface and instantial classes:

public interface QuackBehavior {
public void quack();
} public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
} public class MuteQuack implements QuackBehavior {
public void quack(){
System.out.println("<< Silence >>");
}
} public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak");
}
}

FlyBehavior interface and instantial classes:

public interface FlyBehavior {
public void fly();
} public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
} public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}

And then, we can define the abstract class Duck, which is the base class of our Duck Project:

public abstract class Duck {
QuackBehavior quackBehavior;
FlyBehavior flyBehavior; public Duck() { } public abstract void display(); public void performQuack() {
quackBehavior.quack();
} public void performFly() {
flyBehavior.fly();
} public void swim() {
System.out.println("All ducks float, even decoys!");
}
}

Now, we can make a concrete duck, Mallard!

public class MallardDuck extends Duck {

	public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
} public void display() {
System.out.println("I'm a real Mallard duck");
}
}

Finally,let's write a  simulator to test our codes:

public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
mallard.display();
}
} /* Output:
Quack
I'm flying!!
I'm a real Mallard duck
*/

Pay attention, the duck mallard here only offer two APIs: performQuack(), performFly(). We can't call quack() or fly(), which also make the process of calling behaviors simple.

It's a shame to have all dynamic talent built into our ducks and not be using it!

If you want to set duck's behavior now, you should make it through instantiating it in the duck's constructor rather than adding a setter method on the duck subclass.

Add two new methods to the Duck class:

public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
} public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}

Let's make a new Duck type:

public class ModelDuck extends Duck {
public ModelDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyNoWay();
} public void display() {
System.out.println("I'm a model duck");
}
}

adding new FlyBehavior type:

public class FlyRocketPowered implements FlyBehavior {
public void fly() {
System.out.println("I'm flying with a rocket!");
}
}

Now, we can add some codes in simulator to test our set methods:

Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();

Here, the model duck dynamically changed its flying behavior!

You can't do THAT if the implementation lives inside the duck class!

Design Principle

Favor composition over inheritance.

In this project, we use the Stragegy pattern, the formal definition is:

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Libraries and frameworks can't help us structure our own applications in ways that are easier to understand, more maintainable and flexible.

Design patterns are higher level than libraries. Design patterns tell us how to structure classes and objects to solve certain problems and it is our job to adapt those designs to fit our particular application. And it can help you more quickly understand APIs that are structured around design patterns.

Misunderstandings of object-oriented development:

by know the OO basics we are automatically going to be good at building flexible, reusable, and maintainable systems.

A design guru always thinks about how to create flexible designs that are maintaqinable and that can cope with change.


Weather Monitoring application

Publishers + Subscribers = Observer Pattern

You know how newspaper or magazine subscriptions work:

  1. A newspaper publisher goes into business and begins publishing newpapers.
  2. You subscribe to a particular publisher, and every time there's a new edition it gets delivered to you. As long as you remain a subscriber, you get new newspaper.
  3. You unsubscribe when you don't want papers anymoe, and they stop delivered.
  4. While the publisher remains in business, people, hotels, airlines and other businesses constantly subscribe and unsubscribe to the newspaper.

In the Observer Pattern, we call the publisher the SUBJECT, and the subscribers the OBSERVERS.

Formal definition:

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

Loosely coupled designs allow us to build flexible OO systems that can handle change because they minimize the interdependency between objects.

Design Principle

Strive for loosely coupled designs between objects that interact.

The classes design:

  • Our WeatherData class is the "one".
  • Various display elements are the "many".
  • State is: temperature, humidity and harometric pressure.

If we make the WeatherData object the subject, and the display elements the observers, then the displays will register themselves with the WeatherData object in order to get information they want.

As every element can be different, all the components should implement the same interface so that the WeatherData object will know how to send them the measurements. So every display will have an updata() method that WeatherData will call.

The design diagram should be:

Implement the Weather Station

While in some cases you can make use of Java's built-in support, in a lot of cases it's more flexible to build your own (and it's not all that hard). So, let's get started with the interfaces:

public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
} public interface Observer {
public void update(float temp, float humidity, float pressure);
} public interface DisplayElement {
public void display();
}

Here, passing the measurements directly to the observers was the most straightforward method of updating state.

  • Is this an area of the application that might change in the future?
  • If it did change, would the change be well encapsulated?
  • Or would it require changes in many parts of the code?

Implement the Subject interface in WeatherData:

public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure; public WeatherData() {
observers = new ArrayList();
} public void registerObserver(Observer o) {
observers.add(o);
} public void removeObserver(Observer o) {
int i = observers.indexOf(o); if(i >= 0)
observers.remove(i);
} public void notifyObservers() {
for(int i = 0; i < observers.size(); ++i) {
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
} public void measurementsChanged() {
notifyObservers();
} public void setMeasuremets(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
} // other WeatherData methods here. }

Now that we've got our WeatherData class straightened out, it's time to build the Display Elements:

public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
} public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
} public void display() {
System.out.println("Current conditions: " + temperature +"F degrees and " + humidity + "% humidity");
} }

Q: Is update() the best place to call display() ?

A: There're much better ways to desgin the way the data gets displayed. We're going to se this when we get to the model-view-controller pattern.

Q: Why did you store a reference to the Subject? It doesn't look like you use it again after the constructor?

A: True, but in the future we may want to un-register ourselves as an observer and it would be handy to already have a reference to the subject.

The Weather Station is ready to go, all we need is some coe to glue everything together:

public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); weatherData.setMeasuremets(80, 65, 30.4f);
weatherData.setMeasuremets(82, 70, 29.2f);
weatherData.setMeasuremets(78, 90, 29.2f);
}
} /* Output:
Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Current conditions: 78.0F degrees and 90.0% humidity
*/

Using Java's built-in Observer Pattern

The most general is the Observer interface and the Observable class in the java.util package.

With Java's built-in support, all you have to do is extend Observable and tell t when to notify the Observers. The API does the rest for you.

To get a high level feel for java.util.Observer and java.util.Observable, check out this reworked OO desgin for the WeatherStation:

The Observable class keeps track of all your observers and notifies them for you.

Observable is a CLASS not an interface, so WeatherData extends Observable.

For the observable to send notifications:

First, you should be Observable by extending the java.util.Observable superclass.

  1. You first must call the setChanged() method to signify that the state has changed in your object.
  2. Then, call one of two notifyObservers() methods:
    • notifyObservers()
    • notifyObservers(Object arg)  (arg is an arbitrary data object)

For an observer to receive notifications:

It implements the update method, as before, but the signature of the method is a bit different:

update(Observable o, Object arg);
  • If you want to "push" data to the observers, you can pass the data as a data object to the notifyObservers(arg) method.
  • If not, then the Observer has to "pull" the data it wants from the Observable object object passed to it.

The setChanged() method is used to signify that the state has changed and that notifyObservers(), when it is called, should update its observers.

Pseudo code for the Observable class:

setChanged() {
changed = true
} notifyObservers(Object arg) {
if(changed) {
for every observer on the list {
call update(this, arg)
} changed = false
}
} notifyObservers() {
notifyObservers(null)
}

Why is this necessary? The setChanged() method is meant to give you more flexibility in how you update observers by allowing you to optimize the notifications.

For example, if our measurements were so sensitive that the temperature readings were constantly fluctuating by a few tenths of a degree. That might cause the WeatherData object to send out notifications constantly. Instead, we might want to send our notifications only if the temperature changes more than half a degree and we could call setChanged() only after that happend.

Reworking the Weather Station with the built-in support:

import java.util.Observable;
import java.util.Observer; public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure; public WeatherData() {} public void measurementsChanged() {
setChanged();
notifyObservers();
} public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
} public float getTemperature() {
return temperature;
} public float getHumdity() {
return Humidity;
} public float getPressure() {
return pressure;
}
}

Our constructor no longer needs to create a data structure to hold Observers.

We aren't sending a data object with the notifyObservers() call. That means we're using the PULL model.

Now, let's rework the CurrentConditionsDisplay:

import java.util.Observable;
import java.util.Observer; public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity; public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
} public void update(Observable obs, Object arg) {
if(obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
} public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
} }

Reworking the ForecastDisplay class:

import java.util.Observable;
import java.util.Observer; public class ForecastDisplay implements Observer, DisplayElement {
private Observable observable;
private float currentPressure = 29.92f;
private float lastPressure; public ForecastDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
} public void update(Observable obs, Object arg) {
if(observable instanceof WeatherData){
WeatherData weatherData = (WeatherData)obs;
lastPressure = currentPressure;
currentPressure = weatherData.getPressure();
display();
}
} public void display() {
// display code here.
} }

The dark side of java.util.Observable

  • Because Observable is a class, you have to subclass it. That means you can't add on the Observable behavior to an existing class that already extends another superclass.
  • If you look at the Observer API, the setChanged() method is protected. This means you can't call setChanged() unless you've subclassed Observable. This means you can't even create an instance of the Observable class and compose it with your own objects, you have to subclass.

In either case, you know the Observer Pattern well and you're in a good position to work with any API that makes use of the pattern.

The java.util implementation of Observer/Observable is not the only place you'll find the Observer Pattern in the JDK. Both JavaBeans and Swing also provide their own implementations of the pattern. Let's look at a simple part of the Swing API, the JButton.

If you look under the hood at JButton's superclass, AbstractButton, you'll see that it has a lot of add/remove listener methods. Theses methods allow you to add and remove observers, or as they are called in Swing, listeners, to listen for various type of events that occure on the Swing component.

For instance, an ActionListener lets you "listen in" on any types of actions that might occur on a button, like a button press.

import javax.swing.*;

public class SwingObserverExample {
JFrame frame; public static void main(String[] args) {
SwingObserverExample example = new SwingObserverExample();
example.go();
} public void go() {
frame = new JFrame();
JButton button = new JButton("Should I do it?");
button.addActionListener(new AngelListener());
button.addActionListener(new DevilListener());
frame.getContentPane().add(BorderLayout.CENTER, button);
// Set frame properties here
} class AngelListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("Don't do it, you might regret it!");
}
} class DevilListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("Come on, do it!");
}
} }

Starbuzz Coffee

We'll re-examine the typical overuse of inheritance and you'll learn how to decorate your classes at runtime using a form of object composition. Once you know the techniques of decorating, you'll be able to give your (or someone else's) objects new responsibilities without making any code changes to the underlying classes.

Design Principle

Classes should be open for extension, but closed for modification.

When we compose a decorator with a component, we are adding new behavior. We are acquiring new behavior not by inheriting it from a superclass, but by composing objects together.

Decroating our Beverages:

public abstract class Beverage {
String description = "Unknown Beverage"; public String getDescription() {
return description;
} public abstract double cost();
}

Beverage is simple enough. Let's implement the abstract class for the Condiments(Decorator) as well:

public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}

First, we need to be interchangeable with a Beverage, so we extend the Beverage class.

Then, we're going to require  that the condiment decorators all reimplement the getDescription() method.

Now we'll implement some beverage. We'll start with Epresso.

public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
} public double cost() {
return 1.99;
}
}

HouseBlend:

public class HouseBlend extends Beverage {
public HouseBlend() {
description = "HouseBlend";
} public double cost() {
return .89;
}
}

Implement concrete decorators:

public class Mocha extends CondimentDecorator {
Beverage beverage; public Mocha(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Mocha";
} public double cost() {
return .20 + beverage.cost();
} }

Finally

public class StarbuzzCoffe {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost() ); // a dark roast with double Mocha and whip.
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost() ); // a house blend with Soy Mocha and whip.
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription() + " $" + beverage3.cost() );
}
} /* Output:
Espresso $1.99
DarkRoast, Mocha, Mocha, Whip $1.49
HouseBlend, Soy, Mocha, Whip $1.34
*/

Write your own Java I/O Decorator:

import java.io.*;

public class LowerCaseInputStream extends FilterInputStream {
public LowerCaseInputStream(InputStream in) {
super(in);
} public int read() throws IOException {
int c = super.read();
return (-1 == c ? c : Character.toLowerCase((char) c));
} public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len); for(int i = offset; i < offset + result; ++i) {
b[i] = (byte) Character.toLowerCase((char)b[i]);
} return result;
} }

Test I/O decorator:

import java.io.*;

public class InputTest {
public static void main(String[] args) throws IOException {
int c; try {
InputStream in =
new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt"))); while((c = in.read()) >= 0) {
System.out.print((char)c);
} in.close();
} catch(IOException e) {
e.printStackTrace();
}
}
} /* Output:
i know the decorator pattern thereform i rule!
*/

Factory Patterns

Let's say you have a pizza shop, and as a cutting-edge pizza store owner in Objectiville you might end up writing some code like this:

Pizza orderPizza() {
Pizza pizza = new Pizza(); pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
}

So then you'd add some code that determines the appropriate type of pizza and then goes about making the pizza:

Pizza orderPizza(String type) {
Pizza pizza; if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")) {
pizza = new GreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
}

This code is NOT closed for modification. If the Pizza Shop changes its pizza offerings, we have to get into this code and modify it.

Clearly, dealing with which concrete class is instantiated is really messing up our orderPizza() method and preventing it from being closed for modification.

So now we know we'd better off moving the object creation out of the orderPizza() method.

We place that code in an object that is only going to worry about how to create pizzas. If any object needs a pizza created, this is the object to come to.

Build a simple pizza factory to define a class that encapsulates the object creation for all pizzas:

public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null; if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if(type.equals("clam")) {
pizza = new ClamPizza();
} else if(type.equals("veggie")) {
pizza = new VeggiePizza();
} return pizza;
}
}

Now, it's time to fix client code. What we want to do is rely on the factory to create the pizzas for us:

public class PizzaStore {
SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
} public Pizza orderPizza(String type) {
Pizza pizza; pizza = factory.createPizza(type); pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
} // other method here...
}

A framework for the pizza store

There's a way to localize all the pizza making activities to the PizzaStore class, and yet give the franchises freedom to have their own regional style.

What we're going to do is put the createPizza() method back into PizzaStore, but as an abstract method and then create a PizzaStore suclass for each regional style.

public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza; pizza = createPizza(type); pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
} abstract Pizza createPizza(String type);
}

Now we've got a store waiting for subclasses; we're going to have a subclass for each regional type and each subclass is going to make the decision about what makes up a pizza.

public class NYPizzaStore extends PizzaStore {
public Pizza createPizza(type) {
if(type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new NYStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new NYStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
}
}
} public class ChicagoPizzaStore extends PizzaStore {
public Pizza createPizza(type) {
if(type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new ChicagoStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
}
}
} public class CaliforniaPizzaStore extends PizzaStore {
public Pizza createPizza(String type) {
if(type.equals("cheese")) {
pizza = new CaliforniaStyleCheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new CaliforniaStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new CaliforniaStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new CaliforniaStyleVeggiePizza();
}
}
}

Head First Design Patterns的更多相关文章

  1. Design Patterns Simplified - Part 3 (Simple Factory)【设计模式简述--第三部分(简单工厂)】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part3-factory/ Design ...

  2. Design Patterns Simplified - Part 2 (Singleton)【设计模式简述--第二部分(单例模式)】

    原文链接: http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part-2-singleton/ De ...

  3. Apex Design Patterns

    Apex allows you to build just about any custom solution on the Force.com platform. But what are the ...

  4. [Design Patterns] 4. Creation Pattern

    设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性.它是代码编制真正实现工程化. 四个关键元素 ...

  5. [Design Patterns] 3. Software Pattern Overview

    When you're on the way which is unknown and dangerous, just follow your mind and steer the boat. 软件模 ...

  6. [Design Patterns] 1. Primary concept & term - UML

    It's time to review design patterns, especially when I reach the turning-point of my career. That's ...

  7. 设计模式(Design Patterns)

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  8. Learning JavaScript Design Patterns The Observer Pattern

    The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...

  9. Learning JavaScript Design Patterns The Module Pattern

    The Module Pattern Modules Modules are an integral piece of any robust application's architecture an ...

随机推荐

  1. Unity(二)生命周期LifetimeManager

    描述:Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理. //创建一个Unit ...

  2. caffe 安装在win 7 vs2015 无gpu的安装方式-是无法安装 的

    网上多数是vs2012或者vs2013上安装方式,带NA-显卡的需要安装CUDA7.5,安装cuDNN4,cuDNN. 一 :下载caffe源码(microsoft版) 下载地址:https://gi ...

  3. c# 身份证证号验证方法

    #region 验证身份证是否有效        /// <summary>        /// 验证身份证是否有效        /// </summary>        ...

  4. poj 1080 (LCS变形)

    Human Gene Functions 题意: LCS: 设dp[i][j]为前i,j的最长公共序列长度: dp[i][j] = dp[i-1][j-1]+1;(a[i] == b[j]) dp[i ...

  5. mysql之group_concat函数详解

    函数语法: group_concat( [DISTINCT]  要连接的字段   [Order BY 排序字段 ASC/DESC]   [Separator '分隔符'] ) 下面举例说明: sele ...

  6. Javascript 添加自定义静态方法属性JS清除左右空格

    例如加trim()清除左右空格 String.prototype.trim=function() { return this.replace(/(^\s*)|(\s*$)/g,''); } 调用 va ...

  7. 一个创建Coco2d-x项目的脚本

    1.使用环境 我测试的环境是Mac OS 10.10 +Coco2d-x 3.2,是使用shell写的脚本,应该linux/unix都应该 可以使用. 2.使用可能出现的问题 使用中可能会爆权限不足的 ...

  8. [Leetcode][JAVA] Valid Palindrome

    Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...

  9. JQuery学习(层级)ancestor & descendant

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  10. AndroidStudio Lod.d在LogCat中不输出

    今天Log.d无论怎样都没有输出. 要在手机开发者选项那开启权限,莫名其妙,之前一直都没事的,具体操作参考:http://jingyan.baidu.com/article/84b4f56597e7b ...