您的位置:首页 > 运维架构

探究startActivityForResult在singleTop和singleTask启动模式讨论

2016-08-18 11:15 543 查看
最近在研究AMS代码遇到一个问题,在函数startActivityUncheckedLocked中

Slog.d("DDY", "!!!!!!!!!!!!!!!!!!!" );
if (r.packageName != null) {
// If the activity being launched is the same as the one currently
// at the top, then we need to check if it should only be launched
// once.
Slog.d("DDY", "=====11" );
ActivityStack topStack = getFocusedStack();
ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
Slog.d("DDY", "========------ " );
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
top.task);
/// M: AMS log enhancement @{
if(!ActivityManagerService.IS_USER_BUILD)
Slog.d(TAG, "ACT-AM_NEW_INTENT " + top + top.task);
/// @}
// For paranoia, make sure we have correctly
// resumed the top activity.
topStack.mLastPausedActivity = null;
if (doResume) {
resumeTopActivitiesLocked();
}
ActivityOptions.abort(options);
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and
// the client said not to do anything if that
// is the case, so this is it!
if (r.task == null)  Slog.v(TAG,
"startActivityUncheckedLocked: task left null",
new RuntimeException("here").fillInStackTrace());
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(callingUid, r.intent);
if (r.task == null)  Slog.v(TAG,
"startActivityUncheckedLocked: task left null",
new RuntimeException("here").fillInStackTrace());
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
}
}

} else {
Slog.d("DDY", "+++++++++----- " );
if (r.resultTo != null) {
r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
r.requestCode, Activity.RESULT_CANCELED, null);
}
ActivityOptions.abort(options);
if (r.task == null)  Slog.v(TAG,
"startActivityUncheckedLocked: task left null",
new RuntimeException("here").fillInStackTrace());
return ActivityManager.START_CLASS_NOT_FOUND;
}
Slog.d("DDY", "=====22" );


ActivityStack topStack = getFocusedStack();

ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);

这两句的意思是获取当前正在显示的栈顶的Activity,



例如OtherActivity在栈顶。当前获取的就是OtherActivity,也就是显示在当前手机界面的Activity。

if (top != null && r.resultTo == null) {
Slog.d("DDY", "========------ " );
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {


top代表当前正在显示的Activity, r.resultTo :假如A Activity 通过startActivityForResult启动B Activity,那么r.resultTo就是接收返回结果的A Activity。

if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) 意思当前正在显示的Activity正是我们正要启动的Activity,

if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
..........
top.deliverNewIntentLocked(callingUid, r.intent);
这里判断当前的启动模式 为FLAG_ACTIVITY_SINGLE_TOP LAUNCH_SINGLE_TOP 就是 singleTop模式,
LAUNCH_SINGLE_TASK 就是 singleTask 模式。
如果启动模式为上面任何一种的话就会启动deliverNewIntentLocked函数,这个函数主要作用就是调用Activity的onNewIntent函数。就是我们常说的app中的单实例Activity。只存在一个Activity,当我们在启动这个Activity只是重载,并不启动新的


singleTop:

当某Task中有A、B、C、D4个Activity时,如果D想再启动一个D类型的Activity,那么Task将是什么样子呢?在singleTop模式下,Task中仍然是A、B、C、D,只不过D的onNewIntent函数将被调用以处理这个新Intent,而在standard模式下,则Task将变成A、B、C、D、D,最后的D为新创建的D类型Activity对象。在singleTop这种模式下,只有目标Acitivity当前正好在栈顶时才有效,例如只有处于栈顶的D启动时才有用,如果D启动不处于栈顶的A、B、C等,则无效。

上面是有关singleTop简单的介绍,大家有没有想过一个问题。网上有人说startActivityForResult 可以使用singleTop 模式。这句话说得没错。这是有一定条件的那就是A Activity启动 B Activity 可以使用。在B Activity 启动 自己的时候就不可以使用。那B启动自己 会有什么效果呢??

package com.example.systemupdate;

import java.io.File;
import java.io.IOException;
import android.content.Intent;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.RecoverySystem;
import android.os.storage.StorageVolume;
import com.android.internal.os.storage.ExternalStorageFormatter;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {

private TextView txt;
private Button Btn;
private Button Btn2;
private ProgressDialog pd;
private boolean pptv = false;
private Context context;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("我是MainActivity");
Btn = (Button)findViewById(R.id.btn);
Btn2 = (Button)findViewById(R.id.btn2);
Btn2.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

startActivity(new Intent(MainActivity.this,OtherActivity.class));

}
});
}

}


这是主 Activity ,我们在主Activity (MainActivity ) 启动 OtherActivity ,

OtherActivity 模式为singleTop 模式

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.systemupdate"
android:versionCode="11"
android:versionName="1.1" >

<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="pptv" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".OtherActivity"
android:launchMode="singleTop"></activity>
</application>

</manifest>


OtherActivity 代码:

package com.example.systemupdate;

import java.io.File;
import java.io.IOException;
import android.content.Intent;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.RecoverySystem;
import android.os.storage.StorageVolume;
import com.android.internal.os.storage.ExternalStorageFormatter;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;

