欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android6.0 Launcher2应用解析

程序员文章站 2024-03-05 15:52:37
在之前我们分析了android6.0系统在启动时安装应用程序的过程,这些应用程序安装好之后,launcher应用就负责把它们在桌面上展示出来。 一、ams启动laun...

在之前我们分析了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;
  }

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