Android 騰訊互動直播集成

最近剛用完互動直播sdk 做完直播卦方,簡單記錄下曼氛。

官網(wǎng):https://cloud.tencent.com/product/ilvb

隨心博 demo:https://cloud.tencent.com/document/product/268/7658

直播接口:https://cloud.tencent.com/document/product/268/7685

直播API: https://zhaoyang21cn.github.io/iLiveSDK_Help/android_help/

一.旁路推流

首先先弄懂“旁路”的概念

凡是通過url觀看的直播都是旁路直播宠蚂,旁路直播要進行旁路推流蹦渣,否則旁路直播無法觀看直播霉翔。

向在app內我們是通過joinRoom() 加入的房間揣炕,所以不屬于旁路直播。

二.主播方

大概流程是這個樣子稠腊,根據(jù)自己需求進行變更

1.向服務器請求房間號躁染。

2.根據(jù)房間號,創(chuàng)建房間架忌。

 public void createRoom() {
        ILVLiveRoomOption hostOption = new ILVLiveRoomOption(”主播ID“)
                .roomDisconnectListener(this)
                .autoCamera(true)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole(CurLiveInfo.getCurRole())
                //角色是主播 一定不能設置錯  主播走核心機房  觀眾走邊緣機房
//                .controlRole("LiveMaster")
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);
     
        int ret = ILVLiveManager.getInstance().createRoom(“房間ID”, hostOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
            
               if (getView()!=null)
                   getView().enterRoomComplete(MySelfInfo.getInstance().getIdStatus(), true);
                ILiveLog.d(TAG, "ILVB-SXB|startEnterRoom->create room sucess");
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
            
               if (getView()!=null)
                   getView().errRoomFail();
                ILiveLog.d(TAG, "ILVB-SXB|createRoom->create room failed:" + module + "|" + errCode + "|" + errMsg);
            }
        });
  
    }

4.創(chuàng)建房間成功后有旁路直播開啟旁路推流吞彤。


public void pushStream() {
        /**
         * 旁路直播 退出房間時必須退出推流。否則會占用后臺channel叹放。
         */
        ILivePushOption option = new ILivePushOption();
        option.encode(ILivePushOption.Encode.HLS);
//        option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);
        option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);
//        option.channelName("跟ios協(xié)商");
        //推流格式 到時候跟ios統(tǒng)一
        pushStream(option);


    }
/**
     * 開啟推流
     *
     * @param option
     */
    public void pushStream(ILivePushOption option) {
        if (!isPushed) {
            ILiveRoomManager.getInstance().startPushStream(option, new ILiveCallBack<ILivePushRes>() {
                @Override
                public void onSuccess(ILivePushRes data) {
//                List<ILivePushUrl> liveUrls = data.getUrls();
                    Log.i("pushStream", "推流成功J嗡 !>觥B袂丁!>愣瘛雹嗦!");
                 
                }

                @Override
                public void onError(String module, int errCode, String errMsg) {
                    Log.i("pushStream", "推流失敗:鲜恰A俗铩!4先2磁骸!难礼!");
                }
            });
        }
    }

5.退出房間前:在房間發(fā)送自定義消息告訴觀眾主播要退出房間吱七,方便觀眾端切換主播已離開界面,并停止推流。

發(fā)送自定義消息退出房間

 public void sendExitRoomMSG() {
        ILVCustomCmd cmd = new ILVCustomCmd();
        cmd.setCmd(Constans.AVIMCMD_EXITLIVE);
        cmd.setParam(sendExitRoomJson());
        cmd.setType(ILVText.ILVTextType.eGroupMsg);
        ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack<TIMMessage>() {
            @Override
            public void onSuccess(TIMMessage data) {
             
               if (isPushed) {
                    stopStream();
                }
                callExitRoom();

            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
            }

        });
    }

停止推流

 /**
     * 退出直播的時候關閉推流
     */
    public void stopStream() {
        ILiveRoomManager.getInstance().stopPushStream(streamChannelID, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                SxbLog.e(TAG, "stopPush->success");
                isPushed = false;
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                SxbLog.e(TAG, "stopPush->failed:" + module + "|" + errCode + "|" + errMsg);
            }
        });

    }

6.退出房間鹤竭。

private void callExitRoom() {
        ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        quitLiveRoom();
    }

三.觀眾方

大概流程是這個樣子踊餐,根據(jù)自己需求進行變更。

1.向服務器請求房間號臀稚。

