Android APP 定時提醒

前言
近期研究了下APP中實(shí)現(xiàn)定時提醒功能。幾經(jīng)周折算是產(chǎn)出了一個方案乎莉。這絕對不是最優(yōu)的方案琢歇,但起碼是可用的、相對簡單穩(wěn)定的梦鉴,希望對大家的實(shí)際開發(fā)工作有所幫助李茫。喜歡探討Android開發(fā)技術(shù)的同學(xué)可以加學(xué)習(xí)小組QQ群: 193765960

版權(quán)歸作者所有肥橙,如有轉(zhuǎn)發(fā)魄宏,請注明文章出處:http://www.reibang.com/u/d43d948bef39

在實(shí)現(xiàn)定時提醒的過程中,前前后后考慮過定時推送存筏、系統(tǒng)鬧鐘宠互、本地定時系統(tǒng)日歷的方案。具體的情況將分別簡單說一下椭坚。

最終技術(shù)選型:系統(tǒng)日歷

1. 服務(wù)器推送

比如京東的Android端APP予跌,經(jīng)過觀察,其走的是后臺推送的方案善茎。
這個方案有個前提是:你的APP必須高比幔活,京東作為超級APP,無論從技術(shù)上還是和手機(jī)廠商合作上烁焙,其焙叫希活方案肯定沒得說,推送服務(wù)的可到達(dá)率也毋庸置疑骄蝇。
假如膳殷,你所開發(fā)的APP可以有穩(wěn)定的高保活方案九火,走后臺推送還是不錯的赚窃。畢竟,app接收到推送通知后,可做的事情太多了,用戶體驗(yàn)當(dāng)然是很好的蛇数。

但是,假如你的APP沒有做到或做過可靠的長時間高焙又剩活,那么震叙,這個方案是不推薦的掀鹅。APP死掉了,手機(jī)收不到推送是沒有任何意義的媒楼。

(我的理解可能不對乐尊,假如京東的工程師們看到了或者對高保活有靠譜方案的同學(xué)划址,還請多都的賜教扔嵌。)

2. 本地定時

本地定時服務(wù),面臨和推送同樣的問題夺颤,怎么讓服務(wù)殺不死可以監(jiān)聽到定時痢缎。這里不多說了。

3. 系統(tǒng)鬧鐘

我開始是使用的系統(tǒng)鬧鐘世澜,本來打算的挺好:設(shè)置好定時的鬧鐘独旷,然后通過APP提前在清單文件中注冊好的靜態(tài)BroadCastReceiver來監(jiān)聽鬧鐘的系統(tǒng)廣播×攘眩可是實(shí)驗(yàn)發(fā)現(xiàn)嵌洼,這個方案是走不通的或者是我走的姿勢不對?

  • 第一:APP調(diào)用AlarmMannager來設(shè)定的定時是綁定了APP的封恰。什么意思麻养?意思就是,你的app掛了的話诺舔,app之前設(shè)置的定時鬧鐘也都被系統(tǒng)清理掉了鳖昌。
  • 第二:是誰告訴我說通過清單文件靜態(tài)注冊的廣播接收者在APP掛了之后還在系統(tǒng)中繼續(xù)存活監(jiān)聽廣播來备畦?坑我不淺啊。

可能是我走路姿勢不對遗遵?反正這條路在我嘗試了一番之后也被我給斃掉了萍恕。

這是我從網(wǎng)上看到的一篇鬧鐘的實(shí)現(xiàn)方案:http://www.reibang.com/p/fdb4e8c009b7,嘗試了下逸嘀,發(fā)現(xiàn)不管用车要,而且看作者使用的方法,可能針對的安卓系統(tǒng)版本較早崭倘。

貼一下我當(dāng)初研究鬧鐘方案時參考的文章:《關(guān)于Android中設(shè)置鬧鐘的相對比較完善的解決方案》

4. 系統(tǒng)日歷

