Android網絡應用之在WebView中構建Web應用程序

assets 文件夾下的html訪問方式:"file:///android_asset/index.html"

如果要將Web應用程序(或只是網頁)作為客戶端應用程序的一部分提供,可以使用WebView進行操作。 WebView類是Android的View類的擴展,它允許您將網頁顯示為活動布局的一部分。 它不包括完全開發的Web瀏覽器的任何功能,例如導航控件或地址欄。 默認情況下,所有WebView都會顯示一個網頁。
使用WebView的常見情況是您希望在應用程序中提供可能需要更新的信息(例如最終用戶協議或用戶指南)。 在您的Android應用程序中,您可以創建一個包含WebView的活動,然后使用它來顯示在線托管的文檔。
WebView可以幫助的另一種情況是,如果您的應用程序向用戶提供總是需要Internet連接來檢索數據(如電子郵件)的數據。 在這種情況下,您可能會發現在您的Android應用程序中構建WebView更容易,該應用程序顯示具有所有用戶數據的網頁,而不是執行網絡請求,然后解析數據并將其呈現在Android布局中。 相反,您可以設計適合Android設備的網頁,然后在Android應用程序中實現加載網頁的WebView。
本文檔介紹了如何開始使用WebView,以及如何處理頁面導航,以及如何將Web頁面中的JavaScript綁定到Android應用程序中的客戶端代碼

將WebView添加到您的應用程序

要將WebView添加到應用程序中,只需將<WebView>元素添加到活動布局中即可。 例如,這里是WebView填充屏幕的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>

要在WebView中加載網頁,請使用loadUrl()。 例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.baidu.com");

然而,在此之前,您的應用程序必須能夠訪問Internet。 要獲取Internet訪問權限,請在清單文件中請求INTERNET權限。 例如:.

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

這就是顯示網頁的基本WebView所需要的。

在WebView中使用JavaScript

如果您計劃在WebView中加載的網頁使用JavaScript,則必須為WebView啟用JavaScript。 啟用JavaScript后,您還可以在應用程序代碼和JavaScript代碼之間創建接口。

啟用JavaScript

默認情況下,WebView中的JavaScript被禁用。 您可以通過附加到WebView的WebSettings啟用它。 您可以使用getSettings()檢索WebSettings,然后使用setJavaScriptEnabled()啟用JavaScript。

例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings可以訪問您可能會發現有用的各種其他設置。 例如,如果您正在開發專門針對Android應用程序中的WebView設計的Web應用程序,則可以使用setUserAgentString()定義自定義用戶代理字符串,然后在網頁中查詢自定義用戶代理,以驗證 請求您的網頁的客戶端實際上是您的Android應用程序。

將JavaScript代碼綁定到Android代碼

在開發專門針對Android應用程序中的WebView設計的Web應用程序時,您可以在JavaScript代碼和客戶端Android代碼之間創建界面。 例如,您的JavaScript代碼可以調用Android代碼中的方法來顯示對話框,而不是使用JavaScript的alert()函數。

要綁定JavaScript和Android代碼之間的新界面,請調用addJavascriptInterface(),傳遞一個類實例以綁定到您的JavaScript和JavaScript可以調用以訪問該類的接口名稱。

例如,您可以在Android應用程序中包含以下類:

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

注意:如果將targetSdkVersion設置為17或更高版本,則必須將@JavascriptInterface注釋添加到您希望JavaScript可用的任何方法(該方法也必須是公共的)。 如果您不提供注釋,則在Android 4.2或更高版本上運行時,您的網頁無法訪問該方法。
在此示例中,WebAppInterface類允許網頁使用showToast()方法創建Toast消息。

您可以將此類綁定到使用addJavascriptInterface()在WebView中運行的JavaScript,并將其命名為Android。 例如:

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "android");

這將為WebView中運行的JavaScript創建一個名為Android的界面。 此時,您的Web應用程序可以訪問WebAppInterface類。 例如,以下是一些HTML和JavaScript,當用戶單擊按鈕時,使用新界面創建吐司信息:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        android.showToast(toast);
    }
</script>

沒有必要從JavaScript初始化Android界面。 WebView自動使您的網頁可用。 所以,點擊按鈕,showAndroidToast()函數使用Android界面來調用WebAppInterface.showToast()方法。
注意:綁定到您的JavaScript的對象在另一個線程中運行,而不是在其構造的線程中運行。
注意:使用addJavascriptInterface()允許JavaScript控制您的Android應用程序。 這可能是非常有用的功能或危險的安全問題。 當WebView中的HTML不可信(例如,一部分或全部HTML由未知人員或進程提供)時,攻擊者可以包括執行客戶端代碼的HTML,以及攻擊者選擇的任何代碼。 因此,除非您寫入WebView中顯示的所有HTML和JavaScript,否則不應使用addJavascriptInterface()。 您也不應該允許用戶導航到您自己的WebView內的其他網頁(而不是允許用戶的默認瀏覽器應用程序打開外部鏈接) - 默認情況下,用戶的Web瀏覽器會打開所有URL鏈接,因此 只有當您按照以下部分的描述處理頁面導航時,請小心)

