Android中阻止连续点击打开activity的小技巧

我们在开发APP的过程中经常会遇到在某些低端机或者在机器响应比较慢的情况下手抖连续点击某个页面(当然不排除有些人故意这么做)重复弹出好几个相同的页面,不过我发现微信这样的应用都没有做处理……但还是要分享一下我是怎么解决的。

1、通过判断两次点击的时间间隔来防止重复点击

工具类:

 /**
 * Created by mafei on 15/12/8.
 */
public class NoDoubleClickUtils {
    private static long lastClickTime;
    private final static int SPACE_TIME = 500;

    public static void initLastClickTime() {
        lastClickTime = 0;
    }

    public synchronized static boolean isDoubleClick() {
        long currentTime = System.currentTimeMillis();
        boolean isClick2;
        if (currentTime - lastClickTime >
                SPACE_TIME) {
            isClick2 = false;
        } else {
            isClick2 = true;
        }
        lastClickTime = currentTime;
        return isClick2;
    }
}

使用方式:

/**
     * 点击事件
     */
    private View.OnClickListener logListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (!NoDoubleClickUtils.isDoubleClick()) {
                事件响应方法
            }
        }
    };

2、通过修改manifest中页面的launchMode属性改为单例模式

<!-- 市场网贷产品页 -->
        <activity
            android:name=".activity.market.CreditRecordActivity"
            android:launchMode="singleTask"
            android:screenOrientation="portrait" />

3、利用RxBinding实现防重复点击

RxBinding 是 Jake Wharton 的一个开源库,它提供了一套在 Android 平台上的基于 RxJava 的 Binding API。

RxView.clickEvents(button)
    .throttleFirst(500, TimeUnit.MILLISECONDS)
    .subscribe(clickAction);

4、利用Rxjava实现重复点击

button = (Button) findViewById( R.id.bt ) ;

RxView.clicks( button ) .throttleFirst( 2 , TimeUnit.SECONDS )

//两秒钟之内只取一个点击事件,防抖操作

.subscribe(new Action1<Void>() {

@Override

public void call(Void aVoid) {

Toast.makeText(MainActivity.this, “点击了”, Toast.LENGTH_SHORT).show();

}

}) ;

java获取HTML中img的src地址链接(摘录)

java匹配HTML中img的链接地址,下面收集了几种方法,有复杂也有简单的,大家可以随便选取

   
         方法一
/**
    * 获取img标签中的src值
    * @param content
    * @return
    */
   public static List<String> getImgSrc(String content){


      List<String> list = new ArrayList<String>();
      //目前img标签标示有3种表达式
      //<img alt="" src="1.jpg"/>   <img alt="" src="1.jpg"></img>     <img alt="" src="1.jpg">
      //开始匹配content中的<img />标签
      Pattern p_img = Pattern.compile("<(img|IMG)(.*?)(/>|></img>|>)");
      Matcher m_img = p_img.matcher(content);
      boolean result_img = m_img.find();
      if (result_img) {
         while (result_img) {
            //获取到匹配的<img />标签中的内容
            String str_img = m_img.group(2);

            //开始匹配<img />标签中的src
            Pattern p_src = Pattern.compile("(src|SRC)=(\"|\')(.*?)(\"|\')");
            Matcher m_src = p_src.matcher(str_img);
            if (m_src.find()) {
               String str_src = m_src.group(3);
               list.add(str_src);
            }
            //结束匹配<img />标签中的src

            //匹配content中是否存在下一个<img />标签,有则继续以上步骤匹配<img />标签中的src
            result_img = m_img.find();
         }
      }
      return list;
   }

   方法二
      Pattern p = Pattern.compile("<img(.[^>]*?)src=('|\")(.*?)('|\").*?>",Pattern.CASE_INSENSITIVE);
      String str = "<body><IMG src='htttp://adfadf' /></body>";

      Matcher m = p.matcher(str);
      if(m.find()){

      System.out.println(m.group(3));
      }

    
      //jsoup_1.7.2, groovy_2.3.7
      import org.jsoup.Jsoup
      import org.jsoup.nodes.Document
      import org.jsoup.select.Elements

      方法三
      String html = "<html><body><IMG src='http://adfadf' /></body></html>"
      Document doc = Jsoup.parse(html)
      Elements elements=doc.select("img")
      //for (int i = 0; i < postElements.size(); i++) {
      elements.each{
      println(it.attr("src"));
      }

推荐一辑歌曲,非常棒《在这场叫做今天的戏剧中》

Take me out tonight
今夜带我出去吧
Where there’s music and there’s people
那里有音乐和人群
And they’re young and alive
那里青春永不息
Driving in your car
坐上你的车
I never never want to go home
我真不想回家
Because I haven’t got one
那是因为我
Anymore
一无所有

– The Smiths

Every breaking wave on the shore
每一个破碎在岸上的浪花
Tells the next one there’ll be one more
告知今后还会有更多的前赴后继
And every gambler knows that to lose
每个赌徒都清楚失败
Is what you’re really there for
正是你真实的存在过

– U2

It’s not a case
努力使你满足
of aiming to please
那根本不是什么难事
You know you’re always crying
但你懂你为什么总是在哭泣
It’s just your part
这就是你的角色
In the play
在这场叫做 今天
for today
的戏剧中

– The Cure

本期音乐为落网第900期音乐期刊,我们又跨过了一个具有象征意义的数字。

在那时,我们在意路上匆忙的人群、在意明媚午后、在意自己是否时髦、以及在意脑海里的所有肆意妄为。在今天,这样一个信息快速且泛滥的时代里,我们再也找不回一整张专辑翻来覆去的听得稀巴烂的状态,只剩下回味里假想的脉脉温情,以及对未来充满着暴力色彩的急切渴求与期盼。在这么多年过去后,我们依然沒有得到自己想要的,可好像這一切也沒有那么糟。

音乐无非就两种,一种是浮在表面的,而另一种则是渗人灵魂的。感谢所有曾经在黑夜中给予我们孤独的灵魂以安抚的声音。

我们明天见。

聆听地址:http://www.luoo.net/music/900

拿破仑.希尔-致富思考

