讀讀SDWebImageDownloader

SD源碼中下載只涉及到兩個(gè)類析桥,一個(gè)是SDWebImageDownloader司草,另外一個(gè)是SDWebImageDownloaderOperation艰垂。由文件命名不難看出,SD采用的是NSOperation異步下載的方式埋虹。下面就說(shuō)說(shuō)它的幾個(gè)細(xì)節(jié)處理猜憎。


  • 如何實(shí)現(xiàn)一個(gè)下載manager?
  • 如何處理一個(gè)異步下載搔课?
  • 一個(gè)下載任務(wù)具體實(shí)現(xiàn)胰柑?
  • 其他細(xì)節(jié)處理

1. 如何實(shí)現(xiàn)一個(gè)下載manager?

通常設(shè)計(jì)一個(gè)Manager就是對(duì)外提供一個(gè)統(tǒng)一的接口爬泥,Manager應(yīng)該具備的功能主要有就是對(duì)被操作對(duì)象進(jìn)行管理(增加柬讨,刪除,修改)和處理操作(具體就是被操作對(duì)象的功能方法)并且維護(hù)任務(wù)狀態(tài)急灭。要實(shí)現(xiàn)一個(gè)Manager姐浮,常規(guī)做法就是維護(hù)一個(gè)對(duì)象容器,并對(duì)該容器進(jìn)行相關(guān)操作葬馋。在此處卖鲤,SDWebImageDownloader也是這樣來(lái)實(shí)現(xiàn)的。其具體做法就是內(nèi)部維護(hù)一個(gè)NSOperationqueue畴嘶,來(lái)處理外部下載任務(wù)蛋逾。同時(shí),它也維護(hù)一個(gè)下載任務(wù)的容器窗悯,用來(lái)維護(hù)對(duì)應(yīng)任務(wù)的一個(gè)狀態(tài)区匣。它提供了實(shí)以下兩個(gè)方法,如下:

添加并執(zhí)行一個(gè)下載任務(wù)

  • (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
    options:(SDWebImageDownloaderOptions)options
    progress:(SDWebImageDownloaderProgressBlock)progressBlock
    completed:(SDWebImageDownloaderCompletedBlock)completedBlock;

設(shè)置是否掛起一個(gè)任務(wù)

  • (void)setSuspended:(BOOL)suspended;

除了以上方法蒋院,SDWebImageDownloader提供了一些配置類操作亏钩,比如設(shè)置最大并發(fā)數(shù),設(shè)置Http相關(guān)配置等欺旧。


2. 如何處理一個(gè)異步下載姑丑?

SDWebImageDownloader處理一個(gè)下載任務(wù)很簡(jiǎn)單,就只是把下載任務(wù)添加到并發(fā)隊(duì)列辞友,同時(shí)維護(hù)該下載任務(wù)的一個(gè)狀態(tài)即可栅哀。

1. 維護(hù)任務(wù)狀態(tài)

一個(gè)下載任務(wù)通常具有以下幾個(gè)狀態(tài):1.下載開始,2.下載中称龙,3.下載結(jié)束留拾,4.下載取消。SDWebImageDownloader對(duì)外通過(guò)提供block回調(diào)來(lái)處理下載結(jié)束鲫尊,下載中的一個(gè)狀態(tài)痴柔。具體有以下幾個(gè)block:

typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);

typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData    *data, NSError *error, BOOL finished);

typedef void(^SDWebImageNoParamsBlock)();

SDWebImageDownloader在外部調(diào)用downloadImageWithURL時(shí),通過(guò)被請(qǐng)求URL來(lái)創(chuàng)建一個(gè)字典用來(lái)維護(hù)該任務(wù)的一個(gè)狀態(tài)處理block疫向,在這里竞帽,SDWebImageDownloader對(duì)相同URL請(qǐng)求進(jìn)行了過(guò)濾扛施,即多次請(qǐng)求相同URL任務(wù)只會(huì)被處理一次。

dispatch_barrier_sync(self.barrierQueue, ^{
    BOOL first = NO;
    if (!self.URLCallbacks[url]) {
        self.URLCallbacks[url] = [NSMutableArray new];
        first = YES;
    }

    // Handle single download of simultaneous download request for the same URL
    NSMutableArray *callbacksForURL = self.URLCallbacks[url];
    NSMutableDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    [callbacksForURL addObject:callbacks];
    self.URLCallbacks[url] = callbacksForURL;

    if (first) {
        createCallback();
    }
});

