日期: 2021 年 10 月 22 日

利用Theme自定义Activity间的切换动画

利用Theme自定义Activity间的切换动画

 

大多Android系统默认Activity间的动画切换效果为,右边滑入,左边滑出;有时候我们的需求可能是要求所有Activity的切换为淡入淡出的效果,这时候就可能需要改变一下默认的切换风格。

下面开始实现:

首先在res文件夹下建立anim文件夹,然后在里面建立fade_in.xml和fade_out.xml两个动画资源

fade_in.xml

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <alpha xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:duration=“300”  
  4.     android:fromAlpha=“0.0”  
  5.     android:interpolator=“@android:anim/accelerate_interpolator”  
  6.     android:toAlpha=“1.0” />  

fade_out.xml

 

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <alpha xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:duration=“300”  
  4.     android:fromAlpha=“1.0”  
  5.     android:interpolator=“@android:anim/accelerate_interpolator”  
  6.     android:toAlpha=“0.0” />  

然后在values文件夹下的styles.xml中的resources标签内写:

 

 

  1. <style name=“Anim_fade” parent=”android:Theme.NoTitleBar”>  
  2.        <item name=“android:windowAnimationStyle”>@style/fade</item>  
  3.    </style>  
  4.    <style name=“fade” parent=”@android:style/Animation.Activity”>  
  5.        <item name=“android:activityOpenEnterAnimation”>@anim/fade_in</item>  
  6.        <item name=“android:activityOpenExitAnimation”>@anim/fade_out</item>  
  7.        <item name=“android:activityCloseEnterAnimation”>@anim/fade_in</item>  
  8.        <item name=“android:activityCloseExitAnimation”>@anim/fade_out</item>  
  9.    </style>  

*后一步在AndroidManifest.xml中的Activity的声明上加入android:theme=”@style/Anim_fade”

 

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.customanimationforactivity”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk  
  7.         android:minSdkVersion=“10”  
  8.         android:targetSdkVersion=“10” />  
  9.     <application  
  10.         android:allowBackup=“true”  
  11.         android:icon=“@drawable/ic_launcher”  
  12.         android:label=“@string/app_name”  
  13.         android:theme=“@android:style/Theme.NoTitleBar” >  
  14.         <activity  
  15.             android:name=“com.example.customanimationforactivity.MainActivity”  
  16.             android:label=“@string/app_name”  
  17.             android:theme=“@style/Anim_fade” >  
  18.             <intent-filter>  
  19.                 <action android:name=“android.intent.action.MAIN” />  
  20.                 <category android:name=“android.intent.category.LAUNCHER” />  
  21.             </intent-filter>  
  22.         </activity>  
  23.         <activity android:name=“.AppActivity” android:theme=”@style/Anim_fade” >  
  24.         </activity>  
  25.     </application>  
  26. </manifest>  

贴下Splash Activity的代码:

 

 

  1. package com.example.customanimationforactivity;  
  2. import android.app.Activity;  
  3. import android.content.Intent;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. public class MainActivity extends Activity  
  7. {
  8.     private Handler handler = new Handler();  
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState)  
  11.     {
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);
  14.         handler.postDelayed(new Runnable()  
  15.         {
  16.             @Override  
  17.             public void run()  
  18.             {
  19.                 Intent intent = new Intent(MainActivity.this, AppActivity.class);  
  20.                 startActivity(intent);
  21.                 finish();
  22.             }
  23.         }, 1000);  
  24.     }
  25. }

 

彻底理解ThreadLocal

彻底理解ThreadLocal