这本书算是一本心灵鸡汤的书,不可否认是一本值得拜读的励志书籍,作者对致富思考分成12步曲,使读者逐渐递增的对自己的梦想根深蒂固,相信自己终将有所成就,实现自己的伟大梦想,拥有财富和快乐,作者书中举例了在历史上成功的发明家,汽车制造家,钢铁大王等。
比如爱迪生发明家,发明了几百项发明,在人类历史发展中使用其生产都有50多种,我们常见的留声机,电灯泡等都给人类做出很大的贡献,他的发明不是一天二天就能成功的都是靠对自己梦想的不断执着,经历不断的失败才有给人类留下了宝贵的财富。福特汽车也是在40岁后才进入汽车行业,没有高等的学历,组建智囊团,也拥了显赫的地位和财富。
人在生命的短暂时光里要对生活充满欲望,自我暗示,听从第六感的判断,要对自己充满信心,人生就是不断试错,找到正确的方法,不要对人生产生恐惧感,不要对死亡产生恐惧感,把这些都转换为自己对财富的欲望,将会有更大的收获,其实人生在40-60的年龄更加成熟,心智也得到了很好的修炼,基础也比较扎实,所以不要以为已经年入半百,或者中国有句固话说的那英三十而立没有成功咱们40就没有希望,40岁的人生对性对爱对事业对家庭更有责任感,对财富的欲望更强烈,更容易成功。
什么是致富思考,就像本书所阐述的那样,对梦想要有执着的意念,不断追求,不断提升自己技能丰富专业知识,光有欲望是不能成功的,当有机会摆在你面前你没有能力实现它,那将是人生的遗憾,所以我们要时刻准备,对自己大脑的欲望储备能力,终有一天你会将成功实现财富自由,享受人生。
读了这本书认识到自己的不足,对爱,对生活,对人生偶尔也抱有偏见,书中的道理让自己拥有更强大的力量,只有不抱怨,对生活充满希望,吸取别人成功的经验,让自己拥有强大的力量,必将成功,我有此信念,有此欲望。

Java 泛型详解(转)

java泛型是开发中经常遇到的,对于降低类的低耦合,强内聚,重复利用,代码冗余都有这非常大的作用。

引言

泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理,以及让人苦恼的类型擦除。

泛型基础

泛型类

我们首先定义一个简单的Box类:

public class Box {
    private String object;
    public void set(String object) { this.object = object; }
    public String get() { return object; }
}

这是最常见的做法,这样做的一个坏处是Box里面现在只能装入String类型的元素,今后如果我们需要装入Integer等其他类型的元素,还必须要另外重写一个Box,代码得不到复用,使用泛型可以很好的解决这个问题。

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

这样我们的Box类便可以得到复用,我们可以将T替换成任何我们想要的类型:

Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();

泛型方法

看完了泛型类,接下来我们来了解一下泛型方法。声明一个泛型方法很简单,只要在返回类型前面加上一个类似<K, V>的形式就行了:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}
public class Pair<K, V> {
    private K key;
    private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

我们可以像下面这样去调用泛型方法:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

或者在Java1.7/1.8利用type inference,让Java自动推导出相应的类型参数:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

边界符

现在我们要实现这样一个功能,查找一个泛型数组中大于某个特定元素的个数,我们可以这样实现:

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

但是这样很明显是错误的,因为除了short, int, double, long, float, byte, char等原始类型,其他的类并不一定能使用操作符>,所以编译器报错,那怎么解决这个问题呢?答案是使用边界符。

public interface Comparable<T> {
    public int compareTo(T o);
}

做一个类似于下面这样的声明,这样就等于告诉编译器类型参数T代表的都是实现了Comparable接口的类,这样等于告诉编译器它们都至少实现了compareTo方法。

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

通配符

在了解通配符之前,我们首先必须要澄清一个概念,还是借用我们上面定义的Box类,假设我们添加一个这样的方法:

public void boxTest(Box<Number> n) { /* ... */ }

那么现在Box<Number> n允许接受什么类型的参数?我们是否能够传入Box<Integer>或者Box<Double>呢?答案是否定的,虽然Integer和Double是Number的子类,但是在泛型中Box<Integer>或者Box<Double>Box<Number>之间并没有任何的关系。这一点非常重要,接下来我们通过一个完整的例子来加深一下理解。

首先我们先定义几个简单的类,下面我们将用到它:

class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}

下面这个例子中,我们创建了一个泛型类Reader,然后在f1()中当我们尝试Fruit f = fruitReader.readExact(apples);编译器会报错,因为List<Fruit>List<Apple>之间并没有任何的关系。

public class GenericReading {
    static List<Apple> apples = Arrays.asList(new Apple());
    static List<Fruit> fruit = Arrays.asList(new Fruit());
    static class Reader<T> {
        T readExact(List<T> list) {
            return list.get(0);
        }
    }
    static void f1() {
        Reader<Fruit> fruitReader = new Reader<Fruit>();
        // Errors: List<Fruit> cannot be applied to List<Apple>.
        // Fruit f = fruitReader.readExact(apples);
    }
    public static void main(String[] args) {
        f1();
    }
}

但是按照我们通常的思维习惯,Apple和Fruit之间肯定是存在联系,然而编译器却无法识别,那怎么在泛型代码中解决这个问题呢?我们可以通过使用通配符来解决这个问题:

static class CovariantReader<T> {
    T readCovariant(List<? extends T> list) {
        return list.get(0);
    }
}
static void f2() {
    CovariantReader<Fruit> fruitReader = new CovariantReader<Fruit>();
    Fruit f = fruitReader.readCovariant(fruit);
    Fruit a = fruitReader.readCovariant(apples);
}
public static void main(String[] args) {
    f2();
}

这样就相当与告诉编译器, fruitReader的readCovariant方法接受的参数只要是满足Fruit的子类就行(包括Fruit自身),这样子类和父类之间的关系也就关联上了。

PECS原则

上面我们看到了类似<? extends T>的用法,利用它我们可以从list里面get元素,那么我们可不可以往list里面add元素呢?我们来尝试一下:

public class GenericsAndCovariance {
    public static void main(String[] args) {
        // Wildcards allow covariance:
        List<? extends Fruit> flist = new ArrayList<Apple>();
        // Compile Error: can't add any type of object:
        // flist.add(new Apple())
        // flist.add(new Orange())
        // flist.add(new Fruit())
        // flist.add(new Object())
        flist.add(null); // Legal but uninteresting
        // We Know that it returns at least Fruit:
        Fruit f = flist.get(0);
    }
}

答案是否定,Java编译器不允许我们这样做,为什么呢?对于这个问题我们不妨从编译器的角度去考虑。因为List<? extends Fruit> flist它自身可以有多种含义:

