12306 半自動(dòng)刷票

*** 說(shuō)明***:
這不是黑科技,并不是自動(dòng)購(gòu)票,請(qǐng)根據(jù)自己的需求使用,建議使用搶票 APP 靠譜一些,360 搶票也比這個(gè)好,至少它能智能識(shí)圖!!!
只是自動(dòng)查詢(xún)你想要的車(chē)次,并自動(dòng)點(diǎn)擊預(yù)訂,自動(dòng)填寫(xiě)用戶(hù)名和密碼,但是圖片驗(yàn)證碼還需要自己點(diǎn)擊(這個(gè)無(wú)解)。
本文先說(shuō)使用方法,再講解。

使用方法

  • 首先,打開(kāi) 12306 的 車(chē)票預(yù)定頁(yè)面

  • 摁 F12 鍵打開(kāi)瀏覽器控制臺(tái)(Chrome 瀏覽為例),選擇 console,如下圖所示:

    console.png

  • 將配置好的代碼粘貼到圖中區(qū)域,按 Enter 鍵回車(chē)就行。可以在 Network 里面看到已經(jīng)在刷了:

log.png
  • 建議運(yùn)行時(shí)關(guān)閉控制臺(tái) 或者 將控制臺(tái)放到右側(cè)之類(lèi),不然小屏幕下登陸框會(huì)看不全。點(diǎn)擊控制臺(tái)右側(cè)的三個(gè)豎點(diǎn)選擇:
    setRight.png

可能的結(jié)果

第一種情況是需要重新登錄一次,這種很常見(jiàn)。用戶(hù)名密碼都自動(dòng)幫你填好了,然后自己再填這個(gè)坑爹的驗(yàn)證碼吧(ˉ▽ˉ;)...,示意圖如下:

needLogin.png

另一種情況是直接跳到購(gòu)買(mǎi)頁(yè)面,你需要自己勾選乘客和點(diǎn)擊提交訂單即可:

noLogin.png

無(wú)論是哪種結(jié)果,都需要重新運(yùn)行代碼。在控制臺(tái)按向上箭頭,再按回車(chē)鍵就行(當(dāng)然重新粘貼也行)。

<br />


配置代碼

先粘代碼:

var WISH = {
        train_date: '2017-01-24', // 乘車(chē)日期
        from_station_telecode: 'HGH', // HGH - 杭州東
        to_station_telecode: 'NXG', // NXG - 南昌西
        purpose_codes: 'ADULT', // ADULT - 成年人,0X00 - 學(xué)生

        station_train_code: [ // 想買(mǎi)的車(chē)次,排前面的優(yōu)先
            'G2365',
            'G1417',
            'G1341'
        ],
        setType: [ // 座位類(lèi)型,不填 - 不限制; yz_num - 硬座; rz_num - 軟座; yw_num - 硬臥; rw_num - 軟臥; gr_num - 高級(jí)軟臥; zy_num - 一等座; ze_num - 二等座; tz_num - 特等座; wz_num - 無(wú)座; qt_num - 其它; swz_num - 商務(wù)座
            "zy_num", // 一等座
            "ze_num", // 二等座
            "wz_num" // 無(wú)座
        ]
        
    },
    USER = {
        name: 'xxx@163.com', // 用戶(hù)名稱(chēng)
        password: 'xxx' // 用戶(hù)密碼
    },
    SEARCH_RATE = 5000, // 刷新頻率,5000 毫秒

    timer = null,
    matchTicket = {},
    availableTicketsMap = {};

/**
 * Ajax
 * @param  {Object}   data     搜索參數(shù)
 * @param  {Function} callback 回調(diào)函數(shù),用于處理返回的數(shù)據(jù)
 */
function queryAjax(data, callback) {
    var ajaxData = {
        'leftTicketDTO.train_date': data.train_date,
        'leftTicketDTO.from_station': data.from_station_telecode,
        'leftTicketDTO.to_station': data.to_station_telecode,
        'purpose_codes': data.purpose_codes
    };

    // log, it's no use for me
    $.ajax({
        type: "GET",
        isTakeParam: false,
        beforeSend: function(xhr) {
            xhr.setRequestHeader("If-Modified-Since", "0");
            xhr.setRequestHeader("Cache-Control", "no-cache");
        },
        url: "/otn/leftTicket/log",
        data: ajaxData,
        timeout: 15000,
        success: function(res) {}
    });

    // query
    $.ajax({
        type: 'GET',
        isTakeParam: false,
        beforeSend: function(xhr) {
            xhr.setRequestHeader('If-Modified-Since', '0');
            xhr.setRequestHeader('Cache-Control', 'no-cache');
        },
        url: '/otn/leftTicket/queryA',
        data: ajaxData,
        timeout: 10000,
        success: function(res) {
            if (res.status) {
                callback(res.data);
            }
        }
    });
}

