龙空技术网

Android源码分析之设置默认启动的Launcher

嵌入式软件开发 179

前言:

而今朋友们对“onclick属性值”都比较关怀,小伙伴们都需要了解一些“onclick属性值”的相关资讯。那么小编同时在网摘上汇集了一些关于“onclick属性值””的相关知识,希望各位老铁们能喜欢,兄弟们一起来学习一下吧!

背景

在Android启动过程中,系统中如果有多个Launcher时,会弹出一个对话框让我们选择哪个应用作为Launcher。所以我们来分析一下Launcher的启动过程。

Launcher的启动过程首先是从ActivityManagerService的startHomeActivityLocked方法开始。所以先来看该方法。

源码分析

//Android6.0   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;       }       //获取Home Intent       Intent intent = getHomeIntent();       //获取Activity信息       ActivityInfo aInfo =           resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);       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);           //根据userId获取应用信息           aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);           //获取应用进程,每个android进程,在AMS中对应有一个ProcessRecord           ProcessRecord app = getProcessRecordLocked(aInfo.processName,                   aInfo.applicationInfo.uid, true);           if (app == null || app.instrumentationClass == null) {               //如果进程不存在,就要在启动时为该应用新建一个堆栈(FLAG_ACTIVITY_NEW_TASK)               intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);               //启动Home Activity(Launcher)               mStackSupervisor.startHomeActivity(intent, aInfo, reason);           }       }       return true;   }

接下来看ActivityStackSupervisor类中的startHomeActivity方法

//Android6.0   void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {       //将Home Activity移动到栈顶       moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);       //启动Home Activity       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,但是如果有多个Launcher,会弹出一个Activity来让我们选择,这个Activity为ResolverActivity。

先看一下这个Activity的介绍:

/*** This activity is displayed when the system attempts to start an Intent for* which there is more than one matching activity, allowing the user to decide* which to go to.  It is not normally used directly by application developers.*/

上面的意思就是当Intent要启动(隐式)的Activity时,有多个Activity匹配这个Intent,就会显示这个Activity,让用户选择要启动哪个。其实就是一个应用选择器,这个Activity不单单是有多个Launcher时才会显示,其它应用也会。

接下就来看它的onCreate方法