通過app中設(shè)定系統(tǒng)日歷的日歷事件翼岁,并對日歷事件設(shè)置提醒。不論app是否存活司光,提醒的時間到了琅坡,系統(tǒng)日歷總能按時的彈出提醒,唯一的問題是残家,點(diǎn)擊日歷的提醒榆俺,會進(jìn)入系統(tǒng)日歷的日歷事件界面,而無法直接喚醒APP并跳轉(zhuǎn)到相關(guān)界面的坞淮;系統(tǒng)日歷也是沒有響應(yīng)的廣播的茴晋;

通過從網(wǎng)上搜集資料,我也采用了折中方案:

  • APP設(shè)置定時提醒到系統(tǒng)日歷(日歷的日歷事件并設(shè)定提醒回窘、描述中填入需要跳轉(zhuǎn)的URL诺擅、事件的標(biāo)題);
  • 定時到達(dá)啡直,系統(tǒng)日歷主動彈窗或通知欄提醒用戶(不同的安卓手機(jī)形式不太一樣)烁涌;
  • 用戶點(diǎn)擊日歷提示界面,進(jìn)入日歷事件詳情界面
  • 點(diǎn)擊日歷事件備注中的跳轉(zhuǎn)鏈接喚起系統(tǒng)選擇器酒觅;
  • 選擇器展示可以處理跳轉(zhuǎn)URL的app
  • 選擇瀏覽器撮执,跳到wap頁;選擇APP舷丹,使用deeplink跳轉(zhuǎn)到相關(guān)的原生界面二打。

4.1 Deeplink

使用系統(tǒng)日歷需要使用到的關(guān)鍵技術(shù)是Deeplink, 這個大家自己去百度,資料很多掂榔,而且不難继效。
另一個關(guān)鍵的點(diǎn)是:定義deeplink的scheme時,要注意下格式装获,有的格式系統(tǒng)日歷可能不能識別瑞信。
推薦大家使用https://開頭的,缺點(diǎn)就是系統(tǒng)除了app之外還會喚醒瀏覽器穴豫,需要用戶手動選擇凡简,加入用戶選擇了瀏覽器逼友,還需要一個WAP界面來對應(yīng)一下。

4.2 代碼

下面給出設(shè)置系統(tǒng)日歷的關(guān)鍵代碼:

/**
 * 作者: Xiao Danchen.
 * 工具類:
 * 通過日歷添加事件提醒的方式實(shí)現(xiàn)秒殺秤涩、搶購等提醒功能帜乞。
 * 要求內(nèi)部實(shí)現(xiàn):
 * 1,新增提醒是否是重復(fù)提醒筐眷,是則添加到相關(guān)事件下黎烈;否則添加到新事件
 * 2,過期事件匀谣、提醒的清理能力
 *
 * 日歷相關(guān)的資料:https://developer.android.com/guide/topics/providers/calendar-provider.html?hl=zh-cn#calendar
 */
public class CalendarUtils {
    private static String calanderURL;
    private static String calanderEventURL;
    private static String calanderRemiderURL;

    private static String CALENDARS_NAME = "XXXX";
    private static String CALENDARS_ACCOUNT_NAME = "XXXX";
    private static String CALENDARS_ACCOUNT_TYPE = "XXXXX";
    private static String CALENDARS_DISPLAY_NAME = "XXXXX";

    /**
     * 初始化uri
     */
    static {
        if (Build.VERSION.SDK_INT >= 8) {
            calanderURL = "content://com.android.calendar/calendars";
            calanderEventURL = "content://com.android.calendar/events";
            calanderRemiderURL = "content://com.android.calendar/reminders";
        } else {
            calanderURL = "content://calendar/calendars";
            calanderEventURL = "content://calendar/events";
            calanderRemiderURL = "content://calendar/reminders";
        }
    }

