栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

Android6.0 Launcher2应用解析

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android6.0 Launcher2应用解析

在之前我们分析了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 , intent, null , aInfo,
 null , null , null ,
 null , 0 , 0 , 0 ,
 null , 0 , 0 ,
 0 , null , false ,
 false ,
 null , null , null );
    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 

  
     
      
 
 
 
 
      
    
    ...... 

在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,然后封装在ArrayList 中。

  public List getActivityList(String packageName, UserHandle user) {
    List 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 lais = new ArrayList();
    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 getLauncherActivities(String packageName, UserHandle user)
 throws RemoteException {
      ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
      if (!isUserEnabled(user)) {
 return new ArrayList();
      }

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

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

  public void bindAllApplications(final ArrayList 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;
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/161370.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号