Android8.1 Car Model No Launcher 实现原理

我们知道Car Model 属于 No Launcher 模式,但真的是没有 Launcher应用吗?我们一步一步分析:

Car Module Navigation Bar

packages/services/Car/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml


    
    
        @drawable/car_ic_navigation
        @drawable/car_ic_phone
        @drawable/car_ic_overview
        @drawable/car_ic_music
        @drawable/car_ic_car
    
    
        
        intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end
        intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end
        intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;S.system_command=toggle_notifications;end
        intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end
        intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end
    
    
        
        
        
        
        
        intent:#Intent;component=com.google.android.car.bugreport/.BugReportActivity;end
    
    
        android.intent.category.APP_MAPS
        android.intent.category.APP_MESSAGING
        
        android.intent.category.APP_MUSIC
        
    
    
        com.android.car.mapsplaceholder
        com.android.car.dialer
        com.android.car.overview
        
        com.android.car.hvac;com.android.settings;com.android.car.settings;com.android.vending;com.google.android.car.bugreport;com.google.android.car.kitchensink;com.android.car.systemupdater;org.chromium.webview_shell;com.android.contacts;org.codeaurora.bluetooth.bttestapp;com.google.android.projection.sink
    

我们通过这个配置文件可以看出这个文件配置了Car Module 中NavigationBar 上的按钮个数,以及按钮点击之后所携带的intent参数,Uri,Category, Package Filters。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java

    /**
     * Handles a click on a facet. A click will trigger the given Intent.
     *
     * @param index The index of the facet that was clicked.
     */
    private void onFacetClicked(Intent intent, int index) {
        String packageName = intent.getPackage();

        if (packageName == null) {
            return;
        }

        intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
        intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
        // The facet is identified by the index in which it was added to the nav bar.
        // This value can be used to determine which facet was selected
        intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));

        // If the current facet is clicked, we want to launch the picker by default
        // rather than the "preferred/last run" app.
        intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);

        int stackId = StackId.FULLSCREEN_WORKSPACE_STACK_ID;
        if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
            stackId = StackId.HOME_STACK_ID;
        }

        setCurrentFacet(index);
        mStatusBar.startActivityOnStack(intent, stackId);
    }

可以发现NavigationBar 启动Map, dialer, 等等其他应用,都是先去启动包名为com.android.support.car.lenspicker这个应用。
packages/apps/Car/LensPicker/AndroidManifest.xml



    

    
    
    

    

        

        

        
            
                
                
                
                
            
        
    

我们可以看到 它是一个Launcher应用
packages/apps/Car/LensPicker/src/com/android/support/car/lenspicker/LensPickerTrampolineActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        PackageManager packageManager = getPackageManager();
        mSharedPrefs = LensPickerUtils.getFacetSharedPrefs(this);

        Intent intent = getIntent();

        String systemCommand =
                intent.getStringExtra(LensPickerConstants.EXTRA_FACET_SYSTEM_COMMAND);
        if (systemCommand != null && executeSystemCommand(systemCommand)) {
            finish();
            return;
        }

        // Hide the shade if switching to a different facet.
        hideNotificationsShade();

        String facetId = intent.getStringExtra(LensPickerConstants.EXTRA_FACET_ID);

        // If no facetId was passed to this activity, then that means that we cannot retrieve the
        // application categories to determine an activity to launch. Thus, just launch the
        // default application.
        if (TextUtils.isEmpty(facetId)) {
            launchLastRunOrDefaultApplication();
            finish();
            return;
        }

        String facetKey = LensPickerUtils.getFacetKey(facetId);
        String savedPackageName = mSharedPrefs.getString(facetKey, null /* defaultValue */);
        String[] categories = intent.getStringArrayExtra(
                LensPickerConstants.EXTRA_FACET_CATEGORIES);
        String[] packages = intent.getStringArrayExtra(LensPickerConstants.EXTRA_FACET_PACKAGES);

        boolean alwaysLaunchPicker = intent.getBooleanExtra(
                LensPickerConstants.EXTRA_FACET_LAUNCH_PICKER, false);

        Intent launchIntent;
        if (!alwaysLaunchPicker && savedPackageName != null) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Launching saved package: " + savedPackageName);
            }

            launchIntent = packageManager.getLaunchIntentForPackage(savedPackageName);
        } else {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Delegating to LensPickerActivity to handle application launch.");
            }

            launchIntent = new Intent(this, LensPickerActivity.class);
            launchIntent.putExtra(LensPickerConstants.EXTRA_FACET_PACKAGES, packages);
            launchIntent.putExtra(LensPickerConstants.EXTRA_FACET_CATEGORIES, categories);
            launchIntent.putExtra(LensPickerConstants.EXTRA_FACET_ID, facetId);
        }

        startActivity(launchIntent);
        finish();
    }