List<? extends Fruit> flist = new ArrayList<Fruit>();
List<? extends Fruit> flist = new ArrayList<Apple>();
List<? extends Fruit> flist = new ArrayList<Orange>();
  • 当我们尝试add一个Apple的时候,flist可能指向new ArrayList<Orange>();
  • 当我们尝试add一个Orange的时候,flist可能指向new ArrayList<Apple>();
  • 当我们尝试add一个Fruit的时候,这个Fruit可以是任何类型的Fruit,而flist可能只想某种特定类型的Fruit,编译器无法识别所以会报错。

所以对于实现了<? extends T>的集合类只能将它视为Producer向外提供(get)元素,而不能作为Consumer来对外获取(add)元素。

如果我们要add元素应该怎么做呢?可以使用<? super T>

public class GenericWriting {
    static List<Apple> apples = new ArrayList<Apple>();
    static List<Fruit> fruit = new ArrayList<Fruit>();
    static <T> void writeExact(List<T> list, T item) {
        list.add(item);
    }
    static void f1() {
        writeExact(apples, new Apple());
        writeExact(fruit, new Apple());
    }
    static <T> void writeWithWildcard(List<? super T> list, T item) {
        list.add(item)
    }
    static void f2() {
        writeWithWildcard(apples, new Apple());
        writeWithWildcard(fruit, new Apple());
    }
    public static void main(String[] args) {
        f1(); f2();
    }
}

这样我们可以往容器里面添加元素了,但是使用super的坏处是以后不能get容器里面的元素了,原因很简单,我们继续从编译器的角度考虑这个问题,对于List<? super Apple> list,它可以有下面几种含义:

List<? super Apple> list = new ArrayList<Apple>();
List<? super Apple> list = new ArrayList<Fruit>();
List<? super Apple> list = new ArrayList<Object>();

当我们尝试通过list来get一个Apple的时候,可能会get得到一个Fruit,这个Fruit可以是Orange等其他类型的Fruit。

根据上面的例子,我们可以总结出一条规律,”Producer Extends, Consumer Super”:

  • “Producer Extends” – 如果你需要一个只读List,用它来produce T,那么使用? extends T
  • “Consumer Super” – 如果你需要一个只写List,用它来consume T,那么使用? super T
  • 如果需要同时读取以及写入,那么我们就不能使用通配符了。

如何阅读过一些Java集合类的源码,可以发现通常我们会将两者结合起来一起用,比如像下面这样:

public class Collections {
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i=0; i<src.size(); i++)
            dest.set(i, src.get(i));
    }
}

类型擦除

Java泛型中最令人苦恼的地方或许就是类型擦除了,特别是对于有C++经验的程序员。类型擦除就是说Java泛型只能用于在编译期间的静态类型检查,然后编译器生成的代码会擦除相应的类型信息,这样到了运行期间实际上JVM根本就知道泛型所代表的具体类型。这样做的目的是因为Java泛型是1.5之后才被引入的,为了保持向下的兼容性,所以只能做类型擦除来兼容以前的非泛型代码。对于这一点,如果阅读Java集合框架的源码,可以发现有些类其实并不支持泛型。

说了这么多,那么泛型擦除到底是什么意思呢?我们先来看一下下面这个简单的例子:

public class Node<T> {
    private T data;
    private Node<T> next;
    public Node(T data, Node<T> next) }
        this.data = data;
        this.next = next;
    }
    public T getData() { return data; }
    // ...
}

编译器做完相应的类型检查之后,实际上到了运行期间上面这段代码实际上将转换成:

public class Node {
    private Object data;
    private Node next;
    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }
    public Object getData() { return data; }
    // ...
}

这意味着不管我们声明Node<String>还是Node<Integer>,到了运行期间,JVM统统视为Node<Object>。有没有什么办法可以解决这个问题呢?这就需要我们自己重新设置bounds了,将上面的代码修改成下面这样:

public class Node<T extends Comparable<T>> {
    private T data;
    private Node<T> next;
    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }
    public T getData() { return data; }
    // ...
}

这样编译器就会将T出现的地方替换成Comparable而不再是默认的Object了:

public class Node {
    private Comparable data;
    private Node next;
    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }
    public Comparable getData() { return data; }
    // ...
}

上面的概念或许还是比较好理解,但其实泛型擦除带来的问题远远不止这些,接下来我们系统地来看一下类型擦除所带来的一些问题,有些问题在C++的泛型中可能不会遇见,但是在Java中却需要格外小心。

问题一

在Java中不允许创建泛型数组,类似下面这样的做法编译器会报错:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

为什么编译器不支持上面这样的做法呢?继续使用逆向思维,我们站在编译器的角度来考虑这个问题。

我们先来看一下下面这个例子:

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

对于上面这段代码还是很好理解,字符串数组不能存放整型元素,而且这样的错误往往要等到代码运行的时候才能发现,编译器是无法识别的。接下来我们再来看一下假设Java支持泛型数组的创建会出现什么后果:

Object[] stringLists = new List<String>[];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
// An ArrayStoreException should be thrown, but the runtime can't detect it.
stringLists[1] = new ArrayList<Integer>();

假设我们支持泛型数组的创建,由于运行时期类型信息已经被擦除,JVM实际上根本就不知道new ArrayList<String>()new ArrayList<Integer>()的区别。类似这样的错误假如出现才实际的应用场景中,将非常难以察觉。

如果你对上面这一点还抱有怀疑的话,可以尝试运行下面这段代码:

public class ErasedTypeEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println(c1 == c2); // true
    }
}

问题二

继续复用我们上面的Node的类,对于泛型代码,Java编译器实际上还会偷偷帮我们实现一个Bridge method。

