Android Settings源码学习笔记

Android Settings

读源码,从AndroidManifest.xml文件入手,android版本8.0

AndroidManifest.xml

<activity android:name="Settings"
    android:taskAffinity="com.android.settings.root">
activity>
<activity-alias android:name="Settings"
    android:taskAffinity="com.android.settings.root">
activity-alias>
<activity android:name=".SubSettings"
    android:taskAffinity="com.android.settings"
    android:parentActivityName="Settings"/>
    ...

1.主启动页面为Settings
2.通过taskAffinity将activity分为几个部分(Task)
com.android.settings.root
com.android.settings
与application一致的" "
com.android.settings.storage_wizard
3.通过:parentActivityName指定为某个Activity的逻辑子类
4.meta-data:
元数据,可以为application、activity、recevier等提供附加数据项
android:name:唯一名称
android:resource:对资源的引用,该资源ID可以通过该metaData.getInt()获得

<meta-data android:name="android.app.shortcuts" android.resource="@xml/shortcuts" />
android:value:分配给该标签的值

获取元数据:

//SettingsActivity.java中的getMetaData()
ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),PackageManager.GET_META_DATA);
mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);

由于只看了xml文件,并不确定是否有使用其他属性配合实现效果(Intent.FLAG_ACTIVITY_NEW_TASK或者allowTaskReparenting)

Settings

public class Settings extends SettingsActivity{
    /*静态内部类,且全部继承自SettingsActivity*/
}

1.没有实现内容,但继承自SettingsActivity,且SettingsActivity有实现内容
2.在点击其中一部分静态内部类时会跳转至其他java文件,说明会在其他具体的方法、广播以及activity时引入

SettingsActivity

onCreate

protected void onCreate(Bundle savedState){
    ...
    final FeatureFactory factory = FeatureFactory.getFactory(this);
    mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
    //加载元数据,初始化字符串mFragmentClass
    getMetaData();
    ...
    final ComponentName cn = intent.getComponent();
    final String className = cn.getClassName();
    //当前intent的类名与settings是否一致(意味着启动settings主页面)
    mIsShowingDashboard = className.equals(Settings.class.getName());
    //通过mIsShowingDashboard来加载主页面的布局
    setContentView(mIsShowingDashboard?R.layout.settings_main_dashboard:R.layout.settings_main_prefs);
    ...
    //如果之前已经启动过,有保存的状态和一些值,那么用它来进行初始化,而不是新建
    if(savedState != null){
        setTitleFromIntent(intent);
        ArrayList<DashboardCategory> categories = savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
        if(categories != null){
            mCategories.clear();
            mCategories.addAll(categories);
            setTitleFromBackStack();        
        }else{//否则就调用这个函数进行第一次页面的加载
            launchSettingFragment(initialFragmentName,isSubSettings,intent);        
        }
    }
}

1.进入一个Activity时,习惯性的按照生命周期进行查看,所以跳转到onCreate
2.之前没有细读,所以一直存在疑惑:为什么首次进入是执行launchSettingFragment()这个方法?
在onCreate()中对之前是否进行状态保存的值(savedState)进行了判断,不为空就意味着不是首次启动,可以拿之前保存的值来初始化,而我们第一次进入的时候是没有值的,所以才会执行launchSettingFragment()
3.R.layout.settings_main_dashboard是主页面
Android Settings源码学习笔记_第1张图片

  • R.layout.settings_main_prefs的作用/模块

launchSettingFragment

void launchSettingFragment(String initialFragmentName,boolean isSubSettings,Intent intent){
    if(!mIsShowDashboard && initialFragmentName != null){
        setTitleFromIntent(intent);
        Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
        switchToFragment(initialFragmentName,initialArguments,true,false,mInitialTitleResId,mInitialTitle,false);
    }else{
        mInitialTitleResId = R.string.dashboard_titlel
        switchToFragment(DashboardSummary.class.getName(),null,false,false,mInitialTitleResId,mInitialTitle,false);
    }
}

1.mIsShowDashboard为true,所以if不会执行,直接到else后面的语句
2.R.string.dashboard_title=mInitialTitleResId=>“Settings”

  • mInitialTitle=null?

switchToFragment

private Fragment switchToFragment(String fragmentName,Bundle args,boolean validate,boolean addToBackStack,int titleResId,CharSequence title,boolean withTransition){
    ...
    //实例化DashboardSummary类,替换main_content
    Fragment f = Fragment.instantiate(this,fragmentName,args);
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.main_content, f);
    ...
    if(titleResId > 0){
        transcation.setBreadCrumbTitle(titleResId);            
    }else if(title != null){
        transcation.setBreadCrumbTitle(title);    
    }
    transaction.commitAllowingStateLoss();
    getFragmentManager().executePendingTransactions();
    return f;
}

1.在Fragment.instantiate()中,完成DashboardSummary类的实例化,在这里进行数据的加载,并且替换掉main_content,呈现出进入的页面效果
2.设置标题的时候,会先对titleResId进行判断,如果没有再去判断title,由于之前找到title的时候只有定义并未赋值,推测应该为null,如果是进行初始化而非创建,title不会为null,但也不会调用launchSettingFragment,所以只会判断titleResId,且以它的值作为标题(搜索栏的hint提示)

  • 3.getFragmentManager().executePendingTransactions()作用

效果

Android Settings源码学习笔记_第2张图片