从Intent中取出package,categrory,facet 信息,然后启动LensPickerActivity
packages/apps/Car/LensPicker/src/com/android/support/car/lenspicker/LensPickerActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPackageManager = getPackageManager();
        mSharedPrefs = LensPickerUtils.getFacetSharedPrefs(this);

        setContentView(R.layout.lens_list);
        mPagedListView = (PagedListView) findViewById(R.id.list_view);
        // Set this to light mode, since the scroll bar buttons always appear
        // on top of a dark scrim.
        mPagedListView.setLightMode();

        findViewById(R.id.dismiss_area).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();

        Intent intent = getIntent();
        String[] categories = intent.getStringArrayExtra(
                LensPickerConstants.EXTRA_FACET_CATEGORIES);
        String[] packages = intent.getStringArrayExtra(LensPickerConstants.EXTRA_FACET_PACKAGES);
        String facetId = intent.getStringExtra(LensPickerConstants.EXTRA_FACET_ID);

        List resolveInfos = getComponents(packages, categories);

        if (resolveInfos != null && resolveInfos.size() == 1) {
            // Directly launch the package rather than showing a list of 1.
            ResolveInfo rInfo = resolveInfos.get(0);
            String packageName = LensPickerUtils.getPackageName(rInfo);
            Intent launchIntent = LensPickerUtils.getLaunchIntent(packageName, rInfo,
                    mPackageManager);
            if (launchIntent != null) {
                launch(facetId, packageName, launchIntent);
            } else {
                Log.e(TAG, "Failed to get launch intent for package" + packageName);
            }
            finish();
            return;
        }

        mPagedListView.setAdapter(new LensPickerAdapter(this, resolveInfos, facetId,
                this /* LensPickerSelectionHandler */));
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mLastLaunchedFacetId == null || mLastLaunchedPackageName == null
                || mLastLaunchedIntent == null) {
            return;
        }

        LensPickerUtils.saveLastLaunchedAppInfo(mSharedPrefs, mLastLaunchedFacetId,
                mLastLaunchedPackageName, mLastLaunchedIntent);
    }

在onResume 方法中去启动Intent中保留的应用。
第一:以上我们可以得到 Car Model No Launcher 启动流程:

启动目标应用 --> 跳转LensPicker应用 --> 启动目标应用

第二:让我们无感它的切换过程,认定它是No Launcher 模式主要是 LensPicker AndroidManifest.xml 文件中配置的这两个属性:

android:excludeFromRecents="true"

该属性让我们在Recent 中无法查看到这个应用进程的信息

android:noHistory="true"

