Android的換膚原理和Android的皮膚,裝載機(jī)框架解析

image.png

一 前言

Android的換膚技術(shù)已經(jīng)是很久之前就已經(jīng)被成熟使用的技術(shù)了,然而我最近才在學(xué)習(xí)和接觸熱修復(fù)的時(shí)候才看到住册,在看了一些換膚的方法之后,并且對(duì)市面上比較認(rèn)可的Android的皮膚瓮具,裝載機(jī)換膚框架的源碼進(jìn)行了分析總結(jié)荧飞。再次記錄一下祭奠自己逝去的時(shí)間。

換膚介紹

換膚本質(zhì)上是對(duì)資源的一中替換包括名党,字體叹阔,顏色,背景传睹,圖片耳幢,大小等等。當(dāng)然這些我們都有成熟的API可以通過控制代碼邏輯做到欧啤。比如查看的修改背景顏色setBackgroundColor睛藻,TextView中的setTextSize。修改字體等等但是作為程序員我們怎么能忍受對(duì)每個(gè)頁面的每個(gè)元素一個(gè)行行代碼做換膚處理呢堂油?我們需要用最少的代碼實(shí)現(xiàn)最容易維護(hù)和使用效果完美(動(dòng)態(tài)切換修档,及時(shí)生效)的換膚框架。

1.換膚方式一:切換使用主題主題

使用相同的資源ID府框,但在不同的主題下邊自定義不同的資源吱窝。我們通過主動(dòng)切換到不同的主題從而切換界面元素創(chuàng)建時(shí)使用的資源。這種方案的代碼量不多發(fā)迫靖,而且有個(gè)很明顯的缺點(diǎn)不支持已經(jīng)創(chuàng)建界面的換膚院峡,必須重新加載界面元素.GitHub Demo

2. 換膚方式二:加載資源包

加載資源包是各種應(yīng)用程序都在使用的換膚方法,例如我們最常用的輸入法皮膚系宜,瀏覽器皮膚等等照激。我們可以將皮膚的資源文件放入安裝包內(nèi)部,也可以進(jìn)行下載緩存到磁盤上.Android的應(yīng)用程序可以使用這種方式進(jìn)行換膚.GitHub上面有一個(gè)開始非常高的換膚框架Android的皮膚下載器就是通過加載資源包對(duì)應(yīng)用程序進(jìn)行換膚盹牧。對(duì)這個(gè)框架的分析這個(gè)也是這篇文章主要的講述內(nèi)容俩垃。

對(duì)比一下發(fā)現(xiàn)切換主題可以進(jìn)行小幅度的換膚設(shè)置(比如某個(gè)自定義組件的主題)励幼,而如果我們想要對(duì)整個(gè)應(yīng)用程序做主題切換那么通過加載資源包的這種方式目前應(yīng)該說是比較好的了。

Android的換膚知識(shí)點(diǎn)

1. 換膚相應(yīng)的API

我們先來看一下Android的提供的一些基本的API口柳,通過使用這些API可以在應(yīng)用程序內(nèi)部進(jìn)行資源對(duì)象的替換苹粟。

