1. 程式人生 > >Android 7.0 Settings Summary 小記

Android 7.0 Settings Summary 小記

改Setting的主選單的一個顯示問題,發現android原生的Settings 在7.0 發生了很大的變化。
主要有:
1、增加了側滑選單;
2、增加了Suggestions
3、可以在主介面顯示狀態(這篇部落格主要針對這個)
4、主介面使用RecyclerView

這個是主介面載入的時序圖
偷懶截了別人的圖

本文主要介紹下 Summary的顯示流程

通過搜尋可以定位到這個配置的地方是在 下面程式碼加粗的地方配置

 public void setListening(boolean listening) {
            if (listening) {
                // TODO: Listen.
BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() { @Override public void onBatteryInfoLoaded(BatteryInfo info) { *mLoader.setSummary(SummaryProvider.this, "("+(int)BatteryCapcityValue+"mAh)"+info.mChargeLabelString);* } }); } }

知其然要知其所以然 所以今天分析下為什麼一個Summary 需要在這個地方配置。
進入setSummary 方法

public void setSummary(SummaryProvider provider, final CharSequence summary) {
        final ComponentName component= mSummaryMap.get(provider);
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                // Since tiles are not always cached (like on locale change for instance),
// we need to always get the latest one. Tile tile = mAdapter.getTile(component); if (tile == null) return; if (DEBUG) Log.d(TAG, "setSummary " + tile.title + " - " + summary); tile.summary = summary; mAdapter.notifyChanged(tile); } }); }

可以發現主要就是設定titile的summary 然後通知Adapter更新,而Adapter 是DashboardAdapter的例項,是設定給DashboardSummary的,通過上面的時序圖其實可以Settings的佈局其實就是在DashboardSummary中進行處理的。

而mLoader.setSummary(SummaryProvider.this, “(“+(int)BatteryCapcityValue+”mAh)”+info.mChargeLabelString);又是在什麼時候呼叫的呢
通過時序圖可以發現在DashboardSummary中的oncreate中

 public void onCreate(Bundle savedInstanceState) {
        long startTime = System.currentTimeMillis();
        super.onCreate(savedInstanceState);

        List<DashboardCategory> categories =
                ((SettingsActivity) getActivity()).getDashboardCategories();
        **mSummaryLoader = new SummaryLoader(getActivity(), categories);**
        setHasOptionsMenu(true);
        Context context = getContext();
        mConditionManager = ConditionManager.get(context, false);
        mSuggestionParser = new SuggestionParser(context,
                context.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);
        mSuggestionsChecks = new SuggestionsChecks(getContext());
        if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
                + " ms");
    }

會new一個SummaryLoader 它的構造方法主要做了這幾件事:
1、建立一個Handler物件;
2、建立非同步執行緒;
3、利用雙重迴圈遍歷每個title,然後通過非同步執行緒處理各個tile;

 public SummaryLoader(Activity activity, List<DashboardCategory> categories) {

        mHandler = new Handler();
        mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
        mWorkerThread.start();
        mWorker = new Worker(mWorkerThread.getLooper());
        mActivity = activity;
        for (int i = 0; i < categories.size(); i++) {
            List<Tile> tiles = categories.get(i).tiles;
            for (int j = 0; j < tiles.size(); j++) {
                Tile tile = tiles.get(j);
                mWorker.obtainMessage(Worker.MSG_GET_PROVIDER, tile).sendToTarget();
            }
        }
    }

 private synchronized void makeProviderW(Tile tile) {
        SummaryProvider provider = getSummaryProvider(tile);
        if (provider != null) {
            if (DEBUG) Log.d(TAG, "Creating " + tile);
            mSummaryMap.put(provider, tile.intent.getComponent());
        }
    }

可以發現最後把 SummaryProvider和 Component已鍵值對的形式放到了一個ArrayMap中。
這裡關鍵看下getSummaryProvider的實現

 private SummaryProvider getSummaryProvider(Tile tile) {
        if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) {
            // Not within Settings, can't load Summary directly.
            // TODO: Load summary indirectly.
            // return null;
            /// M: support external app dynamic summary
            return ExternalSummaryProvider.createExternalSummaryProvider(mActivity, this, tile);
        }
        Bundle metaData = getMetaData(tile);
        if (metaData == null) {
            if (DEBUG) Log.d(TAG, "No metadata specified for " + tile.intent.getComponent());
            return null;
        }
        String clsName = metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
        if (clsName == null) {
            if (DEBUG) Log.d(TAG, "No fragment specified for " + tile.intent.getComponent());
            return null;
        }
        try {
            Class<?> cls = Class.forName(clsName);
            Field field = cls.getField(SUMMARY_PROVIDER_FACTORY);
            SummaryProviderFactory factory = (SummaryProviderFactory) field.get(null);
            return factory.createSummaryProvider(mActivity, this);
        } catch (ClassNotFoundException e) {
            if (DEBUG) Log.d(TAG, "Couldn't find " + clsName, e);
        } catch (NoSuchFieldException e) {
            if (DEBUG) Log.d(TAG, "Couldn't find " + SUMMARY_PROVIDER_FACTORY, e);
        } catch (ClassCastException e) {
            if (DEBUG) Log.d(TAG, "Couldn't cast " + SUMMARY_PROVIDER_FACTORY, e);
        } catch (IllegalAccessException e) {
            if (DEBUG) Log.d(TAG, "Couldn't get " + SUMMARY_PROVIDER_FACTORY, e);
        }
        return null;
    }

這個方法主要做了以下幾件事:
1、獲取MetaData;
2、獲取MetaData的的META_DATA_KEY_FRAGMENT_CLASS值的值
3、根據上一步獲取的字串 通過反射來獲取這個類(上一步獲取的字串形式就是“com.android.settings.fuelgauge.PowerUsageSummary” 這樣的)
4、通過上步獲取的類獲取類中的SUMMARY_PROVIDER_FACTORY這個屬性
5、返回類中的SummaryProvider物件。

看第三步可以知道這裡獲取的就是主介面的各個選項,這裡以PowerUsageSummary為例進去看下

public class PowerUsageSummary extends PowerUsageBase {

...
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
        private final Context mContext;
        private final SummaryLoader mLoader;

        private SummaryProvider(Context context, SummaryLoader loader) {
            mContext = context;
            mLoader = loader;

        }

        @Override
        public void setListening(boolean listening) {
            if (listening) {
                // TODO: Listen.
                BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
                    @Override
                    public void onBatteryInfoLoaded(BatteryInfo info) {
                        mLoader.setSummary(SummaryProvider.this, "("+(int)BatteryCapcityValue+"mAh)"+info.mChargeLabelString);//add by linyu.li for battery show 20170724
                    }
                });
            }
        }
    }

    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
            = new SummaryLoader.SummaryProviderFactory() {
        @Override
        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
                                                                   SummaryLoader summaryLoader) {
            return new SummaryProvider(activity, summaryLoader);
        }
    };
}

所以主介面的每個選項都必須要實現SummaryProvider 和SummaryLoader.SummaryProviderFactory這兩個類才能正確的顯示Summary

參考文章:http://blog.csdn.net/a771642/article/details/70284335