當(dāng)下主流 APP , Hybrid app 中需要熟練掌握的交互知識(一)

2014年10月29日鳍贾,萬維網(wǎng)聯(lián)盟宣布,經(jīng)過接近8年的艱苦努力般贼,該標(biāo)準(zhǔn)(HTML5)規(guī)范終于制定完成愧哟。接著,整個(gè)移動(dòng)端開發(fā)領(lǐng)域開始了一場翻天覆地的改革變遷哼蛆,主要「受益」者蕊梧, ios , Android.

現(xiàn)如今的形勢無疑使這個(gè)行業(yè)的門檻抬的非常高,我們都知道以前會(huì)寫幾個(gè) demo 工作就沒問題了腮介,這是移動(dòng)開發(fā)正火熱的時(shí)期肥矢。之后接著有很多同學(xué)的自學(xué)效率不錯(cuò),憑著一己之力叠洗,培訓(xùn)也算甘改,每年大量新人涌入這個(gè)高薪行業(yè)!

前兩天吃飯的時(shí)候 HR 告訴我說灭抑,他說兩年前招 Android 的時(shí)候給出的價(jià)格是 15k十艾,而且還招不到,如今 10 都不愿意給了腾节,發(fā)一個(gè)職位來的人排著隊(duì)面試 忘嫉!可見移動(dòng)行業(yè)的現(xiàn)狀的確是跟之前的差距拉的太大。

包括我剛出來的時(shí)候基本沒有接觸過 WebApp 領(lǐng)域的東西案腺,更不會(huì)去嫻熟的利用 WebView 做著各種交互庆冕,以至于找工作就費(fèi)了很大的力氣,如今劈榨,Hybrid App 已然是絕對的主流地位访递!

什么是 Hybrid App

Native App(原生界面)+ WebApp (基于 H5 的 WebView 網(wǎng)頁)== Hybrid App(混合型)

那這樣一個(gè)公式就說的很清楚了,所謂的 Hybrid 就是原生與網(wǎng)頁混合開發(fā)同辣,有時(shí)候原生界面調(diào) H5 頁面拷姿,有時(shí) H5 網(wǎng)頁又來調(diào)原生惭载,為什么會(huì)衍生出這種開發(fā)模式呢?H5 可跨平臺(tái)跌前,一套頁面 ios 和 android 公用棕兼,開發(fā)成本較低陡舅,再者網(wǎng)頁不占內(nèi)存等等抵乓,一張圖來稍微概括:



關(guān)于三者之間的對比這只是片面的,更多詳細(xì)的資料大家自行搜索查看靶衍!雖然前兩者的體驗(yàn)與性能都不會(huì)優(yōu)于后者灾炭,但是大勢所趨,我們誰也改變不了颅眶!

  1. 使用 WebView 加載網(wǎng)頁
    這里沒什么好說的蜈出,直接上代碼:
<!-- 頁面布局 -->
<WebView   
android:id="@+id/web"   
android:layout_width="match_parent  
 android:layout_height="match_parent" />

***第一部分 :

//加載顯示網(wǎng)頁
mWebView = (WebView) mMainContainer.findViewById(R.id.wv_container);
?// 設(shè)置加載網(wǎng)頁的地址
mWebView.loadUrl("http://www.baidu.com");?WebSettings settings = mWebView.getSettings();?
// 啟用 JavaScript 支持(WebView的設(shè)置)
settings.setJavaScriptEnabled(true);?
 new WebViewClient解決跳轉(zhuǎn)到第三方瀏覽器* 
這里面處理行對應(yīng)的頁面邏輯*/mWebView.setWebViewClient(new WebViewClient() {?
    //處理頁面邏輯的方法 詳細(xì)舉例在下一片段
    @Override 
     public boolean shouldOverrideUrlLoading(WebView view, String url) {  
       mWebView.loadUrl(url); 
       return true; 
    }? 
// 頁面開始加載
 @Override  public void onPageStarted(WebView view, String url, Bitmap favicon) {  
  super.onPageStarted(view, url, favicon);  
     if (!mDialog.isShowing())  
       mDialog.show(); //顯示加載進(jìn)度條  
   }  
  /** 
  * 頁面加載完成 
  * @param view  
  * @param url 
  */  @Override 
  public void onPageFinished(WebView view, String url) {  
     super.onPageFinished(view, url);  
       if (mDialog.isShowing())  
         mDialog.dismiss(); //加載完成 隱藏進(jìn)度條     
  }
 });

