kerkee 之Web和iOS開發使用篇

簡單認識:kerkee 是一個多主體共存型 Hybrid 框架,具有跨平臺、用戶體驗好、性能高、擴展性好、靈活性強、易維護、規范化、集成云服務、具有Debug環境、徹底解決跨域問題。該框架從開發者角度支持三種團隊開發模式:Web開發者Native開發者Web開發者和Native團隊共同合作的開發團隊

下面我將從 Web開發者和Native(iOS)團隊共同合作的開發團隊 模式來分析使用該框架。

一、相關資料:

官網地址:http://www.kerkee.com

Github 源碼地址:https://github.com/kercer

QQ交流群:110710084

二、簡單的使用:

[kerkee在iOS上的快速上手指南](http://blog.linzihong.com/kerkeezai-iosshang-de-kuai-su-sha ng-shou-zhi-nan/)

1、建立新項目 kerkeeHDDemo 在項目目錄中建立 Podfile 文件:

platform :ios, '7.0'

inhibit_all_warnings!
pod ‘kerkee’, ’~> 1.0.1’  

2、使用 CocoaPods 來導入 kerkee 框架,使用終端 cd 到你的 Podfile 文件所在的目錄:

cd $PodfilePath
pod install

3、運行 Podfile 同目錄中的 kerkeeHDDemo.xcworkspace 即可;

4、在項目中添加 html 代碼:(本人對 html 不熟悉,相信懂的人肯定懂)

kerkeeTest.html :

<!--Create by Harry on 17/06/2016-->
<!--  Copyright ? 2016年 HarryDeng. All rights reserved.-->

<html>
    <head>
        <title>kerkee測試h5界面</title>
        <meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
            <script>
                function test()
                {
                    alert("test alert...");
                    return "abcd";
                }
                </script>
            </head>
    
    <body>
        <br/>
        <br/>
        
        <input type="button" value="js數據傳遞給oc" onclick="kerkeeJSManager.jsToOc('js數據傳遞給oc'); ">
            
        <br/>
            
        <input type="button" value="js數據傳遞給oc,oc回傳給js" onclick="kerkeeJSManager.mutualJSOC('js數據傳遞給oc,oc回傳給js', function(json)
                {
                console.log(JSON.stringify(json))
                }); ">
                
        <br/>
                
        <button id="hallo" onclick="buttonClick1('js數據傳遞給oc,h5的方法單獨寫出buttonClick1')">
            js數據傳遞給oc,h5的方法單獨寫出buttonClick1 </button>
        
        <br/>
        
        <button id="hallo" onclick="buttonClick2('js數據傳遞給oc,h5的方法單獨寫出buttonClick2')">
            js數據傳遞給oc,h5的方法在js文件實現 buttonClick2 </button>
        
        <br/>
                
    </body>

            <script>
            var kerkeeJSManager;
            function buttonClick1(s1)
            {
                kerkeeJSManager.jsToOc(s1);
            }
              
            function ocToJs(s1)
            {
                console.log(JSON.stringify(s1))
            }
            </script>
            
            <!--html頁面綁定js事件,一般放在href之后,如果放在之前加載JS事件會阻塞界面-->
            <script type="text/javascript" src="kerkeeTest.js"></script>
            
</html>

上面有三種方式來處理按鈕的點擊事件:

i. 直接在<button> </button> 中處理

ii. 使用 function 處理;

iii. 使用 js 來處理 function。

代碼解釋:

代碼 理解分析
kerkeeJSManager js 這邊中 iOSjs 交互橋梁,在 iOS 也同樣存在 kerkeeJSManager 關鍵字
jsToOc() kerkeeJSManager 方法 ,使用 kerkeeJSManager.jsToOc() 可以調用 iOS 對應的 - (void)jsToOc:(KCWebView)aWebView argList:(KCArgList)args 方法,實現 js 調用 iOS 原生代碼**
ocToJs() iOS 中使用 [KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView]; 可以調用 jsocToJs() 方法,實現了 iOS 調用 js 代碼

kerkeeTest.js :

var kerkeeJSManager;
function buttonClick2(s1)
{
    kerkeeJSManager.jsToOc(s1);
}