该属性清除了它自身的启动路径TaskRecord,
注意:让我们使用Back 按键时直接在相邻的两个应用中进行TaskRecord切换,避免再次经过LensPicker做启动,同时也减轻了LensPicker的工作,不需要保留所有的启动记录,而是让专业的人去做专业的事(AMS, ActivityStack, TaskRecord...)。
第三:而LensPicker只保留了当前最新的启动应用记录,当用户触发Home 按键,会回到LensPickerActivity页面中,而LensPickerActivity保留了最新的应用启动记录,会跳转到目标应用,这就实现了Car Model No Launcher模式。
下面我们贴一下栈信息,印证我们的想法:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
  Stack #1:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #217
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{c4be22 #217 A=com.android.car.radio U=0 StackId=1 sz=1}
      userId=0 effectiveUid=u0a11 mCallingUid=u0a44 mUserSetupComplete=true mCallingPackage=com.android.support.car.lenspicker
      affinity=com.android.car.radio
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.android.car.radio cmp=com.android.car.radio/.CarRadioActivity}
      realActivity=com.android.car.radio/.CarRadioActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
      rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{aa50792 u0 com.android.car.radio/.CarRadioActivity t217}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/217_task_thumbnail.png
      stackId=1
      hasBeenVisible=true mResizeMode=RESIZE_MODE_RESIZEABLE mSupportsPictureInPicture=false isResizeable=true firstActiveTime=1564637925171 lastActiveTime=1564637925203 (inactive for 29s)
      * Hist #0: ActivityRecord{aa50792 u0 com.android.car.radio/.CarRadioActivity t217}
          packageName=com.android.car.radio processName=com.android.car.radio
          launchedFromUid=10044 launchedFromPackage=com.android.support.car.lenspicker userId=0
          app=ProcessRecord{cdd5c6d 2175:com.android.car.radio/u0a11}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.android.car.radio cmp=com.android.car.radio/.CarRadioActivity }
          frontOfTask=true task=TaskRecord{c4be22 #217 A=com.android.car.radio U=0 StackId=1 sz=1}
          taskAffinity=com.android.car.radio
          realActivity=com.android.car.radio/.CarRadioActivity
          baseDir=/system/priv-app/CarRadioApp/CarRadioApp.apk
          dataDir=/data/user/0/com.android.car.radio
          stateNotNeeded=false componentSpecified=true mActivityType=0
          compat={160dpi always-compat} labelRes=0x7f0d0028 icon=0x7f020076 theme=0x7f0e01a6
          mLastReportedConfigurations:
           mGlobalConfig={1.0 310mcc260mnc [en_US] ldltr sw990dp w1964dp h878dp 160dpi xlrg long land car finger -keyb/v/h tball/v appBounds=Rect(0, 0 - 1964, 934) s.7}
           mOverrideConfig={1.0 310mcc260mnc [en_US] ldltr sw990dp w1964dp h878dp 160dpi xlrg long land car finger -keyb/v/h tball/v appBounds=Rect(0, 0 - 1964, 934) s.7}
          CurrentConfiguration={1.0 310mcc260mnc [en_US] ldltr sw990dp w1964dp h878dp 160dpi xlrg long land car finger -keyb/v/h tball/v appBounds=Rect(0, 0 - 1964, 934) s.7}
          taskDescription: iconFilename=null label="null" primaryColor=fff5f5f5
            backgroundColor=fffafafa
            statusBarColor=ff283593
            navigationBarColor=ff000000
          launchFailed=false launchCount=1 lastLaunchTime=-29s467ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false
          keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-26s401ms
          connections=[ConnectionRecord{9ddac5 u0 CR com.android.car.radio/.RadioService:@eecd3c}]
          resizeMode=RESIZE_MODE_RESIZEABLE
          mLastReportedMultiWindowMode=false mLastReportedPictureInPictureMode=false

    Running activities (most recent first):
      TaskRecord{c4be22 #217 A=com.android.car.radio U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{aa50792 u0 com.android.car.radio/.CarRadioActivity t217}

    mResumedActivity: ActivityRecord{aa50792 u0 com.android.car.radio/.CarRadioActivity t217}

  Stack #0:
  mFullscreen=true
  isSleeping=false
  mBounds=null

    mLastPausedActivity: ActivityRecord{21dc322 u0 com.android.support.car.lenspicker/.LensPickerTrampolineActivity t-1 f}

    mLastNoHistoryActivity: ActivityRecord{21dc322 u0 com.android.support.car.lenspicker/.LensPickerTrampolineActivity t-1 f}

  ResumedActivity: ActivityRecord{aa50792 u0 com.android.car.radio/.CarRadioActivity t217}
  mFocusedStack=ActivityStack{5ff80b3 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{5ff80b3 stackId=1, 1 tasks}
  mCurTaskIdForUser={0=217}
  mUserStackInFront={}
  mStacks={0=ActivityStack{4728670 stackId=0, 0 tasks}, 1=ActivityStack{5ff80b3 stackId=1, 1 tasks}}
  mLockTaskModeState=NONE  mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]
  KeyguardController:
    mKeyguardShowing=false
    mKeyguardGoingAway=false
    mOccluded=false
    mDismissingKeyguardActivity=null
    mDismissalRequested=false
    mVisibilityTransactionDepth=0

你可能感兴趣的:(Android8.1 Car Model No Launcher 实现原理)