我們首先利用 WebView 加載了這個(gè)網(wǎng)頁,其次給他設(shè)置涛酗,解決瀏覽器跳轉(zhuǎn)铡原,顯示加載進(jìn)度條等等!

相信大家都很明白這還只是入門 Demo 的寫法商叹,那么在項(xiàng)目中我們是怎么處理這些設(shè)置和邏輯呢燕刻?
第一 :實(shí)際項(xiàng)目中的 WebView 存在與多個(gè)地方
第二 :原生與 Web 混合型
第三 : 網(wǎng)頁大多數(shù)有他后臺(tái)自己加好的邏輯與功能(包括 js 方法),怎么配合剖笙!
第四:你必須要做的 Cookie 處理

那么我們將以這幾個(gè)問題開始進(jìn)行分享卵洗,所以接下來會(huì)開是一個(gè) HyBrid 主題系列的文章,估計(jì)會(huì)有好幾篇弥咪!

  1. 建立 Activity 管理類

這一步是項(xiàng)目開發(fā)必備过蹂,我們面對這越來越多的頁面的時(shí)候,如果不管理聚至,你總有無從下手的時(shí)候酷勺!

/**
 * Activity 管理類
 * Created by ShiYunpeng on 2016/12/28. 
*/
  public class ActivityCollector {?  
  private static List<Activity> activityList = new ArrayList<>();
   //用來管理所有的activity? 
  /**    
 * 添加當(dāng)前的Activity到棧中    
 * @param activity  
   */  
   public static void addActivity(Activity activity){
         activityList.add(activity); 
    }?  
 /**   
  * 將當(dāng)前Activity移出返回棧    
 * @param activity    
 */   
  public static void removeActivity(Activity activity){   
      activityList.remove(activity);  
   }?  
 /**  
   * 獲取到棧頂?shù)腁ctivity 
    * @return   
  */  
  public static Activity getTopActivity(){ 
      if (activityList.isEmpty()){    
       return null;   
      }else {    
       return activityList.get(activityList.size() - 1);    
      } 
  }
}

那么這三個(gè)方法解決了我們很多問題,包括由于資源未及時(shí)釋放以及讓你措手不及的 ANR扳躬,可別小看他脆诉!

以上寫法的應(yīng)用:在 Activity 中我們需要在項(xiàng)目初始化的時(shí),用我們建好的這個(gè)類 ActivityClooector.addActivity() 來添加我們的 Activity 到棧中坦报,同樣的方法库说,在 Activity 將要銷毀的時(shí)候去選擇 remove 他,后面我們會(huì)在一些重要的類中(非 Activity)去用 getToopActivity() 來獲取到我們的棧頂 Activity片择,非常重要潜的!

  1. 建立 WebViewController 管理類
    那么這一步更加重要了,還記得我們第一小節(jié)是怎么設(shè)置 WebView 的嗎字管?我們是在加載他的 Activity 中來進(jìn)行設(shè)置的啰挪,那么當(dāng)有多個(gè)頁面時(shí)信不,又當(dāng)每個(gè)頁面加載 WebView 所要的設(shè)置數(shù)量不同的時(shí)候,就比較難處理了亡呵!
    所以這里只用一個(gè)類來將項(xiàng)目中所有關(guān)于 WebView 的設(shè)置抽活,以及 WebView 單層面的處理都放在這里!

先行列出 WebView 中常用設(shè)置以及方法
WebSettings