/**
 * 處理返回的數(shù)據(jù)
 * @param  {Array} data  返回的所有車(chē)次信息
 * @return {Object}      可購(gòu)買(mǎi)的車(chē)次 map
 */
function getAvailableTicketsMap(data) {
    var i = 0,
        ticket = {},
        result = {};

    for (i = 0; i < data.length; i++) {
        ticket = {
            secretStr: data[i].secretStr,
            train_no: data[i].queryLeftNewDTO.train_no,
            start_time: data[i].queryLeftNewDTO.start_time,
            station_train_code: data[i].queryLeftNewDTO.station_train_code,
            to_station_telecode: data[i].queryLeftNewDTO.to_station_telecode,
            from_station_telecode: data[i].queryLeftNewDTO.from_station_telecode
        };

        if (ticket.secretStr !== '' && // or data[i].queryLeftNewDTO.canWebBuy === 'Y'
            ticket.to_station_telecode === WISH.to_station_telecode &&
            ticket.from_station_telecode === WISH.from_station_telecode &&
            hasSiteType(data[i].queryLeftNewDTO) ) {

            result[ticket.station_train_code] = {
                train_no: ticket.train_no,
                secretStr: ticket.secretStr,
                start_time: ticket.start_time,
                station_train_code: ticket.station_train_code,
                to_station_telecode: ticket.to_station_telecode,
                from_station_telecode: ticket.from_station_telecode
            };
        }
    }

    return result;
}

/**
 * 是否有想要的座位
 * @param  {[type]}  queryLeftNewDTO [description]
 * @return {Boolean}                 true-有,false-無(wú)
 */
function hasSiteType( queryLeftNewDTO ) {
    var i = 0,
        wantSetTypeArr = WISH.setType;

    if ( wantSetTypeArr.length === 0 ) {
        return true;
    }

    for (i = 0; i < wantSetTypeArr.length; i++) {
        if ( /有|[1-9]/.test(queryLeftNewDTO[ wantSetTypeArr[i] ]) ) {
            return true;
        }
    }

    return false;
}

/**
 * 匹配想要購(gòu)買(mǎi)的車(chē)次
 * @param  {Object} ticketsMap 可購(gòu)買(mǎi)的所有車(chē)次
 * @return {Boolean}           true-匹配到,false-沒(méi)匹配到
 */
function matchYourTickets(ticketsMap) {
    var i = 0,
        ticket = {},
        wantTrains = WISH.station_train_code;

    console.log(ticketsMap);

    for (i = 0; i < wantTrains.length; i++) {
        ticket = ticketsMap[wantTrains[i]];

        if (typeof ticket !== 'undefined') {
            clearInterval(timer); // 清除定時(shí)器
            reserveTicket(ticket); // 預(yù)訂車(chē)票
            setFormData(USER); // 填寫(xiě)用戶(hù)信息

            return true;
        }
    }

    return false;
}

/**
 * 預(yù)訂車(chē)票
 * @param  {Object} ticket 改車(chē)次信息
 */
function reserveTicket(ticket) {
    checkG1234(ticket.secretStr, ticket.start_time, ticket.train_no, ticket.from_station_telecode, ticket.to_station_telecode);
}

/**
 * 填寫(xiě)表單信息
 * @param {Object} user 用戶(hù)信息對(duì)象
 */
function setFormData(user) {
    $('#username').val(user.name);
    $('#password').val(user.password);
}

/**
 * 初始化
 */
function init() {
    // 一開(kāi)始執(zhí)行就查詢(xún)匹配一次
    queryAjax(WISH, function(data) {
        availableTicketsMap = getAvailableTicketsMap(data);
        matchYourTickets(availableTicketsMap);
    });

    // 定時(shí)器
    timer = setInterval(function() {
        queryAjax(WISH, function(data) {
            availableTicketsMap = getAvailableTicketsMap(data);
            matchYourTickets(availableTicketsMap);
        });
    }, SEARCH_RATE);
}