2.根據(jù)房間號加入房間吝岭。

 //加入房間
    public void joinRoom(int roomId) {
        ILVLiveManager.getInstance().quitRoom(null);
        this.rommId = roomId + "";
        ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())
                .autoCamera(false)
                .roomDisconnectListener(this)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole("Guest")
                .authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)
                .autoMic(false);
        int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");
                if (null != mGuestLiveView)

                    mGuestLiveView.enterRoomComplete(true);
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                LogUtils.d("suyan", "===========加入房間失敗" + module + errCode + errMsg);
                if (null != mGuestLiveView) {
                    mGuestLiveView.enterRoomComplete(false);
                }
            }
        });
    }

3.加入房間成功后,在房間發(fā)送自定義消息,告訴房間內有人加入窜管,方便主播統(tǒng)計目前的房間人數(shù)散劫。

 /**
     * 發(fā)送信令,點贊幕帆,進入房間
     */

    public int sendGroupCmd(int cmd, String param) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        //1.傳cmd
        customCmd.setCmd(cmd);
        //2.創(chuàng)建自定義數(shù)據(jù)
        String json = sendCusJsn();
        //3.傳自定義數(shù)據(jù)
        customCmd.setParam(json);
        //4.設置自定義類型
        customCmd.setType(ILVText.ILVTextType.eGroupMsg);
        return sendCmd(customCmd);
    }

4.退出房間前:發(fā)送房間自定義消息获搏,告訴主播觀眾退出,方便主播統(tǒng)計目前的在線人數(shù)失乾。

5.退出直播間常熙。

  public void startExitRoom() {
        if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {
            ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        }
        quitLiveRoom();
    }
 private void quitLiveRoom() {
        ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                //1.回調給頁面
                if (null != mGuestLiveView) {
                    LogUtils.d("suyan", "=========退出成功");
                    mGuestLiveView.quiteRoomComplete(true);
                }
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                if (null != mGuestLiveView) {
                    mGuestLiveView.quiteRoomComplete(false);
                }
            }
        });
    }

自己封裝了一個觀眾端的使用類,也是根據(jù)互動直播demo改的

package com.jyjt.ydyl.txlive.presenters;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.jyjt.ydyl.Entity.LiveCusEntity;
import com.jyjt.ydyl.tools.ConfigUtils;
import com.jyjt.ydyl.tools.Constans;
import com.jyjt.ydyl.tools.LogUtils;
import com.jyjt.ydyl.tools.ToastUtil;
import com.jyjt.ydyl.txlive.LiveDetailActivity;
import com.jyjt.ydyl.txlive.event.LiveMessageEvent;
import com.jyjt.ydyl.txlive.views.GuestLiveView;
import com.tencent.TIMConversationType;
import com.tencent.TIMCustomElem;
import com.tencent.TIMElem;
import com.tencent.TIMElemType;
import com.tencent.TIMGroupSystemElem;
import com.tencent.TIMGroupSystemElemType;
import com.tencent.TIMMessage;
import com.tencent.av.sdk.AVRoomMulti;
import com.tencent.ilivesdk.ILiveCallBack;
import com.tencent.ilivesdk.ILiveConstants;
import com.tencent.ilivesdk.ILiveSDK;
import com.tencent.ilivesdk.core.ILiveLog;
import com.tencent.ilivesdk.core.ILiveRoomManager;
import com.tencent.ilivesdk.core.ILiveRoomOption;
import com.tencent.livesdk.ILVCustomCmd;
import com.tencent.livesdk.ILVLiveConstants;
import com.tencent.livesdk.ILVLiveManager;
import com.tencent.livesdk.ILVLiveRoomOption;
import com.tencent.livesdk.ILVText;
import com.tencent.openqq.protocol.imsdk.im_common;

import org.json.JSONObject;
import org.json.JSONTokener;

import java.util.Observable;
import java.util.Observer;

/**
 * 觀眾端碱茁,直播幫助類
 * <p>
 * 1.加入房間
 * 2.離開房間
 * 3.發(fā)送點贊裸卫,和加入房間指令
 * 4.解析傳過來的自定義消息(點贊,其他成員加入纽竣,用戶退出直播墓贿, 達到上限,主播回來了)
 * 5.房間鏈接斷開監(jiān)聽
 * <p>
 * 蘇艷
 */

public class GuestLiveHelper implements ILiveRoomOption.onRoomDisconnectListener, Observer {
    private final String TAG = "LiveHelper";
    private GuestLiveView mGuestLiveView;
    public Activity mContext;
    private String rommId;
    Gson gson;

