OSCHINA客戶端完全剖析(一)

首先感謝開源中國(guó)為我們帶來(lái)了學(xué)習(xí)資源:http://www.oschina.net/p/oschina-android-app
這份代碼雖然存在不少小瑕疵耸序,但總體上是一個(gè)功能齊備的應(yīng)用镀赌,而且關(guān)鍵是其代碼非常易讀

今天先分析一下啟動(dòng)及主界面

啟動(dòng)頁(yè)
主界面

BaseApplication extends Application

這個(gè)類中主要是做了一些功能封裝:

  1. 已讀文章列表相關(guān)
/**
     * 放入已讀文章列表中
     *
     */
    public static void putReadedPostList(String prefFileName, String key,
                                         String value) {
        SharedPreferences preferences = getPreferences(prefFileName);
        int size = preferences.getAll().size();
        Editor editor = preferences.edit();
        if (size >= 100) {
            editor.clear();
        }
        editor.putString(key, value);
        apply(editor);
    }
  1. SharedPreferences相關(guān)
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
    public static void apply(SharedPreferences.Editor editor) {
        if (sIsAtLeastGB) {
            editor.apply();
        } else {
            editor.commit();
        }
    }

    public static void set(String key, int value) {
        Editor editor = getPreferences().edit();
        editor.putInt(key, value);
        apply(editor);
    }
  1. 獲取String相關(guān)
    public static String string(int id) {
        return _resource.getString(id);
    }

    public static String string(int id, Object... args) {
        return _resource.getString(id, args);
    }
  1. Toast相關(guān)
public static void showToast(String message, int icon) {
        showToast(message, Toast.LENGTH_LONG, icon, Gravity.BOTTOM);
    }

    public static void showToastShort(int message) {
        showToast(message, Toast.LENGTH_SHORT, 0);
    }

AppContext extends BaseApplication

正式的自定義Application了耘子,所做的事很簡(jiǎn)單,看如下代碼:

@Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        init();
        initLogin();

        UIHelper.sendBroadcastForNotice(this); // 注一焰宣,其后會(huì)對(duì)這里進(jìn)行說(shuō)明
    }
private void init() {
        // 初始化網(wǎng)絡(luò)請(qǐng)求
        AsyncHttpClient client = new AsyncHttpClient();
        PersistentCookieStore myCookieStore = new PersistentCookieStore(this);
        client.setCookieStore(myCookieStore);
        ApiHttpClient.setHttpClient(client);
        ApiHttpClient.setCookie(ApiHttpClient.getCookie(this));

        // Log控制器
        KJLoger.openDebutLog(true);
        TLog.DEBUG = BuildConfig.DEBUG;

        // Bitmap緩存地址
        HttpConfig.CACHEPATH = "OSChina/imagecache";
    }

登錄信息相關(guān):

private void initLogin() {
        User user = getLoginUser();
        if (null != user && user.getId() > 0) {
            login = true;
            loginUid = user.getId();
        } else {
            this.cleanLoginInfo();
        }
    }

還有就是代碼中封裝了Properties的使用,雖然使用SharedPreferences會(huì)占用內(nèi)存,但個(gè)人認(rèn)為這里數(shù)據(jù)量很少并不是很有必要挂绰,實(shí)際操作代碼封裝在AppConfig中:

    public Properties getProperties() {
        return AppConfig.getAppConfig(this).get();
    }

    public void setProperty(String key, String value) {
        AppConfig.getAppConfig(this).set(key, value);
    }

AppStart extends Activity

入口Activity,實(shí)現(xiàn)啟動(dòng)頁(yè)服赎,方法是設(shè)置其style為全屏無(wú)ActionBar葵蒂,延時(shí)跳轉(zhuǎn):

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 防止第三方跳轉(zhuǎn)時(shí)出現(xiàn)雙實(shí)例
        Activity aty = AppManager.getActivity(MainActivity.class);
        if (aty != null && !aty.isFinishing()) {
            finish();
        }
        // SystemTool.gc(this); //針對(duì)性能好的手機(jī)使用,加快應(yīng)用相應(yīng)速度

        final View view = View.inflate(this, R.layout.app_start, null);
        setContentView(view);
        // 漸變展示啟動(dòng)屏
        AlphaAnimation aa = new AlphaAnimation(0.5f, 1.0f);
        aa.setDuration(800);
        view.startAnimation(aa);
        aa.setAnimationListener(new AnimationListener() {
            @Override
            public void onAnimationEnd(Animation arg0) {
                redirectTo();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {}

            @Override
            public void onAnimationStart(Animation animation) {}
        });
    }