//下面三個(gè)最常用锰什,基本都需要設(shè)置 
setCacheMode 設(shè)置緩存的模式 eg: settings.setCacheMode(WebSettings.LOAD_NO_CACHE); 
setJavaSciptEnabled 設(shè)置是否支持Javascript eg: settings.setJavaScriptEnabled(true); 
setDefaultTextEncodingName 設(shè)置在解碼時(shí)使用的默認(rèn)編碼 eg: settings.setDefaultTextEncodingName(“utf-8”); 
setAllowFileAccess 啟用或禁止WebView訪問文件數(shù)據(jù) setBlockNetworkImage 是否顯示網(wǎng)絡(luò)圖像 setBuiltInZoomControls 設(shè)置是否支持縮放 
setDefaultFontSize 設(shè)置默認(rèn)的字體大小 
setFixedFontFamily 設(shè)置固定使用的字體
 setLayoutAlgorithm 設(shè)置布局方式
 setLightTouchEnabled 設(shè)置用鼠標(biāo)激活被選項(xiàng) 
setSupportZoom 設(shè)置是否支持變焦

WebViewClient

mWebView.setWebViewClient(new WebViewClient(){
這里包含以下方法
});
方法:

onPageStarted 網(wǎng)頁開始加載 
onReceivedError 報(bào)告錯(cuò)誤信息
 onLoadResource 加載指定地址提供的資源
 shouldOverrideUrlLoading 控制新的連接在當(dāng)前WebView中打開 
onPageFinished 網(wǎng)頁加載完畢下硕,此方法并沒有方法名表現(xiàn)的那么美好,調(diào)用時(shí)機(jī)很不確定汁胆。如需監(jiān)聽網(wǎng)頁加載完成可以使用onProgressChanged梭姓,當(dāng)int progress返回100時(shí)表示網(wǎng)頁加載完畢。
 doUpdate VisitedHistory 更新歷史記錄 
onFormResubmission 應(yīng)用程序重新請求網(wǎng)頁數(shù)據(jù)
 onScaleChanged WebView發(fā)生改變

**WebChromeClient** mWebView.setWebViewClient(new WebChromeClient(){
? 同樣例舉出常用方法
}); 
方法:
 onProgressChanged 加載進(jìn)度條改變
 onJsPrompt 用在解決4.2以下addJavascriptInterface漏洞問題
 onCloseWindow 關(guān)閉WebView 
 onCreateWindow 創(chuàng)建WebView 
 onJsAlert 處理Javascript中的Alert對話框
 onJsConfirm處理Javascript中的Confirm對話框 
 onJsPrompt處理Javascript中的Prompt對話框
 onReceivedlcon 網(wǎng)頁圖標(biāo)更改 
 onReceivedTitle 網(wǎng)頁Title更改
 onRequestFocus WebView顯示焦點(diǎn) 
 onConsoleMessage 在Logcat中輸出javascript的日志信息

下來列舉我對于此模塊的處理方法
我在這里建立了一個(gè) WebViewCtroller類嫩码,用于將所有能用到設(shè)置以及需要添加的功能都寫在里面誉尖,據(jù)我所知,只是現(xiàn)在普遍流行的一種寫法铸题,我們開始看铡恕,注釋很詳細(xì)!

//項(xiàng)目所在包路徑丢间,待會(huì)要用
package com.lansum.eip.webview;
public class WebViewController extends WebView {

    //聲明上下文對象
    private Context context;

    //聲明WebViewController全局對象
    private WebViewController control;

    //網(wǎng)絡(luò)未加載完的loading圖片
    private ImageView imageView;

    //截取到的網(wǎng)址上的消息頭字符串 表示要進(jìn)入到那個(gè)頁面
    private String sbFunName = "";


    public WebViewController(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        webviewSettings();
    }

    public WebViewController(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        webviewSettings();
    }

    public WebViewController(Context context) {
        super(context);
        webviewSettings();
    }

