文件管理app源码分析

一.界面分析

主activity为LauncherActivity
主要为启动filesActivity

 protected void onCreate(Bundle savedInstanceState) {
        launch();
    }
  private void launch() {
        ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        Intent intent = findTask(activities);
         if (restoreTask(intent)) {
                return;
            } else {
                // We failed to restore the task. It may happen when system was just updated and we
                // moved the location of the targeted activity. Chances is that the rest of tasks
                // can't be restored either, so clean those tasks and start a new one.
                clearTask(activities);
            }
        startTask();
    }   
private void startTask() {
        Intent intent = createLaunchIntent(this);//FilesActivity
        startActivity(intent);
    } 

FilesActivity继承BaseActivity,先查看BaseActivity

super(R.layout.files_activity, TAG);  //files_activity定义在layouts.xml中,实际为drawer_layout
 public BaseActivity(@LayoutRes int layoutId, String tag) {
        mLayoutId = layoutId;   //files_activity
        mTag = tag;  //FilesActivity
    }
public void onCreate(Bundle icicle) {
    addListenerForLaunchCompletion();  //Closes the activity when it's idle
    setContentView(mLayoutId);
    setContainer();设置容器,实质设置coordinator_layout,container_save和container_roots
    mInjector = getInjector();
      mState = getState(icicle);
      mDrawer = DrawerController.create(this, mInjector.config);
      Metrics.logActivityLaunch(mState, intent);
      mProviders = DocumentsApplication.getProvidersCache(this);
      mDocs = DocumentsAccess.create(this);
      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
     Breadcrumb breadcrumb =Shared.findView(this, R.id.dropdown_breadcrumb, R.id.horizontal_breadcrumb);
    mNavigator = new NavigationViewManager
    SearchManagerListener searchListener = new SearchManagerListener()
    ViewGroup chipGroup = findViewById(R.id.search_chip_group);
    mSearchManager = new SearchViewManager
      mNavigator.setSearchBarClickListener(v -> {
            mSearchManager.onSearchBarClicked();
            mNavigator.update();
        });
     mSortController = SortController.create
    mPreferencesMonitor.start();
}
//FilesActivity
 public void onCreate(Bundle icicle) {
    DocumentClipper clipper = DocumentsApplication.getDocumentClipper(this);
      mInjector.selectionMgr = DocsSelectionHelper.create();
    mInjector.focusManager = new FocusManager
    mInjector.menuManager = new MenuManager
      mInjector.actionModeController = new ActionModeController
    mInjector.actions = new ActionHandler   //AbstractActionHandler
     mInjector.searchManager = mSearchManager
     RootsFragment.show(getSupportFragmentManager(), null);
}

//布局drawer_layout

<androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/coordinator_layout">
    <androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawer_layout">
        <androidx.coordinatorlayout.widget.CoordinatorLayout android:orientation="vertical">
            <FrameLayout>
                <LinearLayout android:orientation="vertical">
                    <FrameLayout android:id="@+id/container_directory"/>
                </LinearLayout>
                <View android:id="@+id/drawer_edge"/>
            </FrameLayout>
            <androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/container_save" />
            <include layout="@layout/directory_app_bar"/>
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
        <LinearLayout android:id="@+id/drawer_roots" android:orientation="vertical">
            <androidx.appcompat.widget.Toolbar android:id="@+id/roots_toolbar"/>
            <FrameLayout android:id="@+id/container_roots" />
        </LinearLayout>
    </androidx.drawerlayout.widget.DrawerLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

//directory_app_bar
<com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar">
    <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar">
        <include layout="@layout/directory_header"/>
        <View android:id="@+id/toolbar_background_layout"/>
        <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar">
            <TextView android:id="@+id/searchbar_title"/>
            <com.android.documentsui.DropdownBreadcrumb android:id="@+id/dropdown_breadcrumb"/>
        </androidx.appcompat.widget.Toolbar>
    </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> 

//directory_header
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical">
    <include layout="@layout/search_chip_row"/>
    <include layout="@layout/apps_row"/>
    <LinearLayout>
        <TextView android:id="@+id/header_title">
        <androidx.appcompat.widget.ActionMenuView android:id="@+id/sub_menu"/>
    </LinearLayout>
    <include layout="@layout/column_headers"/>
</LinearLayout>

//search_chip_row
<HorizontalScrollView>
    <LinearLayout android:id="@+id/search_chip_group"/>
</HorizontalScrollView>

//apps_row
<LinearLayout android:id="@+id/apps_row" android:orientation="vertical">
    <TextView android:text="@string/apps_row_title"/>  //Browse files in other apps
    <HorizontalScrollView>
        <LinearLayout
            android:id="@+id/apps_group"/>
    </HorizontalScrollView>
</LinearLayout>

//column_headers  
  <merge /> 

//查看RootsFragment

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mInjector = getBaseActivity().getInjector();
        final View view = inflater.inflate(R.layout.fragment_roots, container, false);
        mList = (ListView) view.findViewById(R.id.roots_list);
        mList.setOnItemClickListener(mItemListener);
        mList.setOnGenericMotionListener(
        });
        mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        mList.setSelector(new ColorDrawable(Color.TRANSPARENT));
        return view;
    }
 public static RootsFragment show(FragmentManager fm, Intent includeApps) {
        final RootsFragment fragment = new RootsFragment();     
        ft.replace(R.id.container_roots, fragment);
        return fragment;
    }
 public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.root_menu_eject_root: //Eject
            case R.id.root_menu_open_in_new_window: //Open in new window
            case R.id.root_menu_paste_into_folder: //Paste into folder
            case R.id.root_menu_settings: //Storage settings
        }
    }