處理頁面導航

當用戶從WebView中的網頁單擊鏈接時,默認行為是啟動一個處理URL的應用程序。 通常,默認Web瀏覽器打開并加載目標URL。 但是,您可以覆蓋WebView的此行為,因此在WebView中打開鏈接。 然后,您可以允許用戶通過WebView維護的網頁歷史向后和向后瀏覽。
要打開用戶點擊的鏈接,只需使用setWebViewClient()為您的WebView提供一個WebViewClient。 例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient());

就這樣 現在 用戶所有點擊的鏈接都會在你的WebView

如果您想要更多地控制加載點擊的鏈接的位置,請創建自己的WebViewClient來覆蓋shouldOverrideUrlLoading()方法。 例如:

private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (Uri.parse(url).getHost().equals("www.example.com")) {
                // 這是我的網站,所以不要重寫; 讓我的WebView加載頁面
                return false;
            }
            // 否則,該鏈接不是我的網站上的頁面,因此啟動另一個處理URL的活動
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(intent);
            return true;
        }
    }

然后為WebView創建一個新的WebViewClient實例:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

現在當用戶單擊鏈接時,系統調用shouldOverrideUrlLoading(),它檢查URL主機是否與特定域匹配(如上定義)。 如果它匹配,那么該方法返回false,以便不覆蓋URL加載(它允許WebView像往常一樣加載URL)。 如果URL主機不匹配,則創建Intent以啟動用于處理URL的默認活動(解析為用戶的默認Web瀏覽器)。

瀏覽網頁歷史記錄

當您的WebView覆蓋URL加載時,它會自動累積訪問的網頁的歷史記錄。 您可以通過goBack()和goForward()向后和向前瀏覽歷史記錄。
例如,您的“活動”可以使用“設備返回”按鈕向后導航:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check if the key event was the Back button and if there's history
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it wasn't the Back key or there's no web page history, bubble up to the default
    // system behavior (probably exit the activity)
    return super.onKeyDown(keyCode, event);
}

如果用戶訪問實際的網頁歷史記錄,canGoBack()方法返回true。 同樣,您可以使用canGoForward()來檢查是否存在轉發歷史記錄。 如果您不執行此檢查,則一旦用戶到達歷史記錄的末尾,goBack()或goForward()不執行任何操作。

在Android4.4上的WebView

Android 4.4(API級別19)引入了基于Chromium的新版本的WebView。 此更改將針對HTML5,CSS3和JavaScript的WebView性能和標準支持進行升級,以配合最新的Web瀏覽器。 任何使用WebView的應用程序將在Android 4.4及更高版本上運行時繼承這些升級。
本文檔描述了如果將targetSdkVersion設置為“19”或更高版本,您應該注意的WebView的其他更改。
注意:如果您的targetSdkVersion設置為“18”或更低版本,則WebView將以“怪異”模式運行,以避免在盡可能接近的情況下,盡可能避免一些行為變化,同時仍然為應用程序提供性能和Web標準升級。 不過,請注意,Android 4.4中根本不支持單列和窄欄列布局和默認縮放級別,并且還可能存在尚未識別的其他行為差異,因此請確保在Android 4.4或更高版本上測試您的應用程序,即使 您將targetSdkVersion設置為“18”或更低。
為了幫助您解決在Android 4.4中將應用程序遷移到WebView時遇到的任何問題,您可以通過調用setWebContentsDebuggingEnabled()來啟用桌面上的Chrome的遠程調試。 WebView中的新功能允許您在WebView中運行時檢查和分析Web內容,腳本和網絡活動。 有關詳細信息,請參閱Android上的遠程調試。

用戶代理更改

