基于AndroidAsync框架搭建android http server

使用的框架是AndroidAsync
項目地址:https://github.com/koush/AndroidAsync/tree/master/AndroidAsync/src/com/koushikdutta/async
參考的文章是:
http://programminglife.io/android-http-server-with-androidasync/
https://github.com/reneweb/AndroidAsyncSocketExamples
WifiTransfer

項目中的代碼如下:

package com.duotin.car.widget.wifiTransfer;

import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;


import com.duotin.car.BaseApplication;
import com.duotin.car.R;
import com.duotin.car.constant.Constants;
import com.duotin.car.scan.AlbumManager;
import com.duotin.car.scan.ResultFile;
import com.duotin.car.scan.ResultFolder;
import com.duotin.car.util.FileUtils;
import com.duotin.car.util.Log;
import com.duotin.car.util.Tool;
import com.duotin.lib.api2.Resource;
import com.duotin.lib.api2.model.Track;
import com.duotin.lib.util.AsyncTask;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.AsyncServerSocket;
import com.koushikdutta.async.AsyncSocket;
import com.koushikdutta.async.ByteBufferList;
import com.koushikdutta.async.DataEmitter;
import com.koushikdutta.async.Util;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.DataCallback;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.AsyncHttpRequest;
import com.koushikdutta.async.http.AsyncHttpResponse;
import com.koushikdutta.async.http.body.MultipartFormDataBody;
import com.koushikdutta.async.http.body.Part;
import com.koushikdutta.async.http.callback.HttpConnectCallback;
import com.koushikdutta.async.http.server.AsyncHttpServer;
import com.koushikdutta.async.http.server.AsyncHttpServerRequest;
import com.koushikdutta.async.http.server.AsyncHttpServerRequestImpl;
import com.koushikdutta.async.http.server.AsyncHttpServerResponse;
import com.koushikdutta.async.http.server.HttpServerRequestCallback;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * * Created by hongjunmin on 15/8/26
 *
 * @author hongjunmin
 */
public class WifiTransferServerHolder {
    private static final String TEXT_CONTENT_TYPE = "text/html;charset=utf-8";
    private static final String CSS_CONTENT_TYPE = "text/css;charset=utf-8";
    private static final String BINARY_CONTENT_TYPE = "application/octet-stream";
    private static final String JS_CONTENT_TYPE = "application/javascript";
    private static final String PNG_CONTENT_TYPE = "application/x-png";
    private static final String WOFF_CONTENT_TYPE = "application/x-font-woff";
    private static final String TTF_CONTENT_TYPE = "application/x-font-truetype";
    private static final String SVG_CONTENT_TYPE = "image/svg+xml";
    private static final String EOT_CONTENT_TYPE = "image/vnd.ms-fontobject";
    private static final String MP3_CONTENT_TYPE = "audio/mp3";
    private static final String MP4_CONTENT_TYPE = "video/mpeg4";
    public static final int AUDIO_FILE_DURATION_THRESHOLD_IN_SECOND = 600;
    public static final String MY_IMPORTED_AUDIO = "導(dǎo)入的音頻";
    public static final String MY_IMPORTED_MUSIC = "導(dǎo)入的音樂";

    private static final String TAG = "WifiTransferServerHolder";
    private static final int DEFAULT_SOCKET_PORT = 34510;
    private AsyncHttpServer mHttpServer;
    private String defaultDirectory;
    private WifiTransferAdapter mWifiTransferAdapter;
    private AsyncServer mAsyncServer = AsyncServer.getDefault();


    private static final int BufferSize = 8190;

    public WifiTransferServerHolder(WifiTransferAdapter wifiTransferAdapter) {
        mWifiTransferAdapter = wifiTransferAdapter;
    }