@Override   protected void onCreate(Bundle savedInstanceState) {       // Use a specialized prompt when we're handling the 'Home' app startActivity()       //当启动的是Launcher时会弹出一个专门的提示       final Intent intent = makeMyIntent();       final Set<String> categories = intent.getCategories();       if (Intent.ACTION_MAIN.equals(intent.getAction())               && categories != null               && categories.size() == 1               && categories.contains(Intent.CATEGORY_HOME)) {           // Note: this field is not set to true in the compatibility version.           mResolvingHome = true;       }       setSafeForwardingMode(true);       onCreate(savedInstanceState, intent, null, 0, null, null, true);   }

最后一行又调用另一个onCreate方法,继续来看这个方法

protected void onCreate(Bundle savedInstanceState, Intent intent,           CharSequence title, int defaultTitleRes, Intent[] initialIntents,           List<ResolveInfo> rList, boolean alwaysUseOption) {       setTheme(R.style.Theme_DeviceDefault_Resolver);       super.onCreate(savedInstanceState);       // Determine whether we should show that intent is forwarded       // from managed profile to owner or other way around.       setProfileSwitchMessageId(intent.getContentUserHint());       try {           //获取UID,Android支持多用户,不同用户可能Launcher不同           mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(                   getActivityToken());       } catch (RemoteException e) {           mLaunchedFromUid = -1;       }       if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {           // Gulp!           finish();           return;       }       //获取PackageManager       mPm = getPackageManager();       mPackageMonitor.register(this, getMainLooper(), false);       mRegistered = true;       //获取ActivityManager       final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);       //获取Launcher Icon的大小       mIconDpi = am.getLauncherLargeIconDensity();       // Add our initial intent as the first item, regardless of what else has already been added.       mIntents.add(0, new Intent(intent));       //获取推荐的包名       final String referrerPackage = getReferrerPackageName();       mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);       //设置布局,类似于setContentView       if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {           return;       }       // Prevent the Resolver window from becoming the top fullscreen window and thus from taking       // control of the system bars.       //防止选择窗口变成全屏       getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);       //接下来就是初始化界面,设置监听器       final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);       if (rdl != null) {           rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {               @Override               public void onDismissed() {                   finish();               }           });           if (isVoiceInteraction()) {               rdl.setCollapsed(false);           }           mResolverDrawerLayout = rdl;       }       if (title == null) {           title = getTitleForAction(intent.getAction(), defaultTitleRes);       }       if (!TextUtils.isEmpty(title)) {           final TextView titleView = (TextView) findViewById(R.id.title);           if (titleView != null) {               titleView.setText(title);           }           setTitle(title);           // Try to initialize the title icon if we have a view for it and a title to match           final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);           if (titleIcon != null) {               ApplicationInfo ai = null;               try {                   if (!TextUtils.isEmpty(referrerPackage)) {                       ai = mPm.getApplicationInfo(referrerPackage, 0);                   }               } catch (NameNotFoundException e) {                   Log.e(TAG, "Could not find referrer package " + referrerPackage);               }               if (ai != null) {                   titleIcon.setImageDrawable(ai.loadIcon(mPm));               }           }       }       final ImageView iconView = (ImageView) findViewById(R.id.icon);       final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();       if (iconView != null && iconInfo != null) {           new LoadIconIntoViewTask(iconInfo, iconView).execute();       }       if (alwaysUseOption || mAdapter.hasFilteredItem()) {           final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);           if (buttonLayout != null) {               buttonLayout.setVisibility(View.VISIBLE);               //Always按钮               mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);               //Once按钮               mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);           } else {               mAlwaysUseOption = false;           }       }       if (mAdapter.hasFilteredItem()) {           setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);           mOnceButton.setEnabled(true);       }       mProfileView = findViewById(R.id.profile_button);       if (mProfileView != null) {           mProfileView.setOnClickListener(new View.OnClickListener() {               @Override               public void onClick(View v) {                   final DisplayResolveInfo dri = mAdapter.getOtherProfile();                   if (dri == null) {                       return;                   }                   // Do not show the profile switch message anymore.                   mProfileSwitchMessageId = -1;                   onTargetSelected(dri, false);                   finish();               }           });           bindProfileView();       }       if (isVoiceInteraction()) {           onSetupVoiceInteraction();       }       getWindow().getDecorView().addOnAttachStateChangeListener(               new OnAttachStateChangeListener() {           @Override           public void onViewAttachedToWindow(View v) {               v.getViewRootImpl().setDrawDuringWindowsAnimating(true);           }           @Override           public void onViewDetachedFromWindow(View v) {           }       });   }

上面其实主要就是在初始化弹出的选择界面,设置监听器之类的。

接下来来看Always和once按钮的监听器

public void onButtonClick(View v) {       //获取Button id       final int id = v.getId();       startSelected(mAlwaysUseOption ?                       mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),               id == R.id.button_always,               mAlwaysUseOption);   }

上面的监听器是在xml中设置onclick属性,这个方法就是调用了startSelected方法,用来启动所选的Launcher。

void startSelected(int which, boolean always, boolean filtered) {       if (isFinishing()) {           return;       }       //ResolveInfo是Activity信息的集合       ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);       if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {           Toast.makeText(this, String.format(getResources().getString(                   com.android.internal.R.string.activity_resolver_work_profiles_support),                   ri.activityInfo.loadLabel(getPackageManager()).toString()),                   Toast.LENGTH_LONG).show();           return;       }       //获取要启动的Activity(Launcher)       //TargetInfo包含了Activity的信息       TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);       if (onTargetSelected(target, always)) {           finish();       }   }

上面最关键的就是最后几行了,调用onTargetSelected方法,然后结束ResolverActivity。

protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {       //获取Activity信息       final ResolveInfo ri = target.getResolveInfo();       final Intent intent = target != null ? target.getResolvedIntent() : null;           ........           if (filter != null) {               final int N = mAdapter.mOrigResolveList.size();               ComponentName[] set = new ComponentName[N];               int bestMatch = 0;               for (int i=0; i<N; i++) {                   ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);                   set[i] = new ComponentName(r.activityInfo.packageName,                           r.activityInfo.name);                   if (r.match > bestMatch) bestMatch = r.match;               }               //always被选中               if (alwaysCheck) {                   final int userId = getUserId();                   final PackageManager pm = getPackageManager();                   // Set the preferred Activity                   //设置首选的Activity                   pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());                   //省略                   ........               } else {                   //省略                 ........               }           }       }       if (target != null) {           safelyStartActivity(target);       }       return true;   }

上面最重要的就是pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());这行代码,它将某个Launcher设置默认Launcher,这样以后启动后就优先选择这个。

所以假如我们要在Settings中加一个选项来设置默认Launcher,那就是使用PackageManager中的addPreferredActivity方法即可。

Android中的偏好设置会被保存在用户的目录下,比如:data/system/users/0/package-restrictions.xml

上面贴了很多源码,其实我们在看源码时要站在宏观的角度去看,要看到它的一个大概流程不要去抠每一行代码的意思。只要到真正需要去修改某一个东西时,我们才需要去关注它具体的实现。

喜欢这篇文章,欢迎点赞,分享,关注

更多精彩文章,欢迎关注微信公众号"嵌入式软件开发交流"

标签: #onclick属性值