public class OtherActivity  extends Activity {
public static int n =0;
private TextView txt;
private Button Btn;
private Button Btn2;
private ProgressDialog pd;
private boolean pptv = false;
private Context context;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
n = n+ 1;
setTitle("我是OtherActivity" + n);
Btn = (Button)findViewById(R.id.btn);
Btn2 = (Button)findViewById(R.id.btn2);
Btn2.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

startActivity(new Intent(OtherActivity.this,OtherActivity.class));

}
});
}
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
Log.d("DDY", "OnNewIntent");
}
}


再此情况下点击 MainActivity的Btn2 启动OtherActivity是可以正常启动的此时显示 “我是OtherActivity1”。OtherActivity的Btn2 启动自己也是可以正常启动。此时 会发现 界面仍是OtherActivity1,显示我是“OtherActivity1”你不断点击OtherActivity1的Btn2 。界面仍是显示我是“OtherActivity1”

此时打印日志会显示

Log.d(“DDY”, “OnNewIntent”); 这句话被打印出来了。说明OtherActivity此时的启动并不是新建OtherActivity,只是重载原来的OtherActivity。

D/DDY     ( 5791): !!!!!!!!!!!!!!!!!!!
D/DDY     ( 5791): =====11
D/DDY     ( 5791): ========------
D/DDY     ( 6515): OnNewIntent


说明 函数 top.deliverNewIntentLocked(callingUid, r.intent)被调用了,由于此函数主要作用就是调用 onNewIntent 函数。所以 界面一直重载并不会新建OtherActivity。

现在考虑使用startActivityForResult 情况:

package com.example.systemupdate;

import java.io.File;
import java.io.IOException;
import android.content.Intent;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.RecoverySystem;
import android.os.storage.StorageVolume;
import com.android.internal.os.storage.ExternalStorageFormatter;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {
final public int CODE= 0x717;
public static int n =1;
private TextView txt;
private Button Btn;
private Button Btn2;
private ProgressDialog pd;
private boolean pptv = false;
private Context context;
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode==CODE && resultCode==CODE){
Log.e("DDY", "======================:");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("我是MainActivity");
Btn = (Button)findViewById(R.id.btn);
Btn2 = (Button)findViewById(R.id.btn2);
Btn2.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

startActivityForResult(new Intent(MainActivity.this,OtherActivity.class),CODE);

}
});
}

}


package com.example.systemupdate;

import java.io.File;
import java.io.IOException;
import android.content.Intent;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.RecoverySystem;
import android.os.storage.StorageVolume;
import com.android.internal.os.storage.ExternalStorageFormatter;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;

public class OtherActivity  extends Activity {
final public int CODE= 0x717;
public static int n =1;
private TextView txt;
private Button Btn;
private Button Btn2;
private ProgressDialog pd;
private boolean pptv = false;
private Context context;

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode==CODE && resultCode==CODE){
Log.e("DDY", "======================:");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
n = n+ 1;
setTitle("我是OtherActivity" + n );
Btn = (Button)findViewById(R.id.btn);
Btn2 = (Button)findViewById(R.id.btn2);
Btn2.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

startActivityForResult(new Intent(OtherActivity.this,OtherActivity.class),CODE);

}
});
}
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
Log.d("DDY", "OnNewIntent");
}
}


在这种情况下MainActivity的Btn2 启动OtherActivity是可以正常启动的此时显示 “我是OtherActivity1”。

点击返回键可以正常返回到MainActivity ,可以正常打印出

Log.e("DDY", "======================:");


D/DDY     ( 5791): !!!!!!!!!!!!!!!!!!!
D/DDY     ( 5791): =====11
D/DDY     ( 5791): =====22
E/DDY     ( 7156): ======================:


startActivityForResult 在 MainActivity 启动OtherActivity 是起作用的。

如果OtherActivity 启动自己,不断点击Btn2 ,发现 OtherActivity 不断新建 OtherActivity ,并不会重载此时OtherActivity 显示随着点击 不断变化 “我是OtherActivity1” “我是OtherActivity2” “我是OtherActivity3” …….. 等等。

按返回键也可以 返回OtherActivity3 -> OtherActivity2 -> OtherActivity1 .说明此时的 singleTop 在 startActivityForResult 模式下面并没有起作用。这个不起作用有条件的就是:

此时OtherActivity 正在栈顶且为singleTop 模式。并且用startActivityForResult 启动 自己。此时的singleTop 不起作用。被当做standard 模式。会不断新建OtherActivity 。

之所以在startActivityForResult 启动singleTop 不起作用,在源码中一句话决定了。

ActivityStack topStack = getFocusedStack();
ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
Slog.d("DDY", "========------ " );
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
......
top.deliverNewIntentLocked(callingUid, r.intent);


上面一句话 if (top != null && r.resultTo == null) {

r.resultTo == null 表示启动没有返回结果。当我们的OtherActivity 用startActivity 或者startActivityForResult启动自己,调用到底层时候都会调用到这里。其中startActivity 启动的时候r.resultTo == null 表示不需要返回结果,startActivityForResult启动时候r.resultTo != null 表示要有返回结果。

startActivity 的 r.resultTo == null 成立。所以会继续往下执行。调用

top.deliverNewIntentLocked(callingUid, r.intent)函数 ,进而使用 onNewIntent 重载OtherActivity ,而startActivityForResult 的r.resultTo != null 函数成立,会跳过这段函数。进而当做标准启动模式,不断新建OtherActivity,所以 singleTop 不起作用。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android-源码
相关文章推荐