Android 6.0中其他app跳转到messaging后,按back键无法返回原应用,分析了下原因,原来是处在messaging的启动方式上。
在Android 6.0中联系人的详情统一为了QuickContactActivity,乍看里面内容很多、很杂,整理了下,发现主要是onCreate里的这几个自定义的ExpandingEntryCardView。
@Override
protected void onCreate(Bundle savedInstanceState) {
……………………
mContactCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
mNoContactDetailsCard = (ExpandingEntryCardView) findViewById(R.id.no_contact_data_card);
mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
mAboutCard = (ExpandingEntryCardView) findViewById(R.id.about_card);
……………………
}
跳转到短信,必然是有点击事件的,这些内容和上面的代码一样都是在onCreate中:
mNoContactDetailsCard.setOnClickListener(mEntryClickHandler);
mContactCard.setOnClickListener(mEntryClickHandler);
mContactCard.setExpandButtonText(
getResources().getString(R.string.expanding_entry_card_view_see_all));
mContactCard.setOnCreateContextMenuListener(mEntryContextMenuListener);
mRecentCard.setOnClickListener(mEntryClickHandler);
mRecentCard.setTitle(getResources().getString(R.string.recent_card_title));
mAboutCard.setOnClickListener(mEntryClickHandler);
mAboutCard.setOnCreateContextMenuListener(mEntryContextMenuListener);
跳转逻辑就在这个mEntryClickHandler中:
final OnClickListener mEntryClickHandler = new OnClickListener() {
@Override
public void onClick(View v) {
final Object entryTagObject = v.getTag();
if (entryTagObject == null || !(entryTagObject instanceof EntryTag)) {
Log.w(TAG, "EntryTag was not used correctly");
return;
}
final EntryTag entryTag = (EntryTag) entryTagObject;
final Intent intent = entryTag.getIntent();
final int dataId = entryTag.getId();
if (dataId == CARD_ENTRY_ID_EDIT_CONTACT) {
editContact();
return;
}
//?????
//intent.setAction(Intent.ACTION_DIAL);
//-----------------------------------//
// Pass the touch point through the intent for use in the InCallUI
if (Intent.ACTION_CALL.equals(intent.getAction())) {
if (TouchPointManager.getInstance().hasValidPoint()) {
Bundle extras = new Bundle();
extras.putParcelable(TouchPointManager.TOUCH_POINT,
TouchPointManager.getInstance().getPoint());
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mHasIntentLaunched = true;
try {
startActivity(intent);
} catch (SecurityException ex) {
Toast.makeText(QuickContactActivity.this, R.string.missing_app,
Toast.LENGTH_SHORT).show();
Log.e(TAG, "QuickContacts does not have permission to launch "
+ intent);
} catch (ActivityNotFoundException ex) {
Toast.makeText(QuickContactActivity.this, R.string.missing_app,
Toast.LENGTH_SHORT).show();
}
// Default to USAGE_TYPE_CALL. Usage is summed among all types for sorting each data id
// so the exact usage type is not necessary in all cases
String usageType = DataUsageFeedback.USAGE_TYPE_CALL;
final Uri intentUri = intent.getData();
if ((intentUri != null && intentUri.getScheme() != null &&
intentUri.getScheme().equals(ContactsUtils.SCHEME_SMSTO)) ||
(intent.getType() != null && intent.getType().equals(MIMETYPE_SMS))) {
usageType = DataUsageFeedback.USAGE_TYPE_SHORT_TEXT;
}
// Data IDs start at 1 so anything less is invalid
if (dataId > 0) {
final Uri dataUsageUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
.appendPath(String.valueOf(dataId))
.appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType)
.build();
try {
final boolean successful = getContentResolver().update(
dataUsageUri, new ContentValues(), null, null) > 0;
if (!successful) {
Log.w(TAG, "DataUsageFeedback increment failed");
}
} catch (SecurityException ex) {
Log.w(TAG, "DataUsageFeedback increment failed", ex);
}
} else {
Log.w(TAG, "Invalid Data ID");
}
}
};
在onClick中,intent的获取是通过view -> getTag -> getIntent来获取的。发送短信这个button对应view获取到的intent的action自然为ACTION_SENDTO,这个action是会由默认的短信应用来处理的。在Android 6.0中,我们将老旧的Mms替换为了messaging,因此被启动的activity为LaunchConversationActivity,到了这里就开始有趣了。
本来我以为这个activity就是编辑短信并发送的activity,查看源码发现其仅有区区137行代码,而且还在onCreate中把自己finish了!?!
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (UiUtils.redirectToPermissionCheckIfNeeded(this)) {
return;
}
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_SENDTO.equals(action) || Intent.ACTION_VIEW.equals(action)) {
String[] recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
final boolean haveAddress = !TextUtils.isEmpty(intent.getStringExtra(ADDRESS));
final boolean haveEmail = !TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_EMAIL));
if (recipients == null && (haveAddress || haveEmail)) {
if (haveAddress) {
recipients = new String[] { intent.getStringExtra(ADDRESS) };
} else {
recipients = new String[] { intent.getStringExtra(Intent.EXTRA_EMAIL) };
}
}
mSmsBody = intent.getStringExtra(SMS_BODY);
if (TextUtils.isEmpty(mSmsBody)) {
// Used by intents sent from the web YouTube (and perhaps others).
mSmsBody = getBody(intent.getData());
if (TextUtils.isEmpty(mSmsBody)) {
// If that fails, try yet another method apps use to share text
if (ContentType.TEXT_PLAIN.equals(intent.getType())) {
mSmsBody = intent.getStringExtra(Intent.EXTRA_TEXT);
}
}
}
if (recipients != null) {
mBinding.bind(DataModel.get().createLaunchConversationData(this));
mBinding.getData().getOrCreateConversation(mBinding, recipients);
} else {
// No recipients were specified in the intent.
// Start a new conversation with contact picker. The new conversation will be
// primed with the (optional) message in mSmsBody.
onGetOrCreateNewConversation(null);
}
} else {
LogUtil.w(LogUtil.BUGLE_TAG, "Unsupported conversation intent action : " + action);
}
// As of M, activities without a visible window must finish before onResume completes.
finish();
}
原来这是一个没有UI的activity,它的作用就是启动ConversationActivity,启动代码如下:
@Override
public void onGetOrCreateNewConversation(final String conversationId) {
final Context context = Factory.get().getApplicationContext();
UIIntents.get().launchConversationActivityWithParentStack(context, conversationId, mSmsBody);
}
launchConversationActivityWithParentStack就是这个方法导致了back无法退回,其实现在UIIntentsImpl.java中:
@Override
public void launchConversationActivityWithParentStack(final Context context,
final String conversationId, final String smsBody) {
final MessageData messageData = TextUtils.isEmpty(smsBody)
? null
: MessageData.createDraftSmsMessage(conversationId, null, smsBody);
TaskStackBuilder.create(context)
.addNextIntentWithParentStack(
getConversationActivityIntent(context, conversationId, messageData,
false /* withCustomTransition */))
.startActivities();
}
做过通知栏的朋友可能很早就猜到原因了,就是TaskStackBuilder设置了回退栈的内容,虽然这是在Android 4.1就引入了的内容,以前还真没遇到:
<activity
android:name=".ui.conversation.ConversationActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="user"
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/BugleTheme.ConversationActivity"
android:parentActivityName="com.android.messaging.ui.conversationlist.ConversationListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.android.messaging.ui.conversationlist.ConversationListActivity" />
</activity>
parentActivityName设置为了ConversationListActivity. So,只要回退,肯定是回到短信列表页面。