    @SuppressLint("JavascriptInterface")
    private void webviewSettings() {

        control = this;

        // TODO Auto-generated constructor stub
        WebSettings webSettings = this.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setSupportZoom(false);
        webSettings.setBuiltInZoomControls(false);
        webSettings.setAllowFileAccess(true);
        webSettings.setAllowContentAccess(true);
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setGeolocationEnabled(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setAppCachePath(context.getCacheDir().getPath());
        webSettings.setDefaultTextEncodingName("gbk");
        // 屏幕自適應(yīng)
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        } else {
            webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            webSettings.setDisplayZoomControls(false);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            webSettings.setLoadsImagesAutomatically(true);
        } else {
            webSettings.setLoadsImagesAutomatically(false);
        }
        /**禁止屏幕自動(dòng)旋轉(zhuǎn)*/
        this.setScrollBarStyle(this.SCROLLBARS_INSIDE_OVERLAY);
        this.setHorizontalScrollBarEnabled(false);
        this.setHorizontalFadingEdgeEnabled(false);
        this.setVerticalFadingEdgeEnabled(false);


        HtmlMessageForLocal newWebViewActivity = new HtmlMessageForLocal();
        this.addJavascriptInterface(newWebViewActivity, "android");

        /**
         * 讓網(wǎng)頁的彈框轉(zhuǎn)化為原生化的彈框
         */
        setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {

                AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle("錯(cuò)誤提醒")
                        .setMessage(message)
                        .setIcon(R.drawable.icon_login)
                        .setPositiveButton("好", new AlertDialog.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.confirm();
                            }
                        });
                builder.setCancelable(false);
                builder.create();
                builder.show();
                return true;
            }

        });
        setWebViewClient(new WebViewClient() {
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 這些頁面添加頂部導(dǎo)航
                if (url != null && !url.matches(".*"+ Constants.urlLogIn+".*")) {
                    Intent intent = new Intent(context, NewWebViewActivity.class);
                    intent.putExtra("url", url);
                    intent.putExtra("animation",R.anim.slide_right_out);
                    context.startActivity(intent);
                    //((Activity)context).overridePendingTransition(R.anim.slide_right_in, R.anim.none);
                    return true;
                } else {
                    return false;
                }

            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

            }

            /**
             * WebView開始加載
             * 1.通過截取網(wǎng)頁 url 中指定的參數(shù)確定這個(gè)頁面
             * 2.根據(jù)匹配結(jié)果而確定所要操作的是哪個(gè)頁面
             * 3.注冊廣播接收器探熔,接受消息處理類中發(fā)過來的廣播去刷新數(shù)據(jù)
             * @param view
             * @param url
             * @param favicon
             */
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                // TODO Auto-generated method stub
                super.onPageStarted(view, url, favicon);
                control.setTag("");

                int hhh = url.indexOf("WebViewRefreshNotification=");
                if (hhh != -1){
                    int xxx = hhh + 27;
                    sbFunName = url.substring(xxx);
                    int ttt = sbFunName.indexOf('&');
                    if (ttt!= -1){
                        sbFunName = sbFunName.substring(0,ttt);
                    }
                }

                /**
                 * 當(dāng)最終截取到的字符串不是空的時(shí)候
                 * 再去注冊廣播來接受發(fā)過來的廣播消息
                 */
                if (!sbFunName.equals("")){
                    IntentFilter intentFilter = new IntentFilter();
                    intentFilter.addAction(sbFunName);
                    context.registerReceiver(new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            if (intent.getAction().equals(sbFunName)) {  //接受廣播
                                String name = intent.getStringExtra("name");
                                WebViewController.this.loadUrl("javascript:" + name);
                            }
                        }
                    }, intentFilter);
                }

                /**
                 * 動(dòng)態(tài)創(chuàng)建網(wǎng)絡(luò)加載中的GIf圖片
                 */
                imageView = new ImageView(ActivityCollector.getTopActivity().getApplicationContext());
                ViewGroup.LayoutParams size = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 80);
                imageView.setLayoutParams(size);
                Glide.with(ActivityCollector.getTopActivity()).load(R.drawable.loading3).into(imageView);
                ViewGroup parentLayout = (ViewGroup) WebViewController.this.getParent();
                parentLayout.addView(imageView);
                parentLayout.getPaddingRight();
                imageView.setVisibility(View.VISIBLE);
            }

            /**
             * WebView加載完成
             * @param view
             * @param url
             */
            @Override
            public void onPageFinished(WebView view, String url) {
                imageView.setVisibility(View.GONE);
            }
        });
    }

其實(shí)說起來代碼也不多,根據(jù)需求來添加即可千劈。注意祭刚,這個(gè)類世紀(jì)城于 WebView 的!

