MediaButtonReceiver相關(guān)筆記

注意: 寫該文章主要幫助自己記憶证舟,貼出來(lái)希望可以給有同樣問(wèn)題的人解惑拴曲,不喜勿噴,可以提意見(jiàn)哦。

一枷遂、待了解內(nèi)容
1樱衷、MediaButtonReceiver使用的情景
2、系統(tǒng)發(fā)送廣播到MediaButtonReceiver接收廣播流程的講解酒唉。

二矩桂、
1、MediaButtonReceiver使用的情景

Android源碼中并沒(méi)有MediaButtonReceiver這個(gè)類痪伦,MediaButtonReceiver繼承了BroadCastReceiver,即MediaButtonReceiver也就是一個(gè)簡(jiǎn)單的廣播接收者侄榴。但為什么單獨(dú)要講MediaButtonReceiver者呢,顧名思義网沾,該廣播接受者主要處理媒體按鈕的一些事件癞蚕,例如耳機(jī)中暫停,播放辉哥,上一曲,下一曲,音量調(diào)大碌燕,音量減小等咒劲。

例如:當(dāng)有如下幾個(gè)應(yīng)用圖片,本地音樂(lè)饲齐,藍(lán)牙音樂(lè)钉凌,收音機(jī)等,其中這些應(yīng)用中都有如下功能捂人,暫停甩骏,播放,上一個(gè)先慷,下一個(gè)饮笛,并且都注冊(cè)了語(yǔ)音操作廣播,當(dāng)操作這些功能的時(shí)候论熙,應(yīng)用都會(huì)做相應(yīng)的處理福青,為了避免語(yǔ)音操作播放,所有的應(yīng)用都接收到了該廣播脓诡,并執(zhí)行播放動(dòng)作无午,這個(gè)時(shí)候就需要利用MediaButtonReceiver的一個(gè)特別之處,只有當(dāng)前獲取焦點(diǎn)的應(yīng)用才會(huì)接收到該廣播祝谚。

2宪迟、系統(tǒng)發(fā)送廣播到MediaButtonReceiver接收廣播流程的講解。

注冊(cè)交惯、接收廣播簡(jiǎn)單實(shí)現(xiàn)次泽。
(1)實(shí)現(xiàn)MediaButtonReceiver代碼

public class MediaButtonReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
        KeyEvent event = 
               (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
        if (event == null) {
            return;
        }

        int keyCode = event.getKeyCode();
        switch (keyCode) {
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                    break;
        case KeyEvent.KEYCODE_MEDIA_NEXT: 
                    break;
        case KeyEvent.KEYCODE_MEDIA_PAUSE: 
                   break;
        case KeyEvent.KEYCODE_MEDIA_PLAY: 
                   break;
    }
    }
}}

(2)實(shí)現(xiàn)廣播接收者之后穿仪,需要注冊(cè)。在AndroidManifest.xml中靜態(tài)注冊(cè)廣播意荤。因?yàn)樾枨髥?wèn)題啊片,這里只監(jiān)聽(tīng)了一個(gè)action,表示媒體按鍵按下的時(shí)候會(huì)觸發(fā)該廣播玖像,如果有其他需要紫谷,用戶還可以監(jiān)聽(tīng)這些廣播,如,android.intent.action.HEADSET_PLUG表示耳機(jī)插入與移除捐寥,android.media.AUDIO_BECOMING_NOISY笤昨, 但是這個(gè)廣播只是針對(duì)有線耳機(jī),或者無(wú)線耳機(jī)的手機(jī)斷開(kāi)連接的事件握恳,監(jiān)聽(tīng)不到有線耳機(jī)和藍(lán)牙耳機(jī)的接入瞒窒。

  <receiver android:name="com.hwatong.music.MediaButtonReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>

(3)廣播接收者實(shí)現(xiàn)和注冊(cè)后,接下來(lái)睡互,就需要對(duì)廣播接收者進(jìn)行封裝根竿。U盤音樂(lè),藍(lán)牙音樂(lè)就珠,一般是需要啟動(dòng)一個(gè)服務(wù)在后臺(tái)播放寇壳。

public class MusicService extends Service{
    private AudioManager mAudioManager;
    private ComponentName mComponentName;