公共類資源{
    public String getString(int id)throws NotFoundException {
        CharSequence res = mAssets.getResourceText(id);
        if(res!= null){
            返回資源;
        }
        拋出新的NotFoundException(“字符串資源ID#0x”
                                    + Integer.toHexString(id));
    }
    public Drawable getDrawable(int id)throws NotFoundException {
        / ********部分代碼省略******* /
    }
    public int getColor(int id)throws NotFoundException {{
        / ********部分代碼省略******* /
    }
    / ********部分代碼省略******* /
}

這個(gè)是我們常用的資源類的API跃闹,我們通城断鳎可以使用在資源文件中定義的@+id字符串類型,然后在編譯出的R.java中對(duì)應(yīng)的資源文件生產(chǎn)的編號(hào)(INT類型)望艺,從而通過這個(gè)ID(INT類型)調(diào)用資源提供的這些API獲取到對(duì)應(yīng)的資源對(duì)象苛秕。這個(gè)在同一個(gè)應(yīng)用程序下沒有任何問題,但是在皮膚包中我們怎么獲取這個(gè)ID值呢找默。

公共類資源{
    / ********部分代碼省略******* /
    / **
*通過給的資源名稱返回一個(gè)資源的標(biāo)識(shí)id艇劫。
* @paramname描述資源的名稱
* @ paramdefType資源的類型
* @paramdefPackage包名
*
* @返回返回資源ID,0標(biāo)識(shí)未找到該資源
* /
    public int getIdentifier(String name啡莉,String defType港准,String defPackage){
        if(name == null){
            拋出新的NullPointerException(“name is null”);
        }
        嘗試{
            return Integer.parseInt(name);
        } catch(例外e){
            // 忽視
        }
        return mAssets.getResourceIdentifier(name旨剥,defType咧欣,defPackage);
    }
}

資源提供了可以通過@+id,類型轨帜,PACKAGENAME這三個(gè)參數(shù)就可以在AssetManager中尋找相應(yīng)的軟件包名中有沒有輸入類型并且ID值都能與參數(shù)對(duì)應(yīng)上的ID魄咕,進(jìn)行返回。然后我們可以通過這個(gè)ID再調(diào)用資源的獲取資源的API就可以得到相應(yīng)的資源蚌父。

我們這里需要注意的一點(diǎn)一的英文getIdentifier(String name, String defType, String defPackage)方法狀語從句:getString(int id)方法所調(diào)用資源對(duì)象的mAssets對(duì)象必須是同一個(gè)哮兰,并且包含有PACKAGENAME這個(gè)資源包。

2.AssetManager構(gòu)造

怎么構(gòu)造一個(gè)包含特定的packageName資源的AssetManager對(duì)象實(shí)例呢苟弛?

public final class AssetManagerimplements AutoCloseable {
    / ********部分代碼省略******* /
    / **
*創(chuàng)建僅包含基本系統(tǒng)資產(chǎn)的新AssetManager喝滞。
*應(yīng)用程序通常不會(huì)使用此方法,而是檢索
* {@ linkResources#getAssets}的適當(dāng)資產(chǎn)經(jīng)理膏秫。不是為了
*由應(yīng)用程序使用右遭。
* {@hide}
* /
    public AssetManager(){
        synchronized(this){
            if(DEBUG_REFS){
                mNumRefs = 0;
                incRefsLocked(this.hashCode());
            }
            INIT(假);
            if(localLOGV)Log.v(TAG,“新資產(chǎn)經(jīng)理:”+這個(gè));
            ensureSystemAssets();
        }
    }

從AssetManager構(gòu)造的函數(shù)來看有{@hide}的朱姐缤削,所以在其他類里面是直接創(chuàng)建AssetManager實(shí)例窘哈。但是不要忘記的Java中還有反射機(jī)制可以創(chuàng)建類對(duì)象。

1
AssetManager assetManager = AssetManager.class.newInstance();


讓創(chuàng)建的assetManager包含特定的PACKAGENAME的資源信息亭敢,怎么辦滚婉?我們在AssetManager中找到相應(yīng)的API可以調(diào)用。

public final class AssetManagerimplements AutoCloseable {
    / ********部分代碼省略******* /
    / **
*向資產(chǎn)經(jīng)理添加一組額外資產(chǎn)帅刀。這可以
*目錄或ZIP文件让腹。不適用于應(yīng)用程序远剩。返回
*添加資產(chǎn)的cookie,或失敗時(shí)為0骇窍。
* {@hide}
* /
    public final int addAssetPath(String path){
        synchronized(this){
            int res = addAssetPathNative(path);
            if(mStringBlocks民宿!= null){
                makeStringBlocks(mStringBlocks);
            }
            返回資源;
        }
    }
}

同樣改方法也不支持外部調(diào)用,我們只能通過反射的方法來調(diào)用像鸡。

/ **
* apk路徑
* /
String apkPath = Environment.getExternalStorageDirectory()+“/ skin.apk”;
AssetManager assetManager = null;
嘗試{
    AssetManager assetManager = AssetManager.class.newInstance();
    AssetManager.class.getDeclaredMethod(“addAssetPath”活鹰,String.class).invoke(assetManager,apkPath);
} catch(Throwable th){
    th.printStackTrace();
}

至此我們可以構(gòu)造屬于自己換膚的資源了只估。

3.換膚資源構(gòu)造

public Resources getSkinResources(Context context){
    / **
*插件apk路徑
* /
    String apkPath = Environment.getExternalStorageDirectory()+“/ skin.apk”;
    AssetManager assetManager = null;
    嘗試{
        AssetManager assetManager = AssetManager.class.newInstance();
        AssetManager.class.getDeclaredMethod(“addAssetPath”志群,String.class).invoke(assetManager,apkPath);
    } catch(Throwable th){
        th.printStackTrace();
    }
    返回新資源(assetManager蛔钙,context.getResources()锌云。getDisplayMetrics(),context.getResources()吁脱。getConfiguration());
}

4.使用資源包中的資源換膚

我們將上述所有的代碼組合在一起就可以實(shí)現(xiàn)桑涎,使用資源包中的資源對(duì)應(yīng)用程序進(jìn)行換膚。

public Resources getSkinResources(Context context){
    / **
*插件apk路徑
* /
    String apkPath = Environment.getExternalStorageDirectory()+“/ skin.apk”;
    AssetManager assetManager = null;
    嘗試{
        AssetManager assetManager = AssetManager.class.newInstance();
        AssetManager.class.getDeclaredMethod(“addAssetPath”兼贡,String.class).invoke(assetManager攻冷,apkPath);
    } catch(Throwable th){
        th.printStackTrace();
    }
    返回新資源(assetManager,context.getResources()遍希。getDisplayMetrics()等曼,context.getResources()。getConfiguration());
}
@覆蓋
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    的setContentView(R.layout.activity_main);
    ImageView imageView =(ImageView)findViewById(R.id.imageView);
    TextView textView =(TextView)findViewById(R.id.text);
    / **
*插件資源對(duì)象
* /
    Resources resources = getSkinResources(this);
    / **
*獲取圖片資源
* /
    Drawable drawable = resources.getDrawable(resources.getIdentifier(“night_icon”凿蒜,“drawable”禁谦,“com.tzx.skin”));
    / **
*獲取文本資源
* /
    int color = resources.getColor(resources.getIdentifier(“night_color”,“color”废封,“com.tzx.skin”));

    imageView.setImageDrawable(繪制);
    textView.setText(文本);

}

通過上述介紹州泊,我們可以簡單的對(duì)當(dāng)前頁面進(jìn)行換膚了。但是想要做出一個(gè)一個(gè)成熟換膚框架那么僅僅這些還是不夠的漂洋,提高一下我們的思維高度遥皂,如果我們在查看創(chuàng)建的時(shí)候就直接使用皮膚資源包中的資源文件,那么這無疑就使換膚更加的簡單已維護(hù)氮发。

5. LayoutInflater.Factory

我看過一篇前遇見LayoutInflater及工廠文章的這部分可以省略掉渴肉。

很幸運(yùn)的Android給我們在查看生產(chǎn)的時(shí)候做修改提供了法門。

公共抽象類LayoutInflater {
    / ***部分代碼省略**** /
    公共接口工廠{
        public View onCreateView(String name爽冕,Context context仇祭,AttributeSet attrs);
    }

    public interface Factory2extends Factory {
        public View onCreateView(查看父級(jí),字符串名稱颈畸,上下文上下文乌奇,AttributeSet attrs);
    }
    / ***部分代碼省略**** /
}

我們可以給當(dāng)前的頁面的窗口對(duì)象在創(chuàng)建的時(shí)候設(shè)置工廠没讲,那么在窗口中的視圖進(jìn)行創(chuàng)建的時(shí)候就會(huì)先通過自己設(shè)置的工廠進(jìn)行創(chuàng)建.Factory方式使用相關(guān)狀語從句:注意事項(xiàng)請移位到遇見LayoutInflater及工廠,關(guān)于工廠的相關(guān)知識(shí)點(diǎn)盡在其中礁苗。

四 Android的皮膚爬凑,裝載機(jī)解析

1. 初始化

  • 初始化換膚框架,導(dǎo)入需要換膚的資源包(當(dāng)前為一個(gè)APK文件试伙,其中只有資源文件)嘁信。
公共類SkinApplicationextends Application {
    public void onCreate(){
        super.onCreate();
        initSkinLoader();
    }
    / **
*必須先調(diào)用init
* /
    private void initSkinLoader(){
        。SkinManager.getInstance()的init(本);
        SkinManager.getInstance()負(fù)載();
    }
}

2.構(gòu)造換膚對(duì)象

導(dǎo)入需要換膚的資源包疏叨,并構(gòu)造換膚的資源實(shí)例潘靖。

/ **
*在asyc任務(wù)中從apk加載資源
* @ paramskinPackagePath皮膚路徑apk
* @paramcallback回調(diào)通知用戶
* /
public void load(String skinPackagePath,final ILoaderListener callback){
    
    新的AsyncTask(){

        protected void onPreExecute(){
            if(callback蚤蔓!= null){
                callback.onStart();
            }
        };

        @覆蓋
        protected資源doInBackground(String ... params){
            嘗試{
                if(params.length == 1){
                    String skinPkgPath = params [0];
                    
                    File file = new File(skinPkgPath); 
                    if(file == null ||卦溢!file.exists()){
                        return null;
                    }
                    
                    PackageManager mPm = context.getPackageManager();
                    //檢索程序外的一個(gè)安裝包文件
                    PackageInfo mInfo = mPm.getPackageArchiveInfo(skinPkgPath,PackageManager.GET_ACTIVITIES);
                    //獲取安裝包報(bào)名
                    skinPackageName = mInfo.packageName;
                    //構(gòu)建換膚的AssetManager實(shí)例
                    AssetManager assetManager = AssetManager.class.newInstance();
                    方法addAssetPath = assetManager.getClass()秀又。getMethod(“addAssetPath”单寂,String.class);
                    addAssetPath.invoke(assetManager,skinPkgPath);
                    //構(gòu)建換膚的資源實(shí)例
                    資源superRes = context.getResources();
                    資源skinResource = new Resources(assetManager吐辙,superRes.getDisplayMetrics()宣决,superRes.getConfiguration());
                    //存儲(chǔ)當(dāng)前皮膚路徑
                    SkinConfig.saveSkinPath(context,skinPkgPath);
                    
                    skinPath = skinPkgPath;
                    isDefaultSkin = false;
                    return skinResource;
                }
                return null;
            } catch(例外e){
                e.printStackTrace();
                return null;
            }
        };

        protected void onPostExecute(參考資料結(jié)果){
            mResources =結(jié)果;

            if(mResources袱讹!= null){
                if(callback疲扎!= null)callback.onSuccess();
                //更新多有可換膚的界面
                notifySkinUpdate();
            }其他{
                isDefaultSkin = true;
                if(callback昵时!= null)callback.onFailed();
            }
        };

    } .execute(skinPackagePath);
}

定義基類
換膚頁面的基類的通用代碼實(shí)現(xiàn)基本換膚功能捷雕。

public class BaseFragmentActivityextends FragmentActivityimplements ISkinUpdate,IDynamicNewView {
    
    / ***部分代碼省略**** /
    
    //自定義LayoutInflater.Factory
    private SkinInflaterFactory mSkinInflaterFactory;
    
    @覆蓋
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
    
        嘗試{
            //設(shè)置LayoutInflater的mFactorySet為真壹甥,表示還未設(shè)置mFactory救巷,否則會(huì)拋出異常。
            Field field = LayoutInflater.class.getDeclaredField(“mFactorySet”);
            field.setAccessible(真);
            field.setBoolean(getLayoutInflater()句柠,false);
            //設(shè)置LayoutInflater的MFactory
            mSkinInflaterFactory = new SkinInflaterFactory();
            getLayoutInflater()setFactory(mSkinInflaterFactory)浦译。

        } catch(NoSuchFieldException e){
            e.printStackTrace();
        } catch(IllegalArgumentException e){
            e.printStackTrace();
        } catch(IllegalAccessException e){
            e.printStackTrace();
        } 
        
    }

    @覆蓋
    protected void onResume(){
        super.onResume();
        //注冊皮膚管理對(duì)象
        。SkinManager.getInstance()連接(本);
    }
    
    @覆蓋
    protected void onDestroy(){
        super.onDestroy();
        //反注冊皮膚管理對(duì)象
        溯职。SkinManager.getInstance()分離(本);
    }
    / ***部分代碼省略**** /
}

3.SkinInflaterFactory

SkinInflaterFactory進(jìn)行查看的創(chuàng)建并對(duì)視圖進(jìn)行換膚精盅。

構(gòu)造查看
公共類SkinInflaterFactoryimplements Factory {
    / ***部分代碼省略**** /
    public View onCreateView(String name,Context context谜酒,AttributeSet attrs){
        //讀取查看的皮膚:使屬性叹俏,假的為不需要換膚
        //如果不允許進(jìn)行優(yōu)化,請簡單地跳過它
        boolean isSkinEnable = attrs.getAttributeBooleanValue(SkinConfig.NAMESPACE僻族,SkinConfig.ATTR_SKIN_ENABLE粘驰,false);
        if(屡谐!isSkinEnable){
                return null;
        }
        //創(chuàng)建視圖
        View view = createView(context,name蝌数,attrs);
        if(view == null){
            return null;
        }
        //如果視圖創(chuàng)建成功愕掏,對(duì)視圖進(jìn)行換膚
        parseSkinAttr(context,attrs顶伞,view);
        返回視圖;
    }
    //創(chuàng)建視圖饵撑,類比可以查看LayoutInflater的createViewFromTag方法
    private View createView(Context context,String name唆貌,AttributeSet attrs){
        View view = null;
        嘗試{
            if(-1 == name.indexOf('肄梨。')){
                if(“查看”.equals(name)){
                    view = LayoutInflater.from(context).createView(name,“android.view挠锥≈谙郏”,attrs);
                } 
                if(view == null){
                    view = LayoutInflater.from(context).createView(name蓖租,“android.widget粱侣。”蓖宦,attrs);
                } 
                if(view == null){
                    view = LayoutInflater.from(context).createView(name齐婴,“android.webkit〕砻”柠偶,attrs);
                } 
            } else {
                view = LayoutInflater.from(context).createView(name,null睬关,attrs);
            }

            李(“即將創(chuàng)造”+名稱);

        } catch(例外e){ 
            Le(“創(chuàng)建時(shí)出錯(cuò)”“+ +名+”:“+ e.getMessage());
            view = null;
        }
        返回視圖;
    }
}

4.對(duì)生產(chǎn)的景觀進(jìn)行換膚

公共類SkinInflaterFactoryimplements Factory {
    //存儲(chǔ)當(dāng)前活動(dòng)中的需要換膚的查看
    private List mSkinItems = new ArrayList();
    / ***部分代碼省略**** /
    private void parseSkinAttr(Context context诱担,AttributeSet attrs,View view){
        //當(dāng)前查看的所有屬性標(biāo)簽
        List viewAttrs = new ArrayList();
        
        for(int i = 0; i <attrs.getAttributeCount(); i ++){
            String attrName = attrs.getAttributeName(i);
            String attrValue = attrs.getAttributeValue(i);
            
            如果(电爹!AttrFactory.isSupportedAttr(attrName)){
                繼續(xù);
            }
            //過濾視圖屬性標(biāo)簽中屬性的值的值為引用類型
            如果(attrValue.startsWith( “@”)){
                嘗試{
                    int id = Integer.parseInt(attrValue.substring(1));
                    String entryName = context.getResources()蔫仙。getResourceEntryName(id);
                    String typeName = context.getResources()。getResourceTypeName(id);
                    //構(gòu)造SkinAttr實(shí)例丐箩,attrname摇邦,ID,entryName參數(shù)typeName
                    //屬性的名稱(背景)屎勘,屬性的ID值(INT類型)施籍,屬性的ID值(@ + ID,串類型)概漱,屬性的值類型(顏色)
                    SkinAttr mSkinAttr = AttrFactory.get(attrName丑慎,id,entryName,typeName);
                    if(mSkinAttr立哑!= null){
                        viewAttrs.add(mSkinAttr);
                    }
                } catch(NumberFormatException e){
                    e.printStackTrace();
                } catch(NotFoundException e){
                    e.printStackTrace();
                }
            }
        }
        //如果當(dāng)前視圖需要換膚夜惭,那么添加在mSkinItems中
        如果(!ListUtils.isEmpty(viewAttrs)){
            SkinItem skinItem = new SkinItem();
            skinItem.view = view;
            skinItem.attrs = viewAttrs;

            mSkinItems.add(skinItem);
            //是否是使用外部皮膚進(jìn)行換膚
            如果(SkinManager.getInstance()铛绰。isExternalSkin()){
                skinItem.apply();
            }
        }
    }
}

5.資源獲取

通過當(dāng)前的資源ID诈茧,找到對(duì)應(yīng)的資源名稱。再從皮膚包中找到該資源名稱所對(duì)應(yīng)的資源ID捂掰。

公共類SkinManagerimplements ISkinLoader {
    / ***部分代碼省略**** /
    public int getColor(int resId){
        int originColor = context.getResources()敢会。getColor(resId);
        //是否沒有下載皮膚或者當(dāng)前使用默認(rèn)皮膚
        if(mResources == null || isDefaultSkin){
            return originColor;
        }
        //根據(jù)渣油值獲取對(duì)應(yīng)的XML的的@ + ID的字符串類型的值
        String resName = context.getResources()。getResourceEntryName(resId);
        //更具resName在皮膚包的mResources中獲取對(duì)應(yīng)的渣油
        int trueResId = mResources.getIdentifier(resName这嚣,“color”鸥昏,skinPackageName);
        int trueColor = 0;
        嘗試{
            //根據(jù)渣油獲取對(duì)應(yīng)的資源值
            trueColor = mResources.getColor(trueResId);
        } catch(NotFoundException e){
            e.printStackTrace();
            trueColor = originColor;
        }
        
        return trueColor;
    }
    public Drawable getDrawable(int resId){...}
}
其他

除此之外再增加以下對(duì)于皮膚的管理API(下載,監(jiān)聽回調(diào)姐帚,應(yīng)用吏垮,取消,異常處理罐旗,擴(kuò)展模塊等等)膳汪。

五 總結(jié)

換膚就是這么簡單?!?九秀!

視頻→

hook源碼實(shí)現(xiàn)阿里無閃爍換膚鏈接:https://pan.baidu.com/s/1E9gdeeLADBiszUU-DFRWvw

想學(xué)習(xí)更多Android知識(shí)遗嗽,或者獲取相關(guān)資料請加入Android技術(shù)開發(fā)交流2群:935654177。本群可免費(fèi)獲取Gradle鼓蜒,RxJava痹换,小程序,Hybrid都弹,移動(dòng)架構(gòu)娇豫,NDK,React Native缔杉,性能優(yōu)化等技術(shù)教程锤躁!

FMNX`NW45P@2{~8XII3B4VF.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市或详,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌郭计,老刑警劉巖霸琴,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異昭伸,居然都是意外死亡梧乘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來选调,“玉大人夹供,你說我怎么就攤上這事∪士埃” “怎么了哮洽?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弦聂。 經(jīng)常有香客問我鸟辅,道長,這世上最難降的妖魔是什么莺葫? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任匪凉,我火速辦了婚禮,結(jié)果婚禮上捺檬,老公的妹妹穿的比我還像新娘再层。我一直安慰自己,他們只是感情好堡纬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布树绩。 她就那樣靜靜地躺著,像睡著了一般隐轩。 火紅的嫁衣襯著肌膚如雪饺饭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天职车,我揣著相機(jī)與錄音瘫俊,去河邊找鬼。 笑死悴灵,一個(gè)胖子當(dāng)著我的面吹牛扛芽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播积瞒,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼川尖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茫孔?” 一聲冷哼從身側(cè)響起叮喳,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缰贝,沒想到半個(gè)月后馍悟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剩晴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年锣咒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侵状。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅整,死狀恐怖趣兄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悼嫉,我是刑警寧澤艇潭,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站承粤,受9級(jí)特大地震影響暴区,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辛臊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一仙粱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧彻舰,春花似錦伐割、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尚胞,卻和暖如春硬霍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笼裳。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工唯卖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躬柬。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓拜轨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親允青。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橄碾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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