關(guān)于如何知道網(wǎng)頁服務(wù)端給我們傳過來的是什么參數(shù)墙牌,以便于我們將參數(shù)填到所需的地方涡驮,這里就以這個(gè)在 WebView 中彈出原生的提示框?yàn)槔?br> 如果不處理網(wǎng)頁的 js 彈框,他是這樣的:

那么這樣用戶體驗(yàn)就不好了喜滨!凡是網(wǎng)頁時(shí)有 js 彈框的地方捉捅,我們都可以用在 setWebChromeClient(new WebChromeClient() {} 中 重寫父類的 onJsAlert() 來講之轉(zhuǎn)換為原生彈框。

接下來我們打一個(gè)斷點(diǎn)來追蹤一下網(wǎng)頁需要什么參數(shù)讓我們傳虽风,關(guān)于網(wǎng)頁服務(wù)端的所有接口都是這樣來看參數(shù)的棒口,所以你看好了!

funName.png

可以看到辜膝,我在沒有填密碼的時(shí)候點(diǎn)擊了修改密碼无牵,它就接著執(zhí)行了這個(gè)方法,并且有一個(gè) message 參數(shù)顯示的是提示信息厂抖,那么我們直接就將這個(gè) message 設(shè)置到我們原生彈框中所需要的消息就 OK 了茎毁!記住,后面的接口方法都是這么判斷,再說一遍七蜘!

重點(diǎn)來了谭溉,將設(shè)置應(yīng)用到 WebView 中去。

<com.lansum.eip.webview.WebViewController
       android:id="@+id/main_webview"
       android:layout_width="match_parent" 
       android:layout_height="match_parent" />

還記得上面類第一行的包吧橡卤!沒錯(cuò)扮念,就是把這個(gè)類的包路徑直接添加到布局中,像添加自定義布局的方式一樣碧库,現(xiàn)在起我們的 WebView 就已經(jīng)具備了代碼中所有的設(shè)置柜与!

關(guān)于這個(gè)類(WebViewController),是我們專門用于處理 WebView 的類谈为,如何去搭配下一個(gè)重頭戲的類(HtmlMessageForLocal 類)旅挤!作用踢关,本地與WebView交互類伞鲫,根據(jù)點(diǎn)擊的WebView位置服務(wù)端發(fā)送相應(yīng)接口,本地去實(shí)現(xiàn)這些接口签舞!非常重要秕脓!

此次就到這里,我們下篇文章繼續(xù)儒搭,揭開這個(gè) HtmlMessageForLocal 類的真實(shí)面目吠架,并與我們的 WebViewController 類搭配起來完成所有網(wǎng)頁的處理,此次分享落幕搂鲫!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末傍药,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子魂仍,更是在濱河造成了極大的恐慌拐辽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擦酌,死亡現(xiàn)場離奇詭異俱诸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赊舶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門睁搭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笼平,你說我怎么就攤上這事园骆。” “怎么了寓调?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵锌唾,是天一觀的道長。 經(jīng)常有香客問我捶牢,道長鸠珠,這世上最難降的妖魔是什么巍耗? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮渐排,結(jié)果婚禮上炬太,老公的妹妹穿的比我還像新娘。我一直安慰自己驯耻,他們只是感情好亲族,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著可缚,像睡著了一般霎迫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帘靡,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天知给,我揣著相機(jī)與錄音,去河邊找鬼描姚。 笑死涩赢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的轩勘。 我是一名探鬼主播筒扒,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绊寻!你這毒婦竟也來了花墩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤澄步,失蹤者是張志新(化名)和其女友劉穎冰蘑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驮俗,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡懂缕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了王凑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搪柑。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖索烹,靈堂內(nèi)的尸體忽然破棺而出工碾,到底是詐尸還是另有隱情,我是刑警寧澤百姓,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布渊额,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旬迹。R本人自食惡果不足惜火惊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奔垦。 院中可真熱鬧屹耐,春花似錦、人聲如沸椿猎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽犯眠。三九已至按灶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐咧,已是汗流浹背鸯旁。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗜浮,地道東北人羡亩。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像危融,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子雷袋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容