DashboardSummary

onAttach()

public void onAttach(Context context){
    final SuggestionFeatureProvider suggestionFeatureProvider = FeatureFactory
        .getFactory(context)
        .getSuggestionFeatureProvider(context);
    if(suggestionFeatureProvider.isSuggestionEnable(context)){//true
    //初始化mSuggestionControllerMixin,
        mSuggestionControllerMixin = new SuggestionControllerMixin(context, this /* host */,
        getLifecycle(), suggestionFeatureProvider
        .getSuggestionServiceComponent());
    }
}

1.实际上是一个Fragment,继承自InstrumentedFragment,按照生命周期进行查看
2.suggestionFeatureProvider.isSuggestionEnable(context)实现类是SuggestionFeatureProviderImpl.java中,其最后返回的结果是以设备是否为低内存设备的结果进行取非

public boolean isSuggestionEnabled(Context context) {
    final ActivityManager am =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    return !am.isLowRamDevice();
}

3.在这里对mSuggestionControllerMixin进行了初始化,实例化一个SuggestionController对象,并添加了监听事件,在实例化SuggestionController对象时,新建了ServiceConnection对象管理服务连接与失去连接事件

onCreate()

public void onCreate(Bundle savedInstanceState){
    mDashboardFeatureProvider = FeatureFactory.getFactory(activity).getDashboardFeatureProvider(activity);
    //开启一个后台工作线程
    mSummaryLoader = new SummaryLoader(activity,CategoryKey.CATEGORY_HOMEPAGE);
    //开启一个异步任务
    mConditionManager = ConditionManager.get(activity, false);
    getLifecycle().addObserver(mConditionManager);
    ...
}

1.CategoryKey.CATEGORY_HOMEPAGE定义在CategoryKey.java文件中(frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java)

//Activities in this category shows up in Settings homepage
//显示在设置主页的活动,在AndroidManifest.xml中meta-data设置的一些活动
public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage";

3.mConditionManager最终会实例化ConditionLoader类开启异步任务,去加载模块

//位于ConditionManager.java中,内部类
private class ConditionLoader extends AsyncTask<Void,Void,ArrayList<Condition>>{
    @Override
    protected ArrayList<Condition> doInBackground(Void... params) {
    Log.d(TAG, "loading conditions from xml");
    ArrayList<Condition> conditions = new ArrayList<>();
    mXmlFile = new File(mContext.getFilesDir(), FILE_NAME);
    if (mXmlFile.exists()) {//如果xml文件存在,则读取
        readFromXml(mXmlFile, conditions);
    }
    addMissingConditions(conditions);//添加不在xml文件中的类
    return conditions;
}
    @Override
    protected void onPostExecute(ArrayList<Condition> conditions) {
    Log.d(TAG, "conditions loaded from xml, refreshing conditions");
    mConditions.clear();
    mConditions.addAll(conditions);
    refreshAll();//刷新
}
}
  • 4.初始化的mSummaryLoader的作用

onCreateView()

public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle bundle){
    final View root = inflater.inflate(R.layout.dashboard, container, false);
    mDashboard = root.findViewById(R.id.dashboard_container);
    ...
    /*对mDashboard进行一些初始化,如绑定布局、添加监听事件等*/
    //设置适配器,添加数据
    mAdapter = new DashboardAdapter(getContext(),bundle,mConditionManager.getConditions(),mSuggestionControllerMixin,getLifecycle());
    mDashboard.setAdapter(mAdapter);
    ...
    rebuildUI();
}

1.root加载的布局是拓展的RecycleView,并且布局文件只有它一个View,没有设置任何布局,是在java中动态添加的线性布局
2.所有的数据加载都是在DashboardAdapter中

DashboardAdapter

public DashboardAdapter(Context context,Bundle savedInstanceState,
    List<Condition> conditions,SuggestionControllerMixin suggestionControllerMixin,
    Lifecycle lifecycle){
          ...
          mSuggestionAdapter = new SuggestionAdapter(mContext,suggestionControllerMixin,
                  savedInstanceState,this,lifecycle);
          ...
          mDashboardData = new DashboardData.Builder()
                          .setConditions(conditions)
                          .setSuggestions(mSuggestionAdapter.getSuggestions())
                          .setCategory(catrgory)
                          .setConditionExpanded(conditionExpanded)
                          .build();
    }

1.通过new SuggestionAdapter,和Lifecycle绑定,并初始化mSuggestionShowLogged=new ArrayList<>()
2.mDashboardData为数据来源,通过DashboardData.Builder设置值,调用builf方法来创建

public DashboardData build(){
    return new DashboardData(this);
}

DashboardData

private DashboardData(Builder builder){
    ...
    buildItemsData();
}

buildItemsData()

private void buildItemsData(){
    ...
    //主页面搜索框下的条目呈现
    if(mCategory != null){
        final List<Tile> tiles = mCategory.getTiles();
        for(int i = 0; i < tiles.size(); i++){
            final Tile tile = tiles.get(i);
            addToItemList(tile,R.layout.dashboard_tile,Ojects.hash(tile.title),true);        
        }    
    }
}

1.使用addToItemList对suggestions和conditions添加到mItem中

  • mCategory的值在哪进行了设置

数据加载,页面呈现流程走完,分析跳转子页面,以及子页面的逻辑

你可能感兴趣的:(Android源码学习,android,学习,java)