    public void setUp() {
        mHttpServer = new AsyncHttpServer();
        AsyncServerSocket asyncServerSocket = mHttpServer.listen(mAsyncServer, DEFAULT_SOCKET_PORT);
        mHttpServer.post("/upload", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
                final MultipartFormDataBody body = (MultipartFormDataBody) request.getBody();
                final FileUploadHolder fileUploadHolder = new FileUploadHolder();
                body.setMultipartCallback(new MultipartFormDataBody.MultipartCallback() {
                    @Override
                    public void onPart(final Part part) {
                        if (part.isFile()) {
                            body.setDataCallback(new DataCallback() {
                                @Override
                                public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
                                    if (fileUploadHolder.getFileOutPutStream() != null)
                                    //已經(jīng)開始傳輸文件
                                    {
                                        try {
                                            fileUploadHolder.getFileOutPutStream().write(bb.getAllByteArray());
                                            mWifiTransferAdapter.onRecievingFile();
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                        bb.recycle();
                                    }
                                }
                            });
                        } else {
                            if (body.getDataCallback() == null) {
                                body.setDataCallback(new DataCallback() {
                                    @Override
                                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
                                        String fileName = getFileNameFromHttpByteBufferList(bb);
                                        //還沒設(shè)置文件名
                                        if (TextUtils.isEmpty(fileUploadHolder.getFileName()) && !TextUtils.isEmpty(fileName)) {
                                            generateReceivedFileAndFileStream(fileUploadHolder, fileName);
                                            mWifiTransferAdapter.onRecievingFile();
                                        }
                                    }
                                });
                            }
                        }
                        if (!TextUtils.isEmpty(part.getFilename())) {
                            if (!Track.isProgramFileName(part.getFilename())) {
                                mWifiTransferAdapter.formateNotsupported(part.getFilename());
                            }
                        }
                    }
                });
                request.setEndCallback(new CompletedCallback() {
                    @Override
                    public void onCompleted(Exception ex) {
                        response.send(new JSONObject());
                        if (fileUploadHolder.getFileOutPutStream() != null) {
                            try {
                                fileUploadHolder.getFileOutPutStream().close();
                                if (fileUploadHolder.getRecievedFile() != null && fileUploadHolder.getRecievedFile().exists()) {
                                    String filePath = fileUploadHolder.getRecievedFile().getPath();
                                    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                                    try {
                                        retriever.setDataSource(filePath);
                                    } catch (Exception e) {
                                        try {
                                            FileInputStream fI = new FileInputStream(filePath);
                                            FileDescriptor fd = fI.getFD();
                                            retriever.setDataSource(fd);
                                        } catch (Exception e1) {
                                            e1.printStackTrace();
                                        }
                                    } finally {
                                        mWifiTransferAdapter.fileReceived(fileUploadHolder.getFileName());
                                        locateProgramAndReadyToSync(retriever, fileUploadHolder.getFileName());
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
            }
        });
        mHttpServer.get("/wf_images/.*", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
                sendResources(request, response);
            }
        });
        mHttpServer.get("/wf_resources/.*", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
                sendResources(request, response);
            }
        });
        mHttpServer.get("/wf_files/.*", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
                sendResources(request, response);
            }
        });
        mHttpServer.get("/list", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {

                Log.d(TAG, request.getPath());
                Map<String, String> headMap = new HashMap<>();
                headMap.put("Expires:", "-1");
                headMap.put("Cache-Control:", "no-cache");
                headMap.put("Pragma:", "no-cache");
                printHead(response, TEXT_CONTENT_TYPE, headMap);
                response.send(getProgramFileNames());
            }
        });

        mHttpServer.get("/", new HttpServerRequestCallback() {
            @Override
            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
                Log.d(TAG, request.getPath());
                try {
                    response.send(Resource.assetGet(BaseApplication.appContext, "wifiImport.html"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        mHttpServer.get("/temp/.*?", new HttpServerRequestCallback() {
            @Override
            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
                Log.d(TAG, request.getPath());
                Map<String, String> headMap = new HashMap<>();
                headMap.put("Expires:", "-1");
                headMap.put("Cache-Control:", "no-cache");
                headMap.put("Pragma:", "no-cache");
                headMap.put("Connection:", "keep-alive");
                printHead(response, TEXT_CONTENT_TYPE, headMap);
                try {
                    String statusLine = ((AsyncHttpServerRequestImpl) (request)).getStatusLine();
                    String[] parts = statusLine.split(" ");
                    String fullPath = parts[1];
                    fullPath = fullPath.replace("%20", " ");
                    String resourceName = fullPath.substring(fullPath.lastIndexOf("/") + 1);
                    if (resourceName.endsWith("?")) {
                        resourceName = resourceName.substring(0, resourceName.length() - 1);
                    }
                    if (!TextUtils.isEmpty(getContentTypeByResourceName(resourceName))) {
                        response.setContentType(getContentTypeByResourceName(resourceName));
                    }
                    String path = request.getMatcher().replaceAll("");
                    String storagePath = "/storage/emulated/0/";
                    File file = new File(storagePath, path);
                    FileInputStream ex = new FileInputStream(file);
                    response.sendStream(ex, ex.available());
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        });
        mHttpServer.setErrorCallback(new CompletedCallback() {
            @Override
            public void onCompleted(Exception ex) {
                Log.dMultiString("mHttpServer ErrorCallback onCompleted");
            }
        });
        if (asyncServerSocket != null) {
            mWifiTransferAdapter.setIpString(asyncServerSocket.getLocalPort());
        } else {
            mWifiTransferAdapter.setIpString(0);
        }

    }

    private void generateReceivedFileAndFileStream(FileUploadHolder fileUploadHolder, String fileName) {
        //取到文件名函卒,生成目標(biāo)文件和路徑
        fileUploadHolder.setFileName(fileName);
        File exStorage = Environment.getExternalStorageDirectory();
        defaultDirectory = BaseApplication.getWifiMusicImportPath();
        if (TextUtils.isEmpty(defaultDirectory)) {
            mWifiTransferAdapter.showToast(R.string.no_external_cannot_import);
            return;
        }
        StringBuilder dir = new StringBuilder();
        dir.append(defaultDirectory);
        if (!dir.toString().contains(exStorage.getPath()))
            dir.insert(0, exStorage.getPath());
        File dirFile;
        dirFile = new File(dir.toString()); // convert spaces appropriately
        if (!dirFile.exists() || !dirFile.isDirectory()) // catch issues in the directory path
        {
            dir.replace(0, dir.length(), defaultDirectory); // replace it with defaultDirectory if invalid
            dirFile = new File(dir.toString());
        }
        File recievedFile = new File(dirFile, fileUploadHolder.getFileName());
        fileUploadHolder.setRecievedFile(recievedFile);
        BufferedOutputStream fs = null;
        try {
            fs = new BufferedOutputStream(new FileOutputStream(recievedFile));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        fileUploadHolder.setFileOutPutStream(fs);
    }

    private String getProgramFileNames() {
        List<File> importedProgramFiles = new ArrayList<>();
        importedProgramFiles.addAll(getFilesSortedByLastModified(BaseApplication.getWifiAudioImportPath()));
        importedProgramFiles.addAll(getFilesSortedByLastModified(BaseApplication.getWifiMusicImportPath()));
        JSONArray jsonArray = new JSONArray();
        for (File file : importedProgramFiles) {
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("path", file.getAbsolutePath().toString());
                jsonObject.put("name", file.getName());
                jsonObject.put("size", file.length());
                jsonArray.put(jsonObject);
            } catch (Exception e) {

            }
        }
        return jsonArray.toString();
    }

    private List<File> getFilesSortedByLastModified(String path) {
        List<File> importedAudioFiles = new ArrayList<>();
        File importedAudioFolder = new File(path);
        if (importedAudioFolder.exists()) {
            File[] importedAudioArray = importedAudioFolder.listFiles();
            if (!Tool.isEmpty(importedAudioArray)) {
                importedAudioFiles = Arrays.asList(importedAudioArray);
                Collections.sort(importedAudioFiles,
                        new Comparator<File>() {
                            @Override
                            public int compare(File o1,
                                               File o2) {
                                return (int) (o1.lastModified() - o2.lastModified());
                            }
                        });
            }

        }
        return importedAudioFiles;
    }

    private void sendResources(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {
        try {
            String statusLine = ((AsyncHttpServerRequestImpl) (request)).getStatusLine();
            String[] parts = statusLine.split(" ");
            String fullPath = parts[1];
            fullPath = fullPath.replace("%20", " ");
            String resourceName = fullPath.substring(fullPath.lastIndexOf("/") + 1);

            if (resourceName.endsWith("?")) {
                resourceName = resourceName.substring(0, resourceName.length() - 1);
            }
            if (!TextUtils.isEmpty(getContentTypeByResourceName(resourceName))) {
                response.setContentType(getContentTypeByResourceName(resourceName));
            }
            BufferedInputStream bInputStream = new BufferedInputStream(BaseApplication.appContext.getAssets().open(resourceName));
            response.sendStream(bInputStream, bInputStream.available());
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

    private String getContentTypeByResourceName(String resourceName) {
        if (resourceName.endsWith(".css")) {
            return CSS_CONTENT_TYPE;
        } else if (resourceName.endsWith(".js")) {
            return JS_CONTENT_TYPE;
        } else if (resourceName.endsWith(".swf")) {
            return BINARY_CONTENT_TYPE;
        } else if (resourceName.endsWith(".png")) {
            return PNG_CONTENT_TYPE;
        } else if (resourceName.endsWith(".woff")) {
            return WOFF_CONTENT_TYPE;
        } else if (resourceName.endsWith(".ttf")) {
            return TTF_CONTENT_TYPE;
        } else if (resourceName.endsWith(".svg")) {
            return SVG_CONTENT_TYPE;
        } else if (resourceName.endsWith(".eot")) {
            return EOT_CONTENT_TYPE;
        } else if (resourceName.endsWith(".mp3")) {
            return MP3_CONTENT_TYPE;
        } else if (resourceName.endsWith(".mp4")) {
            return MP4_CONTENT_TYPE;
        }
        return "";
    }

    private void locateProgramAndReadyToSync(MediaMetadataRetriever retriever, String fileName) {
        String folderName = BaseApplication.getWifiMusicImportPath();
        String albumName = MY_IMPORTED_MUSIC;
        String duration = "0";
        Constants.TrackType trackType = Constants.TrackType.LOCAL;
        int albumId = MY_IMPORTED_MUSIC_ALBUM_ID;
        Log.dMultiString("synchronized (SingleSocketConnection.class) { 903");
        try {
            duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            long durationInmillisec = Long.parseLong(duration);
            long durationInsec = durationInmillisec / 1000;
            if (durationInsec > AUDIO_FILE_DURATION_THRESHOLD_IN_SECOND) {
                FileUtils.moveFile(BaseApplication.getWifiMusicImportPath(), fileName, BaseApplication.getWifiAudioImportPath());
                folderName = BaseApplication.getWifiAudioImportPath();
                albumName = MY_IMPORTED_AUDIO;
                trackType = Constants.TrackType.LOCAL;
                albumId = MY_IMPORTED_AUDIO_ALBUM_ID;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readtToSync(folderName, fileName, duration, albumName, trackType, albumId);
        }
    }

    private void readtToSync(String folderName, String fileName, String duration, String albumName, Constants.TrackType trackType, int albumId) {
        if (TextUtils.isEmpty(folderName) || TextUtils.isEmpty(fileName) || TextUtils.isEmpty(albumName))
            return;
        File folder = new File(folderName);
        Map<String, String> fileNamesAndDurations = new HashMap<>();
        fileNamesAndDurations.put(fileName, duration);
        AutoAddResultFoler autoAddResultFoler = new AutoAddResultFoler(folder, folderName, fileNamesAndDurations, albumName, trackType, albumId);
        new ImportProgramesAsynTask().execute(autoAddResultFoler);
    }

    public void stop() {
        mHttpServer.stop();
        mAsyncServer.stop();
    }

    static class ImportProgramesAsynTask extends AsyncTask<ResultFolder, String, String> {

        @Override
        protected String doInBackground(ResultFolder... params) {
            ResultFolder resultFolder = params[0];
            if (resultFolder != null)
                AlbumManager.storeFolder(resultFolder);
            return null;
        }
    }

    class AutoAddResultFoler extends ResultFolder {

        public AutoAddResultFoler(File folderFile, String folderName, Map<String, String> fileNamesAndDurations, String albumName, Constants.TrackType trackType, int albumId) {
            super(folderFile);
            if (folderFile.exists() && !Tool.isEmpty(fileNamesAndDurations)) {
                setId(albumId);
                setSource(Constants.TrackSource.LOCAL_WIFI_IMPORT);
                setTrackType(trackType);
                setAlbumName(albumName);
                for (Map.Entry<String, String> entry : fileNamesAndDurations.entrySet()) {
                    File recievedFile = new File(folderName + entry.getKey());
                    if (recievedFile.exists()) {
                        ResultFile resultFile = new ResultFile(recievedFile);
                        resultFile.setToAdd(true);
                        resultFile.setSource(Constants.TrackSource.LOCAL_WIFI_IMPORT);
                        resultFile.setDuration(com.duotin.lib.api2.util.StringUtils.formatTime(entry.getValue()));
                        resultFile.setStatus(Constants.TrackState.DOWNLOAD_SUCCESSFUL);
                        addResultFile(resultFile);
                    }
                }
            }
        }

    }


    class FileUploadHolder {
        private String fileName;

        public File getRecievedFile() {
            return recievedFile;
        }

        public void setRecievedFile(File recievedFile) {
            this.recievedFile = recievedFile;
        }

        private File recievedFile;

        public BufferedOutputStream getFileOutPutStream() {
            return fileOutPutStream;
        }

        public void setFileOutPutStream(BufferedOutputStream fileOutPutStream) {
            this.fileOutPutStream = fileOutPutStream;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public void reset() {
            fileName = null;
            fileOutPutStream = null;
        }

        private BufferedOutputStream fileOutPutStream;
    }

    private String getFileNameFromHttpByteBufferList(ByteBufferList bb) {
        String s = bb.readString();
        Log.dMultiString(s);
        if (Track.isProgramFileName(s)) {
            return s;
        } else {
            if (s.length() > 2) {
                s = s.substring(0, s.length() - 2);
                if (Track.isProgramFileName(s)) {
                    return s;
                }
            }
        }
        return null;
    }

    private void printHead(AsyncHttpServerResponse response, String contentType, Map<String, String> customHeads) {
        response.setContentType(contentType);
        if (!Tool.isEmpty(customHeads)) {
            for (Map.Entry<String, String> entry : customHeads.entrySet()) {
                response.getHeaders().set(entry.getKey(), entry.getValue());
            }
        }
    }
}

代碼中很多是業(yè)務(wù)谷扣,所以大家主要是參照setUp方法和get,post是怎么設(shè)置的谱仪。
另:
下載的jar包是2.1.6的。但用在項目中遇到
問題一:有亂碼否彩。解決方案:項目里的 Charsets.US_ASCII 都改成了 Charsets.UTF_8
問題二: 從客戶端獲取數(shù)據(jù)不完整疯攒。發(fā)現(xiàn)是LineEmitter 類的onDataAvailable(DataEmitter emitter, ByteBufferList bb)內(nèi)的代碼走不通。就把原來的:
@Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { while (bb.remaining() > 0) { byte b = bb.get(); if (b == '\n') { assert mLineCallback != null; mLineCallback.onStringAvailable(data.toString()); data = new StringBuilder(); return; } else { data.append((char)b); } } }
改成了:
@Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { String s = bb.readString(); if (s.indexOf("\n") > 0) mLineCallback.onStringAvailable(s); }}
就OK了
由于我用的是jar包的引用方式列荔,所以是依照上面改動原項目后導(dǎo)出了jar包卸例,導(dǎo)jar包方法:http://blog.csdn.net/u012319317/article/details/48133463
你可以直接下載我導(dǎo)出的jar包:http://download.csdn.net/detail/u012319317/9408485

獲取的結(jié)果中,中文亂碼 更簡潔的方案

另:
推薦一篇NIO科普文:
Java NIO:淺析I/O模型

更多參考:
AndroidAsync方案:
An HTTP server inside your Android application using AndroidAsync
This project includes a few examples on how to create different types of sockets using AndroidAsync. It includes examples for a TCP client/server, TCP client with SSL and UDP client/server.
WifiTransfer

其他方案:
AndServer
android-http-server
nanohttpd

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肌毅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姑原,更是在濱河造成了極大的恐慌悬而,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锭汛,死亡現(xiàn)場離奇詭異笨奠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)唤殴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門般婆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朵逝,你說我怎么就攤上這事蔚袍。” “怎么了配名?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵啤咽,是天一觀的道長。 經(jīng)常有香客問我渠脉,道長宇整,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任芋膘,我火速辦了婚禮鳞青,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘为朋。我一直安慰自己臂拓,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布潜腻。 她就那樣靜靜地躺著埃儿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪融涣。 梳的紋絲不亂的頭發(fā)上童番,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天精钮,我揣著相機(jī)與錄音,去河邊找鬼剃斧。 笑死轨香,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幼东。 我是一名探鬼主播臂容,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼根蟹!你這毒婦竟也來了脓杉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤简逮,失蹤者是張志新(化名)和其女友劉穎球散,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體散庶,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蕉堰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悲龟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屋讶。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖须教,靈堂內(nèi)的尸體忽然破棺而出皿渗,到底是詐尸還是另有隱情,我是刑警寧澤没卸,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布羹奉,位于F島的核電站,受9級特大地震影響约计,放射性物質(zhì)發(fā)生泄漏诀拭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一煤蚌、第九天 我趴在偏房一處隱蔽的房頂上張望耕挨。 院中可真熱鬧,春花似錦尉桩、人聲如沸筒占。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翰苫。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奏窑,已是汗流浹背导披。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留埃唯,地道東北人撩匕。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像墨叛,于是被迫代替她去往敵國和親止毕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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

  • afinalAfinal是一個android的ioc漠趁,orm框架 https://github.com/yangf...
    passiontim閱讀 15,399評論 2 45
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理扁凛,服務(wù)發(fā)現(xiàn),斷路器闯传,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 尊敬的鄒老師令漂,明天就是9月10日教師節(jié)了。這篇日記是我送給您的教師節(jié)禮物丸边。 老師,您每天都會換各種各樣荚孵,好看的衣服...
    彥彥公主閱讀 388評論 0 1
  • 知乎里有人提了一個問題:專家級的用戶和普通用戶看待一個產(chǎn)品的感覺是否一樣的收叶?他還能從普通用戶的角度去看待產(chǎn)...
    Smart熊大閱讀 332評論 0 1