    /**
     * 獲取日歷ID
     * @param context
     * @return 日歷ID
     */
    private static int checkAndAddCalendarAccounts(Context context){
        int oldId = checkCalendarAccounts(context);
        if( oldId >= 0 ){
            return oldId;
        }else{
            long addId = addCalendarAccount(context);
            if (addId >= 0) {
                return checkCalendarAccounts(context);
            } else {
                return -1;
            }
        }
    }

    /**
     * 檢查是否存在日歷賬戶
     * @param context
     * @return
     */
    private static int checkCalendarAccounts(Context context) {

        Cursor userCursor = context.getContentResolver().query(Uri.parse(calanderURL), null, null, null, CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " ASC ");
        try {
            if (userCursor == null)//查詢返回空值
                return -1;
            int count = userCursor.getCount();
            if (count > 0) {//存在現(xiàn)有賬戶照棋,取第一個賬戶的id返回
                userCursor.moveToLast();
                return userCursor.getInt(userCursor.getColumnIndex(CalendarContract.Calendars._ID));
            } else {
                return -1;
            }
        } finally {
            if (userCursor != null) {
                userCursor.close();
            }
        }
    }

    /**
     * 添加一個日歷賬戶
     * @param context
     * @return
     */
    private static long addCalendarAccount(Context context) {
        TimeZone timeZone = TimeZone.getDefault();
        ContentValues value = new ContentValues();
        value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME);