init();

// 用于下一個(gè)頁(yè)面
// function buy() {
//  $( '#normalPassenger_0' ).click();
//  $( '#submitOrder_id' ).click();
// }

// buy();

需要配置的信息就是代碼開(kāi)頭的 WISH 部分。

<br />


城市 code 查詢(xún)

至于城市 code 怎么查詢(xún),請(qǐng)?jiān)诠俜酱a中查找,代碼很長(zhǎng)很長(zhǎng)...鏈接地址
使用瀏覽器的 Ctrl + F 查找你所想查找的城市,比如 “杭州東”(注意:杭州和杭州東的 code 不一樣),緊接著杭州東后面的字母就是對(duì)應(yīng)的 code :

stationCode.png

建議使用可搜索的城市區(qū)間,在官網(wǎng)測(cè)試過(guò)的;注意大小寫(xiě)是區(qū)分的

<br />


思路分析——搜索功能

好了,接下來(lái)是思路分析。

先填寫(xiě)搜索條件,點(diǎn)擊搜索,查看控制臺(tái) Network 里面的 XHR 記錄,也就是發(fā)送的 Ajax 了:

searchLog.png

可以發(fā)現(xiàn),每次點(diǎn)擊搜索,都會(huì)發(fā)送兩個(gè) Ajax 請(qǐng)求:

  • /otn/leftTicket/log:這個(gè)看名字像是記錄搜索日志,不是很清楚
  • /otn/leftTicket/queryA: 這個(gè)就是查詢(xún)了

兩個(gè) Ajax 的參數(shù)都是一致的:

queryString.png

就是我們所填寫(xiě)的搜索參數(shù),格式化后如下:

{
    'leftTicketDTO.train_date': '2017-01-24', // 出發(fā)日
    'leftTicketDTO.from_station': 'HGH', // 出發(fā)地
    'leftTicketDTO.to_station': 'NXG', // 目的地
    'purpose_codes': 'ADULT' // 普通(成年人)
}

所以我們可以使用這些參數(shù)來(lái)模擬搜索。

Tips:
并且發(fā)現(xiàn)查詢(xún)結(jié)果只和 出發(fā)日期出發(fā)地目的地乘客類(lèi)型(普通、學(xué)生) 有關(guān),和車(chē)次的篩選條件無(wú)關(guān):

不填寫(xiě)篩選條件(返回 92 條數(shù)據(jù))

noFilter.png

填寫(xiě)篩選條件(返回 92 條數(shù)據(jù))
hasFilter.png

所以車(chē)次的過(guò)濾,是在瀏覽器端完成的

<br />


思路分析——預(yù)訂

找個(gè)可以預(yù)訂的車(chē)次,查看 預(yù)定 按鈕信息(就是控制臺(tái)左上角的箭頭,先點(diǎn)擊它,再去點(diǎn)按鈕):

viewEle.png

可以看到使用的是 onClick 事件,執(zhí)行函數(shù)是 checkG1234(順帶多觀察了幾個(gè)可預(yù)定的車(chē)次,發(fā)現(xiàn)預(yù)訂按鈕點(diǎn)擊執(zhí)行的函數(shù)名都叫做 checkG1234):

clickHandleFunction.png

checkG1234 格式化:

checkG1234(
    'c7JA6pPfpAnWCp5RcvN8Ks8WYk68Wv0ZxdytFe8c8vmN64VfPKCIdxXSLTCBd1Gc%2F9zXT5i7gaZy%0ATr4SWbEOeZwttRjRaGMfOHoKAnxoDJwtiEm8Ittvm7BZZu1qaJcZqFqg2EbdT%2FyissJoFkqEzenT%0AA%2F1UTQV%2BHLjKKEM6MT9SsmljBbjFH3SAhSavWoWUzjQlLsWofENBOoRg2Hu%2F%2FxSqKmWoLwQ%2F4Ku%2B%0AOZNWfWel2HJQ1Oqn5obNR5mTv8sNkJGaSCO459N4zSjH5Fnv29Nbw207GxdKQGFCVQ%3D%3D',
    '00:35',
    '56000K429760',
    'HZH',
    'NCG'
)

一共有四個(gè)參數(shù),前三個(gè)不知道是啥,后兩個(gè)是始發(fā)地和目的地。先不管,先去扒扒搜索返回的數(shù)據(jù)。