ThreadLocal是什么

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

  • void set(Object value)设置当前线程的线程局部变量的值。
  • public Object get()该方法返回当前线程所对应的线程局部变量。
  • public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
  • protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

 

  1. package com.test;  
  2. public class TestNum {  
  3.     // ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值  
  4.     private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {  
  5.         public Integer initialValue() {  
  6.             return 0;  
  7.         }
  8.     };
  9.     // ②获取下一个序列值  
  10.     public int getNextNum() {  
  11.         seqNum.set(seqNum.get() + 1);  
  12.         return seqNum.get();  
  13.     }
  14.     public static void main(String[] args) {  
  15.         TestNum sn = new TestNum();  
  16.         // ③ 3个线程共享sn,各自产生序列号  
  17.         TestClient t1 = new TestClient(sn);  
  18.         TestClient t2 = new TestClient(sn);  
  19.         TestClient t3 = new TestClient(sn);  
  20.         t1.start();
  21.         t2.start();
  22.         t3.start();
  23.     }
  24.     private static class TestClient extends Thread {  
  25.         private TestNum sn;  
  26.         public TestClient(TestNum sn) {  
  27.             this.sn = sn;  
  28.         }
  29.         public void run() {  
  30.             for (int i = 0; i < 3; i++) {  
  31.                 // ④每个线程打出3个序列值  
  32.                 System.out.println(“thread[” + Thread.currentThread().getName() + “] –> sn[”  
  33.                          + sn.getNextNum() + “]”);  
  34.             }
  35.         }
  36.     }
  37. }

 

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个TestNum实例。运行以上代码,在控制台上输出以下的结果:

 

thread[Thread-0] –> sn[1]
thread[Thread-1] –> sn[1]
thread[Thread-2] –> sn[1]
thread[Thread-1] –> sn[2]
thread[Thread-0] –> sn[2]
thread[Thread-1] –> sn[3]
thread[Thread-2] –> sn[2]
thread[Thread-0] –> sn[3]
thread[Thread-2] –> sn[3]

 

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个TestNum实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

 

Thread同步机制的比较

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。

概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring使用ThreadLocal解决线程安全问题我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,*大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:

通通透透理解ThreadLocal

  同一线程贯通三层这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。

下面的实例能够体现Spring对有状态Bean的改造思路:

代码清单3 TestDao:非线程安全

 

  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.SQLException;  
  4. import java.sql.Statement;  
  5. public class TestDao {  
  6.     private Connection conn;// ①一个非线程安全的变量  
  7.     public void addTopic() throws SQLException {  
  8.         Statement stat = conn.createStatement();// ②引用非线程安全变量  
  9.         // …  
  10.     }
  11. }

 

 

由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:

代码清单4 TestDao:线程安全

 

  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.SQLException;  
  4. import java.sql.Statement;  
  5. public class TestDaoNew {  
  6.     // ①使用ThreadLocal保存Connection变量  
  7.     private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();  
  8.     public static Connection getConnection() {  
  9.         // ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,  
  10.         // 并将其保存到线程本地变量中。  
  11.         if (connThreadLocal.get() == null) {  
  12.             Connection conn = getConnection();
  13.             connThreadLocal.set(conn);
  14.             return conn;  
  15.         } else {  
  16.             return connThreadLocal.get();// ③直接返回线程本地变量  
  17.         }
  18.     }
  19.     public void addTopic() throws SQLException {  
  20.         // ④从ThreadLocal中获取线程对应的Connection  
  21.         Statement stat = getConnection().createStatement();
  22.     }
  23. }

 

 

不同的线程在使用TopicDao时,先判断connThreadLocal.get()是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。

当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一事务多DAO共享同一Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。

 

ConnectionManager.java

  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.DriverManager;  
  4. import java.sql.SQLException;  
  5. public class ConnectionManager {  
  6.     private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {  
  7.         @Override  
  8.         protected Connection initialValue() {  
  9.             Connection conn = null;  
  10.             try {  
  11.                 conn = DriverManager.getConnection(
  12.                         “jdbc:mysql://localhost:3306/test”, “username”,  
  13.                         “password”);  
  14.             } catch (SQLException e) {  
  15.                 e.printStackTrace();
  16.             }
  17.             return conn;  
  18.         }
  19.     };
  20.     public static Connection getConnection() {  
  21.         return connectionHolder.get();  
  22.     }
  23.     public static void setConnection(Connection conn) {  
  24.         connectionHolder.set(conn);
  25.     }
  26. }

 

 

java.lang.ThreadLocal<T>的具体实现

那么到底ThreadLocal类是如何实现这种“为每个线程提供不同的变量拷贝”的呢?先来看一下ThreadLocal的set()方法的源码是如何实现的:

 

  1. /** 
  2.     * Sets the current thread’s copy of this thread-local variable 
  3.     * to the specified value.  Most subclasses will have no need to 
  4.     * override this method, relying solely on the {@link #initialValue} 
  5.     * method to set the values of thread-locals. 
  6.     * 
  7.     * @param value the value to be stored in the current thread’s copy of 
  8.     *        this thread-local. 
  9.     */  
  10.    public void set(T value) {  
  11.        Thread t = Thread.currentThread();
  12.        ThreadLocalMap map = getMap(t);
  13.        if (map != null)  
  14.            map.set(this, value);  
  15.        else  
  16.            createMap(t, value);
  17.    }