block處理下載中狀態(tài)

SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
dispatch_sync(sself.barrierQueue, ^{
    //獲取當(dāng)前狀態(tài)維護(hù)隊(duì)列
    callbacksForURL = [sself.URLCallbacks[url] copy];
    });
    

for (NSDictionary *callbacks in callbacksForURL) {                                                              dispatch_async(dispatch_get_main_queue(), ^{
        //獲取Url對(duì)應(yīng)的下載處理block
        SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
        if (callback) callback(receivedSize, expectedSize);
    });
}

block處理下載完成狀態(tài)

SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
                                                            dispatch_barrier_sync(sself.barrierQueue, ^{
    callbacksForURL = [sself.URLCallbacks[url] copy];
    if (finished) {
        [sself.URLCallbacks removeObjectForKey:url];
    }
});

for (NSDictionary *callbacks in callbacksForURL) {
    //獲取下載完成處理block
    SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
    if (callback) callback(image, data, error, finished);
}

block處理下載取消狀態(tài)

SDWebImageDownloader *sself = wself;
if (!sself) return;
dispatch_barrier_async(sself.barrierQueue, ^{
    [sself.URLCallbacks removeObjectForKey:url];
});

2. 創(chuàng)建任務(wù)并執(zhí)行

由于SDWebImageDownloader是通過(guò)NSOperation來(lái)處理并發(fā)任務(wù)屹篓,所以實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單。創(chuàng)建一個(gè)NSOperation匙奴,并把它添加到Queue隊(duì)列即可堆巧。在此處,SDWebImageDownloader創(chuàng)建了一個(gè)并發(fā)隊(duì)列泼菌,默認(rèn)的最大并發(fā)數(shù)6谍肤。
SDWebImageDownloaderOperation是下載任務(wù)的具體實(shí)現(xiàn)類,它繼承NSOperation哗伯,且該類通過(guò)Url請(qǐng)求來(lái)進(jìn)行初始化荒揣。在處理隊(duì)列中任務(wù)時(shí),提供了兩種處理方式焊刹,一種是FIFO系任,一種是LIFO。如果采用的是LIFO方式處理虐块,則在把新任務(wù)加到隊(duì)列的同時(shí)俩滥,需要更新一下最后入隊(duì)列的任務(wù),并且該任務(wù)依賴當(dāng)前新加入的任務(wù)贺奠。具體實(shí)現(xiàn)如下:

[wself.downloadQueue addOperation:operation];
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
    [wself.lastAddedOperation addDependency:operation];
    wself.lastAddedOperation = operation;
}

3. 一個(gè)下載任務(wù)具體實(shí)現(xiàn)

SDWebImageDownloaderOperation是下載任務(wù)的具體實(shí)現(xiàn)類霜旧。該類維護(hù)了自生的一個(gè)狀態(tài),一個(gè)NSURLConnection儡率,一個(gè)下載線程挂据,還有具體的下載后數(shù)據(jù)。在NSOperation的start方法中儿普。創(chuàng)建一個(gè)NSURLConnection并且開始這個(gè)connection崎逃,同時(shí),開啟一個(gè)后臺(tái)線程用于下載數(shù)據(jù)箕肃。

- (void)start {
    //初始化當(dāng)前狀態(tài)
    @synchronized (self) {
        if (self.isCancelled) {
            self.finished = YES;
            [self reset];
            return;
        }

        ...
        ...
        
        self.executing = YES;
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
        self.thread = [NSThread currentThread];
    }
    
    //執(zhí)行connection
    [self.connection start];

    if (self.connection) {
        if (self.progressBlock) {
            self.progressBlock(0, NSURLResponseUnknownLength);
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
        });

        //開啟線程
        if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
        }
        else {
            CFRunLoopRun();
        }

        if (!self.isFinished) {
            [self.connection cancel];
            [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]];
        }
    }
    else {
        if (self.completedBlock) {
            self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
        }
    }

由于SDWebImageDownloaderOperation是通過(guò)NSURLConnection來(lái)下載圖片婚脱。所以,具體的下載動(dòng)作都由NSURLConnection的delegate來(lái)處理勺像。