<br />


思路分析——返回?cái)?shù)據(jù)分析

假設(shè) K4297 在查詢(xún)結(jié)果中對(duì)應(yīng)的數(shù)據(jù)為 data,則將可以將預(yù)訂函數(shù)參數(shù)分解:
找兩條數(shù)據(jù)對(duì)比一下:

{
    "queryLeftNewDTO": {
        "train_no": "56000K429760",
        "station_train_code": "K4297", // 車(chē)次
        "start_station_telecode": "HZH",
        "start_station_name": "杭州",
        "end_station_telecode": "NCG",
        "end_station_name": "南昌",
        "from_station_telecode": "HZH",
        "from_station_name": "杭州",
        "to_station_telecode": "NCG",
        "to_station_name": "南昌",
        "start_time": "00:35",
        "arrive_time": "10:12",
        "day_difference": "0",
        "train_class_name": "",
        "lishi": "09:37",
        "canWebBuy": "Y",
        "lishiValue": "577",
        "yp_info": "0%2BhvfBij8EeRbc3N5OhdLWdJS%2F%2FutFvI",
        "control_train_day": "20300303",
        "start_train_date": "20170124",
        "seat_feature": "W010",
        "yp_ex": "1010",
        "train_seat_feature": "0",
        "train_type_code": "4",
        "start_province_code": "08",
        "start_city_code": "0904",
        "end_province_code": "11",
        "end_city_code": "1104",
        "seat_types": "11",
        "location_code": "H1",
        "from_station_no": "01",
        "to_station_no": "07",
        "control_day": 29,
        "sale_time": "1030",
        "is_support_card": "0",
        "controlled_train_flag": "0",
        "controlled_train_message": "正常車(chē)次,不受控",
        "yz_num": "--", // 硬座
        "rz_num": "--", // 軟座
        "yw_num": "--", // 硬臥
        "rw_num": "--", // 軟臥
        "gr_num": "--", // 高級(jí)軟臥?
        "zy_num": "有", // 一等座
        "ze_num": "有", // 二等座
        "tz_num": "--", // 特等座
        "gg_num": "--", // ?
        "yb_num": "--", // ?
        "wz_num": "--", // 無(wú)座
        "qt_num": "--", // 其它?
        "swz_num": "11" // 商務(wù)座
    },
    "secretStr": "'c7JA6pPfpAnWCp5RcvN8Ks8WYk68Wv0ZxdytFe8c8vmN64VfPKCIdxXSLTCBd1Gc%2F9zXT5i7gaZy%0ATr4SWbEOeZwttRjRaGMfOHoKAnxoDJwtiEm8Ittvm7BZZu1qaJcZqFqg2EbdT%2FyissJoFkqEzenT%0AA%2F1UTQV%2BHLjKKEM6MT9SsmljBbjFH3SAhSavWoWUzjQlLsWofENBOoRg2Hu%2F%2FxSqKmWoLwQ%2F4Ku%2B%0AOZNWfWel2HJQ1Oqn5obNR5mTv8sNkJGaSCO459N4zSjH5Fnv29Nbw207GxdKQGFCVQ%3D%3D",
    "buttonTextInfo": "預(yù)訂"
}

假設(shè)這條數(shù)據(jù)叫做 data,可以發(fā)現(xiàn)如下對(duì)應(yīng)關(guān)系:

'data.secretStr'  =>  'c7JA6pPfpAnWCp5RcvN8Ks8WYk68Wv0ZxdytFe8c8vmN64VfPKCIdxXSLTCBd1Gc%2F9zXT5i7gaZy%0ATr4SWbEOeZwttRjRaGMfOHoKAnxoDJwtiEm8Ittvm7BZZu1qaJcZqFqg2EbdT%2FyissJoFkqEzenT%0AA%2F1UTQV%2BHLjKKEM6MT9SsmljBbjFH3SAhSavWoWUzjQlLsWofENBOoRg2Hu%2F%2FxSqKmWoLwQ%2F4Ku%2B%0AOZNWfWel2HJQ1Oqn5obNR5mTv8sNkJGaSCO459N4zSjH5Fnv29Nbw207GxdKQGFCVQ%3D%3D',
'data.queryLeftNewDTO.start_time'  =>  '00:35',
'data.queryLeftNewDTO.train_no'  =>  '56000K429760',
'data.queryLeftNewDTO.from_station_telecode'  =>  'HZH',
'data.queryLeftNewDTO.to_station_telecode'  =>  'NCG'