在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。

线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。

为了加深理解,我们接着看上面代码中出现的getMap和createMap方法的实现:

 

  1. /** 
  2.  * Get the map associated with a ThreadLocal. Overridden in 
  3.  * InheritableThreadLocal. 
  4.  * 
  5.  * @param  t the current thread 
  6.  * @return the map 
  7.  */  
  8. ThreadLocalMap getMap(Thread t) {
  9.     return t.threadLocals;  
  10. }
  11. /** 
  12.  * Create the map associated with a ThreadLocal. Overridden in 
  13.  * InheritableThreadLocal. 
  14.  * 
  15.  * @param t the current thread 
  16.  * @param firstValue value for the initial entry of the map 
  17.  * @param map the map to store. 
  18.  */  
  19. void createMap(Thread t, T firstValue) {  
  20.     t.threadLocals = new ThreadLocalMap(this, firstValue);  
  21. }

接下来再看一下ThreadLocal类中的get()方法:

 

  1. /** 
  2.  * Returns the value in the current thread’s copy of this 
  3.  * thread-local variable.  If the variable has no value for the 
  4.  * current thread, it is first initialized to the value returned 
  5.  * by an invocation of the {@link #initialValue} method. 
  6.  * 
  7.  * @return the current thread’s value of this thread-local 
  8.  */  
  9. public T get() {  
  10.     Thread t = Thread.currentThread();
  11.     ThreadLocalMap map = getMap(t);
  12.     if (map != null) {  
  13.         ThreadLocalMap.Entry e = map.getEntry(this);  
  14.         if (e != null)  
  15.             return (T)e.value;  
  16.     }
  17.     return setInitialValue();  
  18. }

再来看setInitialValue()方法:

 

  1. /** 
  2.     * Variant of set() to establish initialValue. Used instead 
  3.     * of set() in case user has overridden the set() method. 
  4.     * 
  5.     * @return the initial value 
  6.     */  
  7.    private T setInitialValue() {  
  8.        T value = initialValue();
  9.        Thread t = Thread.currentThread();
  10.        ThreadLocalMap map = getMap(t);
  11.        if (map != null)  
  12.            map.set(this, value);  
  13.        else  
  14.            createMap(t, value);
  15.        return value;  
  16.    }

获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。

进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。

 

小结

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

ConnectionManager.java

  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.DriverManager;  
  4. import java.sql.SQLException;  
  5. public class ConnectionManager {  
  6.     private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {  
  7.         @Override  
  8.         protected Connection initialValue() {  
  9.             Connection conn = null;  
  10.             try {  
  11.                 conn = DriverManager.getConnection(
  12.                         “jdbc:mysql://localhost:3306/test”, “username”,  
  13.                         “password”);  
  14.             } catch (SQLException e) {  
  15.                 e.printStackTrace();
  16.             }
  17.             return conn;  
  18.         }
  19.     };
  20.     public static Connection getConnection() {  
  21.         return connectionHolder.get();  
  22.     }
  23.     public static void setConnection(Connection conn) {  
  24.         connectionHolder.set(conn);
  25.     }
  26. }

后记

看到网友评论的很激烈,甚至关于ThreadLocalMap不是ThreadLocal里面的,而是Thread里面的这种评论都出现了,于是有了这个后记,下面先把jdk源码贴上,源码*有说服力了。

  1. /** 
  2.      * ThreadLocalMap is a customized hash map suitable only for 
  3.      * maintaining thread local values. No operations are exported 
  4.      * outside of the ThreadLocal class. The class is package private to 
  5.      * allow declaration of fields in class Thread.  To help deal with 
  6.      * very large and long-lived usages, the hash table entries use 
  7.      * WeakReferences for keys. However, since reference queues are not 
  8.      * used, stale entries are guaranteed to be removed only when 
  9.      * the table starts running out of space. 
  10.      */  
  11.     static class ThreadLocalMap {…}  

源码就是以上,这源码自然是在ThreadLocal里面的,有截图为证。%title插图%num

 