//fragment_roots
<com.android.documentsui.sidebar.RootsList xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/roots_list"/>

二…侧边栏对应的fragment是什么

侧边栏对应的界面,点击侧边栏会调用onRootPicked,从打印可以知道均由DirectoryFragment来显示界面,Audio相关打印如下

04-25 09:10:21.131  3867  3867 D test    : onRootPicked,root is Root{authority=com.android.providers.media.documents, rootId=audio_root, title=音频, isUsb=false, isSd=false, isMtp=false} @ content://com.android.providers.media.documents/root/audio_root
04-25 09:10:21.132  3867  3867 W ActionModeController: Tried to finish a null action mode.
04-25 09:10:21.132  3867  3867 D DocumentStack: Root changed to: Root{authority=com.android.providers.media.documents, rootId=audio_root, title=音频, isUsb=false, isSd=false, isMtp=false} @ content://com.android.providers.media.documents/root/audio_root
04-25 09:10:21.180  3867  3867 D DocumentStack: Adding doc to stack: DocumentInfo{docId=audio_root, name=音频, mimeType=vnd.android.document/directory, isContainer=true, isDirectory=true, isArchive=false, isInArchive=false, isPartial=false, isVirtual=false, isDeleteSupported=false, isCreateSupported=false, isMoveSupported=false, isRenameSupported=false, isMetadataSupported=false} @ content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.184  3867  3867 D DirectoryFragment: Showing directory: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.184  3867  3867 D DirectoryFragment: Creating new fragment for directory: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.306  3867  3867 D AbstractActionHandler: Creating new directory loader for: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.345  3867  3867 D AbstractActionHandler: Loader has finished for: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.345  3867  3867 I Model   : Updating model with new result set.  //更新cursor
04-25 09:10:21.346  3867  3867 D DirectoryFragment: Received model update. Loading=false

逻辑如下:

onRootPicked
-->refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);//传进的为1
-->refreshDirectory(anim);  //对应AnimationView.ANIM_NON
-->DirectoryFragment.showDirectory(fm, root, cwd, anim);
-->create(fm, root, doc, anim);
 public static void create(FragmentManager fm,RootInfo root,@Nullable DocumentInfo doc, @AnimationType int anim) {
        args.putParcelable(Shared.EXTRA_ROOT, root);
        args.putParcelable(Shared.EXTRA_DOC, doc);
        final FragmentTransaction ft = fm.beginTransaction();
        AnimationView.setupAnimations(ft, anim, args);
        final DirectoryFragment fragment = new DirectoryFragment();
        fragment.setArguments(args);  //设置相关参数

        ft.replace(getFragmentId(), fragment); //替代R.id.container_directory
        ft.commitAllowingStateLoss();
    }
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  /重点关注mRecView,为显示的列表,是一个recyclerview
        final View view = inflater.inflate(R.layout.fragment_directory, container, false);
        mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
        mRecView.setRecyclerListener(  //回收
                new RecyclerListener() {
                    @Override
                    public void onViewRecycled(ViewHolder holder) {
                        cancelThumbnailTask(holder.itemView);
                    }
                });
        mRecView.setItemAnimator(new DirectoryItemAnimator()); //设置添加、删除、移动、改变的动画效果
        mRecView.setOnDragListener(mDragHoverListener); //拖拽滑动事件
        return view;
  }
public void onActivityCreated(Bundle savedInstanceState) {
    mAdapter = new DirectoryAddonsAdapter(
                mAdapterEnv,
                new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper, mInjector.fileTypeLookup)
        );
    mRecView.setAdapter(mAdapter);
     mRecView.setLayoutManager(mLayout); //设置layout为GridLayout,
     new ScaleHelper(this.getContext(), mInjector.features, this::scaleLayout) .attach(mRecView);
       mActions.loadDocumentsForCurrentStack();  
}

三.DirectoryFragment中mRecView的数据来自于哪

适配器DirectoryAddonsAdapter,主要逻辑如下

//传进的参数,mAdapterEnv为DirectoryFragment.java的子类AdapterEnvironment
//mIconHelper为IconHelper
//fileTypeLookup为<String, String> 的表
mAdapter = new DirectoryAddonsAdapter(mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper, mInjector.fileTypeLookup));

//构造方法
DirectoryAddonsAdapter(Environment environment, DocumentsAdapter delegate) {
        mEnv = environment;
        mDelegate = delegate;
        mHeaderMessage = new HeaderMessage(environment, this::onDismissHeaderMessage);
        mInflateMessage = new InflateMessage(environment, this::onDismissHeaderMessage);
        mDelegate.registerAdapterDataObserver(new EventRelay());
        mModelUpdateListener = this::onModelUpdate;
    }
//onCreateViewHolder
 public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            default:
                holder = mDelegate.createViewHolder(parent, viewType); //Audio界面对应的VierType为2,走default
        }
        return holder;
    }
 public void onBindViewHolder(DocumentHolder holder, int p, List<Object> payload) {
        switch (holder.getItemViewType()) {
            default:
                mDelegate.onBindViewHolder(holder, toDelegatePosition(p), payload);//走default
                break;
        }
    }
//ModelBackedDocumentsAdapter中的createViewHolders

四.整体逻辑

DirectoryLoader进行加载数据,DirectoryFragment进行界面的显示,DirectoryAddonsAdapter为界面的适配器
在这里插入图片描述
在这里插入图片描述

五.传进的参数root来源

为RootsFragment中的onLoadFinished的结果,来自RootsLoader,其监听com.android.documentsui.action.ROOT_CHANGED,由ProvidersCache中的doInBackground发送
而ProvidersCache中由RootsChangedObserver,采用ContentObserver机制监听数据库是否变化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值