Android6.0 Launcher2应用解析

作者:kc58236582 时间:2021-10-29 19:19:43 

在之前我们分析了Android6.0系统在启动时安装应用程序的过程,这些应用程序安装好之后,Launcher应用就负责把它们在桌面上展示出来。

一、AMS启动Launcher 

Launcher应用是在AMS的systemReady方法中直接调用startHomeActivityLocked启动的,下面是systemReady启动Launcher的代码。 

startHomeActivityLocked(mCurrentUserId, "systemReady");我们来看下这个函数,先调用了getHomeIntent方法来获取Intent,然后也是调用resolveActivityInfo函数从PKMS获取ActivityInfo,接着当进程没有启动的话,调用ActivityStackSupervisor的startHomeActivity函数


  boolean startHomeActivityLocked(int userId, String reason) {
   if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
       && mTopAction == null) {
     // We are running in factory test mode, but unable to find
     // the factory test app, so just sit around displaying the
     // error message and don't try to start anything.
     return false;
   }
   Intent intent = getHomeIntent();//获取intent
   ActivityInfo aInfo =
     resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//获取ActivityInfo
   if (aInfo != null) {
     intent.setComponent(new ComponentName(
         aInfo.applicationInfo.packageName, aInfo.name));
     // Don't do this if the home app is currently being
     // instrumented.
     aInfo = new ActivityInfo(aInfo);
     aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
     ProcessRecord app = getProcessRecordLocked(aInfo.processName,
         aInfo.applicationInfo.uid, true);
     if (app == null || app.instrumentationClass == null) {//进程没有启动调用
       EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName);
       intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
       mStackSupervisor.startHomeActivity(intent, aInfo, reason);
     }
   }

return true;
 }

我们先来看看getHomeIntent这个函数。


Intent getHomeIntent() {
   Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
   intent.setComponent(mTopComponent);
   if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
     intent.addCategory(Intent.CATEGORY_HOME);
   }
   return intent;
 }

然后我们来看下ActivityStackSupervisor的startHomeActivity函数,它也是调用了startActivityLocked来启动Activity的,在之前的博客分析过这个函数这里我们就不介绍了。


  void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
   moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
   startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,
       null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,
       null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,
       null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,
       0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,
       false /* componentSpecified */,
       null /* outActivity */, null /* container */, null /* inTask */);
   if (inResumeTopActivity) {
     // If we are in resume section already, home activity will be initialized, but not
     // resumed (to avoid recursive resume) and will stay that way until something pokes it
     // again. We need to schedule another resume.
     scheduleResumeTopActivities();
   }
 }

二、Launcher启动 

接着我们来看下Launcher的AndroidManifest.xml,我们看下其主Activity有一个category为android.intent.category.HOME 


 <application
   android:name="com.android.launcher2.LauncherApplication"
   android:label="@string/application_name"
   android:icon="@mipmap/ic_launcher_home"
   android:hardwareAccelerated="true"
   android:largeHeap="@bool/config_largeHeap"
   android:supportsRtl="true">
   <activity
     android:name="com.android.launcher2.Launcher"
     android:launchMode="singleTask"
     android:clearTaskOnLaunch="true"
     android:stateNotNeeded="true"
     android:resumeWhilePausing="true"
     android:theme="@style/Theme"
     android:windowSoftInputMode="adjustPan"
     android:screenOrientation="nosensor">
     <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.HOME" />
       <category android:name="android.intent.category.DEFAULT" />
       <category android:name="android.intent.category.MONKEY"/>
     </intent-filter>
   </activity>
   ......