    @Override
    public void onCreate() {
      super.onCreate();
      mComponentName = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
              //音頻管理器主要是對(duì)AudioService相關(guān)方法的封裝,具體實(shí)現(xiàn)在AudioService中妻怎。
      mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

     }
//這里只是講解MediaButtonReceiver,具體的播放代碼就不貼出來(lái)了
 
private void play(){
         //當(dāng)應(yīng)用開(kāi)始播放的時(shí)候首先需要請(qǐng)求焦點(diǎn)壳炎,調(diào)用該方法后,原先獲取焦點(diǎn)的應(yīng)用會(huì)釋放焦點(diǎn)
        mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

//對(duì)媒體播放按鈕進(jìn)行封裝
        if(mComponentName==null){
            mComponentName = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
        }
//注冊(cè)封裝的ComponentName
mAudioManager.registerMediaButtonEventReceiver(mComponentNa
me);
      //省略具體播放內(nèi)容逼侦。匿辩。。
      }
} 

(4)榛丢,經(jīng)過(guò)(1)铲球、(2)、(3)步驟后晰赞,MediaButtonReceiver就已經(jīng)注冊(cè)成功稼病。

發(fā)送并分發(fā)廣播的實(shí)現(xiàn)
(1)發(fā)送媒體按鍵或者模擬系統(tǒng)按鍵。
//發(fā)送上一個(gè)媒體按鍵
sendMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
//發(fā)送下一個(gè)媒體按鍵
sendMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT);
//發(fā)送暫停媒體按鍵
sendMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE);
//發(fā)送播放媒體按鍵
sendMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY);

  private void sendMediaKeyEvent(final int keyCode) {
    //wait to handle onAbandonAudioFocus
    mMediaKeyRunnable = new MediaKeyRunnbale(keyCode);
}
    
     private MediaKeyRunnbale mMediaKeyRunnable;
private static class MediaKeyRunnbale implements Runnable {

    int keyCode = -1;
    public MediaKeyRunnbale(int keyCode) {
        this.keyCode = keyCode;
    }

    @Override
    public void run() {
        //模擬按下抬起事件
        sendEvent(KeyEvent.ACTION_DOWN, 0);
        sendEvent(KeyEvent.ACTION_UP, 0);
    }

    private void sendEvent(int action, int flags) {
        final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
         //判斷是否是長(zhǎng)按事件
        final KeyEvent ev = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), action, keyCode, repeatCount,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
            //調(diào)用InputManager中的injectInputEvent(...)方法掖鱼。
        InputManager.getInstance().injectInputEvent(ev,
                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
    }
}

(2)InputManager類位于frameworks\base\core\java\android\hardware\input中然走。上一步調(diào)用的方法如下。下面的對(duì)象mIm是 IInputManager戏挡,具體實(shí)現(xiàn)在ImputManagService芍瑞。

  public boolean injectInputEvent(InputEvent event, int mode) {
    if (event == null) {
        throw new IllegalArgumentException("event must not be null");
    }
    if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
            && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
            && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
        throw new IllegalArgumentException("mode is invalid");
    }

    try {
        return mIm.injectInputEvent(event, mode);
    } catch (RemoteException ex) {
        return false;
    }
}

(3)InputManagerService位于frameworks\base\services\java\com\android\server\input目錄下。上一步調(diào)用方法如下褐墅。該方法中最主要的一步 result = nativeInjectInputEvent(...);該方法是一個(gè)本地方法拆檬。這里涉及到Java和C語(yǔ)言之間的調(diào)用洪己,c中的具體代碼就不貼出來(lái)了。

 @Override // Binder call