收到http的response時(shí)的回調(diào)障贸,在該回調(diào)里面,首先根據(jù)response返回碼來(lái)判斷請(qǐng)求是否成功吟宦。請(qǐng)求成功篮洁,則通過(guò)respone中文件大小來(lái)創(chuàng)建一個(gè)下載數(shù)據(jù)緩存區(qū)。請(qǐng)求失敗殃姓,則主動(dòng)取消改請(qǐng)求袁波,并且停止后臺(tái)下載線程瓦阐,復(fù)位當(dāng)前任務(wù)狀態(tài)。

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
 //'304 Not Modified' is an exceptional one
    if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) {
        NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
        self.expectedSize = expected;
        if (self.progressBlock) {
            self.progressBlock(0, expected);
        }

        self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
        self.response = response;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self];
        });
    }
    else {
        NSUInteger code = [((NSHTTPURLResponse *)response) statusCode];
        
        //This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
        //In case of 304 we need just cancel the operation and return cached image from the cache.
        if (code == 304) {
            [self cancelInternal];
        } else {
            [self.connection cancel];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
        });

        if (self.completedBlock) {
            self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES);
        }
        CFRunLoopStop(CFRunLoopGetCurrent());
        [self done];
    }
}

收到具體下載數(shù)據(jù)時(shí)候的回調(diào)篷牌,在該回調(diào)里面睡蟋,首先對(duì)下載到的數(shù)據(jù)進(jìn)行緩存,然后根據(jù)當(dāng)前緩存到的數(shù)據(jù)枷颊,來(lái)獲取到圖片的長(zhǎng)和寬戳杀,同時(shí),根據(jù)請(qǐng)求下載的數(shù)據(jù)大小來(lái)做判斷夭苗,如果符合信卡,則生成下載的圖片并回調(diào)給上層。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.imageData appendData:data];

    if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) {
 
        // Get the total bytes downloaded
        const NSInteger totalSize = self.imageData.length;

        // Update the data source, we must pass ALL the data, not just the new bytes
        CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL);

        if (width + height == 0) {
            CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
            if (properties) {
                NSInteger orientationValue = -1;
                CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
                val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
                if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
                val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
                if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
                CFRelease(properties);

                // When we draw to Core Graphics, we lose orientation information,
                // which means the image below born of initWithCGIImage will be
                // oriented incorrectly sometimes. (Unlike the image born of initWithData
                // in connectionDidFinishLoading.) So save it here and pass it on later.
                orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
            }

        }

        if (width + height > 0 && totalSize < self.expectedSize) {
            // Create the image
            CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);

    #ifdef TARGET_OS_IPHONE
            // Workaround for iOS anamorphic image
            if (partialImageRef) {
                const size_t partialHeight = CGImageGetHeight(partialImageRef);
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
                CGColorSpaceRelease(colorSpace);
                if (bmContext) {
                    CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
                    CGImageRelease(partialImageRef);
                    partialImageRef = CGBitmapContextCreateImage(bmContext);
                    CGContextRelease(bmContext);
                }
                else {
                    CGImageRelease(partialImageRef);
                    partialImageRef = nil;
                }
            }
    #endif

    if (partialImageRef) {
        UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
        NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
        UIImage *scaledImage = [self scaledImageForKey:key image:image];
        if (self.shouldDecompressImages) {
            image = [UIImage decodedImageWithImage:scaledImage];
        }else {
            image = scaledImage;
        }
            CGImageRelease(partialImageRef);
            dispatch_main_sync_safe(^{
                    if (self.completedBlock) {
                        self.completedBlock(image, nil, nil, NO);
                    }
                });
            }
        }

        CFRelease(imageSource);
    }

    if (self.progressBlock) {
        self.progressBlock(self.imageData.length, self.expectedSize);
    }
}

請(qǐng)求失敗時(shí)的回調(diào)處理题造,終止后臺(tái)下載線程傍菇,并復(fù)位當(dāng)前請(qǐng)求。

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    @synchronized(self) {
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.thread = nil;
        self.connection = nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
        });
    }

    if (self.completedBlock) {
        self.completedBlock(nil, nil, error, YES);
    }
    self.completionBlock = nil;
    [self done];
}