本文是自己在学习ThreadLocal的时候,一时兴起,深入看了源码,思考了此类的作用、使用范围,进而联想到对传统的synchronize共享变量线程安全的问题进行比较,而总结的博文,总结一句话就是一个是锁机制进行时间换空间,一个是存储拷贝进行空间换时间。

android的窗口机制分析——UI管理系统

android的窗口机制分析——UI管理系统

    Activity可以看做是整个Android系统的人机接口,它提供了一个窗口来绘制UI,每个Activity在启动时,我们都需要给它设置一个Content view,作为Activity所呈现的UI内容,这个过程是通过setContentView()方法来实现的。

众所周知,android系统中强化了view的概念,主要是体现在对view的管理上,Android中的view以2种形态存在,单一的View和多个View组成的ViewGroup。Content view是以ViewGroup的形式存在的,也就是说在一个Activity窗口中可以添加多个View,这样就实现了Android窗口系统的UI多样化。activity启动时给activity窗口设置的Content view 是从xml文件中解析出来的,那么android是怎么样对这个ContentView进行管理的呢,它的内部实现逻辑又是怎样的呢?

在进行分析之前,首先给出一个Activity的window和view系统的层级关系,这个层级关系就是在Activity设置完ContentView之后的状况。

如下图。

%title插图%num

下面来一一介绍各个层级的含义与作用

1.1  PhoneWindow

PhoneWindow是Android中的*基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。

1.2  DecorView

DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西,它主要有如下几个功能,可能不全:

A.  Dispatch ViewRoot分发来的key、touch、trackball等外部事件;

B.  DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。可以称这些属性为Window decorations。

C.  作为PhoneWindow与ViewRoot之间的桥梁,ViewRoot通过DecorView设置窗口属性。

1.3  System Layout

目前android根据用户需求预设了几种UI 风格,通过PhoneWindow通过解析预置的layout.xml来获得包含有不同Window decorations的layout,我们称之为System Layout,我们将这个System Layout添加到DecorView中,目前android提供了8种System Layout,如下图。

预设风格可以通过PhoneWindow方法requestFeature()来设置,需要注意的是这个方法需要在setContentView()方法调用之前调用。

1.4  Content Parent

Content Parent这个ViewGroup对象才是真真正正的ContentView的parent,我们的ContentView终于找到了寄主,它其实对应的是System Layout中的id为”content”的一个FrameLayout。这个FrameLayout对象包括的才是我们的Activity的layout(每个System Layout都会有这么一个id为”content”的一个FrameLayout)。

%title插图%num

1.5  Activity Layout

这个ActivityLayout便是我们需要向窗口设置的ContentView,现在我们发现其实它的地位很低,同时这一部分才是和user交互的UI部分,其上的几层并不能响应并完成user输入所期望达到的目的。

UML各种图总结-精华

UML各种图总结-精华

UML(Unified Modeling Language)是一种统一建模语言,为面向对象开发系统的产品进行说明、可视化、和编制文档的一种标准语言。下面将对UML的九种图+包图的基本概念进行介绍以及各个图的使用场景。

一、基本概念

如下图所示,UML图分为用例视图、设计视图、进程视图、实现视图和拓扑视图,又可以静动分为静态视图和动态视图。静态图分为:用例图,类图,对象图,包图,构件图,部署图。动态图分为:状态图,活动图,协作图,序列图。

%title插图%num

1、用例图(UseCase Diagrams):

用例图主要回答了两个问题:1、是谁用软件。2、软件的功能。从用户的角度描述了系统的功能,并指出各个功能的执行者,强调用户的使用者,系统为执行者完成哪些功能。

%title插图%num

2、类图(Class Diagrams):

    用户根据用例图抽象成类,描述类的内部结构和类与类之间的关系,是一种静态结构图。 在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)。

各种关系的强弱顺序: 泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖

2.1.泛化

【泛化关系】:是一种继承关系,表示一般与特殊的关系,它指定了子类如何继承父类的所有特征和行为。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。

%title插图%num

2.2.实现

【实现关系】:是一种类与接口的关系,表示类是接口所有特征和行为的实现。

%title插图%num

2.3.关联

【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。

【代码体现】:成员变量

%title插图%num

2.4.聚合

【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。

聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。

【代码体现】:成员变量

%title插图%num

2.5.组合

【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。

组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。

【代码体现】:成员变量