5、在項目中添加 iOS 代碼:

HDJSToOCManager.h

#import <Foundation/Foundation.h>
#import "KCJSObject.h"
#import "KCArgList.h"

// 這個類作為 和 js 交互橋梁類
@interface HDJSToOCManager : KCJSObject

- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args;

- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args;

@end

HDJSToOCManager.m

#import "HDJSToOCManager.h"

#import <UIKit/UIKit.h>
#import "KCJSBridge.h"

@implementation HDJSToOCManager

- (NSString*)getJSObjectName{
    // 這個 和 js 中的變量要保持一致
    return @"kerkeeJSManager";
}
// js 中 可以調用 jsToOc() 來調用
- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args{
    NSLog(@"JS調用OC args : %@", args);
}

- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args{
    NSLog(@"JS調用OC,OC回調JS args : %@", args);
    
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:@"success" forKey:@"info"];
    NSString *json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:dic options:0 error:nil] encoding:NSUTF8StringEncoding];
    KCAutorelease(json);
    //回調,callbackId,kerkee.js 內部已經處理好
    [KCJSBridge callbackJS:aWebView callBackID:[args getObject:@"callbackId"] jsonString:json];
}

@end

這個類主要是生成和 js 對應的變量 kerkeeJSManager ,是 iOS 這邊 iOSjs 交互橋梁

ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    
     // 將 HDJSToOCManager對象 和 js中的 kerkeeJSManager (詳見HDJSToOCManager)綁定
    [KCJSBridge registObject:[[HDJSToOCManager alloc] init]];
    
    NSString *file = [[NSBundle mainBundle] pathForResource:@"kerkeeTest" ofType:@"html"];
    NSURL *url = [NSURL URLWithString:file];
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];
    m_webView = [[KCWebView alloc] initWithFrame:self.view.bounds];
    [m_webView loadRequest:request];
    [self.view addSubview:m_webView];
    
    // 在m_webView 加載 “kerkee.js” 代碼,具體代碼見 KCApiBridge.m 中的 init 方法 (這個時候需要在項目中添加“kerkee.js” 文件)
    m_jsBridge = [[KCJSBridge alloc] initWithWebView:m_webView delegate:self];
    
    UIButton *bb = [UIButton buttonWithType:UIButtonTypeInfoLight];
    bb.frame = CGRectMake(100, 400, 50, 50);
    [bb addTarget:self action:@selector(ocToJs) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:bb];
}

- (void)ocToJs{
    [KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView];
}

#pragma mark --
#pragma mark KCWebViewProgressDelegate
-(void)webView:(KCWebView*)webView identifierForInitialRequest:(NSURLRequest*)initialRequest{
    
}

#pragma mark - UIWebView Delegate
- (void)webViewDidFinishLoad:(UIWebView *)aWebView{
    NSString *scrollHeight = [aWebView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
    NSLog(@"scrollHeight: %@", scrollHeight);
    NSLog(@"webview.contentSize.height %f", aWebView.scrollView.contentSize.height);
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
    
}

- (BOOL)webView:(UIWebView *)aWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    return YES;
}

上面的代碼并不多,其核心代碼之一在 KCApiBridge 中,可以看到, KCApiBridge 在初始化的時候找到 kerkee.js 并且使用 - (nullable NSString )stringByEvaluatingJavaScriptFromString:(NSString )script;iOSUIWebView 注入 kerkee.js 代碼。這份 js 才是真正的**將 iOS 的橋梁 HDJSToOCManager 實體和 js變量 kerkeeJSManager 的關鍵代碼。在 這里 可以看到作者在demo中的 kerkee.js 源碼。因為我對 js 也是入門階段,所以選擇在源碼上改動之后,代碼如下:

;(function(window){
    if (window.WebViewJSBridge)
        return;
    window.WebViewJSBridge = {
  
  };

    console.log("--- kerkee init begin---");
    var browser={
        versions:function(){
            var u = navigator.userAgent, app = navigator.appVersion;
            return {
                trident: u.indexOf('Trident') > -1, //IE
                presto: u.indexOf('Presto') > -1, //opera
                webKit: u.indexOf('AppleWebKit') > -1, //apple&google kernel
                gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//firfox
                mobile: !!u.match(/AppleWebKit.*Mobile.*/), //is Mobile
                ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //is ios
                android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android
                iPhone: u.indexOf('iPhone') > -1 , //iPhone or QQHD
                iPad: u.indexOf('iPad') > -1, //iPad
                iPod: u.indexOf('iPod') > -1, //iPod
                webApp: u.indexOf('Safari') == -1, //is webapp,no header and footer
                weixin: u.indexOf('MicroMessenger') > -1, //is wechat
                qq: u.match(/\sQQ/i) == " qq" //is qq
            };
        }(),
        language:(navigator.browserLanguage || navigator.language).toLowerCase()
    }

    var global = this || window;
                               
    var ApiBridge ={
        msgQueue : [],
        callbackCache : [],
        callbackId : 0,
        processingMsg : false,
        isReady : false,
        isNotifyReady : false
    };

    ApiBridge.create = function()
    {                  
        ApiBridge.bridgeIframe = document.createElement("iframe");
        ApiBridge.bridgeIframe.style.display = 'none';
        document.documentElement.appendChild(ApiBridge.bridgeIframe);
    };

    ApiBridge.prepareProcessingMessages = function()
    {
        ApiBridge.processingMsg = true;
    };

    ApiBridge.fetchMessages = function()
    {
        if (ApiBridge.msgQueue.length > 0)
        {
            var messages = JSON.stringify(ApiBridge.msgQueue);
            ApiBridge.msgQueue.length = 0;
            return messages;
        }
        ApiBridge.processingMsg = false;
        return null;
    };

    ApiBridge.callNative = function(clz, method, args, callback)
    {
        var msgJson = {};
        msgJson.clz = clz;
        msgJson.method = method;
        if (args != undefined)
            msgJson.args = args;

        if (callback)
        {
            var callbackId = ApiBridge.getCallbackId();
            ApiBridge.callbackCache[callbackId] = callback;
            if (msgJson.args)
            {
                msgJson.args.callbackId = callbackId.toString();
            }
            else
            {
                msgJson.args =
                {
                    "callbackId" : callbackId.toString()
                };
            }
        }

        if (browser.versions.ios)
        {
            if (ApiBridge.bridgeIframe == undefined)
            {
                ApiBridge.create();
            }
            // var msgJson = {"clz": clz, "method": method, "args": args};
            ApiBridge.msgQueue.push(msgJson);
            if (!ApiBridge.processingMsg)
                ApiBridge.bridgeIframe.src = "kcnative://go";
        }
        else if (browser.versions.android)
        {
            // android
            return prompt(JSON.stringify(msgJson));
        }

    };

    ApiBridge.log = function(msg)
    {
        ApiBridge.callNative("ApiBridge", "JSLog",
        {
            "msg" : msg
        });
    }

    ApiBridge.getCallbackId = function()
    {
        return ApiBridge.callbackId++;
    }

    ApiBridge.onCallback = function(callbackId, obj)
    {
        if (ApiBridge.callbackCache[callbackId])
        {
            ApiBridge.callbackCache[callbackId](obj);
            // ApiBridge.callbackCache[callbackId] = undefined;
            // //如果是注冊事件的話,不能undefined;
        }
    }

    ApiBridge.onBridgeInitComplete = function(callback)
    {
        ApiBridge.callNative("ApiBridge", "onBridgeInitComplete", {}, callback);
    }

    ApiBridge.onNativeInitComplete = function(callback)
    {
        ApiBridge.isReady = true;
        console.log("--- kerkee onNativeInitComplete end ---");

        if (callback)
        {
            callback();
            ApiBridge.isNotifyReady = true;
            console.log("--- device ready go--- ");
        }
    }

    ApiBridge.compile = function(aIdentity, aJS)
    {
        var value;
        var error;
        try
        {
            value = eval(aJS);
        }
        catch (e)
        {
            var err = {};
            err.name = e.name;
            err.message = e.message;
            err.number = e.number & 0xFFFF;
            error = err;
        }

        ApiBridge.callNative("ApiBridge", "compile",
        {
            "identity" : aIdentity,
            "returnValue" : value,
            "error" : error
        });
    }

    var _Configs =
    {
        isOpenJSLog:false,
        sysLog:{},
        isOpenNativeXHR:true,
        sysXHR:{}
    };
    _Configs.sysLog = global.console.log;
    _Configs.sysXHR = global.XMLHttpRequest;


    var kerkee = {};

    
    /*****************************************
     * JS和iOS 交互接口
     *****************************************/
    kerkee.jsToOc = function(s1)
       {
       ApiBridge.callNative("kerkeeJSManager", "jsToOc",
            {
                "s1" : s1
            });
       };
       
       kerkee.mutualJSOC = function(aString, callback)
       {
       ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
            {
                "aString" : aString
            }, callback);
       }
       
    global.kerkeeJSManager = kerkee;
                               

    kerkee.openJSLog = function()
    {
        _Configs.isOpenJSLog = true;
        global.console.log = ApiBridge.log;
    }
    kerkee.closeJSLog = function()
    {
        _Configs.isOpenJSLog = false;
        global.console.log = _Configs.sysLog;
    }

    kerkee.onDeviceReady = function(handler)
    {
        ApiBridge.onDeviceReady = handler;

        if (ApiBridge.isReady && !ApiBridge.isNotifyReady && handler)
        {
            console.log("-- device ready --");
            handler();
            ApiBridge.isNotifyReady = true;
        }
    };

    kerkee.invoke = function(clz, method, args, callback)
    {
        if (callback)
        {
            ApiBridge.callNative(clz, method, args, callback);
        }
        else
        {
            ApiBridge.callNative(clz, method, args);
        }
    };

    kerkee.onSetImage = function(srcSuffix, desUri)
    {
        // console.log("--- kerkee onSetImage ---");
        var obj = document.querySelectorAll('img[src$="' + srcSuffix + '"]');
        for (var i = 0; i < obj.length; ++i)
        {
            obj[i].src = desUri;
        }
    };


    /*****************************************
     * XMLHttpRequest實現
     *****************************************/
    var _XMLHttpRequest = function()
    {
        this.id = _XMLHttpRequest.globalId++;
        _XMLHttpRequest.cache[this.id] = this;

        this.status = 0;
        this.statusText = '';
        this.readyState = 0;
        this.responseText = '';
        this.headers = {};
        this.onreadystatechange = undefined;

        ApiBridge.callNative('XMLHttpRequest', 'create',
        {
            "id" : this.id
        });
    }

    _XMLHttpRequest.globalId = 0;
    _XMLHttpRequest.cache = [];
    _XMLHttpRequest.setProperties = function(jsonObj)
    {
        var id = jsonObj.id;
        if (_XMLHttpRequest.cache[id])
        {
            var obj = _XMLHttpRequest.cache[id];

            if (jsonObj.hasOwnProperty('status'))
            {
                obj.status = jsonObj.status;
            }
            if (jsonObj.hasOwnProperty('statusText'))
            {
                obj.statusText = jsonObj.statusText;
            }
            if (jsonObj.hasOwnProperty('readyState'))
            {
                obj.readyState = jsonObj.readyState;
            }
            if (jsonObj.hasOwnProperty('responseText'))
            {
                obj.responseText = jsonObj.responseText;
            }
            if (jsonObj.hasOwnProperty('headers'))
            {
                obj.headers = jsonObj.headers;
            }

            if (_XMLHttpRequest.cache[id].onreadystatechange)
            {
                _XMLHttpRequest.cache[id].onreadystatechange();
            }
        }
    }

    _XMLHttpRequest.prototype.open = function(method, url, async)
    {
        ApiBridge.callNative('XMLHttpRequest', 'open',
        {
            "id" : this.id,
            "method" : method,
            "url" : url,
            "scheme" : window.location.protocol,
            "host" : window.location.hostname,
            "port" : window.location.port,
            "href" : window.location.href,
            "referer" : document.referrer != "" ? document.referrer : undefined,
            "useragent" : navigator.userAgent,
            "cookie" : document.cookie != "" ? document.cookie : undefined,
            "async" : async,
            "timeout" : this.timeout
        });
    }

    _XMLHttpRequest.prototype.send = function(data)
    {
        if (data != null)
        {
            ApiBridge.callNative('XMLHttpRequest', 'send',
            {
                "id" : this.id,
                "data" : data
            });
        }
        else
        {
            ApiBridge.callNative('XMLHttpRequest', 'send',
            {
                "id" : this.id
            });
        }
    }
    _XMLHttpRequest.prototype.overrideMimeType = function(mimetype)
    {
        ApiBridge.callNative('XMLHttpRequest', 'overrideMimeType',
        {
            "id" : this.id,
            "mimetype" : mimetype
        });
    }
    _XMLHttpRequest.prototype.abort = function()
    {
        ApiBridge.callNative('XMLHttpRequest', 'abort',
        {
            "id" : this.id
        });
    }
    _XMLHttpRequest.prototype.setRequestHeader = function(headerName,
            headerValue)
    {
        ApiBridge.callNative('XMLHttpRequest', 'setRequestHeader',
        {
            "id" : this.id,
            "headerName" : headerName,
            "headerValue" : headerValue
        });
    }
    _XMLHttpRequest.prototype.getAllResponseHeaders = function()
    {
        var strHeaders = '';
        for ( var name in this.headers)
        {
            strHeaders += (name + ": " + this.headers[name] + "\r\n");
        }
        return strHeaders;
    }
    _XMLHttpRequest.prototype.getResponseHeader = function(headerName)
    {
        var strHeaders;
        var upperCaseHeaderName = headerName.toUpperCase();
        for ( var name in this.headers)
        {
            if (upperCaseHeaderName == name.toUpperCase())
                strHeaders = this.headers[name]
        }
        return strHeaders;
    }
    _XMLHttpRequest.deleteObject = function(id)
    {
        if (_XMLHttpRequest.cache[id])
        {
            _XMLHttpRequest.cache[id] = undefined;
        }
    }

    global.ApiBridge = ApiBridge;
    global.kerkee = kerkee;
    global.XMLHttpRequest = _XMLHttpRequest;

    kerkee.register = function(_window)
    {
        _window.ApiBridge = window.ApiBridge;
        _window.kerkee = window.kerkee;
        _window.console.log = window.console.log;
        _window.XMLHttpRequest = window.XMLHttpRequest;
        _window.open = window.open;
    };


    ApiBridge.onBridgeInitComplete(function(aConfigs)
    {
        if (aConfigs)
        {
            if (aConfigs.hasOwnProperty('isOpenJSLog'))
            {
                _Configs.isOpenJSLog = aConfigs.isOpenJSLog;
            }
            if (aConfigs.hasOwnProperty('isOpenNativeXHR'))
            {
                _Configs.isOpenNativeXHR = aConfigs.isOpenNativeXHR;
            }
        }

        if (_Configs.isOpenJSLog)
        {
            global.console.log = ApiBridge.log;
        }
        ApiBridge.onNativeInitComplete(ApiBridge.onDeviceReady);

    });

})(window);

如果單純的只是 jsiOS 的交互,只需要關注 下面 的代碼即可:(當然你也可以自己設置多個 kerkeeJSManager 橋梁及 更多的 交互方法)

/*****************************************
 * JS和iOS 交互接口
 *****************************************/
kerkee.jsToOc = function(s1)
   {
   ApiBridge.callNative("kerkeeJSManager", "jsToOc",
        {
            "s1" : s1
        });
   };
   
   kerkee.mutualJSOC = function(aString, callback)
   {
   ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
        {
            "aString" : aString
        }, callback);
   }
   
global.kerkeeJSManager = kerkee;

6、編譯運行:

Xcode 編譯運行效果

7、demo下載地址:GitHub地址

三、總結:

這里只是簡單的介紹了 iOS 使用 kerkee 框架來加載 html 實現 jsiOS 交互 ,如果只是單純的為了簡單的交互,可以看我的另外一篇博客 :JS和OC相互調用 ,這里介紹了幾個更加輕量級的框架實現 jsiOS 交互。但是 jsiOS 交互 功能在 kerkee 框架的一小部分,更多高性能、支持跨平臺、擴展性好、易維護等等優秀的特性,我會慢慢閱讀源碼來說明。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容