From e2d4a61475909d57efc47f16c2a35271d229f5e9 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sun, 5 Nov 2017 11:17:08 +0800 Subject: [PATCH 01/64] First commit --- placeholder | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 placeholder diff --git a/placeholder b/placeholder new file mode 100644 index 0000000..e69de29 From 4abf78b62de9e43295625e388a75ee696c7cf114 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sun, 5 Nov 2017 11:17:09 +0800 Subject: [PATCH 02/64] Site updated: 2017-11-05 11:17:08 --- .../index.html" | 736 ++++ .../index.html" | 724 +++ .../index.html" | 737 ++++ .../index.html" | 761 ++++ archives/2017/10/index.html | 657 +++ archives/2017/11/index.html | 583 +++ archives/2017/index.html | 694 +++ archives/index.html | 694 +++ categories/Android/index.html | 648 +++ categories/index.html | 561 +++ css/main.css | 2993 +++++++++++++ images/algolia_logo.svg | 9 + images/apple-touch-icon-next.png | Bin 0 -> 1544 bytes images/avatar.gif | Bin 0 -> 2926 bytes images/avatar.png | Bin 0 -> 206711 bytes images/cc-by-nc-nd.svg | 121 + images/cc-by-nc-sa.svg | 121 + images/cc-by-nc.svg | 121 + images/cc-by-nd.svg | 117 + images/cc-by-sa.svg | 121 + images/cc-by.svg | 121 + images/cc-zero.svg | 72 + images/favicon-16x16-next.png | Bin 0 -> 435 bytes images/favicon-32x32-next.png | Bin 0 -> 640 bytes images/loading.gif | Bin 0 -> 35 bytes images/logo.svg | 23 + images/placeholder.gif | Bin 0 -> 35 bytes images/quote-l.svg | 13 + images/quote-r.svg | 12 + images/searchicon.png | Bin 0 -> 766 bytes index.html | 1178 +++++ js/src/affix.js | 162 + js/src/algolia-search.js | 115 + js/src/bootstrap.js | 52 + js/src/exturl.js | 15 + js/src/hook-duoshuo.js | 115 + js/src/js.cookie.js | 165 + js/src/motion.js | 352 ++ js/src/post-details.js | 99 + js/src/schemes/pisces.js | 57 + js/src/scroll-cookie.js | 23 + js/src/scrollspy.js | 182 + js/src/utils.js | 337 ++ lib/Han/dist/font/han-space.otf | Bin 0 -> 1748 bytes lib/Han/dist/font/han-space.woff | Bin 0 -> 1452 bytes lib/Han/dist/font/han.otf | Bin 0 -> 26416 bytes lib/Han/dist/font/han.woff | Bin 0 -> 21292 bytes lib/Han/dist/font/han.woff2 | Bin 0 -> 13644 bytes lib/Han/dist/han.css | 1168 +++++ lib/Han/dist/han.js | 3005 +++++++++++++ lib/Han/dist/han.min.css | 6 + lib/Han/dist/han.min.js | 5 + .../instantsearch.min.css | 1 + .../instantsearch.min.js | 15 + lib/canvas-nest/canvas-nest.min.js | 1 + lib/canvas-ribbon/canvas-ribbon.js | 73 + lib/fancybox/source/blank.gif | Bin 0 -> 43 bytes lib/fancybox/source/fancybox_loading.gif | Bin 0 -> 6567 bytes lib/fancybox/source/fancybox_loading@2x.gif | Bin 0 -> 13984 bytes lib/fancybox/source/fancybox_overlay.png | Bin 0 -> 1003 bytes lib/fancybox/source/fancybox_sprite.png | Bin 0 -> 1362 bytes lib/fancybox/source/fancybox_sprite@2x.png | Bin 0 -> 6553 bytes .../source/helpers/fancybox_buttons.png | Bin 0 -> 1080 bytes .../helpers/jquery.fancybox-buttons.css | 97 + .../source/helpers/jquery.fancybox-buttons.js | 122 + .../source/helpers/jquery.fancybox-media.js | 199 + .../source/helpers/jquery.fancybox-thumbs.css | 55 + .../source/helpers/jquery.fancybox-thumbs.js | 162 + lib/fancybox/source/jquery.fancybox.css | 274 ++ lib/fancybox/source/jquery.fancybox.js | 2020 +++++++++ lib/fancybox/source/jquery.fancybox.pack.js | 46 + lib/fastclick/LICENSE | 22 + lib/fastclick/README.html | 70 + lib/fastclick/bower.json | 1 + lib/fastclick/lib/fastclick.js | 841 ++++ lib/fastclick/lib/fastclick.min.js | 1 + lib/font-awesome/HELP-US-OUT.txt | 7 + lib/font-awesome/bower.json | 1 + lib/font-awesome/css/font-awesome.css | 2337 ++++++++++ lib/font-awesome/css/font-awesome.css.map | 7 + lib/font-awesome/css/font-awesome.min.css | 4 + lib/font-awesome/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes .../fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../fonts/fontawesome-webfont.svg | 2671 ++++++++++++ .../fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes lib/jquery/index.js | 4 + lib/jquery_lazyload/CONTRIBUTING.html | 22 + lib/jquery_lazyload/README.html | 20 + lib/jquery_lazyload/bower.json | 1 + lib/jquery_lazyload/jquery.lazyload.js | 242 ++ lib/jquery_lazyload/jquery.scrollstop.js | 72 + lib/needsharebutton/font-embedded.css | 127 + lib/needsharebutton/needsharebutton.css | 372 ++ lib/needsharebutton/needsharebutton.js | 537 +++ lib/pace/pace-theme-barber-shop.min.css | 1 + lib/pace/pace-theme-big-counter.min.css | 1 + lib/pace/pace-theme-bounce.min.css | 1 + lib/pace/pace-theme-center-atom.min.css | 1 + lib/pace/pace-theme-center-circle.min.css | 1 + lib/pace/pace-theme-center-radar.min.css | 1 + lib/pace/pace-theme-center-simple.min.css | 1 + lib/pace/pace-theme-corner-indicator.min.css | 1 + lib/pace/pace-theme-fill-left.min.css | 1 + lib/pace/pace-theme-flash.min.css | 1 + lib/pace/pace-theme-loading-bar.min.css | 1 + lib/pace/pace-theme-mac-osx.min.css | 1 + lib/pace/pace-theme-minimal.min.css | 1 + lib/pace/pace.min.js | 2 + lib/three/canvas_lines.min.js | 20 + lib/three/canvas_sphere.min.js | 20 + lib/three/three-waves.min.js | 20 + lib/three/three.min.js | 859 ++++ lib/ua-parser-js/dist/ua-parser.min.js | 9 + lib/ua-parser-js/dist/ua-parser.pack.js | 9 + lib/velocity/bower.json | 1 + lib/velocity/velocity.js | 3868 +++++++++++++++++ lib/velocity/velocity.min.js | 4 + lib/velocity/velocity.ui.js | 762 ++++ lib/velocity/velocity.ui.min.js | 2 + placeholder | 0 .../index.html" | 591 +++ .../index.html" | 591 +++ tags/Python/index.html | 563 +++ tags/index.html | 561 +++ 126 files changed, 35994 insertions(+) create mode 100644 "2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" create mode 100644 "2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" create mode 100644 "2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" create mode 100644 "2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" create mode 100644 archives/2017/10/index.html create mode 100644 archives/2017/11/index.html create mode 100644 archives/2017/index.html create mode 100644 archives/index.html create mode 100644 categories/Android/index.html create mode 100644 categories/index.html create mode 100644 css/main.css create mode 100644 images/algolia_logo.svg create mode 100644 images/apple-touch-icon-next.png create mode 100644 images/avatar.gif create mode 100644 images/avatar.png create mode 100644 images/cc-by-nc-nd.svg create mode 100644 images/cc-by-nc-sa.svg create mode 100644 images/cc-by-nc.svg create mode 100644 images/cc-by-nd.svg create mode 100644 images/cc-by-sa.svg create mode 100644 images/cc-by.svg create mode 100644 images/cc-zero.svg create mode 100644 images/favicon-16x16-next.png create mode 100644 images/favicon-32x32-next.png create mode 100644 images/loading.gif create mode 100644 images/logo.svg create mode 100644 images/placeholder.gif create mode 100644 images/quote-l.svg create mode 100644 images/quote-r.svg create mode 100644 images/searchicon.png create mode 100644 index.html create mode 100644 js/src/affix.js create mode 100644 js/src/algolia-search.js create mode 100644 js/src/bootstrap.js create mode 100644 js/src/exturl.js create mode 100644 js/src/hook-duoshuo.js create mode 100644 js/src/js.cookie.js create mode 100644 js/src/motion.js create mode 100644 js/src/post-details.js create mode 100644 js/src/schemes/pisces.js create mode 100644 js/src/scroll-cookie.js create mode 100644 js/src/scrollspy.js create mode 100644 js/src/utils.js create mode 100644 lib/Han/dist/font/han-space.otf create mode 100644 lib/Han/dist/font/han-space.woff create mode 100644 lib/Han/dist/font/han.otf create mode 100644 lib/Han/dist/font/han.woff create mode 100644 lib/Han/dist/font/han.woff2 create mode 100644 lib/Han/dist/han.css create mode 100644 lib/Han/dist/han.js create mode 100644 lib/Han/dist/han.min.css create mode 100644 lib/Han/dist/han.min.js create mode 100644 lib/algolia-instant-search/instantsearch.min.css create mode 100644 lib/algolia-instant-search/instantsearch.min.js create mode 100644 lib/canvas-nest/canvas-nest.min.js create mode 100644 lib/canvas-ribbon/canvas-ribbon.js create mode 100644 lib/fancybox/source/blank.gif create mode 100644 lib/fancybox/source/fancybox_loading.gif create mode 100644 lib/fancybox/source/fancybox_loading@2x.gif create mode 100644 lib/fancybox/source/fancybox_overlay.png create mode 100644 lib/fancybox/source/fancybox_sprite.png create mode 100644 lib/fancybox/source/fancybox_sprite@2x.png create mode 100644 lib/fancybox/source/helpers/fancybox_buttons.png create mode 100644 lib/fancybox/source/helpers/jquery.fancybox-buttons.css create mode 100644 lib/fancybox/source/helpers/jquery.fancybox-buttons.js create mode 100644 lib/fancybox/source/helpers/jquery.fancybox-media.js create mode 100644 lib/fancybox/source/helpers/jquery.fancybox-thumbs.css create mode 100644 lib/fancybox/source/helpers/jquery.fancybox-thumbs.js create mode 100644 lib/fancybox/source/jquery.fancybox.css create mode 100644 lib/fancybox/source/jquery.fancybox.js create mode 100644 lib/fancybox/source/jquery.fancybox.pack.js create mode 100644 lib/fastclick/LICENSE create mode 100644 lib/fastclick/README.html create mode 100644 lib/fastclick/bower.json create mode 100644 lib/fastclick/lib/fastclick.js create mode 100644 lib/fastclick/lib/fastclick.min.js create mode 100644 lib/font-awesome/HELP-US-OUT.txt create mode 100644 lib/font-awesome/bower.json create mode 100644 lib/font-awesome/css/font-awesome.css create mode 100644 lib/font-awesome/css/font-awesome.css.map create mode 100644 lib/font-awesome/css/font-awesome.min.css create mode 100644 lib/font-awesome/fonts/FontAwesome.otf create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.eot create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.svg create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.ttf create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.woff create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.woff2 create mode 100644 lib/jquery/index.js create mode 100644 lib/jquery_lazyload/CONTRIBUTING.html create mode 100644 lib/jquery_lazyload/README.html create mode 100644 lib/jquery_lazyload/bower.json create mode 100644 lib/jquery_lazyload/jquery.lazyload.js create mode 100644 lib/jquery_lazyload/jquery.scrollstop.js create mode 100644 lib/needsharebutton/font-embedded.css create mode 100644 lib/needsharebutton/needsharebutton.css create mode 100644 lib/needsharebutton/needsharebutton.js create mode 100644 lib/pace/pace-theme-barber-shop.min.css create mode 100644 lib/pace/pace-theme-big-counter.min.css create mode 100644 lib/pace/pace-theme-bounce.min.css create mode 100644 lib/pace/pace-theme-center-atom.min.css create mode 100644 lib/pace/pace-theme-center-circle.min.css create mode 100644 lib/pace/pace-theme-center-radar.min.css create mode 100644 lib/pace/pace-theme-center-simple.min.css create mode 100644 lib/pace/pace-theme-corner-indicator.min.css create mode 100644 lib/pace/pace-theme-fill-left.min.css create mode 100644 lib/pace/pace-theme-flash.min.css create mode 100644 lib/pace/pace-theme-loading-bar.min.css create mode 100644 lib/pace/pace-theme-mac-osx.min.css create mode 100644 lib/pace/pace-theme-minimal.min.css create mode 100644 lib/pace/pace.min.js create mode 100644 lib/three/canvas_lines.min.js create mode 100644 lib/three/canvas_sphere.min.js create mode 100644 lib/three/three-waves.min.js create mode 100644 lib/three/three.min.js create mode 100644 lib/ua-parser-js/dist/ua-parser.min.js create mode 100644 lib/ua-parser-js/dist/ua-parser.pack.js create mode 100644 lib/velocity/bower.json create mode 100644 lib/velocity/velocity.js create mode 100644 lib/velocity/velocity.min.js create mode 100644 lib/velocity/velocity.ui.js create mode 100644 lib/velocity/velocity.ui.min.js delete mode 100644 placeholder create mode 100644 "tags/Android\345\256\236\350\267\265/index.html" create mode 100644 "tags/Android\347\237\245\350\257\206\347\202\271/index.html" create mode 100644 tags/Python/index.html create mode 100644 tags/index.html diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" new file mode 100644 index 0000000..f550b95 --- /dev/null +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -0,0 +1,736 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android Handler机制理解 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Android Handler机制理解

+ + + +
+ + + + + +
+ + + + + +

1.何谓Handler机制?

一般来说,当你的应用被创建的时候,会创建一条应用的主线程。因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

+

当然,你可以创建自己的线程去做操作,但如何应用的主线程通信呢。那就要使用到Handler机制了。如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的相关方法实现的。

+

那接下来就要介绍一下这个消息通讯机制Handler,涉及到三个主要的类:Looper,Handler和Message类。

+

2.Looper

重点方法为:prepare()和loop()
Looper#prepare():

1
2
3
4
5
6
7
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

+

解释:首先它创建了一个ThreadLocal对象,它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据。然后在prepare方法中将looper存储在线程里面。

+

Looper#Looper():

1
2
3
4
5
private Looper(boolean quitAllowed) {  
     mQueue = new MessageQueue(quitAllowed);  
     mRun = true;  
     mThread = Thread.currentThread();  
}

+

而在构造方法中,Looper创建了一个MessageQueue,虽然是叫queue但其实内部实现是一个单链表。

+

Looper#loop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static void loop() {  
        final Looper me = myLooper();  
        if (me == null) {  
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }  
        final MessageQueue queue = me.mQueue;  
        // Make sure the identity of this thread is that of the local process,  
        // and keep track of what that identity token actually is.  
        Binder.clearCallingIdentity();  
        final long ident = Binder.clearCallingIdentity();  
        for (;;) {  
            Message msg = queue.next(); // might block  
            if (msg == null) {  
                // No message indicates that the message queue is quitting.  
                return;  
            }  
            // This must be in a local variable, in case a UI event sets the logger 
            Printer logging = me.mLogging;  
            if (logging != null) {  
                logging.println(">>>>> Dispatching to " + msg.target + " " +  
                        msg.callback + ": " + msg.what);  
            }    
            msg.target.dispatchMessage(msg);    
            if (logging != null) {  
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
            }   
            // Make sure that during the course of dispatching the  
            // identity of the thread wasn't corrupted.  
            final long newIdent = Binder.clearCallingIdentity();  
            if (ident != newIdent) {  
                Log.wtf(TAG, "Thread identity changed from 0x"  
                        + Long.toHexString(ident) + " to 0x"  
                        + Long.toHexString(newIdent) + " while dispatching to "  
                        + msg.target.getClass().getName() + " "  
                        + msg.callback + " what=" + msg.what);  
            }    
            msg.recycle();  
        }  
public static Looper myLooper() {
      return sThreadLocal.get();
}
}

+

解释:方法直接返回了前面sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。拿到该looper实例中的mQueue即消息队列后进入了无限循环,不断从队列中取出一条消息,如果没有消息则阻塞。如果取得消息使用调用msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。而msg的target是什么呢?其实就是前面讲到的handler对象,最后会释放消息占据的资源。

+

Looper类总结:
1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2.loop()方法,不断从MessageQueue中去取消息,交给message的target的dispatchMessage去处理。

+

接下来就要讲发送消息的对象了,这个对象就是Handler。

+

3.Handler

主要作用是将一个任务切换到某个指定的线程中去执行,同时为了解决在子线程中无法访问UI的矛盾。

+

所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的。

+

Handler#Handler():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Handler() {  
        this(nullfalse);  
}  
public Handler(Callback callback, boolean async) {  
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());  
            }  
        }    
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
}

+

解释:在构造的时候会检查当前的Handler是否为静态类,不是静态声明的话会打印Log,提示会有内存泄漏现象的产生,然后通过Looper.myLooper()方法获取到当前线程的Looper实例(mLooper)并进一步获取到当前线程的消息队列(mQueue),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

+

使用的时候我们会经常使用到sendMessage方法,我们来看看源码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final boolean sendMessage(Message msg)  {  
     return sendMessageDelayed(msg, 0);  
 }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
     Message msg = Message.obtain();  
     msg.what = what;  
     return sendMessageDelayed(msg, delayMillis);  
 } 
public final boolean sendMessageDelayed(Message msg, long delayMillis) {  
       if (delayMillis < 0) {  
           delayMillis = 0;  
       }  
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
}  
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
       MessageQueue queue = mQueue;  
       if (queue == null) {  
           RuntimeException e = new RuntimeException(  
                   this + " sendMessageAtTime() called with no mQueue");  
           Log.w("Looper", e.getMessage(), e);  
           return false;  
       }  
       return enqueueMessage(queue, msg, uptimeMillis);  
   }  
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
       msg.target = this;  
       if (mAsynchronous) {  
           msg.setAsynchronous(true);  
       }  
       return queue.enqueueMessage(msg, uptimeMillis);  
}

+

一路跳到最后的enqueueMessage方法,enqueueMessage中首先为msg.target赋值为this,因为Looper中的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,保存到消息队列中去。

+

现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispatchMessage方法。
Handler#dispatchMessage():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void dispatchMessage(Message msg) {  
        if (msg.callback != null) { 
            // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);  // 并直接调用callback的run方法!
        } else { 
            // 如果handler本身设置了callback,则执行callback 
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }
// 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);  
        }  
}``` 
几个变量和方法的解释: 
1.callback:message携带的Runnable对象,实际上就是Handler的post方法所传递的Runnable参数。
我们来看一下Handler的post方法源码实现:
```java
mHandler.post(new Runnable()  {  
        @Override  
        public void run()  {  
            // code  
        }  
});

+

其实这个Runnable并没有创建什么线程,而是发送了一条消息:

1
2
3
4
5
6
7
8
public final boolean post(Runnable r)  {  
      return  sendMessageDelayed(getPostMessage(r), 0);  
}  
private static Message getPostMessage(Runnable r) {  
      Message m = Message.obtain();  
      m.callback = r;  
      return m;  
}

+

在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message。
注意:产生一个Message对象,可以new,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new重新分配内存。

+

2.mCallback:可通过Handler handler = new Handler(callback); 可以用来创建一个Handler实例但不需要派生Handler子类。它可用来拦截消息!当mCallback的handleMessage返回true的时候可以拦截消息,具体的逻辑看上面的代码很容易理解!

+

3.handleMessage(msg):它是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。

+

到此,这个流程已经解释完毕,总结一下:
1.首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2.Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与MessageQueue相关联。
4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)。
6.在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

+

下面是个人认为在 Activity 中一个合格的 Handler 该有的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class MyHandler extends Handler {
private WeakReference<CustomActivity> activityWeakReference;
public MyHandler(CustomActivity activity) {
activityWeakReference = new WeakReference<CustomActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
CustomActivity activtiy = activityWeakReference.get();
if (activity != null) {
// code
}
}
}

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" new file mode 100644 index 0000000..3356ba2 --- /dev/null +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -0,0 +1,724 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android View动画和属性动画简单解析 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Android View动画和属性动画简单解析

+ + + +
+ + + + + +
+ + + + + +

一:View动画

简介:View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,是一种渐近式动画,并且View动画支持自定义。
1.View动画主要分为四类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation,可通过XML或者Java代码声明使用,动画XML文件需要放在res/anim/filename.xml中。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<set xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:interpolator=""
    android:shareInterpolator="["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate 
        android:fromXDelta="float" 
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate 
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
</set>

+

Java代码:

1
2
3
4
5
// 使用Java代码加载XML动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
mButton.startAnimation(animation);
// 使用Java代码创建动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);

+

2.View动画既可以是单个动画,也可以由一系列动画组成。
3.几个标签解读:
set:
表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的。
android:interpolator:
表示动画集合所采用的插值器,什么是插值器呢?它影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。属性可不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator:
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值。
其余的属性网上都能查到,这里就不详细描述了。

+

二:属性动画

简介:属性动画通过动态地改变相关对象的属性,比如长宽等,从而实现动画效果,属性动画为API 11(Android 3.0)以上的新特性,在低版本无法直接使用属性动画,但仍然可通过兼容库(NineOldAndroids)去使用。
属性动画有ValueAnimator、ObjectAnimator和AnimatorSet等概念。其中ObjectAnimator继承自ValueAnimator、AnimatorSet是动画集合,可以定义一组动画。
(1)使用
举例:改变一个对象(myObject)的translationY属性,让其沿着Y轴上平移一段距离:

1
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();

+

(2)插值器和估值器:
属性动画有两个新概念:
插值器:根据时间流逝的百分比来计算出属性值改变的百分比,对应的接口是Interpolator;
估值器:根据属性改变的百分比计算出属性的改变值,对应的接口是TypeEvaluator;
代码设置:

1
2
ValueAnimator.setEvaluator(TypeEvaluator evaluator)
ValueAnimator.setInterpolator(TimeInterpolator value)

+

(3)属性动画的监听器
属性动画提供了监听器用于监听动画的播放过程。主要有如下两个接口:AnimatorUpdateListener和AnimatorListener。

1
2
3
4
5
6
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}

+

它可以监听动画的开始、结束、取消以及重复播放。系统提供了AnimatorListener的适配器类AnimatorListenerAdapter。
AnimatorUpdateListener:

1
2
3
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}

+

AnimatorUpdateListener会监听整个动画过程,动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被调用一次。
(4)对任意属性做动画
属性动画的原理:属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切地说是随着时间的推移,所传递的值越来越接近最终值。
总结,对object的属性abc做动画,需满足条件:
(1)object必须提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取abc属性的初始值;
(2)object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变。
建议:
①、给对象加上get和set方法;
②、用一个类来包装原始对象,间接为其提供get和set方法;
③、采用ValueAnimator,监听动画过程,实现属性的变化。

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" new file mode 100644 index 0000000..65ff46a --- /dev/null +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -0,0 +1,737 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分享一下自己做的一个图片加载库XImageLoader | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

分享一下自己做的一个图片加载库XImageLoader

+ + + +
+ + + + + +
+ + + + + +

这是一个我自己做的一个Android的自定义图片加载库,主要参考了网上一些大神写过的一些图片加载库,再结合自己的一些想法理解去做了一个较为完整的图片加载库。当然代码中也会存在一些不足的情况,例如代码架构方面不是非常的完善。希望大家提出意见,一起去改进!

+

注意:这是一个用于学习图片加载与缓存的库,不推荐使用在实际项目之中!

+

Github地址:https://github.com/XuDeveloper/XImageLoader

+

如果你想改进这个图片加载库,欢迎在GitHub上fork这个项目然后pull request给我!如果你喜欢它,请给这个项目一个star或者关注我的GitHub!谢谢你们的支持!

+

导入

Android Studio

1
2
3
4
5
6
7
8
9
10
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
dependencies {
compile 'com.github.XuDeveloper:XImageLoader:v1.0'
}
+

Eclipse

+

可以复制源码到你的项目中!

+
+

使用

默认用法:

+
1
2
3
4
5
// 异步接口调用
XImageLoader.build(context).imageview(ImageView).load(imageUrl);
// 加载本地文件,你需要使用这样的格式:"file:///address"
XImageLoader.build(context).imageview(ImageView).load("file:///address");
+

或者:

+
1
2
3
// 同步接口调用(需要运行在一条新线程中)
Bitmap bitmap = XImageLoader.build(context).imageview(ImageView).getBitmap(imageUrl);
+

你可以选择是否缓存或者自定义(使用XImageLoaderConfig):

+
1
2
3
4
5
6
7
8
9
10
11
12
XImageLoader.build(context).imageview(isMemoryCache, isDiskCache, ImageView).load(imageUrl);
XImageLoader.build(context).imageview(isDoubleCache, ImageView).load(imageUrl);
// 具体配置
XImageLoaderConfig config = new XImageLoaderConfig();
config.setCache(new DoubleCache(context));
config.setLoader(new OkhttpImageLoader());
config.setLoadingResId(R.drawable.image_loading);
config.setFailResId(R.drawable.image_fail);
XImageLoader.build(context).imageview(config, ImageView).load(imageUrl);
+

你需要AndroidManifest.xml中设置权限:

+
1
2
3
4
5
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+

如果你使用的是Android 6.0以上的设备,你需要动态设置权限:

+
1
2
XImageLoader.verifyStoragePermissions(activity);
+

代码架构设计:
采用流式编程写法,将加载一张图片分为以下几步:
1.初始化一些基本设置(XImageLoaderConfig ),如是否加入缓存,设置加载时显示的图片资源以及加载失败时显示的图片资源还有图片加载器ImageLoader;(如果不设置下面会自动设置)
2.设置需要加载的ImageView;
3.设置需要加载的图片路径,可以是网络上的图片,也可以是手机本地图片,如果前面没设置ImageLoader,在这里就可以根据路径来动态选择加载哪个特定的ImageLoader;
加载图片有两种方法,一种是同步方法获取图片Bitmap,另一种是在线程池中异步加载图片。

+

加载图片的优化方法:
1.加入缓存机制,包括LruCache以及DiskLruCache;
2.加载时先预读取图片,根据尺寸计算inSampleSize,对图片进行适当压缩,防止OOM;
3.采用线程池加载。

+

需要进一步优化的地方:
读取需要加载的ImageView尺寸时用到了反射的方法,影响了一定的效率;

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" new file mode 100644 index 0000000..405ea1f --- /dev/null +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -0,0 +1,761 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android adb命令的一些实际运用 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Android adb命令的一些实际运用

+ + + +
+ + + + + +
+ + + + + +

在开发应用的过程中,安卓平台给大家提供了非常多的调试工具,包括Android Studio本身自带的工具,如果不想使用Studio的话,也可以在终端使用adb工具进行调试。

+

关于adb的用法网上有很多教程,这里推荐一个https://github.com/mzlogin/awesome-adb。

+

今天记录一下我在实际情况中对adb的运用。

+

1.关于adb shell input text的问题

在使用这个命令的时候,我遇到了一个情况,就是无法输入”&”。我在网上搜了一下,在StackOverFlow里面,解决方案是这样的:
adb shell input text “\&”

+

这个方案在终端运行是可以的,但是我是用python写脚本运行的,这样做是无法成功的。

+
1
2
cmd = "adb shell input text '\&'"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
+

想了很久,最后使用以下解决方法,原因我也不太理解,不知道有没有人来解答一下~

+
1
2
3
cmd = "adb shell input text '\&'"
cmd = cmd.replace('&', "\"\&\"")
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
+

2.关于grep

grep命令起源于Linux系统。grep命令全称是Global Regular Expression Print,是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。在使用adb命令的时候,我们也经常需要使用到它。

+

举一个例子:

1
adb shell dumpsys window policy

+

这个命令会展示出android当前窗口(window)的所有属性信息:

+

image.png

+

如果我们想在其中提取出mShowingLockscreen属性要怎么做?

+

网上给了这样一种方法:

1
adb shell dumpsys window policy | grep mShowingLockscreen

+

同样在终端使用以及用python写脚本运行,出现问题:

+

系统终端测试.png

+

git bash终端测试.png

+

(真是奇怪,同样是终端,差别咋怎么大,无法理解…….)

+

原因不明,下面给出几种解决方案:

+
    +
  • 使用findstr
  • +
+

findstr相当于Windows下的grep命令。

1
adb shell dumpsys window policy | findstr mShowingLockscreen

+

运行成功!

+

success1.png

+
    +
  • 使用^| grep
  • +
+

个人理解:^| 有点类似于转义的作用

1
adb shell dumpsys window policy ^| grep mShowingLockscreen

+

运行成功!

+

success2.png

+

3.关于python运行adb命令返回结果的问题

一般情况下,使用python运行adb命令是非常方便的,例如:

+
1
2
3
import subprocess
cmd = "adb shell input text test"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+

但其实它只对一些立即返回结果的命令有用,对于一些需要一定等待时间的命令,它有时就会出现错误,例如:

+
1
2
3
4
import subprocess
cmd = "adb shell ping -c 4 www.baidu.com"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print "test"
+

这样执行会出现错误,会直接输出”test”。
这是因为subprocess.Popen对象创建后,主程序并不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block):

+
1
2
3
4
5
import subprocess
cmd = "adb shell ping -c 4 www.baidu.com"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.wait()
print "test"
+

大家可以运行对比一下~

+

4.如何获取一段时间的logcat日志

使用的是adb logcat命令,具体的参数网上有很多,这里就不详细展开。

+

这里记录一下我是如何获取一段时间的logcat日志的:

1
2
3
4
5
cmd = "adb logcat -v time"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
time.sleep(10) # 这里用time.sleep模拟了一段时间,可以把它替换成你需要执行的操作
p.terminate() # 终止程序,相当于终端用Ctrl + C
result = p.communicate()[0] # 获取执行操作前后的日志

+

如有问题,请大家踊跃提出,谢谢大家!

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html new file mode 100644 index 0000000..1bc8a04 --- /dev/null +++ b/archives/2017/10/index.html @@ -0,0 +1,657 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html new file mode 100644 index 0000000..2db0a9d --- /dev/null +++ b/archives/2017/11/index.html @@ -0,0 +1,583 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/index.html b/archives/2017/index.html new file mode 100644 index 0000000..8f1e7bf --- /dev/null +++ b/archives/2017/index.html @@ -0,0 +1,694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..f98a3d4 --- /dev/null +++ b/archives/index.html @@ -0,0 +1,694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/Android/index.html b/categories/Android/index.html new file mode 100644 index 0000000..cbfc169 --- /dev/null +++ b/categories/Android/index.html @@ -0,0 +1,648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 分类: Android | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..e68b778 --- /dev/null +++ b/categories/index.html @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 文章分类 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + +
+
+ +

文章分类

+ + + +
+ + + + +
+ + +
+
+ 目前共计 1 个分类 +
+
+ +
+
+ +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..67dbe82 --- /dev/null +++ b/css/main.css @@ -0,0 +1,2993 @@ +/* normalize.css v3.0.2 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +mark { + background: #ff0; + color: #000; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +::selection { + background: #262a30; + color: #fff; +} +body { + position: relative; + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 14px; + line-height: 2; + color: #555; + background: #f5f7f9; +} +@media (max-width: 767px) { + body { + padding-right: 0 !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + body { + padding-right: 0 !important; + } +} +@media (min-width: 1600px) { + body { + font-size: 16px; + } +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; + font-weight: bold; + line-height: 1.5; + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; +} +h2, +h3, +h4, +h5, +h6 { + margin: 20px 0 15px; +} +h1 { + font-size: 22px; +} +@media (max-width: 767px) { + h1 { + font-size: 18px; + } +} +h2 { + font-size: 20px; +} +@media (max-width: 767px) { + h2 { + font-size: 16px; + } +} +h3 { + font-size: 18px; +} +@media (max-width: 767px) { + h3 { + font-size: 14px; + } +} +h4 { + font-size: 16px; +} +@media (max-width: 767px) { + h4 { + font-size: 12px; + } +} +h5 { + font-size: 14px; +} +@media (max-width: 767px) { + h5 { + font-size: 10px; + } +} +h6 { + font-size: 12px; +} +@media (max-width: 767px) { + h6 { + font-size: 8px; + } +} +p { + margin: 0 0 20px 0; +} +a { + color: #555; + text-decoration: none; + outline: none; + border-bottom: 1px solid #999; + word-wrap: break-word; +} +a:hover { + color: #222; + border-bottom-color: #222; +} +blockquote { + margin: 0; + padding: 0; +} +img { + display: block; + margin: auto; + max-width: 100%; + height: auto; +} +hr { + margin: 40px 0; + height: 3px; + border: none; + background-color: #ddd; + background-image: repeating-linear-gradient(-45deg, #fff, #fff 4px, transparent 4px, transparent 8px); +} +blockquote { + padding: 0 15px; + color: #666; + border-left: 4px solid #ddd; +} +blockquote cite::before { + content: "-"; + padding: 0 5px; +} +dt { + font-weight: 700; +} +dd { + margin: 0; + padding: 0; +} +kbd { + border: 1px solid #ccc; + border-radius: 0.2em; + box-shadow: 0.1em 0.1em 0.2em rgba(0,0,0,0.1); + background-color: #f9f9f9; + font-family: inherit; + background-image: -webkit-linear-gradient(top, #eee, #fff, #eee); + padding: 0.1em 0.3em; + white-space: nowrap; +} +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.clearfix:before, +.clearfix:after { + content: " "; + display: table; +} +.clearfix:after { + clear: both; +} +.pullquote { + width: 45%; +} +.pullquote.left { + float: left; + margin-left: 5px; + margin-right: 10px; +} +.pullquote.right { + float: right; + margin-left: 10px; + margin-right: 5px; +} +.affix.affix.affix { + position: fixed; +} +.translation { + margin-top: -20px; + font-size: 14px; + color: #999; +} +.scrollbar-measure { + width: 100px; + height: 100px; + overflow: scroll; + position: absolute; + top: -9999px; +} +.use-motion .motion-element { + opacity: 0; +} +table { + margin: 20px 0; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + border: 1px solid #ddd; + font-size: 14px; + table-layout: fixed; + word-wrap: break-all; +} +table>tbody>tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +table>tbody>tr:hover { + background-color: #f5f5f5; +} +caption, +th, +td { + padding: 8px; + text-align: left; + vertical-align: middle; + font-weight: normal; +} +th, +td { + border-bottom: 3px solid #ddd; + border-right: 1px solid #eee; +} +th { + padding-bottom: 10px; + font-weight: 700; +} +td { + border-bottom-width: 1px; +} +html, +body { + height: 100%; +} +.container { + position: relative; + min-height: 100%; +} +.header-inner { + margin: 0 auto; + padding: 100px 0 70px; + width: 700px; +} +@media (min-width: 1600px) { + .container .header-inner { + width: 900px; + } +} +.main { + padding-bottom: 150px; +} +.main-inner { + margin: 0 auto; + width: 700px; +} +@media (min-width: 1600px) { + .container .main-inner { + width: 900px; + } +} +.footer { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + min-height: 50px; +} +.footer-inner { + box-sizing: border-box; + margin: 20px auto; + width: 700px; +} +@media (min-width: 1600px) { + .container .footer-inner { + width: 900px; + } +} +pre, +.highlight { + overflow: auto; + margin: 20px 0; + padding: 0; + font-size: 13px; + color: #4d4d4c; + background: #f7f7f7; + line-height: 1.6; +} +pre, +code { + font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; +} +code { + padding: 2px 4px; + word-wrap: break-word; + color: #555; + background: #eee; + border-radius: 3px; + font-size: 13px; +} +pre { + padding: 10px; +} +pre code { + padding: 0; + color: #4d4d4c; + background: none; + text-shadow: none; +} +.highlight { + border-radius: 1px; +} +.highlight pre { + border: none; + margin: 0; + padding: 10px 0; +} +.highlight table { + margin: 0; + width: auto; + border: none; +} +.highlight td { + border: none; + padding: 0; +} +.highlight figcaption { + font-size: 1em; + color: #4d4d4c; + line-height: 1em; + margin-bottom: 1em; +} +.highlight figcaption:before, +.highlight figcaption:after { + content: " "; + display: table; +} +.highlight figcaption:after { + clear: both; +} +.highlight figcaption a { + float: right; + color: #4d4d4c; +} +.highlight figcaption a:hover { + border-bottom-color: #4d4d4c; +} +.highlight .gutter pre { + padding-left: 10px; + padding-right: 10px; + color: #869194; + text-align: right; + background-color: #eff2f3; +} +.highlight .code pre { + width: 100%; + padding-left: 10px; + padding-right: 10px; + background-color: #f7f7f7; +} +.highlight .line { + height: 20px; +} +.gutter { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.gist table { + width: auto; +} +.gist table td { + border: none; +} +pre .deletion { + background: #fdd; +} +pre .addition { + background: #dfd; +} +pre .meta { + color: #8959a8; +} +pre .comment { + color: #8e908c; +} +pre .variable, +pre .attribute, +pre .tag, +pre .regexp, +pre .ruby .constant, +pre .xml .tag .title, +pre .xml .pi, +pre .xml .doctype, +pre .html .doctype, +pre .css .id, +pre .css .class, +pre .css .pseudo { + color: #c82829; +} +pre .number, +pre .preprocessor, +pre .built_in, +pre .literal, +pre .params, +pre .constant, +pre .command { + color: #f5871f; +} +pre .ruby .class .title, +pre .css .rules .attribute, +pre .string, +pre .value, +pre .inheritance, +pre .header, +pre .ruby .symbol, +pre .xml .cdata, +pre .special, +pre .number, +pre .formula { + color: #718c00; +} +pre .title, +pre .css .hexcolor { + color: #3e999f; +} +pre .function, +pre .python .decorator, +pre .python .title, +pre .ruby .function .title, +pre .ruby .title .keyword, +pre .perl .sub, +pre .javascript .title, +pre .coffeescript .title { + color: #4271ae; +} +pre .keyword, +pre .javascript .function { + color: #8959a8; +} +.full-image.full-image.full-image { + border: none; + max-width: 100%; + width: auto; + margin: 20px auto; +} +@media (min-width: 992px) { + .full-image.full-image.full-image { + max-width: none; + width: 118%; + margin: 0 -9%; + } +} +.blockquote-center, +.page-home .post-type-quote blockquote, +.page-post-detail .post-type-quote blockquote { + position: relative; + margin: 40px 0; + padding: 0; + border-left: none; + text-align: center; +} +.blockquote-center::before, +.page-home .post-type-quote blockquote::before, +.page-post-detail .post-type-quote blockquote::before, +.blockquote-center::after, +.page-home .post-type-quote blockquote::after, +.page-post-detail .post-type-quote blockquote::after { + position: absolute; + content: ' '; + display: block; + width: 100%; + height: 24px; + opacity: 0.2; + background-repeat: no-repeat; + background-position: 0 -6px; + background-size: 22px 22px; +} +.blockquote-center::before, +.page-home .post-type-quote blockquote::before, +.page-post-detail .post-type-quote blockquote::before { + top: -20px; + background-image: url("../images/quote-l.svg"); + border-top: 1px solid #ccc; +} +.blockquote-center::after, +.page-home .post-type-quote blockquote::after, +.page-post-detail .post-type-quote blockquote::after { + bottom: -20px; + background-image: url("../images/quote-r.svg"); + border-bottom: 1px solid #ccc; + background-position: 100% 8px; +} +.blockquote-center p, +.page-home .post-type-quote blockquote p, +.page-post-detail .post-type-quote blockquote p, +.blockquote-center div, +.page-home .post-type-quote blockquote div, +.page-post-detail .post-type-quote blockquote div { + text-align: center; +} +.post .post-body .group-picture img { + box-sizing: border-box; + padding: 0 3px; + border: none; +} +.post .group-picture-row { + overflow: hidden; + margin-top: 6px; +} +.post .group-picture-row:first-child { + margin-top: 0; +} +.post .group-picture-column { + float: left; +} +.page-post-detail .post-body .group-picture-column { + float: none; + margin-top: 10px; + width: auto !important; +} +.page-post-detail .post-body .group-picture-column img { + margin: 0 auto; +} +.page-archive .group-picture-container { + overflow: hidden; +} +.page-archive .group-picture-row { + float: left; +} +.page-archive .group-picture-row:first-child { + margin-top: 6px; +} +.page-archive .group-picture-column { + max-width: 150px; + max-height: 150px; +} +.post-body .note { + position: relative; + padding: 15px; + margin-bottom: 20px; + border: 1px solid #eee; + border-left-width: 5px; + border-radius: 3px; +} +.post-body .note h2, +.post-body .note h3, +.post-body .note h4, +.post-body .note h5, +.post-body .note h6 { + margin-top: 0; + margin-bottom: 0; + border-bottom: initial; + padding-top: 0 !important; +} +.post-body .note p:first-child, +.post-body .note ul:first-child, +.post-body .note ol:first-child, +.post-body .note table:first-child, +.post-body .note pre:first-child, +.post-body .note blockquote:first-child { + margin-top: 0; +} +.post-body .note p:last-child, +.post-body .note ul:last-child, +.post-body .note ol:last-child, +.post-body .note table:last-child, +.post-body .note pre:last-child, +.post-body .note blockquote:last-child { + margin-bottom: 0; +} +.post-body .note.default { + border-left-color: #777; +} +.post-body .note.default h2, +.post-body .note.default h3, +.post-body .note.default h4, +.post-body .note.default h5, +.post-body .note.default h6 { + color: #777; +} +.post-body .note.primary { + border-left-color: #6f42c1; +} +.post-body .note.primary h2, +.post-body .note.primary h3, +.post-body .note.primary h4, +.post-body .note.primary h5, +.post-body .note.primary h6 { + color: #6f42c1; +} +.post-body .note.info { + border-left-color: #428bca; +} +.post-body .note.info h2, +.post-body .note.info h3, +.post-body .note.info h4, +.post-body .note.info h5, +.post-body .note.info h6 { + color: #428bca; +} +.post-body .note.success { + border-left-color: #5cb85c; +} +.post-body .note.success h2, +.post-body .note.success h3, +.post-body .note.success h4, +.post-body .note.success h5, +.post-body .note.success h6 { + color: #5cb85c; +} +.post-body .note.warning { + border-left-color: #f0ad4e; +} +.post-body .note.warning h2, +.post-body .note.warning h3, +.post-body .note.warning h4, +.post-body .note.warning h5, +.post-body .note.warning h6 { + color: #f0ad4e; +} +.post-body .note.danger { + border-left-color: #d9534f; +} +.post-body .note.danger h2, +.post-body .note.danger h3, +.post-body .note.danger h4, +.post-body .note.danger h5, +.post-body .note.danger h6 { + color: #d9534f; +} +.post-body .label { + display: inline; + padding: 0 2px; + white-space: nowrap; +} +.post-body .label.default { + background-color: #f0f0f0; +} +.post-body .label.primary { + background-color: #efe6f7; +} +.post-body .label.info { + background-color: #e5f2f8; +} +.post-body .label.success { + background-color: #e7f4e9; +} +.post-body .label.warning { + background-color: #fcf6e1; +} +.post-body .label.danger { + background-color: #fae8eb; +} +.post-body .tabs { + position: relative; + display: block; + margin-bottom: 20px; + padding-top: 10px; +} +.post-body .tabs ul.nav-tabs { + margin: 0; + padding: 0; + display: flex; + margin-bottom: -1px; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs { + display: block; + margin-bottom: 5px; + } +} +.post-body .tabs ul.nav-tabs li.tab { + list-style-type: none !important; + margin: 0 0.25em 0 0; + border-top: 3px solid transparent; + border-left: 1px solid transparent; + border-right: 1px solid transparent; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs li.tab { + margin: initial; + border-top: 1px solid transparent; + border-left: 3px solid transparent; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + } +} +.post-body .tabs ul.nav-tabs li.tab a { + outline: 0; + border-bottom: initial; + display: block; + line-height: 1.8em; + padding: 0.25em 0.75em; + transition-duration: 0.2s; + transition-timing-function: ease-out; + transition-delay: 0s; +} +.post-body .tabs ul.nav-tabs li.tab a i { + width: 1.285714285714286em; +} +.post-body .tabs ul.nav-tabs li.tab.active { + border-top: 3px solid #fc6423; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + background-color: #fff; +} +@media (max-width: 413px) { + .post-body .tabs ul.nav-tabs li.tab.active { + border-top: 1px solid #ddd; + border-left: 3px solid #fc6423; + border-right: 1px solid #ddd; + border-bottom: 1px solid #ddd; + } +} +.post-body .tabs ul.nav-tabs li.tab.active a { + cursor: default; + color: #555; +} +.post-body .tabs .tab-content { + background-color: #fff; +} +.post-body .tabs .tab-content .tab-pane { + border: 1px solid #ddd; + padding: 20px 20px 0 20px; +} +.post-body .tabs .tab-content .tab-pane:not(.active) { + display: none !important; +} +.post-body .tabs .tab-content .tab-pane.active { + display: block !important; +} +.btn { + display: inline-block; + padding: 0 20px; + font-size: 14px; + color: #555; + background: #fff; + border: 2px solid #555; + text-decoration: none; + border-radius: 2px; + transition-property: background-color; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; + line-height: 2; +} +.btn:hover { + border-color: #222; + color: #fff; + background: #222; +} +.btn +.btn { + margin: 0 0 8px 8px; +} +.btn .fa-fw { + width: 1.285714285714286em; + text-align: left; +} +.btn-bar { + display: block; + width: 22px; + height: 2px; + background: #555; + border-radius: 1px; +} +.btn-bar+.btn-bar { + margin-top: 4px; +} +.pagination { + margin: 120px 0 40px; + text-align: center; + border-top: 1px solid #eee; +} +.page-number-basic, +.pagination .prev, +.pagination .next, +.pagination .page-number, +.pagination .space { + display: inline-block; + position: relative; + top: -1px; + margin: 0 10px; + padding: 0 11px; +} +@media (max-width: 767px) { + .page-number-basic, + .pagination .prev, + .pagination .next, + .pagination .page-number, + .pagination .space { + margin: 0 5px; + } +} +.pagination .prev, +.pagination .next, +.pagination .page-number { + border-bottom: 0; + border-top: 1px solid #eee; + transition-property: border-color; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; +} +.pagination .prev:hover, +.pagination .next:hover, +.pagination .page-number:hover { + border-top-color: #222; +} +.pagination .space { + padding: 0; + margin: 0; +} +.pagination .prev { + margin-left: 0; +} +.pagination .next { + margin-right: 0; +} +.pagination .page-number.current { + color: #fff; + background: #ccc; + border-top-color: #ccc; +} +@media (max-width: 767px) { + .pagination { + border-top: none; + } + .pagination .prev, + .pagination .next, + .pagination .page-number { + margin-bottom: 10px; + border-top: 0; + border-bottom: 1px solid #eee; + padding: 0 10px; + } + .pagination .prev:hover, + .pagination .next:hover, + .pagination .page-number:hover { + border-bottom-color: #222; + } +} +.comments { + margin: 60px 20px 0; +} +.tag-cloud { + text-align: center; +} +.tag-cloud a { + display: inline-block; + margin: 10px; +} +.back-to-top { + box-sizing: border-box; + position: fixed; + bottom: -100px; + right: 30px; + z-index: 1050; + padding: 0 6px; + width: 24px; + background: #222; + font-size: 12px; + opacity: 0.6; + color: #fff; + cursor: pointer; + text-align: center; + -webkit-transform: translateZ(0); + transition-property: bottom; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; +} +@media (min-width: 768px) and (max-width: 991px) { + .back-to-top { + display: none !important; + } +} +@media (max-width: 767px) { + .back-to-top { + display: none !important; + } +} +.back-to-top.back-to-top-on { + bottom: 30px; +} +.header { + background: transparent; +} +.header-inner { + position: relative; +} +.headband { + height: 3px; + background: #222; +} +.site-meta { + margin: 0; + text-align: center; +} +@media (max-width: 767px) { + .site-meta { + text-align: center; + } +} +.brand { + position: relative; + display: inline-block; + padding: 0 40px; + color: #fff; + background: #222; + border-bottom: none; +} +.brand:hover { + color: #fff; +} +.logo { + display: inline-block; + margin-right: 5px; + line-height: 36px; + vertical-align: top; +} +.site-title { + display: inline-block; + vertical-align: top; + line-height: 36px; + font-size: 20px; + font-weight: normal; + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; +} +.site-subtitle { + margin-top: 10px; + font-size: 13px; + color: #ddd; +} +.use-motion .brand { + opacity: 0; +} +.use-motion .logo, +.use-motion .site-title, +.use-motion .site-subtitle { + opacity: 0; + position: relative; + top: -10px; +} +.site-nav-toggle { + display: none; + position: absolute; + top: 10px; + left: 10px; +} +@media (max-width: 767px) { + .site-nav-toggle { + display: block; + } +} +.site-nav-toggle button { + margin-top: 2px; + padding: 9px 10px; + background: transparent; + border: none; +} +@media (max-width: 767px) { + .site-nav { + display: none; + margin: 0 -10px; + padding: 0 10px; + clear: both; + border-top: 1px solid #ddd; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav { + display: block !important; + } +} +@media (min-width: 992px) { + .site-nav { + display: block !important; + } +} +.menu { + margin-top: 20px; + padding-left: 0; + text-align: center; +} +.menu .menu-item { + display: inline-block; + margin: 0 10px; + list-style: none; +} +@media screen and (max-width: 767px) { + .menu .menu-item { + margin-top: 10px; + } +} +.menu .menu-item a { + display: block; + font-size: 13px; + line-height: inherit; + border-bottom: 1px solid transparent; + transition-property: border-color; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; +} +.menu .menu-item a:hover, +.menu-item-active a { + border-bottom-color: #222; +} +.menu .menu-item .fa { + margin-right: 5px; +} +.use-motion .menu-item { + opacity: 0; +} +.post-body { + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; +} +@media (max-width: 767px) { + .post-body { + word-break: break-word; + } +} +.post-body .fancybox img { + display: block !important; + margin: 0 auto; + cursor: pointer; + cursor: zoom-in; + cursor: -webkit-zoom-in; +} +.post-body .image-caption, +.post-body .figure .caption { + margin: -20px auto 15px; + text-align: center; + font-size: 14px; + color: #999; + font-weight: bold; + line-height: 1; +} +.post-sticky-flag { + display: inline-block; + font-size: 16px; + -ms-transform: rotate(30deg); + -webkit-transform: rotate(30deg); + -moz-transform: rotate(30deg); + -ms-transform: rotate(30deg); + -o-transform: rotate(30deg); + transform: rotate(30deg); +} +.use-motion .post-block, +.use-motion .pagination, +.use-motion .comments { + opacity: 0; +} +.use-motion .post-header { + opacity: 0; +} +.use-motion .post-body { + opacity: 0; +} +.use-motion .collection-title { + opacity: 0; +} +.posts-expand { + padding-top: 40px; +} +@media (max-width: 767px) { + .posts-expand { + margin: 0 20px; + } + .post-body pre .gutter pre { + padding-right: 10px; + } + .post-body .highlight { + margin-left: 0px; + margin-right: 0px; + padding: 0; + } + .post-body .highlight .gutter pre { + padding-right: 10px; + } +} +@media (min-width: 992px) { + .posts-expand .post-body { + text-align: justify; + } +} +.posts-expand .post-body h2, +.posts-expand .post-body h3, +.posts-expand .post-body h4, +.posts-expand .post-body h5, +.posts-expand .post-body h6 { + padding-top: 10px; +} +.posts-expand .post-body h2 .header-anchor, +.posts-expand .post-body h3 .header-anchor, +.posts-expand .post-body h4 .header-anchor, +.posts-expand .post-body h5 .header-anchor, +.posts-expand .post-body h6 .header-anchor { + float: right; + margin-left: 10px; + color: #ccc; + border-bottom-style: none; + visibility: hidden; +} +.posts-expand .post-body h2 .header-anchor:hover, +.posts-expand .post-body h3 .header-anchor:hover, +.posts-expand .post-body h4 .header-anchor:hover, +.posts-expand .post-body h5 .header-anchor:hover, +.posts-expand .post-body h6 .header-anchor:hover { + color: inherit; +} +.posts-expand .post-body h2:hover .header-anchor, +.posts-expand .post-body h3:hover .header-anchor, +.posts-expand .post-body h4:hover .header-anchor, +.posts-expand .post-body h5:hover .header-anchor, +.posts-expand .post-body h6:hover .header-anchor { + visibility: visible; +} +.posts-expand .post-body ul li { + list-style: circle; +} +.posts-expand .post-body img { + box-sizing: border-box; + margin: auto; + padding: 3px; + border: 1px solid #ddd; +} +.posts-expand .post-body .fancybox img { + margin: 0 auto 25px; +} +@media (max-width: 767px) { + .posts-collapse { + margin: 0 20px; + } + .posts-collapse .post-title, + .posts-collapse .post-meta { + display: block; + width: auto; + text-align: left; + } +} +.posts-collapse { + position: relative; + z-index: 1010; + margin-left: 55px; +} +.posts-collapse::after { + content: " "; + position: absolute; + top: 20px; + left: 0; + margin-left: -2px; + width: 4px; + height: 100%; + background: #f5f5f5; + z-index: -1; +} +@media (max-width: 767px) { + .posts-collapse { + margin: 0 20px; + } +} +.posts-collapse .collection-title { + position: relative; + margin: 60px 0; +} +.posts-collapse .collection-title h1, +.posts-collapse .collection-title h2 { + margin-left: 20px; +} +.posts-collapse .collection-title small { + color: #bbb; + margin-left: 5px; +} +.posts-collapse .collection-title::before { + content: " "; + position: absolute; + left: 0; + top: 50%; + margin-left: -4px; + margin-top: -4px; + width: 8px; + height: 8px; + background: #bbb; + border-radius: 50%; +} +.posts-collapse .post { + margin: 30px 0; +} +.posts-collapse .post-header { + position: relative; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; + transition-property: border; + border-bottom: 1px dashed #ccc; +} +.posts-collapse .post-header::before { + content: " "; + position: absolute; + left: 0; + top: 12px; + width: 6px; + height: 6px; + margin-left: -4px; + background: #bbb; + border-radius: 50%; + border: 1px solid #fff; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; + transition-property: background; +} +.posts-collapse .post-header:hover { + border-bottom-color: #666; +} +.posts-collapse .post-header:hover::before { + background: #222; +} +.posts-collapse .post-meta { + position: absolute; + font-size: 12px; + left: 20px; + top: 5px; +} +.posts-collapse .post-comments-count { + display: none; +} +.posts-collapse .post-title { + margin-left: 60px; + font-size: 16px; + font-weight: normal; + line-height: inherit; +} +.posts-collapse .post-title::after { + margin-left: 3px; + opacity: 0.6; +} +.posts-collapse .post-title a { + color: #666; + border-bottom: none; +} +.page-home .post-type-quote .post-header, +.page-post-detail .post-type-quote .post-header, +.page-home .post-type-quote .post-tags, +.page-post-detail .post-type-quote .post-tags { + display: none; +} +.posts-expand .post-title { + text-align: center; + word-break: break-word; + font-weight: 400; +} +.posts-expand .post-title-link { + display: inline-block; + position: relative; + color: #555; + border-bottom: none; + line-height: 1.2; + vertical-align: top; +} +.posts-expand .post-title-link::before { + content: ""; + position: absolute; + width: 100%; + height: 2px; + bottom: 0; + left: 0; + background-color: #000; + visibility: hidden; + -webkit-transform: scaleX(0); + -moz-transform: scaleX(0); + -ms-transform: scaleX(0); + -o-transform: scaleX(0); + transform: scaleX(0); + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; +} +.posts-expand .post-title-link:hover::before { + visibility: visible; + -webkit-transform: scaleX(1); + -moz-transform: scaleX(1); + -ms-transform: scaleX(1); + -o-transform: scaleX(1); + transform: scaleX(1); +} +.posts-expand .post-title-link .fa { + font-size: 16px; +} +.posts-expand .post-meta { + margin: 3px 0 60px 0; + color: #999; + font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 12px; + text-align: center; +} +.posts-expand .post-meta .post-category-list { + display: inline-block; + margin: 0; + padding: 3px; +} +.posts-expand .post-meta .post-category-list-link { + color: #999; +} +.posts-expand .post-meta .post-description { + font-size: 14px; + margin-top: 2px; +} +.post-meta-divider { + margin: 0 0.5em; +} +.post-meta-item-icon { + margin-right: 3px; +} +@media (min-width: 768px) and (max-width: 991px) { + .post-meta-item-icon { + display: inline-block; + } +} +@media (max-width: 767px) { + .post-meta-item-icon { + display: inline-block; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .post-meta-item-text { + display: none; + } +} +@media (max-width: 767px) { + .post-meta-item-text { + display: none; + } +} +@media (max-width: 767px) { + .posts-expand .post-comments-count { + display: none; + } +} +.post-button { + margin-top: 40px; +} +.posts-expand .post-tags { + margin-top: 40px; + text-align: center; +} +.posts-expand .post-tags a { + display: inline-block; + margin-right: 10px; + font-size: 13px; +} +.post-nav { + display: table; + margin-top: 15px; + width: 100%; + border-top: 1px solid #eee; +} +.post-nav-divider { + display: table-cell; + width: 10%; +} +.post-nav-item { + display: table-cell; + padding: 10px 0 0 0; + width: 45%; + vertical-align: top; +} +.post-nav-item a { + position: relative; + display: block; + line-height: 25px; + font-size: 14px; + color: #555; + border-bottom: none; +} +.post-nav-item a:hover { + color: #222; + border-bottom: none; +} +.post-nav-item a:active { + top: 2px; +} +.post-nav-item .fa { + position: absolute; + top: 8px; + left: 0; + font-size: 12px; +} +.post-nav-next a { + padding-left: 15px; +} +.post-nav-prev { + text-align: right; +} +.post-nav-prev a { + padding-right: 15px; +} +.post-nav-prev .fa { + right: 0; + left: auto; +} +.posts-expand .post-eof { + display: block; + margin: 80px auto 60px; + width: 8%; + height: 1px; + background: #ccc; + text-align: center; +} +.post:last-child .post-eof.post-eof.post-eof { + display: none; +} +.post-gallery { + display: table; + table-layout: fixed; + width: 100%; + border-collapse: separate; +} +.post-gallery-row { + display: table-row; +} +.post-gallery .post-gallery-img { + display: table-cell; + text-align: center; + vertical-align: middle; + border: none; +} +.post-gallery .post-gallery-img img { + max-width: 100%; + max-height: 100%; + border: none; +} +.fancybox-close, +.fancybox-close:hover { + border: none; +} +.rtl.post-body p, +.rtl.post-body a, +.rtl.post-body h1, +.rtl.post-body h2, +.rtl.post-body h3, +.rtl.post-body h4, +.rtl.post-body h5, +.rtl.post-body h6, +.rtl.post-body li, +.rtl.post-body ul, +.rtl.post-body ol { + direction: rtl; + font-family: UKIJ Ekran; +} +.rtl.post-title { + font-family: UKIJ Ekran; +} +.sidebar { + position: fixed; + right: 0; + top: 0; + bottom: 0; + width: 0; + z-index: 1040; + box-shadow: inset 0 2px 6px #000; + background: #222; + -webkit-transform: translateZ(0); +} +.sidebar a { + color: #999; + border-bottom-color: #555; +} +.sidebar a:hover { + color: #eee; +} +@media (min-width: 768px) and (max-width: 991px) { + .sidebar { + display: none !important; + } +} +@media (max-width: 767px) { + .sidebar { + display: none !important; + } +} +.sidebar-inner { + position: relative; + padding: 20px 10px; + color: #999; + text-align: center; +} +.site-overview-wrap { + overflow: hidden; +} +.site-overview { + overflow-y: auto; + overflow-x: hidden; +} +.sidebar-toggle { + position: fixed; + right: 30px; + bottom: 45px; + width: 14px; + height: 14px; + padding: 5px; + background: #222; + line-height: 0; + z-index: 1050; + cursor: pointer; + -webkit-transform: translateZ(0); +} +@media (min-width: 768px) and (max-width: 991px) { + .sidebar-toggle { + display: none !important; + } +} +@media (max-width: 767px) { + .sidebar-toggle { + display: none !important; + } +} +.sidebar-toggle-line { + position: relative; + display: inline-block; + vertical-align: top; + height: 2px; + width: 100%; + background: #fff; + margin-top: 3px; +} +.sidebar-toggle-line:first-child { + margin-top: 0; +} +.site-author-image { + display: block; + margin: 0 auto; + padding: 2px; + max-width: 120px; + height: auto; + border: 1px solid #eee; +} +.site-author-name { + margin: 0; + text-align: center; + color: #222; + font-weight: 600; +} +.site-description { + margin-top: 0; + text-align: center; + font-size: 13px; + color: #999; +} +.site-state { + overflow: hidden; + line-height: 1.4; + white-space: nowrap; + text-align: center; +} +.site-state-item { + display: inline-block; + padding: 0 15px; + border-left: 1px solid #eee; +} +.site-state-item:first-child { + border-left: none; +} +.site-state-item a { + border-bottom: none; +} +.site-state-item-count { + display: block; + text-align: center; + color: inherit; + font-weight: 600; + font-size: 16px; +} +.site-state-item-name { + font-size: 13px; + color: #999; +} +.feed-link { + margin-top: 20px; +} +.feed-link a { + display: inline-block; + padding: 0 15px; + color: #fc6423; + border: 1px solid #fc6423; + border-radius: 4px; +} +.feed-link a i { + color: #fc6423; + font-size: 14px; +} +.feed-link a:hover { + color: #fff; + background: #fc6423; +} +.feed-link a:hover i { + color: #fff; +} +.links-of-author { + margin-top: 20px; +} +.links-of-author a { + display: inline-block; + vertical-align: middle; + margin-right: 10px; + margin-bottom: 10px; + border-bottom-color: #555; + font-size: 13px; +} +.links-of-author a:before { + display: inline-block; + vertical-align: middle; + margin-right: 3px; + content: " "; + width: 4px; + height: 4px; + border-radius: 50%; + background: #2296ff; +} +.links-of-blogroll { + font-size: 13px; +} +.links-of-blogroll-title { + margin-top: 20px; + font-size: 14px; + font-weight: 600; +} +.links-of-blogroll-list { + margin: 0; + padding: 0; + list-style: none; +} +.links-of-blogroll-item { + padding: 2px 10px; +} +.links-of-blogroll-item a { + max-width: 280px; + box-sizing: border-box; + display: inline-block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.sidebar-nav { + margin: 0 0 20px; + padding-left: 0; +} +.sidebar-nav li { + display: inline-block; + cursor: pointer; + border-bottom: 1px solid transparent; + font-size: 14px; + color: #555; +} +.sidebar-nav li:hover { + color: #fc6423; +} +.page-post-detail .sidebar-nav-toc { + padding: 0 5px; +} +.page-post-detail .sidebar-nav-overview { + margin-left: 10px; +} +.sidebar-nav .sidebar-nav-active { + color: #fc6423; + border-bottom-color: #fc6423; +} +.sidebar-nav .sidebar-nav-active:hover { + color: #fc6423; +} +.sidebar-panel { + display: none; +} +.sidebar-panel-active { + display: block; +} +.post-toc-empty { + font-size: 14px; + color: #666; +} +.post-toc-wrap { + overflow: hidden; +} +.post-toc { + overflow: auto; +} +.post-toc ol { + margin: 0; + padding: 0 2px 5px 10px; + text-align: left; + list-style: none; + font-size: 14px; +} +.post-toc ol > ol { + padding-left: 0; +} +.post-toc ol a { + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; + transition-property: all; + color: #666; + border-bottom-color: #ccc; +} +.post-toc ol a:hover { + color: #000; + border-bottom-color: #000; +} +.post-toc .nav-item { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 1.8; +} +.post-toc .nav .nav-child { + display: none; +} +.post-toc .nav .active > .nav-child { + display: block; +} +.post-toc .nav .active-current > .nav-child { + display: block; +} +.post-toc .nav .active-current > .nav-child > .nav-item { + display: block; +} +.post-toc .nav .active > a { + color: #fc6423; + border-bottom-color: #fc6423; +} +.post-toc .nav .active-current > a { + color: #fc6423; +} +.post-toc .nav .active-current > a:hover { + color: #fc6423; +} +.footer { + font-size: 14px; + color: #999; +} +.footer img { + border: none; +} +.footer-inner { + text-align: center; +} +.with-love { + display: inline-block; + margin: 0 5px; +} +.powered-by, +.theme-info { + display: inline-block; +} +.cc-license { + margin-top: 10px; + text-align: center; +} +.cc-license .cc-opacity { + opacity: 0.7; + border-bottom: none; +} +.cc-license .cc-opacity:hover { + opacity: 0.9; +} +.cc-license img { + display: inline-block; +} +.theme-next #ds-thread #ds-reset { + color: #555; +} +.theme-next #ds-thread #ds-reset .ds-replybox { + margin-bottom: 30px; +} +.theme-next #ds-thread #ds-reset .ds-replybox .ds-avatar, +.theme-next #ds-reset .ds-avatar img { + box-shadow: none; +} +.theme-next #ds-thread #ds-reset .ds-textarea-wrapper { + border-color: #c7d4e1; + background: none; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.theme-next #ds-thread #ds-reset .ds-textarea-wrapper textarea { + height: 60px; +} +.theme-next #ds-reset .ds-rounded-top { + border-radius: 0; +} +.theme-next #ds-thread #ds-reset .ds-post-toolbar { + box-sizing: border-box; + border: 1px solid #c7d4e1; + background: #f6f8fa; +} +.theme-next #ds-thread #ds-reset .ds-post-options { + height: 40px; + border: none; + background: none; +} +.theme-next #ds-thread #ds-reset .ds-toolbar-buttons { + top: 11px; +} +.theme-next #ds-thread #ds-reset .ds-sync { + top: 5px; +} +.theme-next #ds-thread #ds-reset .ds-post-button { + top: 4px; + right: 5px; + width: 90px; + height: 30px; + border: 1px solid #c5ced7; + border-radius: 3px; + background-image: linear-gradient(#fbfbfc, #f5f7f9); + color: #60676d; +} +.theme-next #ds-thread #ds-reset .ds-post-button:hover { + background-position: 0 -30px; + color: #60676d; +} +.theme-next #ds-thread #ds-reset .ds-comments-info { + padding: 10px 0; +} +.theme-next #ds-thread #ds-reset .ds-sort { + display: none; +} +.theme-next #ds-thread #ds-reset li.ds-tab a.ds-current { + border: none; + background: #f6f8fa; + color: #60676d; +} +.theme-next #ds-thread #ds-reset li.ds-tab a.ds-current:hover { + background-color: #e9f0f7; + color: #60676d; +} +.theme-next #ds-thread #ds-reset li.ds-tab a { + border-radius: 2px; + padding: 5px; +} +.theme-next #ds-thread #ds-reset .ds-login-buttons p { + color: #999; + line-height: 36px; +} +.theme-next #ds-thread #ds-reset .ds-login-buttons .ds-service-list li { + height: 28px; +} +.theme-next #ds-thread #ds-reset .ds-service-list a { + background: none; + padding: 5px; + border: 1px solid; + border-radius: 3px; + text-align: center; +} +.theme-next #ds-thread #ds-reset .ds-service-list a:hover { + color: #fff; + background: #666; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-weibo { + color: #fc9b00; + border-color: #fc9b00; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-weibo:hover { + background: #fc9b00; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-qq { + color: #60a3ec; + border-color: #60a3ec; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-qq:hover { + background: #60a3ec; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-renren { + color: #2e7ac4; + border-color: #2e7ac4; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-renren:hover { + background: #2e7ac4; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-douban { + color: #37994c; + border-color: #37994c; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-douban:hover { + background: #37994c; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-kaixin { + color: #fef20d; + border-color: #fef20d; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-kaixin:hover { + background: #fef20d; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-netease { + color: #f00; + border-color: #f00; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-netease:hover { + background: #f00; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-sohu { + color: #ffcb05; + border-color: #ffcb05; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-sohu:hover { + background: #ffcb05; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-baidu { + color: #2831e0; + border-color: #2831e0; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-baidu:hover { + background: #2831e0; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-google { + color: #166bec; + border-color: #166bec; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-google:hover { + background: #166bec; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-weixin { + color: #00ce0d; + border-color: #00ce0d; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-weixin:hover { + background: #00ce0d; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-more-services { + border: none; +} +.theme-next #ds-thread #ds-reset .ds-service-list .ds-more-services:hover { + background: none; +} +.theme-next #ds-reset .duoshuo-ua-admin { + display: inline-block; + color: #f00; +} +.theme-next #ds-reset .duoshuo-ua-platform, +.theme-next #ds-reset .duoshuo-ua-browser { + color: #ccc; +} +.theme-next #ds-reset .duoshuo-ua-platform .fa, +.theme-next #ds-reset .duoshuo-ua-browser .fa { + display: inline-block; + margin-right: 3px; +} +.theme-next #ds-reset .duoshuo-ua-separator { + display: inline-block; + margin-left: 5px; +} +.theme-next .this_ua { + background-color: #ccc !important; + border-radius: 4px; + padding: 0 5px !important; + margin: 1px 1px !important; + border: 1px solid #bbb !important; + color: #fff; + display: inline-block !important; +} +.theme-next .this_ua.admin { + background-color: #d9534f !important; + border-color: #d9534f !important; +} +.theme-next .this_ua.platform.iOS, +.theme-next .this_ua.platform.Mac, +.theme-next .this_ua.platform.Windows { + background-color: #39b3d7 !important; + border-color: #46b8da !important; +} +.theme-next .this_ua.platform.Linux { + background-color: #3a3a3a !important; + border-color: #1f1f1f !important; +} +.theme-next .this_ua.platform.Android { + background-color: #00c47d !important; + border-color: #01b171 !important; +} +.theme-next .this_ua.browser.Mobile, +.theme-next .this_ua.browser.Chrome { + background-color: #5cb85c !important; + border-color: #4cae4c !important; +} +.theme-next .this_ua.browser.Firefox { + background-color: #f0ad4e !important; + border-color: #eea236 !important; +} +.theme-next .this_ua.browser.Maxthon, +.theme-next .this_ua.browser.IE { + background-color: #428bca !important; + border-color: #357ebd !important; +} +.theme-next .this_ua.browser.baidu, +.theme-next .this_ua.browser.UCBrowser, +.theme-next .this_ua.browser.Opera { + background-color: #d9534f !important; + border-color: #d43f3a !important; +} +.theme-next .this_ua.browser.Android, +.theme-next .this_ua.browser.QQBrowser { + background-color: #78ace9 !important; + border-color: #4cae4c !important; +} +.post-spread { + margin-top: 20px; + text-align: center; +} +.jiathis_style { + display: inline-block; +} +.jiathis_style a { + border: none; +} +.fa { + font-family: FontAwesome !important; +} +.post-spread { + margin-top: 20px; + text-align: center; +} +.bdshare-slide-button-box a { + border: none; +} +.bdsharebuttonbox { + display: inline-block; +} +.bdsharebuttonbox a { + border: none; +} +.local-search-pop-overlay { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 2080; + background-color: rgba(0,0,0,0.3); +} +.local-search-popup { + display: none; + position: fixed; + top: 10%; + left: 50%; + margin-left: -350px; + width: 700px; + height: 80%; + padding: 0; + background: #fff; + color: #333; + z-index: 9999; + border-radius: 5px; +} +@media (max-width: 767px) { + .local-search-popup { + padding: 0; + top: 0; + left: 0; + margin: 0; + width: 100%; + height: 100%; + border-radius: 0; + } +} +.local-search-popup ul.search-result-list { + padding: 0; + margin: 0 5px; +} +.local-search-popup p.search-result { + border-bottom: 1px dashed #ccc; + padding: 5px 0; +} +.local-search-popup a.search-result-title { + font-weight: bold; + font-size: 16px; +} +.local-search-popup .search-keyword { + border-bottom: 1px dashed #f00; + font-weight: bold; + color: #f00; +} +.local-search-popup .local-search-header { + padding: 5px; + height: 36px; + background: #f5f5f5; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.local-search-popup #local-search-result { + overflow: auto; + position: relative; + padding: 5px 25px; + height: calc(100% - 55px); +} +.local-search-popup .local-search-input-wrapper { + display: inline-block; + width: calc(100% - 90px); + height: 36px; + line-height: 36px; + padding: 0 5px; +} +.local-search-popup .local-search-input-wrapper input { + padding: 8px 0; + height: 20px; + display: block; + width: 100%; + outline: none; + border: none; + background: transparent; + vertical-align: middle; +} +.local-search-popup .search-icon, +.local-search-popup .popup-btn-close { + display: inline-block; + font-size: 18px; + color: #999; + height: 36px; + width: 18px; + padding-left: 10px; + padding-right: 10px; +} +.local-search-popup .search-icon { + float: left; +} +.local-search-popup .popup-btn-close { + border-left: 1px solid #eee; + float: right; + cursor: pointer; +} +.local-search-popup #no-result { + position: absolute; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + color: #ccc; +} +@media (min-width: 768px) and (max-width: 991px) { + .busuanzi-count { + width: auto; + } +} +@media (max-width: 767px) { + .busuanzi-count { + width: auto; + } +} +.site-uv, +.site-pv, +.page-pv { + display: inline-block; +} +.site-uv .busuanzi-value, +.site-pv .busuanzi-value, +.page-pv .busuanzi-value { + margin: 0 5px; +} +.site-uv { + margin-right: 10px; +} +.site-uv::after { + content: "|"; + padding-left: 10px; +} +.page-archive .archive-page-counter { + position: relative; + top: 3px; + left: 20px; +} +@media (max-width: 767px) { + .page-archive .archive-page-counter { + top: 5px; + } +} +.page-archive .posts-collapse .archive-move-on { + position: absolute; + top: 11px; + left: 0; + margin-left: -6px; + width: 10px; + height: 10px; + opacity: 0.5; + background: #555; + border: 1px solid #fff; + border-radius: 50%; +} +.category-all-page .category-all-title { + text-align: center; +} +.category-all-page .category-all { + margin-top: 20px; +} +.category-all-page .category-list { + margin: 0; + padding: 0; + list-style: none; +} +.category-all-page .category-list-item { + margin: 5px 10px; +} +.category-all-page .category-list-count { + color: #bbb; +} +.category-all-page .category-list-count:before { + display: inline; + content: " ("; +} +.category-all-page .category-list-count:after { + display: inline; + content: ") "; +} +.category-all-page .category-list-child { + padding-left: 10px; +} +#schedule ul#event-list { + padding-left: 30px; +} +#schedule ul#event-list hr { + margin: 20px 0 45px 0 !important; + background: #222; +} +#schedule ul#event-list hr:after { + display: inline-block; + content: 'NOW'; + background: #222; + color: #fff; + font-weight: bold; + text-align: right; + padding: 0 5px; +} +#schedule ul#event-list li.event { + margin: 20px 0px; + background: #f9f9f9; + padding-left: 10px; + min-height: 40px; +} +#schedule ul#event-list li.event h2.event-summary { + margin: 0; + padding-bottom: 3px; +} +#schedule ul#event-list li.event h2.event-summary:before { + display: inline-block; + font-family: FontAwesome; + font-size: 8px; + content: '\f111'; + vertical-align: middle; + margin-right: 25px; + color: #bbb; +} +#schedule ul#event-list li.event span.event-relative-time { + display: inline-block; + font-size: 12px; + font-weight: 400; + padding-left: 12px; + color: #bbb; +} +#schedule ul#event-list li.event span.event-details { + display: block; + color: #bbb; + margin-left: 56px; + padding-top: 3px; + padding-bottom: 6px; + text-indent: -24px; + line-height: 18px; +} +#schedule ul#event-list li.event span.event-details:before { + text-indent: 0; + display: inline-block; + width: 14px; + font-family: FontAwesome; + text-align: center; + margin-right: 9px; + color: #bbb; +} +#schedule ul#event-list li.event span.event-details.event-location:before { + content: '\f041'; +} +#schedule ul#event-list li.event span.event-details.event-duration:before { + content: '\f017'; +} +#schedule ul#event-list li.event-past { + background: #fcfcfc; +} +#schedule ul#event-list li.event-past > * { + opacity: 0.6; +} +#schedule ul#event-list li.event-past h2.event-summary { + color: #bbb; +} +#schedule ul#event-list li.event-past h2.event-summary:before { + color: #dfdfdf; +} +#schedule ul#event-list li.event-now { + background: #222; + color: #fff; + padding: 15px 0 15px 10px; +} +#schedule ul#event-list li.event-now h2.event-summary:before { + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + -ms-transform: scale(1.2); + -o-transform: scale(1.2); + transform: scale(1.2); + color: #fff; + animation: dot-flash 1s alternate infinite ease-in-out; +} +#schedule ul#event-list li.event-now * { + color: #fff !important; +} +@-moz-keyframes dot-flash { + from { + opacity: 1; + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); + } + to { + opacity: 0; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +@-webkit-keyframes dot-flash { + from { + opacity: 1; + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); + } + to { + opacity: 0; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +@-o-keyframes dot-flash { + from { + opacity: 1; + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); + } + to { + opacity: 0; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +@keyframes dot-flash { + from { + opacity: 1; + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); + } + to { + opacity: 0; + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +.page-post-detail .sidebar-toggle-line { + background: #fc6423; +} +.page-post-detail .comments { + overflow: hidden; +} +.header { + position: relative; + margin: 0 auto; + width: 960px; +} +@media (min-width: 768px) and (max-width: 991px) { + .header { + width: auto; + } +} +@media (max-width: 767px) { + .header { + width: auto; + } +} +.header-inner { + position: absolute; + top: 0; + overflow: hidden; + padding: 0; + width: 240px; + background: #fff; + box-shadow: initial; + border-radius: initial; +} +@media (min-width: 1600px) { + .container .header-inner { + width: 240px; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .header-inner { + position: relative; + width: auto; + border-radius: initial; + } +} +@media (max-width: 767px) { + .header-inner { + position: relative; + width: auto; + border-radius: initial; + } +} +.main:before, +.main:after { + content: " "; + display: table; +} +.main:after { + clear: both; +} +@media (min-width: 768px) and (max-width: 991px) { + .main { + padding-bottom: 100px; + } +} +@media (max-width: 767px) { + .main { + padding-bottom: 100px; + } +} +.container .main-inner { + width: 960px; +} +@media (min-width: 768px) and (max-width: 991px) { + .container .main-inner { + width: auto; + } +} +@media (max-width: 767px) { + .container .main-inner { + width: auto; + } +} +.content-wrap { + float: right; + box-sizing: border-box; + padding: 40px; + width: 700px; + background: #fff; + min-height: 700px; + box-shadow: initial; + border-radius: initial; +} +@media (min-width: 768px) and (max-width: 991px) { + .content-wrap { + width: 100%; + padding: 20px; + border-radius: initial; + } +} +@media (max-width: 767px) { + .content-wrap { + width: 100%; + padding: 20px; + min-height: auto; + border-radius: initial; + } +} +.sidebar { + position: static; + float: left; + margin-top: 300px; + width: 240px; + background: #f5f7f9; + box-shadow: none; +} +@media (min-width: 768px) and (max-width: 991px) { + .sidebar { + display: none; + } +} +@media (max-width: 767px) { + .sidebar { + display: none; + } +} +.sidebar-toggle { + display: none; +} +.footer-inner { + width: 960px; + padding-left: 260px; +} +@media (min-width: 768px) and (max-width: 991px) { + .footer-inner { + width: auto; + padding-left: 0 !important; + padding-right: 0 !important; + } +} +@media (max-width: 767px) { + .footer-inner { + width: auto; + padding-left: 0 !important; + padding-right: 0 !important; + } +} +.sidebar-position-right .header-inner { + right: 0; +} +.sidebar-position-right .content-wrap { + float: left; +} +.sidebar-position-right .sidebar { + float: right; +} +.sidebar-position-right .footer-inner { + padding-left: 0; + padding-right: 260px; +} +.site-brand-wrapper { + position: relative; +} +.site-meta { + padding: 20px 0; + color: #fff; + background: #222; +} +@media (min-width: 768px) and (max-width: 991px) { + .site-meta { + box-shadow: 0 0 16px rgba(0,0,0,0.5); + } +} +@media (max-width: 767px) { + .site-meta { + box-shadow: 0 0 16px rgba(0,0,0,0.5); + } +} +.brand { + padding: 0; + background: none; +} +.brand:hover { + color: #fff; +} +.site-subtitle { + margin: 10px 10px 0; + font-weight: initial; +} +.site-search form { + display: none; +} +.site-nav { + border-top: none; +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav-on { + display: block !important; + } +} +.menu .menu-item { + display: block; + margin: 0; +} +.menu .menu-item a { + position: relative; + box-sizing: border-box; + padding: 5px 20px; + text-align: left; + line-height: inherit; + transition-property: background-color; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + transition-delay: 0s; +} +.menu .menu-item a:hover, +.menu-item-active a { + background: #f9f9f9; + border-bottom-color: #fff; +} +.menu .menu-item br { + display: none; +} +.menu-item-active a:after { + content: " "; + position: absolute; + top: 50%; + margin-top: -3px; + right: 15px; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: #bbb; +} +.btn-bar { + background-color: #fff; +} +.site-nav-toggle { + left: 20px; + top: 50%; + -webkit-transform: translateY(-50%); + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); +} +@media (min-width: 768px) and (max-width: 991px) { + .site-nav-toggle { + display: block; + } +} +.use-motion .sidebar .motion-element { + opacity: 1; +} +.sidebar { + margin-left: -100%; + right: auto; + bottom: auto; + -webkit-transform: none; +} +.sidebar-inner { + box-sizing: border-box; + width: 240px; + color: #555; + background: #fff; + box-shadow: initial; + border-radius: initial; + opacity: 0; +} +.sidebar-inner.affix { + position: fixed; + top: 12px; +} +.sidebar-inner.affix-bottom { + position: absolute; +} +.site-overview { + margin: 0 2px; + text-align: left; +} +.site-author:before, +.site-author:after { + content: " "; + display: table; +} +.site-author:after { + clear: both; +} +.sidebar a { + color: #555; +} +.sidebar a:hover { + color: #222; +} +.site-state-item { + padding: 0 10px; +} +.links-of-author-item a:before { + display: none; +} +.links-of-author-item a { + border-bottom: none; + text-decoration: underline; +} +.feed-link { + border-top: 1px dotted #ccc; + border-bottom: 1px dotted #ccc; + text-align: center; +} +.feed-link a { + display: block; + color: #fc6423; + border: none; +} +.feed-link a:hover { + background: none; + color: #e34603; +} +.feed-link a:hover i { + color: #e34603; +} +.links-of-author { + display: flex; + flex-wrap: wrap; + justify-content: center; +} +.links-of-author-item { + margin: 5px 0 0; + width: 50%; +} +.links-of-author-item a { + max-width: 216px; + box-sizing: border-box; + display: inline-block; + margin-right: 0; + margin-bottom: 0; + padding: 0 5px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.links-of-author-item a { + display: block; + text-decoration: none; +} +.links-of-author-item a:hover { + border-radius: 4px; + background: #eee; +} +.links-of-author-item .fa { + margin-right: 2px; + font-size: 16px; +} +.links-of-author-item .fa-globe { + font-size: 15px; +} +.links-of-blogroll { + text-align: center; + margin-top: 20px; + padding: 3px 0 0; + border-top: 1px dotted #ccc; +} +.links-of-blogroll-title { + margin-top: 0; +} +.links-of-blogroll-item { + padding: 0; +} +.links-of-blogroll-inline:before, +.links-of-blogroll-inline:after { + content: " "; + display: table; +} +.links-of-blogroll-inline:after { + clear: both; +} +.links-of-blogroll-inline .links-of-blogroll-item { + margin: 5px 0 0; + width: 50%; + display: inline-block; + width: unset; +} +.links-of-blogroll-inline .links-of-blogroll-item a { + max-width: 216px; + box-sizing: border-box; + display: inline-block; + margin-right: 0; + margin-bottom: 0; + padding: 0 5px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +@media (max-width: 767px) { + .post-body { + text-align: justify; + } +} diff --git a/images/algolia_logo.svg b/images/algolia_logo.svg new file mode 100644 index 0000000..4702423 --- /dev/null +++ b/images/algolia_logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/images/apple-touch-icon-next.png b/images/apple-touch-icon-next.png new file mode 100644 index 0000000000000000000000000000000000000000..86a0d1d33bc2ae8a0416ebba67d1bbb60aa29c38 GIT binary patch literal 1544 zcmV+j2KV`iP)Px#OHfQyMIs_1FE20E)zv2_C(h2!$jHdT!ovUm|GT@p zWo2de_xJq#{8d#|IyyQ>M@Qx533`J9dBE>4W{r{KFjMLpp4N$2iMRJCGfPM7@le{)BPzVgz zp?ByVdWYVj|4-=tUS5SkR}Te{-Fr#UrxsqJ9+ed6YY9KmoXq6n^B9DRMwF@T2u)qH z!=wh-sAdPH_8fFy0-I1d68Zp);!FTtdweUZc2nr)?5^hh99w*FOU z@NYFT^!v%iPrGly0Q*+sLVtesYh+the|AET4WK8lR^dv1IZ-3FhAuiK5V|-?gf2#j z&_yW`x^K3G&OKl4kP$$ih@p!?B6M+)2(4Rz(6$=G6WT(;6uP)cgkEi(xI(Yi7`D(w zo(Y6r>FyarFV`4e&^@lk@PfWf|DSeM5}?agV`>aDXt-c%3^!*Ij0g0Q8X5*mjbRC$9oB0MOK51O_53LU8p`RzcozW; zx3wC>721@58xhdzx?E$}LW9xpjUu4UhOZO>Z8Us+D+1bR_}&sQXJ}B}yadb|8fraT zV|YUw4WF(>KpPF;mjp}*eTo5XG<+u!&}PFoihwq!uhI`XPYZ1{e1FDs0nqR?Ym5YF zqv0DwK${KUMFjNpqb>9(0vg8Y^d$otmiJc}L#s|8wD~Z~fHtQu8PG6DgwAq-&}PFY z1KMo(WI!7YpAcwxN`zJiV(2pgG_<|npE+y^Z8UsBppAx42(;Po$$&N*J{izP!?y+W zi2YsL=fkK`KXsymhWcmL#_y-S*Y&V_u#us^S9pDvbMSkxv7uidoc36Qa`CQ+5AC^d z=CTJ%0ZkrQ=v?t(E_)>OCL8YaU+nX$$--iAUNb;m^jBG2!5*5enU|exmY3aQcv=O- znjO}ft-yw2|VZs2g-W`Epf@n*5gsJ2LLtC^nFlq8+?%Xc!{Z3_|hFI~!oeU$ep z%4jE9Y9n1{ah68@W{++)8|fc~HIM4)QycHpg}3P<_K$M$L~}7jBXs?6bDwjvo+I;D zsoi}398La6+~*osM@45l#ptQ6MrZFz597*no3v)ffsUEletxWeW~`A+ntYvaeQj?e z*kSqGXZsrrhZ>IsxH$({IR{z11zkG`GR^j$#{%z41lp5(Ne#glJc5nj-BUaKR!X?# zzN6F-<0?&z;|30?9q0Z}oHG*G)Q)s%Db~RbjMU)mp-u&Dmm0zkF~o9eRh}p?ByVnjn4wmEZ*c=^{+H00012dQ@0+Qek%>aB^>E zX>4U6ba`-PAZc)PV*mhnoa6Eg2ys>@D9TUE%t_@^00ScnE@KN5BNI!L6ay0=M1VBI uWCJ6!R3OXP)X2ol#2my2%YaCrN-hBE7ZG&wLN%2D0000`v literal 0 HcmV?d00001 diff --git a/images/avatar.gif b/images/avatar.gif new file mode 100644 index 0000000000000000000000000000000000000000..989902574302a6378709709f7baab789b242ebbb GIT binary patch literal 2926 zcmc&#X;@R|5pPyvCW zxS$0@5JiQmxK`QsB_M$WA_QcYeMe9lZ0Wr}dhc_e`{&M&bH4e$cjld$Z|0nR&W@IN zN;}XF3;@8#j~_pL_)uG2-B4FoUt8ND7O$+V)Ya51EiH|{d$+i_*!T9Wq_eYKC>-eT zf8E^N+S1b0(C|hg>F(-k5eSx-mn9t?vokYub8~{m#<9`S@v*Uy;o+uG*m=Ns$m7Z(<0XJ@CTrt0hJW~Qen#>Z!7W?r|nh()5ofq~kZ znzy~Z9U{@v;$mk<$K=Gs^wd;;U*G)PobdJQcQP3nnVX#*8XTOMm=L$OkBp3rkB_%B zH+PD~0|Nu1wzg)0U~FuxOCnjApC29`Zfa~4wzdur4YjqlO1rx!CnrZoM^{%@YpSY% z|F^(bRSvkhgZ30YMfTof9m1thkJ0&{AKjnHCL$)Dze0da8WG`zb3?gtNpuF&=>(7N zdEzTF^~5nMo`$gA3+@Rags`|QI-dfDumaeDgb*TvLgV<+39|LtHWC4Tg7A+K5w>dw z;2I~1!=r;ZQ=ADEg~oywcvCbEjkQD@gBTRr42d#BqRmXu7y=4UK;yta1tDYO(T)&2 z>>dB)l4(Q)gU{y@kjUf5kDDGhGv)C7k!U;~k3?aR7>tPwVG5=Noacq2M2p|lsV3J-PV~M$fvNW^mT5gj2mZ$B01vGSPUM6`?t0P z5|2*db9iJ9Ct$q`+!-7`Cy>G6f+Q07r5lCHWUuKv*Y4<#l7TmvgH~-G|iV84!C`1kb;7jtK zzIVoJ&$S-_ptu@Fj);tkj){#sbM{<(LgM+PhjYk1v zGsMZRtu^i;cU2>iVRh6_@?3F;0OlZnzx#y!d~gIf&5;0V^2ZPv#gm6&+cXcmNJU&o z&BY_+;62cpY-oao^{Jt)Es*9!?bK7*rNGfL1#&){|Naq0_&{^RkZ&h9_SVx)!lAJz z{*xs0xX%-L5<%F5+6jn`AU!E+D8`7aA0@wh*sv2eHJ|vHv}n+`x?OyVNc0&Ko_kVt zkhXk&>G)8j%QiP`=w!J)_glm>SEtThN0zaF*D4Pg^?7*PB)nY?^vGNu{$w4ZH-D ztNExN)lDUZkh$*uL_Mk4=h`GIt>n5VBYFoU2oaVGrsnOe$ReZMcR-WWCkK)N`yvL< zw*$xngTwRMbDLQBH@Wfb(5<)0)eL$G(;d>Pik=vp2Sc;vxl3v$^urL zrr_`-?b6CPr2W2TWs;t=}soo-~^9Wg^} zJ!E_>lVqSC#jrKJTODx~KZHeWk87K5jg^ABst5E-f1y4KCRS_QC&K5!{V$5j#bmXl ze1<}~RZvyYPZFHm%a`AVOGs83>CgkueIHVRg3DMrBVR8_H>d*2huTS93jv!S)57|o zSKa$hT3iA!(9k9?xtmJbg2-rMu?|ZfZu4E15?t-+9Yuq9<344BLka#id+U`4HteuG2dWbiJ_6`m&dZYZv5zzh=hj8fOeP5?QRGqH102<_*#8ppi*Wv)eW-fPS zkpL?WuN!dFwzyzJ$vI($eCE1*Y@HZ;>(i4;d!0;k**l5mF z_+wi&oPy|Etioim$}WHC;K54h$wAxxbl_AiQ1ag82}aF0QZAC3$pCDgQJq|3#`EB4 zpD@zOiwvEp;hvLfX7O?Yo4CbHTcsaMAcpg?u#)PF8p}6Id@dD7c)FgK+taQ>q!(KM z)C+5UDbkf-&-H?4>%*BRqn@25L+soFIMlU7rIgfMei h5TAq1b#6H7J#$4=pJV>Grh$E8=BiZB9s~fZ-vK!n_o@H@ literal 0 HcmV?d00001 diff --git a/images/avatar.png b/images/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..bc05906f469115b0e8e4201b42ea3875e666e0c5 GIT binary patch literal 206711 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

r_$WH(b$d%gE&cghRBG&XNJ)<$V6FK@H% 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

T7>(O&O^su7}LHW&!=cU zE!D2>lSF4?@H-^Zd4mUY1HcEIW2HC3-T~8|@(o@HbqBGvxQ;L0Tp2vF&9Pkk$Ag}I zKbNE0eV@k~_u4c|FqcuT>7C(~vTRM|Wbeq`j8s9#3y{l!^fyP4ChOOerU^_1~?;_i8 zmeSu%92K+{N2a2wx({z1Oc(ll_Jjev_xR_(GbjsuEMRxk0nFgPv#U1|u_I&$O$+(kJxJXr4 z{{RwgED!t)txPA^V3ZWC%y=Bv9uo01lK59f(#M*x4W>3{AfA~koPHozGkyC!-fNcQ zQ1NB8y2BEbxIq%RUT_OB74|=Zyf>tHLrc0{KJF_WS5S{-#p7F&tPd(t@K*qL-Bc&= zw~B1;m2Y)P=7rp~(*2r$CP@1H?jmI$pFWi?tBsnunx`GCen$LA{h44otEY!AETmFO zMpOVm&kM9;psx(pKV-|V4R~VbUcIqdZv5O_O&aem&(7dGX1{TwUkhJdO%!Qi8B$g& zn{@NCf8A9KKj1)D8-L(UZsOiqmfqrc8OH6=1OvA06Oca&>Z6-gjiVNG)Wf!aJ(vFg z3C)W~9=oC5Ttw_0ZYGR0aq;QSE0ggL!aWO9xrQ5CYpss^TgipCpdN>SEBX^(fjX_G zqHZLT)uEgM7FF}(LGR_TEBK1d@u%#wCZ}kU-(N!=t~V&Ljx|tEVnHpBz?${po>o?C zWML{ENAnrtAAwr;hpuFn+%4P^oWwdxgQn6m&#wSiAFTX3yV7kf<(?ao;z8yGiH_0u z4r}^DZ`l_`)1s2&OVBk7$<+%j^bZ}xB;z3M4ZHBhYm%S#Nce$$rG2bLX#}MI0I0f( zMauR(FY>Quml!mZWUg~d4)3A-@4a`j(K{&?dILAuor1)p! zAGGI5M7TsGGr2fErF}=ee(&oavR8}k9nXm+(^A;;C8&;8-hdtl10uY?SpAqM(zO`l z(cr%Q=myr_CEW}3;4xp6eSb>rp~d#UGO{wKhiBv!pM^E+TV@)HBz)wKy#6AXd^4#* z{*WhuK~3s$gI}xqkL;a&;vG^uDQxu!WRwMXVkmIKx%T{O?}&e8;h^c}!^AobGtx; zW=z)d#&EW zkm~yaoJNg;N7Auj)4V@&e!(O&1xCPG6z;(EV_#x;YxZ05we`fhjr?~|qz}DXB3dR> zo*U3r*}rBFh<+oLn?Q<5qVqPhx`3kcYEw&-0)oWEx$vpzt$gHh8KJ3Sr+F~~rQ zNX9~cz5%YI{t?@Ydw6c{u3(XX9%`V+C(^$_d@1`3_^UKA56NxMjTuXu0J8gcc1Xe-xxpW1$F-be#iLN RKlZ&Ik}~#E{=ebR|JfKNV8j3b literal 0 HcmV?d00001 diff --git a/images/cc-by-nc-nd.svg b/images/cc-by-nc-nd.svg new file mode 100644 index 0000000..79a4f2e --- /dev/null +++ b/images/cc-by-nc-nd.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nc-sa.svg b/images/cc-by-nc-sa.svg new file mode 100644 index 0000000..bf6bc26 --- /dev/null +++ b/images/cc-by-nc-sa.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nc.svg b/images/cc-by-nc.svg new file mode 100644 index 0000000..3697349 --- /dev/null +++ b/images/cc-by-nc.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-nd.svg b/images/cc-by-nd.svg new file mode 100644 index 0000000..934c61e --- /dev/null +++ b/images/cc-by-nd.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/images/cc-by-sa.svg b/images/cc-by-sa.svg new file mode 100644 index 0000000..463276a --- /dev/null +++ b/images/cc-by-sa.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-by.svg b/images/cc-by.svg new file mode 100644 index 0000000..4bccd14 --- /dev/null +++ b/images/cc-by.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/images/cc-zero.svg b/images/cc-zero.svg new file mode 100644 index 0000000..0f86639 --- /dev/null +++ b/images/cc-zero.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/favicon-16x16-next.png b/images/favicon-16x16-next.png new file mode 100644 index 0000000000000000000000000000000000000000..de8c5d3a5f8215c3f7b095c9e284e061bda65f86 GIT binary patch literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%A+0*}aIAe|4wj2{=>aRxHH1AIbUeKHFb!9Yn#DLy_vBO^maMdk0`zd#Zw z0tY~@yu3V6p{Ay$mX_Az$B+O0`?r4m`qisfPnj~MwY3$f@^9nqaG!lvI6;x#X;^) z4C~IxyaaMM3p^r=f%IJvW-JI^#13S11^9%x`eYU=fq|TyoQjGHgbgIWfB*jF%NI8{ zx2;>Zu3x`iO-&6bs0aoM3JPUqWq0n}*}Qo(P|Le_@AmKC57(%ytPE5IHwwrGiqzKD z{{8#c)YSCg!Gk}3{P_R>|Gj(no6k4eJyZq^Jd4Jw%*zpM57yY9pK zMcQgs21j>QigM54W!RCVazM0UCtHDVhG46xh5pA!(TqDDnlwLEm~eRS(JM@6p3293 zNMwDW(!P~x!s$%jghh-wOB)P19fJ88oOHECttPa5*~LF^Vp#0%pwrbyN~~|szWB|o z_J4%n&U>rEu5s2%I6jof{A;(j!|>g|;yo?%ZfFGheBJp^9_Zijs*s41pu}>8f};Gi z%$!t(lFEWqh0KDIWCn(cIgdZ_a1@4VXq@stea7=?5CgL^w_Y;0u(GiCWD#az1(ybs x!zs+ln?n>%-?(z($eANDN7zp{cr5VJV|XPlSn|oqbSlsa22WQ%mvv4FO#p=N1nU3* literal 0 HcmV?d00001 diff --git a/images/loading.gif b/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..efb6768db9d768e6c7322905ed131b4f184c23f8 GIT binary patch literal 35 ncmZ?wbhEHbWMp7uXkcLY|NlP&1A`6_1Nn>$3`|Tej11NQg@y&% literal 0 HcmV?d00001 diff --git a/images/logo.svg b/images/logo.svg new file mode 100644 index 0000000..cbb3937 --- /dev/null +++ b/images/logo.svg @@ -0,0 +1,23 @@ + +image/svg+xml diff --git a/images/placeholder.gif b/images/placeholder.gif new file mode 100644 index 0000000000000000000000000000000000000000..efb6768db9d768e6c7322905ed131b4f184c23f8 GIT binary patch literal 35 ncmZ?wbhEHbWMp7uXkcLY|NlP&1A`6_1Nn>$3`|Tej11NQg@y&% literal 0 HcmV?d00001 diff --git a/images/quote-l.svg b/images/quote-l.svg new file mode 100644 index 0000000..6dd94a4 --- /dev/null +++ b/images/quote-l.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/images/quote-r.svg b/images/quote-r.svg new file mode 100644 index 0000000..312b64d --- /dev/null +++ b/images/quote-r.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/images/searchicon.png b/images/searchicon.png new file mode 100644 index 0000000000000000000000000000000000000000..14a16cad3e0722952bae78768f4ef457dbfb4c0d GIT binary patch literal 766 zcmVJ#lUOPJ;Vj#I(msyiw!_!8!*~r|1AXz-KMD5FGF_S0z}juE=ksK$FPr z1&d6=n}oZS!R}eX;|zGLjbmV~Nw`Ru>4Ft5zJp65S7H)gs*T@)msa?=1Ab}a<$epu zBxfErz6xQZxgsL>$C|!~n1s{XcrJ@W*eDc5Y04!0Oq{=!{~=r{wr|KJd{-N11u66* z3cwbT>oo~?vuM~+6;iwzEYZded|?eIahvBNf7#%*3 zRS(z-F7h+W&)&suB^9-%Kl-?`PY%!G{OlE(5Vl((wXzQ`hO5n#ur4kQ_eA+8lfw2T wk(|G@S?Y{yH0NDkLt#Wf;(f|Me07*qoM6N<$f@1()lK=n! literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..38f62ce --- /dev/null +++ b/index.html @@ -0,0 +1,1178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Xu的博客 + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/src/affix.js b/js/src/affix.js new file mode 100644 index 0000000..11a3d39 --- /dev/null +++ b/js/src/affix.js @@ -0,0 +1,162 @@ +/* ======================================================================== + * Bootstrap: affix.js v3.3.5 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = null + this.unpin = null + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.3.5' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && scrollTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var height = this.$element.height() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + var scrollHeight = Math.max($(document).height(), $(document.body).height()) + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) + + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - height - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/js/src/algolia-search.js b/js/src/algolia-search.js new file mode 100644 index 0000000..9787e2a --- /dev/null +++ b/js/src/algolia-search.js @@ -0,0 +1,115 @@ +/* global instantsearch: true */ +/*jshint camelcase: false */ + +$(document).ready(function () { + var algoliaSettings = CONFIG.algolia; + var isAlgoliaSettingsValid = algoliaSettings.applicationID && + algoliaSettings.apiKey && + algoliaSettings.indexName; + + if (!isAlgoliaSettingsValid) { + window.console.error('Algolia Settings are invalid.'); + return; + } + + var search = instantsearch({ + appId: algoliaSettings.applicationID, + apiKey: algoliaSettings.apiKey, + indexName: algoliaSettings.indexName, + searchFunction: function (helper) { + var searchInput = $('#algolia-search-input').find('input'); + + if (searchInput.val()) { + helper.search(); + } + } + }); + + // Registering Widgets + [ + instantsearch.widgets.searchBox({ + container: '#algolia-search-input', + placeholder: algoliaSettings.labels.input_placeholder + }), + + instantsearch.widgets.hits({ + container: '#algolia-hits', + hitsPerPage: algoliaSettings.hits.per_page || 10, + templates: { + item: function (data) { + var link = data.permalink ? data.permalink : (CONFIG.root + data.path); + return ( + '' + + data._highlightResult.title.value + + '' + ); + }, + empty: function (data) { + return ( + '
' + + algoliaSettings.labels.hits_empty.replace(/\$\{query}/, data.query) + + '
' + ); + } + }, + cssClasses: { + item: 'algolia-hit-item' + } + }), + + instantsearch.widgets.stats({ + container: '#algolia-stats', + templates: { + body: function (data) { + var stats = algoliaSettings.labels.hits_stats + .replace(/\$\{hits}/, data.nbHits) + .replace(/\$\{time}/, data.processingTimeMS); + return ( + stats + + '' + + ' Algolia' + + '' + + '
' + ); + } + } + }), + + instantsearch.widgets.pagination({ + container: '#algolia-pagination', + scrollTo: false, + showFirstLast: false, + labels: { + first: '', + last: '', + previous: '', + next: '' + }, + cssClasses: { + root: 'pagination', + item: 'pagination-item', + link: 'page-number', + active: 'current', + disabled: 'disabled-item' + } + }) + ].forEach(search.addWidget, search); + + search.start(); + + $('.popup-trigger').on('click', function(e) { + e.stopPropagation(); + $('body') + .append('
') + .css('overflow', 'hidden'); + $('.popup').toggle(); + $('#algolia-search-input').find('input').focus(); + }); + + $('.popup-btn-close').click(function(){ + $('.popup').hide(); + $('.algolia-pop-overlay').remove(); + $('body').css('overflow', ''); + }); + +}); diff --git a/js/src/bootstrap.js b/js/src/bootstrap.js new file mode 100644 index 0000000..d9c33ed --- /dev/null +++ b/js/src/bootstrap.js @@ -0,0 +1,52 @@ +/* global NexT: true */ + +$(document).ready(function () { + + $(document).trigger('bootstrap:before'); + + NexT.utils.isMobile() && window.FastClick.attach(document.body); + + NexT.utils.lazyLoadPostsImages(); + + NexT.utils.registerESCKeyEvent(); + + NexT.utils.registerBackToTop(); + + // Mobile top menu bar. + $('.site-nav-toggle button').on('click', function () { + var $siteNav = $('.site-nav'); + var ON_CLASS_NAME = 'site-nav-on'; + var isSiteNavOn = $siteNav.hasClass(ON_CLASS_NAME); + var animateAction = isSiteNavOn ? 'slideUp' : 'slideDown'; + var animateCallback = isSiteNavOn ? 'removeClass' : 'addClass'; + + $siteNav.stop()[animateAction]('fast', function () { + $siteNav[animateCallback](ON_CLASS_NAME); + }); + }); + + /** + * Register JS handlers by condition option. + * Need to add config option in Front-End at 'layout/_partials/head.swig' file. + */ + CONFIG.fancybox && NexT.utils.wrapImageWithFancyBox(); + CONFIG.tabs && NexT.utils.registerTabsTag(); + + NexT.utils.embeddedVideoTransformer(); + NexT.utils.addActiveClassToMenuItem(); + + + // Define Motion Sequence. + NexT.motion.integrator + .add(NexT.motion.middleWares.logo) + .add(NexT.motion.middleWares.menu) + .add(NexT.motion.middleWares.postList) + .add(NexT.motion.middleWares.sidebar); + + $(document).trigger('motion:before'); + + // Bootstrap Motion. + CONFIG.motion.enable && NexT.motion.integrator.bootstrap(); + + $(document).trigger('bootstrap:after'); +}); diff --git a/js/src/exturl.js b/js/src/exturl.js new file mode 100644 index 0000000..b85062a --- /dev/null +++ b/js/src/exturl.js @@ -0,0 +1,15 @@ +/* global NexT: true */ + +$(document).ready(function () { + + // Create Base64 Object + var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,"n");var t="";for(var n=0;n127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}; + + $('.exturl').on('click', function () { + var $exturl = $(this).attr('data-url'); + var $decurl = Base64.decode($exturl); + window.open($decurl, '_blank'); + return false; + }); + +}); diff --git a/js/src/hook-duoshuo.js b/js/src/hook-duoshuo.js new file mode 100644 index 0000000..ca64dbd --- /dev/null +++ b/js/src/hook-duoshuo.js @@ -0,0 +1,115 @@ +/* global DUOSHUO: true */ +/* jshint camelcase: false */ + +typeof DUOSHUO !== 'undefined' ? + hookTemplate() : + ($('#duoshuo-script')[0].onload = hookTemplate); + + +function hookTemplate() { + var post = DUOSHUO.templates.post; + + DUOSHUO.templates.post = function (e, t) { + var rs = post(e, t); + var agent = e.post.agent; + var userId = e.post.author.user_id; + var admin = ''; + + if (userId && (userId == CONFIG.duoshuo.userId)) { + admin = '' + CONFIG.duoshuo.author + ''; + } + + if (agent && /^Mozilla/.test(agent)) { + rs = rs.replace(/<\/div>

/, admin + getAgentInfo(agent) + '

'); + } + + return rs; + }; +} + +function getAgentInfo(string) { + $.ua.set(string); + + var UNKNOWN = 'Unknown'; + var sua = $.ua; + var separator = isMobile() ? '

' : ''; + var osName = sua.os.name || UNKNOWN; + var osVersion = sua.os.version || UNKNOWN; + var browserName = sua.browser.name || UNKNOWN; + var browserVersion = sua.browser.version || UNKNOWN; + var iconMapping = { + os: { + android : 'android', + linux : 'linux', + windows : 'windows', + ios : 'apple', + 'mac os': 'apple', + unknown : 'desktop' + }, + browser: { + chrome : 'chrome', + chromium : 'chrome', + firefox : 'firefox', + opera : 'opera', + safari : 'safari', + ie : 'internet-explorer', + wechat : 'wechat', + qq : 'qq', + unknown : 'globe' + } + }; + var osIcon = iconMapping.os[osName.toLowerCase()]; + var browserIcon = iconMapping.browser[getBrowserKey()]; + + return separator + + '' + + '' + + osName + ' ' + osVersion + + '' + separator + + '' + + '' + + browserName + ' ' + browserVersion + + ''; + + function getBrowserKey () { + var key = browserName.toLowerCase(); + + if (key.match(/WeChat/i)) { + return 'wechat'; + } + + if (key.match(/QQBrowser/i)) { + return 'qq'; + } + + return key; + } + + function isMobile() { + var userAgent = window.navigator.userAgent; + + var isiPad = userAgent.match(/iPad/i) !== null; + var mobileUA = [ + 'iphone', 'android', 'phone', 'mobile', + 'wap', 'netfront', 'x11', 'java', 'opera mobi', + 'opera mini', 'ucweb', 'windows ce', 'symbian', + 'symbianos', 'series', 'webos', 'sony', + 'blackberry', 'dopod', 'nokia', 'samsung', + 'palmsource', 'xda', 'pieplus', 'meizu', + 'midp' ,'cldc' , 'motorola', 'foma', + 'docomo', 'up.browser', 'up.link', 'blazer', + 'helio', 'hosin', 'huawei', 'novarra', + 'coolpad', 'webos', 'techfaith', 'palmsource', + 'alcatel', 'amoi', 'ktouch', 'nexian', + 'ericsson', 'philips', 'sagem', 'wellcom', + 'bunjalloo', 'maui', 'smartphone', 'iemobile', + 'spice', 'bird', 'zte-', 'longcos', + 'pantech', 'gionee', 'portalmmm', 'jig browser', + 'hiptop', 'benq', 'haier', '^lct', + '320x320', '240x320', '176x220' + ]; + var pattern = new RegExp(mobileUA.join('|'), 'i'); + + return !isiPad && userAgent.match(pattern); + } +} diff --git a/js/src/js.cookie.js b/js/src/js.cookie.js new file mode 100644 index 0000000..c6c3975 --- /dev/null +++ b/js/src/js.cookie.js @@ -0,0 +1,165 @@ +/*! + * JavaScript Cookie v2.1.4 + * https://github.com/js-cookie/js-cookie + * + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack + * Released under the MIT license + */ +;(function (factory) { + var registeredInModuleLoader = false; + if (typeof define === 'function' && define.amd) { + define(factory); + registeredInModuleLoader = true; + } + if (typeof exports === 'object') { + module.exports = factory(); + registeredInModuleLoader = true; + } + if (!registeredInModuleLoader) { + var OldCookies = window.Cookies; + var api = window.Cookies = factory(); + api.noConflict = function () { + window.Cookies = OldCookies; + return api; + }; + } +}(function () { + function extend () { + var i = 0; + var result = {}; + for (; i < arguments.length; i++) { + var attributes = arguments[ i ]; + for (var key in attributes) { + result[key] = attributes[key]; + } + } + return result; + } + + function init (converter) { + function api (key, value, attributes) { + var result; + if (typeof document === 'undefined') { + return; + } + + // Write + + if (arguments.length > 1) { + attributes = extend({ + path: '/' + }, api.defaults, attributes); + + if (typeof attributes.expires === 'number') { + var expires = new Date(); + expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); + attributes.expires = expires; + } + + // We're using "expires" because "max-age" is not supported by IE + attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; + + try { + result = JSON.stringify(value); + if (/^[\{\[]/.test(result)) { + value = result; + } + } catch (e) {} + + if (!converter.write) { + value = encodeURIComponent(String(value)) + .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); + } else { + value = converter.write(value, key); + } + + key = encodeURIComponent(String(key)); + key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); + key = key.replace(/[\(\)]/g, escape); + + var stringifiedAttributes = ''; + + for (var attributeName in attributes) { + if (!attributes[attributeName]) { + continue; + } + stringifiedAttributes += '; ' + attributeName; + if (attributes[attributeName] === true) { + continue; + } + stringifiedAttributes += '=' + attributes[attributeName]; + } + return (document.cookie = key + '=' + value + stringifiedAttributes); + } + + // Read + + if (!key) { + result = {}; + } + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling "get()" + var cookies = document.cookie ? document.cookie.split('; ') : []; + var rdecode = /(%[0-9A-Z]{2})+/g; + var i = 0; + + for (; i < cookies.length; i++) { + var parts = cookies[i].split('='); + var cookie = parts.slice(1).join('='); + + if (cookie.charAt(0) === '"') { + cookie = cookie.slice(1, -1); + } + + try { + var name = parts[0].replace(rdecode, decodeURIComponent); + cookie = converter.read ? + converter.read(cookie, name) : converter(cookie, name) || + cookie.replace(rdecode, decodeURIComponent); + + if (this.json) { + try { + cookie = JSON.parse(cookie); + } catch (e) {} + } + + if (key === name) { + result = cookie; + break; + } + + if (!key) { + result[name] = cookie; + } + } catch (e) {} + } + + return result; + } + + api.set = api; + api.get = function (key) { + return api.call(api, key); + }; + api.getJSON = function () { + return api.apply({ + json: true + }, [].slice.call(arguments)); + }; + api.defaults = {}; + + api.remove = function (key, attributes) { + api(key, '', extend(attributes, { + expires: -1 + })); + }; + + api.withConverter = init; + + return api; + } + + return init(function () {}); +})); diff --git a/js/src/motion.js b/js/src/motion.js new file mode 100644 index 0000000..1129179 --- /dev/null +++ b/js/src/motion.js @@ -0,0 +1,352 @@ +/* global NexT: true */ + +$(document).ready(function () { + NexT.motion = {}; + + var sidebarToggleLines = { + lines: [], + push: function (line) { + this.lines.push(line); + }, + init: function () { + this.lines.forEach(function (line) { + line.init(); + }); + }, + arrow: function () { + this.lines.forEach(function (line) { + line.arrow(); + }); + }, + close: function () { + this.lines.forEach(function (line) { + line.close(); + }); + } + }; + + function SidebarToggleLine(settings) { + this.el = $(settings.el); + this.status = $.extend({}, { + init: { + width: '100%', + opacity: 1, + left: 0, + rotateZ: 0, + top: 0 + } + }, settings.status); + } + + SidebarToggleLine.prototype.init = function () { + this.transform('init'); + }; + SidebarToggleLine.prototype.arrow = function () { + this.transform('arrow'); + }; + SidebarToggleLine.prototype.close = function () { + this.transform('close'); + }; + SidebarToggleLine.prototype.transform = function (status) { + this.el.velocity('stop').velocity(this.status[status]); + }; + + var sidebarToggleLine1st = new SidebarToggleLine({ + el: '.sidebar-toggle-line-first', + status: { + arrow: {width: '50%', rotateZ: '-45deg', top: '2px'}, + close: {width: '100%', rotateZ: '-45deg', top: '5px'} + } + }); + var sidebarToggleLine2nd = new SidebarToggleLine({ + el: '.sidebar-toggle-line-middle', + status: { + arrow: {width: '90%'}, + close: {opacity: 0} + } + }); + var sidebarToggleLine3rd = new SidebarToggleLine({ + el: '.sidebar-toggle-line-last', + status: { + arrow: {width: '50%', rotateZ: '45deg', top: '-2px'}, + close: {width: '100%', rotateZ: '45deg', top: '-5px'} + } + }); + + sidebarToggleLines.push(sidebarToggleLine1st); + sidebarToggleLines.push(sidebarToggleLine2nd); + sidebarToggleLines.push(sidebarToggleLine3rd); + + var SIDEBAR_WIDTH = '320px'; + var SIDEBAR_DISPLAY_DURATION = 200; + var xPos, yPos; + + var sidebarToggleMotion = { + toggleEl: $('.sidebar-toggle'), + dimmerEl: $('#sidebar-dimmer'), + sidebarEl: $('.sidebar'), + isSidebarVisible: false, + init: function () { + this.toggleEl.on('click', this.clickHandler.bind(this)); + this.dimmerEl.on('click', this.clickHandler.bind(this)); + this.toggleEl.on('mouseenter', this.mouseEnterHandler.bind(this)); + this.toggleEl.on('mouseleave', this.mouseLeaveHandler.bind(this)); + this.sidebarEl.on('touchstart', this.touchstartHandler.bind(this)); + this.sidebarEl.on('touchend', this.touchendHandler.bind(this)); + this.sidebarEl.on('touchmove', function(e){e.preventDefault();}); + + $(document) + .on('sidebar.isShowing', function () { + NexT.utils.isDesktop() && $('body').velocity('stop').velocity( + {paddingRight: SIDEBAR_WIDTH}, + SIDEBAR_DISPLAY_DURATION + ); + }) + .on('sidebar.isHiding', function () { + }); + }, + clickHandler: function () { + this.isSidebarVisible ? this.hideSidebar() : this.showSidebar(); + this.isSidebarVisible = !this.isSidebarVisible; + }, + mouseEnterHandler: function () { + if (this.isSidebarVisible) { + return; + } + sidebarToggleLines.arrow(); + }, + mouseLeaveHandler: function () { + if (this.isSidebarVisible) { + return; + } + sidebarToggleLines.init(); + }, + touchstartHandler: function(e) { + xPos = e.originalEvent.touches[0].clientX; + yPos = e.originalEvent.touches[0].clientY; + }, + touchendHandler: function(e) { + var _xPos = e.originalEvent.changedTouches[0].clientX; + var _yPos = e.originalEvent.changedTouches[0].clientY; + if (_xPos-xPos > 30 && Math.abs(_yPos-yPos) < 20) { + this.clickHandler(); + } + }, + showSidebar: function () { + var self = this; + + sidebarToggleLines.close(); + + this.sidebarEl.velocity('stop').velocity({ + width: SIDEBAR_WIDTH + }, { + display: 'block', + duration: SIDEBAR_DISPLAY_DURATION, + begin: function () { + $('.sidebar .motion-element').velocity( + 'transition.slideRightIn', + { + stagger: 50, + drag: true, + complete: function () { + self.sidebarEl.trigger('sidebar.motion.complete'); + } + } + ); + }, + complete: function () { + self.sidebarEl.addClass('sidebar-active'); + self.sidebarEl.trigger('sidebar.didShow'); + } + } + ); + + this.sidebarEl.trigger('sidebar.isShowing'); + }, + hideSidebar: function () { + NexT.utils.isDesktop() && $('body').velocity('stop').velocity({paddingRight: 0}); + this.sidebarEl.find('.motion-element').velocity('stop').css('display', 'none'); + this.sidebarEl.velocity('stop').velocity({width: 0}, {display: 'none'}); + + sidebarToggleLines.init(); + + this.sidebarEl.removeClass('sidebar-active'); + this.sidebarEl.trigger('sidebar.isHiding'); + + // Prevent adding TOC to Overview if Overview was selected when close & open sidebar. + if (!!$('.post-toc-wrap')) { + if ($('.site-overview-wrap').css('display') === 'block') { + $('.post-toc-wrap').removeClass('motion-element'); + } else { + $('.post-toc-wrap').addClass('motion-element'); + } + } + } + }; + sidebarToggleMotion.init(); + + NexT.motion.integrator = { + queue: [], + cursor: -1, + add: function (fn) { + this.queue.push(fn); + return this; + }, + next: function () { + this.cursor++; + var fn = this.queue[this.cursor]; + $.isFunction(fn) && fn(NexT.motion.integrator); + }, + bootstrap: function () { + this.next(); + } + }; + + NexT.motion.middleWares = { + logo: function (integrator) { + var sequence = []; + var $brand = $('.brand'); + var $title = $('.site-title'); + var $subtitle = $('.site-subtitle'); + var $logoLineTop = $('.logo-line-before i'); + var $logoLineBottom = $('.logo-line-after i'); + + $brand.size() > 0 && sequence.push({ + e: $brand, + p: {opacity: 1}, + o: {duration: 200} + }); + + NexT.utils.isMist() && hasElement([$logoLineTop, $logoLineBottom]) && + sequence.push( + getMistLineSettings($logoLineTop, '100%'), + getMistLineSettings($logoLineBottom, '-100%') + ); + + hasElement($title) && sequence.push({ + e: $title, + p: {opacity: 1, top: 0}, + o: { duration: 200 } + }); + + hasElement($subtitle) && sequence.push({ + e: $subtitle, + p: {opacity: 1, top: 0}, + o: {duration: 200} + }); + + if (CONFIG.motion.async) { + integrator.next(); + } + + if (sequence.length > 0) { + sequence[sequence.length - 1].o.complete = function () { + integrator.next(); + }; + $.Velocity.RunSequence(sequence); + } else { + integrator.next(); + } + + + function getMistLineSettings (element, translateX) { + return { + e: $(element), + p: {translateX: translateX}, + o: { + duration: 500, + sequenceQueue: false + } + }; + } + + /** + * Check if $elements exist. + * @param {jQuery|Array} $elements + * @returns {boolean} + */ + function hasElement ($elements) { + $elements = Array.isArray($elements) ? $elements : [$elements]; + return $elements.every(function ($element) { + return $.isFunction($element.size) && $element.size() > 0; + }); + } + }, + + menu: function (integrator) { + + if (CONFIG.motion.async) { + integrator.next(); + } + + $('.menu-item').velocity('transition.slideDownIn', { + display: null, + duration: 200, + complete: function () { + integrator.next(); + } + }); + }, + + postList: function (integrator) { + //var $post = $('.post'); + var $postBlock = $('.post-block, .pagination, .comments'); + var $postBlockTransition = CONFIG.motion.transition.post_block; + var $postHeader = $('.post-header'); + var $postHeaderTransition = CONFIG.motion.transition.post_header; + var $postBody = $('.post-body'); + var $postBodyTransition = CONFIG.motion.transition.post_body; + var $collHeader = $('.collection-title, .archive-year'); + var $collHeaderTransition = CONFIG.motion.transition.coll_header; + var $sidebarAffix = $('.sidebar-inner'); + var $sidebarAffixTransition = CONFIG.motion.transition.sidebar; + var hasPost = $postBlock.size() > 0; + + hasPost ? postMotion() : integrator.next(); + + if (CONFIG.motion.async) { + integrator.next(); + } + + function postMotion () { + var postMotionOptions = window.postMotionOptions || { + stagger: 100, + drag: true + }; + postMotionOptions.complete = function () { + // After motion complete need to remove transform from sidebar to let affix work on Pisces | Gemini. + if (CONFIG.motion.transition.sidebar && (NexT.utils.isPisces() || NexT.utils.isGemini())) { + $sidebarAffix.css({ 'transform': 'initial' }); + } + integrator.next(); + }; + + //$post.velocity('transition.slideDownIn', postMotionOptions); + if (CONFIG.motion.transition.post_block) { + $postBlock.velocity('transition.' + $postBlockTransition, postMotionOptions); + } + if (CONFIG.motion.transition.post_header) { + $postHeader.velocity('transition.' + $postHeaderTransition, postMotionOptions); + } + if (CONFIG.motion.transition.post_body) { + $postBody.velocity('transition.' + $postBodyTransition, postMotionOptions); + } + if (CONFIG.motion.transition.coll_header) { + $collHeader.velocity('transition.' + $collHeaderTransition, postMotionOptions); + } + // Only for Pisces | Gemini. + if (CONFIG.motion.transition.sidebar && (NexT.utils.isPisces() || NexT.utils.isGemini())) { + $sidebarAffix.velocity('transition.' + $sidebarAffixTransition, postMotionOptions); + } + } + }, + + sidebar: function (integrator) { + if (CONFIG.sidebar.display === 'always') { + NexT.utils.displaySidebar(); + } + integrator.next(); + } + }; + +}); diff --git a/js/src/post-details.js b/js/src/post-details.js new file mode 100644 index 0000000..a82bcc2 --- /dev/null +++ b/js/src/post-details.js @@ -0,0 +1,99 @@ +/* global NexT: true */ + +$(document).ready(function () { + + initScrollSpy(); + + function initScrollSpy () { + var tocSelector = '.post-toc'; + var $tocElement = $(tocSelector); + var activeCurrentSelector = '.active-current'; + + $tocElement + .on('activate.bs.scrollspy', function () { + var $currentActiveElement = $(tocSelector + ' .active').last(); + + removeCurrentActiveClass(); + $currentActiveElement.addClass('active-current'); + + // Scrolling to center active TOC element if TOC content is taller then viewport. + $tocElement.scrollTop($currentActiveElement.offset().top - $tocElement.offset().top + $tocElement.scrollTop() - ($tocElement.height() / 2)); + }) + .on('clear.bs.scrollspy', removeCurrentActiveClass); + + $('body').scrollspy({ target: tocSelector }); + + function removeCurrentActiveClass () { + $(tocSelector + ' ' + activeCurrentSelector) + .removeClass(activeCurrentSelector.substring(1)); + } + } + +}); + +$(document).ready(function () { + var html = $('html'); + var TAB_ANIMATE_DURATION = 200; + var hasVelocity = $.isFunction(html.velocity); + + $('.sidebar-nav li').on('click', function () { + var item = $(this); + var activeTabClassName = 'sidebar-nav-active'; + var activePanelClassName = 'sidebar-panel-active'; + if (item.hasClass(activeTabClassName)) { + return; + } + + var currentTarget = $('.' + activePanelClassName); + var target = $('.' + item.data('target')); + + hasVelocity ? + currentTarget.velocity('transition.slideUpOut', TAB_ANIMATE_DURATION, function () { + target + .velocity('stop') + .velocity('transition.slideDownIn', TAB_ANIMATE_DURATION) + .addClass(activePanelClassName); + }) : + currentTarget.animate({ opacity: 0 }, TAB_ANIMATE_DURATION, function () { + currentTarget.hide(); + target + .stop() + .css({'opacity': 0, 'display': 'block'}) + .animate({ opacity: 1 }, TAB_ANIMATE_DURATION, function () { + currentTarget.removeClass(activePanelClassName); + target.addClass(activePanelClassName); + }); + }); + + item.siblings().removeClass(activeTabClassName); + item.addClass(activeTabClassName); + }); + + // TOC item animation navigate & prevent #item selector in adress bar. + $('.post-toc a').on('click', function (e) { + e.preventDefault(); + var targetSelector = NexT.utils.escapeSelector(this.getAttribute('href')); + var offset = $(targetSelector).offset().top; + + hasVelocity ? + html.velocity('stop').velocity('scroll', { + offset: offset + 'px', + mobileHA: false + }) : + $('html, body').stop().animate({ + scrollTop: offset + }, 500); + }); + + // Expand sidebar on post detail page by default, when post has a toc. + var $tocContent = $('.post-toc-content'); + var isSidebarCouldDisplay = CONFIG.sidebar.display === 'post' || + CONFIG.sidebar.display === 'always'; + var hasTOC = $tocContent.length > 0 && $tocContent.html().trim().length > 0; + if (isSidebarCouldDisplay && hasTOC) { + CONFIG.motion.enable ? + (NexT.motion.middleWares.sidebar = function () { + NexT.utils.displaySidebar(); + }) : NexT.utils.displaySidebar(); + } +}); diff --git a/js/src/schemes/pisces.js b/js/src/schemes/pisces.js new file mode 100644 index 0000000..0e6e426 --- /dev/null +++ b/js/src/schemes/pisces.js @@ -0,0 +1,57 @@ +$(document).ready(function () { + + var sidebarInner = $('.sidebar-inner'); + + initAffix(); + resizeListener(); + + function initAffix () { + var headerOffset = getHeaderOffset(), + footerOffset = getFooterOffset(), + sidebarHeight = $('#sidebar').height() + NexT.utils.getSidebarb2tHeight(), + contentHeight = $('#content').height(); + + // Not affix if sidebar taller then content (to prevent bottom jumping). + if (headerOffset + sidebarHeight < contentHeight) { + sidebarInner.affix({ + offset: { + top: headerOffset - CONFIG.sidebar.offset, + bottom: footerOffset + } + }); + } + + setSidebarMarginTop(headerOffset).css({ 'margin-left': 'initial' }); + } + + function resizeListener () { + var mql = window.matchMedia('(min-width: 991px)'); + mql.addListener(function(e){ + if(e.matches){ + recalculateAffixPosition(); + } + }); + } + + function getHeaderOffset () { + return $('.header-inner').height() + CONFIG.sidebar.offset; + } + + function getFooterOffset () { + var footerInner = $('.footer-inner'), + footerMargin = footerInner.outerHeight(true) - footerInner.outerHeight(), + footerOffset = footerInner.outerHeight(true) + footerMargin; + return footerOffset; + } + + function setSidebarMarginTop (headerOffset) { + return $('#sidebar').css({ 'margin-top': headerOffset }); + } + + function recalculateAffixPosition () { + $(window).off('.affix'); + sidebarInner.removeData('bs.affix').removeClass('affix affix-top affix-bottom'); + initAffix(); + } + +}); diff --git a/js/src/scroll-cookie.js b/js/src/scroll-cookie.js new file mode 100644 index 0000000..34ff200 --- /dev/null +++ b/js/src/scroll-cookie.js @@ -0,0 +1,23 @@ +$(document).ready(function() { + + // Set relative link path (without domain) + var rpath = window.location.href.replace(window.location.origin, ""); + + // Write position in cookie + var timeout; + $(window).on("scroll", function() { + clearTimeout(timeout); + timeout = setTimeout(function () { + Cookies.set("scroll-cookie", ($(window).scrollTop() + "|" + rpath), { expires: 365, path: '' }); + }, 250); + }); + + // Read position from cookie + if (Cookies.get("scroll-cookie") !== undefined) { + var cvalues = Cookies.get("scroll-cookie").split('|'); + if (cvalues[1] == rpath) { + $(window).scrollTop(cvalues[0]); + } + } + +}); diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js new file mode 100644 index 0000000..f5c5c6c --- /dev/null +++ b/js/src/scrollspy.js @@ -0,0 +1,182 @@ +/* ======================================================================== +* Bootstrap: scrollspy.js v3.3.2 +* http://getbootstrap.com/javascript/#scrollspy +* ======================================================================== +* Copyright 2011-2015 Twitter, Inc. +* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) +* ======================================================================== */ + +/** + * Custom by iissnan + * + * - Add a `clear.bs.scrollspy` event. + * - Esacpe targets selector. + */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + this.$body = $(document.body) + this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.3.2' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var that = this + var offsetMethod = 'offset' + var offsetBase = 0 + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(NexT.utils.escapeSelector(href)) // Need to escape selector. + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + that.offsets.push(this[0]) + that.targets.push(this[1]) + }) + + + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop < offsets[0]) { + $(this.selector).trigger('clear.bs.scrollspy') // Add a custom event. + this.activeTarget = null + return this.clear() + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + this.clear() + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); diff --git a/js/src/utils.js b/js/src/utils.js new file mode 100644 index 0000000..33c50e8 --- /dev/null +++ b/js/src/utils.js @@ -0,0 +1,337 @@ +/* global NexT: true */ + +NexT.utils = NexT.$u = { + /** + * Wrap images with fancybox support. + */ + wrapImageWithFancyBox: function () { + $('.content img') + .not('[hidden]') + .not('.group-picture img, .post-gallery img') + .each(function () { + var $image = $(this); + var imageTitle = $image.attr('title'); + var $imageWrapLink = $image.parent('a'); + + if ($imageWrapLink.size() < 1) { + var imageLink = ($image.attr('data-original')) ? this.getAttribute('data-original') : this.getAttribute('src'); + $imageWrapLink = $image.wrap('').parent('a'); + } + + $imageWrapLink.addClass('fancybox fancybox.image'); + $imageWrapLink.attr('rel', 'group'); + + if (imageTitle) { + $imageWrapLink.append('

' + imageTitle + '

'); + + //make sure img title tag will show correctly in fancybox + $imageWrapLink.attr('title', imageTitle); + } + }); + + $('.fancybox').fancybox({ + helpers: { + overlay: { + locked: false + } + } + }); + }, + + lazyLoadPostsImages: function () { + $('#posts').find('img').lazyload({ + //placeholder: '/images/loading.gif', + effect: 'fadeIn', + threshold : 0 + }); + }, + + /** + * Tabs tag listener (without twitter bootstrap). + */ + registerTabsTag: function () { + var tNav = '.tabs ul.nav-tabs '; + + // Binding `nav-tabs` & `tab-content` by real time permalink changing. + $(function() { + $(window).bind('hashchange', function() { + var tHash = location.hash; + if (tHash !== '') { + $(tNav + 'li:has(a[href="' + tHash + '"])').addClass('active').siblings().removeClass('active'); + $(tHash).addClass('active').siblings().removeClass('active'); + } + }).trigger('hashchange'); + }); + + $(tNav + '.tab').on('click', function (href) { + href.preventDefault(); + // Prevent selected tab to select again. + if(!$(this).hasClass('active')){ + + // Add & Remove active class on `nav-tabs` & `tab-content`. + $(this).addClass('active').siblings().removeClass('active'); + var tActive = $(this).find('a').attr('href'); + $(tActive).addClass('active').siblings().removeClass('active'); + + // Clear location hash in browser if #permalink exists. + if (location.hash !== '') { + history.pushState('', document.title, window.location.pathname + window.location.search); + } + } + }); + + }, + + registerESCKeyEvent: function () { + $(document).on('keyup', function (event) { + var shouldDismissSearchPopup = event.which === 27 && + $('.search-popup').is(':visible'); + if (shouldDismissSearchPopup) { + $('.search-popup').hide(); + $('.search-popup-overlay').remove(); + $('body').css('overflow', ''); + } + }); + }, + + registerBackToTop: function () { + var THRESHOLD = 50; + var $top = $('.back-to-top'); + + $(window).on('scroll', function () { + $top.toggleClass('back-to-top-on', window.pageYOffset > THRESHOLD); + + var scrollTop = $(window).scrollTop(); + var contentVisibilityHeight = NexT.utils.getContentVisibilityHeight(); + var scrollPercent = (scrollTop) / (contentVisibilityHeight); + var scrollPercentRounded = Math.round(scrollPercent*100); + var scrollPercentMaxed = (scrollPercentRounded > 100) ? 100 : scrollPercentRounded; + $('#scrollpercent>span').html(scrollPercentMaxed); + }); + + $top.on('click', function () { + $('body').velocity('scroll'); + }); + }, + + /** + * Transform embedded video to support responsive layout. + * @see http://toddmotto.com/fluid-and-responsive-youtube-and-vimeo-videos-with-fluidvids-js/ + */ + embeddedVideoTransformer: function () { + var $iframes = $('iframe'); + + // Supported Players. Extend this if you need more players. + var SUPPORTED_PLAYERS = [ + 'www.youtube.com', + 'player.vimeo.com', + 'player.youku.com', + 'music.163.com', + 'www.tudou.com' + ]; + var pattern = new RegExp( SUPPORTED_PLAYERS.join('|') ); + + $iframes.each(function () { + var iframe = this; + var $iframe = $(this); + var oldDimension = getDimension($iframe); + var newDimension; + + if (this.src.search(pattern) > 0) { + + // Calculate the video ratio based on the iframe's w/h dimensions + var videoRatio = getAspectRadio(oldDimension.width, oldDimension.height); + + // Replace the iframe's dimensions and position the iframe absolute + // This is the trick to emulate the video ratio + $iframe.width('100%').height('100%') + .css({ + position: 'absolute', + top: '0', + left: '0' + }); + + + // Wrap the iframe in a new
which uses a dynamically fetched padding-top property + // based on the video's w/h dimensions + var wrap = document.createElement('div'); + wrap.className = 'fluid-vids'; + wrap.style.position = 'relative'; + wrap.style.marginBottom = '20px'; + wrap.style.width = '100%'; + wrap.style.paddingTop = videoRatio + '%'; + // Fix for appear inside tabs tag. + (wrap.style.paddingTop === '') && (wrap.style.paddingTop = '50%'); + + // Add the iframe inside our newly created
+ var iframeParent = iframe.parentNode; + iframeParent.insertBefore(wrap, iframe); + wrap.appendChild(iframe); + + // Additional adjustments for 163 Music + if (this.src.search('music.163.com') > 0) { + newDimension = getDimension($iframe); + var shouldRecalculateAspect = newDimension.width > oldDimension.width || + newDimension.height < oldDimension.height; + + // 163 Music Player has a fixed height, so we need to reset the aspect radio + if (shouldRecalculateAspect) { + wrap.style.paddingTop = getAspectRadio(newDimension.width, oldDimension.height) + '%'; + } + } + } + }); + + function getDimension($element) { + return { + width: $element.width(), + height: $element.height() + }; + } + + function getAspectRadio(width, height) { + return height / width * 100; + } + }, + + /** + * Add `menu-item-active` class name to menu item + * via comparing location.path with menu item's href. + */ + addActiveClassToMenuItem: function () { + var path = window.location.pathname; + path = path === '/' ? path : path.substring(0, path.length - 1); + $('.menu-item a[href^="' + path + '"]:first').parent().addClass('menu-item-active'); + }, + + hasMobileUA: function () { + var nav = window.navigator; + var ua = nav.userAgent; + var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g; + + return pa.test(ua); + }, + + isTablet: function () { + return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA(); + }, + + isMobile: function () { + return window.screen.width < 767 && this.hasMobileUA(); + }, + + isDesktop: function () { + return !this.isTablet() && !this.isMobile(); + }, + + /** + * Escape meta symbols in jQuery selectors. + * + * @param selector + * @returns {string|void|XML|*} + */ + escapeSelector: function (selector) { + return selector.replace(/[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&'); + }, + + displaySidebar: function () { + if (!this.isDesktop() || this.isPisces() || this.isGemini()) { + return; + } + $('.sidebar-toggle').trigger('click'); + }, + + isMist: function () { + return CONFIG.scheme === 'Mist'; + }, + + isPisces: function () { + return CONFIG.scheme === 'Pisces'; + }, + + isGemini: function () { + return CONFIG.scheme === 'Gemini'; + }, + + getScrollbarWidth: function () { + var $div = $('
').addClass('scrollbar-measure').prependTo('body'); + var div = $div[0]; + var scrollbarWidth = div.offsetWidth - div.clientWidth; + + $div.remove(); + + return scrollbarWidth; + }, + + getContentVisibilityHeight: function () { + var docHeight = $('#content').height(), + winHeight = $(window).height(), + contentVisibilityHeight = (docHeight > winHeight) ? (docHeight - winHeight) : ($(document).height() - winHeight); + return contentVisibilityHeight; + }, + + getSidebarb2tHeight: function () { + //var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? document.getElementsByClassName('back-to-top')[0].clientHeight : 0; + var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? $('.back-to-top').height() : 0; + //var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? 24 : 0; + return sidebarb2tHeight; + }, + + getSidebarSchemePadding: function () { + var sidebarNavHeight = ($('.sidebar-nav').css('display') == 'block') ? $('.sidebar-nav').outerHeight(true) : 0, + sidebarInner = $('.sidebar-inner'), + sidebarPadding = sidebarInner.innerWidth() - sidebarInner.width(), + sidebarSchemePadding = this.isPisces() || this.isGemini() ? + ((sidebarPadding * 2) + sidebarNavHeight + (CONFIG.sidebar.offset * 2) + this.getSidebarb2tHeight()) : + ((sidebarPadding * 2) + (sidebarNavHeight / 2)); + return sidebarSchemePadding; + } + + /** + * Affix behaviour for Sidebar. + * + * @returns {Boolean} + */ +// needAffix: function () { +// return this.isPisces() || this.isGemini(); +// } +}; + +$(document).ready(function () { + + initSidebarDimension(); + + /** + * Init Sidebar & TOC inner dimensions on all pages and for all schemes. + * Need for Sidebar/TOC inner scrolling if content taller then viewport. + */ + function initSidebarDimension () { + var updateSidebarHeightTimer; + + $(window).on('resize', function () { + updateSidebarHeightTimer && clearTimeout(updateSidebarHeightTimer); + + updateSidebarHeightTimer = setTimeout(function () { + var sidebarWrapperHeight = document.body.clientHeight - NexT.utils.getSidebarSchemePadding(); + + updateSidebarHeight(sidebarWrapperHeight); + }, 0); + }); + + // Initialize Sidebar & TOC Width. + var scrollbarWidth = NexT.utils.getScrollbarWidth(); + if ($('.sidebar-panel').height() > (document.body.clientHeight - NexT.utils.getSidebarSchemePadding())) { + $('.site-overview').css('width', 'calc(100% + ' + scrollbarWidth + 'px)'); + } + $('.post-toc').css('width', 'calc(100% + ' + scrollbarWidth + 'px)'); + + // Initialize Sidebar & TOC Height. + updateSidebarHeight(document.body.clientHeight - NexT.utils.getSidebarSchemePadding()); + } + + function updateSidebarHeight (height) { + height = height || 'auto'; + $('.site-overview, .post-toc').css('max-height', height); + } + +}); diff --git a/lib/Han/dist/font/han-space.otf b/lib/Han/dist/font/han-space.otf new file mode 100644 index 0000000000000000000000000000000000000000..845b1bc2898c9924eff3e1b0bd92b6178b4908db GIT binary patch literal 1748 zcmZ`)OKclO82)G1kGN``&?+b(sD?I36;R_Oa+~0&hq!4YhC@kkF92qeUb0|z9ojRX?nL_i=e6(>MK1@%-|TK4MP!_r`TlRd z|1q;Wn{1(wr5L?VGWBH6o#SioKJ_9|*d}75+1Zx|D}OKE117YEa$RZA8!Sx}`VRK> zs;X4z?)YuskI+w5L4+T$B=FDZ{nfgCwfhGkihP0LSY5f=pjb{2t{@GiuJ+u_Uq}1} zBDvNun|9}88btmA*14A!sXx2-;Q7nlqxVS;fhPLCbz)Y`KNfHQBxbfL-NGc3BrG)e z$U~hW+F+ZWx#@4fTl0r5BGeIsA+xX9OBALq=>tiFJ>}0#Lmj^gaaas~b`QLNSyT(=hq8Zj`HLVSa4td|q8#t|`_)BAFaWi&IH^^vQrPs#a5ntG}L`<&b&O zs%e)^S3Jo-rHOEPb(R^QKz1MM}-YB33lhu(@q16}7HdOI)#SOD`>Z62r9hvf5107%9{w zl`42@CK(tfz!>N&o^67eCLK(fs$dE<51%PIPkDGP(lXVcwP=76cnV3(X_})+sAhn6 z)hGX>FT&eGW?f=)x3raG!vLQJ92P)iMwOL&(P zl=Av84Z#|L-&q=`BKj0%aT%t8W>Dw;mF5sjMRajW95t(mFKXw=Bu>Z1xrmhvtOi}f zijH%sg6E!38EAW#8Z^GA?;~u(Q`+DaJO$O;iG{sz*fxG5UJ2*C8^wT^u*y(3u{-e% z@Ov@19o}0Hu1FDj1#=9yOK3#fo_%Y-QKk^im8c6p|NC-?c&FpZebm*t-}ybr=SK-5 zcRs{P|4(BSl87-D$8E}(mgT>#f*Emp$UVl~IN=9TJ%*XD$?quU@>g;I6jT-Mi}yWBBPSG}IXU5mz@KjRNS nk3U?Ae;Ieb5q~Z5YemEtoz3At5Wb}HA2{3-L=v8l=-cgIj2C6J literal 0 HcmV?d00001 diff --git a/lib/Han/dist/font/han-space.woff b/lib/Han/dist/font/han-space.woff new file mode 100644 index 0000000000000000000000000000000000000000..6ccc84f8ea3543926a1a1e78b5c50a56c0dc8025 GIT binary patch literal 1452 zcmZvbdown3C; zDMTc#%d#solk4eV249gK88qL!g0HhnV>uu^1?sonu2P(`XL%%S6 zupex9^*BO;EE6(EC>!9L}GI4oum04OcU4ntX^Bzo645JKhv zV|^$?!AcNMsG>Y4TLdyEXaiLJFq*HXsqm@c0_p8 zW!P^K^z4KpZrlN+*b=Y2$4(t?TIuqOySagFF9IA4^Go-XaQEbIb00KI(V`UGbsxct zZpq5aAKLp%XGSX$G;F4mQ^Vh&yk%>X*4k%wnkx|lW= zy+?{C&MsKZdHoq;Au@?dnCdw3!ur6PPq|^mIDwmjBrK|vFCf}W$uwCGHdAC{ zNhWF>s^3Qaoo~=0VD~)64CU8+rZ5|Pc}K5w^!bRxyrb_aiY4RCmA$<4(>`NeUq4~d ztLyI3_)B$vwQ#$lg&{VSC5hAu0Vc}`OKm+W<)1QLuT$)*I2la}(eUw)7o`%U&`PJX zRbn;Lz5a7yJb}%!E@hg`VrmCWi*v6rvzF3sTNYEqv< z0Tx7d07HqgjmSnU;sI(I*lgz5>+-4&=`*R!N@VgqpD$CPSXNr#E$oyQ`0f#<(oW@k zekA2d$6B)1kyK>Ed{SU~gDDmptc!b29wqmx0-biXPn&fZ40bX@7fnOs<`4^bS^O?E zir9}3oMGtF(P)D(m&USe)eDn+^g zFJqMZR(|yI&^{5D6UiA&`v&q~2DaBu(Fy%Klsxa11eLZtB0F!2J8wW)`O3}wdjwlOCr-N zFMNnRJxJ+g8?^u0^2Xd!n6>=*)CRlx=>hJQaI;`Vo#>mm!WGF(es#JfD^V=TagK1V z{AoG#*v+@`tksDqTINo%-`}nQ&iy3gy|+EC4)8K&TBfyn8V8DEa*IX-qocZ<4DMR| z+u>Qv>&Dci7x`k^_0BZ;%}mu@7VJM8WAK~Pm|y)w3vgW<(faO<3-a1okkh;-;7vk#y KIOv6tgZ}`kqe+wi literal 0 HcmV?d00001 diff --git a/lib/Han/dist/font/han.otf b/lib/Han/dist/font/han.otf new file mode 100644 index 0000000000000000000000000000000000000000..2ce2f46ca183a46f1b06517b1a2a8f96bcaab1b8 GIT binary patch literal 26416 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

{jkyM4+x$u7WP$0Wudi^7_mDKDqxGOv> zh!_C?{RQOAs*Ywvh$9GiMcLO9G#O=@qHr-#7ZuZhMxe=%b?rWng{2RW6N?G;mjt&& zHbgfhwzpI>W&3|y{gLX6Gr`0FOOx;N`{Q2|kbaWp6zF_OZB+;dn*PN&aFreQsHF{u z)rOm3RRD}FOb#rQ;*3MlrIqDMi{{Kp-^S>6TB~f}+FRmAwUI|qNX7Aukxuq_^3R~~ zwlb74S2*=GN;}o3Y)!n954U0-4V^zY)H;wn*woP!`Dq?OAYjV`*`PQiE|yuSGpM)e!$>lI$a9i^x%?UYD2Ety}uY zo#~&aClBR0kVXzEV@*B#2GxUS5B#RHGsD0NDk(>-gfCdlr-CGar$ai zMh6$^4-D~VS&-M8V#->sH+UpC6q>|U`%N`U+U_4=*-q;6Ww#NU%ac8XqA+`kz|d<; z)Bln~Qxk%OGKuS-1ka+xMs$6=v4F1*8IOiw$jVoOSgkXnx{9jI=EkU*eOSJFxnrF&MXDX4!opVOnn7YlOHSVvM#-?EX6Iu3;* z1>`O$G!Cec53PyoaON@!I=e*$9SZ$|y`p{rEqHqIQ_~kS4!6hURyK^IP(z@jdn~DZ zZqv*iD=OAi7Pk`Wvbq}FK@Zzp9$Ge2N!2?>gGu~u1e2($i?``Gwo&V+6Z(e@=`St_ z&wWHEKpzD99akxC{I6Wj`SD8np4|C2u3X9y-W>bmm3cR=jHA+1-w3aMku_`)Ou2Ow z`GRH+5grO9j;m1Oz;7lAy%<$PCOkzU-q8+^uTbqV`#^K2kq%Xy&~HyeJcH&Y2$dea zE|lTsdo`pjF!D8_<6746V+hdFN^LfZbOODh$0Ww6S3@`GP)w`Hl5N$YxxjzDiMxC zL}e}wwKjC@QYLEKeLZS#N9~6~o)T9iqnz>?gi}OK6=;M|)G~Y!>BOpKt9oeXT`#2x zOY8jMx&{@`?W)r#Gn7j;jY3c$CH?j^y|vPCWIEAAl__Y4K;~`~52%UNLQK5wq=dQ+e6uO+Dp@`Y94uDI~5AYH7-HURd{z zaMuTDeIU#`C;aLnWjheW?(HX3oc#iHMluQo)Ib~_T3A@RSyz^P>ga-tvwpLNLgJ~q znm3CQPc&ClfDx|3&Oqx0Iw{l|tptBhr-$@azNj3NLnGjUM_4Md4%C#11{!Jlnc>9m zq-RcNow>0smo6G{)CFd3N|>uBR8@&Q)kx}&MoVK_-o&`~6M6>bkRl(MiN@0is1vd8 z2pvsU

FJW;hXg_T+VIb^sCs)mY6^h{7N(+OTwCvL@Zz_4fBMnvmT^MoM5~pyz(b{EK4|YV@z(knFh(p?@!R9Ssj>DLK7Vk$q@IO3?3G|hoyAF zmp4AmOupZx5zxz#t1FN=p^+gBH?y`k55Hw$p_F>8HfJ^cee~P}>UG0ZG+|DO^3~Gs zffW#u$5SGtcxr=)tiMQGA(1^(g!Dbh10u#sq_>Mw-FU443Z>cT6Y(JWXJjy2d>zvR zslyqPMbhOG<5?Ffy#5+! zL>nsEf5#Iv{`<}`(AE}@Rs4rBSB|3K|7iczzX|>SAH=c!5kNfdR-g_D0Ox={;4@$= zP>pkv13ZLwJrG1&iq~F1+_oL?08!u&-~enu86b|M0p16?fwzG$Py>jc*#f)>90tVW zF#$!uw$Z;awv<6T36zXN9AAX^8F7Eh0dafrd+Y$jeeNCoTig!gOIMDWA(;kD9rLi{ zc|eKxSB|+;axXATdb?!pn0(2sG211D|Gb^pCz%S3bzGAS@Y&25qvMI!2-kao2(dlp zd&$FDzm(i@b8I!9fbzeLd$a$ReM5eo01e`?ARsAf`>UbTE?xBG(&Z9H{3(b}N6GX^ z%S*OyzdCamKo#8~apU-!B$y47efUrQ E2Tw{-o0zK2^O2nx$HD*Ub4 zfAi39thQEk1SlvoFn?>$-(2b&1}68G4#qZy-yS*9@7&iQAV1harD>`FriLaUAPTJC z9)@qQLxA;u0lvj=?OXq^9Hg+EP~8BVKW^Vz(D%OI`N7)nl(>>@4Bftur?~pKXTYFO|aAUG_`|IlsC4EEHdBthQecI`=3;h=z2}u;u-*fjD zwgxM*_&s3@cN;mJxNPx4KI|_st#>SBUot))9kd><{kk(zV~HYN5^Z*Ze!Cu>6t>31 znhvtF9DSApx)omM0Xhqdo>o(pji{DFxvrvCw?A4vil~wk@~9=7@h+b0d={t+#YKGC zkJjt$jp?k;=`>YjHbfVI^$d%zq{pYmn)#5wxPLeP!VJP2@z@3vC z5oYjndCR|!-{Klc2KM5s7U(}bOh+>Y3e;)<9eeqnTEjOzf*5vDDimS|#(yFN}O zL402RH>pg>7<+x_IYmnX&zL~GU92zh7th`B<;EZH_PTZI+rDZLROLtRe7vX<<>x{igl@?fBiIIv5O*MOk_cNTn3kPNUEyB zs+1~`{QwRrp;Hab*qfRmK3R8eObV5pKUJ1oF3t!&?m#_dQM$H&%zkQHPyJ9G!`C&~ z*?usou@Y1v`Je+g%I#KM=TJt1zp~n_X##mN7UeN}|FyD% zF_l}D|6F2n`Tu_&%v?#lsnWkM|#==y-EE4;B#tn@Id1WZq9FLTkD+ot6$4DHP0dGY zvZ1%=O>9jq^prJJv=u`sEj|)4)!ouEG9H=4#&VRX}dj9Pi(U zD7A;?kp7H%!|f2lDU(SujcSL4`IjRsVo*C(@gvPG)Geh|v{T|8^M>Cc?HTR{^hSM8 zWRGOecn@{YZg4wHA#CRFOqef{K9W9)KJu5`dxD^7rX-R+Dle0$m&iNf%~xSb5$egw zN&bE0A7(xlJ{tT&{RDnk<)*Xw8ig789^wzo6OQ5-`5CDmD&0(e@{g1gj*>6xkCl@i zsUEVAmelFP;_dX$j{A*yJB1#SkEavY;ux78nvctS&-p949)^$4d#L%55(UYe)MibC z*t-RcLON+r{tIXaiaOYcGuj(S+vnzciiL;;x_ln7eNmLTut+BCm(;vEY;6`j;u(4Ej!w=?c{^ zZ)`dGcZa2wN->et_)E;ESypykE@MsIRtkEq1J15GPtVSn)pnEJY;Rq-kUn#6`;&7V zG5k6EJr;Ftuc`VUj{Q?79E2ZB$2~8HbgrK+539j#xOQDbo5EFRm-(7R9m9IvDK06w@L%|hKy1168pgF9c%+pXV@ zC#gj*GF38A$)64<(+=rp2aJ6WqyNo#&(V#uL#D9i8tXho~tvI-mWHFOHTk z%ZGMVqdE>7;U(&o`ny_nHbW=q`X9vl5c=U?7i3^xz9TaEt#d~C3a+SS0*RI! z3K!pK>M!c>FJgL>g2o|==j}*c1k978mB7cggFGO=oxl%F$kXgmy3~vzf%_fd?AEhE zmaj=ZP{fv>=ZIBXi>)irS_-kwl_7yVp!&^%yfw{NO{m%}B+Ra{gbA8W?QS2i(Ds4H zbCJrxxQ4;F9+GE&%4#9%x`8X`K1xBvsrSU0L%XbD{Q-relCG(U*}=M!ow`TNmVo#D zgY@`cQ`FCtIv%}#)u2G}LW~}84hEN-A3KKRQKNiNGdmd1#i_Io4DA;85Pw*5UX-TE zR5DA^?5Z#5skQXAwdOTjoX?jptNs6FE<|msgztktA^tQm%~{Tjh~bgIdqH#dq7E1E z?>FLiRoyxmtB}v#&i{uAwe9PB#qw@9f?&Ef^j?ti%=!nh{j5h3GChPKD`5V)?9V$F{IR zB_q@gc_X15$)55otCE+YS9fbY3jMZ5Tiq?lzrVgapzqH6jvAUU^;I|6K-Im&XOp>! z8;+ryvG*UX8NLu_}eQn)p{-~pytJY{)F#n4)upax15=PrPS=^wE#D=;+MHhn9 zy-=^bS}l=koRv9H)0|cN93eI<$}G=Glo!qheTY!JVNhFFo5Pj|(!w=3^xFr zK4t*i0$A5ph0w5Iumluxo@x&Wo*>LoV7L0CmC#LF)8*KLYf>Nvpz-e(#S{fjJfj+Q zj4w@=dWFlMOj};B@WwBMKG1R3DoBU?tQ-QT0{-F5v>lf|!>W)uRu=1P=R0_v&ex^U zTXNLuNgvx#(?*!6i=W@ZA8ij5Y-owl^O4HTY1*741fGESgRCv!uEM(tGu*FI-8~k| zsp>9m7WsogjXs%WF?P4&9SN~Cu2u~PPpH#1`l>HYY>1VP(`>~x&DMG4+_Y{&Y_^I7 zG7xYIfnP;Wf_Z8>soJRODz~-u+J=w={La9N_*98jWgjJ9Gg@F&lk%-RVeTtz^1Xpt zOw=b?0Y@0OP9-L0cfkPPQy8PUoFOl%B{H8&cGjogw@MZi{=>FNd3B{K@7Ey$JxedZ zjinggc77Q*wc51RP-**Mh}QloFDWy257s&`RisjE)xUcQs8M?wkrzRHwGtb|5o1Ou z&5+Bz4_ertb5hqUINPtO@Pj5<+?h}=&qho{Dwz>xNF{Ud$nA(#s6Ffg%iLdsJ7L9= zdRq$=IHnXw45M%pW7a+{6|_u8SpHrTqCk@v&YOwKSn^!F2^yBAa7t z!!cxNDEpxXZ^(><;Bn?8A7{>kV`1Zjq1iS2L`&LFBt%CD?%KkK#3nHdO{N^UxyDEDC3d+5Mojn{;h6 zs*DLF2G)06G%^)FuysUn)?7$$S@UNxaF)IA1E)Dh!-niKNcB=IRx(&|n)E8zY4UJR z@gOlrHB0QNEYRUh@XBt>|G{b?ST!-132*6?(1G%>Y#PSo6gz0EfH=;Yn^(5B*K(P_ zp8{BH@~V`)NBjHuS1;^rz;`8cBhVMMgQ@F!q$4m%BXqZRdcaaTChU`cLN1xa7Jaq0+*=KIx16Gwkk@UHEtC{R9u=i_I+&951F z`uxe)5B|BuCpu!Q1m#Wch*41(^zJonI< z#*gow-w(Xu#Q8rNaw6O=R;-3vqET$+LgTX-uEdG2QV)ECRg=VmyHq!t?SPxj zO&-QVwvQ}8DOuH{yc0LUD+>CEt(Y2&yE~MWxz0*E`a56LZFXuV25{ej7G~erm$MVj zr)!Z+*@3owSxAgwG9Nt*0Xz855JEGu2{{FC@h>)qu%1Zz?LSnz=o-3us={hZrw@@F*@L z{Ft?2+G`MEeXVc`H182EfD!{p7zJxQs?x$GMM{y+M73Ehay-23bx0hV{|gjwjS((* z+1AZERKDpd*z+BGrk{wV0czVnu~uO4;2wv;CRhr#ZFc5Q7bFl|ilgCqWf z=xJ8|eyv3pe^6+)ZFREC-D}z4C}xb#&VHu?KM*jTOZoYZ-sb%(;{Q_zd9_3!F_HSy zKeb2C<&RFevijwI_0~fsylzgI!8-$qSQ#Oew0D0v9HNg4QSw6+8%gu~0`F%#ZC=6n zs$OD~g5;Ms({07j6Z9|M>xEyF89_tlC?3@J3uh`k+tP zVduLtM*{m~yNSVlOX9af$-MR9Cp3{h>Q*hP; z8uYPIjP{|1y6WC;OdfANhw93m+@DN!i#R8Y0L#}JmxNv%T*;b&z~nx>$%qc7P_0h>Dti z@-X^svg*NY=k2uP`&Dyw+MSV*y0503`k>5~(7ubC6%9k{ha+W$G2r#upco-r z2H~h$p|0uRTPE*k#x?T%_(~^;!7c#X;7c+HO9{a5JQyJ->1T3HZ@B8jlt0 zbb0Zmu^^5>YJNi}fCV>Pbl8v7J(b?T6e8j9VXx~Y^}f2~9WzuckEv31{{|GOe{wPg zw(V{izvE2GJbq59f1wP-pE_MOUC9l{Xu#D(ZU-$vlW087#c?$Z!zg#$+$WXcJ4OP( zh_XWym3tM@^%)!W-c@P8IzS5>soDJOYz}yKui|~J2nDDgGo3oVbZN%cXusqmLXxj= zm8g8VvIA)c1Zq0?S_c3P199>+4Kc6+)4sZ%C zE7r=k?4tb45*C;TDiLN`pv5CHv`xS7OOaK?Bw|q8nx=!bVSk?#GoUMr6>6YYrEm)M zyJx-tJ(;%wRsVVsw?Ij6FFG&!$q6IR$<3a;qL~=8x;(85nBBf~`p_SbSad_BnY2Pg z>-(ZB)K243jIX-^H|zX?IRMU?TQbQhpESB$JuxvuqoLM#^q7)!Sw5tcwS-UwqSb%; z8DitCeF}NHT%}j{+LjcdUR_D6!ipPZ_LrsgoB*18XY2YzF2yCA@AlgRl@O}ExXbo9)sgekUxc=DKgB8=V>cWb+Q$Lu)sZ}F5jZQ z4Im$3DRhZ5dw7>dqKV0Xh!d+D??vXG%?$TEKnS1gPExla4+(Y4v|GJrIKTNM4_lD0 z3VONjcTMte`JSEeqoX+=kb}K({H>!3{V;LTH@h<&J zdAlatMhhlN@l27^e_CpsL|Q!1c>&-j4ah6y(e3nFm^sFpn5CluiO619B?_v z5;&8y(i1`N6(V=W8C_e)cIrqK+%=Y4nxoG7@dD#mrWc$v16>GW=<7l#8h#mI}&M z^We0x%EuqmXF7-tOM0g++uw7iR=HGxhXGrmX>M--zS{FpH1IZ!=W9MNQ41cjY*&pK zh)6;@Ztb118G2QMD439fXE=yUvq9bJDBfQtVh?ip zuk-5Mitt>5OM4crd}iqHlY)HzwW> zGGW*f{b=B)1w2J;m^B_JvwdM#%EJ$h^-!ph^1$yK?O?h_U!~FrF0MXd)G(Q4s3Vg} zR&)hw#UOWsR93}gek4780mm0J@i3tE9Eq@ zc5K*B^@m-Ne+2D>j67@Ef~h|}y)@>#j|6qGb|xF8XGp6WVUH>gRs79{DQSb9-4R2&&tH^t^{>H%{n<`~&j_L7N33#csH>jL28`bGuX_WU z=$x6-YOr0MtIWzx>e~Mm4#@Wfkiq0ez=(Gg@Y&#Uv%A<@Tfi_H4o~b=vy=-u%Q(vf zGEhaIGh9k~G2!r~xn0k-I4&Dbq=(Q=luid9t~b>ejhjCBmTDRQ)^^q)HPa#lAe4H$ zS=$1gLOY3uyXS8$)Cb<{*D46#1`^Sok{MV|>BJ4!rVU*U)rS;`2?bkpUMbp-!O9_5 zm;AO^@(8f%J)0CEj{#oKYhA98(e@msl+VU98n+Pwi||8l}J- zTb7H=J$tfH$IRQ8s~9Z(5#I z!(=Mw90mf2XnkfikTNz#MhyN3p>a30l>EeX2;db`R!N#PIN5X_wYE|pTAFQ@qS{ds zrLd~jI(3Hb+wW5>AnM4aBk6`_mHdVnl;JjO2ksAT3w}MwQDnHr?a0}a85OY}*VHk2LnMuqvlVu-d5 z29Ve4_Vc;n%$`-WE~@Z{FLfA!-OGTLyajE3SRERx642V zREdyOmcK8|$0CVwt*X||T2pOO_OZdHnevq5+*}-^F9yojKc4ak7x2i`oT|is>nrN7 zQ7WG{YBhj*GSL+os`}YHVrv2eea!t%pYRP78hniq-J6YiAI>fuTSYq*Us(Ze?PgMA zS{8DF(jgaJVe!ugiNj>HM7TD%XB}tSwRcdF(xqiTo1O4=Q?_{`ZGnP_T)$Le53#1{ zcF0!n%KO~bem#}tLEu#4H1sQ4(bw|ZdJNafmNej;2YA+!M~z(M*EPCGc=Y3qa-Es} zgC~dYJJQe{@$B|h1ZuUyJZ^Aoa90Z9l4)>+$b)Fw2;$g6b1(^ifED^>BATu{F2J#W z8sZC7V+X!5JJqSeJ}4f~a`~^BI;_{)@B4ZN3i4frqa4>hSnR_8^p)WAb>+*cP9xj- zbxG6M5gE`*NmUuLuj;z=57yGQ%B#{kN$4r7>mm5693n45f-}9EQ4%C zT?%q#)(i6B*q@>vLv^yutuSA0!(`sL+8F-vAm5*Mbq~5tWBlqC7Tv%aE>Y~15m5?d z^BM5xeDeG`C{x48S~$woYBmiw$!eLO>y1Cw$^OxMhJmtD2{#H%s`7fgzg-voO>ed5 zd3Nsu?ui#QKDdz>IzXqD2J_L%6PKH&neUzZt768=Xv)x7m?nQ)AOR@oS<#*e@`BMs$yO05S&+H|6& z@pJebSRXWa4CCns=!29EA9FP@bjBn1p>6jxZJVP;m?((QtT>RRMyg#3~Rv({k)9Q z-5S-+;!IL~tI}FlgZdaVR{n?mZnBGL@z5D~*E3bGvR{+u+MhvaV zF&ml!R4jZ_aYR&cL^VuhSGGn#o&4cwM;GbqA&3W2fSJg(Ixcq8b6uRXP`GxN{>6&N z^}&kvDE9)&9mx^5XHnBCd?o<@CHgBS8`H3*z#IG@G=fQAir1Sq!aT98FaWp!iSr9q zO^OZf5}C&Z;EHdj=@aag3IVQ>=M55N}UX z(68)2k)AhI^uP+%9Dh{fdOZYD?6uDwIA+-lr|l(PLuL}G`PAER9c!Er0YM)VILvo_ z>J5uA>Mc!u;zIcB0gEi~luj9!1#KL`Lg|zhb()?Y&*jR`?EbzFo217uD2b2(7qVMC z+t(ezkj?Y)3PrSx59{3+AR7(Iffj0m#QTHr-C3Z0EhhV!@)aUN@-((!BBDrojZba;bV#WbR}gA*_`<}s zJ_~-LR;u#p^h3^2Ok)bGu6~Dd14QMO%i|NX9cprBtU8O@Iv17Zi_gfj3uem!L3TK~yQpsh?6Q-|2br);|AJt`#FD%n27n za^f_UKjEt;{qPX45|;l(^9mgx@epo<%nil1usv0+I1mEFX zzhp=1T#wbVQr{7JFCv6K;$;R`ijCEcPRMES5kb^dDDliA^b#zUPAEP^`=kM{J@3Ni z%LTgqV|=M?J#WdRNS=ytVgw0AL`DL+&2V$L=tF@8HitNPhxP1dD>&A>7FmYcrCw)C zqb`V6<0bF{T~+IE5Yfn3>H3AsD~wh8vqc=WPv)TLKt$;@g%KYwmr z>^ENJ1nc?oNIHi|=}F4>K!+nhzr1H>vE5m6W_{0s)QaWCgk{PZ9)d7h-miMGg#X*u zaWWd*u8Uw;MDHVvQZG0Vx=NE`W@;i}#!y@9qxF-@CcAuI%c2I2GFS`*Pm9p{()usJ z8;t*T6WPP_x@qSSq_#}*LU~=a{lP41iRir46TUKeiOEb0S}zk>J=C1XI}uXir0O!6 zrh?wmIm%=+9xCVi&zn~g>>GnqI2g#E53|J2vG3ANbjo`dXM@57+Yi&sNsaN2<3L5)s3Vyr)l+z+SqO&3jJGF@7!3}6f+av&OHf_FHd823hE&7cSFh3+AGtLO zH5B=ajK&OW?AiH)Ig_7iMVysP37qCTDPg^*Yq{rZr4*pwYh~Efjkcg$8I~VE;0bD zbIuze^Ac^`jWZ>>z#}vGc7A0&PJJ$u7Fm^@RazyyJq*woi~hR_p)?#qE%)cnQmUS= z(brI!tO070oqkErwW0a2iCyXHDuIVz{JuI7E~^_ zuJpaaWtx~~0T^jDuo!|ql^=#@9tL_s!swf5u_f00rfSxSD4fCIp63`7^0+8M_h^1y zTgwIp$Gs5}2t8|UO;t_7uuOUagO4RqQam`ua{T`33?6(0{QD!%<6Zv6Jq*H6L;FTe zb4HROk96S#Wjkt2PWBL`AG2csaI^=;-L2)!7lxyZPA%auzEUS2LYDy`7fk!TN?PZO?vA0V~}_s$QG zE)TncE8tx&IZ;h+bt9L7__+Tz{kV6$Hhr)gJ4RXVsb*j>f%S1hJjR%*0Or&Yy+S5Z zL>dR#QhyxT38trnjOC2J+4w_~1XV+I10cYWQpYT1&9I;3b2II^Qn{-{qF@IX(4STk zbLEK{m=ZLTK z^VpSkt>5oI)rw)Zjrf5&J-s8m||EV;kVG@nFY#UhW51eyYrj!A|bl zI?GZMjqR#>V-^t#;8>MCQfhZN?$AyJ8eSgefb(T96mcJm{l;ASL^Q8DliP_F&9FFm zPgd)RHsz(nl(hMr-kh>;tpgl88xx#4uGJ1nZR1@-*4+XEDhv5|rM$&na4}CYTUo@j zLypAoQN(1S&iK-FO(9}a6_I521UmZ(jfgSdi; zgj=MDo*GkNT&{lhNaW4!AbrGJwdB6Kj6;IC#1}v`8p3D(s@z*-C1>T*$F8HNoNNlA zRu0C-I%kyqjain$Ym<5kHwsjTB2$71DK^spqq0$)W}m0gS@5fv*E}o*<;_x9-*&+- zaRpg{L+TRd6_^XB5*N;Qu*$bB4=IIz#kpXZ^% z4WZG;c)~JA5$oQJlqJ&f!M-Dlg02V|~PmU#ooCTJYPz02RET4i$dl7s;G<`E3qV7ug;#OS)`7;NZ$KZ9Bsngsr!dx z^D@j$!w+1~->^QtwopXj%mV+x9jJRH6{(n+)#RYI9*w_c@KeU^Ra}D*P zFxXj|y{Hkz&ZCAFXmGG`BGQ+T+AeAzX&P%)wCT?6LQb0R;|qs+PseNgeT+Tf zfetMJ?NqzB@N=O#x{g@Nu#!GRJ)!&`lv2{qHcGt&H-a<&B{Mdf2%40d%pOM{%{&z< z^^+jw@F>pk1#nhl40l`skLgL+GI8^H!$Q_)6(&u1sP0u|q2=$|kTx0|W^N2Wu?Smb zq8j6aj~pe$CqF|dmnv3>>L!~u%^Ms?^(k^PB%T`d)b4n9u%CbcY6s(Z3Y_tqf^T&F zXIQoOhl($w_!}6Q*op*NRrs=*TEyH?z&;g`b_T~G`=@yCw&P#cR=U-BV$73^?VhiL zV4`GVfBazIMbH-?|QTLgUX3 z5mAoWPN^juYnk$lgnXAlgl70*`9YUA?w~v0exu;}wo+T6&HmFN^a+2VDpl4A%%@6Q zs$J;-wno`p>g5COh$$=Yw#%jdm6X8xCb2Wv+m!0`j@MnxNb-m!1bj6L7bK%6n>0w8yQ7~VAkRBWCVTSPx;zfxt0-Dck(u+&( zO;b@%YYlh%TIu?KSLDFCo_lx?Jk}D5zDL%sjR0C-B|jKh`ip1}PfIecl(q`etMv`Q zxjh}1hL0MRmgO~5>a~B$nKkJ_-Vzc;$*q$o374?L$ux2L?IvcU{>H3)SHZ6q&U zVM0K2hb)GfnzqUUm}BpVb#gr#Uu}D5sz|M-exH|T)K#c;gg6tvMf}}4iL`cH4n$j> z#y_Ta9BSeJdZW11u4DAIF|C@jX4>B(EfVchyGlkH#pe9}3S*u9e&VP@2r9|5@b%hE{4pKs6kd9QiXzqjcuH)^w37$`z zxYFUF^HtmR>e4FsP2J=H@3CHpsp4mE43a?(xJ&rxBl)-SS@X;H$s5QE_N4Xt;L|W8 zj>wQ=)M`~`I~3VPU_~_yU$NMzb{F)@{{EKg5RC7sR&pI4G$FzSqIBb4xWZ6L|D&_9 zHWOvBQQDIc>X8Em{u#^P#V4ef3VA*Jt(+a!o2`RQmPGfAK4@as?eKa0h7A#IMypAb zm<3?(XW*j6WPI1pxPbh!7svve>| zR`xh$JY)bBDW`4i*I%c%OSxLk3fj>)ny_y%?p>1z4TA8&NZ{`YNZ22hi5LJcb0~7j zD*F+DQVBgCOKrUgqVF$xAY`n0UrQn`mR6hM3>T4VD5ru0u|#*~%}hKC|G5$f=m)x@ z9@p}n&I%09^n*!{r{Zgs;DdE!Es9jE<$)v6oG8AeYnjm1*U}R)!kEu)4&S@G@O0ni zpS=TH`X;)CEF?lErLIs>ZYUVlIQVe{?6*+I-P zY)6GfZ~*r|qPRpM6DLbHmPJ~&qooHg%0p|mwDEWJ*+eBn^nYDCxDUK*sRS^-t{L^4?N1jP3@)4rEIJ$9eQxqF#df*9 zj)pHN=w>#0JC`J){~?2P1zW9IRaxe^ct0<2=kY|a=}`+JkLxmN1{@>S}Xs-$CcD zUTObQ+-Yf$#o*3CJhNrRqW}Yi+AM-_d2=5x$39QuatMiiB zVUd=%cUK>X<>|O(xTPQZTV-9eEJjEfdvjFOOM%Yl3W{|M(;rH-Gr$FOQ+-_Z<^@AD z!JuIqcncVBn$qeK(8apMLstnpGfhXZU^+PIRaNfp=E_nms$Q$A|I}b+aUg#7T`ywO zY(V_)K28j>&cT9IbKDY)p6bqYBu#I~z;{y{U)<-afAfOPm4oNnPiqhZf{cH|N};D( z%lkW+>Z^dXnqMne5mo3&jXl!y_}P?0FIrTQfZn*^HRb{%2IiXIp2VvO?&IxiMA4)u1QC9+q1_&>LJ3Y7Lp+aK(sd3W9P&I3nuOgc#Z@+=?!tV>m? zUsdG9YhMuNtgMxc%w3mMOQ+5>mDSboo_xTDHtmZ!c>=Rz&iF^Sin%Kvlp(^_#}H5g z^ucnjl~0)nEfB{q`KdN|+G$oYW{8AfJL+Ki9>nY;4)GCo=pr~qIC(m8FNmLISXE~0m;{nz}OjAlqZ0YquE>}tl zR+0lFkbY^pv%~%R`tfeA`+B)|dRA|nc*UW0drgK(W%dv~*(q>JCkoNk9k(IJeF^HH zWxPeQ3+qeb zJpily*&@nB-n!W^t7uOA^qf2c{cH7ymepY9BIG;yG;lsv@oKhxv360-wwl>MEl;aV z<6moecd41Fje{;eAl~X{;U+>|H%P?7(Qx^RAARTsC>ZNn-$Ub*-Q~sEKa`ENb`D2U zw{hRWH6HdGv0|K7cYW!iQ4j@rBT!_4nn25RMrMiwwg((W7DMmjEW9AIU#X< zLAJtQ{lpHTJh-S(VfN?oTpWw&d)1veejFc9AFkzmDnSniWk3)EL{Y~Q8F}s*LSftAfoMTiVoQWWrhF8%yESx7c^ur#j)>C1cED)c}SqJR=F&jjy|8d@%CdLCG+`Rod z+q?N18M+B>=CvYNX1wJb?;AY+m*zAwyICT7^E49?swzcSic(ryMnzL~a)Jw|DHN$g z8<6y~GXl&=1E4Ro9${@oOJgk`F#$||F-hEMG@-hw8fg8!TVqoL74Xhz-KXY>8U1zx zuc7NwQdOxL~qEdhQgE zbjv>qzMaseAOxKEqXLTuexy_4?Fe=-TD=4Hvv;xhm2P}5;U#!Ka06HE9yZ?XQO|E& z8(kaUJg-+$sfwedTB>bmdbu+hk}$xG?+$vtC_>qMUICJ=`MwZ9PnU)F?hNTA0%;7Q|yK&)Kyh?EIctp>f>)U+WR`mh z5xNLD13v>*TEKB<6hs#cOgKN3=U6pL$bK%1XY|3sKl0KkpvOjk(91H$SGa%fF7YZP z)QY}-Eb*@Lwx#vJ#0%6NtX-ZxU6_I_cBsV)^qi(?-T&y%KVBuI_mCEuN}(GQQ+Sh^ zmC%FMf%WS3H-@MBd-xOxTp5TLf{Q80h>k#!(R2tdsKy<@IlJ|Svf_ugs!A{Xdq|$7 z1zzcr&}Ru1Wj1fLvTKk054}gBbHgdp!XZu|vF6BK`66cAbt0}>?yK30Q1wn>5gKLd z##gu>zk8~C^D8-M!&|2peJC-|(#g4x|CQu(y)Uf-7MCf&7#cGJig7ii^HL!7y8rMNGtZ`E(GV3MriV^jlr=WK`% z6*`B1i{P#TZCyvwPuaq7FMeGC@)3|%-z~MQqSRGwFqIzuOVjlEWRqn^9^6yimf^~d zx9biV!9Z}bIy;YU3)kc}5bc-6;`wu$yjf{m5l)Svl7Xiy=l^3B>#(4R!ZuLU zU|}0+REoV58j7>9k(mhP>wEY1;DXnqUc6ys6mi*o+sY|J5FeHD2Nk%Pe+!Z9#Uyq2`q~<+SW$|t>tyl1>K@FMViKts&W(L zEk3g^C0=Fxr)Xp4{HC8NH690eHA7wV8ortY@=+mKz1<7ICX{rj&BFj{0RxhN??k;m zBxmDD_QEkBXZuLjX81cq$^CZ#BZ1yy;hHVvsZR;cR>0BL&y})0!dou3X%#TXK_ocnnG0l^uUX{Zp&qXm258Xfl|)n2XxZD$>BzMQC|bcwf?r=(?CtcOV}Z zj56U9wj0(}!^i{2D!TUd5aXPpG=kNLlMzp1uRwI~BOzb(MSsw2o!mN{l;`xz&`l|v zi}(k1sf!ghjp_ZVX6>6^-HN<%Fd9Je?Fk?$w83qd0=^)uxV5NZMXTBP^`bk$@fAUq z`lUg)t@nD=fSLU{cSy{^-VpUQK3Uz_>5qmv`zjEzXSEMCm^x1%f++ZD_~yO*&|9Av zG}Rd2;jzFc8*Gm*Wz*PgB9cH94l>Hcl|pDh%ZKTfcKMc;!twjc*3b9hR{V7H=XH(V zmSk54=x+wQ9ZnfHE1>VkqZDRG0#Coc^&4RhKi~0;)5lE#rg*{V7{m7KNF1K)KXd_7 zpD^+b9U;sM5PJNv(J~JWa1+f(Ee&{e|07!K$t47cSu?IHgqckZ+IKF3^?${-EQ9C0 zNhfljH|Yeeb9HN=j>AtL!m^e_%Dd{%8N80rj@e5>MNNIsmQ#gG^p)=5%4X3Uk;cBf z{NL|KEVt|*1kV%(ghYm`bC*DL@}XUqi^AqPaFBK|sJy@+A=O%Px7DtVH7ITTN|02& zDt(;aj5syf{SVL)6x6FGaX19AcPs4Iu8S!+>R3l-(ihALnsC;dNTH#LZ8GcSG41{f zHz+dPQ&`RCe$4Te;|&BDpq?1f9JqKmeqBNFtkcZh28sTeI{O}3vQ;Y8H7V*Ajb;T) zmJK+%7&Uy>Fl!>yQPJ|%MR1Le5bY<;qwd>EBQCurLOmoQ67?%BOO-)= z2r%pTy+=e)o!Y=E0^cxmDg>=(oeg6%)!W1p1ZsdqF1PbIDrtEl zu$DaSLcq`96~nkU96**~uqdnU>AyOFM45WAAxu^te>k+HYM3HmsB5q9kP5M)aDu_< zsF61`s|>(uO}Y!4Bl-7*SZ9P9i1b8SBcQ9TrHky?+`czP|Wc5woe_bi|K_Uv5wUCHW<0TJ-jzGVt@Pp z)^ULT@QKJrG1WZPvx?;)`{EIF(`!Wh)0fdrII1c_Vez*RE_fpW!b_9h=*k+B;Eclj z0&%ailWS{l69ByL3V`Qh0B=af^+icijY&B)In2P83GF!aH@KU;N=^-0ivR6BW?>8K z-`!*8%$zo;OYJfLU)e+FG(qTr^mlvc5p`!>zpz`eg%4~T{jE#L*&VmWC1Slt&luR} z|5Ulp*lCOz{CFm)CRZIsT9s8}Hdrl2h*7}o)d#hq=BO-I*HhM4v~}%jeX*g&P-v*P z=zj*LA9GDHjQ#oA+zu3^tIyCbtjPB)jafki1+%3b7d4!u%dz*q%PAM#>jU-LL zwt>u%3vTKc5Uqlo!{=nqU+tluhm+xL=9&66VAw$!JW5W&NqlOL;Gf!=ecbCFMB31J z`yuB>Hw#8Os2}(z3wYW{(oC$E;d%zzTBB6ZOAGH035+!+k15z@Vb4!*Yy!HH#)7KV zigIObvp^7qgUcX$*C90bDEd|31|d*E3r-^P^r_3!-bBm(ob$oKxI6x|*r6s}`PLe} zVzs5Dv945BWGwO)yK6#qJ$3u4U(z1*Zt-=7f^DF?i|doeER@ly8$N`ls!_<~+6q;f zx42gwGz4q`U~X}=Fi|Gz^ZO{Ti*|Y(l*5HV7qq*{KG@OY?sn~QY;*J-^X;a3z7yFp z;jq2PJmWi=jd&q5jD5N{>C=B!X1I)5s{=WoJ<+5r)330CCyns$|H*N;9Q4G`o_HIM zc}5)P16QBtqwXZ1(B`TCpe`~nyjwKS!p0av%c zBXV?TDrU=AM_nl$-4r|=ybuD}F&R9odVh5Mh2uE`i3#y<#MaDdjdx;?E1zImgYg+y zvs}_v&2}s3$sB$yu{>htZ?W+=IC&9Nne1vz*(3Hau%VR|XEP3p{cTdvziw$ukCUdjJk&DqB^amI1HrIg<8 zkR@Vq(F!hc&SW}uzA^!)lF1^QFVjU};tN_3utty~SW4|1ln3DoeLWD%o8v`55f?Cu z3`cY*ZoX|uHZ@h}OmH!o+jHeW_dJJ)TcXL9{V~(rwfFpfz27}|zuxnG`rPv|XV|n1 z>I2qQ*LNJdP=B-HAzfBzFEkUyC1POKJG097qQ(Swd9}G}gW8yxo1Pn{2~{mp66pr9 zDX)MI0!76PQ^8c&9YwUoLYr}0sY6LUa_am}e89Gk{i)+Kb_}=XD}NV^L$#hw`ggoX z!qowDNPl`R>34z+M!nqEFNxT6WSvL)$$PK#UAumrUkh)0+@jl_NzxFHyH7IY=H8Kp zdEC7cc*c9k9qMuW47mquUT7Spp`L+>&jV5ClNe~afV|@|VrJJfhADP?4r98I>TY|6PDrD}iO=A=p|Yw8|v?A}ef_4iwfr zF7z2FP)G-iAg|`J0f>1Y0~{}eQ{Z^G621Xr(EB#DtU?R=EMluzLYkPUh?mzX8iOkG z${luB^_K~qF{tgi84yMe1qLi8Wnx)CVv-_0$Rw{fuzXx z))U{?4)~HVf^-$vSZznn7W(>jQ{6QleWCA|K+@-l?^u~&fxlofCSQ{RnP7@wx?rv# zL=Y~B6s#3Q3pNQ71iJ(of-2#B;X2_4VVAJSYo1rB*G;cSqM4#)q9~D8RPC+tc6v8Y zHPKwsr2;YajNVsQcdR+NeoGL=g?sM6a0!LMckqr6h1XBFBk(qiz;r$dF2BW>%am8& z?)IU0QVqpJ%&5p|OO;UOLtS%mlejUcz_o-2qfi=Sjy|X$RT;Bksd|qdKh2Uu>#S;< z8a3CruS=I%8Xc#dmz@N4qqNO}pQzcd11j*5Ix=Nta#+gN45XH_=}b$SC7U(@LYsqH z4UoS-ufSBER+?rW5`7ZpPiRwR2fq< zwkEIBWE$`^JEK|e&>}O?>+)0tWm0P5l~L)@nR3-`b&5V6WC5);?cny(MAycuHI@|1 z-eLn$q%Y=&0If7_VVd+!iY6lDzF5r|97s+%%#6M4aBJmJXPxbkt<+XrOc&xg^G-dJ zRZ3))X5opq6cXDy6CW=-K^dit&XB88N8%yfkx;(H73+#Y@l-{6g4W}aI)2E}a-c!q zlzfy}bYGm!7@SB}^6?tlDvvU?e~HJScqN1y#Z#epdJv_3H)Hyq<@+|_A=X?&5S=Y9 zQWs<()f28=yk}uXaQdpy~DbE9+I|uW? zdsaLO`WBq7I&zZPRG)f^=Yj9EX-dE`f{IK!eBn}i<3pYWo)jIIihg)heo1}_U;tbY z*3ZenhhW{Vxb<=C(<88e54klN&Hxucm#gDAhJVK;ZrmFK=7P`9=%ARO`asKCq@{+z z-=dbzUbH)IVr;~$S+N)41URwn;?H+mBe)5H6c_4#6HW`f3c9LV+K;!pdcX|;L+Aa@ zO#@57>Xgmvw?ru;z(Rr=Bqf;C9lYiEjS*5ebv+DDg0dfbAr;8_NvMCRzFq<8MQz7- zM-X3|)U@9PDjoKN)y>ou<001D) z0xmovDXJ^5?(AM)U)~^vVggiMx2tc0^V?w@MZAggKp=oF*p*-vh z_4rqs#g-GrnE^vmCM?KVvt>ugk(2G+iI|!muBYqedb>VSX8KBV(^Z<8Zm!Bzn<6@L z`(vi7uKv2v`vDc46p#RToMT{S)@9UTU}fN7n#q*Lz`)=Gp&8Q{j2IpQ`9LW+P+;Of za6-Mq{Xq2p|IEh!jTz3eFfg12av4ElOeP@JK*$6D0$&R&0001ZoTZXaOB+EL#-E*u z*=QQlQZLraB4tb4K(Z|+f|s-iD%2j@i`N`-$SJ4vLnu_O9=sF+{RTyvs|X?@*y5oQ zF9y_;7eRV5!}OUyl>SjDIPja_JF~m*?999X1^`G>ASobtg#c!%#M!{kOAOihR1k4I zg(|<{o5&$T4RtioLyyx zHnleET$5#sS$4&N&2lC#C4OZIeU?(ZP`pS6lD5qf=pXt-f6{IJQLpP&-Oz8V>MRNmW)wRZvgWWA(6q)GPF!^u~JkjxyaZ-`;$k-T(f1-|hI;?T+b!Q$Kq2+mpcV z(;-OYG3Q?Mm=mpg%$+nmY0~tBq~*zwwr7xZJiIHhMYb7d=^Y31^e)+Be3w2Thm7yj zT?aOS2Ewj7&LDq(cWvbk2cZ_IF(gW;e3|2yp87=R)uU>yLN=m!IMoR!kMN&`_4 z2H<~AR!DpSqYHwm3?eKs1fqo?h-qvzfi1i=q9PJREY(6%ScoWI5YZ-B1-ujzEPMb9 z3-K8&v`?-6vra^U7A}0uIXiPMyXQy)(c>inNdPW|e#%QX#$5q`-SiS%N08jt`uK)meoRw0$ZWBQiJ?lp-kP;<`0!0)f`mAh6 zRuZ|hV__+_V@P6G6l;6dyYhM`8jm9-;vBppbIy(v$MLv$mqAkCwJtsvEJjkVs7&O|4KD;Y z3nOKwD!s`9{!z8L3>H$E&`;t?-Ea7fzHBcV!-FglpYWuO-#Fh<9dwM|Kt uiKlYq|DBI`bVB0ch#q;ge*g_t!k7SfoMT{S0E7Rt7>pRO001gg0nY&KoVp_b literal 0 HcmV?d00001 diff --git a/lib/Han/dist/font/han.woff2 b/lib/Han/dist/font/han.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..02c49afbd642c373142ce96b321820b2e31136dc GIT binary patch literal 13644 zcmV-SHM7chPew8T0RR9105wbi4*&oF0Do)%05t3X0RR9100000000000000000000 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. + 'initCond', + + // Address element normalisation + 'renderElem', + + // Handle Biaodian + /* 'jinzify', */ + 'renderJiya', + 'renderHanging', + + // Address Biaodian correction + 'correctBiaodian', + + // Address Hanzi and Western script mixed spacing + 'renderHWS', + + // Address presentational correction to combining ligatures + 'substCombLigaWithPUA' + + // Address semantic correction to inaccurate characters + // **Note:** inactivated by default + /* 'substInaccurateChar', */ +] + +// Define Han +var Han = function( context, condition ) { + return new Han.fn.init( context, condition ) +} + +var init = function() { + if ( arguments[ 0 ] ) { + this.context = arguments[ 0 ] + } + if ( arguments[ 1 ] ) { + this.condition = arguments[ 1 ] + } + return this +} + +Han.version = VERSION + +Han.fn = Han.prototype = { + version: VERSION, + + constructor: Han, + + // Body as the default target context + context: body, + + // Root element as the default condition + condition: root, + + // Default rendering routine + routine: ROUTINE, + + init: init, + + setRoutine: function( routine ) { + if ( Array.isArray( routine )) { + this.routine = routine + } + return this + }, + + // Note that the routine set up here will execute + // only once. The method won't alter the routine in + // the instance or in the prototype chain. + render: function( routine ) { + var it = this + var routine = Array.isArray( routine ) + ? routine + : this.routine + + routine + .forEach(function( method ) { + if ( + typeof method === 'string' && + typeof it[ method ] === 'function' + ) { + it[ method ]() + } else if ( + Array.isArray( method ) && + typeof it[ method[0] ] === 'function' + ) { + it[ method.shift() ].apply( it, method ) + } + }) + return this + } +} + +Han.fn.init.prototype = Han.fn + +/** + * Shortcut for `render()` under the default + * situation. + * + * Once initialised, replace `Han.init` with the + * instance for future usage. + */ +Han.init = function() { + return Han.init = Han().render() +} + +var UNICODE = { + /** + * Western punctuation (西文標點符號) + */ + punct: { + base: '[\u2026,.;:!?\u203D_]', + sing: '[\u2010-\u2014\u2026]', + middle: '[\\\/~\\-&\u2010-\u2014_]', + open: '[\'"‘“\\(\\[\u00A1\u00BF\u2E18\u00AB\u2039\u201A\u201C\u201E]', + close: '[\'"”’\\)\\]\u00BB\u203A\u201B\u201D\u201F]', + end: '[\'"”’\\)\\]\u00BB\u203A\u201B\u201D\u201F\u203C\u203D\u2047-\u2049,.;:!?]', + }, + + /** + * CJK biaodian (CJK標點符號) + */ + biaodian: { + base: '[︰.、,。:;?!ー]', + liga: '[—…⋯]', + middle: '[·\/-゠\uFF06\u30FB\uFF3F]', + open: '[「『《〈(〔[{【〖]', + close: '[」』》〉)〕]}】〗]', + end: '[」』》〉)〕]}】〗︰.、,。:;?!ー]' + }, + + /** + * CJK-related blocks (CJK相關字符區段) + * + * 1. 中日韓統一意音文字:[\u4E00-\u9FFF] + Basic CJK unified ideographs + * 2. 擴展-A區:[\u3400-\u4DB5] + Extended-A + * 3. 擴展-B區:[\u20000-\u2A6D6]([\uD840-\uD869][\uDC00-\uDED6]) + Extended-B + * 4. 擴展-C區:[\u2A700-\u2B734](\uD86D[\uDC00-\uDF3F]|[\uD86A-\uD86C][\uDC00-\uDFFF]|\uD869[\uDF00-\uDFFF]) + Extended-C + * 5. 擴展-D區:[\u2B740-\u2B81D](急用漢字,\uD86D[\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1F]) + Extended-D + * 6. 擴展-E區:[\u2B820-\u2F7FF](暫未支援) + Extended-E (not supported yet) + * 7. 擴展-F區(暫未支援) + Extended-F (not supported yet) + * 8. 筆畫區:[\u31C0-\u31E3] + Strokes + * 9. 意音數字「〇」:[\u3007] + Ideographic number zero + * 10. 相容意音文字及補充:[\uF900-\uFAFF][\u2F800-\u2FA1D](不使用) + Compatibility ideograph and supplement (not supported) + + 12 exceptions: + [\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29] + + https://zh.wikipedia.org/wiki/中日韓統一表意文字#cite_note-1 + + * 11. 康熙字典及簡化字部首:[\u2F00-\u2FD5\u2E80-\u2EF3] + Kangxi and supplement radicals + * 12. 意音文字描述字元:[\u2FF0-\u2FFA] + Ideographic description characters + */ + hanzi: { + base: '[\u4E00-\u9FFF\u3400-\u4DB5\u31C0-\u31E3\u3007\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD800-\uDBFF][\uDC00-\uDFFF]', + desc: '[\u2FF0-\u2FFA]', + radical: '[\u2F00-\u2FD5\u2E80-\u2EF3]' + }, + + /** + * Latin script blocks (拉丁字母區段) + * + * 1. 基本拉丁字母:A-Za-z + Basic Latin + * 2. 阿拉伯數字:0-9 + Digits + * 3. 補充-1:[\u00C0-\u00FF] + Latin-1 supplement + * 4. 擴展-A區:[\u0100-\u017F] + Extended-A + * 5. 擴展-B區:[\u0180-\u024F] + Extended-B + * 5. 擴展-C區:[\u2C60-\u2C7F] + Extended-C + * 5. 擴展-D區:[\uA720-\uA7FF] + Extended-D + * 6. 附加區:[\u1E00-\u1EFF] + Extended additional + * 7. 變音組字符:[\u0300-\u0341\u1DC0-\u1DFF] + Combining diacritical marks + */ + latin: { + base: '[A-Za-z0-9\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u2C60-\u2C7F\uA720-\uA7FF\u1E00-\u1EFF]', + combine: '[\u0300-\u0341\u1DC0-\u1DFF]' + }, + + /** + * Elli̱niká (Greek) script blocks (希臘字母區段) + * + * 1. 希臘字母及擴展:[\u0370–\u03FF\u1F00-\u1FFF] + Basic Greek & Greek Extended + * 2. 阿拉伯數字:0-9 + Digits + * 3. 希臘字母變音組字符:[\u0300-\u0345\u1DC0-\u1DFF] + Combining diacritical marks + */ + ellinika: { + base: '[0-9\u0370-\u03FF\u1F00-\u1FFF]', + combine: '[\u0300-\u0345\u1DC0-\u1DFF]' + }, + + /** + * Kirillica (Cyrillic) script blocks (西里爾字母區段) + * + * 1. 西里爾字母及補充:[\u0400-\u0482\u048A-\u04FF\u0500-\u052F] + Basic Cyrillic and supplement + * 2. 擴展B區:[\uA640-\uA66E\uA67E-\uA697] + Extended-B + * 3. 阿拉伯數字:0-9 + Digits + * 4. 西里爾字母組字符:[\u0483-\u0489\u2DE0-\u2DFF\uA66F-\uA67D\uA69F](位擴展A、B區) + Cyrillic combining diacritical marks (in extended-A, B) + */ + kirillica: { + base: '[0-9\u0400-\u0482\u048A-\u04FF\u0500-\u052F\uA640-\uA66E\uA67E-\uA697]', + combine: '[\u0483-\u0489\u2DE0-\u2DFF\uA66F-\uA67D\uA69F]' + }, + + /** + * Kana (假名) + * + * 1. 日文假名:[\u30A2\u30A4\u30A6\u30A8\u30AA-\u30FA\u3042\u3044\u3046\u3048\u304A-\u3094\u309F\u30FF] + Japanese Kana + * 2. 假名補充[\u1B000\u1B001](\uD82C[\uDC00-\uDC01]) + Kana supplement + * 3. 日文假名小寫:[\u3041\u3043\u3045\u3047\u3049\u30A1\u30A3\u30A5\u30A7\u30A9\u3063\u3083\u3085\u3087\u308E\u3095\u3096\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6\u31F0-\u31FF] + Japanese small Kana + * 4. 假名組字符:[\u3099-\u309C] + Kana combining characters + * 5. 半形假名:[\uFF66-\uFF9F] + Halfwidth Kana + * 6. 符號:[\u309D\u309E\u30FB-\u30FE] + Marks + */ + kana: { + base: '[\u30A2\u30A4\u30A6\u30A8\u30AA-\u30FA\u3042\u3044\u3046\u3048\u304A-\u3094\u309F\u30FF]|\uD82C[\uDC00-\uDC01]', + small: '[\u3041\u3043\u3045\u3047\u3049\u30A1\u30A3\u30A5\u30A7\u30A9\u3063\u3083\u3085\u3087\u308E\u3095\u3096\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6\u31F0-\u31FF]', + combine: '[\u3099-\u309C]', + half: '[\uFF66-\uFF9F]', + mark: '[\u30A0\u309D\u309E\u30FB-\u30FE]' + }, + + /** + * Eonmun (Hangul, 諺文) + * + * 1. 諺文音節:[\uAC00-\uD7A3] + Eonmun (Hangul) syllables + * 2. 諺文字母:[\u1100-\u11FF\u314F-\u3163\u3131-\u318E\uA960-\uA97C\uD7B0-\uD7FB] + Eonmun (Hangul) letters + * 3. 半形諺文字母:[\uFFA1-\uFFDC] + Halfwidth Eonmun (Hangul) letters + */ + eonmun: { + base: '[\uAC00-\uD7A3]', + letter: '[\u1100-\u11FF\u314F-\u3163\u3131-\u318E\uA960-\uA97C\uD7B0-\uD7FB]', + half: '[\uFFA1-\uFFDC]' + }, + + /** + * Zhuyin (注音符號, Mandarin & Dialect Phonetic Symbols) + * + * 1. 國語注音、方言音符號:[\u3105-\u312D][\u31A0-\u31BA] + Bopomofo phonetic symbols + * 2. 平上去聲調號:[\u02D9\u02CA\u02C5\u02C7\u02EA\u02EB\u02CB] (**註:**國語三聲包含乙個不合規範的符號) + Level, rising, departing tones + * 3. 入聲調號:[\u31B4-\u31B7][\u0358\u030d]? + Checked (entering) tones + */ + zhuyin: { + base: '[\u3105-\u312D\u31A0-\u31BA]', + initial: '[\u3105-\u3119\u312A-\u312C\u31A0-\u31A3]', + medial: '[\u3127-\u3129]', + final: '[\u311A-\u3129\u312D\u31A4-\u31B3\u31B8-\u31BA]', + tone: '[\u02D9\u02CA\u02C5\u02C7\u02CB\u02EA\u02EB]', + checked: '[\u31B4-\u31B7][\u0358\u030d]?' + } +} + +var TYPESET = (function() { + var rWhite = '[\\x20\\t\\r\\n\\f]' + // Whitespace characters + // http://www.w3.org/TR/css3-selectors/#whitespace + + var rPtOpen = UNICODE.punct.open + var rPtClose = UNICODE.punct.close + var rPtEnd = UNICODE.punct.end + var rPtMid = UNICODE.punct.middle + var rPtSing = UNICODE.punct.sing + var rPt = rPtOpen + '|' + rPtEnd + '|' + rPtMid + + var rBDOpen = UNICODE.biaodian.open + var rBDClose = UNICODE.biaodian.close + var rBDEnd = UNICODE.biaodian.end + var rBDMid = UNICODE.biaodian.middle + var rBDLiga = UNICODE.biaodian.liga + '{2}' + var rBD = rBDOpen + '|' + rBDEnd + '|' + rBDMid + + var rKana = UNICODE.kana.base + UNICODE.kana.combine + '?' + var rKanaS = UNICODE.kana.small + UNICODE.kana.combine + '?' + var rKanaH = UNICODE.kana.half + var rEon = UNICODE.eonmun.base + '|' + UNICODE.eonmun.letter + var rEonH = UNICODE.eonmun.half + + var rHan = UNICODE.hanzi.base + '|' + UNICODE.hanzi.desc + '|' + UNICODE.hanzi.radical + '|' + rKana + + var rCbn = UNICODE.ellinika.combine + var rLatn = UNICODE.latin.base + rCbn + '*' + var rGk = UNICODE.ellinika.base + rCbn + '*' + + var rCyCbn = UNICODE.kirillica.combine + var rCy = UNICODE.kirillica.base + rCyCbn + '*' + + var rAlph = rLatn + '|' + rGk + '|' + rCy + + // For words like `it's`, `Jones’s` or `'99` + var rApo = '[\u0027\u2019]' + var rChar = rHan + '|(?:' + rAlph + '|' + rApo + ')+' + + var rZyS = UNICODE.zhuyin.initial + var rZyJ = UNICODE.zhuyin.medial + var rZyY = UNICODE.zhuyin.final + var rZyD = UNICODE.zhuyin.tone + '|' + UNICODE.zhuyin.checked + + return { + /* Character-level selector (字級選擇器) + */ + char: { + punct: { + all: new RegExp( '(' + rPt + ')', 'g' ), + open: new RegExp( '(' + rPtOpen + ')', 'g' ), + end: new RegExp( '(' + rPtEnd + ')', 'g' ), + sing: new RegExp( '(' + rPtSing + ')', 'g' ) + }, + + biaodian: { + all: new RegExp( '(' + rBD + ')', 'g' ), + open: new RegExp( '(' + rBDOpen + ')', 'g' ), + close: new RegExp( '(' + rBDClose + ')', 'g' ), + end: new RegExp( '(' + rBDEnd + ')', 'g' ), + liga: new RegExp( '(' + rBDLiga + ')', 'g' ) + }, + + hanzi: new RegExp( '(' + rHan + ')', 'g' ), + + latin: new RegExp( '(' + rLatn + ')', 'ig' ), + ellinika: new RegExp( '(' + rGk + ')', 'ig' ), + kirillica: new RegExp( '(' + rCy + ')', 'ig' ), + + kana: new RegExp( '(' + rKana + '|' + rKanaS + '|' + rKanaH + ')', 'g' ), + eonmun: new RegExp( '(' + rEon + '|' + rEonH + ')', 'g' ) + }, + + /* Word-level selectors (詞級選擇器) + */ + group: { + biaodian: [ + new RegExp( '((' + rBD + '){2,})', 'g' ), + new RegExp( '(' + rBDLiga + rBDOpen + ')', 'g' ) + ], + punct: null, + hanzi: new RegExp( '(' + rHan + ')+', 'g' ), + western: new RegExp( '(' + rLatn + '|' + rGk + '|' + rCy + '|' + rPt + ')+', 'ig' ), + kana: new RegExp( '(' + rKana + '|' + rKanaS + '|' + rKanaH + ')+', 'g' ), + eonmun: new RegExp( '(' + rEon + '|' + rEonH + '|' + rPt + ')+', 'g' ) + }, + + /* Punctuation Rules (禁則) + */ + jinze: { + hanging: new RegExp( rWhite + '*([、,。.])(?!' + rBDEnd + ')', 'ig' ), + touwei: new RegExp( '(' + rBDOpen + '+)(' + rChar + ')(' + rBDEnd + '+)', 'ig' ), + tou: new RegExp( '(' + rBDOpen + '+)(' + rChar + ')', 'ig' ), + wei: new RegExp( '(' + rChar + ')(' + rBDEnd + '+)', 'ig' ), + middle: new RegExp( '(' + rChar + ')(' + rBDMid + ')(' + rChar + ')', 'ig' ) + }, + + zhuyin: { + form: new RegExp( '^\u02D9?(' + rZyS + ')?(' + rZyJ + ')?(' + rZyY + ')?(' + rZyD + ')?$' ), + diao: new RegExp( '(' + rZyD + ')', 'g' ) + }, + + /* Hanzi and Western mixed spacing (漢字西文混排間隙) + * - Basic mode + * - Strict mode + */ + hws: { + base: [ + new RegExp( '('+ rHan + ')(' + rAlph + '|' + rPtOpen + ')', 'ig' ), + new RegExp( '('+ rAlph + '|' + rPtEnd + ')(' + rHan + ')', 'ig' ) + ], + + strict: [ + new RegExp( '('+ rHan + ')' + rWhite + '?(' + rAlph + '|' + rPtOpen + ')', 'ig' ), + new RegExp( '('+ rAlph + '|' + rPtEnd + ')' + rWhite + '?(' + rHan + ')', 'ig' ) + ] + }, + + // The feature displays the following characters + // in its variant form for font consistency and + // presentational reason. Meanwhile, this won't + // alter the original character in the DOM. + 'display-as': { + 'ja-font-for-hant': [ + // '夠 够', + '查 査', + '啟 啓', + '鄉 鄕', + '值 値', + '污 汚' + ], + + 'comb-liga-pua': [ + [ '\u0061[\u030d\u0358]', '\uDB80\uDC61' ], + [ '\u0065[\u030d\u0358]', '\uDB80\uDC65' ], + [ '\u0069[\u030d\u0358]', '\uDB80\uDC69' ], + [ '\u006F[\u030d\u0358]', '\uDB80\uDC6F' ], + [ '\u0075[\u030d\u0358]', '\uDB80\uDC75' ], + + [ '\u31B4[\u030d\u0358]', '\uDB8C\uDDB4' ], + [ '\u31B5[\u030d\u0358]', '\uDB8C\uDDB5' ], + [ '\u31B6[\u030d\u0358]', '\uDB8C\uDDB6' ], + [ '\u31B7[\u030d\u0358]', '\uDB8C\uDDB7' ] + ], + + 'comb-liga-vowel': [ + [ '\u0061[\u030d\u0358]', '\uDB80\uDC61' ], + [ '\u0065[\u030d\u0358]', '\uDB80\uDC65' ], + [ '\u0069[\u030d\u0358]', '\uDB80\uDC69' ], + [ '\u006F[\u030d\u0358]', '\uDB80\uDC6F' ], + [ '\u0075[\u030d\u0358]', '\uDB80\uDC75' ] + ], + + 'comb-liga-zhuyin': [ + [ '\u31B4[\u030d\u0358]', '\uDB8C\uDDB4' ], + [ '\u31B5[\u030d\u0358]', '\uDB8C\uDDB5' ], + [ '\u31B6[\u030d\u0358]', '\uDB8C\uDDB6' ], + [ '\u31B7[\u030d\u0358]', '\uDB8C\uDDB7' ] + ] + }, + + // The feature actually *converts* the character + // in the DOM for semantic reason. + // + // Note that this could be aggressive. + 'inaccurate-char': [ + [ '[\u2022\u2027]', '\u00B7' ], + [ '\u22EF\u22EF', '\u2026\u2026' ], + [ '\u2500\u2500', '\u2014\u2014' ], + [ '\u2035', '\u2018' ], + [ '\u2032', '\u2019' ], + [ '\u2036', '\u201C' ], + [ '\u2033', '\u201D' ] + ] + } +})() + +Han.UNICODE = UNICODE +Han.TYPESET = TYPESET + +// Aliases +Han.UNICODE.cjk = Han.UNICODE.hanzi +Han.UNICODE.greek = Han.UNICODE.ellinika +Han.UNICODE.cyrillic = Han.UNICODE.kirillica +Han.UNICODE.hangul = Han.UNICODE.eonmun +Han.UNICODE.zhuyin.ruyun = Han.UNICODE.zhuyin.checked + +Han.TYPESET.char.cjk = Han.TYPESET.char.hanzi +Han.TYPESET.char.greek = Han.TYPESET.char.ellinika +Han.TYPESET.char.cyrillic = Han.TYPESET.char.kirillica +Han.TYPESET.char.hangul = Han.TYPESET.char.eonmun + +Han.TYPESET.group.hangul = Han.TYPESET.group.eonmun +Han.TYPESET.group.cjk = Han.TYPESET.group.hanzi + +var $ = { + /** + * Query selectors which return arrays of the resulted + * node lists. + */ + id: function( selector, $context ) { + return ( $context || document ).getElementById( selector ) + }, + + tag: function( selector, $context ) { + return this.makeArray( + ( $context || document ).getElementsByTagName( selector ) + ) + }, + + qs: function( selector, $context ) { + return ( $context || document ).querySelector( selector ) + }, + + qsa: function( selector, $context ) { + return this.makeArray( + ( $context || document ).querySelectorAll( selector ) + ) + }, + + parent: function( $node, selector ) { + return selector + ? (function() { + if ( typeof $.matches !== 'function' ) return + + while (!$.matches( $node, selector )) { + if ( + !$node || + $node === document.documentElement + ) { + $node = undefined + break + } + $node = $node.parentNode + } + return $node + })() + : $node + ? $node.parentNode : undefined + }, + + /** + * Create a document fragment, a text node with text + * or an element with/without classes. + */ + create: function( name, clazz ) { + var $elmt = '!' === name + ? document.createDocumentFragment() + : '' === name + ? document.createTextNode( clazz || '' ) + : document.createElement( name ) + + try { + if ( clazz ) { + $elmt.className = clazz + } + } catch (e) {} + + return $elmt + }, + + /** + * Clone a DOM node (text, element or fragment) deeply + * or childlessly. + */ + clone: function( $node, deep ) { + return $node.cloneNode( + typeof deep === 'boolean' + ? deep + : true + ) + }, + + /** + * Remove a node (text, element or fragment). + */ + remove: function( $node ) { + return $node.parentNode.removeChild( $node ) + }, + + /** + * Set attributes all in once with an object. + */ + setAttr: function( target, attr ) { + if ( typeof attr !== 'object' ) return + var len = attr.length + + // Native `NamedNodeMap``: + if ( + typeof attr[0] === 'object' && + 'name' in attr[0] + ) { + for ( var i = 0; i < len; i++ ) { + if ( attr[ i ].value !== undefined ) { + target.setAttribute( attr[ i ].name, attr[ i ].value ) + } + } + + // Plain object: + } else { + for ( var name in attr ) { + if ( + attr.hasOwnProperty( name ) && + attr[ name ] !== undefined + ) { + target.setAttribute( name, attr[ name ] ) + } + } + } + return target + }, + + /** + * Indicate whether or not the given node is an + * element. + */ + isElmt: function( $node ) { + return $node && $node.nodeType === Node.ELEMENT_NODE + }, + + /** + * Indicate whether or not the given node should + * be ignored (`` or comments). + */ + isIgnorable: function( $node ) { + if ( !$node ) return false + + return ( + $node.nodeName === 'WBR' || + $node.nodeType === Node.COMMENT_NODE + ) + }, + + /** + * Convert array-like objects into real arrays. + */ + makeArray: function( object ) { + return Array.prototype.slice.call( object ) + }, + + /** + * Extend target with an object. + */ + extend: function( target, object ) { + if (( + typeof target === 'object' || + typeof target === 'function' ) && + typeof object === 'object' + ) { + for ( var name in object ) { + if (object.hasOwnProperty( name )) { + target[ name ] = object[ name ] + } + } + } + return target + } +} + +var Fibre = +/*! + * Fibre.js v0.2.1 | MIT License | github.com/ethantw/fibre.js + * Based on findAndReplaceDOMText + */ + +function( Finder ) { + +'use strict' + +var VERSION = '0.2.1' +var NON_INLINE_PROSE = Finder.NON_INLINE_PROSE +var AVOID_NON_PROSE = Finder.PRESETS.prose.filterElements + +var global = window || {} +var document = global.document || undefined + +function matches( node, selector, bypassNodeType39 ) { + var Efn = Element.prototype + var matches = Efn.matches || Efn.mozMatchesSelector || Efn.msMatchesSelector || Efn.webkitMatchesSelector + + if ( node instanceof Element ) { + return matches.call( node, selector ) + } else if ( bypassNodeType39 ) { + if ( /^[39]$/.test( node.nodeType )) return true + } + return false +} + +if ( typeof document === 'undefined' ) throw new Error( 'Fibre requires a DOM-supported environment.' ) + +var Fibre = function( context, preset ) { + return new Fibre.fn.init( context, preset ) +} + +Fibre.version = VERSION +Fibre.matches = matches + +Fibre.fn = Fibre.prototype = { + constructor: Fibre, + + version: VERSION, + + finder: [], + + context: undefined, + + portionMode: 'retain', + + selector: {}, + + preset: 'prose', + + init: function( context, noPreset ) { + if ( !!noPreset ) this.preset = null + + this.selector = { + context: null, + filter: [], + avoid: [], + boundary: [] + } + + if ( !context ) { + throw new Error( 'A context is required for Fibre to initialise.' ) + } else if ( context instanceof Node ) { + if ( context instanceof Document ) this.context = context.body || context + else this.context = context + } else if ( typeof context === 'string' ) { + this.context = document.querySelector( context ) + this.selector.context = context + } + return this + }, + + filterFn: function( node ) { + var filter = this.selector.filter.join( ', ' ) || '*' + var avoid = this.selector.avoid.join( ', ' ) || null + var result = matches( node, filter, true ) && !matches( node, avoid ) + return ( this.preset === 'prose' ) ? AVOID_NON_PROSE( node ) && result : result + }, + + boundaryFn: function( node ) { + var boundary = this.selector.boundary.join( ', ' ) || null + var result = matches( node, boundary ) + return ( this.preset === 'prose' ) ? NON_INLINE_PROSE( node ) || result : result + }, + + filter: function( selector ) { + if ( typeof selector === 'string' ) { + this.selector.filter.push( selector ) + } + return this + }, + + endFilter: function( all ) { + if ( all ) { + this.selector.filter = [] + } else { + this.selector.filter.pop() + } + return this + }, + + avoid: function( selector ) { + if ( typeof selector === 'string' ) { + this.selector.avoid.push( selector ) + } + return this + }, + + endAvoid: function( all ) { + if ( all ) { + this.selector.avoid = [] + } else { + this.selector.avoid.pop() + } + return this + }, + + addBoundary: function( selector ) { + if ( typeof selector === 'string' ) { + this.selector.boundary.push( selector ) + } + return this + }, + + removeBoundary: function() { + this.selector.boundary = [] + return this + }, + + setMode: function( portionMode ) { + this.portionMode = portionMode === 'first' ? 'first' : 'retain' + return this + }, + + replace: function( regexp, newSubStr ) { + var it = this + it.finder.push(Finder( it.context, { + find: regexp, + replace: newSubStr, + filterElements: function( currentNode ) { + return it.filterFn( currentNode ) + }, + forceContext: function( currentNode ) { + return it.boundaryFn( currentNode ) + }, + portionMode: it.portionMode + })) + return it + }, + + wrap: function( regexp, strElemName ) { + var it = this + it.finder.push(Finder( it.context, { + find: regexp, + wrap: strElemName, + filterElements: function( currentNode ) { + return it.filterFn( currentNode ) + }, + forceContext: function( currentNode ) { + return it.boundaryFn( currentNode ) + }, + portionMode: it.portionMode + })) + return it + }, + + revert: function( level ) { + var max = this.finder.length + var level = Number( level ) || ( level === 0 ? Number(0) : + ( level === 'all' ? max : 1 )) + + if ( typeof max === 'undefined' || max === 0 ) return this + else if ( level > max ) level = max + + for ( var i = level; i > 0; i-- ) { + this.finder.pop().revert() + } + return this + } +} + +// Deprecated API(s) +Fibre.fn.filterOut = Fibre.fn.avoid + +// Make sure init() inherit from Fibre() +Fibre.fn.init.prototype = Fibre.fn + +return Fibre + +}( + +/** + * findAndReplaceDOMText v 0.4.3 + * @author James Padolsey http://james.padolsey.com + * @license http://unlicense.org/UNLICENSE + * + * Matches the text of a DOM node against a regular expression + * and replaces each match (or node-separated portions of the match) + * in the specified element. + */ + (function() { + + var PORTION_MODE_RETAIN = 'retain' + var PORTION_MODE_FIRST = 'first' + var doc = document + var toString = {}.toString + var hasOwn = {}.hasOwnProperty + function isArray(a) { + return toString.call(a) == '[object Array]' + } + + function escapeRegExp(s) { + return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1') + } + + function exposed() { + // Try deprecated arg signature first: + return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments) + } + + function deprecated(regex, node, replacement, captureGroup, elFilter) { + if ((node && !node.nodeType) && arguments.length <= 2) { + return false + } + var isReplacementFunction = typeof replacement == 'function' + if (isReplacementFunction) { + replacement = (function(original) { + return function(portion, match) { + return original(portion.text, match.startIndex) + } + }(replacement)) + } + + // Awkward support for deprecated argument signature (<0.4.0) + var instance = findAndReplaceDOMText(node, { + + find: regex, + + wrap: isReplacementFunction ? null : replacement, + replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'), + + prepMatch: function(m, mi) { + + // Support captureGroup (a deprecated feature) + + if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches' + if (captureGroup > 0) { + var cg = m[captureGroup] + m.index += m[0].indexOf(cg) + m[0] = cg + } + + m.endIndex = m.index + m[0].length + m.startIndex = m.index + m.index = mi + return m + }, + filterElements: elFilter + }) + exposed.revert = function() { + return instance.revert() + } + return true + } + + /** + * findAndReplaceDOMText + * + * Locates matches and replaces with replacementNode + * + * @param {Node} node Element or Text node to search within + * @param {RegExp} options.find The regular expression to match + * @param {String|Element} [options.wrap] A NodeName, or a Node to clone + * @param {String|Function} [options.replace='$&'] What to replace each match with + * @param {Function} [options.filterElements] A Function to be called to check whether to + * process an element. (returning true = process element, + * returning false = avoid element) + */ + function findAndReplaceDOMText(node, options) { + return new Finder(node, options) + } + + exposed.NON_PROSE_ELEMENTS = { + br:1, hr:1, + // Media / Source elements: + script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1, + // Input elements + input:1, textarea:1, select:1, option:1, optgroup: 1, button:1 + } + exposed.NON_CONTIGUOUS_PROSE_ELEMENTS = { + + // Elements that will not contain prose or block elements where we don't + // want prose to be matches across element borders: + + // Block Elements + address:1, article:1, aside:1, blockquote:1, dd:1, div:1, + dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1, + h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1, + output:1, p:1, pre:1, section:1, ul:1, + // Other misc. elements that are not part of continuous inline prose: + br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1, + // Media / Source elements: + script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1, + // Input elements + input:1, textarea:1, select:1, option:1, optgroup: 1, button:1, + // Table related elements: + table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1 + + } + exposed.NON_INLINE_PROSE = function(el) { + return hasOwn.call(exposed.NON_CONTIGUOUS_PROSE_ELEMENTS, el.nodeName.toLowerCase()) + } + // Presets accessed via `options.preset` when calling findAndReplaceDOMText(): + exposed.PRESETS = { + prose: { + forceContext: exposed.NON_INLINE_PROSE, + filterElements: function(el) { + return !hasOwn.call(exposed.NON_PROSE_ELEMENTS, el.nodeName.toLowerCase()) + } + } + } + exposed.Finder = Finder + /** + * Finder -- encapsulates logic to find and replace. + */ + function Finder(node, options) { + + var preset = options.preset && exposed.PRESETS[options.preset] + options.portionMode = options.portionMode || PORTION_MODE_RETAIN + if (preset) { + for (var i in preset) { + if (hasOwn.call(preset, i) && !hasOwn.call(options, i)) { + options[i] = preset[i] + } + } + } + + this.node = node + this.options = options + // ENable match-preparation method to be passed as option: + this.prepMatch = options.prepMatch || this.prepMatch + this.reverts = [] + this.matches = this.search() + if (this.matches.length) { + this.processMatches() + } + + } + + Finder.prototype = { + + /** + * Searches for all matches that comply with the instance's 'match' option + */ + search: function() { + + var match + var matchIndex = 0 + var offset = 0 + var regex = this.options.find + var textAggregation = this.getAggregateText() + var matches = [] + var self = this + regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex + matchAggregation(textAggregation) + function matchAggregation(textAggregation) { + for (var i = 0, l = textAggregation.length; i < l; ++i) { + + var text = textAggregation[i] + if (typeof text !== 'string') { + // Deal with nested contexts: (recursive) + matchAggregation(text) + continue + } + + if (regex.global) { + while (match = regex.exec(text)) { + matches.push(self.prepMatch(match, matchIndex++, offset)) + } + } else { + if (match = text.match(regex)) { + matches.push(self.prepMatch(match, 0, offset)) + } + } + + offset += text.length + } + } + + return matches + }, + + /** + * Prepares a single match with useful meta info: + */ + prepMatch: function(match, matchIndex, characterOffset) { + + if (!match[0]) { + throw new Error('findAndReplaceDOMText cannot handle zero-length matches') + } + + match.endIndex = characterOffset + match.index + match[0].length + match.startIndex = characterOffset + match.index + match.index = matchIndex + return match + }, + + /** + * Gets aggregate text within subject node + */ + getAggregateText: function() { + + var elementFilter = this.options.filterElements + var forceContext = this.options.forceContext + return getText(this.node) + /** + * Gets aggregate text of a node without resorting + * to broken innerText/textContent + */ + function getText(node, txt) { + + if (node.nodeType === 3) { + return [node.data] + } + + if (elementFilter && !elementFilter(node)) { + return [] + } + + var txt = [''] + var i = 0 + if (node = node.firstChild) do { + + if (node.nodeType === 3) { + txt[i] += node.data + continue + } + + var innerText = getText(node) + if ( + forceContext && + node.nodeType === 1 && + (forceContext === true || forceContext(node)) + ) { + txt[++i] = innerText + txt[++i] = '' + } else { + if (typeof innerText[0] === 'string') { + // Bridge nested text-node data so that they're + // not considered their own contexts: + // I.e. ['some', ['thing']] -> ['something'] + txt[i] += innerText.shift() + } + if (innerText.length) { + txt[++i] = innerText + txt[++i] = '' + } + } + } while (node = node.nextSibling) + return txt + } + + }, + + /** + * Steps through the target node, looking for matches, and + * calling replaceFn when a match is found. + */ + processMatches: function() { + + var matches = this.matches + var node = this.node + var elementFilter = this.options.filterElements + var startPortion, + endPortion, + innerPortions = [], + curNode = node, + match = matches.shift(), + atIndex = 0, // i.e. nodeAtIndex + matchIndex = 0, + portionIndex = 0, + doAvoidNode, + nodeStack = [node] + out: while (true) { + + if (curNode.nodeType === 3) { + + if (!endPortion && curNode.length + atIndex >= match.endIndex) { + + // We've found the ending + endPortion = { + node: curNode, + index: portionIndex++, + text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex), + indexInMatch: atIndex - match.startIndex, + indexInNode: match.startIndex - atIndex, // always zero for end-portions + endIndexInNode: match.endIndex - atIndex, + isEnd: true + } + } else if (startPortion) { + // Intersecting node + innerPortions.push({ + node: curNode, + index: portionIndex++, + text: curNode.data, + indexInMatch: atIndex - match.startIndex, + indexInNode: 0 // always zero for inner-portions + }) + } + + if (!startPortion && curNode.length + atIndex > match.startIndex) { + // We've found the match start + startPortion = { + node: curNode, + index: portionIndex++, + indexInMatch: 0, + indexInNode: match.startIndex - atIndex, + endIndexInNode: match.endIndex - atIndex, + text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex) + } + } + + atIndex += curNode.data.length + } + + doAvoidNode = curNode.nodeType === 1 && elementFilter && !elementFilter(curNode) + if (startPortion && endPortion) { + + curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion) + // processMatches has to return the node that replaced the endNode + // and then we step back so we can continue from the end of the + // match: + + atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode) + startPortion = null + endPortion = null + innerPortions = [] + match = matches.shift() + portionIndex = 0 + matchIndex++ + if (!match) { + break; // no more matches + } + + } else if ( + !doAvoidNode && + (curNode.firstChild || curNode.nextSibling) + ) { + // Move down or forward: + if (curNode.firstChild) { + nodeStack.push(curNode) + curNode = curNode.firstChild + } else { + curNode = curNode.nextSibling + } + continue + } + + // Move forward or up: + while (true) { + if (curNode.nextSibling) { + curNode = curNode.nextSibling + break + } + curNode = nodeStack.pop() + if (curNode === node) { + break out + } + } + + } + + }, + + /** + * Reverts ... TODO + */ + revert: function() { + // Reversion occurs backwards so as to avoid nodes subsequently + // replaced during the matching phase (a forward process): + for (var l = this.reverts.length; l--;) { + this.reverts[l]() + } + this.reverts = [] + }, + + prepareReplacementString: function(string, portion, match, matchIndex) { + var portionMode = this.options.portionMode + if ( + portionMode === PORTION_MODE_FIRST && + portion.indexInMatch > 0 + ) { + return '' + } + string = string.replace(/\$(\d+|&|`|')/g, function($0, t) { + var replacement + switch(t) { + case '&': + replacement = match[0] + break + case '`': + replacement = match.input.substring(0, match.startIndex) + break + case '\'': + replacement = match.input.substring(match.endIndex) + break + default: + replacement = match[+t] + } + return replacement + }) + if (portionMode === PORTION_MODE_FIRST) { + return string + } + + if (portion.isEnd) { + return string.substring(portion.indexInMatch) + } + + return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length) + }, + + getPortionReplacementNode: function(portion, match, matchIndex) { + + var replacement = this.options.replace || '$&' + var wrapper = this.options.wrap + if (wrapper && wrapper.nodeType) { + // Wrapper has been provided as a stencil-node for us to clone: + var clone = doc.createElement('div') + clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper) + wrapper = clone.firstChild + } + + if (typeof replacement == 'function') { + replacement = replacement(portion, match, matchIndex) + if (replacement && replacement.nodeType) { + return replacement + } + return doc.createTextNode(String(replacement)) + } + + var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper + replacement = doc.createTextNode( + this.prepareReplacementString( + replacement, portion, match, matchIndex + ) + ) + if (!replacement.data) { + return replacement + } + + if (!el) { + return replacement + } + + el.appendChild(replacement) + return el + }, + + replaceMatch: function(match, startPortion, innerPortions, endPortion) { + + var matchStartNode = startPortion.node + var matchEndNode = endPortion.node + var preceedingTextNode + var followingTextNode + if (matchStartNode === matchEndNode) { + + var node = matchStartNode + if (startPortion.indexInNode > 0) { + // Add `before` text node (before the match) + preceedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode)) + node.parentNode.insertBefore(preceedingTextNode, node) + } + + // Create the replacement node: + var newNode = this.getPortionReplacementNode( + endPortion, + match + ) + node.parentNode.insertBefore(newNode, node) + if (endPortion.endIndexInNode < node.length) { // ????? + // Add `after` text node (after the match) + followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode)) + node.parentNode.insertBefore(followingTextNode, node) + } + + node.parentNode.removeChild(node) + this.reverts.push(function() { + if (preceedingTextNode === newNode.previousSibling) { + preceedingTextNode.parentNode.removeChild(preceedingTextNode) + } + if (followingTextNode === newNode.nextSibling) { + followingTextNode.parentNode.removeChild(followingTextNode) + } + newNode.parentNode.replaceChild(node, newNode) + }) + return newNode + } else { + // Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order) + + preceedingTextNode = doc.createTextNode( + matchStartNode.data.substring(0, startPortion.indexInNode) + ) + followingTextNode = doc.createTextNode( + matchEndNode.data.substring(endPortion.endIndexInNode) + ) + var firstNode = this.getPortionReplacementNode( + startPortion, + match + ) + var innerNodes = [] + for (var i = 0, l = innerPortions.length; i < l; ++i) { + var portion = innerPortions[i] + var innerNode = this.getPortionReplacementNode( + portion, + match + ) + portion.node.parentNode.replaceChild(innerNode, portion.node) + this.reverts.push((function(portion, innerNode) { + return function() { + innerNode.parentNode.replaceChild(portion.node, innerNode) + } + }(portion, innerNode))) + innerNodes.push(innerNode) + } + + var lastNode = this.getPortionReplacementNode( + endPortion, + match + ) + matchStartNode.parentNode.insertBefore(preceedingTextNode, matchStartNode) + matchStartNode.parentNode.insertBefore(firstNode, matchStartNode) + matchStartNode.parentNode.removeChild(matchStartNode) + matchEndNode.parentNode.insertBefore(lastNode, matchEndNode) + matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode) + matchEndNode.parentNode.removeChild(matchEndNode) + this.reverts.push(function() { + preceedingTextNode.parentNode.removeChild(preceedingTextNode) + firstNode.parentNode.replaceChild(matchStartNode, firstNode) + followingTextNode.parentNode.removeChild(followingTextNode) + lastNode.parentNode.replaceChild(matchEndNode, lastNode) + }) + return lastNode + } + } + + } + return exposed +}()) + +); + +var isNodeNormalizeNormal = (function() { + //// Disabled `Node.normalize()` for temp due to + //// issue below in IE11. + //// See: http://stackoverflow.com/questions/22337498/why-does-ie11-handle-node-normalize-incorrectly-for-the-minus-symbol + var div = $.create( 'div' ) + + div.appendChild($.create( '', '0-' )) + div.appendChild($.create( '', '2' )) + div.normalize() + + return div.firstChild.length !== 2 +})() + +function getFuncOrElmt( obj ) { + return ( + typeof obj === 'function' || + obj instanceof Element + ) + ? obj + : undefined +} + +function createBDGroup( portion ) { + var clazz = portion.index === 0 && portion.isEnd + ? 'biaodian cjk' + : 'biaodian cjk portion ' + ( + portion.index === 0 + ? 'is-first' + : portion.isEnd + ? 'is-end' + : 'is-inner' + ) + + var $elmt = $.create( 'h-char-group', clazz ) + $elmt.innerHTML = portion.text + return $elmt +} + +function createBDChar( char ) { + var div = $.create( 'div' ) + var unicode = char.charCodeAt( 0 ).toString( 16 ) + + div.innerHTML = ( + '' + char + '' + ) + return div.firstChild +} + +function getBDType( char ) { + return char.match( TYPESET.char.biaodian.open ) + ? 'bd-open' + : char.match( TYPESET.char.biaodian.close ) + ? 'bd-close bd-end' + : char.match( TYPESET.char.biaodian.end ) + ? ( + /(?:\u3001|\u3002|\uff0c)/i.test( char ) + ? 'bd-end bd-cop' + : 'bd-end' + ) + : char.match(new RegExp( UNICODE.biaodian.liga )) + ? 'bd-liga' + : char.match(new RegExp( UNICODE.biaodian.middle )) + ? 'bd-middle' + : '' +} + +$.extend( Fibre.fn, { + normalize: function() { + if ( isNodeNormalizeNormal ) { + this.context.normalize() + } + return this + }, + + // Force punctuation & biaodian typesetting rules to be applied. + jinzify: function( selector ) { + return ( + this + .filter( selector || null ) + .avoid( 'h-jinze' ) + .replace( + TYPESET.jinze.touwei, + function( portion, match ) { + var elem = $.create( 'h-jinze', 'touwei' ) + elem.innerHTML = match[0] + return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 ) ? elem : '' + } + ) + .replace( + TYPESET.jinze.wei, + function( portion, match ) { + var elem = $.create( 'h-jinze', 'wei' ) + elem.innerHTML = match[0] + return portion.index === 0 ? elem : '' + } + ) + .replace( + TYPESET.jinze.tou, + function( portion, match ) { + var elem = $.create( 'h-jinze', 'tou' ) + elem.innerHTML = match[0] + return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 ) + ? elem : '' + } + ) + .replace( + TYPESET.jinze.middle, + function( portion, match ) { + var elem = $.create( 'h-jinze', 'middle' ) + elem.innerHTML = match[0] + return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 ) + ? elem : '' + } + ) + .endAvoid() + .endFilter() + ) + }, + + groupify: function( option ) { + var option = $.extend({ + biaodian: false, + //punct: false, + hanzi: false, // Includes Kana + kana: false, + eonmun: false, + western: false // Includes Latin, Greek and Cyrillic + }, option || {}) + + this.avoid( 'h-word, h-char-group' ) + + if ( option.biaodian ) { + this.replace( + TYPESET.group.biaodian[0], createBDGroup + ).replace( + TYPESET.group.biaodian[1], createBDGroup + ) + } + + if ( option.hanzi || option.cjk ) { + this.wrap( + TYPESET.group.hanzi, $.clone($.create( 'h-char-group', 'hanzi cjk' )) + ) + } + if ( option.western ) { + this.wrap( + TYPESET.group.western, $.clone($.create( 'h-word', 'western' )) + ) + } + if ( option.kana ) { + this.wrap( + TYPESET.group.kana, $.clone($.create( 'h-char-group', 'kana' )) + ) + } + if ( option.eonmun || option.hangul ) { + this.wrap( + TYPESET.group.eonmun, $.clone($.create( 'h-word', 'eonmun hangul' )) + ) + } + + this.endAvoid() + return this + }, + + charify: function( option ) { + var option = $.extend({ + avoid: true, + biaodian: false, + punct: false, + hanzi: false, // Includes Kana + latin: false, + ellinika: false, + kirillica: false, + kana: false, + eonmun: false + }, option || {}) + + if ( option.avoid ) { + this.avoid( 'h-char' ) + } + + if ( option.biaodian ) { + this.replace( + TYPESET.char.biaodian.all, + getFuncOrElmt( option.biaodian ) + || + function( portion ) { return createBDChar( portion.text ) } + ).replace( + TYPESET.char.biaodian.liga, + getFuncOrElmt( option.biaodian ) + || + function( portion ) { return createBDChar( portion.text ) } + ) + } + if ( option.hanzi || option.cjk ) { + this.wrap( + TYPESET.char.hanzi, + getFuncOrElmt( option.hanzi || option.cjk ) + || + $.clone($.create( 'h-char', 'hanzi cjk' )) + ) + } + if ( option.punct ) { + this.wrap( + TYPESET.char.punct.all, + getFuncOrElmt( option.punct ) + || + $.clone($.create( 'h-char', 'punct' )) + ) + } + if ( option.latin ) { + this.wrap( + TYPESET.char.latin, + getFuncOrElmt( option.latin ) + || + $.clone($.create( 'h-char', 'alphabet latin' )) + ) + } + if ( option.ellinika || option.greek ) { + this.wrap( + TYPESET.char.ellinika, + getFuncOrElmt( option.ellinika || option.greek ) + || + $.clone($.create( 'h-char', 'alphabet ellinika greek' )) + ) + } + if ( option.kirillica || option.cyrillic ) { + this.wrap( + TYPESET.char.kirillica, + getFuncOrElmt( option.kirillica || option.cyrillic ) + || + $.clone($.create( 'h-char', 'alphabet kirillica cyrillic' )) + ) + } + if ( option.kana ) { + this.wrap( + TYPESET.char.kana, + getFuncOrElmt( option.kana ) + || + $.clone($.create( 'h-char', 'kana' )) + ) + } + if ( option.eonmun || option.hangul ) { + this.wrap( + TYPESET.char.eonmun, + getFuncOrElmt( option.eonmun || option.hangul ) + || + $.clone($.create( 'h-char', 'eonmun hangul' )) + ) + } + + this.endAvoid() + return this + } +}) + +$.extend( Han, { + isNodeNormalizeNormal: isNodeNormalizeNormal, + find: Fibre, + createBDGroup: createBDGroup, + createBDChar: createBDChar +}) + +$.matches = Han.find.matches + +void [ + 'setMode', + 'wrap', 'replace', 'revert', + 'addBoundary', 'removeBoundary', + 'avoid', 'endAvoid', + 'filter', 'endFilter', + 'jinzify', 'groupify', 'charify' +].forEach(function( method ) { + Han.fn[ method ] = function() { + if ( !this.finder ) { + // Share the same selector + this.finder = Han.find( this.context ) + } + + this.finder[ method ]( arguments[ 0 ], arguments[ 1 ] ) + return this + } +}) + +var Locale = {} + +function writeOnCanvas( text, font ) { + var canvas = $.create( 'canvas' ) + var context + + canvas.width = '50' + canvas.height = '20' + canvas.style.display = 'none' + + body.appendChild( canvas ) + + context = canvas.getContext( '2d' ) + context.textBaseline = 'top' + context.font = '15px ' + font + ', sans-serif' + context.fillStyle = 'black' + context.strokeStyle = 'black' + context.fillText( text, 0, 0 ) + + return { + node: canvas, + context: context, + remove: function() { + $.remove( canvas, body ) + } + } +} + +function compareCanvases( treat, control ) { + var ret + var a = treat.context + var b = control.context + + try { + for ( var j = 1; j <= 20; j++ ) { + for ( var i = 1; i <= 50; i++ ) { + if ( + typeof ret === 'undefined' && + a.getImageData(i, j, 1, 1).data[3] !== b.getImageData(i, j, 1, 1).data[3] + ) { + ret = false + break + } else if ( typeof ret === 'boolean' ) { + break + } + + if ( i === 50 && j === 20 && typeof ret === 'undefined' ) { + ret = true + } + } + } + + // Remove and clean from memory + treat.remove() + control.remove() + treat = null + control = null + + return ret + } catch (e) {} + return false +} + +function detectFont( treat, control, text ) { + var treat = treat + var control = control || 'sans-serif' + var text = text || '辭Q' + var ret + + control = writeOnCanvas( text, control ) + treat = writeOnCanvas( text, treat ) + + return !compareCanvases( treat, control ) +} + +Locale.writeOnCanvas = writeOnCanvas +Locale.compareCanvases = compareCanvases +Locale.detectFont = detectFont + +Locale.support = (function() { + + var PREFIX = 'Webkit Moz ms'.split(' ') + + // Create an element for feature detecting + // (in `testCSSProp`) + var elem = $.create( 'h-test' ) + + function testCSSProp( prop ) { + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1) + var allProp = ( prop + ' ' + PREFIX.join( ucProp + ' ' ) + ucProp ).split(' ') + var ret + + allProp.forEach(function( prop ) { + if ( typeof elem.style[ prop ] === 'string' ) { + ret = true + } + }) + return ret || false + } + + function injectElementWithStyle( rule, callback ) { + var fakeBody = body || $.create( 'body' ) + var div = $.create( 'div' ) + var container = body ? div : fakeBody + var callback = typeof callback === 'function' ? callback : function() {} + var style, ret, docOverflow + + style = [ '' ].join('') + + container.innerHTML += style + fakeBody.appendChild( div ) + + if ( !body ) { + fakeBody.style.background = '' + fakeBody.style.overflow = 'hidden' + docOverflow = root.style.overflow + + root.style.overflow = 'hidden' + root.appendChild( fakeBody ) + } + + // Callback + ret = callback( container, rule ) + + // Remove the injected scope + $.remove( container ) + if ( !body ) { + root.style.overflow = docOverflow + } + return !!ret + } + + function getStyle( elem, prop ) { + var ret + + if ( window.getComputedStyle ) { + ret = document.defaultView.getComputedStyle( elem, null ).getPropertyValue( prop ) + } else if ( elem.currentStyle ) { + // for IE + ret = elem.currentStyle[ prop ] + } + return ret + } + + return { + columnwidth: testCSSProp( 'columnWidth' ), + + fontface: (function() { + var ret + + injectElementWithStyle( + '@font-face { font-family: font; src: url("//"); }', + function( node, rule ) { + var style = $.qsa( 'style', node )[0] + var sheet = style.sheet || style.styleSheet + var cssText = sheet ? + ( sheet.cssRules && sheet.cssRules[0] ? + sheet.cssRules[0].cssText : sheet.cssText || '' + ) : '' + + ret = /src/i.test( cssText ) && + cssText.indexOf( rule.split(' ')[0] ) === 0 + } + ) + + return ret + })(), + + ruby: (function() { + var ruby = $.create( 'ruby' ) + var rt = $.create( 'rt' ) + var rp = $.create( 'rp' ) + var ret + + ruby.appendChild( rp ) + ruby.appendChild( rt ) + root.appendChild( ruby ) + + // Browsers that support ruby hide the `` via `display: none` + ret = ( + getStyle( rp, 'display' ) === 'none' || + // but in IE, `` has `display: inline`, so the test needs other conditions: + getStyle( ruby, 'display' ) === 'ruby' && + getStyle( rt, 'display' ) === 'ruby-text' + ) ? true : false + + // Remove and clean from memory + root.removeChild( ruby ) + ruby = null + rt = null + rp = null + + return ret + })(), + + 'ruby-display': (function() { + var div = $.create( 'div' ) + + div.innerHTML = '' + return div.querySelector( 'h-test-a' ).style.display === 'ruby' && div.querySelector( 'h-test-b' ).style.display === 'ruby-text-container' + })(), + + 'ruby-interchar': (function() { + var IC = 'inter-character' + var div = $.create( 'div' ) + var css + + div.innerHTML = '' + css = div.querySelector( 'h-test' ).style + return css.rubyPosition === IC || css.WebkitRubyPosition === IC || css.MozRubyPosition === IC || css.msRubyPosition === IC + })(), + + textemphasis: testCSSProp( 'textEmphasis' ), + + // Address feature support test for `unicode-range` via + // detecting whether it's Arial (supported) or + // Times New Roman (not supported). + unicoderange: (function() { + var ret + + injectElementWithStyle( + '@font-face{font-family:test-for-unicode-range;src:local(Arial),local("Droid Sans")}@font-face{font-family:test-for-unicode-range;src:local("Times New Roman"),local(Times),local("Droid Serif");unicode-range:U+270C}', + function() { + ret = !Locale.detectFont( + 'test-for-unicode-range', // treatment group + 'Arial, "Droid Sans"', // control group + 'Q' // ASCII characters only + ) + } + ) + return ret + })(), + + writingmode: testCSSProp( 'writingMode' ) + } +})() + +Locale.initCond = function( target ) { + var target = target || root + var ret = '' + var clazz + + for ( var feature in Locale.support ) { + clazz = ( Locale.support[ feature ] ? '' : 'no-' ) + feature + + target.classList.add( clazz ) + ret += clazz + ' ' + } + return ret +} + +var SUPPORT_IC = Locale.support[ 'ruby-interchar' ] + +// 1. Simple ruby polyfill; +// 2. Inter-character polyfill for Zhuyin +function renderSimpleRuby( $ruby ) { + var frag = $.create( '!' ) + var clazz = $ruby.classList + var $rb, $ru + + frag.appendChild( $.clone( $ruby )) + + $ + .tag( 'rt', frag.firstChild ) + .forEach(function( $rt ) { + var $rb = $.create( '!' ) + var airb = [] + var irb + + // Consider the previous nodes the implied + // ruby base + do { + irb = ( irb || $rt ).previousSibling + if ( !irb || irb.nodeName.match( /((?:h\-)?r[ubt])/i )) break + + $rb.insertBefore( $.clone( irb ), $rb.firstChild ) + airb.push( irb ) + } while ( !irb.nodeName.match( /((?:h\-)?r[ubt])/i )) + + // Create a real `` to append. + $ru = clazz.contains( 'zhuyin' ) ? createZhuyinRu( $rb, $rt ) : createNormalRu( $rb, $rt ) + + // Replace the ruby text with the new ``, + // and remove the original implied ruby base(s) + try { + $rt.parentNode.replaceChild( $ru, $rt ) + airb.map( $.remove ) + } catch ( e ) {} + }) + return createCustomRuby( frag ) +} + +function renderInterCharRuby( $ruby ) { + var frag = $.create( '!' ) + frag.appendChild( $.clone( $ruby )) + + $ + .tag( 'rt', frag.firstChild ) + .forEach(function( $rt ) { + var $rb = $.create( '!' ) + var airb = [] + var irb, $zhuyin + + // Consider the previous nodes the implied + // ruby base + do { + irb = ( irb || $rt ).previousSibling + if ( !irb || irb.nodeName.match( /((?:h\-)?r[ubt])/i )) break + + $rb.insertBefore( $.clone( irb ), $rb.firstChild ) + airb.push( irb ) + } while ( !irb.nodeName.match( /((?:h\-)?r[ubt])/i )) + + $zhuyin = $.create( 'rt' ) + $zhuyin.innerHTML = getZhuyinHTML( $rt ) + $rt.parentNode.replaceChild( $zhuyin, $rt ) + }) + return frag.firstChild +} + +// 3. Complex ruby polyfill +// - Double-lined annotation; +// - Right-angled annotation. +function renderComplexRuby( $ruby ) { + var frag = $.create( '!' ) + var clazz = $ruby.classList + var $cloned, $rb, $ru, maxspan + + frag.appendChild( $.clone( $ruby )) + $cloned = frag.firstChild + + $rb = $ru = $.tag( 'rb', $cloned ) + maxspan = $rb.length + + // First of all, deal with Zhuyin containers + // individually + // + // Note that we only support one single Zhuyin + // container in each complex ruby + void function( $rtc ) { + if ( !$rtc ) return + + $ru = $ + .tag( 'rt', $rtc ) + .map(function( $rt, i ) { + if ( !$rb[ i ] ) return + var ret = createZhuyinRu( $rb[ i ], $rt ) + + try { + $rb[ i ].parentNode.replaceChild( ret, $rb[ i ] ) + } catch ( e ) {} + return ret + }) + + // Remove the container once it's useless + $.remove( $rtc ) + $cloned.setAttribute( 'rightangle', 'true' ) + }( $cloned.querySelector( 'rtc.zhuyin' )) + + // Then, normal annotations other than Zhuyin + $ + .qsa( 'rtc:not(.zhuyin)', $cloned ) + .forEach(function( $rtc, order ) { + var ret + ret = $ + .tag( 'rt', $rtc ) + .map(function( $rt, i ) { + var rbspan = Number( $rt.getAttribute( 'rbspan' ) || 1 ) + var span = 0 + var aRb = [] + var $rb, ret + + if ( rbspan > maxspan ) rbspan = maxspan + + do { + try { + $rb = $ru.shift() + aRb.push( $rb ) + } catch (e) {} + + if ( typeof $rb === 'undefined' ) break + span += Number( $rb.getAttribute( 'span' ) || 1 ) + } while ( rbspan > span ) + + if ( rbspan < span ) { + if ( aRb.length > 1 ) { + console.error( 'An impossible `rbspan` value detected.', ruby ) + return + } + aRb = $.tag( 'rb', aRb[0] ) + $ru = aRb.slice( rbspan ).concat( $ru ) + aRb = aRb.slice( 0, rbspan ) + span = rbspan + } + + ret = createNormalRu( aRb, $rt, { + 'class': clazz, + span: span, + order: order + }) + + try { + aRb[0].parentNode.replaceChild( ret, aRb.shift() ) + aRb.map( $.remove ) + } catch (e) {} + return ret + }) + $ru = ret + if ( order === 1 ) $cloned.setAttribute( 'doubleline', 'true' ) + + // Remove the container once it's useless + $.remove( $rtc ) + }) + return createCustomRuby( frag ) +} + +// Create a new fake `` element so the +// style sheets will render it as a polyfill, +// which also helps to avoid the UA style. +function createCustomRuby( frag ) { + var $ruby = frag.firstChild + var hruby = $.create( 'h-ruby' ) + + hruby.innerHTML = $ruby.innerHTML + $.setAttr( hruby, $ruby.attributes ) + hruby.normalize() + return hruby +} + +function simplifyRubyClass( elem ) { + if ( !elem instanceof Element ) return elem + var clazz = elem.classList + + if ( clazz.contains( 'pinyin' )) clazz.add( 'romanization' ) + else if ( clazz.contains( 'romanization' )) clazz.add( 'annotation' ) + else if ( clazz.contains( 'mps' )) clazz.add( 'zhuyin' ) + else if ( clazz.contains( 'rightangle' )) clazz.add( 'complex' ) + return elem +} + +/** + * Create and return a new `` element + * according to the given contents + */ +function createNormalRu( $rb, $rt, attr ) { + var $ru = $.create( 'h-ru' ) + var $rt = $.clone( $rt ) + var attr = attr || {} + attr.annotation = 'true' + + if ( Array.isArray( $rb )) { + $ru.innerHTML = $rb.map(function( rb ) { + if ( typeof rb === 'undefined' ) return '' + return rb.outerHTML + }).join('') + $rt.outerHTML + } else { + $ru.appendChild( $.clone( $rb )) + $ru.appendChild( $rt ) + } + + $.setAttr( $ru, attr ) + return $ru +} + +/** + * Create and return a new `` element + * in Zhuyin form + */ +function createZhuyinRu( $rb, $rt ) { + var $rb = $.clone( $rb ) + + // Create an element to return + var $ru = $.create( 'h-ru' ) + $ru.setAttribute( 'zhuyin', true ) + + // - + // - + // - + // - + // - + // - + // - + $ru.appendChild( $rb ) + $ru.innerHTML += getZhuyinHTML( $rt ) + return $ru +} + +/** + * Create a Zhuyin-form HTML string + */ +function getZhuyinHTML( rt ) { + // #### Explanation #### + // * `zhuyin`: the entire phonetic annotation + // * `yin`: the plain pronunciation (w/out tone) + // * `diao`: the tone + // * `len`: the length of the plain pronunciation (`yin`) + var zhuyin = typeof rt === 'string' ? rt : rt.textContent + var yin, diao, len + + yin = zhuyin.replace( TYPESET.zhuyin.diao, '' ) + len = yin ? yin.length : 0 + diao = zhuyin + .replace( yin, '' ) + .replace( /[\u02C5]/g, '\u02C7' ) + .replace( /[\u030D]/g, '\u0358' ) + return len === 0 ? '' : '' + yin + '' + diao + '' +} + +/** + * Normalize `ruby` elements + */ +$.extend( Locale, { + + // Address normalisation for both simple and complex + // rubies (interlinear annotations) + renderRuby: function( context, target ) { + var target = target || 'ruby' + var $target = $.qsa( target, context ) + + $.qsa( 'rtc', context ) + .concat( $target ).map( simplifyRubyClass ) + + $target + .forEach(function( $ruby ) { + var clazz = $ruby.classList + var $new + + if ( clazz.contains( 'complex' )) $new = renderComplexRuby( $ruby ) + else if ( clazz.contains( 'zhuyin' )) $new = SUPPORT_IC ? renderInterCharRuby( $ruby ) : renderSimpleRuby( $ruby ) + + // Finally, replace it + if ( $new ) $ruby.parentNode.replaceChild( $new, $ruby ) + }) + }, + + simplifyRubyClass: simplifyRubyClass, + getZhuyinHTML: getZhuyinHTML, + renderComplexRuby: renderComplexRuby, + renderSimpleRuby: renderSimpleRuby, + renderInterCharRuby: renderInterCharRuby + + // ### TODO list ### + // + // * Debug mode + // * Better error-tolerance +}) + +/** + * Normalisation rendering mechanism + */ +$.extend( Locale, { + + // Render and normalise the given context by routine: + // + // ruby -> u, ins -> s, del -> em + // + renderElem: function( context ) { + this.renderRuby( context ) + this.renderDecoLine( context ) + this.renderDecoLine( context, 's, del' ) + this.renderEm( context ) + }, + + // Traverse all target elements and address + // presentational corrections if any two of + // them are adjacent to each other. + renderDecoLine: function( context, target ) { + var $$target = $.qsa( target || 'u, ins', context ) + var i = $$target.length + + traverse: while ( i-- ) { + var $this = $$target[ i ] + var $prev = null + + // Ignore all `` and comments in between, + // and add class `.adjacent` once two targets + // are next to each other. + ignore: do { + $prev = ( $prev || $this ).previousSibling + + if ( !$prev ) { + continue traverse + } else if ( $$target[ i-1 ] === $prev ) { + $this.classList.add( 'adjacent' ) + } + } while ( $.isIgnorable( $prev )) + } + }, + + // Traverse all target elements to render + // emphasis marks. + renderEm: function( context, target ) { + var method = target ? 'qsa' : 'tag' + var target = target || 'em' + var $target = $[ method ]( target, context ) + + $target + .forEach(function( elem ) { + var $elem = Han( elem ) + + if ( Locale.support.textemphasis ) { + $elem + .avoid( 'rt, h-char' ) + .charify({ biaodian: true, punct: true }) + } else { + $elem + .avoid( 'rt, h-char, h-char-group' ) + .jinzify() + .groupify({ western: true }) + .charify({ + hanzi: true, + biaodian: true, + punct: true, + latin: true, + ellinika: true, + kirillica: true + }) + } + }) + } +}) + +Han.normalize = Locale +Han.localize = Locale +Han.support = Locale.support +Han.detectFont = Locale.detectFont + +Han.fn.initCond = function() { + this.condition.classList.add( 'han-js-rendered' ) + Han.normalize.initCond( this.condition ) + return this +} + +void [ + 'Elem', + 'DecoLine', + 'Em', + 'Ruby' +].forEach(function( elem ) { + var method = 'render' + elem + + Han.fn[ method ] = function( target ) { + Han.normalize[ method ]( this.context, target ) + return this + } +}) + +$.extend( Han.support, { + // Assume that all devices support Heiti for we + // use `sans-serif` to do the comparison. + heiti: true, + // 'heiti-gb': true, + + songti: Han.detectFont( '"Han Songti"' ), + 'songti-gb': Han.detectFont( '"Han Songti GB"' ), + + kaiti: Han.detectFont( '"Han Kaiti"' ), + // 'kaiti-gb': Han.detectFont( '"Han Kaiti GB"' ), + + fangsong: Han.detectFont( '"Han Fangsong"' ) + // 'fangsong-gb': Han.detectFont( '"Han Fangsong GB"' ) +}) + +Han.correctBiaodian = function( context ) { + var context = context || document + var finder = Han.find( context ) + + finder + .avoid( 'h-char' ) + .replace( /([‘“])/g, function( portion ) { + var $char = Han.createBDChar( portion.text ) + $char.classList.add( 'bd-open', 'punct' ) + return $char + }) + .replace( /([’”])/g, function( portion ) { + var $char = Han.createBDChar( portion.text ) + $char.classList.add( 'bd-close', 'bd-end', 'punct' ) + return $char + }) + + return Han.support.unicoderange + ? finder + : finder.charify({ biaodian: true }) +} + +Han.correctBasicBD = Han.correctBiaodian +Han.correctBD = Han.correctBiaodian + +$.extend( Han.fn, { + biaodian: null, + + correctBiaodian: function() { + this.biaodian = Han.correctBiaodian( this.context ) + return this + }, + + revertCorrectedBiaodian: function() { + try { + this.biaodian.revert( 'all' ) + } catch (e) {} + return this + } +}) + +// Legacy support (deprecated): +Han.fn.correctBasicBD = Han.fn.correctBiaodian +Han.fn.revertBasicBD = Han.fn.revertCorrectedBiaodian + +var hws = '<>' + +var $hws = $.create( 'h-hws' ) +$hws.setAttribute( 'hidden', '' ) +$hws.innerHTML = ' ' + +function sharingSameParent( $a, $b ) { + return $a && $b && $a.parentNode === $b.parentNode +} + +function properlyPlaceHWSBehind( $node, text ) { + var $elmt = $node + var text = text || '' + + if ( + $.isElmt( $node.nextSibling ) || + sharingSameParent( $node, $node.nextSibling ) + ) { + return text + hws + } else { + // One of the parental elements of the current text + // node would definitely have a next sibling, since + // it is of the first portion and not `isEnd`. + while ( !$elmt.nextSibling ) { + $elmt = $elmt.parentNode + } + if ( $node !== $elmt ) { + $elmt.insertAdjacentHTML( 'afterEnd', '' ) + } + } + return text +} + +function firstStepLabel( portion, mat ) { + return portion.isEnd && portion.index === 0 + ? mat[1] + hws + mat[2] + : portion.index === 0 + ? properlyPlaceHWSBehind( portion.node, portion.text ) + : portion.text +} + +function real$hwsElmt( portion ) { + return portion.index === 0 + ? $.clone( $hws ) + : '' +} + +var last$hwsIdx + +function apostrophe( portion ) { + var $elmt = portion.node.parentNode + + if ( portion.index === 0 ) { + last$hwsIdx = portion.endIndexInNode-2 + } + + if ( + $elmt.nodeName.toLowerCase() === 'h-hws' && ( + portion.index === 1 || portion.indexInMatch === last$hwsIdx + )) { + $elmt.classList.add( 'quote-inner' ) + } + return portion.text +} + +function curveQuote( portion ) { + var $elmt = portion.node.parentNode + + if ( $elmt.nodeName.toLowerCase() === 'h-hws' ) { + $elmt.classList.add( 'quote-outer' ) + } + return portion.text +} + +$.extend( Han, { + renderHWS: function( context, strict ) { + // Elements to be filtered according to the + // HWS rendering mode. + var AVOID = strict + ? 'textarea, code, kbd, samp, pre' + : 'textarea' + + var mode = strict ? 'strict' : 'base' + var context = context || document + var finder = Han.find( context ) + + finder + .avoid( AVOID ) + + // Basic situations: + // - 字a => 字a + // - A字 => A字 + .replace( Han.TYPESET.hws[ mode ][0], firstStepLabel ) + .replace( Han.TYPESET.hws[ mode ][1], firstStepLabel ) + + // Convert text nodes `` into real element nodes: + .replace( new RegExp( '(' + hws + ')+', 'g' ), real$hwsElmt ) + + // Deal with: + // - '' => '字' + // - "" => "字" + .replace( /([\'"])\s(.+?)\s\1/g, apostrophe ) + + // Deal with: + // - “字” + // - ‘字’ + .replace( /\s[‘“]/g, curveQuote ) + .replace( /[’”]\s/g, curveQuote ) + .normalize() + + // Return the finder instance for future usage + return finder + } +}) + +$.extend( Han.fn, { + renderHWS: function( strict ) { + Han.renderHWS( this.context, strict ) + return this + }, + + revertHWS: function() { + $.tag( 'h-hws', this.context ) + .forEach(function( hws ) { + $.remove( hws ) + }) + this.HWS = [] + return this + } +}) + +var HANGABLE_CLASS = 'bd-hangable' +var HANGABLE_AVOID = 'h-char.bd-hangable' +var HANGABLE_CS_HTML = '' + +var matches = Han.find.matches + +function detectSpaceFont() { + var div = $.create( 'div' ) + var ret + + div.innerHTML = 'a ba b' + body.appendChild( div ) + ret = div.firstChild.offsetWidth !== div.lastChild.offsetWidth + $.remove( div ) + return ret +} + +function insertHangableCS( $jinze ) { + var $cs = $jinze.nextSibling + + if ( $cs && matches( $cs, 'h-cs.jinze-outer' )) { + $cs.classList.add( 'hangable-outer' ) + } else { + $jinze.insertAdjacentHTML( + 'afterend', + HANGABLE_CS_HTML + ) + } +} + +Han.support['han-space'] = detectSpaceFont() + +$.extend( Han, { + detectSpaceFont: detectSpaceFont, + isSpaceFontLoaded: detectSpaceFont(), + + renderHanging: function( context ) { + var context = context || document + var finder = Han.find( context ) + + finder + .avoid( 'textarea, code, kbd, samp, pre' ) + .avoid( HANGABLE_AVOID ) + .replace( + TYPESET.jinze.hanging, + function( portion ) { + if ( /^[\x20\t\r\n\f]+$/.test( portion.text )) { + return '' + } + + var $elmt = portion.node.parentNode + var $jinze, $new, $bd, biaodian + + if ( $jinze = $.parent( $elmt, 'h-jinze' )) { + insertHangableCS( $jinze ) + } + + biaodian = portion.text.trim() + + $new = Han.createBDChar( biaodian ) + $new.innerHTML = '' + biaodian + '' + $new.classList.add( HANGABLE_CLASS ) + + $bd = $.parent( $elmt, 'h-char.biaodian' ) + + return !$bd + ? $new + : (function() { + $bd.classList.add( HANGABLE_CLASS ) + + return matches( $elmt, 'h-inner, h-inner *' ) + ? biaodian + : $new.firstChild + })() + } + ) + return finder + } +}) + +$.extend( Han.fn, { + renderHanging: function() { + var classList = this.condition.classList + Han.isSpaceFontLoaded = detectSpaceFont() + + if ( + Han.isSpaceFontLoaded && + classList.contains( 'no-han-space' ) + ) { + classList.remove( 'no-han-space' ) + classList.add( 'han-space' ) + } + + Han.renderHanging( this.context ) + return this + }, + + revertHanging: function() { + $.qsa( + 'h-char.bd-hangable, h-cs.hangable-outer', + this.context + ).forEach(function( $elmt ) { + var classList = $elmt.classList + classList.remove( 'bd-hangable' ) + classList.remove( 'hangable-outer' ) + }) + return this + } +}) + +var JIYA_CLASS = 'bd-jiya' +var JIYA_AVOID = 'h-char.bd-jiya' +var CONSECUTIVE_CLASS = 'bd-consecutive' +var JIYA_CS_HTML = '' + +var matches = Han.find.matches + +function trimBDClass( clazz ) { + return clazz.replace( + /(biaodian|cjk|bd-jiya|bd-consecutive|bd-hangable)/gi, '' + ).trim() +} + +function charifyBiaodian( portion ) { + var biaodian = portion.text + var $elmt = portion.node.parentNode + var $bd = $.parent( $elmt, 'h-char.biaodian' ) + var $new = Han.createBDChar( biaodian ) + var $jinze + + $new.innerHTML = '' + biaodian + '' + $new.classList.add( JIYA_CLASS ) + + if ( $jinze = $.parent( $elmt, 'h-jinze' )) { + insertJiyaCS( $jinze ) + } + + return !$bd + ? $new + : (function() { + $bd.classList.add( JIYA_CLASS ) + + return matches( $elmt, 'h-inner, h-inner *' ) + ? biaodian + : $new.firstChild + })() +} + +var prevBDType, $$prevCS + +function locateConsecutiveBD( portion ) { + var prev = prevBDType + var $elmt = portion.node.parentNode + var $bd = $.parent( $elmt, 'h-char.biaodian' ) + var $jinze = $.parent( $bd, 'h-jinze' ) + var classList + + classList = $bd.classList + + if ( prev ) { + $bd.setAttribute( 'prev', prev ) + } + + if ( $$prevCS && classList.contains( 'bd-open' )) { + $$prevCS.pop().setAttribute( 'next', 'bd-open' ) + } + + $$prevCS = undefined + + if ( portion.isEnd ) { + prevBDType = undefined + classList.add( CONSECUTIVE_CLASS, 'end-portion' ) + } else { + prevBDType = trimBDClass($bd.getAttribute( 'class' )) + classList.add( CONSECUTIVE_CLASS ) + } + + if ( $jinze ) { + $$prevCS = locateCS( $jinze, { + prev: prev, + 'class': trimBDClass($bd.getAttribute( 'class' )) + }) + } + return portion.text +} + +function insertJiyaCS( $jinze ) { + if ( + matches( $jinze, '.tou, .touwei' ) && + !matches( $jinze.previousSibling, 'h-cs.jiya-outer' ) + ) { + $jinze.insertAdjacentHTML( 'beforebegin', JIYA_CS_HTML ) + } + if ( + matches( $jinze, '.wei, .touwei' ) && + !matches( $jinze.nextSibling, 'h-cs.jiya-outer' ) + ) { + $jinze.insertAdjacentHTML( 'afterend', JIYA_CS_HTML ) + } +} + +function locateCS( $jinze, attr ) { + var $prev, $next + + if (matches( $jinze, '.tou, .touwei' )) { + $prev = $jinze.previousSibling + + if (matches( $prev, 'h-cs' )) { + $prev.className = 'jinze-outer jiya-outer' + $prev.setAttribute( 'prev', attr.prev ) + } + } + if (matches( $jinze, '.wei, .touwei' )) { + $next = $jinze.nextSibling + + if (matches( $next, 'h-cs' )) { + $next.className = 'jinze-outer jiya-outer ' + attr[ 'class' ] + $next.removeAttribute( 'prev' ) + } + } + return [ $prev, $next ] +} + +Han.renderJiya = function( context ) { + var context = context || document + var finder = Han.find( context ) + + finder + .avoid( 'textarea, code, kbd, samp, pre, h-cs' ) + + .avoid( JIYA_AVOID ) + .charify({ + avoid: false, + biaodian: charifyBiaodian + }) + // End avoiding `JIYA_AVOID`: + .endAvoid() + + .avoid( 'textarea, code, kbd, samp, pre, h-cs' ) + .replace( TYPESET.group.biaodian[0], locateConsecutiveBD ) + .replace( TYPESET.group.biaodian[1], locateConsecutiveBD ) + + return finder +} + +$.extend( Han.fn, { + renderJiya: function() { + Han.renderJiya( this.context ) + return this + }, + + revertJiya: function() { + $.qsa( + 'h-char.bd-jiya, h-cs.jiya-outer', + this.context + ).forEach(function( $elmt ) { + var classList = $elmt.classList + classList.remove( 'bd-jiya' ) + classList.remove( 'jiya-outer' ) + }) + return this + } +}) + +var QUERY_RU_W_ANNO = 'h-ru[annotation]' +var SELECTOR_TO_IGNORE = 'textarea, code, kbd, samp, pre' + +function createCompareFactory( font, treat, control ) { + return function() { + var a = Han.localize.writeOnCanvas( treat, font ) + var b = Han.localize.writeOnCanvas( control, font ) + return Han.localize.compareCanvases( a, b ) + } +} + +function isVowelCombLigaNormal() { + return createCompareFactory( '"Romanization Sans"', '\u0061\u030D', '\uDB80\uDC61' ) +} + +function isVowelICombLigaNormal() { + return createCompareFactory( '"Romanization Sans"', '\u0069\u030D', '\uDB80\uDC69' ) +} + +function isZhuyinCombLigaNormal() { + return createCompareFactory( '"Zhuyin Kaiti"', '\u31B4\u0358', '\uDB8C\uDDB4' ) +} + +function createSubstFactory( regexToSubst ) { + return function( context ) { + var context = context || document + var finder = Han.find( context ).avoid( SELECTOR_TO_IGNORE ) + + regexToSubst + .forEach(function( pattern ) { + finder + .replace( + new RegExp( pattern[ 0 ], 'ig' ), + function( portion, match ) { + var ret = $.clone( charCombLiga ) + + // Put the original content in an inner container + // for better presentational effect of hidden text + ret.innerHTML = '' + match[0] + '' + ret.setAttribute( 'display-as', pattern[ 1 ] ) + return portion.index === 0 ? ret : '' + } + ) + }) + return finder + } +} + +var charCombLiga = $.create( 'h-char', 'comb-liga' ) + +$.extend( Han, { + isVowelCombLigaNormal: isVowelCombLigaNormal(), + isVowelICombLigaNormal: isVowelICombLigaNormal(), + isZhuyinCombLigaNormal: isZhuyinCombLigaNormal(), + + isCombLigaNormal: isVowelICombLigaNormal()(), // ### Deprecated + + substVowelCombLiga: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-vowel' ] ), + substZhuyinCombLiga: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-zhuyin' ] ), + substCombLigaWithPUA: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-pua' ] ), + + substInaccurateChar: function( context ) { + var context = context || document + var finder = Han.find( context ) + + finder.avoid( SELECTOR_TO_IGNORE ) + + Han.TYPESET[ 'inaccurate-char' ] + .forEach(function( pattern ) { + finder + .replace( + new RegExp( pattern[ 0 ], 'ig' ), + pattern[ 1 ] + ) + }) + } +}) + +$.extend( Han.fn, { + 'comb-liga-vowel': null, + 'comb-liga-vowel-i': null, + 'comb-liga-zhuyin': null, + 'inaccurate-char': null, + + substVowelCombLiga: function() { + this['comb-liga-vowel'] = Han.substVowelCombLiga( this.context ) + return this + }, + + substVowelICombLiga: function() { + this['comb-liga-vowel-i'] = Han.substVowelICombLiga( this.context ) + return this + }, + + substZhuyinCombLiga: function() { + this['comb-liga-zhuyin'] = Han.substZhuyinCombLiga( this.context ) + return this + }, + + substCombLigaWithPUA: function() { + if ( !Han.isVowelCombLigaNormal()) { + this['comb-liga-vowel'] = Han.substVowelCombLiga( this.context ) + } else if ( !Han.isVowelICombLigaNormal()) { + this['comb-liga-vowel-i'] = Han.substVowelICombLiga( this.context ) + } + + if ( !Han.isZhuyinCombLigaNormal()) { + this['comb-liga-zhuyin'] = Han.substZhuyinCombLiga( this.context ) + } + return this + }, + + revertVowelCombLiga: function() { + try { + this['comb-liga-vowel'].revert( 'all' ) + } catch (e) {} + return this + }, + + revertVowelICombLiga: function() { + try { + this['comb-liga-vowel-i'].revert( 'all' ) + } catch (e) {} + return this + }, + + revertZhuyinCombLiga: function() { + try { + this['comb-liga-zhuyin'].revert( 'all' ) + } catch (e) {} + return this + }, + + revertCombLigaWithPUA: function() { + try { + this['comb-liga-vowel'].revert( 'all' ) + this['comb-liga-vowel-i'].revert( 'all' ) + this['comb-liga-zhuyin'].revert( 'all' ) + } catch (e) {} + return this + }, + + substInaccurateChar: function() { + this['inaccurate-char'] = Han.substInaccurateChar( this.context ) + return this + }, + + revertInaccurateChar: function() { + try { + this['inaccurate-char'].revert( 'all' ) + } catch (e) {} + return this + } +}) + +window.addEventListener( 'DOMContentLoaded', function() { + var initContext + + // Use the shortcut under the default situation + if ( root.classList.contains( 'han-init' )) { + Han.init() + + // Consider ‘a configured context’ the special + // case of the default situation. Will have to + // replace the `Han.init` with the instance as + // well (for future usage). + } else if ( initContext = document.querySelector( '.han-init-context' )) { + Han.init = Han( initContext ).render() + } +}) + +// Expose to global namespace +if ( typeof noGlobalNS === 'undefined' || noGlobalNS === false ) { + window.Han = Han +} + +return Han +}); + diff --git a/lib/Han/dist/han.min.css b/lib/Han/dist/han.min.css new file mode 100644 index 0000000..29c753e --- /dev/null +++ b/lib/Han/dist/han.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8"; + +/*! 漢字標準格式 v3.3.0 | MIT License | css.hanzi.co */ +/*! Han.css: the CSS typography framework optimised for Hanzi */ + +progress,sub,sup{vertical-align:baseline}button,hr,input,select{overflow:visible}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{cursor:pointer}[disabled]{cursor:default}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button:-moz-focusring,input:-moz-focusring{outline:ButtonText dotted 1px}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:"Han Heiti";src:local("Hiragino Sans GB"),local("Lantinghei TC Extralight"),local("Lantinghei SC Extralight"),local(FZLTXHB--B51-0),local(FZLTZHK--GBK1-0),local("Pingfang SC Light"),local("Pingfang TC Light"),local("Pingfang-SC-Light"),local("Pingfang-TC-Light"),local("Pingfang SC"),local("Pingfang TC"),local("Heiti SC Light"),local(STHeitiSC-Light),local("Heiti SC"),local("Heiti TC Light"),local(STHeitiTC-Light),local("Heiti TC"),local("Microsoft Yahei"),local("Microsoft Jhenghei"),local("Noto Sans CJK KR"),local("Noto Sans CJK JP"),local("Noto Sans CJK SC"),local("Noto Sans CJK TC"),local("Source Han Sans K"),local("Source Han Sans KR"),local("Source Han Sans JP"),local("Source Han Sans CN"),local("Source Han Sans HK"),local("Source Han Sans TW"),local("Source Han Sans TWHK"),local("Droid Sans Fallback")}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Heiti";src:local(YuGothic),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro")}@font-face{font-family:"Han Heiti CNS";src:local("Pingfang TC Light"),local("Pingfang-TC-Light"),local("Pingfang TC"),local("Heiti TC Light"),local(STHeitiTC-Light),local("Heiti TC"),local("Lantinghei TC Extralight"),local(FZLTXHB--B51-0),local("Lantinghei TC"),local("Microsoft Jhenghei"),local("Microsoft Yahei"),local("Noto Sans CJK TC"),local("Source Han Sans TC"),local("Source Han Sans TW"),local("Source Han Sans TWHK"),local("Source Han Sans HK"),local("Droid Sans Fallback")}@font-face{font-family:"Han Heiti GB";src:local("Hiragino Sans GB"),local("Pingfang SC Light"),local("Pingfang-SC-Light"),local("Pingfang SC"),local("Lantinghei SC Extralight"),local(FZLTXHK--GBK1-0),local("Lantinghei SC"),local("Heiti SC Light"),local(STHeitiSC-Light),local("Heiti SC"),local("Microsoft Yahei"),local("Noto Sans CJK SC"),local("Source Han Sans SC"),local("Source Han Sans CN"),local("Droid Sans Fallback")}@font-face{font-family:"Han Heiti";font-weight:600;src:local("Hiragino Sans GB W6"),local(HiraginoSansGB-W6),local("Lantinghei TC Demibold"),local("Lantinghei SC Demibold"),local(FZLTZHB--B51-0),local(FZLTZHK--GBK1-0),local("Pingfang-SC-Semibold"),local("Pingfang-TC-Semibold"),local("Heiti SC Medium"),local("STHeitiSC-Medium"),local("Heiti SC"),local("Heiti TC Medium"),local("STHeitiTC-Medium"),local("Heiti TC"),local("Microsoft Yahei Bold"),local("Microsoft Jhenghei Bold"),local(MicrosoftYahei-Bold),local(MicrosoftJhengHeiBold),local("Microsoft Yahei"),local("Microsoft Jhenghei"),local("Noto Sans CJK KR Bold"),local("Noto Sans CJK JP Bold"),local("Noto Sans CJK SC Bold"),local("Noto Sans CJK TC Bold"),local(NotoSansCJKkr-Bold),local(NotoSansCJKjp-Bold),local(NotoSansCJKsc-Bold),local(NotoSansCJKtc-Bold),local("Source Han Sans K Bold"),local(SourceHanSansK-Bold),local("Source Han Sans K"),local("Source Han Sans KR Bold"),local("Source Han Sans JP Bold"),local("Source Han Sans CN Bold"),local("Source Han Sans HK Bold"),local("Source Han Sans TW Bold"),local("Source Han Sans TWHK Bold"),local("SourceHanSansKR-Bold"),local("SourceHanSansJP-Bold"),local("SourceHanSansCN-Bold"),local("SourceHanSansHK-Bold"),local("SourceHanSansTW-Bold"),local("SourceHanSansTWHK-Bold"),local("Source Han Sans KR"),local("Source Han Sans CN"),local("Source Han Sans HK"),local("Source Han Sans TW"),local("Source Han Sans TWHK")}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Heiti";font-weight:600;src:local("YuGothic Bold"),local("Hiragino Kaku Gothic ProN W6"),local("Hiragino Kaku Gothic Pro W6"),local(YuGo-Bold),local(HiraKakuProN-W6),local(HiraKakuPro-W6)}@font-face{font-family:"Han Heiti CNS";font-weight:600;src:local("Pingfang TC Semibold"),local("Pingfang-TC-Semibold"),local("Heiti TC Medium"),local("STHeitiTC-Medium"),local("Heiti TC"),local("Lantinghei TC Demibold"),local(FZLTXHB--B51-0),local("Microsoft Jhenghei Bold"),local(MicrosoftJhengHeiBold),local("Microsoft Jhenghei"),local("Microsoft Yahei Bold"),local(MicrosoftYahei-Bold),local("Noto Sans CJK TC Bold"),local(NotoSansCJKtc-Bold),local("Noto Sans CJK TC"),local("Source Han Sans TC Bold"),local("SourceHanSansTC-Bold"),local("Source Han Sans TC"),local("Source Han Sans TW Bold"),local("SourceHanSans-TW"),local("Source Han Sans TW"),local("Source Han Sans TWHK Bold"),local("SourceHanSans-TWHK"),local("Source Han Sans TWHK"),local("Source Han Sans HK"),local("SourceHanSans-HK"),local("Source Han Sans HK")}@font-face{font-family:"Han Heiti GB";font-weight:600;src:local("Hiragino Sans GB W6"),local(HiraginoSansGB-W6),local("Pingfang SC Semibold"),local("Pingfang-SC-Semibold"),local("Lantinghei SC Demibold"),local(FZLTZHK--GBK1-0),local("Heiti SC Medium"),local("STHeitiSC-Medium"),local("Heiti SC"),local("Microsoft Yahei Bold"),local(MicrosoftYahei-Bold),local("Microsoft Yahei"),local("Noto Sans CJK SC Bold"),local(NotoSansCJKsc-Bold),local("Noto Sans CJK SC"),local("Source Han Sans SC Bold"),local("SourceHanSansSC-Bold"),local("Source Han Sans CN Bold"),local("SourceHanSansCN-Bold"),local("Source Han Sans SC"),local("Source Han Sans CN")}@font-face{font-family:"Han Songti";src:local("Songti SC Regular"),local(STSongti-SC-Regular),local("Songti SC"),local("Songti TC Regular"),local(STSongti-TC-Regular),local("Songti TC"),local(STSong),local("Lisong Pro"),local(SimSun),local(PMingLiU)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Songti";src:local(YuMincho),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("MS Mincho")}@font-face{font-family:"Han Songti CNS";src:local("Songti TC Regular"),local(STSongti-TC-Regular),local("Songti TC"),local("Lisong Pro"),local("Songti SC Regular"),local(STSongti-SC-Regular),local("Songti SC"),local(STSong),local(PMingLiU),local(SimSun)}@font-face{font-family:"Han Songti GB";src:local("Songti SC Regular"),local(STSongti-SC-Regular),local("Songti SC"),local(STSong),local(SimSun),local(PMingLiU)}@font-face{font-family:"Han Songti";font-weight:600;src:local("STSongti SC Bold"),local("STSongti TC Bold"),local(STSongti-SC-Bold),local(STSongti-TC-Bold),local("STSongti SC"),local("STSongti TC")}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Songti";font-weight:600;src:local("YuMincho Demibold"),local("Hiragino Mincho ProN W6"),local("Hiragino Mincho Pro W6"),local(YuMin-Demibold),local(HiraMinProN-W6),local(HiraMinPro-W6),local(YuMincho),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro")}@font-face{font-family:"Han Songti CNS";font-weight:600;src:local("STSongti TC Bold"),local("STSongti SC Bold"),local(STSongti-TC-Bold),local(STSongti-SC-Bold),local("STSongti TC"),local("STSongti SC")}@font-face{font-family:"Han Songti GB";font-weight:600;src:local("STSongti SC Bold"),local(STSongti-SC-Bold),local("STSongti SC")}@font-face{font-family:cursive;src:local("Kaiti TC Regular"),local(STKaiTi-TC-Regular),local("Kaiti TC"),local("Kaiti SC"),local(STKaiti),local(BiauKai),local("標楷體"),local(DFKaiShu-SB-Estd-BF),local(Kaiti),local(DFKai-SB)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Kaiti";src:local("Kaiti TC Regular"),local(STKaiTi-TC-Regular),local("Kaiti TC"),local("Kaiti SC"),local(STKaiti),local(BiauKai),local("標楷體"),local(DFKaiShu-SB-Estd-BF),local(Kaiti),local(DFKai-SB)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Kaiti CNS";src:local(BiauKai),local("標楷體"),local(DFKaiShu-SB-Estd-BF),local("Kaiti TC Regular"),local(STKaiTi-TC-Regular),local("Kaiti TC")}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Kaiti GB";src:local("Kaiti SC Regular"),local(STKaiTi-SC-Regular),local("Kaiti SC"),local(STKaiti),local(Kai),local(Kaiti),local(DFKai-SB)}@font-face{font-family:cursive;font-weight:600;src:local("Kaiti TC Bold"),local(STKaiTi-TC-Bold),local("Kaiti SC Bold"),local(STKaiti-SC-Bold),local("Kaiti TC"),local("Kaiti SC")}@font-face{font-family:"Han Kaiti";font-weight:600;src:local("Kaiti TC Bold"),local(STKaiTi-TC-Bold),local("Kaiti SC Bold"),local(STKaiti-SC-Bold),local("Kaiti TC"),local("Kaiti SC")}@font-face{font-family:"Han Kaiti CNS";font-weight:600;src:local("Kaiti TC Bold"),local(STKaiTi-TC-Bold),local("Kaiti TC")}@font-face{font-family:"Han Kaiti GB";font-weight:600;src:local("Kaiti SC Bold"),local(STKaiti-SC-Bold)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Fangsong";src:local(STFangsong),local(FangSong)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Fangsong CNS";src:local(STFangsong),local(FangSong)}@font-face{unicode-range:U+4E00-9FFF,U+3400-4DB5,U+20000-2A6D6,U+2A700-2B734,U+2B740-2B81D,U+FA0E-FA0F,U+FA11,U+FA13-FA14,U+FA1F,U+FA21,U+FA23,U+FA24,U+FA27-FA29,U+3040-309F,U+30A0-30FF,U+3099-309E,U+FF66-FF9F,U+3007,U+31C0-31E3,U+2F00-2FD5,U+2E80-2EF3;font-family:"Han Fangsong GB";src:local(STFangsong),local(FangSong)}@font-face{font-family:"Biaodian Sans";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local("MS Gothic"),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Serif";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Sans";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local("MS Gothic"),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Serif";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local("MS Gothic"),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Serif CNS";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Sans GB";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local("MS Gothic"),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local(SimSun);unicode-range:U+FF0E}@font-face{font-family:"Biaodian Sans";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Serif";src:local("Songti SC"),local(STSong),local("Heiti SC"),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Sans";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Serif";src:local("Songti SC"),local(STSong),local("Heiti SC"),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Serif CNS";src:local("Songti SC"),local(STSong),local("Heiti SC"),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Sans GB";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Songti SC"),local(STSong),local("Heiti SC"),local(SimSun);unicode-range:U+00B7}@font-face{font-family:"Biaodian Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Yakumono Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Arial Unicode MS"),local("MS Gothic");unicode-range:U+2014}@font-face{font-family:"Yakumono Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("MS Mincho"),local("Microsoft Yahei");unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Sans CNS";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Serif CNS";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Sans GB";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Pro Serif GB";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSong),local("Microsoft Yahei"),local(SimSun);unicode-range:U+2014}@font-face{font-family:"Biaodian Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(Meiryo),local("MS Gothic"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local("MS Mincho"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Yakumono Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(Meiryo),local("MS Gothic");unicode-range:U+2026}@font-face{font-family:"Yakumono Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("MS Mincho");unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Sans";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Serif";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Sans CNS";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Serif CNS";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSongti),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Sans GB";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Sans GB"),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Serif GB";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype"),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Songti SC"),local(STSongti),local(SimSun),local(PMingLiU);unicode-range:U+2026}@font-face{font-family:"Biaodian Pro Sans GB";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun),local(PMingLiU);unicode-range:U+201C-201D,U+2018-2019}@font-face{font-family:"Biaodian Pro Sans GB";font-weight:700;src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun),local(PMingLiU);unicode-range:U+201C-201D,U+2018-2019}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Lisong Pro"),local("Heiti SC"),local(STHeiti),local(SimSun),local(PMingLiU);unicode-range:U+201C-201D,U+2018-2019}@font-face{font-family:"Biaodian Pro Serif GB";font-weight:700;src:local("Lisong Pro"),local("Heiti SC"),local(STHeiti),local(SimSun),local(PMingLiU);unicode-range:U+201C-201D,U+2018-2019}@font-face{font-family:"Biaodian Sans";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Serif";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Sans";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Serif";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Sans CNS";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Serif CNS";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Sans GB";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Serif GB";src:local(Georgia),local("Times New Roman"),local(Arial),local("Droid Sans Fallback");unicode-range:U+25CF}@font-face{font-family:"Biaodian Pro Sans";src:local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("MS Gothic");unicode-range:U+3002,U+FF0C,U+3001,U+FF1B,U+FF1A,U+FF1F,U+FF01,U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Serif";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("MS Mincho");unicode-range:U+3002,U+FF0C,U+3001,U+FF1B,U+FF1A,U+FF1F,U+FF01,U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Heiti TC"),local("Lihei Pro"),local("Microsoft Jhenghei"),local(PMingLiU);unicode-range:U+3002,U+FF0C,U+3001}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Heiti TC"),local("Lihei Pro"),local("Microsoft Jhenghei"),local(PMingLiU),local("MS Gothic");unicode-range:U+FF1B,U+FF1A,U+FF1F,U+FF01}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("MS Mincho");unicode-range:U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Serif CNS";src:local(STSongti-TC-Regular),local("Lisong Pro"),local("Heiti TC"),local(PMingLiU);unicode-range:U+3002,U+FF0C,U+3001}@font-face{font-family:"Biaodian Pro Serif CNS";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local(PMingLiU),local("MS Mincho");unicode-range:U+FF1B,U+FF1A,U+FF1F,U+FF01,U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Sans GB";src:local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local(SimSun),local("MS Gothic");unicode-range:U+3002,U+FF0C,U+3001,U+FF1B,U+FF1A,U+FF1F,U+FF01,U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Songti SC"),local(STSongti),local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Hiragino Sans GB"),local("Heiti SC"),local(STHeiti),local(SimSun),local("MS Mincho");unicode-range:U+3002,U+FF0C,U+3001,U+FF1B,U+FF1A,U+FF1F,U+FF01}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local(PMingLiU),local("MS Mincho");unicode-range:U+FF0D,U+FF0F,U+FF3C}@font-face{font-family:"Biaodian Pro Sans";src:local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Yu Gothic"),local(YuGothic),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Pro Serif";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Yu Mincho"),local(YuMincho),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Pro Sans CNS";src:local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Yu Gothic"),local(YuGothic),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Pro Serif CNS";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Yu Mincho"),local(YuMincho),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Pro Sans GB";src:local("Hiragino Kaku Gothic ProN"),local("Hiragino Kaku Gothic Pro"),local("Yu Gothic"),local(YuGothic),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Pro Serif GB";src:local("Hiragino Mincho ProN"),local("Hiragino Mincho Pro"),local("Yu Mincho"),local(YuMincho),local(SimSun),local(PMingLiU);unicode-range:U+300C-300F,U+300A-300B,U+3008-3009,U+FF08-FF09,U+3014-3015}@font-face{font-family:"Biaodian Basic";src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Basic";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Sans";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Sans";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Sans";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Sans CNS";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Sans GB";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Serif";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Serif CNS";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Biaodian Pro Serif GB";font-weight:700;src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+2014,U+2026,U+00B7}@font-face{font-family:"Latin Italic Serif";src:local("Georgia Italic"),local("Times New Roman Italic"),local(Georgia-Italic),local(TimesNewRomanPS-ItalicMT),local(Times-Italic)}@font-face{font-family:"Latin Italic Serif";font-weight:700;src:local("Georgia Bold Italic"),local("Times New Roman Bold Italic"),local(Georgia-BoldItalic),local(TimesNewRomanPS-BoldItalicMT),local(Times-Italic)}@font-face{font-family:"Latin Italic Sans";src:local("Helvetica Neue Italic"),local("Helvetica Oblique"),local("Arial Italic"),local(HelveticaNeue-Italic),local(Helvetica-LightOblique),local(Arial-ItalicMT)}@font-face{font-family:"Latin Italic Sans";font-weight:700;src:local("Helvetica Neue Bold Italic"),local("Helvetica Bold Oblique"),local("Arial Bold Italic"),local(HelveticaNeue-BoldItalic),local(Helvetica-BoldOblique),local(Arial-BoldItalicMT)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral TF Sans";src:local(Skia),local("Neutraface 2 Text"),local(Candara),local(Corbel)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral TF Serif";src:local(Georgia),local("Hoefler Text"),local("Big Caslon")}@font-face{unicode-range:U+0030-0039;font-family:"Numeral TF Italic Serif";src:local("Georgia Italic"),local("Hoefler Text Italic"),local(Georgia-Italic),local(HoeflerText-Italic)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Sans";src:local("Helvetica Neue"),local(Helvetica),local(Arial)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Italic Sans";src:local("Helvetica Neue Italic"),local("Helvetica Oblique"),local("Arial Italic"),local(HelveticaNeue-Italic),local(Helvetica-LightOblique),local(Arial-ItalicMT)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Italic Sans";font-weight:700;src:local("Helvetica Neue Bold Italic"),local("Helvetica Bold Oblique"),local("Arial Bold Italic"),local(HelveticaNeue-BoldItalic),local(Helvetica-BoldOblique),local(Arial-BoldItalicMT)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Serif";src:local(Palatino),local("Palatino Linotype"),local("Times New Roman")}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Italic Serif";src:local("Palatino Italic"),local("Palatino Italic Linotype"),local("Times New Roman Italic"),local(Palatino-Italic),local(Palatino-Italic-Linotype),local(TimesNewRomanPS-ItalicMT)}@font-face{unicode-range:U+0030-0039;font-family:"Numeral LF Italic Serif";font-weight:700;src:local("Palatino Bold Italic"),local("Palatino Bold Italic Linotype"),local("Times New Roman Bold Italic"),local(Palatino-BoldItalic),local(Palatino-BoldItalic-Linotype),local(TimesNewRomanPS-BoldItalicMT)}@font-face{src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+3105-312D,U+31A0-31BA,U+02D9,U+02CA,U+02C5,U+02C7,U+02CB,U+02EA-02EB,U+0307,U+030D,U+0358,U+F31B4-F31B7,U+F0061,U+F0065,U+F0069,U+F006F,U+F0075;font-family:"Zhuyin Kaiti"}@font-face{unicode-range:U+3105-312D,U+31A0-31BA,U+02D9,U+02CA,U+02C5,U+02C7,U+02CB,U+02EA-02EB,U+0307,U+030D,U+0358,U+F31B4-F31B7,U+F0061,U+F0065,U+F0069,U+F006F,U+F0075;font-family:"Zhuyin Heiti";src:local("Hiragino Sans GB"),local("Heiti TC"),local("Microsoft Jhenghei"),url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype")}@font-face{font-family:"Zhuyin Heiti";src:local("Heiti TC"),local("Microsoft Jhenghei"),url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");unicode-range:U+3127}@font-face{src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");font-family:"Zhuyin Heiti";unicode-range:U+02D9,U+02CA,U+02C5,U+02C7,U+02CB,U+02EA-02EB,U+31B4,U+31B5,U+31B6,U+31B7,U+0307,U+030D,U+0358,U+F31B4-F31B7,U+F0061,U+F0065,U+F0069,U+F006F,U+F0075}@font-face{src:url(./font/han.woff2?v3.3.0) format("woff2"),url(./font/han.woff?v3.3.0) format("woff"),url(./font/han.otf?v3.3.0) format("opentype");font-family:"Romanization Sans";unicode-range:U+0307,U+030D,U+0358,U+F31B4-F31B7,U+F0061,U+F0065,U+F0069,U+F006F,U+F0075}article strong :lang(ja-Latn),article strong :lang(zh-Latn),article strong :not(:lang(zh)):not(:lang(ja)),article strong:lang(ja-Latn),article strong:lang(zh-Latn),article strong:not(:lang(zh)):not(:lang(ja)),html :lang(ja-Latn),html :lang(zh-Latn),html :not(:lang(zh)):not(:lang(ja)),html:lang(ja-Latn),html:lang(zh-Latn),html:not(:lang(zh)):not(:lang(ja)){font-family:"Helvetica Neue",Helvetica,Arial,"Han Heiti",sans-serif}[lang*=Hant],[lang=zh-TW],[lang=zh-HK],[lang^=zh],article strong:lang(zh),article strong:lang(zh-Hant),html:lang(zh),html:lang(zh-Hant){font-family:"Biaodian Pro Sans CNS","Helvetica Neue",Helvetica,Arial,"Zhuyin Heiti","Han Heiti",sans-serif}.no-unicoderange [lang*=Hant],.no-unicoderange [lang=zh-TW],.no-unicoderange [lang=zh-HK],.no-unicoderange [lang^=zh],.no-unicoderange article strong:lang(zh),.no-unicoderange article strong:lang(zh-Hant),html:lang(zh).no-unicoderange,html:lang(zh-Hant).no-unicoderange{font-family:"Helvetica Neue",Helvetica,Arial,"Han Heiti",sans-serif}[lang*=Hans],[lang=zh-CN],article strong:lang(zh-CN),article strong:lang(zh-Hans),html:lang(zh-CN),html:lang(zh-Hans){font-family:"Biaodian Pro Sans GB","Helvetica Neue",Helvetica,Arial,"Han Heiti GB",sans-serif}.no-unicoderange [lang*=Hans],.no-unicoderange [lang=zh-CN],.no-unicoderange article strong:lang(zh-CN),.no-unicoderange article strong:lang(zh-Hans),html:lang(zh-CN).no-unicoderange,html:lang(zh-Hans).no-unicoderange{font-family:"Helvetica Neue",Helvetica,Arial,"Han Heiti GB",sans-serif}[lang^=ja],article strong:lang(ja),html:lang(ja){font-family:"Yakumono Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.no-unicoderange [lang^=ja],.no-unicoderange article strong:lang(ja),html:lang(ja).no-unicoderange{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}article blockquote i :lang(ja-Latn),article blockquote i :lang(zh-Latn),article blockquote i :not(:lang(zh)):not(:lang(ja)),article blockquote i:lang(ja-Latn),article blockquote i:lang(zh-Latn),article blockquote i:not(:lang(zh)):not(:lang(ja)),article blockquote var :lang(ja-Latn),article blockquote var :lang(zh-Latn),article blockquote var :not(:lang(zh)):not(:lang(ja)),article blockquote var:lang(ja-Latn),article blockquote var:lang(zh-Latn),article blockquote var:not(:lang(zh)):not(:lang(ja)){font-family:"Latin Italic Sans","Helvetica Neue",Helvetica,Arial,"Han Heiti",sans-serif}article blockquote i:lang(zh),article blockquote i:lang(zh-Hant),article blockquote var:lang(zh),article blockquote var:lang(zh-Hant){font-family:"Biaodian Pro Sans CNS","Latin Italic Sans","Helvetica Neue",Helvetica,Arial,"Zhuyin Heiti","Han Heiti",sans-serif}.no-unicoderange article blockquote i:lang(zh),.no-unicoderange article blockquote i:lang(zh-Hant),.no-unicoderange article blockquote var:lang(zh),.no-unicoderange article blockquote var:lang(zh-Hant){font-family:"Latin Italic Sans","Helvetica Neue",Helvetica,Arial,"Han Heiti",sans-serif}article blockquote i:lang(zh-CN),article blockquote i:lang(zh-Hans),article blockquote var:lang(zh-CN),article blockquote var:lang(zh-Hans){font-family:"Biaodian Pro Sans GB","Latin Italic Sans","Helvetica Neue",Helvetica,Arial,"Han Heiti GB",sans-serif}.no-unicoderange article blockquote i:lang(zh-CN),.no-unicoderange article blockquote i:lang(zh-Hans),.no-unicoderange article blockquote var:lang(zh-CN),.no-unicoderange article blockquote var:lang(zh-Hans){font-family:"Latin Italic Sans","Helvetica Neue",Helvetica,Arial,"Han Heiti GB",sans-serif}article blockquote i:lang(ja),article blockquote var:lang(ja){font-family:"Yakumono Sans","Latin Italic Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.no-unicoderange article blockquote i:lang(ja),.no-unicoderange article blockquote var:lang(ja){font-family:"Latin Italic Sans","Helvetica Neue",Helvetica,Arial,sans-serif}article figure blockquote :lang(ja-Latn),article figure blockquote :lang(zh-Latn),article figure blockquote :not(:lang(zh)):not(:lang(ja)),article figure blockquote:lang(ja-Latn),article figure blockquote:lang(zh-Latn),article figure blockquote:not(:lang(zh)):not(:lang(ja)){font-family:Georgia,"Times New Roman","Han Songti",cursive,serif}article figure blockquote:lang(zh),article figure blockquote:lang(zh-Hant){font-family:"Biaodian Pro Serif CNS","Numeral LF Serif",Georgia,"Times New Roman","Zhuyin Kaiti","Han Songti",serif}.no-unicoderange article figure blockquote:lang(zh),.no-unicoderange article figure blockquote:lang(zh-Hant){font-family:"Numeral LF Serif",Georgia,"Times New Roman","Han Songti",serif}article figure blockquote:lang(zh-CN),article figure blockquote:lang(zh-Hans){font-family:"Biaodian Pro Serif GB","Numeral LF Serif",Georgia,"Times New Roman","Han Songti GB",serif}.no-unicoderange article figure blockquote:lang(zh-CN),.no-unicoderange article figure blockquote:lang(zh-Hans){font-family:"Numeral LF Serif",Georgia,"Times New Roman","Han Songti GB",serif}article figure blockquote:lang(ja){font-family:"Yakumono Serif","Numeral LF Serif",Georgia,"Times New Roman",serif}.no-unicoderange article figure blockquote:lang(ja){font-family:"Numeral LF Serif",Georgia,"Times New Roman",serif}article blockquote :lang(ja-Latn),article blockquote :lang(zh-Latn),article blockquote :not(:lang(zh)):not(:lang(ja)),article blockquote:lang(ja-Latn),article blockquote:lang(zh-Latn),article blockquote:not(:lang(zh)):not(:lang(ja)){font-family:Georgia,"Times New Roman","Han Kaiti",cursive,serif}article blockquote:lang(zh),article blockquote:lang(zh-Hant){font-family:"Biaodian Pro Serif CNS","Numeral LF Serif",Georgia,"Times New Roman","Zhuyin Kaiti","Han Kaiti",cursive,serif}.no-unicoderange article blockquote:lang(zh),.no-unicoderange article blockquote:lang(zh-Hant){font-family:"Numeral LF Serif",Georgia,"Times New Roman","Han Kaiti",cursive,serif}article blockquote:lang(zh-CN),article blockquote:lang(zh-Hans){font-family:"Biaodian Pro Serif GB","Numeral LF Serif",Georgia,"Times New Roman","Han Kaiti GB",cursive,serif}.no-unicoderange article blockquote:lang(zh-CN),.no-unicoderange article blockquote:lang(zh-Hans){font-family:"Numeral LF Serif",Georgia,"Times New Roman","Han Kaiti GB",cursive,serif}article blockquote:lang(ja){font-family:"Yakumono Serif","Numeral LF Serif",Georgia,"Times New Roman",cursive,serif}.no-unicoderange article blockquote:lang(ja){font-family:"Numeral LF Serif",Georgia,"Times New Roman",cursive,serif}i :lang(ja-Latn),i :lang(zh-Latn),i :not(:lang(zh)):not(:lang(ja)),i:lang(ja-Latn),i:lang(zh-Latn),i:not(:lang(zh)):not(:lang(ja)),var :lang(ja-Latn),var :lang(zh-Latn),var :not(:lang(zh)):not(:lang(ja)),var:lang(ja-Latn),var:lang(zh-Latn),var:not(:lang(zh)):not(:lang(ja)){font-family:"Latin Italic Serif",Georgia,"Times New Roman","Han Kaiti",cursive,serif}i:lang(zh),i:lang(zh-Hant),var:lang(zh),var:lang(zh-Hant){font-family:"Biaodian Pro Serif CNS","Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman","Zhuyin Kaiti","Han Kaiti",cursive,serif}.no-unicoderange i:lang(zh),.no-unicoderange i:lang(zh-Hant),.no-unicoderange var:lang(zh),.no-unicoderange var:lang(zh-Hant){font-family:"Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman","Han Kaiti",cursive,serif}i:lang(zh-CN),i:lang(zh-Hans),var:lang(zh-CN),var:lang(zh-Hans){font-family:"Biaodian Pro Serif GB","Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman","Han Kaiti GB",cursive,serif}.no-unicoderange i:lang(zh-CN),.no-unicoderange i:lang(zh-Hans),.no-unicoderange var:lang(zh-CN),.no-unicoderange var:lang(zh-Hans){font-family:"Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman","Han Kaiti GB",cursive,serif}i:lang(ja),var:lang(ja){font-family:"Yakumono Serif","Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman",cursive,serif}.no-unicoderange i:lang(ja),.no-unicoderange var:lang(ja){font-family:"Numeral LF Italic Serif","Latin Italic Serif",Georgia,"Times New Roman",cursive,serif}code :lang(ja-Latn),code :lang(zh-Latn),code :not(:lang(zh)):not(:lang(ja)),code:lang(ja-Latn),code:lang(zh-Latn),code:not(:lang(zh)):not(:lang(ja)),kbd :lang(ja-Latn),kbd :lang(zh-Latn),kbd :not(:lang(zh)):not(:lang(ja)),kbd:lang(ja-Latn),kbd:lang(zh-Latn),kbd:not(:lang(zh)):not(:lang(ja)),pre :lang(ja-Latn),pre :lang(zh-Latn),pre :not(:lang(zh)):not(:lang(ja)),pre:lang(ja-Latn),pre:lang(zh-Latn),pre:not(:lang(zh)):not(:lang(ja)),samp :lang(ja-Latn),samp :lang(zh-Latn),samp :not(:lang(zh)):not(:lang(ja)),samp:lang(ja-Latn),samp:lang(zh-Latn),samp:not(:lang(zh)):not(:lang(ja)){font-family:Menlo,Consolas,Courier,"Han Heiti",monospace,monospace,sans-serif}code:lang(zh),code:lang(zh-Hant),kbd:lang(zh),kbd:lang(zh-Hant),pre:lang(zh),pre:lang(zh-Hant),samp:lang(zh),samp:lang(zh-Hant){font-family:"Biaodian Pro Sans CNS",Menlo,Consolas,Courier,"Zhuyin Heiti","Han Heiti",monospace,monospace,sans-serif}.no-unicoderange code:lang(zh),.no-unicoderange code:lang(zh-Hant),.no-unicoderange kbd:lang(zh),.no-unicoderange kbd:lang(zh-Hant),.no-unicoderange pre:lang(zh),.no-unicoderange pre:lang(zh-Hant),.no-unicoderange samp:lang(zh),.no-unicoderange samp:lang(zh-Hant){font-family:Menlo,Consolas,Courier,"Han Heiti",monospace,monospace,sans-serif}code:lang(zh-CN),code:lang(zh-Hans),kbd:lang(zh-CN),kbd:lang(zh-Hans),pre:lang(zh-CN),pre:lang(zh-Hans),samp:lang(zh-CN),samp:lang(zh-Hans){font-family:"Biaodian Pro Sans GB",Menlo,Consolas,Courier,"Han Heiti GB",monospace,monospace,sans-serif}.no-unicoderange code:lang(zh-CN),.no-unicoderange code:lang(zh-Hans),.no-unicoderange kbd:lang(zh-CN),.no-unicoderange kbd:lang(zh-Hans),.no-unicoderange pre:lang(zh-CN),.no-unicoderange pre:lang(zh-Hans),.no-unicoderange samp:lang(zh-CN),.no-unicoderange samp:lang(zh-Hans){font-family:Menlo,Consolas,Courier,"Han Heiti GB",monospace,monospace,sans-serif}code:lang(ja),kbd:lang(ja),pre:lang(ja),samp:lang(ja){font-family:"Yakumono Sans",Menlo,Consolas,Courier,monospace,monospace,sans-serif}.no-unicoderange code:lang(ja),.no-unicoderange kbd:lang(ja),.no-unicoderange pre:lang(ja),.no-unicoderange samp:lang(ja){font-family:Menlo,Consolas,Courier,monospace,monospace,sans-serif}.no-unicoderange h-char.bd-liga,.no-unicoderange h-char[unicode=b7],h-ruby [annotation] rt,h-ruby h-zhuyin,h-ruby h-zhuyin h-diao,h-ruby.romanization rt,html,ruby [annotation] rt,ruby h-zhuyin,ruby h-zhuyin h-diao,ruby.romanization rt{-moz-font-feature-settings:"liga";-ms-font-feature-settings:"liga";-webkit-font-feature-settings:"liga";font-feature-settings:"liga"}[lang*=Hant],[lang*=Hans],[lang=zh-TW],[lang=zh-HK],[lang=zh-CN],[lang^=zh],article blockquote i,article blockquote var,article strong,code,html,kbd,pre,samp{-moz-font-feature-settings:"liga=1, locl=0";-ms-font-feature-settings:"liga","locl" 0;-webkit-font-feature-settings:"liga","locl" 0;font-feature-settings:"liga","locl" 0}.no-unicoderange h-char.bd-cop:lang(zh-HK),.no-unicoderange h-char.bd-cop:lang(zh-Hant),.no-unicoderange h-char.bd-cop:lang(zh-TW){font-family:-apple-system,"Han Heiti CNS"}.no-unicoderange h-char.bd-liga,.no-unicoderange h-char[unicode=b7]{font-family:"Biaodian Basic","Han Heiti"}.no-unicoderange h-char[unicode="2018"]:lang(zh-CN),.no-unicoderange h-char[unicode="2018"]:lang(zh-Hans),.no-unicoderange h-char[unicode="2019"]:lang(zh-CN),.no-unicoderange h-char[unicode="2019"]:lang(zh-Hans),.no-unicoderange h-char[unicode="201c"]:lang(zh-CN),.no-unicoderange h-char[unicode="201c"]:lang(zh-Hans),.no-unicoderange h-char[unicode="201d"]:lang(zh-CN),.no-unicoderange h-char[unicode="201d"]:lang(zh-Hans){font-family:"Han Heiti GB"}i,var{font-style:inherit}.no-unicoderange h-ruby h-zhuyin,.no-unicoderange h-ruby h-zhuyin h-diao,.no-unicoderange ruby h-zhuyin,.no-unicoderange ruby h-zhuyin h-diao,h-ruby h-diao,ruby h-diao{font-family:"Zhuyin Kaiti",cursive,serif}h-ruby [annotation] rt,h-ruby.romanization rt,ruby [annotation] rt,ruby.romanization rt{font-family:"Romanization Sans","Helvetica Neue",Helvetica,Arial,"Han Heiti",sans-serif} \ No newline at end of file diff --git a/lib/Han/dist/han.min.js b/lib/Han/dist/han.min.js new file mode 100644 index 0000000..a557ad3 --- /dev/null +++ b/lib/Han/dist/han.min.js @@ -0,0 +1,5 @@ +/*! 漢字標準格式 v3.3.0 | MIT License | css.hanzi.co */ +/*! Han.css: the CSS typography framework optimised for Hanzi */ + +void function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=b(a,!0):"function"==typeof define&&define.amd?define(function(){return b(a,!0)}):b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";function c(a){return"function"==typeof a||a instanceof Element?a:void 0}function d(a){var b=0===a.index&&a.isEnd?"biaodian cjk":"biaodian cjk portion "+(0===a.index?"is-first":a.isEnd?"is-end":"is-inner"),c=S.create("h-char-group",b);return c.innerHTML=a.text,c}function e(a){var b=S.create("div"),c=a.charCodeAt(0).toString(16);return b.innerHTML=''+a+"",b.firstChild}function f(a){return a.match(R["char"].biaodian.open)?"bd-open":a.match(R["char"].biaodian.close)?"bd-close bd-end":a.match(R["char"].biaodian.end)?/(?:\u3001|\u3002|\uff0c)/i.test(a)?"bd-end bd-cop":"bd-end":a.match(new RegExp(Q.biaodian.liga))?"bd-liga":a.match(new RegExp(Q.biaodian.middle))?"bd-middle":""}function g(a,b){var c,d=S.create("canvas");return d.width="50",d.height="20",d.style.display="none",L.appendChild(d),c=d.getContext("2d"),c.textBaseline="top",c.font="15px "+b+", sans-serif",c.fillStyle="black",c.strokeStyle="black",c.fillText(a,0,0),{node:d,context:c,remove:function(){S.remove(d,L)}}}function h(a,b){var c,d=a.context,e=b.context;try{for(var f=1;20>=f;f++)for(var g=1;50>=g;g++){if("undefined"==typeof c&&d.getImageData(g,f,1,1).data[3]!==e.getImageData(g,f,1,1).data[3]){c=!1;break}if("boolean"==typeof c)break;50===g&&20===f&&"undefined"==typeof c&&(c=!0)}return a.remove(),b.remove(),a=null,b=null,c}catch(h){}return!1}function i(a,b,c){var a=a,b=b||"sans-serif",c=c||"\u8fadQ";return b=g(c,b),a=g(c,a),!h(a,b)}function j(a){var b,c=S.create("!"),d=a.classList;return c.appendChild(S.clone(a)),S.tag("rt",c.firstChild).forEach(function(a){var c,e=S.create("!"),f=[];do{if(c=(c||a).previousSibling,!c||c.nodeName.match(/((?:h\-)?r[ubt])/i))break;e.insertBefore(S.clone(c),e.firstChild),f.push(c)}while(!c.nodeName.match(/((?:h\-)?r[ubt])/i));b=d.contains("zhuyin")?p(e,a):o(e,a);try{a.parentNode.replaceChild(b,a),f.map(S.remove)}catch(g){}}),m(c)}function k(a){var b=S.create("!");return b.appendChild(S.clone(a)),S.tag("rt",b.firstChild).forEach(function(a){var b,c,d=S.create("!"),e=[];do{if(b=(b||a).previousSibling,!b||b.nodeName.match(/((?:h\-)?r[ubt])/i))break;d.insertBefore(S.clone(b),d.firstChild),e.push(b)}while(!b.nodeName.match(/((?:h\-)?r[ubt])/i));c=S.create("rt"),c.innerHTML=q(a),a.parentNode.replaceChild(c,a)}),b.firstChild}function l(a){var b,c,d,e,f=S.create("!"),g=a.classList;return f.appendChild(S.clone(a)),b=f.firstChild,c=d=S.tag("rb",b),e=c.length,void function(a){a&&(d=S.tag("rt",a).map(function(a,b){if(c[b]){var d=p(c[b],a);try{c[b].parentNode.replaceChild(d,c[b])}catch(e){}return d}}),S.remove(a),b.setAttribute("rightangle","true"))}(b.querySelector("rtc.zhuyin")),S.qsa("rtc:not(.zhuyin)",b).forEach(function(a,c){var f;f=S.tag("rt",a).map(function(a,b){var f,h,i=Number(a.getAttribute("rbspan")||1),j=0,k=[];i>e&&(i=e);do{try{f=d.shift(),k.push(f)}catch(l){}if("undefined"==typeof f)break;j+=Number(f.getAttribute("span")||1)}while(i>j);if(j>i){if(k.length>1)return void console.error("An impossible `rbspan` value detected.",ruby);k=S.tag("rb",k[0]),d=k.slice(i).concat(d),k=k.slice(0,i),j=i}h=o(k,a,{"class":g,span:j,order:c});try{k[0].parentNode.replaceChild(h,k.shift()),k.map(S.remove)}catch(l){}return h}),d=f,1===c&&b.setAttribute("doubleline","true"),S.remove(a)}),m(f)}function m(a){var b=a.firstChild,c=S.create("h-ruby");return c.innerHTML=b.innerHTML,S.setAttr(c,b.attributes),c.normalize(),c}function n(a){if(!a instanceof Element)return a;var b=a.classList;return b.contains("pinyin")?b.add("romanization"):b.contains("romanization")?b.add("annotation"):b.contains("mps")?b.add("zhuyin"):b.contains("rightangle")&&b.add("complex"),a}function o(a,b,c){var d=S.create("h-ru"),b=S.clone(b),c=c||{};return c.annotation="true",Array.isArray(a)?d.innerHTML=a.map(function(a){return"undefined"==typeof a?"":a.outerHTML}).join("")+b.outerHTML:(d.appendChild(S.clone(a)),d.appendChild(b)),S.setAttr(d,c),d}function p(a,b){var a=S.clone(a),c=S.create("h-ru");return c.setAttribute("zhuyin",!0),c.appendChild(a),c.innerHTML+=q(b),c}function q(a){var b,c,d,e="string"==typeof a?a:a.textContent;return b=e.replace(R.zhuyin.diao,""),d=b?b.length:0,c=e.replace(b,"").replace(/[\u02C5]/g,"\u02c7").replace(/[\u030D]/g,"\u0358"),0===d?"":''+b+""+c+""}function r(a,b){return a&&b&&a.parentNode===b.parentNode}function s(a,b){var c=a,b=b||"";if(S.isElmt(a.nextSibling)||r(a,a.nextSibling))return b+X;for(;!c.nextSibling;)c=c.parentNode;return a!==c&&c.insertAdjacentHTML("afterEnd",""),b}function t(a,b){return a.isEnd&&0===a.index?b[1]+X+b[2]:0===a.index?s(a.node,a.text):a.text}function u(a){return 0===a.index?S.clone(Y):""}function v(a){var b=a.node.parentNode;return 0===a.index&&(Z=a.endIndexInNode-2),"h-hws"!==b.nodeName.toLowerCase()||1!==a.index&&a.indexInMatch!==Z||b.classList.add("quote-inner"),a.text}function w(a){var b=a.node.parentNode;return"h-hws"===b.nodeName.toLowerCase()&&b.classList.add("quote-outer"),a.text}function x(){var a,b=S.create("div");return b.innerHTML="a ba b",L.appendChild(b),a=b.firstChild.offsetWidth!==b.lastChild.offsetWidth,S.remove(b),a}function y(a){var b=a.nextSibling;b&&ba(b,"h-cs.jinze-outer")?b.classList.add("hangable-outer"):a.insertAdjacentHTML("afterend",aa)}function z(a){return a.replace(/(biaodian|cjk|bd-jiya|bd-consecutive|bd-hangable)/gi,"").trim()}function A(a){var b,c=a.text,d=a.node.parentNode,e=S.parent(d,"h-char.biaodian"),f=O.createBDChar(c);return f.innerHTML=""+c+"",f.classList.add(ea),(b=S.parent(d,"h-jinze"))&&C(b),e?function(){return e.classList.add(ea),ba(d,"h-inner, h-inner *")?c:f.firstChild}():f}function B(a){var b,c=ca,d=a.node.parentNode,e=S.parent(d,"h-char.biaodian"),f=S.parent(e,"h-jinze");return b=e.classList,c&&e.setAttribute("prev",c),da&&b.contains("bd-open")&&da.pop().setAttribute("next","bd-open"),da=void 0,a.isEnd?(ca=void 0,b.add(ga,"end-portion")):(ca=z(e.getAttribute("class")),b.add(ga)),f&&(da=D(f,{prev:c,"class":z(e.getAttribute("class"))})),a.text}function C(a){ba(a,".tou, .touwei")&&!ba(a.previousSibling,"h-cs.jiya-outer")&&a.insertAdjacentHTML("beforebegin",ha),ba(a,".wei, .touwei")&&!ba(a.nextSibling,"h-cs.jiya-outer")&&a.insertAdjacentHTML("afterend",ha)}function D(a,b){var c,d;return ba(a,".tou, .touwei")&&(c=a.previousSibling,ba(c,"h-cs")&&(c.className="jinze-outer jiya-outer",c.setAttribute("prev",b.prev))),ba(a,".wei, .touwei")&&(d=a.nextSibling,ba(d,"h-cs")&&(d.className="jinze-outer jiya-outer "+b["class"],d.removeAttribute("prev"))),[c,d]}function E(a,b,c){return function(){var d=O.localize.writeOnCanvas(b,a),e=O.localize.writeOnCanvas(c,a);return O.localize.compareCanvases(d,e)}}function F(){return E('"Romanization Sans"',"a\u030d","\udb80\udc61")}function G(){return E('"Romanization Sans"',"i\u030d","\udb80\udc69")}function H(){return E('"Zhuyin Kaiti"',"\u31b4\u0358","\udb8c\uddb4")}function I(a){return function(b){var b=b||J,c=O.find(b).avoid(ia);return a.forEach(function(a){c.replace(new RegExp(a[0],"ig"),function(b,c){var d=S.clone(ja);return d.innerHTML=""+c[0]+"",d.setAttribute("display-as",a[1]),0===b.index?d:""})}),c}}var J=a.document,K=J.documentElement,L=J.body,M="3.3.0",N=["initCond","renderElem","renderJiya","renderHanging","correctBiaodian","renderHWS","substCombLigaWithPUA"],O=function(a,b){return new O.fn.init(a,b)},P=function(){return arguments[0]&&(this.context=arguments[0]),arguments[1]&&(this.condition=arguments[1]),this};O.version=M,O.fn=O.prototype={version:M,constructor:O,context:L,condition:K,routine:N,init:P,setRoutine:function(a){return Array.isArray(a)&&(this.routine=a),this},render:function(a){var b=this,a=Array.isArray(a)?a:this.routine;return a.forEach(function(a){"string"==typeof a&&"function"==typeof b[a]?b[a]():Array.isArray(a)&&"function"==typeof b[a[0]]&&b[a.shift()].apply(b,a)}),this}},O.fn.init.prototype=O.fn,O.init=function(){return O.init=O().render()};var Q={punct:{base:"[\u2026,.;:!?\u203d_]",sing:"[\u2010-\u2014\u2026]",middle:"[\\/~\\-&\u2010-\u2014_]",open:"['\"\u2018\u201c\\(\\[\xa1\xbf\u2e18\xab\u2039\u201a\u201c\u201e]",close:"['\"\u201d\u2019\\)\\]\xbb\u203a\u201b\u201d\u201f]",end:"['\"\u201d\u2019\\)\\]\xbb\u203a\u201b\u201d\u201f\u203c\u203d\u2047-\u2049,.;:!?]"},biaodian:{base:"[\ufe30\uff0e\u3001\uff0c\u3002\uff1a\uff1b\uff1f\uff01\u30fc]",liga:"[\u2014\u2026\u22ef]",middle:"[\xb7\uff3c\uff0f\uff0d\u30a0\uff06\u30fb\uff3f]",open:"[\u300c\u300e\u300a\u3008\uff08\u3014\uff3b\uff5b\u3010\u3016]",close:"[\u300d\u300f\u300b\u3009\uff09\u3015\uff3d\uff5d\u3011\u3017]",end:"[\u300d\u300f\u300b\u3009\uff09\u3015\uff3d\uff5d\u3011\u3017\ufe30\uff0e\u3001\uff0c\u3002\uff1a\uff1b\uff1f\uff01\u30fc]"},hanzi:{base:"[\u4e00-\u9fff\u3400-\u4db5\u31c0-\u31e3\u3007\ufa0e\ufa0f\ufa11\ufa13\ufa14\ufa1f\ufa21\ufa23\ufa24\ufa27-\ufa29]|[\ud800-\udbff][\udc00-\udfff]",desc:"[\u2ff0-\u2ffa]",radical:"[\u2f00-\u2fd5\u2e80-\u2ef3]"},latin:{base:"[A-Za-z0-9\xc0-\xff\u0100-\u017f\u0180-\u024f\u2c60-\u2c7f\ua720-\ua7ff\u1e00-\u1eff]",combine:"[\u0300-\u0341\u1dc0-\u1dff]"},ellinika:{base:"[0-9\u0370-\u03ff\u1f00-\u1fff]",combine:"[\u0300-\u0345\u1dc0-\u1dff]"},kirillica:{base:"[0-9\u0400-\u0482\u048a-\u04ff\u0500-\u052f\ua640-\ua66e\ua67e-\ua697]",combine:"[\u0483-\u0489\u2de0-\u2dff\ua66f-\ua67d\ua69f]"},kana:{base:"[\u30a2\u30a4\u30a6\u30a8\u30aa-\u30fa\u3042\u3044\u3046\u3048\u304a-\u3094\u309f\u30ff]|\ud82c[\udc00-\udc01]",small:"[\u3041\u3043\u3045\u3047\u3049\u30a1\u30a3\u30a5\u30a7\u30a9\u3063\u3083\u3085\u3087\u308e\u3095\u3096\u30c3\u30e3\u30e5\u30e7\u30ee\u30f5\u30f6\u31f0-\u31ff]",combine:"[\u3099-\u309c]",half:"[\uff66-\uff9f]",mark:"[\u30a0\u309d\u309e\u30fb-\u30fe]"},eonmun:{base:"[\uac00-\ud7a3]",letter:"[\u1100-\u11ff\u314f-\u3163\u3131-\u318e\ua960-\ua97c\ud7b0-\ud7fb]",half:"[\uffa1-\uffdc]"},zhuyin:{base:"[\u3105-\u312d\u31a0-\u31ba]",initial:"[\u3105-\u3119\u312a-\u312c\u31a0-\u31a3]",medial:"[\u3127-\u3129]","final":"[\u311a-\u3129\u312d\u31a4-\u31b3\u31b8-\u31ba]",tone:"[\u02d9\u02ca\u02c5\u02c7\u02cb\u02ea\u02eb]",checked:"[\u31b4-\u31b7][\u0358\u030d]?"}},R=function(){var a="[\\x20\\t\\r\\n\\f]",b=Q.punct.open,c=(Q.punct.close,Q.punct.end),d=Q.punct.middle,e=Q.punct.sing,f=b+"|"+c+"|"+d,g=Q.biaodian.open,h=Q.biaodian.close,i=Q.biaodian.end,j=Q.biaodian.middle,k=Q.biaodian.liga+"{2}",l=g+"|"+i+"|"+j,m=Q.kana.base+Q.kana.combine+"?",n=Q.kana.small+Q.kana.combine+"?",o=Q.kana.half,p=Q.eonmun.base+"|"+Q.eonmun.letter,q=Q.eonmun.half,r=Q.hanzi.base+"|"+Q.hanzi.desc+"|"+Q.hanzi.radical+"|"+m,s=Q.ellinika.combine,t=Q.latin.base+s+"*",u=Q.ellinika.base+s+"*",v=Q.kirillica.combine,w=Q.kirillica.base+v+"*",x=t+"|"+u+"|"+w,y="['\u2019]",z=r+"|(?:"+x+"|"+y+")+",A=Q.zhuyin.initial,B=Q.zhuyin.medial,C=Q.zhuyin["final"],D=Q.zhuyin.tone+"|"+Q.zhuyin.checked;return{"char":{punct:{all:new RegExp("("+f+")","g"),open:new RegExp("("+b+")","g"),end:new RegExp("("+c+")","g"),sing:new RegExp("("+e+")","g")},biaodian:{all:new RegExp("("+l+")","g"),open:new RegExp("("+g+")","g"),close:new RegExp("("+h+")","g"),end:new RegExp("("+i+")","g"),liga:new RegExp("("+k+")","g")},hanzi:new RegExp("("+r+")","g"),latin:new RegExp("("+t+")","ig"),ellinika:new RegExp("("+u+")","ig"),kirillica:new RegExp("("+w+")","ig"),kana:new RegExp("("+m+"|"+n+"|"+o+")","g"),eonmun:new RegExp("("+p+"|"+q+")","g")},group:{biaodian:[new RegExp("(("+l+"){2,})","g"),new RegExp("("+k+g+")","g")],punct:null,hanzi:new RegExp("("+r+")+","g"),western:new RegExp("("+t+"|"+u+"|"+w+"|"+f+")+","ig"),kana:new RegExp("("+m+"|"+n+"|"+o+")+","g"),eonmun:new RegExp("("+p+"|"+q+"|"+f+")+","g")},jinze:{hanging:new RegExp(a+"*([\u3001\uff0c\u3002\uff0e])(?!"+i+")","ig"),touwei:new RegExp("("+g+"+)("+z+")("+i+"+)","ig"),tou:new RegExp("("+g+"+)("+z+")","ig"),wei:new RegExp("("+z+")("+i+"+)","ig"),middle:new RegExp("("+z+")("+j+")("+z+")","ig")},zhuyin:{form:new RegExp("^\u02d9?("+A+")?("+B+")?("+C+")?("+D+")?$"),diao:new RegExp("("+D+")","g")},hws:{base:[new RegExp("("+r+")("+x+"|"+b+")","ig"),new RegExp("("+x+"|"+c+")("+r+")","ig")],strict:[new RegExp("("+r+")"+a+"?("+x+"|"+b+")","ig"),new RegExp("("+x+"|"+c+")"+a+"?("+r+")","ig")]},"display-as":{"ja-font-for-hant":["\u67e5 \u67fb","\u555f \u5553","\u9109 \u9115","\u503c \u5024","\u6c61 \u6c5a"],"comb-liga-pua":[["a[\u030d\u0358]","\udb80\udc61"],["e[\u030d\u0358]","\udb80\udc65"],["i[\u030d\u0358]","\udb80\udc69"],["o[\u030d\u0358]","\udb80\udc6f"],["u[\u030d\u0358]","\udb80\udc75"],["\u31b4[\u030d\u0358]","\udb8c\uddb4"],["\u31b5[\u030d\u0358]","\udb8c\uddb5"],["\u31b6[\u030d\u0358]","\udb8c\uddb6"],["\u31b7[\u030d\u0358]","\udb8c\uddb7"]],"comb-liga-vowel":[["a[\u030d\u0358]","\udb80\udc61"],["e[\u030d\u0358]","\udb80\udc65"],["i[\u030d\u0358]","\udb80\udc69"],["o[\u030d\u0358]","\udb80\udc6f"],["u[\u030d\u0358]","\udb80\udc75"]],"comb-liga-zhuyin":[["\u31b4[\u030d\u0358]","\udb8c\uddb4"],["\u31b5[\u030d\u0358]","\udb8c\uddb5"],["\u31b6[\u030d\u0358]","\udb8c\uddb6"],["\u31b7[\u030d\u0358]","\udb8c\uddb7"]]},"inaccurate-char":[["[\u2022\u2027]","\xb7"],["\u22ef\u22ef","\u2026\u2026"],["\u2500\u2500","\u2014\u2014"],["\u2035","\u2018"],["\u2032","\u2019"],["\u2036","\u201c"],["\u2033","\u201d"]]}}();O.UNICODE=Q,O.TYPESET=R,O.UNICODE.cjk=O.UNICODE.hanzi,O.UNICODE.greek=O.UNICODE.ellinika,O.UNICODE.cyrillic=O.UNICODE.kirillica,O.UNICODE.hangul=O.UNICODE.eonmun,O.UNICODE.zhuyin.ruyun=O.UNICODE.zhuyin.checked,O.TYPESET["char"].cjk=O.TYPESET["char"].hanzi,O.TYPESET["char"].greek=O.TYPESET["char"].ellinika,O.TYPESET["char"].cyrillic=O.TYPESET["char"].kirillica,O.TYPESET["char"].hangul=O.TYPESET["char"].eonmun,O.TYPESET.group.hangul=O.TYPESET.group.eonmun,O.TYPESET.group.cjk=O.TYPESET.group.hanzi;var S={id:function(a,b){return(b||J).getElementById(a)},tag:function(a,b){return this.makeArray((b||J).getElementsByTagName(a))},qs:function(a,b){return(b||J).querySelector(a)},qsa:function(a,b){return this.makeArray((b||J).querySelectorAll(a))},parent:function(a,b){return b?function(){if("function"==typeof S.matches){for(;!S.matches(a,b);){if(!a||a===J.documentElement){a=void 0;break}a=a.parentNode}return a}}():a?a.parentNode:void 0},create:function(a,b){var c="!"===a?J.createDocumentFragment():""===a?J.createTextNode(b||""):J.createElement(a);try{b&&(c.className=b)}catch(d){}return c},clone:function(a,b){return a.cloneNode("boolean"==typeof b?b:!0)},remove:function(a){return a.parentNode.removeChild(a)},setAttr:function(a,b){if("object"==typeof b){var c=b.length;if("object"==typeof b[0]&&"name"in b[0])for(var d=0;c>d;d++)void 0!==b[d].value&&a.setAttribute(b[d].name,b[d].value);else for(var e in b)b.hasOwnProperty(e)&&void 0!==b[e]&&a.setAttribute(e,b[e]);return a}},isElmt:function(a){return a&&a.nodeType===Node.ELEMENT_NODE},isIgnorable:function(a){return a?"WBR"===a.nodeName||a.nodeType===Node.COMMENT_NODE:!1},makeArray:function(a){return Array.prototype.slice.call(a)},extend:function(a,b){if(("object"==typeof a||"function"==typeof a)&&"object"==typeof b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}},T=function(b){function c(a,b,c){var d=Element.prototype,e=d.matches||d.mozMatchesSelector||d.msMatchesSelector||d.webkitMatchesSelector;return a instanceof Element?e.call(a,b):c&&/^[39]$/.test(a.nodeType)?!0:!1}var d="0.2.1",e=b.NON_INLINE_PROSE,f=b.PRESETS.prose.filterElements,g=a||{},h=g.document||void 0;if("undefined"==typeof h)throw new Error("Fibre requires a DOM-supported environment.");var i=function(a,b){return new i.fn.init(a,b)};return i.version=d,i.matches=c,i.fn=i.prototype={constructor:i,version:d,finder:[],context:void 0,portionMode:"retain",selector:{},preset:"prose",init:function(a,b){if(b&&(this.preset=null),this.selector={context:null,filter:[],avoid:[],boundary:[]},!a)throw new Error("A context is required for Fibre to initialise.");return a instanceof Node?a instanceof Document?this.context=a.body||a:this.context=a:"string"==typeof a&&(this.context=h.querySelector(a),this.selector.context=a),this},filterFn:function(a){var b=this.selector.filter.join(", ")||"*",d=this.selector.avoid.join(", ")||null,e=c(a,b,!0)&&!c(a,d);return"prose"===this.preset?f(a)&&e:e},boundaryFn:function(a){var b=this.selector.boundary.join(", ")||null,d=c(a,b);return"prose"===this.preset?e(a)||d:d},filter:function(a){return"string"==typeof a&&this.selector.filter.push(a),this},endFilter:function(a){return a?this.selector.filter=[]:this.selector.filter.pop(),this},avoid:function(a){return"string"==typeof a&&this.selector.avoid.push(a),this},endAvoid:function(a){return a?this.selector.avoid=[]:this.selector.avoid.pop(),this},addBoundary:function(a){return"string"==typeof a&&this.selector.boundary.push(a),this},removeBoundary:function(){return this.selector.boundary=[],this},setMode:function(a){return this.portionMode="first"===a?"first":"retain",this},replace:function(a,c){var d=this;return d.finder.push(b(d.context,{find:a,replace:c,filterElements:function(a){return d.filterFn(a)},forceContext:function(a){return d.boundaryFn(a)},portionMode:d.portionMode})),d},wrap:function(a,c){var d=this;return d.finder.push(b(d.context,{find:a,wrap:c,filterElements:function(a){return d.filterFn(a)},forceContext:function(a){return d.boundaryFn(a)},portionMode:d.portionMode})),d},revert:function(a){var b=this.finder.length,a=Number(a)||(0===a?Number(0):"all"===a?b:1);if("undefined"==typeof b||0===b)return this;a>b&&(a=b);for(var c=a;c>0;c--)this.finder.pop().revert();return this}},i.fn.filterOut=i.fn.avoid,i.fn.init.prototype=i.fn,i}(function(){function a(a){return String(a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}function b(){return c.apply(null,arguments)||d.apply(null,arguments)}function c(a,c,e,f,g){if(c&&!c.nodeType&&arguments.length<=2)return!1;var h="function"==typeof e;h&&(e=function(a){return function(b,c){return a(b.text,c.startIndex)}}(e));var i=d(c,{find:a,wrap:h?null:e,replace:h?e:"$"+(f||"&"),prepMatch:function(a,b){if(!a[0])throw"findAndReplaceDOMText cannot handle zero-length matches";if(f>0){var c=a[f];a.index+=a[0].indexOf(c),a[0]=c}return a.endIndex=a.index+a[0].length,a.startIndex=a.index,a.index=b,a},filterElements:g});return b.revert=function(){return i.revert()},!0}function d(a,b){return new e(a,b)}function e(a,c){var d=c.preset&&b.PRESETS[c.preset];if(c.portionMode=c.portionMode||f,d)for(var e in d)i.call(d,e)&&!i.call(c,e)&&(c[e]=d[e]);this.node=a,this.options=c,this.prepMatch=c.prepMatch||this.prepMatch,this.reverts=[],this.matches=this.search(),this.matches.length&&this.processMatches()}var f="retain",g="first",h=J,i=({}.toString,{}.hasOwnProperty);return b.NON_PROSE_ELEMENTS={br:1,hr:1,script:1,style:1,img:1,video:1,audio:1,canvas:1,svg:1,map:1,object:1,input:1,textarea:1,select:1,option:1,optgroup:1,button:1},b.NON_CONTIGUOUS_PROSE_ELEMENTS={address:1,article:1,aside:1,blockquote:1,dd:1,div:1,dl:1,fieldset:1,figcaption:1,figure:1,footer:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,header:1,hgroup:1,hr:1,main:1,nav:1,noscript:1,ol:1,output:1,p:1,pre:1,section:1,ul:1,br:1,li:1,summary:1,dt:1,details:1,rp:1,rt:1,rtc:1,script:1,style:1,img:1,video:1,audio:1,canvas:1,svg:1,map:1,object:1,input:1,textarea:1,select:1,option:1,optgroup:1,button:1,table:1,tbody:1,thead:1,th:1,tr:1,td:1,caption:1,col:1,tfoot:1,colgroup:1},b.NON_INLINE_PROSE=function(a){return i.call(b.NON_CONTIGUOUS_PROSE_ELEMENTS,a.nodeName.toLowerCase())},b.PRESETS={prose:{forceContext:b.NON_INLINE_PROSE,filterElements:function(a){return!i.call(b.NON_PROSE_ELEMENTS,a.nodeName.toLowerCase())}}},b.Finder=e,e.prototype={search:function(){function b(a){for(var g=0,j=a.length;j>g;++g){var k=a[g];if("string"==typeof k){if(f.global)for(;c=f.exec(k);)h.push(i.prepMatch(c,d++,e));else(c=k.match(f))&&h.push(i.prepMatch(c,0,e));e+=k.length}else b(k)}}var c,d=0,e=0,f=this.options.find,g=this.getAggregateText(),h=[],i=this;return f="string"==typeof f?RegExp(a(f),"g"):f,b(g),h},prepMatch:function(a,b,c){if(!a[0])throw new Error("findAndReplaceDOMText cannot handle zero-length matches");return a.endIndex=c+a.index+a[0].length,a.startIndex=c+a.index,a.index=b,a},getAggregateText:function(){function a(d,e){if(3===d.nodeType)return[d.data];if(b&&!b(d))return[];var e=[""],f=0;if(d=d.firstChild)do if(3!==d.nodeType){var g=a(d);c&&1===d.nodeType&&(c===!0||c(d))?(e[++f]=g,e[++f]=""):("string"==typeof g[0]&&(e[f]+=g.shift()),g.length&&(e[++f]=g,e[++f]=""))}else e[f]+=d.data;while(d=d.nextSibling);return e}var b=this.options.filterElements,c=this.options.forceContext;return a(this.node)},processMatches:function(){var a,b,c,d=this.matches,e=this.node,f=this.options.filterElements,g=[],h=e,i=d.shift(),j=0,k=0,l=0,m=[e];a:for(;;){if(3===h.nodeType&&(!b&&h.length+j>=i.endIndex?b={node:h,index:l++,text:h.data.substring(i.startIndex-j,i.endIndex-j),indexInMatch:j-i.startIndex,indexInNode:i.startIndex-j,endIndexInNode:i.endIndex-j,isEnd:!0}:a&&g.push({node:h,index:l++,text:h.data,indexInMatch:j-i.startIndex,indexInNode:0}),!a&&h.length+j>i.startIndex&&(a={node:h,index:l++,indexInMatch:0,indexInNode:i.startIndex-j,endIndexInNode:i.endIndex-j,text:h.data.substring(i.startIndex-j,i.endIndex-j)}),j+=h.data.length),c=1===h.nodeType&&f&&!f(h),a&&b){if(h=this.replaceMatch(i,a,g,b),j-=b.node.data.length-b.endIndexInNode,a=null,b=null,g=[],i=d.shift(),l=0,k++,!i)break}else if(!c&&(h.firstChild||h.nextSibling)){h.firstChild?(m.push(h),h=h.firstChild):h=h.nextSibling;continue}for(;;){if(h.nextSibling){h=h.nextSibling;break}if(h=m.pop(),h===e)break a}}},revert:function(){for(var a=this.reverts.length;a--;)this.reverts[a]();this.reverts=[]},prepareReplacementString:function(a,b,c,d){var e=this.options.portionMode;return e===g&&b.indexInMatch>0?"":(a=a.replace(/\$(\d+|&|`|')/g,function(a,b){var d;switch(b){case"&":d=c[0];break;case"`":d=c.input.substring(0,c.startIndex);break;case"'":d=c.input.substring(c.endIndex);break;default:d=c[+b]}return d}),e===g?a:b.isEnd?a.substring(b.indexInMatch):a.substring(b.indexInMatch,b.indexInMatch+b.text.length))},getPortionReplacementNode:function(a,b,c){var d=this.options.replace||"$&",e=this.options.wrap;if(e&&e.nodeType){var f=h.createElement("div");f.innerHTML=e.outerHTML||(new XMLSerializer).serializeToString(e),e=f.firstChild}if("function"==typeof d)return d=d(a,b,c),d&&d.nodeType?d:h.createTextNode(String(d));var g="string"==typeof e?h.createElement(e):e;return d=h.createTextNode(this.prepareReplacementString(d,a,b,c)),d.data&&g?(g.appendChild(d),g):d},replaceMatch:function(a,b,c,d){var e,f,g=b.node,i=d.node;if(g===i){var j=g;b.indexInNode>0&&(e=h.createTextNode(j.data.substring(0,b.indexInNode)),j.parentNode.insertBefore(e,j));var k=this.getPortionReplacementNode(d,a);return j.parentNode.insertBefore(k,j),d.endIndexInNoden;++n){var p=c[n],q=this.getPortionReplacementNode(p,a);p.node.parentNode.replaceChild(q,p.node),this.reverts.push(function(a,b){return function(){b.parentNode.replaceChild(a.node,b)}}(p,q)),m.push(q)}var r=this.getPortionReplacementNode(d,a);return g.parentNode.insertBefore(e,g),g.parentNode.insertBefore(l,g),g.parentNode.removeChild(g),i.parentNode.insertBefore(r,i),i.parentNode.insertBefore(f,i),i.parentNode.removeChild(i),this.reverts.push(function(){e.parentNode.removeChild(e),l.parentNode.replaceChild(g,l),f.parentNode.removeChild(f),r.parentNode.replaceChild(i,r)}),r}},b}()),U=function(){var a=S.create("div");return a.appendChild(S.create("","0-")),a.appendChild(S.create("","2")),a.normalize(),2!==a.firstChild.length}();S.extend(T.fn,{normalize:function(){return U&&this.context.normalize(),this},jinzify:function(a){return this.filter(a||null).avoid("h-jinze").replace(R.jinze.touwei,function(a,b){var c=S.create("h-jinze","touwei");return c.innerHTML=b[0],0===a.index&&a.isEnd||1===a.index?c:""}).replace(R.jinze.wei,function(a,b){var c=S.create("h-jinze","wei");return c.innerHTML=b[0],0===a.index?c:""}).replace(R.jinze.tou,function(a,b){var c=S.create("h-jinze","tou");return c.innerHTML=b[0],0===a.index&&a.isEnd||1===a.index?c:""}).replace(R.jinze.middle,function(a,b){var c=S.create("h-jinze","middle");return c.innerHTML=b[0],0===a.index&&a.isEnd||1===a.index?c:""}).endAvoid().endFilter()},groupify:function(a){var a=S.extend({biaodian:!1,hanzi:!1,kana:!1,eonmun:!1,western:!1},a||{});return this.avoid("h-word, h-char-group"),a.biaodian&&this.replace(R.group.biaodian[0],d).replace(R.group.biaodian[1],d),(a.hanzi||a.cjk)&&this.wrap(R.group.hanzi,S.clone(S.create("h-char-group","hanzi cjk"))),a.western&&this.wrap(R.group.western,S.clone(S.create("h-word","western"))),a.kana&&this.wrap(R.group.kana,S.clone(S.create("h-char-group","kana"))),(a.eonmun||a.hangul)&&this.wrap(R.group.eonmun,S.clone(S.create("h-word","eonmun hangul"))),this.endAvoid(),this},charify:function(a){var a=S.extend({avoid:!0,biaodian:!1,punct:!1,hanzi:!1,latin:!1,ellinika:!1,kirillica:!1,kana:!1,eonmun:!1},a||{});return a.avoid&&this.avoid("h-char"),a.biaodian&&this.replace(R["char"].biaodian.all,c(a.biaodian)||function(a){return e(a.text)}).replace(R["char"].biaodian.liga,c(a.biaodian)||function(a){return e(a.text)}),(a.hanzi||a.cjk)&&this.wrap(R["char"].hanzi,c(a.hanzi||a.cjk)||S.clone(S.create("h-char","hanzi cjk"))),a.punct&&this.wrap(R["char"].punct.all,c(a.punct)||S.clone(S.create("h-char","punct"))),a.latin&&this.wrap(R["char"].latin,c(a.latin)||S.clone(S.create("h-char","alphabet latin"))),(a.ellinika||a.greek)&&this.wrap(R["char"].ellinika,c(a.ellinika||a.greek)||S.clone(S.create("h-char","alphabet ellinika greek"))),(a.kirillica||a.cyrillic)&&this.wrap(R["char"].kirillica,c(a.kirillica||a.cyrillic)||S.clone(S.create("h-char","alphabet kirillica cyrillic"))),a.kana&&this.wrap(R["char"].kana,c(a.kana)||S.clone(S.create("h-char","kana"))),(a.eonmun||a.hangul)&&this.wrap(R["char"].eonmun,c(a.eonmun||a.hangul)||S.clone(S.create("h-char","eonmun hangul"))),this.endAvoid(),this}}),S.extend(O,{isNodeNormalizeNormal:U,find:T,createBDGroup:d,createBDChar:e}),S.matches=O.find.matches,void["setMode","wrap","replace","revert","addBoundary","removeBoundary","avoid","endAvoid","filter","endFilter","jinzify","groupify","charify"].forEach(function(a){O.fn[a]=function(){return this.finder||(this.finder=O.find(this.context)),this.finder[a](arguments[0],arguments[1]),this}});var V={};V.writeOnCanvas=g,V.compareCanvases=h,V.detectFont=i,V.support=function(){function b(a){var b,c=a.charAt(0).toUpperCase()+a.slice(1),d=(a+" "+e.join(c+" ")+c).split(" ");return d.forEach(function(a){"string"==typeof f.style[a]&&(b=!0)}),b||!1}function c(a,b){var c,d,e,f=L||S.create("body"),g=S.create("div"),h=L?g:f,b="function"==typeof b?b:function(){};return c=[""].join(""),h.innerHTML+=c,f.appendChild(g),L||(f.style.background="",f.style.overflow="hidden",e=K.style.overflow,K.style.overflow="hidden",K.appendChild(f)),d=b(h,a),S.remove(h),L||(K.style.overflow=e),!!d}function d(b,c){var d;return a.getComputedStyle?d=J.defaultView.getComputedStyle(b,null).getPropertyValue(c):b.currentStyle&&(d=b.currentStyle[c]),d}var e="Webkit Moz ms".split(" "),f=S.create("h-test");return{columnwidth:b("columnWidth"),fontface:function(){var a;return c('@font-face { font-family: font; src: url("//"); }',function(b,c){var d=S.qsa("style",b)[0],e=d.sheet||d.styleSheet,f=e?e.cssRules&&e.cssRules[0]?e.cssRules[0].cssText:e.cssText||"":"";a=/src/i.test(f)&&0===f.indexOf(c.split(" ")[0])}),a}(),ruby:function(){var a,b=S.create("ruby"),c=S.create("rt"),e=S.create("rp");return b.appendChild(e),b.appendChild(c),K.appendChild(b),a="none"===d(e,"display")||"ruby"===d(b,"display")&&"ruby-text"===d(c,"display")?!0:!1,K.removeChild(b),b=null,c=null,e=null,a}(),"ruby-display":function(){var a=S.create("div");return a.innerHTML='',"ruby"===a.querySelector("h-test-a").style.display&&"ruby-text-container"===a.querySelector("h-test-b").style.display}(),"ruby-interchar":function(){var a,b="inter-character",c=S.create("div");return c.innerHTML='',a=c.querySelector("h-test").style,a.rubyPosition===b||a.WebkitRubyPosition===b||a.MozRubyPosition===b||a.msRubyPosition===b}(),textemphasis:b("textEmphasis"),unicoderange:function(){var a;return c('@font-face{font-family:test-for-unicode-range;src:local(Arial),local("Droid Sans")}@font-face{font-family:test-for-unicode-range;src:local("Times New Roman"),local(Times),local("Droid Serif");unicode-range:U+270C}',function(){a=!V.detectFont("test-for-unicode-range",'Arial, "Droid Sans"',"Q")}),a}(),writingmode:b("writingMode")}}(),V.initCond=function(a){var b,a=a||K,c="";for(var d in V.support)b=(V.support[d]?"":"no-")+d,a.classList.add(b),c+=b+" ";return c};var W=V.support["ruby-interchar"];S.extend(V,{renderRuby:function(a,b){var b=b||"ruby",c=S.qsa(b,a);S.qsa("rtc",a).concat(c).map(n),c.forEach(function(a){var b,c=a.classList;c.contains("complex")?b=l(a):c.contains("zhuyin")&&(b=W?k(a):j(a)),b&&a.parentNode.replaceChild(b,a)})},simplifyRubyClass:n,getZhuyinHTML:q,renderComplexRuby:l,renderSimpleRuby:j,renderInterCharRuby:k}),S.extend(V,{renderElem:function(a){this.renderRuby(a),this.renderDecoLine(a),this.renderDecoLine(a,"s, del"),this.renderEm(a)},renderDecoLine:function(a,b){var c=S.qsa(b||"u, ins",a),d=c.length;a:for(;d--;){var e=c[d],f=null;do{if(f=(f||e).previousSibling,!f)continue a;c[d-1]===f&&e.classList.add("adjacent")}while(S.isIgnorable(f))}},renderEm:function(a,b){var c=b?"qsa":"tag",b=b||"em",d=S[c](b,a);d.forEach(function(a){var b=O(a);V.support.textemphasis?b.avoid("rt, h-char").charify({biaodian:!0,punct:!0}):b.avoid("rt, h-char, h-char-group").jinzify().groupify({western:!0}).charify({hanzi:!0,biaodian:!0,punct:!0,latin:!0,ellinika:!0,kirillica:!0})})}}),O.normalize=V,O.localize=V,O.support=V.support,O.detectFont=V.detectFont,O.fn.initCond=function(){return this.condition.classList.add("han-js-rendered"),O.normalize.initCond(this.condition),this},void["Elem","DecoLine","Em","Ruby"].forEach(function(a){var b="render"+a;O.fn[b]=function(a){return O.normalize[b](this.context,a),this}}),S.extend(O.support,{heiti:!0,songti:O.detectFont('"Han Songti"'),"songti-gb":O.detectFont('"Han Songti GB"'),kaiti:O.detectFont('"Han Kaiti"'),fangsong:O.detectFont('"Han Fangsong"')}),O.correctBiaodian=function(a){var a=a||J,b=O.find(a);return b.avoid("h-char").replace(/([\u2018\u201c])/g,function(a){var b=O.createBDChar(a.text);return b.classList.add("bd-open","punct"),b}).replace(/([\u2019\u201d])/g,function(a){var b=O.createBDChar(a.text);return b.classList.add("bd-close","bd-end","punct"),b}),O.support.unicoderange?b:b.charify({biaodian:!0})},O.correctBasicBD=O.correctBiaodian,O.correctBD=O.correctBiaodian,S.extend(O.fn,{biaodian:null,correctBiaodian:function(){return this.biaodian=O.correctBiaodian(this.context),this},revertCorrectedBiaodian:function(){try{this.biaodian.revert("all")}catch(a){}return this}}),O.fn.correctBasicBD=O.fn.correctBiaodian,O.fn.revertBasicBD=O.fn.revertCorrectedBiaodian;var X="<>",Y=S.create("h-hws");Y.setAttribute("hidden",""),Y.innerHTML=" ";var Z;S.extend(O,{renderHWS:function(a,b){var c=b?"textarea, code, kbd, samp, pre":"textarea",d=b?"strict":"base",a=a||J,e=O.find(a); +return e.avoid(c).replace(O.TYPESET.hws[d][0],t).replace(O.TYPESET.hws[d][1],t).replace(new RegExp("("+X+")+","g"),u).replace(/([\'"])\s(.+?)\s\1/g,v).replace(/\s[\u2018\u201c]/g,w).replace(/[\u2019\u201d]\s/g,w).normalize(),e}}),S.extend(O.fn,{renderHWS:function(a){return O.renderHWS(this.context,a),this},revertHWS:function(){return S.tag("h-hws",this.context).forEach(function(a){S.remove(a)}),this.HWS=[],this}});var $="bd-hangable",_="h-char.bd-hangable",aa='',ba=O.find.matches;O.support["han-space"]=x(),S.extend(O,{detectSpaceFont:x,isSpaceFontLoaded:x(),renderHanging:function(a){var a=a||J,b=O.find(a);return b.avoid("textarea, code, kbd, samp, pre").avoid(_).replace(R.jinze.hanging,function(a){if(/^[\x20\t\r\n\f]+$/.test(a.text))return"";var b,c,d,e,f=a.node.parentNode;return(b=S.parent(f,"h-jinze"))&&y(b),e=a.text.trim(),c=O.createBDChar(e),c.innerHTML=""+e+"",c.classList.add($),d=S.parent(f,"h-char.biaodian"),d?function(){return d.classList.add($),ba(f,"h-inner, h-inner *")?e:c.firstChild}():c}),b}}),S.extend(O.fn,{renderHanging:function(){var a=this.condition.classList;return O.isSpaceFontLoaded=x(),O.isSpaceFontLoaded&&a.contains("no-han-space")&&(a.remove("no-han-space"),a.add("han-space")),O.renderHanging(this.context),this},revertHanging:function(){return S.qsa("h-char.bd-hangable, h-cs.hangable-outer",this.context).forEach(function(a){var b=a.classList;b.remove("bd-hangable"),b.remove("hangable-outer")}),this}});var ca,da,ea="bd-jiya",fa="h-char.bd-jiya",ga="bd-consecutive",ha='',ba=O.find.matches;O.renderJiya=function(a){var a=a||J,b=O.find(a);return b.avoid("textarea, code, kbd, samp, pre, h-cs").avoid(fa).charify({avoid:!1,biaodian:A}).endAvoid().avoid("textarea, code, kbd, samp, pre, h-cs").replace(R.group.biaodian[0],B).replace(R.group.biaodian[1],B),b},S.extend(O.fn,{renderJiya:function(){return O.renderJiya(this.context),this},revertJiya:function(){return S.qsa("h-char.bd-jiya, h-cs.jiya-outer",this.context).forEach(function(a){var b=a.classList;b.remove("bd-jiya"),b.remove("jiya-outer")}),this}});var ia="textarea, code, kbd, samp, pre",ja=S.create("h-char","comb-liga");return S.extend(O,{isVowelCombLigaNormal:F(),isVowelICombLigaNormal:G(),isZhuyinCombLigaNormal:H(),isCombLigaNormal:G()(),substVowelCombLiga:I(O.TYPESET["display-as"]["comb-liga-vowel"]),substZhuyinCombLiga:I(O.TYPESET["display-as"]["comb-liga-zhuyin"]),substCombLigaWithPUA:I(O.TYPESET["display-as"]["comb-liga-pua"]),substInaccurateChar:function(a){var a=a||J,b=O.find(a);b.avoid(ia),O.TYPESET["inaccurate-char"].forEach(function(a){b.replace(new RegExp(a[0],"ig"),a[1])})}}),S.extend(O.fn,{"comb-liga-vowel":null,"comb-liga-vowel-i":null,"comb-liga-zhuyin":null,"inaccurate-char":null,substVowelCombLiga:function(){return this["comb-liga-vowel"]=O.substVowelCombLiga(this.context),this},substVowelICombLiga:function(){return this["comb-liga-vowel-i"]=O.substVowelICombLiga(this.context),this},substZhuyinCombLiga:function(){return this["comb-liga-zhuyin"]=O.substZhuyinCombLiga(this.context),this},substCombLigaWithPUA:function(){return O.isVowelCombLigaNormal()?O.isVowelICombLigaNormal()||(this["comb-liga-vowel-i"]=O.substVowelICombLiga(this.context)):this["comb-liga-vowel"]=O.substVowelCombLiga(this.context),O.isZhuyinCombLigaNormal()||(this["comb-liga-zhuyin"]=O.substZhuyinCombLiga(this.context)),this},revertVowelCombLiga:function(){try{this["comb-liga-vowel"].revert("all")}catch(a){}return this},revertVowelICombLiga:function(){try{this["comb-liga-vowel-i"].revert("all")}catch(a){}return this},revertZhuyinCombLiga:function(){try{this["comb-liga-zhuyin"].revert("all")}catch(a){}return this},revertCombLigaWithPUA:function(){try{this["comb-liga-vowel"].revert("all"),this["comb-liga-vowel-i"].revert("all"),this["comb-liga-zhuyin"].revert("all")}catch(a){}return this},substInaccurateChar:function(){return this["inaccurate-char"]=O.substInaccurateChar(this.context),this},revertInaccurateChar:function(){try{this["inaccurate-char"].revert("all")}catch(a){}return this}}),a.addEventListener("DOMContentLoaded",function(){var a;K.classList.contains("han-init")?O.init():(a=J.querySelector(".han-init-context"))&&(O.init=O(a).render())}),("undefined"==typeof b||b===!1)&&(a.Han=O),O}); \ No newline at end of file diff --git a/lib/algolia-instant-search/instantsearch.min.css b/lib/algolia-instant-search/instantsearch.min.css new file mode 100644 index 0000000..590f6f9 --- /dev/null +++ b/lib/algolia-instant-search/instantsearch.min.css @@ -0,0 +1 @@ +/*! instantsearch.js 1.5.0 | © Algolia Inc. and other contributors; Licensed MIT | github.com/algolia/instantsearch.js */.ais-search-box--powered-by{font-size:.8em;text-align:right;margin-top:2px}.ais-search-box--powered-by-link{display:inline-block;width:45px;height:16px;text-indent:101%;overflow:hidden;white-space:nowrap;background-image:url(data:image/png;base64,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);background-repeat:no-repeat;background-size:contain;vertical-align:middle}.ais-pagination--item{display:inline-block;padding:3px}.ais-range-slider--value,.ais-range-slider--value-sub{font-size:.8em;padding-top:15px}.ais-pagination--item__disabled{visibility:hidden}.ais-hierarchical-menu--list__lvl1,.ais-hierarchical-menu--list__lvl2{margin-left:10px}.ais-range-slider--target{position:relative;direction:ltr;background:#F3F4F7;height:6px;margin-top:2em;margin-bottom:2em}.ais-range-slider--base{height:100%;position:relative;z-index:1;border-top:1px solid #DDD;border-bottom:1px solid #DDD;border-left:2px solid #DDD;border-right:2px solid #DDD}.ais-range-slider--origin{position:absolute;right:0;top:0;left:0;bottom:0}.ais-range-slider--connect{background:#46AEDA}.ais-range-slider--background{background:#F3F4F7}.ais-range-slider--handle{width:20px;height:20px;position:relative;z-index:1;background:#FFF;border:1px solid #46AEDA;border-radius:50%;cursor:pointer}.ais-range-slider--handle-lower{left:-10px;bottom:7px}.ais-range-slider--handle-upper{right:10px;bottom:7px}.ais-range-slider--tooltip{position:absolute;background:#FFF;top:-22px;font-size:.8em}.ais-range-slider--pips{box-sizing:border-box;position:absolute;height:3em;top:100%;left:0;width:100%}.ais-range-slider--value{width:40px;position:absolute;text-align:center;margin-left:-20px}.ais-range-slider--marker{position:absolute;background:#DDD;margin-left:-1px;width:1px;height:5px}.ais-range-slider--marker-sub{background:#DDD;width:2px;margin-left:-2px;height:13px}.ais-range-slider--marker-large{background:#DDD;width:2px;margin-left:-2px;height:12px}.ais-star-rating--star,.ais-star-rating--star__empty{display:inline-block;width:1em;height:1em}.ais-range-slider--marker-large:first-child{margin-left:0}.ais-star-rating--item{vertical-align:middle}.ais-star-rating--item__active{font-weight:700}.ais-star-rating--star:before{content:'\2605';color:#FBAE00}.ais-star-rating--star__empty:before{content:'\2606';color:#FBAE00}.ais-star-rating--link__disabled .ais-star-rating--star:before,.ais-star-rating--link__disabled .ais-star-rating--star__empty:before{color:#C9C9C9}.ais-root__collapsible .ais-header{cursor:pointer}.ais-root__collapsed .ais-body,.ais-root__collapsed .ais-footer{display:none} \ No newline at end of file diff --git a/lib/algolia-instant-search/instantsearch.min.js b/lib/algolia-instant-search/instantsearch.min.js new file mode 100644 index 0000000..2bd5d59 --- /dev/null +++ b/lib/algolia-instant-search/instantsearch.min.js @@ -0,0 +1,15 @@ +/*! instantsearch.js 1.5.0 | © Algolia Inc. and other contributors; Licensed MIT | github.com/algolia/instantsearch.js */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.instantsearch=t():e.instantsearch=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}var o=n(1),i=r(o);e.exports=i["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0}),n(2),n(3);var o=n(4),i=r(o),a=n(5),s=r(a),u=n(99),l=r(u),c=n(222),f=r(c),p=n(400),d=r(p),h=n(404),m=r(h),v=n(408),g=r(v),y=n(411),b=r(y),_=n(416),C=r(_),w=n(420),x=r(w),P=n(422),E=r(P),R=n(424),S=r(R),O=n(425),T=r(O),k=n(432),N=r(k),j=n(437),A=r(j),M=n(439),F=r(M),I=n(443),D=r(I),U=n(444),L=r(U),H=n(447),V=r(H),B=n(450),q=r(B),W=n(220),K=r(W),Q=(0,i["default"])(s["default"]);Q.widgets={clearAll:f["default"],currentRefinedValues:d["default"],hierarchicalMenu:m["default"],hits:g["default"],hitsPerPageSelector:b["default"],menu:C["default"],refinementList:x["default"],numericRefinementList:E["default"],numericSelector:S["default"],pagination:T["default"],priceRanges:N["default"],searchBox:A["default"],rangeSlider:F["default"],sortBySelector:D["default"],starRating:L["default"],stats:V["default"],toggle:q["default"]},Q.version=K["default"],Q.createQueryString=l["default"].url.getQueryStringFromState,t["default"]=Q},function(e,t){"use strict";Object.freeze||(Object.freeze=function(e){if(Object(e)!==e)throw new TypeError("Object.freeze can only be called on Objects.");return e})},function(e,t){"use strict";var n={};if(!Object.setPrototypeOf&&!n.__proto__){var r=Object.getPrototypeOf;Object.getPrototypeOf=function(e){return e.__proto__?e.__proto__:r.call(Object,e)}}},function(e,t){"use strict";function n(e){var t=function(){for(var t=arguments.length,n=Array(t),o=0;t>o;o++)n[o]=arguments[o];return new(r.apply(e,[null].concat(n)))};return t.__proto__=e,t.prototype=e.prototype,t}var r=Function.prototype.bind;e.exports=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function s(){return"#"}function u(e,t){if(!t.getConfiguration)return e;var n=t.getConfiguration(e);return(0,y["default"])({},e,n,function(e,t){return Array.isArray(e)?(0,_["default"])(e,t):void 0})}Object.defineProperty(t,"__esModule",{value:!0});var l=Object.assign||function(e){for(var t=1;te;e+=2){var t=re[e],n=re[e+1];t(n),re[e]=void 0,re[e+1]=void 0}G=0}function v(){try{var e=n(11);return Q=e.runOnLoop||e.runOnContext,f()}catch(t){return h()}}function g(e,t){var n=this,r=n._state;if(r===se&&!e||r===ue&&!t)return this;var o=new this.constructor(b),i=n._result;if(r){var a=arguments[r-1];X(function(){F(r,o,a,i)})}else N(n,o,e,t);return o}function y(e){var t=this;if(e&&"object"==typeof e&&e.constructor===t)return e;var n=new t(b);return S(n,e),n}function b(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function C(){return new TypeError("A promises callback cannot return that same promise.")}function w(e){try{return e.then}catch(t){return le.error=t,le}}function x(e,t,n,r){try{e.call(t,n,r)}catch(o){return o}}function P(e,t,n){X(function(e){var r=!1,o=x(n,t,function(n){r||(r=!0,t!==n?S(e,n):T(e,n))},function(t){r||(r=!0,k(e,t))},"Settle: "+(e._label||" unknown promise"));!r&&o&&(r=!0,k(e,o))},e)}function E(e,t){t._state===se?T(e,t._result):t._state===ue?k(e,t._result):N(t,void 0,function(t){S(e,t)},function(t){k(e,t)})}function R(e,t,n){t.constructor===e.constructor&&n===oe&&constructor.resolve===ie?E(e,t):n===le?k(e,le.error):void 0===n?T(e,t):s(n)?P(e,t,n):T(e,t)}function S(e,t){e===t?k(e,_()):a(t)?R(e,t,w(t)):T(e,t)}function O(e){e._onerror&&e._onerror(e._result),j(e)}function T(e,t){e._state===ae&&(e._result=t,e._state=se,0!==e._subscribers.length&&X(j,e))}function k(e,t){e._state===ae&&(e._state=ue,e._result=t,X(O,e))}function N(e,t,n,r){var o=e._subscribers,i=o.length;e._onerror=null,o[i]=t,o[i+se]=n,o[i+ue]=r,0===i&&e._state&&X(j,e)}function j(e){var t=e._subscribers,n=e._state;if(0!==t.length){for(var r,o,i=e._result,a=0;aa;a++)N(r.resolve(e[a]),void 0,t,n);return o}function L(e){var t=this,n=new t(b);return k(n,e),n}function H(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function V(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function B(e){this._id=he++,this._state=void 0,this._result=void 0,this._subscribers=[],b!==e&&("function"!=typeof e&&H(),this instanceof B?I(this,e):V())}function q(e,t){this._instanceConstructor=e,this.promise=new e(b),Array.isArray(t)?(this._input=t,this.length=t.length,this._remaining=t.length,this._result=new Array(this.length),0===this.length?T(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&T(this.promise,this._result))):k(this.promise,this._validationError())}function W(){var e;if("undefined"!=typeof o)e=o;else if("undefined"!=typeof self)e=self;else try{e=Function("return this")()}catch(t){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=e.Promise;n&&"[object Promise]"===Object.prototype.toString.call(n.resolve())&&!n.cast||(e.Promise=me)}var K;K=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var Q,$,z,Y=K,G=0,X=function(e,t){re[G]=e,re[G+1]=t,G+=2,2===G&&($?$(m):z())},J="undefined"!=typeof window?window:void 0,Z=J||{},ee=Z.MutationObserver||Z.WebKitMutationObserver,te="undefined"!=typeof e&&"[object process]"==={}.toString.call(e),ne="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,re=new Array(1e3);z=te?c():ee?p():ne?d():void 0===J?v():h();var oe=g,ie=y,ae=void 0,se=1,ue=2,le=new A,ce=new A,fe=D,pe=U,de=L,he=0,me=B;B.all=fe,B.race=pe,B.resolve=ie,B.reject=de,B._setScheduler=u,B._setAsap=l,B._asap=X,B.prototype={constructor:B,then:oe,"catch":function(e){return this.then(null,e)}};var ve=q;q.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},q.prototype._enumerate=function(){for(var e=this.length,t=this._input,n=0;this._state===ae&&e>n;n++)this._eachEntry(t[n],n)},q.prototype._eachEntry=function(e,t){var n=this._instanceConstructor,r=n.resolve;if(r===ie){var o=w(e);if(o===oe&&e._state!==ae)this._settledAt(e._state,t,e._result);else if("function"!=typeof o)this._remaining--,this._result[t]=e;else if(n===me){var i=new n(b);R(i,e,o),this._willSettleAt(i,t)}else this._willSettleAt(new n(function(t){t(e)}),t)}else this._willSettleAt(r(e),t)},q.prototype._settledAt=function(e,t,n){var r=this.promise;r._state===ae&&(this._remaining--,e===ue?k(r,n):this._result[t]=n),0===this._remaining&&T(r,this._result)},q.prototype._willSettleAt=function(e,t){var n=this;N(e,void 0,function(e){n._settledAt(se,t,e)},function(e){n._settledAt(ue,t,e)})};var ge=W,ye={Promise:me,polyfill:ge};n(12).amd?(r=function(){return ye}.call(t,n,t,i),!(void 0!==r&&(i.exports=r))):"undefined"!=typeof i&&i.exports?i.exports=ye:"undefined"!=typeof this&&(this.ES6Promise=ye),ge()}).call(this)}).call(t,n(9),function(){return this}(),n(10)(e))},function(e,t){function n(){l=!1,a.length?u=a.concat(u):c=-1,u.length&&r()}function r(){if(!l){var e=setTimeout(n);l=!0;for(var t=u.length;t;){for(a=u,u=[];++c1)for(var n=1;n=u.hosts[e.hostType].length&&(d||!h)?u._promise.reject(r):(u.hostIndex[e.hostType]=++u.hostIndex[e.hostType]%u.hosts[e.hostType].length,r instanceof c.RequestTimeout?v():(d||(f=1/0),t(n,s)))}function v(){return u.hostIndex[e.hostType]=++u.hostIndex[e.hostType]%u.hosts[e.hostType].length,s.timeout=u.requestTimeout*(f+1),t(n,s)}var g;if(u._useCache&&(g=e.url),u._useCache&&r&&(g+="_body_"+s.body),u._useCache&&a&&void 0!==a[g])return i("serving response from cache"),u._promise.resolve(JSON.parse(a[g]));if(f>=u.hosts[e.hostType].length)return!h||d?(i("could not get any response"),u._promise.reject(new c.AlgoliaSearchError("Cannot connect to the AlgoliaSearch API. Send an email to support@algolia.com to report and resolve the issue. Application id was: "+u.applicationID))):(i("switching to fallback"),f=0,s.method=e.fallback.method,s.url=e.fallback.url,s.jsonBody=e.fallback.body,s.jsonBody&&(s.body=l(s.jsonBody)),o=u._computeRequestHeaders(),s.timeout=u.requestTimeout*(f+1),u.hostIndex[e.hostType]=0,d=!0,t(u._request.fallback,s));var y=u.hosts[e.hostType][u.hostIndex[e.hostType]]+s.url,b={body:s.body,jsonBody:s.jsonBody,method:s.method,headers:o,timeout:s.timeout,debug:i};return i("method: %s, url: %s, headers: %j, timeout: %d",b.method,y,b.headers,b.timeout),n===u._request.fallback&&i("using fallback"),n.call(u,y,b).then(p,m)}var r,o,i=n(42)("algoliasearch:"+e.url),a=e.cache,u=this,f=0,d=!1,h=u._useFallback&&u._request.fallback&&e.fallback;this.apiKey.length>p&&void 0!==e.body&&void 0!==e.body.params?(e.body.apiKey=this.apiKey,o=this._computeRequestHeaders(!1)):o=this._computeRequestHeaders(),void 0!==e.body&&(r=l(e.body)),i("request start");var m=t(u._request,{url:e.url,method:e.method,body:r,jsonBody:e.body,timeout:u.requestTimeout*(f+1)});return e.callback?void m.then(function(t){s(function(){e.callback(null,t)},u._setTimeout||setTimeout)},function(t){s(function(){e.callback(t)},u._setTimeout||setTimeout)}):m},_getSearchParams:function(e,t){if(void 0===e||null===e)return t;for(var n in e)null!==n&&void 0!==e[n]&&e.hasOwnProperty(n)&&(t+=""===t?"":"&",t+=n+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(e[n])?l(e[n]):e[n]));return t},_computeRequestHeaders:function(e){var t=n(15),r={"x-algolia-agent":this._ua,"x-algolia-application-id":this.applicationID};return e!==!1&&(r["x-algolia-api-key"]=this.apiKey),this.userToken&&(r["x-algolia-usertoken"]=this.userToken),this.securityTags&&(r["x-algolia-tagfilters"]=this.securityTags),this.extraHeaders&&t(this.extraHeaders,function(e){r[e.name]=e.value}),r}},r.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(e,t,n){var r=this;return 1!==arguments.length&&"function"!=typeof t||(n=t,t=void 0),this.as._jsonRequest({method:void 0!==t?"PUT":"POST",url:"/1/indexes/"+encodeURIComponent(r.indexName)+(void 0!==t?"/"+encodeURIComponent(t):""),body:e,hostType:"write",callback:n})},addObjects:function(e,t){var r=n(34),o="Usage: index.addObjects(arrayOfObjects[, callback])";if(!r(e))throw new Error(o);for(var i=this,a={requests:[]},s=0;sa&&(t=a),"published"!==e.status?c._promise.delay(t).then(n):e})}function r(e){s(function(){t(null,e)},c._setTimeout||setTimeout)}function o(e){s(function(){t(e)},c._setTimeout||setTimeout)}var i=100,a=5e3,u=0,l=this,c=l.as,f=n();return t?void f.then(r,o):f},clearIndex:function(e){var t=this;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(t.indexName)+"/clear",hostType:"write",callback:e})},getSettings:function(e){var t=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(t.indexName)+"/settings",hostType:"read",callback:e})},setSettings:function(e,t){var n=this;return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(n.indexName)+"/settings",hostType:"write",body:e,callback:t})},listUserKeys:function(e){var t=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(t.indexName)+"/keys",hostType:"read",callback:e})},getUserKeyACL:function(e,t){var n=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(n.indexName)+"/keys/"+e,hostType:"read",callback:t})},deleteUserKey:function(e,t){var n=this;return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(n.indexName)+"/keys/"+e,hostType:"write",callback:t})},addUserKey:function(e,t,r){var o=n(34),i="Usage: index.addUserKey(arrayOfAcls[, params, callback])";if(!o(e))throw new Error(i);1!==arguments.length&&"function"!=typeof t||(r=t,t=null);var a={acl:e};return t&&(a.validity=t.validity,a.maxQueriesPerIPPerHour=t.maxQueriesPerIPPerHour,a.maxHitsPerQuery=t.maxHitsPerQuery,a.description=t.description,t.queryParameters&&(a.queryParameters=this.as._getSearchParams(t.queryParameters,"")),a.referers=t.referers),this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys",body:a,hostType:"write",callback:r})},addUserKeyWithValidity:u(function(e,t,n){return this.addUserKey(e,t,n)},a("index.addUserKeyWithValidity()","index.addUserKey()")),updateUserKey:function(e,t,r,o){var i=n(34),a="Usage: index.updateUserKey(key, arrayOfAcls[, params, callback])";if(!i(t))throw new Error(a);2!==arguments.length&&"function"!=typeof r||(o=r,r=null);var s={acl:t};return r&&(s.validity=r.validity,s.maxQueriesPerIPPerHour=r.maxQueriesPerIPPerHour,s.maxHitsPerQuery=r.maxHitsPerQuery,s.description=r.description,r.queryParameters&&(s.queryParameters=this.as._getSearchParams(r.queryParameters,"")),s.referers=r.referers),this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys/"+e,body:s,hostType:"write",callback:o})},_search:function(e,t,n){return this.as._jsonRequest({cache:this.cache,method:"POST",url:t||"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:{params:e},hostType:"read",fallback:{method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName),body:{params:e}},callback:n})},as:null,indexName:null,typeAheadArgs:null,typeAheadValueOption:null}},function(e,t,n){"use strict";function r(e,t){var r=n(15),o=this;"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):o.stack=(new Error).stack||"Cannot get a stacktrace, browser is too old",this.name=this.constructor.name,this.message=e||"Unknown error",t&&r(t,function(e,t){o[t]=e})}function o(e,t){function n(){var n=Array.prototype.slice.call(arguments,0);"string"!=typeof n[0]&&n.unshift(t),r.apply(this,n),this.name="AlgoliaSearch"+e+"Error"}return i(n,r),n}var i=n(7);i(r,Error),e.exports={AlgoliaSearchError:r,UnparsableJSON:o("UnparsableJSON","Could not parse the incoming response as JSON, see err.more for details"),RequestTimeout:o("RequestTimeout","Request timedout before getting a response"),Network:o("Network","Network issue, see err.more for details"),JSONPScriptFail:o("JSONPScriptFail"," + + + + + + + + + + 标签: Android实践 | Xu的博客 + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" new file mode 100644 index 0000000..84659fe --- /dev/null +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -0,0 +1,591 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Android知识点 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/Python/index.html b/tags/Python/index.html new file mode 100644 index 0000000..79d45f5 --- /dev/null +++ b/tags/Python/index.html @@ -0,0 +1,563 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Python | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..cc63fe1 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 文章标签 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + +
+
+ +

文章标签

+ + + +
+ + + + +
+ + +
+
+ 目前共计 3 个标签 +
+ +
+ +
+ + + +
+ + + +
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ +
+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b6564e7a399f0f6209600236eae0e61e3ea1a043 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 7 Nov 2017 21:31:45 +0800 Subject: [PATCH 03/64] Site updated: 2017-11-07 21:31:44 --- .../index.html" | 28 +++- .../index.html" | 31 +++- .../index.html" | 22 ++- .../index.html" | 30 +++- archives/2017/10/index.html | 12 +- archives/2017/11/index.html | 12 +- archives/2017/index.html | 12 +- archives/index.html | 12 +- categories/Android/index.html | 12 +- categories/index.html | 12 +- css/main.css | 9 +- favicon.ico | Bin 0 -> 1150 bytes index.html | 148 +++++------------- .../index.html" | 12 +- .../index.html" | 12 +- tags/Python/index.html | 12 +- tags/index.html | 12 +- 17 files changed, 228 insertions(+), 160 deletions(-) create mode 100644 favicon.ico diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index f550b95..0b58e07 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -79,18 +79,18 @@ - + - + - + - + @@ -304,6 +304,11 @@

Android Handler机制理解

+ + 阅读量 + 次 + + @@ -323,6 +328,7 @@

Android Handler机制理解

1.何谓Handler机制?

一般来说,当你的应用被创建的时候,会创建一条应用的主线程。因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

+

当然,你可以创建自己的线程去做操作,但如何应用的主线程通信呢。那就要使用到Handler机制了。如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的相关方法实现的。

那接下来就要介绍一下这个消息通讯机制Handler,涉及到三个主要的类:Looper,Handler和Message类。

2.Looper

重点方法为:prepare()和loop()
Looper#prepare():

1
2
3
4
5
6
7
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

@@ -522,7 +528,7 @@

+
@@ -556,16 +562,24 @@

| + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index 3356ba2..eb89e9c 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -79,18 +79,18 @@ - + - + - + - + @@ -304,6 +304,11 @@

Android View动画和属性动 + + 阅读量 + 次 + + @@ -322,7 +327,9 @@

Android View动画和属性动 -

一:View动画

简介:View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,是一种渐近式动画,并且View动画支持自定义。
1.View动画主要分为四类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation,可通过XML或者Java代码声明使用,动画XML文件需要放在res/anim/filename.xml中。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<set xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:interpolator=""
    android:shareInterpolator="["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate 
        android:fromXDelta="float" 
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate 
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
</set>

+

一:View动画

简介:View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,是一种渐近式动画,并且View动画支持自定义。

+ +

1.View动画主要分为四类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation,可通过XML或者Java代码声明使用,动画XML文件需要放在res/anim/filename.xml中。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<set xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:interpolator=""
    android:shareInterpolator="["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate 
        android:fromXDelta="float" 
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate 
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
</set>

Java代码:

1
2
3
4
5
// 使用Java代码加载XML动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
mButton.startAnimation(animation);
// 使用Java代码创建动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);

2.View动画既可以是单个动画,也可以由一系列动画组成。
3.几个标签解读:
set:
表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的。
android:interpolator:
表示动画集合所采用的插值器,什么是插值器呢?它影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。属性可不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator:
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值。
其余的属性网上都能查到,这里就不详细描述了。

二:属性动画

简介:属性动画通过动态地改变相关对象的属性,比如长宽等,从而实现动画效果,属性动画为API 11(Android 3.0)以上的新特性,在低版本无法直接使用属性动画,但仍然可通过兼容库(NineOldAndroids)去使用。
属性动画有ValueAnimator、ObjectAnimator和AnimatorSet等概念。其中ObjectAnimator继承自ValueAnimator、AnimatorSet是动画集合,可以定义一组动画。
(1)使用
举例:改变一个对象(myObject)的translationY属性,让其沿着Y轴上平移一段距离:

1
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();

@@ -510,7 +517,7 @@

+
@@ -544,16 +551,24 @@

| + + - +
+ + + + + +
+ diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index 65ff46a..e699b6a 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -87,7 +87,7 @@ - + @@ -304,6 +304,11 @@

分享一下自己做的一个 + + 阅读量 + 次 + + @@ -325,6 +330,7 @@

分享一下自己做的一个

这是一个我自己做的一个Android的自定义图片加载库,主要参考了网上一些大神写过的一些图片加载库,再结合自己的一些想法理解去做了一个较为完整的图片加载库。当然代码中也会存在一些不足的情况,例如代码架构方面不是非常的完善。希望大家提出意见,一起去改进!

注意:这是一个用于学习图片加载与缓存的库,不推荐使用在实际项目之中!

Github地址:https://github.com/XuDeveloper/XImageLoader

+

如果你想改进这个图片加载库,欢迎在GitHub上fork这个项目然后pull request给我!如果你喜欢它,请给这个项目一个star或者关注我的GitHub!谢谢你们的支持!

导入

Android Studio

1
2
3
4
5
6
7
8
9
10
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
dependencies {
compile 'com.github.XuDeveloper:XImageLoader:v1.0'
}

Eclipse

@@ -523,7 +529,7 @@

使用 +
@@ -557,16 +563,24 @@

使用| + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index 405ea1f..8240d7e 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -79,23 +79,23 @@ - + - + - + - + @@ -310,6 +310,11 @@

Android adb命令的一些实际 + + 阅读量 + 次 + + @@ -329,8 +334,9 @@

Android adb命令的一些实际

在开发应用的过程中,安卓平台给大家提供了非常多的调试工具,包括Android Studio本身自带的工具,如果不想使用Studio的话,也可以在终端使用adb工具进行调试。

-

关于adb的用法网上有很多教程,这里推荐一个https://github.com/mzlogin/awesome-adb。

+

关于adb的用法网上有很多教程,这里推荐一个较为完整的指南https://github.com/mzlogin/awesome-adb。

今天记录一下我在实际情况中对adb的运用。

+

1.关于adb shell input text的问题

在使用这个命令的时候,我遇到了一个情况,就是无法输入”&”。我在网上搜了一下,在StackOverFlow里面,解决方案是这样的:
adb shell input text “\&”

这个方案在终端运行是可以的,但是我是用python写脚本运行的,这样做是无法成功的。

1
2
cmd = "adb shell input text '\&'"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
@@ -547,7 +553,7 @@

+
@@ -581,16 +587,24 @@

| + + - +
+ + + + + +
+ diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 1bc8a04..8ec9eda 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -481,16 +481,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 2db0a9d..067bd73 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -407,16 +407,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/archives/2017/index.html b/archives/2017/index.html index 8f1e7bf..9d09f07 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -518,16 +518,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/archives/index.html b/archives/index.html index f98a3d4..46e89d1 100644 --- a/archives/index.html +++ b/archives/index.html @@ -518,16 +518,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/categories/Android/index.html b/categories/Android/index.html index cbfc169..68f707d 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -472,16 +472,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/categories/index.html b/categories/index.html index e68b778..e3d04d6 100644 --- a/categories/index.html +++ b/categories/index.html @@ -381,16 +381,24 @@

文章分类

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/css/main.css b/css/main.css index 67dbe82..2aa4b87 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #2296ff; + background: #2518c2; } .links-of-blogroll { font-size: 13px; @@ -2365,13 +2365,6 @@ pre .javascript .function { .page-pv .busuanzi-value { margin: 0 5px; } -.site-uv { - margin-right: 10px; -} -.site-uv::after { - content: "|"; - padding-left: 10px; -} .page-archive .archive-page-counter { position: relative; top: 3px; diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5b896214d4990aa4b95d41d2a33a6f6d89cb4aaa GIT binary patch literal 1150 zcmb7^?@tqV9LKw4&&(1Z?a{KCC2l^LW=h!D7R{n=f(p1{k@3r_Z2`d+n8s|VGxD>g zKWM${_1a#qy(=KJ6pFYE%!II@OVAmOV~aAg>>qjU+v=YAWOsSp`~Cgg`}?`?eII-+ zme<6+b*n`@w^`a>w^-h^SS&jPq9iIW>Ox+5**ZPg&W>=p%Y)rDinBXFdv6eDj~_?3 z4~Kh{GcGS@dj{#~8KTWCa1G+<8o=J!_fPInGtKj$fB?ILS;2`?zt{u_5+xJFm+ z0PgeG=q&bf576Q2r}0!D<)2h>@Nf&(=5IOH;Noaq2m20Msn}c1{>mmk|KbF-);7NW z#$BSe{v0*OyJ)oa(dO)B@28)!Wy>49_4f7>?`+#a`JOVqJg}dWEjD(2u#-cTUoq^J z=^qT!+-&3h?e9@_q?Kc}$Emkkx%6Eh?GA^)&ebcIvDVd5TU|pTpXX##6V*qKvcCR+ zU+=D=Dp4958YtWS5vHy&m!0Ct!v{RRzs|;^hy3~Y5jRYO<=adAvV4b&=Pz*hP$e51 zf8g_a*u8TX&7x;T*&g~_Ey$xoJllA}?<4!QTXPjP2g>;P!*_AmYG^%CNozwrt2ZZE z`7uu>GJ^MeC)v37MGwu#tRA3{4ze(zkTc_?)DV#|9}{Vfg`B~BMiN;cIg!n20kR1{ z(}|$a05gV4-cXp$M3~Dc6wEO5lQ9aDaS9W0X3YpQdXQZ5<-C}eDb>S7)X!ur#8gsY zI;D#5CYVl!xDoRR9b+mUU_uc&)hBX6##KM#Q6J+GFB#cGeDo5U{|EHIRnnmW%q0e>5f!~el%%W(IXW2hlT5J3q_L;_)yF-eS7;!+Y#9Y>31&=Xk - -

在开发应用的过程中,安卓平台给大家提供了非常多的调试工具,包括Android Studio本身自带的工具,如果不想使用Studio的话,也可以在终端使用adb工具进行调试。

-

关于adb的用法网上有很多教程,这里推荐一个https://github.com/mzlogin/awesome-adb。

+

在开发应用的过程中,安卓平台给大家提供了非常多的调试工具,包括Android Studio本身自带的工具,如果不想使用Studio的话,也可以在终端使用adb工具进行调试。

+

关于adb的用法网上有很多教程,这里推荐一个较为完整的指南https://github.com/mzlogin/awesome-adb。

今天记录一下我在实际情况中对adb的运用。

-

1.关于adb shell input text的问题

在使用这个命令的时候,我遇到了一个情况,就是无法输入”&”。我在网上搜了一下,在StackOverFlow里面,解决方案是这样的:
adb shell input text “\&”

-

这个方案在终端运行是可以的,但是我是用python写脚本运行的,这样做是无法成功的。

-
1
2
cmd = "adb shell input text '\&'"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
-

想了很久,最后使用以下解决方法,原因我也不太理解,不知道有没有人来解答一下~

-
1
2
3
cmd = "adb shell input text '\&'"
cmd = cmd.replace('&', "\"\&\"")
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
-

2.关于grep

grep命令起源于Linux系统。grep命令全称是Global Regular Expression Print,是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。在使用adb命令的时候,我们也经常需要使用到它。

-

举一个例子:

1
adb shell dumpsys window policy

-

这个命令会展示出android当前窗口(window)的所有属性信息:

-

image.png

-

如果我们想在其中提取出mShowingLockscreen属性要怎么做?

-

网上给了这样一种方法:

1
adb shell dumpsys window policy | grep mShowingLockscreen

-

同样在终端使用以及用python写脚本运行,出现问题:

-

系统终端测试.png

-

git bash终端测试.png

-

(真是奇怪,同样是终端,差别咋怎么大,无法理解…….)

-

原因不明,下面给出几种解决方案:

-
    -
  • 使用findstr
  • -
-

findstr相当于Windows下的grep命令。

1
adb shell dumpsys window policy | findstr mShowingLockscreen

-

运行成功!

-

success1.png

-
    -
  • 使用^| grep
  • -
-

个人理解:^| 有点类似于转义的作用

1
adb shell dumpsys window policy ^| grep mShowingLockscreen

-

运行成功!

-

success2.png

-

3.关于python运行adb命令返回结果的问题

一般情况下,使用python运行adb命令是非常方便的,例如:

-
1
2
3
import subprocess
cmd = "adb shell input text test"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-

但其实它只对一些立即返回结果的命令有用,对于一些需要一定等待时间的命令,它有时就会出现错误,例如:

-
1
2
3
4
import subprocess
cmd = "adb shell ping -c 4 www.baidu.com"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print "test"
-

这样执行会出现错误,会直接输出”test”。
这是因为subprocess.Popen对象创建后,主程序并不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block):

-
1
2
3
4
5
import subprocess
cmd = "adb shell ping -c 4 www.baidu.com"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.wait()
print "test"
-

大家可以运行对比一下~

-

4.如何获取一段时间的logcat日志

使用的是adb logcat命令,具体的参数网上有很多,这里就不详细展开。

-

这里记录一下我是如何获取一段时间的logcat日志的:

1
2
3
4
5
cmd = "adb logcat -v time"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
time.sleep(10) # 这里用time.sleep模拟了一段时间,可以把它替换成你需要执行的操作
p.terminate() # 终止程序,相当于终端用Ctrl + C
result = p.communicate()[0] # 获取执行操作前后的日志

-

如有问题,请大家踊跃提出,谢谢大家!

- - + +
+ + 阅读全文 » + +
+ @@ -506,30 +472,16 @@

- -

这是一个我自己做的一个Android的自定义图片加载库,主要参考了网上一些大神写过的一些图片加载库,再结合自己的一些想法理解去做了一个较为完整的图片加载库。当然代码中也会存在一些不足的情况,例如代码架构方面不是非常的完善。希望大家提出意见,一起去改进!

+

这是一个我自己做的一个Android的自定义图片加载库,主要参考了网上一些大神写过的一些图片加载库,再结合自己的一些想法理解去做了一个较为完整的图片加载库。当然代码中也会存在一些不足的情况,例如代码架构方面不是非常的完善。希望大家提出意见,一起去改进!

注意:这是一个用于学习图片加载与缓存的库,不推荐使用在实际项目之中!

Github地址:https://github.com/XuDeveloper/XImageLoader

-

如果你想改进这个图片加载库,欢迎在GitHub上fork这个项目然后pull request给我!如果你喜欢它,请给这个项目一个star或者关注我的GitHub!谢谢你们的支持!

-

导入

Android Studio

1
2
3
4
5
6
7
8
9
10
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
dependencies {
compile 'com.github.XuDeveloper:XImageLoader:v1.0'
}
-

Eclipse

-

可以复制源码到你的项目中!

-
-

使用

默认用法:

-
1
2
3
4
5
// 异步接口调用
XImageLoader.build(context).imageview(ImageView).load(imageUrl);
// 加载本地文件,你需要使用这样的格式:"file:///address"
XImageLoader.build(context).imageview(ImageView).load("file:///address");
-

或者:

-
1
2
3
// 同步接口调用(需要运行在一条新线程中)
Bitmap bitmap = XImageLoader.build(context).imageview(ImageView).getBitmap(imageUrl);
-

你可以选择是否缓存或者自定义(使用XImageLoaderConfig):

-
1
2
3
4
5
6
7
8
9
10
11
12
XImageLoader.build(context).imageview(isMemoryCache, isDiskCache, ImageView).load(imageUrl);
XImageLoader.build(context).imageview(isDoubleCache, ImageView).load(imageUrl);
// 具体配置
XImageLoaderConfig config = new XImageLoaderConfig();
config.setCache(new DoubleCache(context));
config.setLoader(new OkhttpImageLoader());
config.setLoadingResId(R.drawable.image_loading);
config.setFailResId(R.drawable.image_fail);
XImageLoader.build(context).imageview(config, ImageView).load(imageUrl);
-

你需要AndroidManifest.xml中设置权限:

-
1
2
3
4
5
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-

如果你使用的是Android 6.0以上的设备,你需要动态设置权限:

-
1
2
XImageLoader.verifyStoragePermissions(activity);
-

代码架构设计:
采用流式编程写法,将加载一张图片分为以下几步:
1.初始化一些基本设置(XImageLoaderConfig ),如是否加入缓存,设置加载时显示的图片资源以及加载失败时显示的图片资源还有图片加载器ImageLoader;(如果不设置下面会自动设置)
2.设置需要加载的ImageView;
3.设置需要加载的图片路径,可以是网络上的图片,也可以是手机本地图片,如果前面没设置ImageLoader,在这里就可以根据路径来动态选择加载哪个特定的ImageLoader;
加载图片有两种方法,一种是同步方法获取图片Bitmap,另一种是在线程池中异步加载图片。

-

加载图片的优化方法:
1.加入缓存机制,包括LruCache以及DiskLruCache;
2.加载时先预读取图片,根据尺寸计算inSampleSize,对图片进行适当压缩,防止OOM;
3.采用线程池加载。

-

需要进一步优化的地方:
读取需要加载的ImageView尺寸时用到了反射的方法,影响了一定的效率;

- - + +
+ + 阅读全文 » + +
+ @@ -668,17 +620,14 @@

- -

一:View动画

简介:View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,是一种渐近式动画,并且View动画支持自定义。
1.View动画主要分为四类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation,可通过XML或者Java代码声明使用,动画XML文件需要放在res/anim/filename.xml中。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<set xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:interpolator=""
    android:shareInterpolator="["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate 
        android:fromXDelta="float" 
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate 
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
</set>

-

Java代码:

1
2
3
4
5
// 使用Java代码加载XML动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
mButton.startAnimation(animation);
// 使用Java代码创建动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);

-

2.View动画既可以是单个动画,也可以由一系列动画组成。
3.几个标签解读:
set:
表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的。
android:interpolator:
表示动画集合所采用的插值器,什么是插值器呢?它影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。属性可不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator:
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值。
其余的属性网上都能查到,这里就不详细描述了。

-

二:属性动画

简介:属性动画通过动态地改变相关对象的属性,比如长宽等,从而实现动画效果,属性动画为API 11(Android 3.0)以上的新特性,在低版本无法直接使用属性动画,但仍然可通过兼容库(NineOldAndroids)去使用。
属性动画有ValueAnimator、ObjectAnimator和AnimatorSet等概念。其中ObjectAnimator继承自ValueAnimator、AnimatorSet是动画集合,可以定义一组动画。
(1)使用
举例:改变一个对象(myObject)的translationY属性,让其沿着Y轴上平移一段距离:

1
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();

-

(2)插值器和估值器:
属性动画有两个新概念:
插值器:根据时间流逝的百分比来计算出属性值改变的百分比,对应的接口是Interpolator;
估值器:根据属性改变的百分比计算出属性的改变值,对应的接口是TypeEvaluator;
代码设置:

1
2
ValueAnimator.setEvaluator(TypeEvaluator evaluator)
ValueAnimator.setInterpolator(TimeInterpolator value)

-

(3)属性动画的监听器
属性动画提供了监听器用于监听动画的播放过程。主要有如下两个接口:AnimatorUpdateListener和AnimatorListener。

1
2
3
4
5
6
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}

-

它可以监听动画的开始、结束、取消以及重复播放。系统提供了AnimatorListener的适配器类AnimatorListenerAdapter。
AnimatorUpdateListener:

1
2
3
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}

-

AnimatorUpdateListener会监听整个动画过程,动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被调用一次。
(4)对任意属性做动画
属性动画的原理:属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切地说是随着时间的推移,所传递的值越来越接近最终值。
总结,对object的属性abc做动画,需满足条件:
(1)object必须提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取abc属性的初始值;
(2)object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变。
建议:
①、给对象加上get和set方法;
②、用一个类来包装原始对象,间接为其提供get和set方法;
③、采用ValueAnimator,监听动画过程,实现属性的变化。

- - +

一:View动画

简介:View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,是一种渐近式动画,并且View动画支持自定义。

+ +
+ + 阅读全文 » + +
+ @@ -817,33 +766,14 @@

- -

1.何谓Handler机制?

一般来说,当你的应用被创建的时候,会创建一条应用的主线程。因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

-

当然,你可以创建自己的线程去做操作,但如何应用的主线程通信呢。那就要使用到Handler机制了。如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的相关方法实现的。

-

那接下来就要介绍一下这个消息通讯机制Handler,涉及到三个主要的类:Looper,Handler和Message类。

-

2.Looper

重点方法为:prepare()和loop()
Looper#prepare():

1
2
3
4
5
6
7
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

-

解释:首先它创建了一个ThreadLocal对象,它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据。然后在prepare方法中将looper存储在线程里面。

-

Looper#Looper():

1
2
3
4
5
private Looper(boolean quitAllowed) {  
     mQueue = new MessageQueue(quitAllowed);  
     mRun = true;  
     mThread = Thread.currentThread();  
}

-

而在构造方法中,Looper创建了一个MessageQueue,虽然是叫queue但其实内部实现是一个单链表。

-

Looper#loop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static void loop() {  
        final Looper me = myLooper();  
        if (me == null) {  
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }  
        final MessageQueue queue = me.mQueue;  
        // Make sure the identity of this thread is that of the local process,  
        // and keep track of what that identity token actually is.  
        Binder.clearCallingIdentity();  
        final long ident = Binder.clearCallingIdentity();  
        for (;;) {  
            Message msg = queue.next(); // might block  
            if (msg == null) {  
                // No message indicates that the message queue is quitting.  
                return;  
            }  
            // This must be in a local variable, in case a UI event sets the logger 
            Printer logging = me.mLogging;  
            if (logging != null) {  
                logging.println(">>>>> Dispatching to " + msg.target + " " +  
                        msg.callback + ": " + msg.what);  
            }    
            msg.target.dispatchMessage(msg);    
            if (logging != null) {  
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
            }   
            // Make sure that during the course of dispatching the  
            // identity of the thread wasn't corrupted.  
            final long newIdent = Binder.clearCallingIdentity();  
            if (ident != newIdent) {  
                Log.wtf(TAG, "Thread identity changed from 0x"  
                        + Long.toHexString(ident) + " to 0x"  
                        + Long.toHexString(newIdent) + " while dispatching to "  
                        + msg.target.getClass().getName() + " "  
                        + msg.callback + " what=" + msg.what);  
            }    
            msg.recycle();  
        }  
public static Looper myLooper() {
      return sThreadLocal.get();
}
}

-

解释:方法直接返回了前面sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。拿到该looper实例中的mQueue即消息队列后进入了无限循环,不断从队列中取出一条消息,如果没有消息则阻塞。如果取得消息使用调用msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。而msg的target是什么呢?其实就是前面讲到的handler对象,最后会释放消息占据的资源。

-

Looper类总结:
1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2.loop()方法,不断从MessageQueue中去取消息,交给message的target的dispatchMessage去处理。

-

接下来就要讲发送消息的对象了,这个对象就是Handler。

-

3.Handler

主要作用是将一个任务切换到某个指定的线程中去执行,同时为了解决在子线程中无法访问UI的矛盾。

-

所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的。

-

Handler#Handler():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Handler() {  
        this(nullfalse);  
}  
public Handler(Callback callback, boolean async) {  
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());  
            }  
        }    
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
}

-

解释:在构造的时候会检查当前的Handler是否为静态类,不是静态声明的话会打印Log,提示会有内存泄漏现象的产生,然后通过Looper.myLooper()方法获取到当前线程的Looper实例(mLooper)并进一步获取到当前线程的消息队列(mQueue),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

-

使用的时候我们会经常使用到sendMessage方法,我们来看看源码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final boolean sendMessage(Message msg)  {  
     return sendMessageDelayed(msg, 0);  
 }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
     Message msg = Message.obtain();  
     msg.what = what;  
     return sendMessageDelayed(msg, delayMillis);  
 } 
public final boolean sendMessageDelayed(Message msg, long delayMillis) {  
       if (delayMillis < 0) {  
           delayMillis = 0;  
       }  
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
}  
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
       MessageQueue queue = mQueue;  
       if (queue == null) {  
           RuntimeException e = new RuntimeException(  
                   this + " sendMessageAtTime() called with no mQueue");  
           Log.w("Looper", e.getMessage(), e);  
           return false;  
       }  
       return enqueueMessage(queue, msg, uptimeMillis);  
   }  
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
       msg.target = this;  
       if (mAsynchronous) {  
           msg.setAsynchronous(true);  
       }  
       return queue.enqueueMessage(msg, uptimeMillis);  
}

-

一路跳到最后的enqueueMessage方法,enqueueMessage中首先为msg.target赋值为this,因为Looper中的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,保存到消息队列中去。

-

现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispatchMessage方法。
Handler#dispatchMessage():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void dispatchMessage(Message msg) {  
        if (msg.callback != null) { 
            // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);  // 并直接调用callback的run方法!
        } else { 
            // 如果handler本身设置了callback,则执行callback 
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }
// 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);  
        }  
}``` 
几个变量和方法的解释: 
1.callback:message携带的Runnable对象,实际上就是Handler的post方法所传递的Runnable参数。
我们来看一下Handler的post方法源码实现:
```java
mHandler.post(new Runnable()  {  
        @Override  
        public void run()  {  
            // code  
        }  
});

-

其实这个Runnable并没有创建什么线程,而是发送了一条消息:

1
2
3
4
5
6
7
8
public final boolean post(Runnable r)  {  
      return  sendMessageDelayed(getPostMessage(r), 0);  
}  
private static Message getPostMessage(Runnable r) {  
      Message m = Message.obtain();  
      m.callback = r;  
      return m;  
}

-

在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message。
注意:产生一个Message对象,可以new,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new重新分配内存。

-

2.mCallback:可通过Handler handler = new Handler(callback); 可以用来创建一个Handler实例但不需要派生Handler子类。它可用来拦截消息!当mCallback的handleMessage返回true的时候可以拦截消息,具体的逻辑看上面的代码很容易理解!

-

3.handleMessage(msg):它是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。

-

到此,这个流程已经解释完毕,总结一下:
1.首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2.Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与MessageQueue相关联。
4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)。
6.在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

-

下面是个人认为在 Activity 中一个合格的 Handler 该有的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class MyHandler extends Handler {
private WeakReference<CustomActivity> activityWeakReference;
public MyHandler(CustomActivity activity) {
activityWeakReference = new WeakReference<CustomActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
CustomActivity activtiy = activityWeakReference.get();
if (activity != null) {
// code
}
}
}

- - +

1.何谓Handler机制?

一般来说,当你的应用被创建的时候,会创建一条应用的主线程。因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

+ +
+ + 阅读全文 » + +
+ @@ -1002,16 +932,24 @@

| + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index 686b917..f7acd62 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -415,16 +415,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 84659fe..4c61815 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -415,16 +415,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/tags/Python/index.html b/tags/Python/index.html index 79d45f5..5086b31 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -387,16 +387,24 @@

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ diff --git a/tags/index.html b/tags/index.html index cc63fe1..05e9e6d 100644 --- a/tags/index.html +++ b/tags/index.html @@ -381,16 +381,24 @@

文章标签

- + + -
主题 — NexT.Pisces v5.1.3
+
+ + + + + +
+ From 12e0e744c2b0f6bb4992371cd51077624af47f5e Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 7 Nov 2017 21:33:38 +0800 Subject: [PATCH 04/64] Site updated: 2017-11-07 21:33:37 --- .../index.html" | 2 +- .../index.html" | 2 +- .../index.html" | 2 +- .../index.html" | 2 +- archives/2017/10/index.html | 2 +- archives/2017/11/index.html | 2 +- archives/2017/index.html | 2 +- archives/index.html | 2 +- categories/Android/index.html | 2 +- categories/index.html | 2 +- css/main.css | 2 +- index.html | 2 +- "tags/Android\345\256\236\350\267\265/index.html" | 2 +- "tags/Android\347\237\245\350\257\206\347\202\271/index.html" | 2 +- tags/Python/index.html | 2 +- tags/index.html | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index 0b58e07..b2b6bd7 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -558,7 +558,7 @@

-
Hexo 强力驱动
+ diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index eb89e9c..d4dc905 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -547,7 +547,7 @@

-
Hexo 强力驱动
+ diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index e699b6a..0fca615 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -559,7 +559,7 @@

使用 -
Hexo 强力驱动
+ diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index 8240d7e..ed4d548 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -583,7 +583,7 @@

Hexo 强力驱动 + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 8ec9eda..5a30078 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -477,7 +477,7 @@

-
Hexo 强力驱动
+ diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 067bd73..641faf2 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -403,7 +403,7 @@

-
Hexo 强力驱动
+ diff --git a/archives/2017/index.html b/archives/2017/index.html index 9d09f07..8de3f4a 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -514,7 +514,7 @@

-
Hexo 强力驱动
+ diff --git a/archives/index.html b/archives/index.html index 46e89d1..aa76313 100644 --- a/archives/index.html +++ b/archives/index.html @@ -514,7 +514,7 @@

-
Hexo 强力驱动
+ diff --git a/categories/Android/index.html b/categories/Android/index.html index 68f707d..1857ee9 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -468,7 +468,7 @@

-
Hexo 强力驱动
+ diff --git a/categories/index.html b/categories/index.html index e3d04d6..0a08039 100644 --- a/categories/index.html +++ b/categories/index.html @@ -377,7 +377,7 @@

文章分类

-
Hexo 强力驱动
+ diff --git a/css/main.css b/css/main.css index 2aa4b87..6998358 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #2518c2; + background: #818a16; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index e003537..ed25fb2 100644 --- a/index.html +++ b/index.html @@ -928,7 +928,7 @@

Hexo 强力驱动 + diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index f7acd62..fa5dfa6 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -411,7 +411,7 @@

-
Hexo 强力驱动
+ diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 4c61815..2980d1f 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -411,7 +411,7 @@

-
Hexo 强力驱动
+ diff --git a/tags/Python/index.html b/tags/Python/index.html index 5086b31..cb318ad 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -383,7 +383,7 @@

-
Hexo 强力驱动
+ diff --git a/tags/index.html b/tags/index.html index 05e9e6d..03c0f95 100644 --- a/tags/index.html +++ b/tags/index.html @@ -377,7 +377,7 @@

文章标签

-
Hexo 强力驱动
+ From f4cf7473b00f1f263f152232886eaa68ab298d69 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 7 Nov 2017 21:34:45 +0800 Subject: [PATCH 05/64] Site updated: 2017-11-07 21:34:44 --- css/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/main.css b/css/main.css index 6998358..1ffa44e 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #818a16; + background: #fff90a; } .links-of-blogroll { font-size: 13px; From 98beb697180997031cf0bedc964e1541b4a84ee0 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 7 Nov 2017 21:59:19 +0800 Subject: [PATCH 06/64] Site updated: 2017-11-07 21:59:18 --- .../index.html" | 118 ++++++++++++++ .../index.html" | 118 ++++++++++++++ .../index.html" | 118 ++++++++++++++ .../index.html" | 118 ++++++++++++++ archives/2017/10/index.html | 107 +++++++++++++ archives/2017/11/index.html | 107 +++++++++++++ archives/2017/index.html | 107 +++++++++++++ archives/index.html | 107 +++++++++++++ categories/Android/index.html | 107 +++++++++++++ categories/index.html | 107 +++++++++++++ css/main.css | 2 +- index.html | 151 ++++++++++++++++++ .../index.html" | 107 +++++++++++++ .../index.html" | 107 +++++++++++++ tags/Python/index.html | 107 +++++++++++++ tags/index.html | 107 +++++++++++++ 16 files changed, 1694 insertions(+), 1 deletion(-) diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index b2b6bd7..c1ddcc7 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -302,6 +302,17 @@

Android Handler机制理解

+ + + + + + + + + @@ -734,6 +745,113 @@

+ + + + diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index d4dc905..e7c3f20 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -302,6 +302,17 @@

Android View动画和属性动 + + + + + + + + + @@ -723,6 +734,113 @@

+ + + + diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index 0fca615..b3cc741 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -302,6 +302,17 @@

分享一下自己做的一个 + + + + + + + + + @@ -735,6 +746,113 @@

使用 + + + + diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index ed4d548..d7272d9 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -308,6 +308,17 @@

Android adb命令的一些实际 + + + + + + + + + @@ -759,6 +770,113 @@

+ + + + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 5a30078..28ad1b9 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -649,6 +649,113 @@

+ + + + + + + diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 641faf2..31057e1 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -575,6 +575,113 @@

+ + + + + + + diff --git a/archives/2017/index.html b/archives/2017/index.html index 8de3f4a..bdc54b9 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -686,6 +686,113 @@

+ + + + + + + diff --git a/archives/index.html b/archives/index.html index aa76313..22f30aa 100644 --- a/archives/index.html +++ b/archives/index.html @@ -686,6 +686,113 @@

+ + + + + + + diff --git a/categories/Android/index.html b/categories/Android/index.html index 1857ee9..90dface 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -640,6 +640,113 @@

+ + + + + + + diff --git a/categories/index.html b/categories/index.html index 0a08039..1472f5a 100644 --- a/categories/index.html +++ b/categories/index.html @@ -553,6 +553,113 @@

文章分类

+ + + + + + + diff --git a/css/main.css b/css/main.css index 1ffa44e..02f783d 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #fff90a; + background: #ff6e82; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index ed25fb2..a2e0e31 100644 --- a/index.html +++ b/index.html @@ -303,6 +303,17 @@

+ + + + + + + + + @@ -451,6 +462,17 @@

+ + + + + + + + + @@ -599,6 +621,17 @@

+ + + + + + + + + @@ -745,6 +778,17 @@

+ + + + + + + + + @@ -1100,6 +1144,113 @@

+ + + + diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index fa5dfa6..03728bb 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -583,6 +583,113 @@

+ + + + + + + diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 2980d1f..e5cd0de 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -583,6 +583,113 @@

+ + + + + + + diff --git a/tags/Python/index.html b/tags/Python/index.html index cb318ad..5489877 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -555,6 +555,113 @@

+ + + + + + + diff --git a/tags/index.html b/tags/index.html index 03c0f95..381513d 100644 --- a/tags/index.html +++ b/tags/index.html @@ -553,6 +553,113 @@

文章标签

+ + + + + + + From 44895a99e24c198cc174fb779864baad7f82d960 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 7 Nov 2017 21:59:58 +0800 Subject: [PATCH 07/64] Site updated: 2017-11-07 21:59:57 --- .../index.html" | 13 ------------- .../index.html" | 13 ------------- .../index.html" | 13 ------------- .../index.html" | 13 ------------- archives/2017/10/index.html | 8 -------- archives/2017/11/index.html | 8 -------- archives/2017/index.html | 8 -------- archives/index.html | 8 -------- categories/Android/index.html | 8 -------- categories/index.html | 8 -------- css/main.css | 2 +- index.html | 8 -------- "tags/Android\345\256\236\350\267\265/index.html" | 8 -------- .../index.html" | 8 -------- tags/Python/index.html | 8 -------- tags/index.html | 8 -------- 16 files changed, 1 insertion(+), 141 deletions(-) diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index c1ddcc7..82b5eee 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -315,11 +315,6 @@

Android Handler机制理解

- - 阅读量 - 次 - - @@ -583,14 +578,6 @@

- - - - - - - diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index e7c3f20..7180689 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -315,11 +315,6 @@

Android View动画和属性动 - - 阅读量 - 次 - - @@ -572,14 +567,6 @@

- - - - - - - diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index b3cc741..69b8e96 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -315,11 +315,6 @@

分享一下自己做的一个 - - 阅读量 - 次 - - @@ -584,14 +579,6 @@

使用 - - - - - - - diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index d7272d9..3a7371e 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -321,11 +321,6 @@

Android adb命令的一些实际 - - 阅读量 - 次 - - @@ -608,14 +603,6 @@

- - - - - - - diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 28ad1b9..272f746 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -491,14 +491,6 @@

-
- - - - - -
- diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 31057e1..a8744e3 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -417,14 +417,6 @@

-
- - - - - -
- diff --git a/archives/2017/index.html b/archives/2017/index.html index bdc54b9..4b181db 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -528,14 +528,6 @@

-
- - - - - -
- diff --git a/archives/index.html b/archives/index.html index 22f30aa..dae67b3 100644 --- a/archives/index.html +++ b/archives/index.html @@ -528,14 +528,6 @@

-
- - - - - -
- diff --git a/categories/Android/index.html b/categories/Android/index.html index 90dface..398d08b 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -482,14 +482,6 @@

-
- - - - - -
- diff --git a/categories/index.html b/categories/index.html index 1472f5a..e6a0be8 100644 --- a/categories/index.html +++ b/categories/index.html @@ -391,14 +391,6 @@

文章分类

-
- - - - - -
- diff --git a/css/main.css b/css/main.css index 02f783d..96f26b5 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #ff6e82; + background: #ffff3b; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index a2e0e31..3d51637 100644 --- a/index.html +++ b/index.html @@ -986,14 +986,6 @@

- - - - - - - diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index 03728bb..17753b0 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -425,14 +425,6 @@

-
- - - - - -
- diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index e5cd0de..01310a1 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -425,14 +425,6 @@

-
- - - - - -
- diff --git a/tags/Python/index.html b/tags/Python/index.html index 5489877..7ff1a72 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -397,14 +397,6 @@

-
- - - - - -
- diff --git a/tags/index.html b/tags/index.html index 381513d..097ce6a 100644 --- a/tags/index.html +++ b/tags/index.html @@ -391,14 +391,6 @@

文章标签

-
- - - - - -
- From 272ec259d82dd8d21f8400f65545c9415ed55e72 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Tue, 5 Dec 2017 22:59:05 +0800 Subject: [PATCH 08/64] Site updated: 2017-12-05 22:59:04 --- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 8 +- .../index.html" | 893 ++++++++++++++++++ archives/2017/10/index.html | 6 +- archives/2017/11/index.html | 6 +- archives/2017/12/index.html | 690 ++++++++++++++ archives/2017/index.html | 43 +- archives/index.html | 43 +- categories/Android/index.html | 32 +- categories/index.html | 6 +- css/main.css | 2 +- index.html | 219 ++++- .../index.html" | 4 +- .../index.html" | 670 +++++++++++++ .../index.html" | 4 +- tags/Python/index.html | 4 +- tags/index.html | 8 +- 19 files changed, 2612 insertions(+), 38 deletions(-) create mode 100644 "2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" create mode 100644 archives/2017/12/index.html create mode 100644 "tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index 82b5eee..dab840b 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -477,7 +477,7 @@

- 4 + 5 日志 @@ -499,7 +499,7 @@

- 3 + 4 标签 diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index 7180689..498a507 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -466,7 +466,7 @@

- 4 + 5 日志 @@ -488,7 +488,7 @@

- 3 + 4 标签 diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index 69b8e96..a195e3e 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -478,7 +478,7 @@

使用 - 4 + 5 日志 @@ -500,7 +500,7 @@

使用 - 3 + 4 标签 diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index 3a7371e..526a43c 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -423,6 +423,10 @@

+ + @@ -502,7 +506,7 @@

- 4 + 5 日志 @@ -524,7 +528,7 @@

- 3 + 4 标签 diff --git "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" new file mode 100644 index 0000000..2b058fb --- /dev/null +++ "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" @@ -0,0 +1,893 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android 7.0 startActivity()源码解析以及对几个问题的思考 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Android 7.0 startActivity()源码解析以及对几个问题的思考

+ + + +
+ + + + + +
+ + + + + +

一、本文需要解决的问题

本文并不是非常详细地解释startActivity()源码每行代码的具体作用(实际上也根本做不到),所以我省略了很多代码,只保留了最核心的代码。我研究这段源码的目的是为了解决以下几个我在开发应用的过程中所思考的问题:

+
    +
  1. 是通过何种方式生成一个新的Activity类的,是通过java反射生成的吗?
  2. +
  3. Activity的生命周期回调方法是通过哪个类调用的,在什么时候调用的?
  4. +
  5. 界面的绘制是在执行Activity#onResume()之后还是之前?
  6. +
  7. 在之前的学习中,我了解到应用程序的真正入口是ActivityThread类,那么ActivityThread#main()方法是在哪里调用的?
  8. +
+

二、相关解析(基于Android 7.1源码)

1
2
3
// 这里解析的是在已有进程中启动一个新Activity的情况
Intent intent = new Intent(this, SubActivity.class);
intent.startActivity();
+

(1)Activity本地调用:
Activity#startActivity()
  –> Activity#startActivityForResult()

1
2
3
4
5
6
7
8
9
@Override
public void startActivityForResult(String who, Intent intent, int requestCode, @Nullable Bundle options) {
// 省略代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// 省略代码
}

+

(2)Instrumentation#execStartActivity
Instrumentation类相当于一个管家,它的职责是管理各个应用程序和系统的交互,Instrumentation将在任何应用程序运行前初始化,每个进程只会存在一个Instrumentation对象,且每个Activity都有此对象的实际引用,可以通过它监测系统与应用程序之间的所有交互。

1
2
3
4
5
6
7
8
9
10
11
12
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 省略代码
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
// 省略代码
}

+

(3)ActivityManagerNative,ActivityManagerService,ActivityManagerProxy类之间的关系
ActivityManagerNative#getDefault()
  –> ActivityManagerProxy#startActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

+

如果学过AIDL的话,对上面这段代码会比较熟悉,其实上面是发起了一次跨进程调用。这里涉及到一种设计模式叫作代理模式,这里不详细介绍这种设计模式,简单总结一下:

+
    +
  • ActivityManagerProxy相当于Proxy
  • +
  • ActivityManagerNative就相当于Stub
  • +
  • ActivityManagerService是ActivityManagerNative的具体实现,换句话说,就是AMS才是服务端的具体实现!
  • +
+

(4)ActivityMangerService
ActivityMangerService#startActivity()
  –> ActivityMangerService#startActivityAsUser()

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null);
}

+

(5)ActivityStarter
ActivityStarter类的注释:

1
2
3
4
5
6
/**
* Controller for interpreting how and then launching activities.
*
* This class collects all the logic for determining how an intent and flags should be turned into
* an activity and associated task and stack.
*/

+

简单来说,ActivityStarter类主要负责处理Activity的Intent和Flags, 还有关联相关的Stack和TaskRecord
ActivityStarter#startActivityMayWait()
  –> ActivityStarter#startActivityLocked()
    –> ActivityStarter#startActivityUnchecked()

+

其中:
startActivityMayWait():获取Activity的启动信息,包括ResolveInfo和ActivityInfo,以及获取CallingPid和CallingUid;
startActivityLocked():创建一个ActivityRecord;
startActivityUnchecked():设置TaskRecord, 完成后执行ActivityStackSupervisor类的resumeFocusedStackTopActivityLocked方法

+

(6)ActivityStackSupervisor和ActivityStack
ActivityStackSupervisor#resumeFocusedStackTopActivityLocked()
  –> ActivityStackSupervisor#resumeTopActivityUncheckedLocked()
    –> ActivityStack#resumeTopActivityInnerLocked()
      –> ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}

+

这里会判断进程是否存在,由于我们是在原有进程中启动一个新的activity,所以会调用 realStartActivityLocked()方法。
ActivityStackSupervisor#realStartActivityLocked()

1
2
3
4
5
6
7
8
9
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
// 省略代码
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
}

+

(7)关于IApplicationThread,ApplicationThreadProxy,ApplicationThreadNative,ApplicationThread
上述代码中,app.thread为IApplicationThread类型,继承了IInterface,我们查看IApplicationThread的直接实现ApplicationThreadNative
ApplicationThreadNative#scheduleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
if (overrideConfig != null) {
data.writeInt(1);
overrideConfig.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
compatInfo.writeToParcel(data, 0);
data.writeString(referrer);
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
data.writePersistableBundle(persistentState);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
data.writeInt(isForward ? 1 : 0);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}

+

同样的,这里也发起了一次跨进程调用。

+
    +
  • ApplicationThreadProxy相当于Proxy
  • +
  • ApplicationThreadNative相当于Stub
  • +
  • ApplicationThread相当于服务器端,代码真正的实现者!
  • +
+

(8)ActivityThread.ApplicationThread类
ActivityThread.ApplicationThread#scheduleLaunchActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}

+

这里会调用sendMessage,最后调用到mH.sendMessage(msg);
mH为H类的一个实例,H就是Handler的一个子类,发送消息之后,我们来查看H类的handleMessage()方法:
源码里面就是根据msg.what来执行对应的操作:

1
2
3
4
5
6
7
8
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}

+

(9) ActivityThread#handleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(TAG, "Handling launch of " + r);
// Initialize before creating the activity
WindowManagerGlobal.initialize();
// !!!
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// We need to keep around the original state, in case we need to be created again.
// But we only do this for pre-Honeycomb apps, which always save their state when
// pausing, so we can not have them save their state when restarting from a paused
// state. For HC and later, we want to (and can) let the state be saved as the
// normal part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}

+

这里会调用performLaunchActivity()方法。

+

(10)ActivityThread#performLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
// 11.2
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}

+

这里会收集要启动的Activity的相关信息,主要是package和component信息,然后通过ClassLoader将要启动的Activity类加载出来。

+

这里就解决了我的第一个问题:新的Activity类是通过类加载器方式即通过反射的方式生成的,我们可以看一下mInstrumentation.newActivity()方法:

1
2
3
4
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}

+

最后调用mInstrumentation.callActivityOnCreate()

+

(11)
11.1 Instrumentation#callActivityOnCreate()

1
2
3
4
5
6
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}

+

Activity#performCreate()

1
2
3
4
5
6
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}

+

这里就会显式调用Activity的生命周期方法onCreate()!

+

11.2 Activity#performStart()

1
2
3
4
5
6
final void performStart() {
// 省略代码
// !!!
mInstrumentation.callActivityOnStart(this);
// 省略代码
}

+

在mInstrumentation.callActivityOnStart(this)方法里面就会显式调用Activtiy的onStart()方法!

+

到这里我们也可以基本解决第二个问题:Activity的生命周期方法是通过Instrumentation类调用callActivityOnXXX方法最终调用Activity的onCreate等方法,调用时机为ActivityThread#performLaunchActivitiy()方法中。

+

那么还有一个问题,我们知道启动一个Activity,所经历的生命周期为onCreate() –> onStart() –> onResume()
那么onResume()方法在哪里调用的呢?
我们回到前面的ActivityThread#handleLaunchActivity():

1
2
3
4
5
6
7
8
9
10
11
12
13
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 省略代码
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
// !!!
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
// 省略代码
}

+

在我们调用performLaunchActivity之后返回新生成的Activity实例之后,接下来就会调用handleResumeActivity()方法
ActivityThread#handleResumeActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
// !!!
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */ );
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity " + r.activityInfo.name +
" with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) {
l.softInputMode = (l.softInputMode &
(~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault().
finishActivity(token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex)
throw ex.rethrowFromSystemServer();
}
}

+

这里会调用:
ActivityThread#performResumeActivity()
  –> Activity#performResume()
    –> Instrumentation#callActivityOnResume()
      –> Activity#onResume()
另外,观察执行handleResumeActivity()之后的代码,会发现程序会开始获取DecorView,执行addView()方法,里面最终会调用到ViewRootImpl#performTraversals(),即开始绘制view界面!
这里我们就解决了第三个问题:界面的绘制是在执行Activity#onResume()之后!

+

三、关于第四个问题

那么我们还差第四个问题没有解决!为什么我们这个流程都没有涉及到调用main方法呢,是因为在一开始,我们分析的情况是在已有的App进程中启动一个新的Activity,而通过我们上文的分析,我们知道ActivityThread类才是应用的主线程类,一个app应用进程有且只有一个ActivityThread类,也就是我们一直说的应用程序主线程(UI线程)。所以,在分析源码时,我们已经假设ActivityThread类已经存在实例,所以不会再调用main方法。

+

那么什么情况下会调用到main方法呢,其实我们每次在手机的桌面点击一个应用图标打开应用时,其实是通由Launcher启动起来的。

+

(1)关于Launcher
Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。Launcher其实也是Activity的一个子类。

1
2
3
4
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
...
}

+

所以本质上也是调用了startActivity()方法启动一个新的Activity!
根据上述的流程,一直到ActivityStackSupervisor#startSpecificActivityLocked()这里,代码的调用流程就会开始发生变化!
ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo,
true, 0, "activity", r.intent.getComponent(), false, false, true);
}

+

上文说过。这里会判断进程是否存在,而这次,app为空,所以会跳出if判断,直接到下面的mService.startProcessLocked()方法,这里的mService为ActivityManagerService类的一个实例!
startProcessLocked()通过几次重载函数的调用,最终调用到这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
// 省略代码
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
// !!!
Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid,
uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
// 省略代码
}

+

(2)
Process#start()
  –> Process#startViaZygote()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList < String > argsForZygote = new ArrayList < String > ();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
argsForZygote.add("--generate-debug-info");
}
if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
argsForZygote.add("--always-jit");
}
if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
argsForZygote.add("--native-debuggable");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg: extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

+

上面方法中设置一些参数之后,调用最后的zygoteSendArgsAndGetResult(),方法的作用是向Zygote发送创建进程请求,内部与Zygote进行Socket通信。具体代码逻辑在ZygoteInit#runSelectLoop():

+

(3)ZygoteInit#runSelectLoop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList < FileDescriptor > fds = new ArrayList < FileDescriptor > ();
ArrayList < ZygoteConnection > peers = new ArrayList < ZygoteConnection > ();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}

+

上面方法当中,通过acceptCommandPeer()方法创建一个新的ZygoteConnection,调用runOnce()方法处理请求。
ZygoteConnection#runOnce():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
// 读取参数
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
// 省略代码
// !!!
pid = Zygote.forkAndSpecialize(parsedArgs.uid,
parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,
rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName,
fdsToClose, parsedArgs.instructionSet, parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
// !!!
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}

+

这里会调用Zygote.forkAndSpecialize()方法生成一个新的进程,如果生成成功,则pid的值为0,然后调用handleChildProc()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}

+

这里会根据invokeWith参数决定使用哪种执行方式,我们只要知道SystemServer和apk都是通过RuntimeInit类生成的即可。

+

(4)RuntimeInit#zygoteInit()

1
2
3
4
5
6
7
8
9
10
11
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}

+

最后会调用applicationInit()方法:

1
2
3
4
5
6
7
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// 省略代码
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

+

最关键的就是invokeStaticMain()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

+

上述方法中,通过反射的方式获取main方法,最后抛出一个MethodAndArgsCaller,它继承于Exception,同时他也是实现了Runnable接口。看 throw new ZygoteInit.MethodAndArgsCaller()的代码注释我们可以知道,它会在ZygoteInit.main()方法中被捕获,执行run方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

+

我们可以知道,最终它将会调用ActivityThread类的main方法!
所以,我们解决了第四个问题,ActivityThread的main方法是在生成一个新的app进程过程中调用的,具体是通过与Zygote通信,之后通过RuntimeInit类采用反射的方式调用ActivityThread#main()方法,即生成app中的主线程(UI线程)!

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ +
+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 272f746..8303bc8 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 4 篇日志。 继续努力。 + 嗯..! 目前共计 5 篇日志。 继续努力。 @@ -406,7 +406,7 @@

- 4 + 5 日志 @@ -428,7 +428,7 @@

diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index a8744e3..a7276af 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 4 篇日志。 继续努力。 + 嗯..! 目前共计 5 篇日志。 继续努力。 @@ -332,7 +332,7 @@

- 4 + 5 日志 @@ -354,7 +354,7 @@

diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html new file mode 100644 index 0000000..2504a90 --- /dev/null +++ b/archives/2017/12/index.html @@ -0,0 +1,690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/index.html b/archives/2017/index.html index 4b181db..a53a1fb 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 4 篇日志。 继续努力。 + 嗯..! 目前共计 5 篇日志。 继续努力。 @@ -249,6 +249,43 @@

2017

+ + + + + + + + + + + + + + +
@@ -443,7 +480,7 @@

- 4 + 5 日志 @@ -465,7 +502,7 @@

diff --git a/archives/index.html b/archives/index.html index dae67b3..1dcabd4 100644 --- a/archives/index.html +++ b/archives/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 4 篇日志。 继续努力。 + 嗯..! 目前共计 5 篇日志。 继续努力。 @@ -249,6 +249,43 @@

2017

+ + + + + + + + + + + + + + +
@@ -443,7 +480,7 @@

- 4 + 5 日志 @@ -465,7 +502,7 @@

diff --git a/categories/Android/index.html b/categories/Android/index.html index 398d08b..38ab910 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -231,6 +231,34 @@

Android分类 + + + + + +
@@ -397,7 +425,7 @@

- 4 + 5 日志 @@ -419,7 +447,7 @@

diff --git a/categories/index.html b/categories/index.html index e6a0be8..ba991b3 100644 --- a/categories/index.html +++ b/categories/index.html @@ -242,7 +242,7 @@

文章分类

目前共计 1 个分类 @@ -306,7 +306,7 @@

文章分类

- 4 + 5 日志 @@ -328,7 +328,7 @@

文章分类

diff --git a/css/main.css b/css/main.css index 96f26b5..ddb400b 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #ffff3b; + background: #69c3ff; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index 3d51637..785269b 100644 --- a/index.html +++ b/index.html @@ -227,6 +227,221 @@ +
+ + + +
+ + + + + + + +
+ + + +

+ +

+ + + +
+ + + + + +
+ + + + + + + +

一、本文需要解决的问题

本文并不是非常详细地解释startActivity()源码每行代码的具体作用(实际上也根本做不到),所以我省略了很多代码,只保留了最核心的代码。我研究这段源码的目的是为了解决以下几个我在开发应用的过程中所思考的问题:

+
    +
  1. 是通过何种方式生成一个新的Activity类的,是通过java反射生成的吗?
  2. +
  3. Activity的生命周期回调方法是通过哪个类调用的,在什么时候调用的?
  4. +
  5. 界面的绘制是在执行Activity#onResume()之后还是之前?
  6. +
  7. 在之前的学习中,我了解到应用程序的真正入口是ActivityThread类,那么ActivityThread#main()方法是在哪里调用的?
  8. +
+

二、相关解析(基于Android 7.1源码)

1
2
3
// 这里解析的是在已有进程中启动一个新Activity的情况
Intent intent = new Intent(this, SubActivity.class);
intent.startActivity();
+

(1)Activity本地调用:
Activity#startActivity()
  –> Activity#startActivityForResult()

1
2
3
4
5
6
7
8
9
@Override
public void startActivityForResult(String who, Intent intent, int requestCode, @Nullable Bundle options) {
// 省略代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// 省略代码
}

+

(2)Instrumentation#execStartActivity
Instrumentation类相当于一个管家,它的职责是管理各个应用程序和系统的交互,Instrumentation将在任何应用程序运行前初始化,每个进程只会存在一个Instrumentation对象,且每个Activity都有此对象的实际引用,可以通过它监测系统与应用程序之间的所有交互。

1
2
3
4
5
6
7
8
9
10
11
12
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 省略代码
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
// 省略代码
}

+

(3)ActivityManagerNative,ActivityManagerService,ActivityManagerProxy类之间的关系
ActivityManagerNative#getDefault()
  –> ActivityManagerProxy#startActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

+

如果学过AIDL的话,对上面这段代码会比较熟悉,其实上面是发起了一次跨进程调用。这里涉及到一种设计模式叫作代理模式,这里不详细介绍这种设计模式,简单总结一下:

+
    +
  • ActivityManagerProxy相当于Proxy
  • +
  • ActivityManagerNative就相当于Stub
  • +
  • ActivityManagerService是ActivityManagerNative的具体实现,换句话说,就是AMS才是服务端的具体实现!
  • +
+

(4)ActivityMangerService
ActivityMangerService#startActivity()
  –> ActivityMangerService#startActivityAsUser()

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null);
}

+

(5)ActivityStarter
ActivityStarter类的注释:

1
2
3
4
5
6
/**
* Controller for interpreting how and then launching activities.
*
* This class collects all the logic for determining how an intent and flags should be turned into
* an activity and associated task and stack.
*/

+

简单来说,ActivityStarter类主要负责处理Activity的Intent和Flags, 还有关联相关的Stack和TaskRecord
ActivityStarter#startActivityMayWait()
  –> ActivityStarter#startActivityLocked()
    –> ActivityStarter#startActivityUnchecked()

+

其中:
startActivityMayWait():获取Activity的启动信息,包括ResolveInfo和ActivityInfo,以及获取CallingPid和CallingUid;
startActivityLocked():创建一个ActivityRecord;
startActivityUnchecked():设置TaskRecord, 完成后执行ActivityStackSupervisor类的resumeFocusedStackTopActivityLocked方法

+

(6)ActivityStackSupervisor和ActivityStack
ActivityStackSupervisor#resumeFocusedStackTopActivityLocked()
  –> ActivityStackSupervisor#resumeTopActivityUncheckedLocked()
    –> ActivityStack#resumeTopActivityInnerLocked()
      –> ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}

+

这里会判断进程是否存在,由于我们是在原有进程中启动一个新的activity,所以会调用 realStartActivityLocked()方法。
ActivityStackSupervisor#realStartActivityLocked()

1
2
3
4
5
6
7
8
9
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
// 省略代码
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
}

+

(7)关于IApplicationThread,ApplicationThreadProxy,ApplicationThreadNative,ApplicationThread
上述代码中,app.thread为IApplicationThread类型,继承了IInterface,我们查看IApplicationThread的直接实现ApplicationThreadNative
ApplicationThreadNative#scheduleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
if (overrideConfig != null) {
data.writeInt(1);
overrideConfig.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
compatInfo.writeToParcel(data, 0);
data.writeString(referrer);
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
data.writePersistableBundle(persistentState);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
data.writeInt(isForward ? 1 : 0);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}

+

同样的,这里也发起了一次跨进程调用。

+
    +
  • ApplicationThreadProxy相当于Proxy
  • +
  • ApplicationThreadNative相当于Stub
  • +
  • ApplicationThread相当于服务器端,代码真正的实现者!
  • +
+

(8)ActivityThread.ApplicationThread类
ActivityThread.ApplicationThread#scheduleLaunchActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}

+

这里会调用sendMessage,最后调用到mH.sendMessage(msg);
mH为H类的一个实例,H就是Handler的一个子类,发送消息之后,我们来查看H类的handleMessage()方法:
源码里面就是根据msg.what来执行对应的操作:

1
2
3
4
5
6
7
8
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}

+

(9) ActivityThread#handleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(TAG, "Handling launch of " + r);
// Initialize before creating the activity
WindowManagerGlobal.initialize();
// !!!
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// We need to keep around the original state, in case we need to be created again.
// But we only do this for pre-Honeycomb apps, which always save their state when
// pausing, so we can not have them save their state when restarting from a paused
// state. For HC and later, we want to (and can) let the state be saved as the
// normal part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}

+

这里会调用performLaunchActivity()方法。

+

(10)ActivityThread#performLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
// 11.2
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}

+

这里会收集要启动的Activity的相关信息,主要是package和component信息,然后通过ClassLoader将要启动的Activity类加载出来。

+

这里就解决了我的第一个问题:新的Activity类是通过类加载器方式即通过反射的方式生成的,我们可以看一下mInstrumentation.newActivity()方法:

1
2
3
4
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}

+

最后调用mInstrumentation.callActivityOnCreate()

+

(11)
11.1 Instrumentation#callActivityOnCreate()

1
2
3
4
5
6
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}

+

Activity#performCreate()

1
2
3
4
5
6
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}

+

这里就会显式调用Activity的生命周期方法onCreate()!

+

11.2 Activity#performStart()

1
2
3
4
5
6
final void performStart() {
// 省略代码
// !!!
mInstrumentation.callActivityOnStart(this);
// 省略代码
}

+

在mInstrumentation.callActivityOnStart(this)方法里面就会显式调用Activtiy的onStart()方法!

+

到这里我们也可以基本解决第二个问题:Activity的生命周期方法是通过Instrumentation类调用callActivityOnXXX方法最终调用Activity的onCreate等方法,调用时机为ActivityThread#performLaunchActivitiy()方法中。

+

那么还有一个问题,我们知道启动一个Activity,所经历的生命周期为onCreate() –> onStart() –> onResume()
那么onResume()方法在哪里调用的呢?
我们回到前面的ActivityThread#handleLaunchActivity():

1
2
3
4
5
6
7
8
9
10
11
12
13
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 省略代码
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
// !!!
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
// 省略代码
}

+

在我们调用performLaunchActivity之后返回新生成的Activity实例之后,接下来就会调用handleResumeActivity()方法
ActivityThread#handleResumeActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
// !!!
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */ );
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity " + r.activityInfo.name +
" with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) {
l.softInputMode = (l.softInputMode &
(~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault().
finishActivity(token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex)
throw ex.rethrowFromSystemServer();
}
}

+

这里会调用:
ActivityThread#performResumeActivity()
  –> Activity#performResume()
    –> Instrumentation#callActivityOnResume()
      –> Activity#onResume()
另外,观察执行handleResumeActivity()之后的代码,会发现程序会开始获取DecorView,执行addView()方法,里面最终会调用到ViewRootImpl#performTraversals(),即开始绘制view界面!
这里我们就解决了第三个问题:界面的绘制是在执行Activity#onResume()之后!

+

三、关于第四个问题

那么我们还差第四个问题没有解决!为什么我们这个流程都没有涉及到调用main方法呢,是因为在一开始,我们分析的情况是在已有的App进程中启动一个新的Activity,而通过我们上文的分析,我们知道ActivityThread类才是应用的主线程类,一个app应用进程有且只有一个ActivityThread类,也就是我们一直说的应用程序主线程(UI线程)。所以,在分析源码时,我们已经假设ActivityThread类已经存在实例,所以不会再调用main方法。

+

那么什么情况下会调用到main方法呢,其实我们每次在手机的桌面点击一个应用图标打开应用时,其实是通由Launcher启动起来的。

+

(1)关于Launcher
Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。Launcher其实也是Activity的一个子类。

1
2
3
4
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
...
}

+

所以本质上也是调用了startActivity()方法启动一个新的Activity!
根据上述的流程,一直到ActivityStackSupervisor#startSpecificActivityLocked()这里,代码的调用流程就会开始发生变化!
ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo,
true, 0, "activity", r.intent.getComponent(), false, false, true);
}

+

上文说过。这里会判断进程是否存在,而这次,app为空,所以会跳出if判断,直接到下面的mService.startProcessLocked()方法,这里的mService为ActivityManagerService类的一个实例!
startProcessLocked()通过几次重载函数的调用,最终调用到这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
// 省略代码
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
// !!!
Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid,
uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
// 省略代码
}

+

(2)
Process#start()
  –> Process#startViaZygote()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList < String > argsForZygote = new ArrayList < String > ();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
argsForZygote.add("--generate-debug-info");
}
if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
argsForZygote.add("--always-jit");
}
if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
argsForZygote.add("--native-debuggable");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg: extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

+

上面方法中设置一些参数之后,调用最后的zygoteSendArgsAndGetResult(),方法的作用是向Zygote发送创建进程请求,内部与Zygote进行Socket通信。具体代码逻辑在ZygoteInit#runSelectLoop():

+

(3)ZygoteInit#runSelectLoop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList < FileDescriptor > fds = new ArrayList < FileDescriptor > ();
ArrayList < ZygoteConnection > peers = new ArrayList < ZygoteConnection > ();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}

+

上面方法当中,通过acceptCommandPeer()方法创建一个新的ZygoteConnection,调用runOnce()方法处理请求。
ZygoteConnection#runOnce():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
// 读取参数
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
// 省略代码
// !!!
pid = Zygote.forkAndSpecialize(parsedArgs.uid,
parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,
rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName,
fdsToClose, parsedArgs.instructionSet, parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
// !!!
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}

+

这里会调用Zygote.forkAndSpecialize()方法生成一个新的进程,如果生成成功,则pid的值为0,然后调用handleChildProc()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}

+

这里会根据invokeWith参数决定使用哪种执行方式,我们只要知道SystemServer和apk都是通过RuntimeInit类生成的即可。

+

(4)RuntimeInit#zygoteInit()

1
2
3
4
5
6
7
8
9
10
11
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}

+

最后会调用applicationInit()方法:

1
2
3
4
5
6
7
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// 省略代码
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

+

最关键的就是invokeStaticMain()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

+

上述方法中,通过反射的方式获取main方法,最后抛出一个MethodAndArgsCaller,它继承于Exception,同时他也是实现了Runnable接口。看 throw new ZygoteInit.MethodAndArgsCaller()的代码注释我们可以知道,它会在ZygoteInit.main()方法中被捕获,执行run方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

+

我们可以知道,最终它将会调用ActivityThread类的main方法!
所以,我们解决了第四个问题,ActivityThread的main方法是在生成一个新的app进程过程中调用的,具体是通过与Zygote通信,之后通过RuntimeInit类采用反射的方式调用ActivityThread#main()方法,即生成app中的主线程(UI线程)!

+ + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ +
+
+ + + +
+ + + + + + + + + + +
@@ -901,7 +1116,7 @@

- 4 + 5 日志 @@ -923,7 +1138,7 @@

- 3 + 4 标签 diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index 17753b0..20e9ab7 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -340,7 +340,7 @@

- 4 + 5 日志 @@ -362,7 +362,7 @@

diff --git "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" new file mode 100644 index 0000000..cc075f3 --- /dev/null +++ "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 标签: Android源码解析 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 01310a1..297ae86 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -340,7 +340,7 @@

- 4 + 5 日志 @@ -362,7 +362,7 @@

diff --git a/tags/Python/index.html b/tags/Python/index.html index 7ff1a72..e3b6340 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -312,7 +312,7 @@

- 4 + 5 日志 @@ -334,7 +334,7 @@

diff --git a/tags/index.html b/tags/index.html index 097ce6a..42904c8 100644 --- a/tags/index.html +++ b/tags/index.html @@ -239,10 +239,10 @@

文章标签

- 目前共计 3 个标签 + 目前共计 4 个标签
@@ -306,7 +306,7 @@

文章标签

- 4 + 5 日志 @@ -328,7 +328,7 @@

文章标签

From ea87fd273e176fe57e784f642f39df3daf512fc8 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Wed, 6 Dec 2017 23:55:40 +0800 Subject: [PATCH 09/64] Site updated: 2017-12-06 23:55:39 --- .../index.html" | 3 +- css/main.css | 2 +- index.html | 68 +++---------------- 3 files changed, 11 insertions(+), 62 deletions(-) diff --git "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" index 2b058fb..ebdddd3 100644 --- "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" +++ "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" @@ -87,7 +87,7 @@ - + @@ -340,6 +340,7 @@

二、相关解析(基于Android 7.1源码)

1
2
3
// 这里解析的是在已有进程中启动一个新Activity的情况
Intent intent = new Intent(this, SubActivity.class);
intent.startActivity();

(1)Activity本地调用:
Activity#startActivity()
  –> Activity#startActivityForResult()

1
2
3
4
5
6
7
8
9
@Override
public void startActivityForResult(String who, Intent intent, int requestCode, @Nullable Bundle options) {
// 省略代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// 省略代码
}

(2)Instrumentation#execStartActivity
Instrumentation类相当于一个管家,它的职责是管理各个应用程序和系统的交互,Instrumentation将在任何应用程序运行前初始化,每个进程只会存在一个Instrumentation对象,且每个Activity都有此对象的实际引用,可以通过它监测系统与应用程序之间的所有交互。

1
2
3
4
5
6
7
8
9
10
11
12
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 省略代码
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
// 省略代码
}

diff --git a/css/main.css b/css/main.css index ddb400b..e46ba57 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #69c3ff; + background: #ff6362; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index 785269b..55464e4 100644 --- a/index.html +++ b/index.html @@ -335,72 +335,20 @@

- -

一、本文需要解决的问题

本文并不是非常详细地解释startActivity()源码每行代码的具体作用(实际上也根本做不到),所以我省略了很多代码,只保留了最核心的代码。我研究这段源码的目的是为了解决以下几个我在开发应用的过程中所思考的问题:

+

一、本文需要解决的问题

本文并不是非常详细地解释startActivity()源码每行代码的具体作用(实际上也根本做不到),所以我省略了很多代码,只保留了最核心的代码。我研究这段源码的目的是为了解决以下几个我在开发应用的过程中所思考的问题:

  1. 是通过何种方式生成一个新的Activity类的,是通过java反射生成的吗?
  2. Activity的生命周期回调方法是通过哪个类调用的,在什么时候调用的?
  3. 界面的绘制是在执行Activity#onResume()之后还是之前?
  4. 在之前的学习中,我了解到应用程序的真正入口是ActivityThread类,那么ActivityThread#main()方法是在哪里调用的?
-

二、相关解析(基于Android 7.1源码)

1
2
3
// 这里解析的是在已有进程中启动一个新Activity的情况
Intent intent = new Intent(this, SubActivity.class);
intent.startActivity();
-

(1)Activity本地调用:
Activity#startActivity()
  –> Activity#startActivityForResult()

1
2
3
4
5
6
7
8
9
@Override
public void startActivityForResult(String who, Intent intent, int requestCode, @Nullable Bundle options) {
// 省略代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// 省略代码
}

-

(2)Instrumentation#execStartActivity
Instrumentation类相当于一个管家,它的职责是管理各个应用程序和系统的交互,Instrumentation将在任何应用程序运行前初始化,每个进程只会存在一个Instrumentation对象,且每个Activity都有此对象的实际引用,可以通过它监测系统与应用程序之间的所有交互。

1
2
3
4
5
6
7
8
9
10
11
12
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 省略代码
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
// 省略代码
}

-

(3)ActivityManagerNative,ActivityManagerService,ActivityManagerProxy类之间的关系
ActivityManagerNative#getDefault()
  –> ActivityManagerProxy#startActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

-

如果学过AIDL的话,对上面这段代码会比较熟悉,其实上面是发起了一次跨进程调用。这里涉及到一种设计模式叫作代理模式,这里不详细介绍这种设计模式,简单总结一下:

-
    -
  • ActivityManagerProxy相当于Proxy
  • -
  • ActivityManagerNative就相当于Stub
  • -
  • ActivityManagerService是ActivityManagerNative的具体实现,换句话说,就是AMS才是服务端的具体实现!
  • -
-

(4)ActivityMangerService
ActivityMangerService#startActivity()
  –> ActivityMangerService#startActivityAsUser()

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null);
}

-

(5)ActivityStarter
ActivityStarter类的注释:

1
2
3
4
5
6
/**
* Controller for interpreting how and then launching activities.
*
* This class collects all the logic for determining how an intent and flags should be turned into
* an activity and associated task and stack.
*/

-

简单来说,ActivityStarter类主要负责处理Activity的Intent和Flags, 还有关联相关的Stack和TaskRecord
ActivityStarter#startActivityMayWait()
  –> ActivityStarter#startActivityLocked()
    –> ActivityStarter#startActivityUnchecked()

-

其中:
startActivityMayWait():获取Activity的启动信息,包括ResolveInfo和ActivityInfo,以及获取CallingPid和CallingUid;
startActivityLocked():创建一个ActivityRecord;
startActivityUnchecked():设置TaskRecord, 完成后执行ActivityStackSupervisor类的resumeFocusedStackTopActivityLocked方法

-

(6)ActivityStackSupervisor和ActivityStack
ActivityStackSupervisor#resumeFocusedStackTopActivityLocked()
  –> ActivityStackSupervisor#resumeTopActivityUncheckedLocked()
    –> ActivityStack#resumeTopActivityInnerLocked()
      –> ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}

-

这里会判断进程是否存在,由于我们是在原有进程中启动一个新的activity,所以会调用 realStartActivityLocked()方法。
ActivityStackSupervisor#realStartActivityLocked()

1
2
3
4
5
6
7
8
9
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
// 省略代码
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
}

-

(7)关于IApplicationThread,ApplicationThreadProxy,ApplicationThreadNative,ApplicationThread
上述代码中,app.thread为IApplicationThread类型,继承了IInterface,我们查看IApplicationThread的直接实现ApplicationThreadNative
ApplicationThreadNative#scheduleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
if (overrideConfig != null) {
data.writeInt(1);
overrideConfig.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
compatInfo.writeToParcel(data, 0);
data.writeString(referrer);
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
data.writePersistableBundle(persistentState);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
data.writeInt(isForward ? 1 : 0);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}

-

同样的,这里也发起了一次跨进程调用。

-
    -
  • ApplicationThreadProxy相当于Proxy
  • -
  • ApplicationThreadNative相当于Stub
  • -
  • ApplicationThread相当于服务器端,代码真正的实现者!
  • -
-

(8)ActivityThread.ApplicationThread类
ActivityThread.ApplicationThread#scheduleLaunchActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}

-

这里会调用sendMessage,最后调用到mH.sendMessage(msg);
mH为H类的一个实例,H就是Handler的一个子类,发送消息之后,我们来查看H类的handleMessage()方法:
源码里面就是根据msg.what来执行对应的操作:

1
2
3
4
5
6
7
8
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}

-

(9) ActivityThread#handleLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(TAG, "Handling launch of " + r);
// Initialize before creating the activity
WindowManagerGlobal.initialize();
// !!!
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// We need to keep around the original state, in case we need to be created again.
// But we only do this for pre-Honeycomb apps, which always save their state when
// pausing, so we can not have them save their state when restarting from a paused
// state. For HC and later, we want to (and can) let the state be saved as the
// normal part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}

-

这里会调用performLaunchActivity()方法。

-

(10)ActivityThread#performLaunchActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
// 11.1
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
// 11.2
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}

-

这里会收集要启动的Activity的相关信息,主要是package和component信息,然后通过ClassLoader将要启动的Activity类加载出来。

-

这里就解决了我的第一个问题:新的Activity类是通过类加载器方式即通过反射的方式生成的,我们可以看一下mInstrumentation.newActivity()方法:

1
2
3
4
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}

-

最后调用mInstrumentation.callActivityOnCreate()

-

(11)
11.1 Instrumentation#callActivityOnCreate()

1
2
3
4
5
6
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}

-

Activity#performCreate()

1
2
3
4
5
6
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}

-

这里就会显式调用Activity的生命周期方法onCreate()!

-

11.2 Activity#performStart()

1
2
3
4
5
6
final void performStart() {
// 省略代码
// !!!
mInstrumentation.callActivityOnStart(this);
// 省略代码
}

-

在mInstrumentation.callActivityOnStart(this)方法里面就会显式调用Activtiy的onStart()方法!

-

到这里我们也可以基本解决第二个问题:Activity的生命周期方法是通过Instrumentation类调用callActivityOnXXX方法最终调用Activity的onCreate等方法,调用时机为ActivityThread#performLaunchActivitiy()方法中。

-

那么还有一个问题,我们知道启动一个Activity,所经历的生命周期为onCreate() –> onStart() –> onResume()
那么onResume()方法在哪里调用的呢?
我们回到前面的ActivityThread#handleLaunchActivity():

1
2
3
4
5
6
7
8
9
10
11
12
13
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 省略代码
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
// !!!
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
// 省略代码
}

-

在我们调用performLaunchActivity之后返回新生成的Activity实例之后,接下来就会调用handleResumeActivity()方法
ActivityThread#handleResumeActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
// !!!
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */ );
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity " + r.activityInfo.name +
" with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) {
l.softInputMode = (l.softInputMode &
(~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault().
finishActivity(token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex)
throw ex.rethrowFromSystemServer();
}
}

-

这里会调用:
ActivityThread#performResumeActivity()
  –> Activity#performResume()
    –> Instrumentation#callActivityOnResume()
      –> Activity#onResume()
另外,观察执行handleResumeActivity()之后的代码,会发现程序会开始获取DecorView,执行addView()方法,里面最终会调用到ViewRootImpl#performTraversals(),即开始绘制view界面!
这里我们就解决了第三个问题:界面的绘制是在执行Activity#onResume()之后!

-

三、关于第四个问题

那么我们还差第四个问题没有解决!为什么我们这个流程都没有涉及到调用main方法呢,是因为在一开始,我们分析的情况是在已有的App进程中启动一个新的Activity,而通过我们上文的分析,我们知道ActivityThread类才是应用的主线程类,一个app应用进程有且只有一个ActivityThread类,也就是我们一直说的应用程序主线程(UI线程)。所以,在分析源码时,我们已经假设ActivityThread类已经存在实例,所以不会再调用main方法。

-

那么什么情况下会调用到main方法呢,其实我们每次在手机的桌面点击一个应用图标打开应用时,其实是通由Launcher启动起来的。

-

(1)关于Launcher
Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。Launcher其实也是Activity的一个子类。

1
2
3
4
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
...
}

-

所以本质上也是调用了startActivity()方法启动一个新的Activity!
根据上述的流程,一直到ActivityStackSupervisor#startSpecificActivityLocked()这里,代码的调用流程就会开始发生变化!
ActivityStackSupervisor#startSpecificActivityLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats);
}
// !!!
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo,
true, 0, "activity", r.intent.getComponent(), false, false, true);
}

-

上文说过。这里会判断进程是否存在,而这次,app为空,所以会跳出if判断,直接到下面的mService.startProcessLocked()方法,这里的mService为ActivityManagerService类的一个实例!
startProcessLocked()通过几次重载函数的调用,最终调用到这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
// 省略代码
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
// !!!
Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid,
uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
// 省略代码
}

-

(2)
Process#start()
  –> Process#startViaZygote()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList < String > argsForZygote = new ArrayList < String > ();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
argsForZygote.add("--generate-debug-info");
}
if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
argsForZygote.add("--always-jit");
}
if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
argsForZygote.add("--native-debuggable");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg: extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

-

上面方法中设置一些参数之后,调用最后的zygoteSendArgsAndGetResult(),方法的作用是向Zygote发送创建进程请求,内部与Zygote进行Socket通信。具体代码逻辑在ZygoteInit#runSelectLoop():

-

(3)ZygoteInit#runSelectLoop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList < FileDescriptor > fds = new ArrayList < FileDescriptor > ();
ArrayList < ZygoteConnection > peers = new ArrayList < ZygoteConnection > ();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}

-

上面方法当中,通过acceptCommandPeer()方法创建一个新的ZygoteConnection,调用runOnce()方法处理请求。
ZygoteConnection#runOnce():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
// 读取参数
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
// 省略代码
// !!!
pid = Zygote.forkAndSpecialize(parsedArgs.uid,
parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,
rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName,
fdsToClose, parsedArgs.instructionSet, parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
// !!!
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}

-

这里会调用Zygote.forkAndSpecialize()方法生成一个新的进程,如果生成成功,则pid的值为0,然后调用handleChildProc()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}

-

这里会根据invokeWith参数决定使用哪种执行方式,我们只要知道SystemServer和apk都是通过RuntimeInit类生成的即可。

-

(4)RuntimeInit#zygoteInit()

1
2
3
4
5
6
7
8
9
10
11
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}

-

最后会调用applicationInit()方法:

1
2
3
4
5
6
7
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// 省略代码
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

-

最关键的就是invokeStaticMain()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

-

上述方法中,通过反射的方式获取main方法,最后抛出一个MethodAndArgsCaller,它继承于Exception,同时他也是实现了Runnable接口。看 throw new ZygoteInit.MethodAndArgsCaller()的代码注释我们可以知道,它会在ZygoteInit.main()方法中被捕获,执行run方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

-

我们可以知道,最终它将会调用ActivityThread类的main方法!
所以,我们解决了第四个问题,ActivityThread的main方法是在生成一个新的app进程过程中调用的,具体是通过与Zygote通信,之后通过RuntimeInit类采用反射的方式调用ActivityThread#main()方法,即生成app中的主线程(UI线程)!

- - + +
+ + 阅读全文 » + +
+ From 9c9781ea9632d0109ebd0c99508749f990ca58f9 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sun, 17 Dec 2017 18:07:45 +0800 Subject: [PATCH 10/64] Site updated: 2017-12-17 18:07:44 --- .../index.html" | 2 +- .../index.html" | 2 +- .../index.html" | 2 +- .../index.html" | 2 +- .../index.html" | 6 +- .../index.html" | 888 ++++++++++++++++++ archives/2017/10/index.html | 4 +- archives/2017/11/index.html | 4 +- archives/2017/12/index.html | 41 +- archives/2017/index.html | 41 +- archives/index.html | 41 +- categories/Android/index.html | 30 +- categories/index.html | 4 +- css/main.css | 2 +- index.html | 165 +++- .../index.html" | 2 +- .../index.html" | 30 +- .../index.html" | 2 +- tags/Python/index.html | 2 +- tags/index.html | 4 +- 20 files changed, 1248 insertions(+), 26 deletions(-) create mode 100644 "2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index dab840b..fa52972 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -477,7 +477,7 @@

- 5 + 6 日志 diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index 498a507..2f8e766 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -466,7 +466,7 @@

- 5 + 6 日志 diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index a195e3e..b5c8126 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -478,7 +478,7 @@

使用 - 5 + 6 日志 diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index 526a43c..3d8e7b3 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -506,7 +506,7 @@

- 5 + 6 日志 diff --git "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" index ebdddd3..b095d8b 100644 --- "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" +++ "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" @@ -437,6 +437,10 @@

+ + @@ -516,7 +520,7 @@

- 5 + 6 日志 diff --git "a/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" new file mode 100644 index 0000000..cbb1716 --- /dev/null +++ "b/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -0,0 +1,888 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Butterknife 8.8.1源码解析 | Xu的博客 + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Butterknife 8.8.1源码解析

+ + + +
+ + + + + +
+ + + + + +

一、本文需要解决的问题

我研究Butterknife源码的目的是为了解决以下几个我在使用过程中所思考的问题:

+
    +
  1. 在很多文章中都提到Butterknife使用编译时注解技术,什么是编译时注解?
  2. +
  3. 是完全不调用findViewById()等方法了吗?
  4. +
  5. 为什么绑定各种view时不能使用private修饰?
  6. +
  7. 绑定监听事件的时候方法命名有限制吗?
  8. +
+ +

二、初步分析

基于Butterknife 8.8.1版本。
为了更好地分析代码,我写了一个demo:
MainActivity.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends Activity {
@BindView(R.id.text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.text)
public void textClick() {
Toast.makeText(MainActivity.this, "textview clicked", Toast.LENGTH_LONG);
}
}

+

我们从Butterknife.bind()方法,即方法入口开始分析:
ButterKnife#bind():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
// !!!
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
// !!!
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}

+

代码还是比较清晰的,bind()方法的流程:

+
    +
  1. 首先获取当前activity的sourceView,其实就是获取Activity的DecorView,DecorView是整个ViewTree的最顶层View,包含标题view和内容view这两个子元素。我们一直调用的setContentView()方法其实就是往内容view中添加view元素。
  2. +
  3. 然后调用createBinding() –> findBindingConstructorForClass(),重点是
    1
    2
    3
    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
    bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
    BINDINGS.put(cls, bindingCtor);
    +
  4. +
+

按照所写的代码,这里会加载一个MainActivity_ViewBinding类,然后获取这个类里面的双参数(Activity, View)构造方法,最后放在BINDINGS里面,它是一个map,主要作用是缓存。在下次使用的时候,就可以从缓存中获取到:

1
2
3
4
5
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}

+

三、关于编译时注解

在上面分析过程中,我们知道最后我们会去加载一个MainActivity_ViewBinding类,而这个类并不是我们自己编写的,而是通过编译时注解(APT - Annotation Processing Tool)的技术生成的。
这一节将会介绍一下这个技术。

+
1、什么是注解

注解其实很常见,比如说Activity自动生成的onCreate()方法上面就有一个@Override注解
image.png

+
    +
  • 注解的概念:
    能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。
  • +
  • 注解的分类:
      +
    • 标准注解,如Override, Deprecated,SuppressWarnings等
    • +
    • 元注解,如@Retention, @Target, @Inherited, @Documented。当我们要自定义注解时,需要使用它们
    • +
    • 自定义注解,表示自己根据需要定义的 Annotation
    • +
    +
  • +
  • 注解的作用:
      +
    • 标记,用于告诉编译器一些信息
    • +
    • 编译时动态处理,如动态生成java代码
    • +
    • 运行时动态处理,如得到注解信息
    • +
    +
  • +
+
2、运行时注解 vs 编译时注解

一般有些人提到注解,普遍就会觉得性能低下。但是真正使用注解的开源框架却很多例如ButterKnife,Retrofit等等。所以注解是好是坏呢?
首先,并不是注解就等于性能差。更确切的说是运行时注解这种方式,由于它的原理是java反射机制,所以的确会造成较为严重的性能问题。
但是像Butterknife这个框架,它使用的技术是编译时注解,它不会影响app实际运行的性能(影响的应该是编译时的效率)。
一句话总结:

+
    +
  • 运行时注解就是在应用运行的过程中,动态地获取相关类,方法,参数等信息,由于使用java反射机制,性能会有问题;
  • +
  • 编译时注解由于是在代码编译过程中对注解进行处理,通过注解获取相关类,方法,参数等信息,然后在项目中生成代码,运行时调用,其实和直接运行手写代码没有任何区别,也就没有性能问题了。
    这样我们就解决了第一个问题。
  • +
+
3、如何使用编译时注解技术

这里要借助到一个类:AbstractProcessor

1
2
3
4
5
6
7
8
9
public class TestProcessor extends AbstractProcessor
{
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
// TODO Auto-generated method stub
return false;
}
}

+

重点是process()方法,它相当于每个处理器的主函数main(),可以在这里写相关的扫描和处理注解的代码,他会帮助生成相关的Java文件。后面我们可以具体看一下Butterknife中的使用。

+

四、进一步分析MainActivity_ViewBinding

我们了解了编译时注解的基本概念之后,我们先看一下MainActivity_ViewBinding类具体实现了什么。
在编写完demo之后,需要先build一下项目,之后可以在build/generated/source/apt/debug/包名/下面找到这个类,如图所示:

接上面的分析,到最后会通过反射的方式去调用MainActivity_ViewBinding的构造方法。我们直接看这个类的构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
// 1
view = Utils.findRequiredView(source, R.id.text, "field 'textView' and method 'textClick'");
// 2
target.textView = Utils.castView(view, R.id.text, "field 'textView'", TextView.class);
// 3
view2131165290 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.textClick();
}
});
}

+
1、findRequiredView()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
+

看到这里我们已经解决了第二个问题:到最后还是会调用findViewById()方法,并没有完全舍弃这个方法,这里的source代表着在上面代码中传入的MainActivity的DecorView。大家可以尝试一下将Activity转化为Fragment的情况~

+
2、Util.castView

在这里,我们解决了第三个问题,绑定各种view时不能使用private修饰,而是需要用public或default去修饰,因为如果采用private修饰的话,将无法通过对象.成员变量方式获取到我们需要绑定的View
Util#castView():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
try {
return cls.cast(view);
} catch (ClassCastException e) {
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}

+

这里直接调用Class.cast强制转换类型,将View转化为我们需要的view(TextView)。

+
3、
1
2
3
4
5
6
7
view2131165290 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.textClick();
}
});
+

这里会生成一个成员变量来保存我们需要绑定的View,重点是下面它会调用setOnClickListener()方法,传入的是DebouncingOnClickListener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* A {@linkplain View.OnClickListener click listener} that debounces multiple clicks posted in the
* same frame. A click on one button disables all buttons for that frame.
*/
public abstract class DebouncingOnClickListener implements View.OnClickListener {
static boolean enabled = true;
private static final Runnable ENABLE_AGAIN = new Runnable() {
@Override public void run() {
enabled = true;
}
};
@Override
public final void onClick(View v) {
if (enabled) {
enabled = false;
v.post(ENABLE_AGAIN);
doClick(v);
}
}
public abstract void doClick(View v);
}

+

这个DebouncingOnClickListener是View.OnClickListener的一个子类,作用是防止一定时间内对view的多次点击,即防止快速点击控件所带来的一些不可预料的错误。个人认为这个类写的非常巧妙,既完美解决了问题,又写的十分优雅,一点都不臃肿。
这里抽象了doClick()方法,实现代码中是直接调用了target.textClick(),这里解决了第四个问题:绑定监听事件的时候方法命名是没有限制的,不一定需要严格命名为onClick,也不一定需要传入View参数。

+

五、MainActivity_ViewBinding的生成

上文提到,MainActivity_ViewBinding类是通过编译时注解技术生成的,我们找到Butterknife相关的继承于AbstractProcessor的类,ButterKnifeProcessor,我们直接看process()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class ButterKnifeProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
// 1
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
}

+

1、findAndParseTargets()
这个方法的作用是处理所有的@BindXX注解,我们直接看处理@BindView的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
// 省略代码
// Process each @BindView element.
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
// 省略代码
}
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// Start by verifying common generated code restrictions.
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);
// Verify that the target type extends from View.
TypeMirror elementType = element.asType();
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
Name qualifiedName = enclosingElement.getQualifiedName();
Name simpleName = element.getSimpleName();
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
if (elementType.getKind() == TypeKind.ERROR) {
note(element, "@%s field with unresolved type (%s) "
+ "must elsewhere be generated as a View or interface. (%s.%s)",
BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
} else {
error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
BindView.class.getSimpleName(), qualifiedName, simpleName);
hasError = true;
}
}
if (hasError) {
return;
}
// Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();
BindingSet.Builder builder = builderMap.get(enclosingElement);
QualifiedId qualifiedId = elementToQualifiedId(element, id);
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(getId(qualifiedId));
if (existingBindingName != null) {
error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
BindView.class.getSimpleName(), id, existingBindingName,
enclosingElement.getQualifiedName(), element.getSimpleName());
return;
}
} else {
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
builder.addField(getId(qualifiedId), new FieldViewBinding(name, type, required));
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}

+

代码逻辑是处理获取相关注解的信息,比如绑定的资源id等等,然后通过获取BindingSet.Builder类的实例来创建一一对应的关系,这里有一个判断,如果builderMap存在相应实例则直接取出builder,否则通过getOrCreateBindingBuilder()方法生成一个新的builder,最后调用builder.addField()方法。

+

后续的话返回到findAndParseTargets()方法的最后一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
// bindView()
// Associate superclass binders with their subclass binders. This is a queue-based tree walk
// which starts at the roots (superclasses) and walks to the leafs (subclasses).
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
new ArrayDeque<>(builderMap.entrySet());
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
TypeElement parentType = findParentType(type, erasedTargetNames);
if (parentType == null) {
bindingMap.put(type, builder.build());
} else {
BindingSet parentBinding = bindingMap.get(parentType);
if (parentBinding != null) {
builder.setParent(parentBinding);
bindingMap.put(type, builder.build());
} else {
// Has a superclass binding but we haven't built it yet. Re-enqueue for later.
entries.addLast(entry);
}
}
}
return bindingMap;
}

+

这里会生成一个bindingMap,key为TypeElement,代表注解元素类型,value为BindSet类,通过上述的builder.build()生成,BindingSet类中存储了很多信息,例如绑定view的类型,生成类的className等等,方便我们后续生成java文件。最后回到process方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}

+

最后通过brewJava()方法生成java代码。
这里使用到的是javapoet。javapoet是一个开源库,通过处理相应注解来生成最后的java文件,这里是项目地址传送门,具体技术不再分析。

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ +
+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 8303bc8..7e59027 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 5 篇日志。 继续努力。 + 嗯..! 目前共计 6 篇日志。 继续努力。 @@ -406,7 +406,7 @@

- 5 + 6 日志 diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index a7276af..58ea69e 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 5 篇日志。 继续努力。 + 嗯..! 目前共计 6 篇日志。 继续努力。 @@ -332,7 +332,7 @@

- 5 + 6 日志 diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html index 2504a90..525549d 100644 --- a/archives/2017/12/index.html +++ b/archives/2017/12/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 5 篇日志。 继续努力。 + 嗯..! 目前共计 6 篇日志。 继续努力。 @@ -249,6 +249,43 @@

2017

+ + + + + + + + + + + + + + +
@@ -332,7 +369,7 @@

- 5 + 6 日志 diff --git a/archives/2017/index.html b/archives/2017/index.html index a53a1fb..9a8ac3a 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 5 篇日志。 继续努力。 + 嗯..! 目前共计 6 篇日志。 继续努力。 @@ -249,6 +249,43 @@

2017

+ + + + + + + + + + + + + + +
@@ -480,7 +517,7 @@

- 5 + 6 日志 diff --git a/archives/index.html b/archives/index.html index 1dcabd4..b90496a 100644 --- a/archives/index.html +++ b/archives/index.html @@ -230,7 +230,7 @@ - 嗯..! 目前共计 5 篇日志。 继续努力。 + 嗯..! 目前共计 6 篇日志。 继续努力。 @@ -249,6 +249,43 @@

2017

+ + + + + + + + + + + + + + +
@@ -480,7 +517,7 @@

- 5 + 6 日志 diff --git a/categories/Android/index.html b/categories/Android/index.html index 38ab910..5ba9850 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -231,6 +231,34 @@

Android分类 + + + + + +
@@ -425,7 +453,7 @@

- 5 + 6 日志 diff --git a/categories/index.html b/categories/index.html index ba991b3..fef932b 100644 --- a/categories/index.html +++ b/categories/index.html @@ -242,7 +242,7 @@

文章分类

目前共计 1 个分类 @@ -306,7 +306,7 @@

文章分类

- 5 + 6 日志 diff --git a/css/main.css b/css/main.css index e46ba57..2de6bee 100644 --- a/css/main.css +++ b/css/main.css @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #ff6362; + background: #ffb17f; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index 55464e4..9cfe6ab 100644 --- a/index.html +++ b/index.html @@ -227,6 +227,169 @@ +
+ + + +
+ + + + + + + +
+ + + +

+ +

+ + + +
+ + + + + +
+ + + + + + +

一、本文需要解决的问题

我研究Butterknife源码的目的是为了解决以下几个我在使用过程中所思考的问题:

+
    +
  1. 在很多文章中都提到Butterknife使用编译时注解技术,什么是编译时注解?
  2. +
  3. 是完全不调用findViewById()等方法了吗?
  4. +
  5. 为什么绑定各种view时不能使用private修饰?
  6. +
  7. 绑定监听事件的时候方法命名有限制吗?
  8. +
+ +
+ + 阅读全文 » + +
+ + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ +
+
+ + + +
+ + + + + + + + + + +
@@ -1064,7 +1227,7 @@

- 5 + 6 日志 diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index 20e9ab7..c796893 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -340,7 +340,7 @@

- 5 + 6 日志 diff --git "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" index cc075f3..6b6a849 100644 --- "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" +++ "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -231,6 +231,34 @@

Android源码解析标签 + + + + + +
@@ -312,7 +340,7 @@

- 5 + 6 日志 diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 297ae86..fef1abd 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -340,7 +340,7 @@

- 5 + 6 日志 diff --git a/tags/Python/index.html b/tags/Python/index.html index e3b6340..9a45943 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -312,7 +312,7 @@

- 5 + 6 日志 diff --git a/tags/index.html b/tags/index.html index 42904c8..7e587eb 100644 --- a/tags/index.html +++ b/tags/index.html @@ -242,7 +242,7 @@

文章标签

目前共计 4 个标签 @@ -306,7 +306,7 @@

文章标签

- 5 + 6 日志 From 623e9617ab0c8a2341ced9bf3f1d0ac2ebf46135 Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sun, 24 Dec 2017 11:19:32 +0800 Subject: [PATCH 11/64] Site updated: 2017-12-24 11:19:31 --- .../index.html" | 4 +++- .../index.html" | 4 +++- .../index.html" | 4 +++- .../index.html" | 4 +++- .../index.html" | 4 +++- .../index.html" | 4 +++- archives/2017/10/index.html | 4 +++- archives/2017/11/index.html | 4 +++- archives/2017/12/index.html | 4 +++- archives/2017/index.html | 4 +++- archives/index.html | 4 +++- categories/Android/index.html | 4 +++- categories/index.html | 4 +++- css/main.css | 4 ++-- index.html | 4 +++- "tags/Android\345\256\236\350\267\265/index.html" | 4 +++- .../index.html" | 4 +++- "tags/Android\347\237\245\350\257\206\347\202\271/index.html" | 4 +++- tags/Python/index.html | 4 +++- tags/index.html | 4 +++- 20 files changed, 59 insertions(+), 21 deletions(-) diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index fa52972..730f5bb 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -100,7 +100,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -593,6 +593,8 @@

+ 0% + diff --git "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" index 2f8e766..55dbf19 100644 --- "a/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" +++ "b/2017/10/21/Android View\345\212\250\347\224\273\345\222\214\345\261\236\346\200\247\345\212\250\347\224\273\347\256\200\345\215\225\350\247\243\346\236\220/index.html" @@ -100,7 +100,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -582,6 +582,8 @@

+ 0% + diff --git "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" index b5c8126..a6840d0 100644 --- "a/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" +++ "b/2017/10/21/\345\210\206\344\272\253\344\270\200\344\270\213\350\207\252\345\267\261\345\201\232\347\232\204\344\270\200\344\270\252\345\233\276\347\211\207\345\212\240\350\275\275\345\272\223XImageLoader/index.html" @@ -100,7 +100,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -594,6 +594,8 @@

使用 + 0% + diff --git "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" index 3d8e7b3..597fe22 100644 --- "a/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" +++ "b/2017/11/04/Android adb\345\221\275\344\273\244\347\232\204\344\270\200\344\272\233\345\256\236\351\231\205\350\277\220\347\224\250/index.html" @@ -106,7 +106,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -622,6 +622,8 @@

+ 0% + diff --git "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" index b095d8b..7215f12 100644 --- "a/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" +++ "b/2017/12/05/Android 7.0 startActivity()\346\272\220\347\240\201\350\247\243\346\236\220\344\273\245\345\217\212\345\257\271\345\207\240\344\270\252\351\227\256\351\242\230\347\232\204\346\200\235\350\200\203/index.html" @@ -100,7 +100,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -636,6 +636,8 @@

+ 0% + diff --git "a/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" index cbb1716..abe7b54 100644 --- "a/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" +++ "b/2017/12/17/Butterknife 8.8.1\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -103,7 +103,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -626,6 +626,8 @@

+ 0% + diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 7e59027..12840c3 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -506,6 +506,8 @@

+ 0% +
diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 58ea69e..52caa7b 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -432,6 +432,8 @@

+ 0% +
diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html index 525549d..afc61b2 100644 --- a/archives/2017/12/index.html +++ b/archives/2017/12/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -469,6 +469,8 @@

+ 0% +
diff --git a/archives/2017/index.html b/archives/2017/index.html index 9a8ac3a..ff29839 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -617,6 +617,8 @@

+ 0% +
diff --git a/archives/index.html b/archives/index.html index b90496a..bbd0808 100644 --- a/archives/index.html +++ b/archives/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -617,6 +617,8 @@

+ 0% +
diff --git a/categories/Android/index.html b/categories/Android/index.html index 5ba9850..6542627 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -553,6 +553,8 @@

+ 0% +
diff --git a/categories/index.html b/categories/index.html index fef932b..bc3ae6f 100644 --- a/categories/index.html +++ b/categories/index.html @@ -99,7 +99,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -406,6 +406,8 @@

文章分类

+ 0% +
diff --git a/css/main.css b/css/main.css index 2de6bee..354a272 100644 --- a/css/main.css +++ b/css/main.css @@ -1063,7 +1063,7 @@ pre .javascript .function { right: 30px; z-index: 1050; padding: 0 6px; - width: 24px; + width: initial; background: #222; font-size: 12px; opacity: 0.6; @@ -1823,7 +1823,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #ffb17f; + background: #e72eed; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index 9cfe6ab..40dac22 100644 --- a/index.html +++ b/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -1327,6 +1327,8 @@

+ 0% + diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index c796893..c010bb2 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -440,6 +440,8 @@

+ 0% +
diff --git "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" index 6b6a849..4fcb278 100644 --- "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" +++ "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -440,6 +440,8 @@

+ 0% +
diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index fef1abd..d969641 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -440,6 +440,8 @@

+ 0% +
diff --git a/tags/Python/index.html b/tags/Python/index.html index 9a45943..e84da12 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -98,7 +98,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -412,6 +412,8 @@

+ 0% +
diff --git a/tags/index.html b/tags/index.html index 7e587eb..751cfc5 100644 --- a/tags/index.html +++ b/tags/index.html @@ -99,7 +99,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -406,6 +406,8 @@

文章标签

+ 0% +
From f933b706cbb2bf9c5c2b1713a38659c23015dace Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sun, 24 Dec 2017 12:18:32 +0800 Subject: [PATCH 12/64] Site updated: 2017-12-24 12:18:32 --- .../index.html" | 99 +++++++++++++-- .../index.html" | 99 +++++++++++++-- .../index.html" | 99 +++++++++++++-- .../index.html" | 99 +++++++++++++-- .../index.html" | 99 +++++++++++++-- .../index.html" | 99 +++++++++++++-- archives/2017/10/index.html | 101 ++++++++++++++-- archives/2017/11/index.html | 101 ++++++++++++++-- archives/2017/12/index.html | 101 ++++++++++++++-- archives/2017/index.html | 101 ++++++++++++++-- archives/index.html | 101 ++++++++++++++-- categories/Android/index.html | 101 ++++++++++++++-- categories/index.html | 101 ++++++++++++++-- css/main.css | 92 ++++++++------ favicon.ico | Bin 1150 -> 0 bytes images/apple-touch-icon.png | Bin 0 -> 76744 bytes images/browserconfig.xml | 9 ++ images/favicon-16x16.png | Bin 0 -> 22013 bytes images/favicon-32x32.png | Bin 0 -> 23321 bytes images/manifest.json | 1 + index.html | 113 +++++++++++++++--- .../index.html" | 101 ++++++++++++++-- .../index.html" | 101 ++++++++++++++-- .../index.html" | 101 ++++++++++++++-- tags/Python/index.html | 101 ++++++++++++++-- tags/index.html | 101 ++++++++++++++-- 26 files changed, 1757 insertions(+), 264 deletions(-) delete mode 100644 favicon.ico create mode 100644 images/apple-touch-icon.png create mode 100644 images/browserconfig.xml create mode 100644 images/favicon-16x16.png create mode 100644 images/favicon-32x32.png create mode 100644 images/manifest.json diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index 730f5bb..dcebb6f 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -14,6 +14,13 @@ + + + + + + + @@ -45,6 +52,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -53,18 +98,20 @@ - + + + - + - - + + @@ -100,7 +147,7 @@ root: '/', scheme: 'Pisces', version: '5.1.3', - sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":false}, + sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":true}, fancybox: true, tabs: true, motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}, @@ -191,6 +238,26 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -

这篇文章会同步到我的个人日志,如有问题,请大家踊跃提出,谢谢大家!

diff --git a/css/main.css b/css/main.css index 8fea1b2..8fd7fd6 100644 --- a/css/main.css +++ b/css/main.css @@ -1817,7 +1817,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #9454cb; + background: #9406ff; } .links-of-blogroll { font-size: 13px; From 1de9cbf31d7278b54c0fa1fa2f2e66308286fb1c Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Thu, 21 Jun 2018 16:31:44 +0800 Subject: [PATCH 20/64] Site updated: 2018-06-21 16:31:43 --- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- .../index.html" | 6 +- archives/2017/10/index.html | 4 +- archives/2017/11/index.html | 4 +- archives/2017/12/index.html | 4 +- archives/2017/index.html | 4 +- archives/2018/01/index.html | 4 +- archives/2018/06/index.html | 4 +- archives/2018/index.html | 4 +- archives/index.html | 4 +- categories/Android/index.html | 4 +- categories/index.html | 4 +- css/main.css | 2 +- index.html | 22 ++--- robots.txt | 11 +++ sitemap.xml | 81 +++++++++++++++++++ .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- tags/Python/index.html | 4 +- tags/index.html | 4 +- 27 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 robots.txt create mode 100644 sitemap.xml diff --git "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" index c52a8fe..07551cf 100644 --- "a/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" +++ "b/2017/10/21/Android Handler\346\234\272\345\210\266\347\220\206\350\247\243/index.html" @@ -130,7 +130,7 @@ - + @@ -167,7 +167,7 @@ - + @@ -300,7 +300,7 @@
- + @@ -475,7 +475,7 @@

- 8 + 9 日志

diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html index 8c47883..93582a6 100644 --- a/archives/2017/11/index.html +++ b/archives/2017/11/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -401,7 +401,7 @@

- 8 + 9 日志 diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html index c1fb633..0c9cc03 100644 --- a/archives/2017/12/index.html +++ b/archives/2017/12/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -438,7 +438,7 @@

- 8 + 9 日志 diff --git a/archives/2017/index.html b/archives/2017/index.html index 11a1f64..39f414a 100644 --- a/archives/2017/index.html +++ b/archives/2017/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -586,7 +586,7 @@

- 8 + 9 日志 diff --git a/archives/2018/01/index.html b/archives/2018/01/index.html index 392e1c1..4546006 100644 --- a/archives/2018/01/index.html +++ b/archives/2018/01/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -401,7 +401,7 @@

- 8 + 9 日志 diff --git a/archives/2018/06/index.html b/archives/2018/06/index.html index ab6d1b2..9346ed9 100644 --- a/archives/2018/06/index.html +++ b/archives/2018/06/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -401,7 +401,7 @@

- 8 + 9 日志 diff --git a/archives/2018/08/index.html b/archives/2018/08/index.html new file mode 100644 index 0000000..5f75989 --- /dev/null +++ b/archives/2018/08/index.html @@ -0,0 +1,769 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 归档 | Xu的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2018/index.html b/archives/2018/index.html index 67ccd17..a53c37b 100644 --- a/archives/2018/index.html +++ b/archives/2018/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -316,6 +316,43 @@

2018

+ + + + + + + + + + + + + + +
@@ -438,7 +475,7 @@

- 8 + 9 日志 diff --git a/archives/index.html b/archives/index.html index c91f09e..5037fd8 100644 --- a/archives/index.html +++ b/archives/index.html @@ -297,7 +297,7 @@ - 嗯..! 目前共计 8 篇日志。 继续努力。 + 嗯..! 目前共计 9 篇日志。 继续努力。 @@ -316,6 +316,43 @@

2018

+ + + + + + + + + + + + + + +
@@ -665,7 +702,7 @@

- 8 + 9 日志 diff --git a/baidusitemap.xml b/baidusitemap.xml index 36e891d..48d818a 100644 --- a/baidusitemap.xml +++ b/baidusitemap.xml @@ -3,6 +3,26 @@ + + https://xudeveloper.github.io/2018/08/08/%E6%98%93%E7%94%A8%E7%89%88Popupwindow%20by%20Kotlin%E4%BA%86%E8%A7%A3%E4%B8%80%E4%B8%8B/ + 2018-08-08T15:59:59.424Z + + + 易用版Popupwindow by Kotlin了解一下 + 2018-08-08T13:07:23.000Z + + Android实践 + + + + + + + + + + + https://xudeveloper.github.io/2018/06/18/Android%20AccessibilityService%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ 2018-07-15T03:54:11.016Z diff --git a/categories/Android/index.html b/categories/Android/index.html index 33694d3..5b1d7d4 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -298,6 +298,34 @@

Android分类 + + + + + +
@@ -578,7 +606,7 @@

- 8 + 9 日志 diff --git a/categories/index.html b/categories/index.html index 9abd13e..1c24292 100644 --- a/categories/index.html +++ b/categories/index.html @@ -309,7 +309,7 @@

文章分类

目前共计 1 个分类 @@ -375,7 +375,7 @@

文章分类

- 8 + 9 日志 diff --git a/css/main.css b/css/main.css index 8059858..c9284fd 100644 --- a/css/main.css +++ b/css/main.css @@ -1817,7 +1817,7 @@ pre .javascript .function { width: 4px; height: 4px; border-radius: 50%; - background: #905e7c; + background: #ab9fff; } .links-of-blogroll { font-size: 13px; diff --git a/index.html b/index.html index d8cfa92..d8416b2 100644 --- a/index.html +++ b/index.html @@ -294,6 +294,164 @@ +
+ + + +
+ + + + + + + +
+ + + +

+ +

+ + + +
+ + + + + +
+ + + + + + +

概述

XPopupWindow,对系统的PopupWindow进行进一步封装和加强以便于使用。采用Kotlin语言,提供了许多额外的功能方法例如设置弹窗位置,调整弹窗动画等等。

+

项目地址

XPopupWindow

+ +
+ + 阅读全文 » + +
+ + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ +
+
+ + + +
+ + + + + + + + + + +
@@ -1621,7 +1779,7 @@

- 8 + 9 日志 diff --git a/sitemap.xml b/sitemap.xml index 6e418c2..a584c7a 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,6 +1,13 @@ + + https://xudeveloper.github.io/2018/08/08/%E6%98%93%E7%94%A8%E7%89%88Popupwindow%20by%20Kotlin%E4%BA%86%E8%A7%A3%E4%B8%80%E4%B8%8B/ + + 2018-08-08T15:59:59.424Z + + + https://xudeveloper.github.io/2018/06/18/Android%20AccessibilityService%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ diff --git "a/tags/Android\345\256\236\350\267\265/index.html" "b/tags/Android\345\256\236\350\267\265/index.html" index 4d5893c..65afb1c 100644 --- "a/tags/Android\345\256\236\350\267\265/index.html" +++ "b/tags/Android\345\256\236\350\267\265/index.html" @@ -298,6 +298,34 @@

Android实践标签 + + + + + +
@@ -409,7 +437,7 @@

- 8 + 9 日志 diff --git "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" index 7662007..b87236c 100644 --- "a/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" +++ "b/tags/Android\346\272\220\347\240\201\350\247\243\346\236\220/index.html" @@ -465,7 +465,7 @@

- 8 + 9 日志 diff --git "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" index 9c0a5de..6069e55 100644 --- "a/tags/Android\347\237\245\350\257\206\347\202\271/index.html" +++ "b/tags/Android\347\237\245\350\257\206\347\202\271/index.html" @@ -409,7 +409,7 @@

- 8 + 9 日志 diff --git a/tags/Python/index.html b/tags/Python/index.html index a0aeaf7..926f1cc 100644 --- a/tags/Python/index.html +++ b/tags/Python/index.html @@ -381,7 +381,7 @@

- 8 + 9 日志 diff --git a/tags/index.html b/tags/index.html index b53dabe..6e0bdee 100644 --- a/tags/index.html +++ b/tags/index.html @@ -309,7 +309,7 @@

文章标签

目前共计 4 个标签 @@ -375,7 +375,7 @@

文章标签

- 8 + 9 日志 From 7a89a5d4a8816e387c4d1d8c4cfdd264dfa832fa Mon Sep 17 00:00:00 2001 From: XuDeveloper <280745397@qq.com> Date: Sat, 18 Aug 2018 11:34:34 +0800 Subject: [PATCH 30/64] Site updated: 2018-08-18 11:34:33 --- archives/2017/10/index.html | 6 +- archives/2017/11/index.html | 2 +- archives/2017/12/index.html | 4 +- archives/2017/index.html | 12 +- archives/2018/01/index.html | 2 +- archives/2018/06/index.html | 2 +- archives/2018/08/index.html | 2 +- archives/2018/index.html | 6 +- archives/index.html | 18 +- archives/undefined.html | 934 ++++++++++++++++++ baidusitemap.xml | 18 +- categories/Android/index.html | 18 +- css/main.css | 2 +- index.html | 72 +- sitemap.xml | 18 +- .../index.html" | 6 +- .../index.html" | 8 +- .../index.html" | 4 +- tags/Python/index.html | 2 +- 19 files changed, 1035 insertions(+), 101 deletions(-) create mode 100644 archives/undefined.html diff --git a/archives/2017/10/index.html b/archives/2017/10/index.html index 77af4d3..8ba682c 100644 --- a/archives/2017/10/index.html +++ b/archives/2017/10/index.html @@ -321,7 +321,7 @@

2017

-

+ +
+
+
+
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +

Android Handler机制理解

+ + + +
+ + + + + +
+ + + + + +

1.何谓Handler机制?

一般来说,当你的应用被创建的时候,会创建一条应用的主线程。因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

+ +

当然,你可以创建自己的线程去做操作,但如何应用的主线程通信呢。那就要使用到Handler机制了。如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的相关方法实现的。

+

那接下来就要介绍一下这个消息通讯机制Handler,涉及到三个主要的类:Looper,Handler和Message类。

+

2.Looper

重点方法为:prepare()和loop()
Looper#prepare():

1
2
3
4
5
6
7
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

+

解释:首先它创建了一个ThreadLocal对象,它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据。然后在prepare方法中将looper存储在线程里面。

+

Looper#Looper():

1
2
3
4
5
private Looper(boolean quitAllowed) {  
     mQueue = new MessageQueue(quitAllowed);  
     mRun = true;  
     mThread = Thread.currentThread();  
}

+

而在构造方法中,Looper创建了一个MessageQueue,虽然是叫queue但其实内部实现是一个单链表。

+

Looper#loop():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static void loop() {  
        final Looper me = myLooper();  
        if (me == null) {  
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }  
        final MessageQueue queue = me.mQueue;  
        // Make sure the identity of this thread is that of the local process,  
        // and keep track of what that identity token actually is.  
        Binder.clearCallingIdentity();  
        final long ident = Binder.clearCallingIdentity();  
        for (;;) {  
            Message msg = queue.next(); // might block  
            if (msg == null) {  
                // No message indicates that the message queue is quitting.  
                return;  
            }  
            // This must be in a local variable, in case a UI event sets the logger 
            Printer logging = me.mLogging;  
            if (logging != null) {  
                logging.println(">>>>> Dispatching to " + msg.target + " " +  
                        msg.callback + ": " + msg.what);  
            }    
            msg.target.dispatchMessage(msg);    
            if (logging != null) {  
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
            }   
            // Make sure that during the course of dispatching the  
            // identity of the thread wasn't corrupted.  
            final long newIdent = Binder.clearCallingIdentity();  
            if (ident != newIdent) {  
                Log.wtf(TAG, "Thread identity changed from 0x"  
                        + Long.toHexString(ident) + " to 0x"  
                        + Long.toHexString(newIdent) + " while dispatching to "  
                        + msg.target.getClass().getName() + " "  
                        + msg.callback + " what=" + msg.what);  
            }    
            msg.recycle();  
        }  
public static Looper myLooper() {
      return sThreadLocal.get();
}
}

+

解释:方法直接返回了前面sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。拿到该looper实例中的mQueue即消息队列后进入了无限循环,不断从队列中取出一条消息,如果没有消息则阻塞。如果取得消息使用调用msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。而msg的target是什么呢?其实就是前面讲到的handler对象,最后会释放消息占据的资源。

+

Looper类总结:
1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2.loop()方法,不断从MessageQueue中去取消息,交给message的target的dispatchMessage去处理。

+

接下来就要讲发送消息的对象了,这个对象就是Handler。

+

3.Handler

主要作用是将一个任务切换到某个指定的线程中去执行,同时为了解决在子线程中无法访问UI的矛盾。

+

所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的。

+

Handler#Handler():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Handler() {  
        this(nullfalse);  
}  
public Handler(Callback callback, boolean async) {  
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());  
            }  
        }    
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
}

+

解释:在构造的时候会检查当前的Handler是否为静态类,不是静态声明的话会打印Log,提示会有内存泄漏现象的产生,然后通过Looper.myLooper()方法获取到当前线程的Looper实例(mLooper)并进一步获取到当前线程的消息队列(mQueue),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

+

使用的时候我们会经常使用到sendMessage方法,我们来看看源码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final boolean sendMessage(Message msg)  {  
     return sendMessageDelayed(msg, 0);  
 }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
     Message msg = Message.obtain();  
     msg.what = what;  
     return sendMessageDelayed(msg, delayMillis);  
 } 
public final boolean sendMessageDelayed(Message msg, long delayMillis) {  
       if (delayMillis < 0) {  
           delayMillis = 0;  
       }  
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
}  
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
       MessageQueue queue = mQueue;  
       if (queue == null) {  
           RuntimeException e = new RuntimeException(  
                   this + " sendMessageAtTime() called with no mQueue");  
           Log.w("Looper", e.getMessage(), e);  
           return false;  
       }  
       return enqueueMessage(queue, msg, uptimeMillis);  
   }  
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
       msg.target = this;  
       if (mAsynchronous) {  
           msg.setAsynchronous(true);  
       }  
       return queue.enqueueMessage(msg, uptimeMillis);  
}

+

一路跳到最后的enqueueMessage方法,enqueueMessage中首先为msg.target赋值为this,因为Looper中的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,保存到消息队列中去。

+

现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispatchMessage方法。
Handler#dispatchMessage():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void dispatchMessage(Message msg) {  
        if (msg.callback != null) { 
            // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);  // 并直接调用callback的run方法!
        } else { 
            // 如果handler本身设置了callback,则执行callback 
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }
// 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);  
        }  
}``` 
几个变量和方法的解释: 
1.callback:message携带的Runnable对象,实际上就是Handler的post方法所传递的Runnable参数。
我们来看一下Handler的post方法源码实现:
```java
mHandler.post(new Runnable()  {  
        @Override  
        public void run()  {  
            // code  
        }  
});

+

其实这个Runnable并没有创建什么线程,而是发送了一条消息:

1
2
3
4
5
6
7
8
public final boolean post(Runnable r)  {  
      return  sendMessageDelayed(getPostMessage(r), 0);  
}  
private static Message getPostMessage(Runnable r) {  
      Message m = Message.obtain();  
      m.callback = r;  
      return m;  
}

+

在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message。
注意:产生一个Message对象,可以new,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new重新分配内存。

+

2.mCallback:可通过Handler handler = new Handler(callback); 可以用来创建一个Handler实例但不需要派生Handler子类。它可用来拦截消息!当mCallback的handleMessage返回true的时候可以拦截消息,具体的逻辑看上面的代码很容易理解!

+

3.handleMessage(msg):它是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。

+

到此,这个流程已经解释完毕,总结一下:
1.首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2.Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与MessageQueue相关联。
4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)。
6.在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

+

下面是个人认为在 Activity 中一个合格的 Handler 该有的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class MyHandler extends Handler {
private WeakReference<CustomActivity> activityWeakReference;
public MyHandler(CustomActivity activity) {
activityWeakReference = new WeakReference<CustomActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
CustomActivity activtiy = activityWeakReference.get();
if (activity != null) {
// code
}
}
}

+ + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+ + +
+ + + + + + + + + +
+ + + + + + + + + +
+
+ +
+ +
+ + +
+ + + 0% + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/baidusitemap.xml b/baidusitemap.xml index 48d818a..ce16d97 100644 --- a/baidusitemap.xml +++ b/baidusitemap.xml @@ -4,7 +4,7 @@ - https://xudeveloper.github.io/2018/08/08/%E6%98%93%E7%94%A8%E7%89%88Popupwindow%20by%20Kotlin%E4%BA%86%E8%A7%A3%E4%B8%80%E4%B8%8B/ + https://xudeveloper.github.io/archives/undefined.html 2018-08-08T15:59:59.424Z @@ -24,7 +24,7 @@ - https://xudeveloper.github.io/2018/06/18/Android%20AccessibilityService%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ + https://xudeveloper.github.io/archives/undefined.html 2018-07-15T03:54:11.016Z @@ -44,7 +44,7 @@ - https://xudeveloper.github.io/2018/01/20/EventBus%203.1.1%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ + https://xudeveloper.github.io/archives/undefined.html 2018-02-05T13:40:32.947Z @@ -65,7 +65,7 @@ - https://xudeveloper.github.io/2017/12/17/Butterknife%208.8.1%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/ + https://xudeveloper.github.io/archives/undefined.html 2017-12-17T10:07:18.029Z @@ -85,7 +85,7 @@ - https://xudeveloper.github.io/2017/12/05/Android%207.0%20startActivity()%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BB%A5%E5%8F%8A%E5%AF%B9%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%E7%9A%84%E6%80%9D%E8%80%83/ + https://xudeveloper.github.io/archives/undefined.html 2017-12-06T15:55:15.207Z @@ -105,7 +105,7 @@ - https://xudeveloper.github.io/2017/11/04/Android%20adb%E5%91%BD%E4%BB%A4%E7%9A%84%E4%B8%80%E4%BA%9B%E5%AE%9E%E9%99%85%E8%BF%90%E7%94%A8/ + https://xudeveloper.github.io/archives/undefined.html 2017-11-07T13:31:11.768Z @@ -127,7 +127,7 @@ - https://xudeveloper.github.io/2017/10/21/%E5%88%86%E4%BA%AB%E4%B8%80%E4%B8%8B%E8%87%AA%E5%B7%B1%E5%81%9A%E7%9A%84%E4%B8%80%E4%B8%AA%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E5%BA%93XImageLoader/ + https://xudeveloper.github.io/archives/undefined.html 2017-11-05T03:14:18.385Z @@ -147,7 +147,7 @@ - https://xudeveloper.github.io/2017/10/21/Android%20Handler%E6%9C%BA%E5%88%B6%E7%90%86%E8%A7%A3/ + https://xudeveloper.github.io/archives/undefined.html 2017-11-05T03:14:17.777Z @@ -167,7 +167,7 @@ - https://xudeveloper.github.io/2017/10/21/Android%20View%E5%8A%A8%E7%94%BB%E5%92%8C%E5%B1%9E%E6%80%A7%E5%8A%A8%E7%94%BB%E7%AE%80%E5%8D%95%E8%A7%A3%E6%9E%90/ + https://xudeveloper.github.io/archives/undefined.html 2017-11-05T03:14:17.122Z diff --git a/categories/Android/index.html b/categories/Android/index.html index 5b1d7d4..36b29b6 100644 --- a/categories/Android/index.html +++ b/categories/Android/index.html @@ -303,7 +303,7 @@

Android分类

-

+