        value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME);
        value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE);
        value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME);
        value.put(CalendarContract.Calendars.VISIBLE, 1);
        value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE);
        value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER);
        value.put(CalendarContract.Calendars.SYNC_EVENTS, 1);
        value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID());
        value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME);
        value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0);

        Uri calendarUri = Uri.parse(calanderURL);
        calendarUri = calendarUri.buildUpon()
                .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
                .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME)
                .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
                .build();

        Uri result = context.getContentResolver().insert(calendarUri, value);
        long id = result == null ? -1 : ContentUris.parseId(result);
        return id;
    }

    /**
     * 向日歷中添加一個事件
     * @param context
     * @param calendar_id (必須參數(shù))
     * @param title
     * @param description
     * @param begintime 事件開始時間,以從公元紀(jì)年開始計算的協(xié)調(diào)世界時毫秒數(shù)表示武翎。 (必須參數(shù))
     * @param endtime 事件結(jié)束時間烈炭,以從公元紀(jì)年開始計算的協(xié)調(diào)世界時毫秒數(shù)表示。(非重復(fù)事件:必須參數(shù))
     * @return
     */
    private static Uri insertCalendarEvent(Context context, long calendar_id, String title, String description , long begintime, long endtime){
        ContentValues event = new ContentValues();
        event.put("title", title);
        event.put("description", description);
        // 插入賬戶的id
        event.put("calendar_id", calendar_id);
        event.put(CalendarContract.Events.DTSTART, begintime);//必須有
        event.put(CalendarContract.Events.DTEND, endtime);//非重復(fù)事件:必須有
        event.put(CalendarContract.Events.HAS_ALARM, 1);//設(shè)置有鬧鐘提醒
        event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());//這個是時區(qū)宝恶,必須有符隙,
        //添加事件
        Uri newEvent = context.getContentResolver().insert(Uri.parse(calanderEventURL), event);
        return newEvent;
    }

    /**
     * 查詢?nèi)諝v事件
     * @param context
     * @param title 事件標(biāo)題
     * @return 事件id,查詢不到則返回""
     */
    private static String queryCalendarEvent(Context context, long calendar_id, String title, String description, long start_time, long end_time){
        // 根據(jù)日期范圍構(gòu)造查詢
        Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
        ContentUris.appendId(builder, start_time);
        ContentUris.appendId(builder, end_time);
        Cursor cursor = context.getContentResolver().query(builder.build(), null, null, null, null);
        String tmp_title;
        String tmp_desc;
        long temp_calendar_id;
        if(cursor.moveToFirst()){
            do{
                tmp_title = cursor.getString(cursor.getColumnIndex("title"));
                tmp_desc = cursor.getString(cursor.getColumnIndex("description"));
                temp_calendar_id = cursor.getLong(cursor.getColumnIndex("calendar_id"));
                long dtstart = cursor.getLong(cursor.getColumnIndex("dtstart"));
                if(TextUtils.equals(title,tmp_title) && TextUtils.equals(description,tmp_desc) && calendar_id == temp_calendar_id && dtstart==start_time){
                    String eventId = cursor.getString(cursor.getColumnIndex("event_id"));
                    return eventId;
                }
            }while(cursor.moveToNext());
        }
        return  "";
    }

    /**
     * 添加日歷提醒:標(biāo)題、描述垫毙、開始時間共同標(biāo)定一個單獨(dú)的提醒事件
     * @param context
     * @param title 日歷提醒的標(biāo)題,不允許為空
     * @param description 日歷的描述(備注)信息
     * @param begintime 事件開始時間霹疫,以從公元紀(jì)年開始計算的協(xié)調(diào)世界時毫秒數(shù)表示。
     * @param endtime 事件結(jié)束時間露久,以從公元紀(jì)年開始計算的協(xié)調(diào)世界時毫秒數(shù)表示更米。
     * @param remind_minutes 提前remind_minutes分鐘發(fā)出提醒
     * @param callback 添加提醒是否成功結(jié)果監(jiān)聽
     */
    public static void addCalendarEventRemind(Context context, @NonNull String title, String description, long begintime, long endtime, int remind_minutes, onCalendarRemindListener callback){
        long calendar_id = checkAndAddCalendarAccounts(context);
        if(calendar_id < 0){
            // 獲取日歷失敗直接返回
            if(null != callback){
                callback.onFailed(onCalendarRemindListener.Status.CALENDAR_ERR);
            }
            return;
        }
        //根據(jù)標(biāo)題、描述毫痕、開始時間查看提醒事件是否已經(jīng)存在
        String event_id = queryCalendarEvent(context,calendar_id,title,description,begintime,endtime);
        //如果提醒事件不存在征峦,則新建事件
        if(TextUtils.isEmpty(event_id)){
            Uri newEvent = insertCalendarEvent(context,calendar_id,title,description,begintime,endtime);
            if (newEvent == null) {
                // 添加日歷事件失敗直接返回
                if(null != callback){
                    callback.onFailed(onCalendarRemindListener.Status.EVENT_ERROR);
                }
                return;
            }
            event_id = ContentUris.parseId(newEvent)+"";
        }
        //為事件設(shè)定提醒
        ContentValues values = new ContentValues();
        values.put(CalendarContract.Reminders.EVENT_ID, event_id);
        // 提前remind_minutes分鐘有提醒
        values.put(CalendarContract.Reminders.MINUTES, remind_minutes);
        values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);
        Uri uri = context.getContentResolver().insert(Uri.parse(calanderRemiderURL), values);
        if(uri == null) {
            // 添加提醒失敗直接返回
            if(null != callback){
                callback.onFailed(onCalendarRemindListener.Status.REMIND_ERROR);
            }
            return;
        }

        //添加提醒成功
        if(null != callback){
            callback.onSuccess();
        }
    }

    /**
     * 刪除日歷提醒事件:根據(jù)標(biāo)題、描述和開始時間來定位日歷事件
     * @param context
     * @param title 提醒的標(biāo)題
     * @param description 提醒的描述:deeplink URI
     * @param startTime 事件的開始時間
     * @param callback 刪除成功與否的監(jiān)聽回調(diào)
     */
    public static void deleteCalendarEventRemind(Context context, String title, String description, long startTime,onCalendarRemindListener callback){
        Cursor eventCursor = context.getContentResolver().query(Uri.parse(calanderEventURL), null, null, null, null);
        try {
            if (eventCursor == null)//查詢返回空值
                return;
            if (eventCursor.getCount() > 0) {
                //遍歷所有事件消请,找到title栏笆、description、startTime跟需要查詢的title臊泰、descriptio蛉加、dtstart一樣的項(xiàng)
                for (eventCursor.moveToFirst(); !eventCursor.isAfterLast(); eventCursor.moveToNext()) {
                    String eventTitle = eventCursor.getString(eventCursor.getColumnIndex("title"));
                    String eventDescription = eventCursor.getString(eventCursor.getColumnIndex("description"));
                    long dtstart = eventCursor.getLong(eventCursor.getColumnIndex("dtstart"));
                    if (!TextUtils.isEmpty(title) && title.equals(eventTitle) && !TextUtils.isEmpty(description) && description.equals(eventDescription) && dtstart==startTime ) {
                        int id = eventCursor.getInt(eventCursor.getColumnIndex(CalendarContract.Calendars._ID));//取得id
                        Uri deleteUri = ContentUris.withAppendedId(Uri.parse(calanderEventURL), id);
                        int rows = context.getContentResolver().delete(deleteUri, null, null);
                        if (rows == -1) {
                            // 刪除提醒失敗直接返回
                            if(null != callback){
                                callback.onFailed(onCalendarRemindListener.Status.REMIND_ERR);
                            }
                            return;
                        }
                        //刪除提醒成功
                        if(null != callback){
                            callback.onSuccess();
                        }
                    }
                }
            }
        } finally {
            if (eventCursor != null) {
                eventCursor.close();
            }
        }
    }

    /**
     * 日歷提醒添加成功與否監(jiān)控器
     */
    public static interface onCalendarRemindListener{
        enum Status {
            _CALENDAR_ERROR,
            _EVENT_ERROR,
            _REMIND_ERROR
        }
        void onFailed(Status error_code);
        void onSuccess();
    }

    /**
     * 輔助方法:獲取設(shè)置時間起止時間的對應(yīng)毫秒數(shù)
     * @param year
     * @param month 1-12
     * @param day 1-31
     * @param hour 0-23
     * @param minute 0-59
     * @return
     */
    public static long remindTimeCalculator(int year,int month,int day,int hour,int minute){
        Calendar calendar = Calendar.getInstance();
        calendar.set(year,month-1,day,hour,minute);
        return calendar.getTimeInMillis();
    }
}