    public GuestLiveHelper(Activity context, GuestLiveView mGuestLiveView) {
        mContext = context;
        this.mGuestLiveView = mGuestLiveView;
        LiveMessageEvent.getInstance().addObserver(this);
        GsonBuilder builder = new GsonBuilder();
        gson = builder.create();
    }

    //加入房間
    public void joinRoom(int roomId) {
        ILVLiveManager.getInstance().quitRoom(null);
        this.rommId = roomId + "";
        ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())
                .autoCamera(false)
                .roomDisconnectListener(this)
                .videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)
                .controlRole("Guest")
                .authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)
                .videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)
                .autoMic(false);
        int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");
                if (null != mGuestLiveView)

                    mGuestLiveView.enterRoomComplete(true);
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                LogUtils.d("suyan", "===========加入房間失敗" + module + errCode + errMsg);
                if (null != mGuestLiveView) {
                    mGuestLiveView.enterRoomComplete(false);
                }
            }
        });
    }

    //退出房間
    public void startExitRoom() {
        if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {
            ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);
        }
        quitLiveRoom();
    }

    private void quitLiveRoom() {
        ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                //1.回調給頁面
                if (null != mGuestLiveView) {
                    LogUtils.d("suyan", "=========退出成功");
                    mGuestLiveView.quiteRoomComplete(true);
                }
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
                if (null != mGuestLiveView) {
                    mGuestLiveView.quiteRoomComplete(false);
                }
            }
        });
    }


    /**
     * 發(fā)送信令蜓氨,點贊聋袋,進入房間
     */

    public int sendGroupCmd(int cmd, String param) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        //1.傳cmd
        customCmd.setCmd(cmd);
        //2.創(chuàng)建自定義數(shù)據(jù)
        String json = sendCusJsn();
        //3.傳自定義數(shù)據(jù)
        customCmd.setParam(json);
        //4.設置自定義類型
        customCmd.setType(ILVText.ILVTextType.eGroupMsg);
        return sendCmd(customCmd);
    }

    //邀請上麥用的,暫時用不到
    public int sendC2CCmd(final int cmd, String param, String destId) {
        ILVCustomCmd customCmd = new ILVCustomCmd();
        customCmd.setDestId(destId);
        customCmd.setCmd(cmd);
        customCmd.setParam(param);
        customCmd.setType(ILVText.ILVTextType.eC2CMsg);
        return sendCmd(customCmd);
    }

    private int sendCmd(final ILVCustomCmd cmd) {
        return ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack() {
            @Override
            public void onSuccess(Object data) {
                LogUtils.d(TAG, "suyan" + cmd.getCmd() + "|" + cmd.getParam() + "====發(fā)送成功");
            }

            @Override
            public void onError(String module, int errCode, String errMsg) {
//                Toast.makeText(mContext, "sendCmd->failed:" + module + "|" + errCode + "|" + errMsg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onRoomDisconnect(int errCode, String errMsg) {
        if (null != mGuestLiveView) {
            //房間異常退出穴吹,通常指斷網(wǎng)幽勒,或者沒電
            mGuestLiveView.roomDisconnect(errCode, errMsg);
        }
    }

    // 解析文本消息
    private void processTextMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof ILVText)) {
            return;
        }
        ILVText text = (ILVText) info.data;
        if (text.getType() == ILVText.ILVTextType.eGroupMsg
                && !text.getDestId().equals(rommId)) {
            if (TextUtils.isEmpty(rommId)) {
                LogUtils.d("suyan","========當前的roomid為空");
            }
            return;
        }

        String name = info.senderId;
        if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {
            name = info.profile.getNickName();
        }

        if (null != mGuestLiveView)
            mGuestLiveView.refreshText(text.getText(), name);
    }

    // 解析自定義信令
    private void processCmdMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof ILVCustomCmd)) {
            return;
        }
        ILVCustomCmd cmd = (ILVCustomCmd) info.data;
        if (cmd.getType() == ILVText.ILVTextType.eGroupMsg
                && !cmd.getDestId().equals(rommId)) {
            if (TextUtils.isEmpty(rommId)) {
                LogUtils.d("suyan","========當前的roomid為空");
            }
            return;
        }

        String name = info.senderId;
        if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {
            name = info.profile.getNickName();
        }

        handleCustomMsg(cmd.getCmd(), cmd.getParam(), info.senderId, name);
    }

    private void processOtherMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null == info.data || !(info.data instanceof TIMMessage)) {
            return;
        }
        TIMMessage currMsg = (TIMMessage) info.data;

        // 過濾非當前群組消息
        if (currMsg.getConversation() != null && currMsg.getConversation().getPeer() != null) {
            if (currMsg.getConversation().getType() == TIMConversationType.Group
                    && !rommId.equals(currMsg.getConversation().getPeer())) {
                if (TextUtils.isEmpty(rommId)) {
                    LogUtils.d("suyan","========當前的roomid為空");
                }
                return;
            }
        }

        for (int j = 0; j < currMsg.getElementCount(); j++) {
            if (currMsg.getElement(j) == null)
                continue;
            TIMElem elem = currMsg.getElement(j);
            TIMElemType type = elem.getType();


            //系統(tǒng)消息
            if (type == TIMElemType.GroupSystem) {  // 群組解散消息
                if (TIMGroupSystemElemType.TIM_GROUP_SYSTEM_DELETE_GROUP_TYPE == ((TIMGroupSystemElem) elem).getSubtype()) {
                    if (null != mGuestLiveView)
                        mGuestLiveView.dismissGroup("host", null);
                }
            } else if (type == TIMElemType.Custom) {
                try {
                    final String strMagic = "__ACTION__";
                    String customText = new String(((TIMCustomElem) elem).getData(), "UTF-8");
                    if (!customText.startsWith(strMagic))   // 檢測前綴
                        continue;
                    JSONTokener jsonParser = new JSONTokener(customText.substring(strMagic.length() + 1));
                    JSONObject json = (JSONObject) jsonParser.nextValue();
                    String action = json.optString("action", "");
                    if (action.equals("force_exit_room") || action.equals("force_disband_room")) {
                        JSONObject objData = json.getJSONObject("data");
                        String strRoomNum = objData.optString("room_num", "");
                        if (strRoomNum.equals(String.valueOf(ILiveRoomManager.getInstance().getRoomId()))) {
                            if (null != mGuestLiveView) {
                                mGuestLiveView.forceQuitRoom("管理員已將房間解散或將您踢出房間!");
                            }
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    @Override
    public void update(Observable observable, Object o) {

        LiveMessageEvent.SxbMsgInfo info = (LiveMessageEvent.SxbMsgInfo) o;
        LogUtils.d("suyan", "======收到消息" + info.msgType);
        switch (info.msgType) {
            case LiveMessageEvent.MSGTYPE_TEXT:
                processTextMsg(info);
                break;
            case LiveMessageEvent.MSGTYPE_CMD:
                processCmdMsg(info);
                break;
            case LiveMessageEvent.MSGTYPE_OTHER:
                LogUtils.d("suyan", "=======其他消息");
                processOtherMsg(info);
            case LiveMessageEvent.MSGTYPE_DDETAIL:
                LogUtils.d("suyan", "=======刷新房間數(shù)據(jù)");
                processDetailMsg(info);
                break;
        }
    }

    // 解析自定義信令
    private void processDetailMsg(LiveMessageEvent.SxbMsgInfo info) {
        if (null != info && !TextUtils.isEmpty(info.senderId)) {
            if (null != mGuestLiveView) {
                mGuestLiveView.refreshData(info.senderId);
            }
        }

    }

    private void handleCustomMsg(int action, String param, String identifier, String nickname) {
        LogUtils.d(TAG, "handleCustomMsg->action: " + action);
        if (null == mGuestLiveView) {
            return;
        }
        LiveCusEntity mliveCusEntity = updateGson(param);
        if (mliveCusEntity != null) {
            LogUtils.d("suyan", "===========解析后的數(shù)據(jù)" + mliveCusEntity.getContent().getLike_num() + "=" + mliveCusEntity.getContent().getOnline_num() + "=" + mliveCusEntity.getUser().getUid());
        }
        switch (action) {
            case Constans.AVIMCMD_PRAISE://點贊
                mGuestLiveView.refreshGoodLike();
                break;
            case Constans.AVIMCMD_ENTERLIVE://其他成員加入
                mGuestLiveView.memberJoin(identifier, nickname);
                break;
            case Constans.AVIMCMD_EXITLIVE: //用戶退出直播
                mGuestLiveView.hostOffline(identifier, nickname);
                break;
            case ILVLiveConstants.ILVLIVE_CMD_LINKROOM_LIMIT:   // 達到上限
                LogUtils.d("suyan","========到達上限");
                break;
            case Constans.AVIMCMD_HOST_BACK://主播回來了
                LogUtils.d("suyan","========主播回來了");
                //昵稱:nickname  id:identifier
            case Constans.AVIMCMD_PRAISE_TOTAL://觀眾進入房間,主播發(fā)送現(xiàn)在的總點贊數(shù)
                try {
                    if (mliveCusEntity != null && !TextUtils.isEmpty(mliveCusEntity.getContent().getLike_num())) {
                        mGuestLiveView.refreshTotalGoodLike(Long.parseLong(mliveCusEntity.getContent().getLike_num()));
                    }
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            default:
                break;
        }
    }

    //解析jsong
    public LiveCusEntity updateGson(String jsonTest) {
        if (!TextUtils.isEmpty(jsonTest)) {
            LiveCusEntity mLiveCusEntity;
            try {
                mLiveCusEntity = gson.fromJson(jsonTest, LiveCusEntity.class);
                return mLiveCusEntity;
            } catch (JsonSyntaxException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            return null;
        }
    }

    //發(fā)送json
    public String sendCusJsn() {
        LiveCusEntity liveCusEntity = new LiveCusEntity();
        LiveCusEntity.LiveContent mLiveContent = new LiveCusEntity.LiveContent();
        liveCusEntity.setContent(mLiveContent);
        LiveCusEntity.User mUser = new LiveCusEntity.User();
        mUser.setUid(ConfigUtils.getUid());
        liveCusEntity.setUser(mUser);
        String json = gson.toJson(liveCusEntity, LiveCusEntity.class);
        LogUtils.d("suyan", "==========自定義數(shù)據(jù)" + json);
        return json;
    }

    //銷毀
    public void onDestory() {
        mGuestLiveView = null;
        mContext = null;
        LiveMessageEvent.getInstance().deleteObserver(this);
        ILVLiveManager.getInstance().quitRoom(null);
        ILiveRoomManager.getInstance().onDestory();
        ILVLiveManager.getInstance().onDestory();
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末刀荒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子棘钞,更是在濱河造成了極大的恐慌缠借,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宜猜,死亡現(xiàn)場離奇詭異泼返,居然都是意外死亡,警方通過查閱死者的電腦和手機姨拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門绅喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叫乌,你說我怎么就攤上這事柴罐。” “怎么了憨奸?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵革屠,是天一觀的道長。 經(jīng)常有香客問我,道長似芝,這世上最難降的妖魔是什么那婉? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮党瓮,結果婚禮上详炬,老公的妹妹穿的比我還像新娘。我一直安慰自己寞奸,他們只是感情好呛谜,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝇闭,像睡著了一般呻率。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呻引,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天礼仗,我揣著相機與錄音,去河邊找鬼逻悠。 笑死元践,一個胖子當著我的面吹牛,可吹牛的內容都是我干的童谒。 我是一名探鬼主播单旁,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼饥伊!你這毒婦竟也來了象浑?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤琅豆,失蹤者是張志新(化名)和其女友劉穎愉豺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茫因,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蚪拦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冻押。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驰贷。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洛巢,靈堂內的尸體忽然破棺而出括袒,到底是詐尸還是另有隱情,我是刑警寧澤稿茉,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布箱熬,位于F島的核電站类垦,受9級特大地震影響,放射性物質發(fā)生泄漏城须。R本人自食惡果不足惜蚤认,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糕伐。 院中可真熱鬧砰琢,春花似錦、人聲如沸良瞧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褥蚯。三九已至挚冤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赞庶,已是汗流浹背训挡。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留歧强,地道東北人澜薄。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像摊册,于是被迫代替她去往敵國和親肤京。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容

  • 主要功能 互動直播定義 互動直播(Interactive Live Video Broadcasting)茅特,顧名思...
    0蛐蛐0閱讀 5,997評論 1 8
  • 全局創(chuàng)建context忘分? 創(chuàng)建一個全局的context,然后退出SDK層房間時不銷毀只是停止context白修。 SD...
    Carden閱讀 1,455評論 0 2
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,262評論 25 707
  • 用兩張圖告訴你妒峦,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,744評論 2 59
  • 啟動電腦發(fā)現(xiàn)熬荆,搜狗拼音出問題了舟山。圖標變大绸狐,輸入沒有選項卤恳。經(jīng)查找,如下步驟解決問題 1寒矿,刪除~/.config目錄下...
    南王農夫閱讀 971評論 0 0