在Launcher.java的onCreate函数中调用了mModel.startLoader函数


  protected void onCreate(Bundle savedInstanceState) {
   ......
   if (!mRestoring) {
     if (sPausedFromUserAction) {
       // If the user leaves launcher, then we should just load items asynchronously when
       // they return.
       mModel.startLoader(true, -1);
     } else {
       // We only load the page synchronously if the user rotates (or triggers a
       // configuration change) while launcher is in the foreground
       mModel.startLoader(true, mWorkspace.getCurrentPage());
     }
   }
   ......

startLoader函数会post一个Runnable消息,我们来看下它的run方法 


 public void startLoader(boolean isLaunching, int synchronousBindPage) {
   synchronized (mLock) {
     if (DEBUG_LOADERS) {
       Log.d(TAG, "startLoader isLaunching=" + isLaunching);
     }

// Clear any deferred bind-runnables from the synchronized load process
     // We must do this before any loading/binding is scheduled below.
     mDeferredBindRunnables.clear();

// Don't bother to start the thread if we know it's not going to do anything
     if (mCallbacks != null && mCallbacks.get() != null) {
       // If there is already one running, tell it to stop.
       // also, don't downgrade isLaunching if we're already running
       isLaunching = isLaunching || stopLoaderLocked();
       mLoaderTask = new LoaderTask(mApp, isLaunching);
       if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
         mLoaderTask.runBindSynchronousPage(synchronousBindPage);
       } else {
         sWorkerThread.setPriority(Thread.NORM_PRIORITY);
         sWorker.post(mLoaderTask);
       }
     }
   }
 }

 在它的run方法中会调用loadAndBindAllApps函数,在loadAndBindAllApps函数中又会调用loadAllAppsByBatch函数 


   public void run() {
     synchronized (mLock) {
       mIsLoaderTaskRunning = true;
     }

final Callbacks cbk = mCallbacks.get();
     final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;

keep_running: {
       // Elevate priority when Home launches for the first time to avoid
       // starving at boot time. Staring at a blank home is not cool.
       synchronized (mLock) {
         if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
             (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
         Process.setThreadPriority(mIsLaunching
             ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
       }

// First step. Load workspace first, this is necessary since adding of apps from
       // managed profile in all apps is deferred until onResume. See http://b/17336902.
       if (loadWorkspaceFirst) {
         if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
         loadAndBindWorkspace();
       } else {
         Log.d(TAG, "step 1: special: loading all apps");
         loadAndBindAllApps();
       }

我们先来看下loadAndBindAllApps函数,这个函数先进入while循环,然后调用了LauncherApps的getActivityList函数,后面又会调用callbacks的bindAllApplications


   private void loadAllAppsByBatch() {
     final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
     ......
     mBgAllAppsList.clear();
     final int profileCount = profiles.size();
     for (int p = 0; p < profileCount; p++) {
       ......
       while (i < N && !mStopped) {
         if (i == 0) {
           final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
           apps = mLauncherApps.getActivityList(null, user);
           ......

mHandler.post(new Runnable() {
           public void run() {
             final long t = SystemClock.uptimeMillis();
             if (callbacks != null) {
               if (firstProfile) {
                 callbacks.bindAllApplications(added);
               } else {
                 callbacks.bindAppsAdded(added);
               }
               if (DEBUG_LOADERS) {
                 Log.d(TAG, "bound " + added.size() + " apps in "
                   + (SystemClock.uptimeMillis() - t) + "ms");
               }
             } else {
               Log.i(TAG, "not binding apps: no Launcher activity");
             }
           }
         });
         ......

我们先来看LauncherApps的getActivityList函数,它先用mService成员变量调用getLauncherActivities函数获取到list<ResolveInfo>,然后封装在ArrayList<LauncherActivityInfo> 中。


 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
   List<ResolveInfo> activities = null;
   try {
     activities = mService.getLauncherActivities(packageName, user);
   } catch (RemoteException re) {
     throw new RuntimeException("Failed to call LauncherAppsService");
   }
   if (activities == null) {
     return Collections.EMPTY_LIST;
   }
   ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
   final int count = activities.size();
   for (int i = 0; i < count; i++) {
     ResolveInfo ri = activities.get(i);
     long firstInstallTime = 0;
     try {
       firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
         PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
     } catch (NameNotFoundException nnfe) {
       // Sorry, can't find package
     }
     LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
         firstInstallTime);
     if (DEBUG) {
       Log.v(TAG, "Returning activity for profile " + user + " : "
           + lai.getComponentName());
     }
     lais.add(lai);
   }
   return lais;
 }

其service是class LauncherAppsImpl extends ILauncherApps.Stub 下面是getLauncherActivities函数,肯定也是通过PKMS来获取相关Activity的ResolveInfo的。 


   @Override
   public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
       throws RemoteException {
     ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
     if (!isUserEnabled(user)) {
       return new ArrayList<ResolveInfo>();
     }

final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
     mainIntent.setPackage(packageName);
     long ident = Binder.clearCallingIdentity();
     try {
       List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,
           user.getIdentifier());
       return apps;
     } finally {
       Binder.restoreCallingIdentity(ident);
     }
   }

最后回调Launcher.java的bindAllApplications函数,最后在这个函数中可以在桌面上展示系统中所有的应用程序了。


 public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
   Runnable setAllAppsRunnable = new Runnable() {
     public void run() {
       if (mAppsCustomizeContent != null) {
         mAppsCustomizeContent.setApps(apps);
       }
     }
   };

// Remove the progress bar entirely; we could also make it GONE
   // but better to remove it since we know it's not going to be used
   View progressBar = mAppsCustomizeTabHost.
     findViewById(R.id.apps_customize_progress_bar);
   if (progressBar != null) {
     ((ViewGroup)progressBar.getParent()).removeView(progressBar);

// We just post the call to setApps so the user sees the progress bar
     // disappear-- otherwise, it just looks like the progress bar froze
     // which doesn't look great
     mAppsCustomizeTabHost.post(setAllAppsRunnable);
   } else {
     // If we did not initialize the spinner in onCreate, then we can directly set the
     // list of applications without waiting for any progress bars views to be hidden.
     setAllAppsRunnable.run();
   }
 }

三、显示应用图标 

我们再来看下Launcher的onClick函数,当调用showWorkspace可以显示所有应用的图标。


  public void onClick(View v) {
   // Make sure that rogue clicks don't get through while allapps is launching, or after the
   // view has detached (it's possible for this to happen if the view is removed mid touch).
   if (v.getWindowToken() == null) {
     return;
   }

if (!mWorkspace.isFinishedSwitchingState()) {
     return;
   }

Object tag = v.getTag();
   if (tag instanceof ShortcutInfo) {
     // Open shortcut
     final Intent intent = ((ShortcutInfo) tag).intent;
     int[] pos = new int[2];
     v.getLocationOnScreen(pos);
     intent.setSourceBounds(new Rect(pos[0], pos[1],
         pos[0] + v.getWidth(), pos[1] + v.getHeight()));

boolean success = startActivitySafely(v, intent, tag);

if (success && v instanceof BubbleTextView) {
       mWaitingForResume = (BubbleTextView) v;
       mWaitingForResume.setStayPressed(true);
     }
   } else if (tag instanceof FolderInfo) {
     if (v instanceof FolderIcon) {
       FolderIcon fi = (FolderIcon) v;
       handleFolderClick(fi);
     }
   } else if (v == mAllAppsButton) {
     if (isAllAppsVisible()) {
       showWorkspace(true);
     } else {
       onClickAllAppsButton(v);
     }
   }
 }

在showWorkspace中会显示所有的图标


  void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
   if (mState != State.WORKSPACE) {
     boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
     mWorkspace.setVisibility(View.VISIBLE);
     hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);

// Show the search bar (only animate if we were showing the drop target bar in spring
     // loaded mode)
     if (mSearchDropTargetBar != null) {
       mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
     }

// We only need to animate in the dock divider if we're going from spring loaded mode
     showDockDivider(animated && wasInSpringLoadedMode);

// Set focus to the AppsCustomize button
     if (mAllAppsButton != null) {
       mAllAppsButton.requestFocus();
     }
   }

mWorkspace.flashScrollingIndicator(animated);

// Change the state *after* we've called all the transition code
   mState = State.WORKSPACE;

// Resume the auto-advance of widgets
   mUserPresent = true;
   updateRunning();

// Send an accessibility event to announce the context change
   getWindow().getDecorView()
       .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
 }

而点击应用图标,最终会调用Launcher.java的startActivitySafely来启动应用。这里调用的startActivity就是Activity的startActivity函数。 


 boolean startActivitySafely(View v, Intent intent, Object tag) {
   boolean success = false;
   try {
     success = startActivity(v, intent, tag);
   } catch (ActivityNotFoundException e) {
     Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
     Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   }
   return success;
 }
标签:android6.0,Launcher2
0
投稿

猜你喜欢

  • while和for可以相互转换的例子分享

    2023-08-23 02:17:46
  • SpringCloud高可用配置中心Config详解

    2023-11-17 11:25:00
  • Android 仿余额宝数字跳动动画效果完整代码

    2021-06-02 16:53:41
  • Android编程开发中的正则匹配操作示例

    2022-12-24 15:24:45
  • 利用Flutter实现“孔雀开屏”的动画效果

    2021-11-04 21:24:17
  • Springboot Vue可配置调度任务实现示例详解

    2023-11-09 03:33:19
  • servlet上传文件实现代码详解(四)

    2021-09-04 21:17:23
  • 快速学习C# 设计模式之职责链模式

    2022-03-28 17:47:45
  • Android实现点击获取验证码60秒后重新获取功能

    2021-09-21 22:42:01
  • Java Socket编程(四) 重复和并发服务器

    2022-06-29 07:07:06
  • c# socket网络编程接收发送数据示例代码

    2021-08-31 06:14:13
  • C#实现员工ID卡的识别功能

    2021-08-11 23:49:25
  • android桌面悬浮窗显示录屏时间控制效果

    2022-04-21 14:54:12
  • Android使用setContentView实现页面的转换效果

    2021-07-30 15:13:32
  • ViewPager顶部导航栏联动效果(标题栏条目多)

    2022-11-03 13:19:38
  • ElasticSearch如何设置某个字段不分词浅析

    2023-01-29 07:22:30
  • Android TextView 设置字体大小的方法

    2022-06-18 03:05:25
  • java中mybatis和hibernate的用法总结

    2022-08-23 07:24:38
  • Java日常练习题,每天进步一点点(63)

    2021-10-22 12:35:44
  • Android自定义播放器控件VideoView

    2023-06-25 03:29:00
  • asp之家 软件编程 m.aspxhome.com