public boolean injectInputEvent(InputEvent event, int mode) {
    if (event == null) {
        throw new IllegalArgumentException("event must not be null");
    }
    if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
            && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
            && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
        throw new IllegalArgumentException("mode is invalid");
    }

    final int pid = Binder.getCallingPid();
    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    final int result;
    try {
        result = nativeInjectInputEvent(mPtr, event, pid, uid, mode,
                INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
    switch (result) {
        case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
            Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
            throw new SecurityException(
                    "Injecting to another application requires INJECT_EVENTS permission");
        case INPUT_EVENT_INJECTION_SUCCEEDED:
            return true;
        case INPUT_EVENT_INJECTION_TIMED_OUT:
            Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
            return false;
        case INPUT_EVENT_INJECTION_FAILED:
        default:
            Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
            return false;
    }
}

(4)秩仆、第3步調(diào)用到本地方法码泛,本地方法經(jīng)過(guò)一番處理之后就會(huì)回調(diào)AudioService.java中的代碼猾封,AudioService位于frameworks\base\media\java\android\media目錄中

//==========================================================================================
// RemoteControl  C代碼處理完畢后澄耍,就會(huì)回調(diào)該方法。關(guān)于Java代碼與本地代碼的相互調(diào)用晌缘,可以看另一篇文章《Java代碼與本地代碼相互調(diào)用主要事項(xiàng)》
//==========================================================================================
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
    //參數(shù)1齐莲,表示KeyEvent,參數(shù)2表示是否喚醒屏幕。
    filterMediaKeyEvent(keyEvent, false );
}



 //filterMediaKeyEvent()方法具體實(shí)現(xiàn)如下磷箕,主要對(duì)于不同的KeyEvent進(jìn)行分發(fā)选酗。
 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    // sanity check on the incoming key event
   //如果KeyEvent為空則直接返回
    if (!isValidMediaKeyEvent(keyEvent)) {
        Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
        return;
    }
    // event filtering for telephony
   //來(lái)電或者通話的時(shí)候,需要調(diào)用dispatchMediaKeyEventForCalls方法
    synchronized(mRingingLock) {
        synchronized(mRCStack) {
            if ((mMediaReceiverForCalls != null) &&
                    (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
                dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
                return;
            }
        }
    }
    // event filtering based on voice-based interactions
    if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
        filterVoiceInputKeyEvent(keyEvent, needWakeLock);
    } else {
        dispatchMediaKeyEvent(keyEvent, needWakeLock);
    }
}
 //對(duì)聲音按鍵進(jìn)行處理岳枷,主要對(duì)聲音按鍵按下抬起芒填,長(zhǎng)按短按的處理。
 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    if (DEBUG_RC) {
        Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
    }

    int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
    int keyAction = keyEvent.getAction();
    synchronized (mVoiceEventLock) {
        if (keyAction == KeyEvent.ACTION_DOWN) {
           //表示短按
            if (keyEvent.getRepeatCount() == 0) {
                // initial down
                mVoiceButtonDown = true;
                mVoiceButtonHandled = false;
            } else if (mVoiceButtonDown && !mVoiceButtonHandled
                    && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                // long-press, start voice-based interactions
                mVoiceButtonHandled = true;
                voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
            }
        } else if (keyAction == KeyEvent.ACTION_UP) {
            if (mVoiceButtonDown) {
                // voice button up
                mVoiceButtonDown = false;
                if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
                    voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
                }
            }
        }
    }//synchronized (mVoiceEventLock)

    // take action after media button event filtering for voice-based interactions
    switch (voiceButtonAction) {
        case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
            if (DEBUG_RC) Log.v(TAG, "   ignore key event");
            break;
        case VOICEBUTTON_ACTION_START_VOICE_INPUT:
            if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
            // then start the voice-based interactions
            startVoiceBasedInteractions(needWakeLock);
            break;
        case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
            if (DEBUG_RC) Log.v(TAG, "   send simulated key event, wakelock=" + needWakeLock);
            sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
            break;
    }
}

private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
    if (needWakeLock) {
        mMediaEventWakeLock.acquire();
        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
    }
    final long ident = Binder.clearCallingIdentity();
    try {
        mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
                null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}
 
 //對(duì)普通按鍵進(jìn)行處理空繁,一般就是自定義的一些KeyEvent
 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    if (needWakeLock) {
        //喚醒屏幕
        mMediaEventWakeLock.acquire();
    }
    //發(fā)送ACTION_MEDIA_BUTTON廣播
    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    synchronized(mRCStack) {
    //mRCStack該對(duì)象是一個(gè)棧數(shù)據(jù)結(jié)構(gòu)殿衰,主要對(duì)PendingIntent進(jìn)行封裝
        if (!mRCStack.empty()) {
            // send the intent that was registered by the client
            try {
                //取出棧頂?shù)腜endingIntent,這里就是關(guān)鍵,為什么當(dāng)前獲取焦點(diǎn)的
               界面才會(huì)收到廣播盛泡。
                mRCStack.peek().mMediaIntent.send(mContext,
                        needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
                        keyIntent, AudioService.this, mAudioHandler);
            } catch (CanceledException e) {
                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
                e.printStackTrace();
            }
        } else {
            // legacy behavior when nobody registered their media button event receiver
            //    through AudioManager
            if (needWakeLock) {
                keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
            }
            final long ident = Binder.clearCallingIdentity();
            try {
                mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
                        null, mKeyEventDone,
                        mAudioHandler, Activity.RESULT_OK, null, null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }
}

(5)看完第(4)步大家可能不是很理解Pending是如何與RCStack產(chǎn)生聯(lián)系的闷祥,這里就來(lái)講解PendingIntent是如何入棧,如何出棧的傲诵。

當(dāng)客戶端調(diào)用
mAudioManager.registerMediaButtonEventReceiver(mComponentName);時(shí)凯砍,這時(shí)會(huì)間接調(diào)用frameworks\base\media\java\android\media目錄下的AudioService.java類中的方法pushMediaButtonReceiver,具體實(shí)現(xiàn)如下:

 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
    // already at top of stack?
    if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
        return;
    }
    Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
    RemoteControlStackEntry rcse = null;
    boolean wasInsideStack = false;
    while(stackIterator.hasNext()) {
        rcse = (RemoteControlStackEntry)stackIterator.next();
        if(rcse.mMediaIntent.equals(mediaIntent)) {
            wasInsideStack = true;
            stackIterator.remove();
            break;
        }
    }
    if (!wasInsideStack) {
        rcse = new RemoteControlStackEntry(mediaIntent, target);
    }    
    //主要代碼拴竹,入棧悟衩。
    mRCStack.push(rcse);

    // post message to persist the default media button receiver
    mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
            MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
}