【箭头及指向】:带实心菱形的实线,菱形指向整体

%title插图%num

2.6.依赖

【依赖关系】:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.

【代码表现】:局部变量、方法的参数或者对静态方法的调用

【箭头及指向】:带箭头的虚线,指向被使用者

%title插图%num

2.7 各种类图关系

%title插图%num

3、对象图(Object Diagrams):

    描述的是参与交互的各个对象在交互过程中某一时刻的状态。对象图可以被看作是类图在某一时刻的实例。

%title插图%num

4、状态图(Statechart Diagrams):

    是一种由状态、变迁、事件和活动组成的状态机,用来描述类的对象所有可能的状态以及时间发生时状态的转移条件。

%title插图%num

5、活动图(Activity Diagrams):

    是状态图的一种特殊情况,这些状态大都处于活动状态。本质是一种流程图,它描述了活动到活动的控制流。

交互图强调的是对象到对象的控制流,而活动图则强调的是从活动到活动的控制流。

活动图是一种表述过程基理、业务过程以及工作流的技术。

它可以用来对业务过程、工作流建模,也可以对用例实现甚至是程序实现来建模。

      %title插图%num

5.1 带泳道的活动图

      泳道表明每个活动是由哪些人或哪些部门负责完成。

      %title插图%num

  5.2 带对象流的活动图

    用活动图描述某个对象时,可以把涉及到的对象放置在活动图中,并用一个依赖将其连接到进行创建、修改和撤销的动作状态或者活动状态上,对象的这种使用方法就构成了对象流。对象流用带有箭头的虚线表示。

    %title插图%num

  6、序列图-时序图(Sequence Diagrams):

    交互图的一种,描述了对象之间消息发送的先后顺序,强调时间顺序。

序列图的主要用途是把用例表达的需求,转化为进一步、更加正式层次的精细表达。用例常常被细化为一个或者更多的序列图。同时序列图更有效地描述如何分配各个类的职责以及各类具有相应职责的原因。

%title插图%num

消息用从一个对象的生命线到另一个对象生命线的箭头表示。箭头以时间顺序在图中从上到下排列。

序列图中涉及的元素:

6.1 生命线

生命线名称可带下划线。当使用下划线时,意味着序列图中的生命线代表一个类的特定实例。

%title插图%num

6.2 同步消息

同步等待消息

%title插图%num

6.3 异步消息

异步发送消息,不需等待

%title插图%num

6.4 注释

%title插图%num

6.5 约束

%title插图%num

6.6 组合

组合片段用来解决交互执行的条件及方式。它允许在序列图中直接表示逻辑组件,用于通过指定条件或子进程的应用区域,为任何生命线的任何部分定义特殊条件和子进程。常用的组合片段有:抉择、选项、循环、并行。

7、协作图(Collaboration Diagrams):

    交互图的一种,描述了收发消息的对象的组织关系,强调对象之间的合作关系。时序图按照时间顺序布图,而写作图按照空间结构布图

%title插图%num

 

8、构件图(Component Diagrams):

    构件图是用来表示系统中构件与构件之间,类或接口与构件之间的关系图。其中,构建图之间的关系表现为依赖关系,定义的类或接口与类之间的关系表现为依赖关系或实现关系。

%title插图%num

  9、部署图(Deployment Diagrams):

    描述了系统运行时进行处理的结点以及在结点上活动的构件的配置。强调了物理设备以及之间的连接关系。

部署模型的目的:

描述一个具体应用的主要部署结构,通过对各种硬件,在硬件中的软件以及各种连接协议的显示,可以很好的描述系统是如何部署的;平衡系统运行时的计算资源分布;可以通过连接描述组织的硬件网络结构或者是嵌入式系统等具有多种硬件和软件相关的系统运行模型。

%title插图%num

二、图的差异比较

1.序列图(时序图)VS协作图

序列图和协作图都是交互图。二者在语义上等价,可以相互转化。但是侧重点不同:序列图侧重时间顺序,协作图侧重对象间的关系。

共同点:时序图与协作图均显示了对象间的交互。

不同点:时序图强调交互的时间次序。

协作图强调交互的空间结构。

2.状态图VS活动图

状态图和活动图都是行为图。状态图侧重从行为的结果来描述,活动图侧重从行为的动作来描述。状态图描述了一个具体对象的可能状态以及他们之间的转换。在实际的项目中,活动图并不是必须的,需要满足以下条件:1、出现并行过程&行为;2、描述算法;3、跨越多个用例的活动图。

