前言:
而今朋友们对“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属性值