另外說(shuō)一句重虑,該應(yīng)用使用了一個(gè)AppManager來(lái)對(duì)所有Activity進(jìn)行堆棧式管理践付,實(shí)現(xiàn)完全退出應(yīng)用。

而跳轉(zhuǎn)到主Activity之前缺厉,則先開啟了一個(gè)用來(lái)上傳Log的Service:

/**
     * 跳轉(zhuǎn)到...
     */
    private void redirectTo() {
        Intent uploadLog = new Intent(this, LogUploadService.class);
        startService(uploadLog);
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }

MainActivity extends ActionBarActivity

這里做的事就比較多了永高,只撿關(guān)鍵的講,上面的注一在initView()中有了眉目:

IntentFilter filter = new IntentFilter(Constants.INTENT_ACTION_NOTICE);
        filter.addAction(Constants.INTENT_ACTION_LOGOUT);
        registerReceiver(mReceiver, filter); // 注二
        NoticeUtils.bindToService(this);

NoticeUtils中:

public static boolean bindToService(Context context) {
        return bindToService(context, null);
    }

    public static boolean bindToService(Context context,
            ServiceConnection callback) {
        context.startService(new Intent(context, NoticeService.class));
        ServiceBinder sb = new ServiceBinder(callback);
        sConnectionMap.put(context, sb);
        return context.bindService(
                (new Intent()).setClass(context, NoticeService.class), sb, 0);
    }

最終是綁定了一個(gè)NoticeService提针,而在注一中命爬,其執(zhí)行代碼正為發(fā)送目標(biāo)為NoticeService的廣播:

    /**
     * 發(fā)送通知廣播
     * 
     * @param context
     */
    public static void sendBroadcastForNotice(Context context) {
        Intent intent = new Intent(NoticeService.INTENT_ACTION_BROADCAST);
        context.sendBroadcast(intent);
    }

而在NoticeService之中,其后續(xù)動(dòng)作為發(fā)送Action為Constants.INTENT_ACTION_NOTICE的廣播辐脖,該廣播許多地方都會(huì)監(jiān)聽饲宛,而在MainActivity中也有注冊(cè)監(jiān)聽(見注二),最后會(huì)有一個(gè)BadgeView 控件發(fā)生改變:

private BadgeView mBvNotice;

    public static Notice mNotice;

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Constants.INTENT_ACTION_NOTICE)) {
                mNotice = (Notice) intent.getSerializableExtra("notice_bean");
                int atmeCount = mNotice.getAtmeCount();// @我
                int msgCount = mNotice.getMsgCount();// 留言
                int reviewCount = mNotice.getReviewCount();// 評(píng)論
                int newFansCount = mNotice.getNewFansCount();// 新粉絲
                int newLikeCount = mNotice.getNewLikeCount();// 收到贊
                int activeCount = atmeCount + reviewCount + msgCount
                        + newFansCount + newLikeCount;

                Fragment fragment = getCurrentFragment();
                if (fragment instanceof MyInformationFragment) {
                    ((MyInformationFragment) fragment).setNotice();
                } else {
                    if (activeCount > 0) {
                        mBvNotice.setText(activeCount + "");
                        mBvNotice.show();
                    } else {
                        mBvNotice.hide();
                        mNotice = null;
                    }
                }
            } else if (intent.getAction()
                    .equals(Constants.INTENT_ACTION_LOGOUT)) {
                mBvNotice.hide();
                mNotice = null;
            }
        }
    };

主界面實(shí)現(xiàn)