3.活动图VS交互图

二者都涉及到对象和他们之间传递的关系。区别在于交互图观察的是传送消息的对象,而活动图观察的是对象之间传递的消息。看似语义相同,但是他们是从不同的角度来观察整个系统的。

三、UML与软件工程

UML图是软件工程的组成部分,软件工程从宏观的角度保证了软件开发的各个过程的质量。而UML作为一种建模语言,更加有效的实现了软件工程的要求。

如下图,在软件的各个开发阶段需要的UML图。

%title插图%num

下表是UML使用人员图示:

%title插图%num

Uri详解之——Uri结构与代码提取

Uri详解之——Uri结构与代码提取

前言:依然没有前言…

 

上几篇给大家讲了ContentProvider,里面有用到Uri,可能很多同学对Uri是什么,怎么来的,这些都不是很熟悉,今天就给大家具体讲讲Uri

一、URI与Uri

大家可能经常会看到在开发时,怎么有的时候是URI,有的时候是Uri,这是怎么回事?

名称如此相像的两个类是有什么区别和联系?

  • 1.所属的包不同。URI位置在java.net.URI,显然是Java提供的一个类。而Uri位置在android.net.Uri,是由Android提供的一个类。所以初步可以判断,Uri是URI的“扩展”以适应Android系统的需要。
  • 2.作用的不同。URI类代表了一个URI(这个URI不是类,而是其本来的意义:通用资源标志符——Uniform Resource Identifier)实例。Uri类是一个不可改变的URI引用,包括一个URI和一些碎片,URI跟在“#”后面。建立并且转换URI引用。而且Uri类对无效的行为不敏感,对于无效的输入没有定义相应的行为,如果没有另外制定,它将返回垃圾而不是抛出一个异常。

看不懂?没关系,知道这个就可以了:Uri是Android开发的,扩展了Java中URI的一些功能来特定的适用于Android开发,所以大家在开发时,只使用Android 提供的Uri即可;

二、Uri结构