請(qǐng)求加載完成時(shí)的回調(diào)處理界赔,在該回調(diào)處理中丢习,首先就是停止了后臺(tái)下載線程,復(fù)位當(dāng)前請(qǐng)求仔蝌。然后判斷該請(qǐng)求是否已經(jīng)被緩存了泛领,如果已經(jīng)緩存,則取出緩存數(shù)據(jù)進(jìn)行解碼操作敛惊,生成相應(yīng)圖片并回調(diào)給上層

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
    SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;
    @synchronized(self) {
        CFRunLoopStop(CFRunLoopGetCurrent());
        self.thread = nil;
        self.connection = nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self];
        });
    }
    
    if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {
        responseFromCached = NO;
    }
    
    if (completionBlock) {
        if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) {
            completionBlock(nil, nil, nil, YES);
        } else if (self.imageData) {
            UIImage *image = [UIImage sd_imageWithData:self.imageData];
            NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
            image = [self scaledImageForKey:key image:image];
            
            // Do not force decoding animated GIFs
            if (!image.images) {
                if (self.shouldDecompressImages) {
                    image = [UIImage decodedImageWithImage:image];
                }
            }
            if (CGSizeEqualToSize(image.size, CGSizeZero)) {
                completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES);
            }
            else {
                completionBlock(image, self.imageData, nil, YES);
            }
        } else {
            completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES);
        }
    }
    self.completionBlock = nil;
    [self done];
}

除了以上幾個(gè)比較重要的回調(diào)處理渊鞋。SDWebImageDownloaderOperation還處理了http鑒權(quán),加載等相關(guān)回調(diào)瞧挤。

4. 其他細(xì)節(jié)處理

1. 通知上層應(yīng)用

針對(duì)具體的下載事件锡宋,需要及時(shí)通知上層,SDWebImageDownloaderOperation提供以下幾個(gè)通知來(lái)做到上層同步特恬。

NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification";
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";

2. 后臺(tái)線程的處理

針對(duì)系統(tǒng)后臺(tái)運(yùn)行的處理

 Class UIApplicationClass = NSClassFromString(@"UIApplication");
        BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
        if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
            __weak __typeof__ (self) wself = self;
            UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
            self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
                __strong __typeof (wself) sself = wself;

                if (sself) {
                    [sself cancel];

                    [app endBackgroundTask:sself.backgroundTaskId];
                    sself.backgroundTaskId = UIBackgroundTaskInvalid;
                }
            }];
        }

針對(duì)后臺(tái)下載線程的處理

if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
        }
        else {
            CFRunLoopRun();
        }

總結(jié)

  1. 掌握Manager與operatior的基本實(shí)現(xiàn)方式
  2. 加深NSOperation执俩,NSURLConnection的應(yīng)用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市癌刽,隨后出現(xiàn)的幾起案子役首,更是在濱河造成了極大的恐慌张足,老刑警劉巖趋艘,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吕喘,居然都是意外死亡远荠,警方通過(guò)查閱死者的電腦和手機(jī)矮固,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)譬淳,“玉大人档址,你說(shuō)我怎么就攤上這事盹兢。” “怎么了守伸?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵绎秒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我尼摹,道長(zhǎng)替裆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任窘问,我火速辦了婚禮,結(jié)果婚禮上宜咒,老公的妹妹穿的比我還像新娘惠赫。我一直安慰自己,他們只是感情好故黑,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布儿咱。 她就那樣靜靜地躺著,像睡著了一般场晶。 火紅的嫁衣襯著肌膚如雪混埠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天诗轻,我揣著相機(jī)與錄音钳宪,去河邊找鬼。 笑死扳炬,一個(gè)胖子當(dāng)著我的面吹牛吏颖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恨樟,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼半醉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了劝术?” 一聲冷哼從身側(cè)響起缩多,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎养晋,沒(méi)想到半個(gè)月后衬吆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匙握,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年咆槽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圈纺。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秦忿,死狀恐怖麦射,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灯谣,我是刑警寧澤潜秋,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站胎许,受9級(jí)特大地震影響峻呛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辜窑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一钩述、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧穆碎,春花似錦牙勘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至色徘,卻和暖如春恭金,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褂策。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工横腿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辙培。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓蔑水,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扬蕊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子搀别,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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