分享、共贏
歡迎大家加入 學(xué)習(xí)小組 QQ群: 193765960

最后編輯于
?著作權(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)我...
    茶點(diǎn)故事閱讀 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
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旺罢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年旷余,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扁达。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡正卧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跪解,到底是詐尸還是另有隱情炉旷,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布叉讥,位于F島的核電站窘行,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏图仓。R本人自食惡果不足惜罐盔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望救崔。 院中可真熱鬧惶看,春花似錦、人聲如沸六孵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劫窒。三九已至本今,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間主巍,已是汗流浹背冠息。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煤禽,地道東北人铐达。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像檬果,于是被迫代替她去往敵國和親瓮孙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫选脊、插件杭抠、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,022評論 4 62
  • 國慶回家休息七天。七號的晚上恳啥,我拖著箱子走在回寢室的路上 偏灿。箱子在石頭砌成的小路上發(fā)出砰砰的響聲。一陣清香飄過钝的。好...
    故事好香閱讀 356評論 0 1
  • 晨讀分享了《感召力》這本書翁垂,“感召力”三個字和文章的贏得本能腦铆遭,贏得情感腦,贏得邏輯腦三方面的內(nèi)容讓我想起了當(dāng)初當(dāng)...
    遇見靖雯閱讀 637評論 13 9
  • 我是一只井底之蛙沿猜, 望著只有井口那么大的藍(lán)天枚荣。 白天,鳥兒在我的頭上歌唱啼肩, 夜晚橄妆,螢火蟲把我團(tuán)團(tuán)圍繞, 我好似國王...
    海王星1984閱讀 560評論 6 2