public class Node<T> {
    public T data;
    public Node(T data) { this.data = data; }
    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

看完上面的分析之后,你可能会认为在类型擦除后,编译器会将Node和MyNode变成下面这样:

public class Node {
    public Object data;
    public Node(Object data) { this.data = data; }
    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}
public class MyNode extends Node {
    public MyNode(Integer data) { super(data); }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

实际上不是这样的,我们先来看一下下面这段代码,这段代码运行的时候会抛出ClassCastException异常,提示String无法转换成Integer:

MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
// Integer x = mn.data;

如果按照我们上面生成的代码,运行到第3行的时候不应该报错(注意我注释掉了第4行),因为MyNode中不存在setData(String data)方法,所以只能调用父类Node的setData(Object data)方法,既然这样上面的第3行代码不应该报错,因为String当然可以转换成Object了,那ClassCastException到底是怎么抛出的?

实际上Java编译器对上面代码自动还做了一个处理:

class MyNode extends Node {
    // Bridge method generated by the compiler
    public void setData(Object data) {
        setData((Integer) data);
    }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
    // ...
}

这也就是为什么上面会报错的原因了,setData((Integer) data);的时候String无法转换成Integer。所以上面第2行编译器提示unchecked warning的时候,我们不能选择忽略,不然要等到运行期间才能发现异常。如果我们一开始加上Node<Integer> n = mn就好了,这样编译器就可以提前帮我们发现错误。

问题三

正如我们上面提到的,Java泛型很大程度上只能提供静态类型检查,然后类型的信息就会被擦除,所以像下面这样利用类型参数创建实例的做法编译器不会通过:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

但是如果某些场景我们想要需要利用类型参数创建实例,我们应该怎么做呢?可以利用反射解决这个问题:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

我们可以像下面这样调用:

List<String> ls = new ArrayList<>();
append(ls, String.class);

实际上对于上面这个问题,还可以采用Factory和Template两种设计模式解决,感兴趣的朋友不妨去看一下Thinking in Java中第15章中关于Creating instance of types(英文版第664页)的讲解,这里我们就不深入了。

问题四

我们无法对泛型代码直接使用instanceof关键字,因为Java编译器在生成代码的时候会擦除所有相关泛型的类型信息,正如我们上面验证过的JVM在运行时期无法识别出ArrayList<Integer>ArrayList<String>的之间的区别:

public static <E> void rtti(List<E> list) {
    if (list instanceof ArrayList<Integer>) {  // compile-time error
        // ...
    }
}
=> { ArrayList<Integer>, ArrayList<String>, LinkedList<Character>, ... }

和上面一样,我们可以使用通配符重新设置bounds来解决这个问题:

public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
        // ...
    }
}

原文链接》

阿里云点播SDK播放MP4的案例

app需要做一个播放mp4的播放器,查询了下阿里云点播SDK 官方文档,可以实现这个功能,需要JIN的so文件和jar包就不再说了,可以看文档需要加载的东西,文档地址-》转到文档

效果图:

布局代码如下:

xml文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="0dp"
    android:id="@+id/container"
    style="@android:style/Theme.NoTitleBar.Fullscreen">

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentTop="false"
        android:layout_alignParentLeft="false"
        android:layout_alignParentStart="false"
        android:id="@+id/GLViewContainer"
        android:background="@android:color/transparent"
        android:layout_alignParentBottom="false"
        android:padding="0dp"
        android:layout_alignParentRight="true"
        android:layout_margin="0dp"
        android:paddingLeft="0dp"
        android:paddingTop="0dp"
        android:paddingRight="0dp"
        android:paddingBottom="0dp"
        android:paddingEnd="0dp"
        android:paddingStart="0dp"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginRight="0dp"
        android:layout_marginBottom="0dp">
    </FrameLayout>

    <ProgressBar
        android:id="@+id/text_tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        style="?android:attr/progressBarStyle"
        android:visibility="gone"></ProgressBar>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center_vertical"
        android:layout_gravity="center_horizontal|bottom"
        android:layout_alignBottom="@id/GLViewContainer"
        android:id="@+id/progress_layout"
        android:background="#50000000"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text="00:00"
            android:id="@+id/current_duration"
            android:background="#00FFFFFF"
            android:textColor="#CCCCCC"
            android:paddingLeft="5dp"
            android:gravity="right" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text=" / "
            android:background="#00FFFFFF"
            android:clickable="false"
            android:textColor="#CCCCCC" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text="00:00"
            android:id="@+id/total_duration"
            android:background="#00FFFFFF"
            android:clickable="false"
            android:textColor="#CCCCCC"/>

        <SeekBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/progress"
            android:progressDrawable="@drawable/seekbar_style"
            android:thumb="@drawable/seek_thumb"
            android:max="100000000"
            android:maxHeight="6dp"
            android:minHeight="6dp"
            android:layout_weight="10"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:progress="0"
            android:thumbOffset="0dp"
            android:focusable="true" />


        <ImageButton
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:id="@+id/pause_button"
            android:layout_gravity="center"
            android:visibility="visible"
            android:background="@drawable/button_player_select"
            android:button="@null"
            android:layout_marginRight="10dp"
            />
    </LinearLayout>
    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:button="@null"
        android:background="@drawable/head_back"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:id="@+id/ib_back"/>

    <ProgressBar
        android:id="@+id/progressBar_bottom"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="2dp"
        android:max="100"
        android:progress="0"
        android:progressDrawable="@drawable/progressbar"
        android:layout_alignParentBottom="true"
        android:visibility="gone"
        />
</RelativeLayout>

progressbar文件

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:id="@android:id/background">

        <shape>

            <corners android:radius="5dip" />

            <gradient
                android:angle="0"
                android:centerColor="@android:color/transparent"
                android:centerY="0.75"
                android:endColor="@android:color/transparent"
                android:startColor="@android:color/transparent" />
        </shape>
    </item>

    <item android:id="@android:id/secondaryProgress">

        <clip>

            <shape>

                <corners android:radius="5dip" />

                <gradient
                    android:angle="0"
                    android:centerColor="#ED4950"
                    android:centerY="0.75"
                    android:endColor="#ED4950"
                    android:startColor="#ED4950" />
            </shape>
        </clip>
    </item>

    <item android:id="@android:id/progress">

        <clip>

            <shape>

                <corners android:radius="5dip" />

                <gradient
                    android:angle="0"
                    android:endColor="#ED4950"
                    android:startColor="#ED4950" />
            </shape>
        </clip>
    </item>

</layer-list>

button_player_select文件(暂停和播放按钮)

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/player_icon_pause" />
    <item android:state_focused="true" android:drawable="@drawable/player_icon_pause" />
    <item android:state_selected="true" android:drawable="@drawable/player_icon_pause" />
    <item android:state_checked="true" android:drawable="@drawable/player_icon_pause" />
    <item android:drawable="@drawable/player_icon_play" />
</selector>

seekbar_style文件

<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <!--  <item
         android:id="@android:id/background" android:drawable="@drawable/player_volume_line_slider"/>
     <item android:id="@android:id/secondaryProgress">
         <clip android:drawable="@drawable/player_volume_line_red" />
     </item>
     <item android:id="@android:id/progress">
         <clip android:drawable="@drawable/player_volume_line_red" />
     </item> -->
    <item android:id="@android:id/background">
        <shape android:shape="line">
            <stroke android:width="2.0dip" android:color="#838080" />
        </shape>
    </item>
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape android:shape="line">
                <stroke android:width="2.0dip" android:color="#838080" />
            </shape>
        </clip>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape android:shape="line">
                <stroke android:width="2.0dip" android:color="#ED4950" />
            </shape>
        </clip>
    </item>