(1)、基本形式:

  1. [scheme:]scheme-specific-part[#fragment]

这里分为三部分:
scheme、scheme-specific-part、fragment

(2)、进一步划分:

如果进一步划分的话是这样子的

  1. [scheme:][//authority][path][?query][#fragment]

其中有下面几个规则:

  • path可以有多个,每个用/连接,比如
    scheme://authority/path1/path2/path3?query#fragment
  • query参数可以带有对应的值,也可以不带,如果带对应的值用=表示,如:
    scheme://authority/path1/path2/path3?id = 1#fragment,这里有一个参数id,它的值是1
  • query参数可以有多个,每个用&连接
    scheme://authority/path1/path2/path3?id = 1&name = mingming&old#fragment
    这里有三个参数:
    参数1:id,其值是:1
    参数2:name,其值是:mingming
    参数3:old,没有对它赋值,所以它的值是null
  • 在android中,除了scheme、authority是必须要有的,其它的几个path、query、fragment,它们每一个可以选择性的要或不要,但顺序不能变,比如:
    其中”path”可不要:scheme://authority?query#fragment
    其中”path”和”query”可都不要:scheme://authority#fragment
    其中”query”和”fragment”可都不要:scheme://authority/path
    “path”,”query”,”fragment”都不要:scheme://authority
    等等……

(3)、终*划分

其中authority,又可以分为host:port的形式,即再次划分后是这样的:

  1. [scheme:][//host:port][path][?query][#fragment]

所以这是划分*细的形式,其中host:port用冒号分隔,冒号前的是host,冒号后的port;

三、示例

经过上面的讲解,想必大家的Uri的结构就有所了解了,下面我们就实例看看各部分的识别方式。

  1. [scheme:]scheme-specific-part[#fragment]
  2. [scheme:][//authority][path][?query][#fragment]
  3. [scheme:][//host:port][path][?query][#fragment]

先列出这三种Uri形式,好让大家对比;
针对下面一个Uri字符串来匹配一下各个部分:

  1. http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic  
  • scheme:匹对上面的两个Uri标准形式,很容易看出在:前的部分是scheme,所以这个Uri字符串的sheme是:http
  • scheme-specific-part:很容易看出scheme-specific-part是包含在scheme和fragment之间的部分,也就是包括第二部分的[//authority][path][?query]这几个小部分,所在这个Uri字符串的scheme-specific-part是://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4 ,注意要带上//,因为除了[scheme:]和[#fragment]部分全部都是scheme-specific-part,当然包括*前面的//;
  • fragment:这个是更容易看出的,因为在*后用#分隔的部分就是fragment,所以这个Uri的fragment是:harvic
    下面就是对scheme-specific-part进行拆分了;
    在scheme-specific-part中,*前端的部分就是authority,?后面的部分是query,中间的部分就是path
  • authority:很容易看出scheme-specific-part*新端的部分是:www.java2s.com:8080
  • query:在scheme-specific-part中,?后的部分为:stove=10&path=32&id=4
  • path:在**query:**在scheme-specific-part中,除了authority和query其余都是path的部分:/yourpath/fileName.htm
    又由于authority又一步可以划分为host:port形式,其中host:port用冒号分隔,冒号前的是host,冒号后的是port,所以:
  • host:www.java2s.com
  • port:8080

四、代码提取

上面我们通过实例讲解了肉眼识别Uri更部分的方式,但在代码中又要怎样提取呢。下面就看看Uri中提取各部分的接口,依然以上面的Uri字符串为例:

  1. http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic  
  • getScheme() :获取Uri中的scheme字符串部分,在这里即,http
  • getSchemeSpecificPart():获取Uri中的scheme-specific-part:部分,这里是://www.java2s.com:8080/yourpath/fileName.htm?
  • getFragment():获取Uri中的Fragment部分,即harvic
  • getAuthority():获取Uri中Authority部分,即www.java2s.com:8080
  • getPath():获取Uri中path部分,即/yourpath/fileName.htm
  • getQuery():获取Uri中的query部分,即stove=10&path=32&id=4
  • getHost():获取Authority中的Host字符串,即www.java2s.com
  • getPost():获取Authority中的Port字符串,即8080

另外还有两个常用的:getPathSegments()、getQueryParameter(String key)

  • List< String> getPathSegments():上面我们的getPath()是把path部分整个获取下来:/yourpath/fileName.htm,getPathSegments()的作用就是依次提取出Path的各个部分的字符串,以字符串数组的形式输出。以上面的Uri为例:
  1. String mUriStr = “http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic”;  
  2. Uri mUri = Uri.parse(mUriStr);
  3. List<String> pathSegList = mUri.getPathSegments();
  4. for (String pathItem:pathSegList){  
  5.     Log.d(“qijian”,”pathSegItem:”+pathItem);  
  6. }

打出来的列表为:
%title插图%num

  • getQueryParameter(String key):在上面我们通过getQuery()获取整个query字段:stove=10&path=32&id=4,getQueryParameter(String key)作用就是通过传进去path中某个Key的字符串,返回他对应的值。
  1. String mUriStr = “http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id#harvic”;  
  2. mUri = Uri.parse(mUriStr);
  3. Log.d(tag,“getQueryParameter(\”stove\”):”+mUri.getQueryParameter(“stove”));  
  4. Log.d(tag,“getQueryParameter(\”id\”):”+mUri.getQueryParameter(“id”));  

注意注意,我稍微更改了下字符串,把query中id的值去掉了!!!!!然后看看通过getQueryParameter(“id”)获取它的值会得到什么!
结果如下:
%title插图%num

可以看到,在path中,即使针对某一个KEY不对它赋值是允许的,但在利用getQueryParameter()获取该KEY对应的值时,获取到的是null;

五、扩展

1、 *对URI和相对URI

*对URI:以scheme组件起始的完整格式,如http://fsjohnhuang.cnblogs.com。表示以对标识出现的环境无依赖的方式引用资源。
相对URI:不以scheme组件起始的非完整格式,如fsjohnhuang.cnblogs.com。表示以对依赖标识出现的环境有依赖的方式引用资源。

2、不透明URI和分层URI

不透明URI:scheme-specific-part组件不是以正斜杠(/)起始的,如mailto:fsjohnhuang@xxx.com。由于不透明URI无需进行分解操作,因此不会对scheme-specific-part组件进行有效性验证。
分层URI:scheme-specific-part组件是以正斜杠(/)起始的,如http://fsjohnhuang.com。

 

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速