如果您根據用戶代理向WebView提供內容,則應注意用戶代理字符串稍有更改,現在包括Chrome版本:
Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16H) AppleWebKit/537.36
(KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
如果您需要檢索用戶代理,但不需要為應用程序存儲或不想實例化WebView,則應使用靜態方法getDefaultUserAgent()。 但是,如果您打算覆蓋WebView中的用戶代理字符串,則可能需要使用getUserAgentString()。

多線程和線程阻塞

如果您從除應用程序的UI線程之外的任何線程調用WebView方法,可能會導致意外的結果。 例如,如果您的應用程序使用多個線程,則可以使用runOnUiThread()方法來確保您的代碼在UI線程上執行:
還要確保你永遠不會阻止UI線程。 某些應用程序出現此錯誤的情況是等待JavaScript回調。 例如,不要使用這樣的代碼:

webView.loadUrl("javascript:fn()");
while(result == null) {
  Thread.sleep(100);
}

您可以使用一種新方法evaluateJavascript()來異步運行JavaScript。

自定義URL處理

新的WebView在請求資源并解析使用自定義URL方案的鏈接時,會附加其他限制。 例如,如果您實現回調,例如shouldOverrideUrlLoading()或shouldInterceptRequest(),則WebView將僅調用有效的URL。

如果您使用自定義URL方案或基本URL,并注意到您的應用程序在這些回調中接收的呼叫較少或在Android 4.4上無法加載資源,請確保請求指定符合RFC 3986的有效URL。

例如,新的WebView可能不會像這樣的鏈接調用你的shouldOverrideUrlLoading()方法:
<a href="showProfile">Show Profile</a>
用戶點擊此鏈接的結果可能有所不同:
如果通過使用無效或空的基本URL調用loadData()或loadDataWithBaseURL()來加載頁面,那么您將不會收到該頁面上此類鏈接的shouldOverrideUrlLoading()回調。
注意:當您使用loadDataWithBaseURL()并且基本URL無效或設置為null時,正在加載的內容中的所有鏈接必須是絕對的。
如果您通過調用loadUrl()或通過loadDataWithBaseURL()提供有效的基本URL來加載頁面,那么您將收到該頁面上此類型鏈接的shouldOverrideUrlLoading()回調,但您收到的URL將是絕對的,相對于 當前頁面。 例如,您收到的URL將是“http://www.example.com/showProfile”而不是“showProfile”。
而不是如上所示在鏈接中使用簡單的字符串,您可以使用自定義方案,如下所示:
<a href="example-app:showProfile">Show Profile</a>
然后,您可以在您的shouldOverrideUrlLoading()方法中處理此URL,如下所示:

private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

如果您不能修改HTML,那么您可以使用loadDataWithBaseURL()并設置由自定義方案和有效主機(例如“example-app:// <valid_host_name> /”)組成的基本URL。 例如:
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA,
null, "UTF-8", null);

有效的主機名應符合RFC 3986的要求,并且最后包括尾部的斜杠很重要,否則可能會丟棄來自加載的頁面的任何請求。

視口更改

不再支持視口target-densitydpi
以前,WebView支持一個名為target-densitydpi的視口屬性,以幫助網頁指定其預期的屏幕密度。 此屬性不再受支持,您應該遷移到使用圖像和CSS的標準解決方案,如WebView中的Pixel-Perfect UI中所述。

視口縮小時放大

以前,如果將視口寬度設置為小于或等于“320”的值,則將其設置為“device-width”,如果將視口高度設置為小于或等于WebView高度的值 將設置為“device-height”。 但是,當在新的WebView中運行時,會粘貼寬度或高度值,WebView放大以填充屏幕寬度

不支持多個視口標簽

以前,如果您在網頁中包含多個視口標簽,則WebView將合并所有標簽的屬性。 在新的WebView中,僅使用最后一個視口,并忽略所有其他視口。

默認縮放已棄用

getDefaultZoom()和setDefaultZoom()用于獲取和設置頁面上的初始縮放級別已不再受支持,您應該在網頁中定義相應的視口。

注意:Android 4.4及更高版本不支持這些API。 即使您的targetSdkVersion設置為“18”或更低,這些API也不起作用。
有關如何在HTML中定義視口屬性的信息,請在WebView中閱讀Pixel-Perfect UI。

如果您無法在HTML中設置視口的寬度,那么您應該調用setUseWideViewPort()來確保頁面被賦予更大的視口。 例如:
WebSettings settings = webView.getSettings();
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
造型變化
背景CSS簡寫覆蓋了背景大小
Chrome和其他瀏覽器的行為已經有一段時間了,但是如果您還指定了背景樣式,那么WebView也將覆蓋背景大小的CSS設置。 例如,這里的大小將被重置為默認值:
.some-class {
background-size: contain;
background: url('images/image.png') no-repeat;
}
修復是簡單地切換兩個屬性。 以這種方式行事一段時間,但是如果您還指定了背景樣式,那么WebView還將覆蓋背景大小的CSS設置。 例如,這里的大小將被重置為默認值:
.some-class {
background: url('images/image.png') no-repeat;
background-size: contain;
}
尺寸為CSS像素而不是屏幕像素
以前,諸如window.outerWidth和window.outerHeight之類的大小參數在實際的屏幕像素中返回一個值。在新的WebView中,這些返回基于CSS像素的值。

通常不好的做法是嘗試計算尺寸元素或其他計算的物理尺寸(以像素為單位)。但是,如果禁用縮放,并將初始化設置為1.0,則可以使用window.devicePixelRatio獲取縮放比例,然后將CSS像素值乘以該像素值。相反,您還可以創建一個JavaScript綁定,以從WebView自身查詢像素大小。

有關更多信息,請參閱quirksmode.org。

不再支持NARROW_COLUMNS和SINGLE_COLUMN
新的WebView不支持WebSettings.LayoutAlgorithm的NARROW_COLUMNS值。

注意:Android 4.4及更高版本不支持這些API。即使您的targetSdkVersion設置為“18”或更低,這些API也不起作用。

您可以通過以下方式處理此更改:

更改應用程序的樣式:
如果您可以控制頁面上的HTML和CSS,可能會發現改變內容的設計可能是最可靠的方法。例如,對于您引用許可證的屏幕,您可能需要在< pre>標簽之間進行換行,您可以使用以下樣式:
<pre style="word-wrap: break-word; white-space: pre-wrap;">
如果您尚未定義頁面的視口屬性,這可能會特別有用。
使用新的TEXT_AUTOSIZING布局算法:
如果您使用窄列作為使廣泛的桌面版網站在移動設備上更易于閱讀的方式,并且您無法更改HTML內容,則新的TEXT_AUTOSIZING算法可能是NARROW_COLUMNS的替代方案。
此外,新的WebView中也不支持SINGLE_COLUMN值(以前已不推薦使用)。

處理JavaScript中的觸摸事件
如果您的網頁直接處理WebView中的觸摸事件,請確保您還處理了touchcancel事件。觸摸屏有幾種情況會被調用,如果沒有收到,可能會導致問題:

一個元素被觸摸(所以touchstart和touchmove被調用),頁面被滾動,導致一個觸摸屏被拋出。
觸摸一個元素(touchstart被調用),但是沒有調用event.preventDefault(),從而足夠早就觸發了觸發事件(所以WebView假設你不想消耗觸摸事件)。

調試Web程序

如果您使用運行Android 4.4或更高版本的設備測試您的網絡應用程序,則可以使用Chrome開發人員工具在WebView中遠程調試網頁,同時繼續支持舊版Android。 有關詳細信息,請參閱Android上的遠程調試。

如果您沒有運行Android 4.4或更高版本的設備,則可以使用控制臺JavaScript API調試JavaScript,并將輸出消息查看到logcat。 如果您熟悉使用Firebug或Web Inspector調試網頁,那么您可能熟悉使用控制臺(如console.log())。 Android的WebKit框架支持大多數相同的API,因此您可以在Android瀏覽器或自己的WebView中調試時從網頁接收日志。 本文檔介紹如何使用控制臺API進行調試。

在Android瀏覽器中使用控制臺API

當調用控制臺函數(在DOM的window.console對象中)時,輸出將顯示在logcat中。 例如,如果您的網頁執行以下JavaScript:
console.log("Hello World");
然后logcat消息看起來像這樣:
Console: Hello World http://www.example.com/hello.html :82
根據您使用的Android版本,郵件的格式可能會有所不同。 在Android 2.1及更高版本上,來自Android瀏覽器的控制臺消息被標記為名稱“browser”。 在Android 1.6及更低版本上,Android瀏覽器消息被標記為名稱“WebCore”。

Android的WebKit不會實現其他桌面瀏覽器中可用的所有控制臺API。 但是,您可以使用基本的文本記錄功能:
console.log(String)
console.info(String)
console.warn(String)
console.error(String)
其他控制臺功能不會引起錯誤,但可能與其他Web瀏覽器的期望不同。

在WebView中使用控制臺API

在WebView中調試時,還支持上述所有控制臺API。 如果您要定位Android 2.1(API級別7)及更高版本,則必須提供實現onConsoleMessage()方法的WebChromeClient,以便控制臺消息出現在logcat中。 然后,使用setWebChromeClient()將WebChromeClient應用到WebView。

例如,為了支持API級別7,這是onConsoleMessage(String,int,String)的代碼可能如下所示:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebChromeClient(new WebChromeClient() {
  public void onConsoleMessage(String message, int lineNumber, String sourceID) {
    Log.d("MyApplication", message + " -- From line "
                         + lineNumber + " of "
                         + sourceID);
  }
});

但是,如果您的最低支持版本是API級別8或更高版本,那么您應該實現onConsoleMessage(ConsoleMessage)。 例如:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebChromeClient(new WebChromeClient() {
  public boolean onConsoleMessage(ConsoleMessage cm) {
    Log.d("MyApplication", cm.message() + " -- From line "
                         + cm.lineNumber() + " of "
                         + cm.sourceId() );
    return true;
  }
});

ConsoleMessage還包括一個MessageLevel對象來指示正在傳遞的控制臺消息的類型。 您可以使用messageLevel()來查詢消息級別,以確定消息的嚴重性,然后使用適當的Log方法或采取其他適當的操作。

無論您是使用onConsoleMessage(String,int,String)還是onConsoleMessage(ConsoleMessage),當您在網頁中執行控制臺方法時,Android都會調用相應的onConsoleMessage()方法,以便您可以報告錯誤。 例如,使用上面的示例代碼,將打印出如下所示的logcat消息:
Hello World -- From line 82 of http://www.example.com/hello.html

Web應用程序的最佳做法

開發用于移動設備的網頁和網絡應用程序與開發典型桌面網絡瀏覽器的網頁相比,具有不同的挑戰。 為了幫助您開始使用,以下是您應遵循的實踐列表,以便為Android和其他移動設備提供最有效的Web應用程序。
將移動設備重定向到您的網站的專用移動版本
使用服務器端重定向,您可以通過幾種方法將請求重定向到您的網站的移動版本。 大多數情況下,這通過“嗅探”Web瀏覽器提供的用戶代理字符串來完成。 要確定是否為您的站點提供移動版本,您應該只是在用戶代理中查找與移動設備類似的“移動”字符串。 如果需要,您還可以識別用戶代理字符串中的特定操作系統(例如“Android 2.1”)。
注意:應提供全尺寸網站(如平板電腦)的大屏幕Android設備不會在用戶代理中包含“移動”字符串,而其余的用戶代理字符串大部分相同。 因此,根據用戶代理中是否存在“移動”字符串,交付您的網站的移動版本非常重要。
使用適合移動設備的有效標記DOCTYPE
用于移動網站的最常用的標記語言是XHTML Basic。 此標準確保您的網站在移動設備上最有效的特定標記。 例如,它不允許在移動設備上表現不佳的HTML框架或嵌套表。 與DOCTYPE一起,請務必為文檔(如UTF-8)聲明適當的字符編碼。
例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
"http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
還要確保您的網頁標記對聲明的DOCTYPE有效。 使用驗證器,例如http://validator.w3.org可用的驗證器。
使用視口元數據來正確調整您的網頁大小
在您的文檔<head>中,您應該提供元數據,指定瀏覽器的視口如何呈現您的網頁。 例如,您的視口元數據可以指定瀏覽器視口的高度和寬度,初始網頁縮放以及目標屏幕密度。
例如:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
有關如何為Android設備使用視口元數據的更多信息,請閱讀從Web應用程序定位屏幕。
避免多個文件請求
由于移動設備的連接速度通常遠低于臺式機,所以您應該盡可能快地加載您的網頁。加速它的一種方法是避免在<head>中加載額外的文件,如樣式表和腳本文件。相反,直接在<head>(或<body>的末尾)直接在頁面加載之前不需要的腳本提供您的CSS和JavaScript。或者,您應該通過使用Minify等工具壓縮文件來優化文件的大小和速度。
使用垂直線性布局
避免用戶在瀏覽網頁時向左右滾動。上下滾動對用戶來說更容易,并使您的網頁更簡單。
有關創建優秀移動Web應用程序的更全面的指南,請參閱W3C的移動網絡最佳實踐。關于提高網站速度(適用于移動和桌面)的其他指導,請參閱Yahoo!的卓越績效指南和Google速度教程,讓我們使網絡更快。

以上為官方翻譯,如有翻譯不正確地方望指正。

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

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,702評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,816評論 18 139
  • 前言 總結 Android WebView 常用的相關知識點,令包含以下干貨內容分析:Js注入漏洞、WebView...
    無名小子的雜貨鋪閱讀 69,855評論 17 169
  • 世界七大奇跡之一的馬丘比丘的名頭實在太大,以至于前來庫斯科的游客都忽略了另一個世界之最——彩虹圣山,這個世界上最特...
    薄暮初陽閱讀 1,145評論 11 16
  • 1.一個由小寫字母組成的字符串可以看成一些同一字母的最大碎片組成的。例如,"aaabbaaac"是由下面碎片組成的...
    雨y飄零久閱讀 91評論 0 0