好了,點(diǎn)擊預(yù)訂需要的參數(shù)都找全了。

<br />


模擬搜索

這個(gè)很簡(jiǎn)單,定義一個(gè) queryAjax 的方法,傳入搜索參數(shù) data 和回調(diào)函數(shù) callback:

/**
 * Ajax
 * @param  {Object}   data     搜索參數(shù)
 * @param  {Function} callback 回調(diào)函數(shù),用于處理返回的數(shù)據(jù)
 */
function queryAjax( data, callback ) {
    var ajaxData = {
        'leftTicketDTO.train_date': data.train_date,
        'leftTicketDTO.from_station': data.from_station_telecode,
        'leftTicketDTO.to_station': data.to_station_telecode,
        'purpose_codes': data.purpose_codes
    };

    // log, it's no use for me
    $.ajax({
        type: "GET",
        isTakeParam: false,
        beforeSend: function( xhr ) {
            xhr.setRequestHeader("If-Modified-Since", "0");
            xhr.setRequestHeader("Cache-Control", "no-cache");
        },
        url: "/otn/leftTicket/log",
        data: ajaxData,
        timeout: 15000,
        success: function( res ) {}
    });

    // query
    $.ajax({
        type: 'GET',
        isTakeParam: false,
        beforeSend: function( xhr ) {
            xhr.setRequestHeader('If-Modified-Since', '0');
            xhr.setRequestHeader('Cache-Control', 'no-cache');
        },
        url: '/otn/leftTicket/queryA',
        data: ajaxData,
        timeout: 10000,
        success: function( res ) {
            if ( res.status ) {
                callback( res.data );
            }
        }
    });
}

<br />


處理返回的數(shù)據(jù)

為了便于后期查找出我們需要的,并且可購(gòu)買(mǎi)的車(chē)次,這里使用鍵值對(duì)保存可購(gòu)買(mǎi)的車(chē)次信息:

/**
 * 處理返回的數(shù)據(jù)
 * @param  {Array} data  返回的所有車(chē)次信息
 * @return {Object}      可購(gòu)買(mǎi)的車(chē)次 map
 */
function getAvailableTicketsMap(data) {
    var i = 0,
        ticket = {},
        result = {};

    for (i = 0; i < data.length; i++) {
        ticket = {
            secretStr: data[i].secretStr,
            train_no: data[i].queryLeftNewDTO.train_no,
            start_time: data[i].queryLeftNewDTO.start_time,
            station_train_code: data[i].queryLeftNewDTO.station_train_code,
            to_station_telecode: data[i].queryLeftNewDTO.to_station_telecode,
            from_station_telecode: data[i].queryLeftNewDTO.from_station_telecode
        };

        if (ticket.secretStr !== '' && // or data[i].queryLeftNewDTO.canWebBuy === 'Y'
            ticket.to_station_telecode === WISH.to_station_telecode &&
            ticket.from_station_telecode === WISH.from_station_telecode &&
            hasSiteType(data[i].queryLeftNewDTO) ) {

            result[ticket.station_train_code] = {
                train_no: ticket.train_no,
                secretStr: ticket.secretStr,
                start_time: ticket.start_time,
                station_train_code: ticket.station_train_code,
                to_station_telecode: ticket.to_station_telecode,
                from_station_telecode: ticket.from_station_telecode
            };
        }
    }

    return result;
}

Tips
分析發(fā)現(xiàn),如果車(chē)次可以購(gòu)買(mǎi),那么 secretStr 的值不為空,并且queryLeftNewDTO.canWebBuy = 'Y'

<br />


匹配座位類(lèi)型

長(zhǎng)度為 0 就是不限制,有賣(mài)就買(mǎi),返回 true;不為 0 就是遍歷期望的類(lèi)型數(shù)組,找到有匹配的就返回 true,否則為 false:

/**
 * 是否有想要的座位
 * @param  {[type]}  queryLeftNewDTO [description]
 * @return {Boolean}                 true-有,false-無(wú)
 */