當(dāng)客戶端調(diào)用
mAudioManager.unregisterMediaButtonEventReceiver(mComponentName);時(shí),這時(shí)會(huì)間接調(diào)用frameworks\base\media\java\android\media目錄下的AudioService.java類中的方法removeMediaButtonReceiverForPackage栓拜,具體實(shí)現(xiàn)如下:

 private void removeMediaButtonReceiverForPackage(String packageName) {
    synchronized(mRCStack) {
        if (mRCStack.empty()) {
            return;
        } else {
            RemoteControlStackEntry oldTop = mRCStack.peek();
            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
            // iterate over the stack entries
            while(stackIterator.hasNext()) {
                RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
                if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
                    // a stack entry is from the package being removed, remove it from the stack
                    stackIterator.remove();
                    rcse.unlinkToRcClientDeath();
                }
            }
            if (mRCStack.empty()) {
                // no saved media button receiver
                mAudioHandler.sendMessage(
                        mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
                                null));
            } else if (oldTop != mRCStack.peek()) {
                // the top of the stack has changed, save it in the system settings
                // by posting a message to persist it
                mAudioHandler.sendMessage(
                        mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
                                mRCStack.peek().mReceiverComponent));
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末座泳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子菱属,更是在濱河造成了極大的恐慌钳榨,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纽门,死亡現(xiàn)場(chǎng)離奇詭異薛耻,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赏陵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門饼齿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饲漾,“玉大人,你說(shuō)我怎么就攤上這事缕溉】即” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵证鸥,是天一觀的道長(zhǎng)僚楞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)枉层,這世上最難降的妖魔是什么泉褐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮鸟蜡,結(jié)果婚禮上膜赃,老公的妹妹穿的比我還像新娘。我一直安慰自己揉忘,他們只是感情好跳座,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著泣矛,像睡著了一般疲眷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乳蓄,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天咪橙,我揣著相機(jī)與錄音,去河邊找鬼虚倒。 笑死美侦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的魂奥。 我是一名探鬼主播菠剩,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼耻煤!你這毒婦竟也來(lái)了具壮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哈蝇,失蹤者是張志新(化名)和其女友劉穎棺妓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炮赦,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怜跑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吠勘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片性芬。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡峡眶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出植锉,到底是詐尸還是另有隱情辫樱,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布俊庇,位于F島的核電站狮暑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏暇赤。R本人自食惡果不足惜心例,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一宵凌、第九天 我趴在偏房一處隱蔽的房頂上張望鞋囊。 院中可真熱鬧,春花似錦瞎惫、人聲如沸溜腐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挺益。三九已至,卻和暖如春乘寒,著一層夾襖步出監(jiān)牢的瞬間望众,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工伞辛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烂翰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓蚤氏,卻偏偏與公主長(zhǎng)得像甘耿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竿滨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程佳恬,因...
    小菜c閱讀 6,402評(píng)論 0 17
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評(píng)論 25 707
  • 先說(shuō)一下這篇文章里面的內(nèi)容:TCP 客戶端, 自定義對(duì)話框, 自定義按鈕, ProgressBar豎直顯示, 重力...
    楊奉武閱讀 3,295評(píng)論 0 3
  • 看天下事榮華富貴無(wú)非兒戲;勸世間人榮辱得失何必認(rèn)真于游。
    璐峰閱讀 222評(píng)論 0 0
  • 愛(ài)一個(gè)人 因?yàn)樘珢?ài) 而不知道怎么用言語(yǔ)表白 所以 遲到的情書(shū) 獻(xiàn)給最初的你 獻(xiàn)給我媳婦 讓一切在這開(kāi)始 ...
    嘉措頓珠閱讀 224評(píng)論 0 0