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

最近在研究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 不起作用,在源码中一句话决定了。

扫描二维码关注公众号,回复: 1545613 查看本文章
  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 不起作用。。。。

猜你喜欢

转载自blog.csdn.net/lb5761311/article/details/52237808
今日推荐