FragmentTabHost即可,布局片段如下:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <FrameLayout
            android:id="@+id/realtabcontent"
            android:layout_width="match_parent"
            android:layout_height="0dip"
            android:layout_weight="1" />

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/windows_bg" >

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="4dip" >

                <net.oschina.app.widget.MyFragmentTabHost
                    android:id="@android:id/tabhost"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="4dip" />
                <View
                    android:layout_width="match_parent"
                    android:layout_height="1px"
                    android:background="?attr/lineColor" />

            </RelativeLayout>

            <!-- 快速操作按鈕 -->

            <ImageView
                android:id="@+id/quick_option_iv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:contentDescription="@null"
                android:src="@drawable/btn_quickoption_selector" />
        </FrameLayout>
    </LinearLayout>

realtabcontent分割剩余空間嗜价,為實(shí)際內(nèi)容頁(yè):

實(shí)際內(nèi)容頁(yè)

在initView()中進(jìn)行了setup:

mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);

中鍵點(diǎn)擊的效果不一樣艇抠,不是Fragment頁(yè)面,而是一個(gè)自定義Dialog了:

所以炭剪,實(shí)現(xiàn)上quick_option_iv這個(gè)ImageView在布局中對(duì)第三個(gè)Tab進(jìn)行了遮擋练链。
而第三個(gè)Tab也需要特殊處理:它在點(diǎn)擊時(shí)不切換頁(yè)面。
在MyFragmentTabHost extends FragmentTabHost中:

@Override
    public void onTabChanged(String tag) {
        
        if (tag.equals(mNoTabChangedTag)) {// 對(duì)第三個(gè)Tab特殊處理
            setCurrentTabByTag(mCurrentTag);
        } else {
            super.onTabChanged(tag);
            mCurrentTag = tag;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奴拦,一起剝皮案震驚了整個(gè)濱河市媒鼓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖绿鸣,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疚沐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡潮模,警方通過(guò)查閱死者的電腦和手機(jī)亮蛔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)擎厢,“玉大人究流,你說(shuō)我怎么就攤上這事《猓” “怎么了芬探?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)厘惦。 經(jīng)常有香客問(wèn)我偷仿,道長(zhǎng),這世上最難降的妖魔是什么宵蕉? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任酝静,我火速辦了婚禮,結(jié)果婚禮上羡玛,老公的妹妹穿的比我還像新娘别智。我一直安慰自己,他們只是感情好缝左,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布亿遂。 她就那樣靜靜地躺著,像睡著了一般渺杉。 火紅的嫁衣襯著肌膚如雪蛇数。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天是越,我揣著相機(jī)與錄音耳舅,去河邊找鬼。 笑死倚评,一個(gè)胖子當(dāng)著我的面吹牛浦徊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播天梧,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼盔性,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呢岗?” 一聲冷哼從身側(cè)響起冕香,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛹尝,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后悉尾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體突那,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年构眯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了愕难。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惫霸,死狀恐怖猫缭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壹店,我是刑警寧澤饵骨,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站茫打,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妖混。R本人自食惡果不足惜老赤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望制市。 院中可真熱鬧抬旺,春花似錦、人聲如沸祥楣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)误褪。三九已至责鳍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兽间,已是汗流浹背历葛。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘀略,地道東北人恤溶。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像帜羊,于是被迫代替她去往敵國(guó)和親咒程。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程讼育,因...
    小菜c閱讀 6,358評(píng)論 0 17
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理帐姻,服務(wù)發(fā)現(xiàn)稠集,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • #Android 基礎(chǔ)知識(shí)點(diǎn)總結(jié) ---------- ##1.adb - android debug bridg...
    Mythqian閱讀 3,250評(píng)論 2 11
  • 流量管理的位置在com.oneplus.security.network下面 包含 calibrate 校準(zhǔn)功能包...
    panberglee閱讀 1,174評(píng)論 2 2
  • 第一行代碼-第二版的酷歐天氣 首先創(chuàng)建數(shù)據(jù)庫(kù)類繼承LitePal,用來(lái)存儲(chǔ)城市數(shù)據(jù)Province.java p...
    A_Coder閱讀 2,108評(píng)論 0 0