在Android M中Google做了很多优化性能、提升用户体验的事情,比如说:App权限、Google Now on Tap、Doze省电系统、AppLinks、DeepLinks等。最近工作接触到了AppLinks、DeepLinks这两个提升用户体验的优化,于是将自己对这两个优化的分析理解记录了下来。
public class SampleChooserTargetService extends ChooserTargetService {
@Override
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
final List<ChooserTarget> targets = new ArrayList<>();
for (int i = 0; i < length; i++) {
// The title of the target
final String title = ...
// The icon to represent the target
final Icon icon = ...
// Ranking score for this target between 0.0f and 1.0f
final float score = ...
// PendingIntent to fill in and send if the user chooses this target
final PendingIntent action = ...
targets.add(new ChooserTarget(title, icon, score, action));
}
return targets;
}
}
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());
...
// 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);
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);
...
}
在20行的位置调用了confiureContentView方法
1234567891011121314151617181920212223
/**
* Returns true if the activity is finishing and creation should halt
*/
boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
// The last argument of createAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
// turn this off when running under voice interaction, since it results in
// a more complicated UI that the current voice interaction flow is not able
// to handle.
mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
mLaunchedFromUid, alwaysUseOption && !isVoiceInteraction());
final int layoutId;
if (mAdapter.hasFilteredItem()) {
layoutId = R.layout.resolver_list_with_default;
alwaysUseOption = false;
} else {
layoutId = getLayoutResource();
}
mAlwaysUseOption = alwaysUseOption;
...
}
void queryTargetServices(ChooserListAdapter adapter) {
final PackageManager pm = getPackageManager();
int targetsToQuery = 0;
for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
final ActivityInfo ai = dri.getResolveInfo().activityInfo;
final Bundle md = ai.metaData;
final String serviceName = md != null ? convertServiceName(ai.packageName,
md.getString(ChooserTargetService.META_DATA_NAME)) : null;
if (serviceName != null) {
final ComponentName serviceComponent = new ComponentName(
ai.packageName, serviceName);
final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
.setComponent(serviceComponent);
if (DEBUG) {
Log.d(TAG, "queryTargets found target with service " + serviceComponent);
}
try {
final String perm = pm.getServiceInfo(serviceComponent, 0).permission;
if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) {
Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require"
+ " permission " + ChooserTargetService.BIND_PERMISSION
+ " - this service will not be queried for ChooserTargets."
+ " add android:permission=\""
+ ChooserTargetService.BIND_PERMISSION + "\""
+ " to the <service> tag for " + serviceComponent
+ " in the manifest.");
continue;
}
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not look up service " + serviceComponent, e);
continue;
}
final ChooserTargetServiceConnection conn =
new ChooserTargetServiceConnection(this, dri);
if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
UserHandle.CURRENT)) {
if (DEBUG) {
Log.d(TAG, "Binding service connection for target " + dri
+ " intent " + serviceIntent);
}
mServiceConnections.add(conn);
targetsToQuery++;
}
}
if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
+ QUERY_TARGET_SERVICE_LIMIT);
break;
}
}
if (!mServiceConnections.isEmpty()) {
if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for "
+ WATCHDOG_TIMEOUT_MILLIS + "ms");
mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
WATCHDOG_TIMEOUT_MILLIS);
} else {
sendVoiceChoicesIfNeeded();
}
}
private final Handler mChooserHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CHOOSER_TARGET_SERVICE_RESULT:
if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
if (isDestroyed()) break;
final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
if (!mServiceConnections.contains(sri.connection)) {
Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
+ " returned after being removed from active connections."
+ " Have you considered returning results faster?");
break;
}
if (sri.resultTargets != null) {
mChooserListAdapter.addServiceResults(sri.originalTarget,
sri.resultTargets);
}
unbindService(sri.connection);
sri.connection.destroy();
mServiceConnections.remove(sri.connection);
if (mServiceConnections.isEmpty()) {
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
sendVoiceChoicesIfNeeded();
}
break;
...
}
}
};