function hasSiteType( queryLeftNewDTO ) {
    var i = 0,
        wantSetTypeArr = WISH.setType;

    if ( wantSetTypeArr.length === 0 ) {
        return true;
    }

    for (i = 0; i < wantSetTypeArr.length; i++) {
        if ( /有|[1-9]/.test(queryLeftNewDTO[ wantSetTypeArr[i] ]) ) {
            return true;
        }
    }

    return false;
}

<br />


匹配需要的車(chē)次

接下來(lái)就是匹配我們需要的車(chē)次了,需要傳入上述所有可購(gòu)買(mǎi)的車(chē)次:

/**
 * 匹配想要購(gòu)買(mǎi)的車(chē)次
 * @param  {Object} ticketsMap 可購(gòu)買(mǎi)的所有車(chē)次
 * @return {Boolean}           true-匹配到,false-沒(méi)匹配到
 */
function matchYourTickets( ticketsMap ) {
    var i          = 0,
        ticket     = {},
        wantTrains = WISH.station_train_code;

    console.log( ticketsMap );

    for (i = 0; i < wantTrains.length; i++) {
        ticket = ticketsMap[ wantTrains[i] ];

        if ( typeof ticket !== 'undefined' ) {
            clearInterval( timer ); // 清除定時(shí)器
            reserveTicket( ticket ); // 預(yù)訂車(chē)票
            setFormData( USER ); // 填寫(xiě)用戶(hù)信息

            return true;
        }
    }

    return false;
}

<br />


預(yù)訂車(chē)票

就是調(diào)預(yù)訂的那個(gè)方法,傳入需要的參數(shù)而已:

/**
 * 預(yù)訂車(chē)票
 * @param  {Object} ticket 改車(chē)次信息
 */
function reserveTicket( ticket ) {
    checkG1234(ticket.secretStr, ticket.start_time,ticket.train_no, ticket.from_station_telecode, ticket.to_station_telecode);
}

<br />


填寫(xiě)用戶(hù)信息

這個(gè)就是查看登陸表單的結(jié)果了,選擇器就直接寫(xiě)死了:

/**
 * 填寫(xiě)表單信息
 * @param {Object} user 用戶(hù)信息對(duì)象
 */
function setFormData( user ) {
    $( '#username' ).val( user.name );
    $( '#password' ).val( user.password );
}

Tips:
點(diǎn)擊查看元素可以看到表單的用戶(hù)名、用戶(hù)密碼輸入框的 id 名稱(chēng)分別為 'username'、'password'

form.png

圖片驗(yàn)證碼無(wú)解,不知道搶票軟件咋弄的,有內(nèi)部接口?貌似移動(dòng)端有獨(dú)立的接口,下次看看。

<br />


初始化

寫(xiě)個(gè)定時(shí)器自動(dòng)查詢(xún)匹配:

/**
 * 初始化
 */
function init() {
    // 一開(kāi)始執(zhí)行就查詢(xún)匹配一次
    queryAjax(WISH, function( data ) {
        availableTicketsMap = getAvailableTicketsMap( data );
        matchYourTickets( availableTicketsMap );
    });

    // 定時(shí)器
    timer = setInterval(function() {
        queryAjax(WISH, function( data ) {
            availableTicketsMap = getAvailableTicketsMap( data );
            matchYourTickets( availableTicketsMap );
        });
    }, SEARCH_RATE);
}

<br />


總結(jié)

自己開(kāi)雙屏,一邊寫(xiě)代碼一邊看著,掛著刷還行,當(dāng)然手機(jī) App 更好吧。春運(yùn)的票好難搶?zhuān)裉焐抖紱](méi)搶到,就看明天了。搶不到就拿這個(gè)掛個(gè)一天....

Good Night ~ ~ o(* ̄▽?zhuān)?)ブ
—— 2016/12/26 By Live

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,933評(píng)論 18 139
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 6,523評(píng)論 0 17
  • 《裕語(yǔ)言》速成開(kāi)發(fā)手冊(cè)3.0 官方用戶(hù)交流:iApp開(kāi)發(fā)交流(1) 239547050iApp開(kāi)發(fā)交流(2) 10...
    葉染柒丶閱讀 27,825評(píng)論 5 19
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,333評(píng)論 25 708
  • 坐在登機(jī)口處,聽(tīng)到廣播里一遍又一遍的晚點(diǎn)通知,很是淡定地掏出手機(jī)打開(kāi)APP開(kāi)始寫(xiě)作。 恰逢高考遇上端午,我很偶然地...
    心之音閱讀 542評(píng)論 5 1