</layer-list>

activity代码如下:

package com.aimeilife.sylive.ui.player;

import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;

import com.aimeilife.sylive.R;
import com.aimeilife.sylive.base.BasicActivity;
import com.aimeilife.sylive.utils.NetUtil;
import com.aimeilife.sylive.utils.Resource;
import com.aimeilife.sylive.utils.Toast;
import com.aimeilife.sylive.utils.live.AnimationUtils;
import com.aimeilife.sylive.widget.ConfirmNetworkLayoutDialog;
import com.alivc.player.AliVcMediaPlayer;
import com.alivc.player.MediaPlayer;
import com.alivc.player.NDKCallback;

import java.util.List;

/**
 * Created by abai on 2017/2/28.
 */

public class PlayerActivity extends BasicActivity {

    public static final String TAG = "PlayerActivity";

    public static final int STATUS_START = 1;
    public static final int STATUS_STOP = 2;
    public static final int STATUS_PAUSE = 3;
    public static final int STATUS_RESUME = 4;


    private AliVcMediaPlayer mPlayer = null;
    private SurfaceHolder mSurfaceHolder = null;
    private SurfaceView mSurfaceView = null;

    private SeekBar mSeekBar = null;
    private ProgressBar mTipView = null;
    private TextView mCurDurationView = null;

    private boolean mEnableUpdateProgress = true;
    private int mLastPercent = -1;
    private int mPlayingIndex = -1;
    private StringBuilder msURI = new StringBuilder("");
    private StringBuilder msTitle = new StringBuilder("");
    private GestureDetector mGestureDetector;
    private int mPosition = 0;
    private int mVolumn = 50;
    private MediaPlayer.VideoScalingMode mScalingMode = MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
    private boolean mMute = false;


    private PowerManager.WakeLock mWakeLock = null;


    private boolean isLastWifiConnected = false;

    // 标记播放器是否已经停止
    private boolean isStopPlayer = false;
    // 标记播放器是否已经暂停
    private boolean isPausePlayer = false;
    private boolean isPausedByUser = false;
    //用来控制应用前后台切换的逻辑
    private boolean isCurrentRunningForeground = true;

    // 重点:发生从wifi切换到4g时,提示用户是否需要继续播放,此处有两种做法:
    // 1.从历史位置从新播放
    // 2.暂停播放,因为存在网络切换,续播有时会不成功
    private BroadcastReceiver connectionReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connectMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
            NetworkInfo mobNetInfo = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
            NetworkInfo wifiNetInfo = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

            Log.d(TAG, "mobile " + mobNetInfo.isConnected() + " wifi " + wifiNetInfo.isConnected());

            if (!isLastWifiConnected && wifiNetInfo.isConnected()) {
                isLastWifiConnected = true;
            }
            if (isLastWifiConnected && mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) {
                isLastWifiConnected = false;
                if (mPlayer != null) {
                    mPosition = mPlayer.getCurrentPosition();
                    // 重点:新增接口,此处必须要将之前的surface释放掉
                    mPlayer.releaseVideoSurface();
                    mPlayer.stop();
                    mPlayer.destroy();
                    mPlayer = null;
                }
                initSurface();
            }
        }
    };
    private ImageButton pause_button = null;
    private View ib_back;
    private LinearLayout progress_layout;
    private String url = "";
    private ProgressBar progressBar_bottom;
    private boolean isPlayerCompleted = false;

    protected void dialog(final View.OnClickListener onClickListener ) {
        final ConfirmNetworkLayoutDialog dialog = ConfirmNetworkLayoutDialog.getInstance().show(this);
        dialog.setCannel(Resource.getResString(R.string.stop_living_look), new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
                _this.finish();
            }
        }).setConfirm(Resource.getResString(R.string.continue_living_look), new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();

                onClickListener.onClick( v );
            }
        }).setHintHideMsg().setTitleHint(Resource.getResString(R.string.network_hint_look));
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.e(TAG, "onCreate.");
        super.onCreate(savedInstanceState);
        Bundle bundle = getIntent().getExtras();
        if (bundle == null) {
            finish();
            return;
        }
        url = bundle.getString("url");

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(connectionReceiver, intentFilter);

        setContentView(R.layout.activity_player);
        mPlayingIndex = -1;

        acquireWakeLock();

        init();
    }

    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                                float distanceX, float distanceY) {

            final double FLING_MIN_DISTANCE = 0.5;
            final double FLING_MIN_VELOCITY = 0.5;

            if (e1.getY() - e2.getY() > FLING_MIN_DISTANCE
                    && Math.abs(distanceY) > FLING_MIN_VELOCITY) {
                onVolumeSlide(1);
            }
            if (e1.getY() - e2.getY() < FLING_MIN_DISTANCE
                    && Math.abs(distanceY) > FLING_MIN_VELOCITY) {
                onVolumeSlide(-1);
            }

            return super.onScroll(e1, e2, distanceX, distanceY);
        }
    }

    private void onVolumeSlide(int vol) {
        if (mPlayer != null) {
            mVolumn += vol;
            if (mVolumn > 100)
                mVolumn = 100;
            if (mVolumn < 0)
                mVolumn = 0;
            mPlayer.setVolume(mVolumn);
        }
    }

    private void acquireWakeLock() {
        if (mWakeLock == null) {
            PowerManager pMgr = (PowerManager) this.getSystemService(this.POWER_SERVICE);
            mWakeLock = pMgr.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
                    "SmsSyncService.sync() wakelock.");

        }
        mWakeLock.acquire();
    }

    private void releaseWakeLock() {
        mWakeLock.release();
        mWakeLock = null;
    }

    private void update_progress(int ms) {
        if (mEnableUpdateProgress) {
            mSeekBar.setProgress(ms);
        }
        return;
    }
//
//    private void update_second_progress(int ms) {
//        if (mEnableUpdateProgress) {
//            mSeekBar.setSecondaryProgress(ms);
//            progressBar_bottom.setSecondaryProgress( ms );
//        }
//        return;
//    }

    private void show_progress_ui(boolean bShowPause) {


        if (bShowPause) {
            if( progress_layout.getVisibility() == View.GONE  ) {
                progress_layout.setVisibility(View.VISIBLE);
                progress_layout.setAnimation(AnimationUtils.getShowAnim());
            }
            ib_back.setVisibility(View.VISIBLE);
            progressBar_bottom.setVisibility( View.GONE );
        } else {
            if( progress_layout.getVisibility() == View.VISIBLE  ) {
                progress_layout.setAnimation(AnimationUtils.getHideAnim());
                progress_layout.setVisibility(View.GONE);
            }
            ib_back.setVisibility(View.GONE);
            progressBar_bottom.setVisibility( View.VISIBLE );
//            video_title.setVisibility(View.GONE);
        }
    }

    private void show_pause_ui(boolean bShowPauseBtn ) {
        pause_button.setSelected( bShowPauseBtn ?  false : true );
        return;
    }


    private void show_buffering_ui(boolean bShowTip) {

        mTipView.setVisibility(bShowTip ? View.VISIBLE : View.GONE);

    }

    private void update_total_duration(final int ms) {
        int var = (int) (ms / 1000.0f + 0.5f);
        int min = var / 60;
        int sec = var % 60;
        TextView total = (TextView) findViewById(R.id.total_duration);
        total.setText( String.format("%1$02d:%2$02d", min, sec) );
        progressBar_bottom.setMax(100);
        progressBar_bottom.setProgress(0);
        progressBar_bottom.setSecondaryProgress(0); //reset progress now.

        SeekBar sb = (SeekBar) findViewById(R.id.progress);
        sb.setMax(ms);
        sb.setKeyProgressIncrement(10000); //5000ms = 5sec.
        sb.setProgress(0);
        sb.setSecondaryProgress(0); //reset progress now.

        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            public void onProgressChanged(SeekBar seekBar, int i, boolean fromuser) {
                int var = (int) (i / 1000.0f + 0.5f);
                int min = var / 60;
                int sec = var % 60;
                String strCur = String.format("%1$02d:%2$02d", min, sec);
                mCurDurationView.setText(strCur);

                progressBar_bottom.setProgress(  var * 100 / (int) (ms / 1000.0f + 0.5f)  );

            }

            public void onStartTrackingTouch(SeekBar seekBar) {
                mEnableUpdateProgress = false;
            }

            public void onStopTrackingTouch(SeekBar seekBar) {
                int ms = seekBar.getProgress();
                if (mPlayer != null) {
                    mPlayer.seekTo(ms);
                }
            }
        });

        return;
    }

    private void report_error(String err, boolean bshow) {

        Toast.toast(err);
        return;
    }


    private SurfaceHolder.Callback mSurfaceHolderCB = new SurfaceHolder.Callback() {
        @SuppressWarnings("deprecation")
        public void surfaceCreated(SurfaceHolder holder) {
            holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
            holder.setKeepScreenOn(true);
            //Log.d(TAG, "AlivcPlayer onSurfaceCreated.");
            // 重点:
            if (mPlayer != null) {
                // 对于从后台切换到前台,需要重设surface;部分手机锁屏也会做前后台切换的处理
                mPlayer.setVideoSurface(mSurfaceView.getHolder().getSurface());
            } else {
                // 创建并启动播放器

                if (NetUtil.isWifi(_this)) {
                    startToPlay();
                } else {
                    dialog(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startToPlay();
                        }
                    });
                }


            }
        }
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            //Log.d(TAG, "onSurfaceChanged is valid ? " + holder.getSurface().isValid());
            if (mPlayer != null)
                mPlayer.setSurfaceChanged();
        }
        public void surfaceDestroyed(SurfaceHolder holder) {
            //Log.d(TAG, "onSurfaceDestroy.");

            if (mPlayer != null) {
                mPlayer.releaseVideoSurface();
            }
        }
    };

    //设置播放模式
    public void switchScalingMode(View view) {
        if (mPlayer != null) {
            if (mScalingMode == MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING) {
                mPlayer.setVideoScalingMode(MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT);
                mScalingMode = MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT;
            } else {
                mPlayer.setVideoScalingMode(MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
                mScalingMode = MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
            }
        }
    }

    //设置静音模式
    public void switchMute(View view) {
        if (mPlayer != null) {
            if (mMute == false) {
                mMute = true;
                mPlayer.setMuteMode(true);
            } else {
                mMute = false;
                mPlayer.setMuteMode(false);
            }
        }
    }

    /**
     * 重点:初始化播放器使用的SurfaceView,此处的SurfaceView采用动态添加
     *
     * @return 是否成功
     */
    private boolean initSurface() {
        FrameLayout frameContainer = (FrameLayout) findViewById(R.id.GLViewContainer);
        frameContainer.setBackgroundColor(Color.rgb(0, 0, 0));
        mSurfaceView = new SurfaceView(this);
        mGestureDetector = new GestureDetector(this, new MyGestureListener());

        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT);
        params.gravity = Gravity.CENTER;
        mSurfaceView.setLayoutParams(params);
        // 为避免重复添加,事先remove子view
        frameContainer.removeAllViews();
        frameContainer.addView(mSurfaceView);

        mSurfaceView.setZOrderOnTop(false);

        mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
            private long mLastDownTimestamp = 0;

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                if (mGestureDetector.onTouchEvent(event))
                    return true;

                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    mLastDownTimestamp = System.currentTimeMillis();
                    return true;
                }

                if (event.getAction() == MotionEvent.ACTION_UP) {
                    show_progress_ui(true);
                    mTimerHandler.removeCallbacks(mUIRunnable);
                    mTimerHandler.postDelayed(mUIRunnable, 3000);
                    return true;
                }
                return false;
            }
        });

        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(mSurfaceHolderCB);


        return true;
    }

    private boolean init() {
        mSeekBar = (SeekBar) findViewById(R.id.progress);
        mTipView = (ProgressBar) findViewById(R.id.text_tip);
        mCurDurationView = (TextView) findViewById(R.id.current_duration);
        progressBar_bottom  = (ProgressBar) findViewById(R.id.progressBar_bottom);
        //播放暂停的控制
        pause_button = (ImageButton) findViewById(R.id.pause_button);
        ib_back = findViewById(R.id.ib_back);
        progress_layout = (LinearLayout) findViewById(R.id.progress_layout);
        ib_back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        pause_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (pause_button.isSelected()) {//暂停
                    pause_button.setSelected(false);
                    if (mPlayer != null && mPlayer.getDuration() > 0) {
                        pause();
                    }
                } else { //播放
                    pause_button.setSelected(true);
                    if (mPlayer != null  ) {
                        if( isPlayerCompleted ){//是否播放完成
                            isPlayerCompleted = false;
                            mPosition = 0;
                            // 重点:新增接口,此处必须要将之前的surface释放掉
                            mPlayer.releaseVideoSurface();
                            mPlayer.stop();
                            mPlayer.destroy();
                            mPlayer = null;
                            initSurface();
                        }else {
                            if (!mPlayer.isPlaying() && mPlayer.getDuration() > 0) {
                                start();
                            }
                        }
                    }
                }
            }
        });
        //3秒关闭操作框
        mTimerHandler.postDelayed(mUIRunnable, 3000);
        initSurface();
        return true;
    }


    private boolean startToPlay() {
        Log.d(TAG, "start play.");
        resetUI();

        if (mPlayer == null) {
            // 初始化播放器
            mPlayer = new AliVcMediaPlayer(this, mSurfaceView);
            mPlayer.setPreparedListener(new VideoPreparedListener());
            mPlayer.setErrorListener(new VideoErrorListener());
            mPlayer.setInfoListener(new VideoInfolistener());
            mPlayer.setSeekCompleteListener(new VideoSeekCompletelistener());
            mPlayer.setCompletedListener(new VideoCompletelistener());
            mPlayer.setVideoSizeChangeListener(new VideoSizeChangelistener());
            mPlayer.setBufferingUpdateListener(new VideoBufferUpdatelistener());
            mPlayer.setStopedListener(new VideoStoppedListener());
            mPlayer.setMediaType(MediaPlayer.MediaType.Vod);
            // 如果同时支持软解和硬解是有用
            //Bundle bundle = (Bundle) getIntent().getExtras();
            mPlayer.setDefaultDecoder(1);
            // 重点: 在调试阶段可以使用以下方法打开native log
            mPlayer.enableNativeLog();
            mPlayer.setVideoScalingMode(MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
//            if (mPosition != 0) {
//                mPlayer.seekTo(mPosition);
//            }
        }
        mPlayer.prepareAndPlay(url);
        return true;

    }

    private void resetUI() {
        mSeekBar.setProgress(0);
        progressBar_bottom.setProgress( 0 );
        show_pause_ui(false );
        show_progress_ui(false);
    }

    //pause the video
    private void pause() {
        if (mPlayer != null) {
            mPlayer.pause();
            isPausePlayer = true;
            isPausedByUser = true;
            show_pause_ui(true );
            show_progress_ui(true);
        }
    }

    //start the video
    private void start() {

        if (mPlayer != null) {
            isPausePlayer = false;
            isPausedByUser = false;
            isStopPlayer = false;
            mPlayer.play();
            show_pause_ui(false );
            show_progress_ui(false);
        }
    }

    //stop the video
    private void stop() {
        Log.d(TAG, "AudioRender: stop play");
        if (mPlayer != null) {
            mPlayer.stop();
            mPlayer.destroy();
            mPlayer = null;
        }
    }

    /**
     * 准备完成监听器:调度更新进度
     */
    private class VideoPreparedListener implements MediaPlayer.MediaPlayerPreparedListener {

        @Override
        public void onPrepared() {
            if (mPlayer != null) {
                mPlayer.setVideoScalingMode(MediaPlayer.VideoScalingMode.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
                update_total_duration(mPlayer.getDuration());
                mTimerHandler.postDelayed(mRunnable, 1000);
                show_progress_ui(true);
                mTimerHandler.postDelayed(mUIRunnable, 3000);
                if (mPosition != 0) {
                    mPlayer.seekTo(mPosition);
                }
            }
        }
    }


    /**
     * 错误处理监听器
     */
    private class VideoErrorListener implements MediaPlayer.MediaPlayerErrorListener {

        public void onError(int what, int extra) {
            int errCode = 0;

            if (mPlayer == null) {
                return;
            }

            errCode = mPlayer.getErrorCode();
            switch (errCode) {
                case MediaPlayer.ALIVC_ERR_LOADING_TIMEOUT:
                    report_error("缓冲超时,请确认网络连接正常后重试", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_NO_INPUTFILE:
                    report_error("no input file", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_NO_VIEW:
                    report_error("no surface", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_INVALID_INPUTFILE:
                    report_error("视频资源或者网络不可用1", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_NO_SUPPORT_CODEC:
                    report_error("no codec", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_FUNCTION_DENIED:
                    report_error("no priority", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_UNKNOWN:
                    report_error("unknown error", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_NO_NETWORK:
                    report_error("视频资源或者网络不可用2", true);
                    mPlayer.reset();
                    break;
                case MediaPlayer.ALIVC_ERR_ILLEGALSTATUS:
                    report_error("illegal call", true);
                    break;
                case MediaPlayer.ALIVC_ERR_NOTAUTH:
                    report_error("auth failed", true);
                    break;
                case MediaPlayer.ALIVC_ERR_READD:
                    report_error("资源访问失败,请重试", true);
                    mPlayer.reset();
                    break;
                default:
                    break;

            }
        }
    }

    /**
     * 信息通知监听器:重点是缓存开始/结束
     */
    private class VideoInfolistener implements MediaPlayer.MediaPlayerInfoListener {

        public void onInfo(int what, int extra) {
            Log.d(TAG, "onInfo what = " + what + " extra = " + extra);
            switch (what) {
                case MediaPlayer.MEDIA_INFO_UNKNOW:
                    break;
                case MediaPlayer.MEDIA_INFO_BUFFERING_START:
                    //pause();
                    show_buffering_ui(true);
                    break;
                case MediaPlayer.MEDIA_INFO_BUFFERING_END:
                    //start();
                    show_buffering_ui(false);
                    break;
                case MediaPlayer.MEDIA_INFO_TRACKING_LAGGING:
                    break;
                case MediaPlayer.MEDIA_INFO_NETWORK_ERROR:
                    report_error("�������!", true);
                    break;
                case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
                    if (mPlayer != null)
                        Log.d(TAG, "on Info first render start : " + ((long) mPlayer.getPropertyDouble(AliVcMediaPlayer.FFP_PROP_DOUBLE_1st_VFRAME_SHOW_TIME, -1) - (long) mPlayer.getPropertyDouble(AliVcMediaPlayer.FFP_PROP_DOUBLE_OPEN_STREAM_TIME, -1)));

                    break;
            }
        }
    }

    /**
     * 快进完成监听器
     */
    private class VideoSeekCompletelistener implements MediaPlayer.MediaPlayerSeekCompleteListener {

        public void onSeekCompleted() {
            mEnableUpdateProgress = true;
        }
    }

    /**
     * 视频播完监听器
     */
    private class VideoCompletelistener implements MediaPlayer.MediaPlayerCompletedListener {

        public void onCompleted() {
            //播放完成
            show_pause_ui( true );
            isPlayerCompleted = true;
            if( mPlayer !=null )
            mSeekBar.setProgress( mPlayer.getDuration() );
        }
    }

    /**
     * 视频大小变化监听器
     */
    private class VideoSizeChangelistener implements MediaPlayer.MediaPlayerVideoSizeChangeListener {

        public void onVideoSizeChange(int width, int height) {
            Log.d(TAG, "onVideoSizeChange width = " + width + " height = " + height);
            //mSurfaceView.getHolder().setFixedSize(width, height);
        }
    }

    /**
     * 视频缓存变化监听器: percent 为 0~100之间的数字】
     */
    private class VideoBufferUpdatelistener implements MediaPlayer.MediaPlayerBufferingUpdateListener {

        public void onBufferingUpdateListener(int percent) {

        }
    }

    private class VideoStoppedListener implements MediaPlayer.MediaPlayerStopedListener {
        @Override
        public void onStopped() {
            Log.d(TAG, "onVideoStopped.");
        }
    }

    @Override
    protected void onDestroy() {
        Log.e(TAG, "AudioRender: onDestroy.");
        if (mPlayer != null) {
//            stop();
            mTimerHandler.removeCallbacks(mRunnable);
        }

        releaseWakeLock();

        if (connectionReceiver != null) {
            unregisterReceiver(connectionReceiver);
        }
        // 重点:在 activity destroy的时候,要停止播放器并释放播放器
        if (mPlayer != null) {
            mPosition = mPlayer.getCurrentPosition();
            stop();
        }

        super.onDestroy();
        return;
    }

    @Override
    public void onResume() {
        Log.e(TAG, "onResume");
        super.onResume();
        // 重点:如果播放器是从锁屏/后台切换到前台,那么调用player.stat
        if (mPlayer != null && !isStopPlayer && isPausePlayer) {
            if (!isPausedByUser) {
                isPausePlayer = false;
                mPlayer.play();
                // 更新ui
                show_pause_ui(false );
                show_progress_ui(false);
            }
        }

    }

    @Override
    protected void onStart() {
        Log.e(TAG, "onStart.");
        super.onStart();
        if (!isCurrentRunningForeground) {
            Log.d(TAG, ">>>>>>>>>>>>>>>>>>>切到前台 activity process");
        }
    }

    @Override
    public void onPause() {
        Log.e(TAG, "onPause." + isStopPlayer + " " + isPausePlayer + " " + (mPlayer == null));
        super.onPause();
        // 重点:播放器没有停止,也没有暂停的时候,在activity的pause的时候也需要pause
        if (!isStopPlayer && !isPausePlayer && mPlayer != null) {
            Log.e(TAG, "onPause mpayer.");
            mPlayer.pause();
            isPausePlayer = true;
        }
    }

    @Override
    protected void onStop() {
        Log.e(TAG, "onStop.");
        super.onStop();

        isCurrentRunningForeground = isRunningForeground();
        if (!isCurrentRunningForeground) {
            Log.d(TAG, ">>>>>>>>>>>>>>>>>>>切到后台 activity process");
        }
    }
    private Handler mTimerHandler = new Handler();
    Runnable mRunnable = new Runnable() {
        @Override
        public void run() {

            if (mPlayer != null && mPlayer.isPlaying())
                update_progress(mPlayer.getCurrentPosition());

            mTimerHandler.postDelayed(this, 1000);
        }
    };

    Runnable mUIRunnable = new Runnable() {
        @Override
        public void run() {
            show_progress_ui(false);
        }
    };


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_BACK) {
            isStopPlayer = true;
        }
        return super.onKeyDown(keyCode, event);
    }

    // 重点:判定是否在前台工作
    public boolean isRunningForeground() {
        ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();
        // 枚举进程
        for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {
            if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                if (appProcessInfo.processName.equals(this.getApplicationInfo().processName)) {
                    Log.d(TAG, "EntryActivity isRunningForeGround");
                    return true;
                }
            }
        }
        Log.d(TAG, "EntryActivity isRunningBackGround");
        return false;
    }

}

代码activity中涉及到我的项目中的东西,就没有说明了,如果有不懂的地方可以留言。这样就实现播放器的功能。

 

摇摆.难以抗拒的非理性诱惑

读了这本书花了两天时间零零碎碎的时间,感觉没有彻底读透作者表达的意思,从标题看大概中心思想,就是人在面临选择,价值取向,非理性思维判断中摇摆不定,或是被表面的东西所牵累做出错误的选择。
本书中举例了很多逻辑例证来做推理,我们在面对生活所遇到的事情,也三思 放弃该放弃,获取应获,多听反对拒绝声音,他们也许让你做出更理性的决定,生活没有多面选择,在人生路上总是坎坷不平,这就是TMD人生。

本书值得一读,

我们终将奔赴向前

时间如梭,太快太快,每分每秒就在弹指间流过,而立之年我依然盲目的在向前奔赴,不管任何代价,不管压力担子有多重,往事如烟莫去回首,未来在哪里?

我终将向前。

今天听了这几首音乐,又增加了我昂扬斗志的勇气,节奏舒缓,仿佛看到青绿的草原,雪白的雪山,一切都是美好优雅。希望不是泡沫,是皮球,你越努力拍跳得越高,飞得更远。未来曙光比七彩泡沫更加灿烂!

当和煦春风吹拂过细节与平淡的山顶,当往昔的时光洒向林间的湖泊,这温暖如同爱人的怀抱,美好且静默。我们手握着夜间孤芳自赏的花朵,在流离失所的纯白和微妙的纯粹中,用一种自信满满的暖色调涂抹着这世界。

我们终将奔赴向前。

去听听-》

java单例模式的几种方法

最近阅读java多线程编程技术的书,看到其中的单例模式的使用,感觉值得分享一下,下面介绍几种单例模式的实例化方法:

饿汉模式

class Object{

private static Object object = new Object();

public static Object getInstance(){

return object;

}

}

懒汉模式:

1,使用DCL双检查机制

class Object{

private volatile static Object object;

public static  Object getInstance(){

if( object ==null ){

     object = new Object();

}

return object;

}

}

2,使用静态内置类实现单例模式

class Object{

private static class ObjectHandle{

private static Object object = new Object();

}

private  Object(){

}

public static  Object getInstance(){

return ObjectHandle.object;

}

}

3,使用static 代码块实现单例模式

class Object {

private static Object object;

static {

object = new Object();

}

public Object(){

}

public static  Object getInstance(){

return object;

}

}

以上就是几种线程安全的单例模式的写法。