Android Activity 的启动流程

Activity 的启动流程

这里我们以API 28 版本为例。源码太长,文中的代码片段都为截取重要部分。

首先我们从Activity 的startActivity()开始,

//Activity.java

public void startActivity(Intent intent) {

    this.startActivity(intent, null);

}

这里调用了一个重载方法,statActivity(Intent intent,Bundle options)

@Override

public void startActivity(Intent intent, @Nullable Bundle options) {

        ......

        startActivityForResult(intent, -1);

       ......

}

接着我们来到了startActivityForResult() 方法

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,

        @Nullable Bundle options) {

      ......

        Instrumentation.ActivityResult ar =

            mInstrumentation.execStartActivity(

                this, mMainThread.getApplicationThread(), mToken, this,

                intent, requestCode, options);

        ......

 }

可以看到最终走到了Instrumentation 的execStartActivity() 方法里。

//Instrumentation.java

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
   
       ......
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
       ......
}

这里有个参数我们需要注意一下,IBinder contexThread, 可以看到这里是个IBinder对象是用来进程间通信的,可以看到传入的参数是mMainThread.getApplicationThread(),

mMainThread 是AcitivityThread的实例,AcitivityThread维护着main()方法,是应用程序的入口,它管理着应用程序的主线程,内部封装着Acitivity、BroadcastRecevier、Service的启动、切换等操作,但是它不是独立完成这份工作的,以上行为的调度都需要通过系统服务ActivityManagerService来进行。

MainThread的内部类ApplicationThread继承了IApplicationThread.Stub,可以用来进程间通信,实际上应用程序同系统服务ActivityManagerService 的交互正是通过ApplicationThread来进行的,我们应用页面的启动、广播的发送、服务的启动都要通过ApplicationThread 和ActivityMamangerService的通信来进行。

介绍下Instrumentation, Instrumentation 译名为仪表盘,它早于应用Application 初始化,所以可以用来监控系统同应用的交互。我们来看它的类结构

发现它的大部分方法都和应用Application 的创建和生命周期回调、Activity 创建以及生命周期回调有关,后面我们也会看到它在Activity 启动流程中的作用。

我们继续往下走,这里代码来到了ActivityManager.getService().startActivity(),

//ActivityManager.java

public static IActivityManager getService() {

    return IActivityManagerSingleton.get();

}


private static final Singleton<IActivityManager> IActivityManagerSingleton =

        new Singleton<IActivityManager>() {

            @Override

            protected IActivityManager create() {

                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

                final IActivityManager am = IActivityManager.Stub.asInterface(b);

                return am;

            }

        };

 ActivityManager.getService() 这里获取的是一个IActivityManager的单例对象,从单例对象创建的方法中我们可以看到获取了ActivityService系统服务IBinder对象,然后将其转为了IActivityManager, 那么获取的这个系统服务对象到底是谁呢?

其实这个系统服务对象就是我们上文提到的ActivityManagerService(AMS)

public class ActivityManagerService extends IActivityManager.Stub

        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {}

ActivityManagerService 系统服务是android 系统最重要的服务之一,负责着四大组件的启动、切换、调度以及应用程序的管理和调度,可以说没有它,我们应用的正常运行就无从谈起,回归正题,代码调用来到了ActivityManagerService 的startActivity() 方法,

从这里我们就从我们的应用程序进程通过IPC 通信来到了ActivityManagerSerice所在的进程

//ActivityManagerService.java

public final int startActivity(IApplicationThread caller, String callingPackage,

            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,

                resultWho, requestCode, startFlags, profilerInfo, bOptions,

                UserHandle.getCallingUserId());

}

这里调用了startActivityAsUser()方法,然后又调用了一个重载方法来到了

//ActivityManagerService.java

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,

            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,

            boolean validateIncomingUser) {

    ......

        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")

                .setCaller(caller)

                .setCallingPackage(callingPackage)

                .setResolvedType(resolvedType)

                .setResultTo(resultTo)

                .setResultWho(resultWho)

                .setRequestCode(requestCode)

                .setStartFlags(startFlags)

                .setProfilerInfo(profilerInfo)

                .setActivityOptions(bOptions)

                .setMayWait(userId)

                .execute();



    }

可以看到通过mActivityStartController 对象获取了一个ActivityStarter 对象,ActivityStartController 是个用来代理Activity 启动的控制器,ActivtyStarter 也是一个控制器用来解释一个Activity 要如何被启动, 我们看下execute()方法

//ActivityStarter.java

int execute() {

        try {

            // TODO(b/64750076): Look into passing request directly to these methods to allow

            // for transactional diffs and preprocessing.

            if (mRequest.mayWait) {

                return startActivityMayWait(mRequest.caller, mRequest.callingUid,

                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,

                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,

                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,

                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,

                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,

                        mRequest.inTask, mRequest.reason,

                        mRequest.allowPendingRemoteAnimationRegistryLookup);

            } else {

                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,

                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,

                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,

                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,

                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,

                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,

                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,

                        mRequest.outActivity, mRequest.inTask, mRequest.reason,

                        mRequest.allowPendingRemoteAnimationRegistryLookup);

            }

        } finally {

            onExecutionComplete();

        }

    }

可以看到有个判断,如果mRequest.mayWait为true, 则执行startActivityMayWait(),否则执行startActivity(),  这里我们只需要关注startActivityMayWait(), 因为在我们获取ActivityStarter 对象时调用了setMayWait()方法,mayWait 字段则被设为了true.

//ActivityStarter.java

private int startActivityMayWait(IApplicationThread caller, int callingUid,

        String callingPackage, Intent intent, String resolvedType,

        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

        IBinder resultTo, String resultWho, int requestCode, int startFlags,

        ProfilerInfo profilerInfo, WaitResult outResult,

        Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,

        int userId, TaskRecord inTask, String reason,

        boolean allowPendingRemoteAnimationRegistryLookup) {

    // Refuse possible leaked file descriptors

   ......

        final ActivityRecord[] outRecord = new ActivityRecord[1];

        int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,

                voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,

                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,

                ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,

                allowPendingRemoteAnimationRegistryLookup);

        ......

        return res;

    }

}

接着调用了startActivity()重载方法,创建了目标活动Activity 的ActivitRcord对象,

//ActivityStarter.java

  private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,

            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,

            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,

            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,

            SafeActivityOptions options,

            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,

            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {

      ......

        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,

                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),

                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,

                mSupervisor, checkedOptions, sourceRecord);

       

        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,

                true /* doResume */, checkedOptions, inTask, outActivity);

    }

最终来到了startActivityUnChekced()方法,

//AcitivityStarter.java

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,

            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,

            ActivityRecord[] outActivity) {

       ......

        computeLaunchingTaskFlags();

        computeSourceStack();

        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask

                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {

            newTask = true;

            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);

        } else if (mSourceRecord != null) {

            result = setTaskFromSourceRecord();

        } else if (mInTask != null) {

            result = setTaskFromInTask();

        } else {

            setTaskToCurrentTopOrCreateNewTask();

        }

        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,

                mOptions);

           ......

        mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,

                        mOptions);

        return START_SUCCESS;

    }

这里我们要理解几个相关类,ActivityStackSupervisor,ActivityStack,TaskRecord,ActivityRecord。

我们从小往大的开始说,ActivityRecord : 当我们启动Actitivity 或者关闭Activity 时,我们经常说这个活动入栈或者出栈了,那到底是什么入栈出栈了呢?就是ActivityRecord对象,Android 系统中用ActivityReocrd来表示Activity, 每一个正在运行的Activity 都对应着一个ActivityReocrd 对象,它存储着Activity 的状态信息包括标识符、所属任务栈、进程信息、启动模式、可见性等。

TaskRecord 任务栈,他是用来表示任务的类,主要用于存储和管理任务的状态信息,帮助系统进行任务的管理和调度,他有几个属性我们可以关注下,affinity  就是我们在清单文件里指定的taskAffinity, ArrayList<ActitivityRecord> mActivities 就是运行在当前任务的所有活动集合,mStack 指的是当前任务所在的活动堆栈。在任务中的Activity是按照入栈出栈的方式管理的。

ActivityStack 活动堆栈, 顾名思义,他是真正用来管理活动入栈出栈的类,他是TaskRecord 和ActivityRecord 的统一上司,管理着一个或多个任务栈,也负责着活动的创建、启动、移除、切换的等调度,ActivityStack 维护着TaskRecord列表同时也记录着当前同用户交互的活动和正在暂停的活动ActivityRecord。

ActivityStackSupervisor, 活动堆栈的管理者,他维护着设备中存在的活动堆栈的信息,譬如 mHomeStack 桌面应用的活动堆栈、mFocusedStack 当前同用户交互的活动堆栈等。

在没有开启多任务的应用程序中,他们的对应关系大致如下:

接着说回startActivityUnChekced() 方法,

这个方法主要做了几件事:

1. computeLaunchingTaskFlag(), 主要是为了计算启动活动的启动标记位launchFlags,不同的标记位决定了目标ActivitRecord最终会放在哪一个TaskRecord中,这里分几种步骤:

  •       如果来源ActivityReocrd 为null,即并不是从Activity 启动的,但是指定了目标ActivityRecord 的入栈mInTask,这时如果目标ActivityRecord 的意图指定了FLAG_ACTIVITY_NEW_TASK,说明目标活动要在一个新的任务栈中,那么确定任务栈标记mAddingToTask 为false 否则为true, 同时将指定入栈mInTask 赋值给mReuseTask
  •     如果来源ActivityReocrd不为null, 将指定入栈mInTask置为null并且将是否加入栈标记mAddingToTask 设置为true ;
  •       如果目标ActivityRecord 的指定入栈不存在并且来源ActivityRecord 为null, 说明目标Activity 的启动并不是由另外一个Activity 启动的,所以标记位需要设置为FLAG_ACTIVITY_NEW_TASK,也就是新创建一个Task, 
  •       如果启动目标Activity的Activity 存在并且其启动模式是SingleInstance,则目标Actiivty的启动标记位同样要设置为FLAG_ACTIVITY_NEW_TASK;
  •      如果目标Activity 的入栈不存在并且目标Activity 的启动模式是SingleInstance 或者SingleTask, 则目标Actiivty的启动标记位同样要设置为FLAG_ACTIVITY_NEW_TASK;

2.创建新的TaskRecord 或者复用来源Activity 指定的TaskRecord:

  •       如果来源Activity 为空、指定的入栈为空、确定任务栈标记mAddingToTask为false并且目标Activity的启动标记位是FLAG_ACTIVITY_NEW_TASK,调用setTaskFromReuseOrCreateNewTask(), 这里需要看computeLaunchingTaskFlag() 中是否指定了mReuseTask,如果未指定,则新创建TaskRecord, 然后将TaskRecord加入到栈中,最后将目标ActivityRecord 加入到TaskRecord顶部;
  •      如果来源ActivityReocrd 不为空,则将目标ActivityRecord 加入来源ActivityRecord 的TaskReocrd的顶部;
  •        指定入栈不为null , 将目标ActivityRecord 加入指定的TaskReocrd的顶部;

3. 调用目标ActivityReocrd 所在的ActivityStack 中方法 startActivityLocked(),这里主要是通过调用insertTaskAtTop()方法将目标ActivityReocrd 所在TaskRecord 移动到栈顶。

   //AcitivityStack.java

  void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,

            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
             ......

            insertTaskAtTop(rTask, r);

              ......

        }

    }

4. 调用ActivityStackSupervisor 的 resumeFocusedStackTopActivityLocked() 方法

  //AcitivityStackSupervisor.java

    boolean resumeFocusedStackTopActivityLocked(

            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
             ......
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);

             ......

        return false;

}

上面我们来到了ActivityStack 的 resumeTopActivityUncheckedLocked() 方法,里面调用了resumeTopActivityInnerLocked()方法,

  //AcitivityStack.java

  private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {

         ......

if (mResumedActivity != null) {

            if (DEBUG_STATES) Slog.d(TAG_STATES,

                    "resumeTopActivityLocked: Pausing " + mResumedActivity);

            pausing |= startPausingLocked(userLeaving, false, next, false);

        }

         mStackSupervisor.startSpecificActivityLocked(next, true, true);

        return true;

    }

这里有一点要注意的是这里我们调用了一个startPausingLocked ()方法, 如果当前获取焦点的Activity不为null, 我们还要暂停它,最终调用了当前活动的onPause()  方法,也就是说在开启新的Activity之前要先暂停当前Activity,所以为了使新启动的Activity 更快的启动,我们不要在onPause()  方法中做过多的操作。

最终我们又回到了ActivityStackSupervisor中,我们看下startSpecificActivityLocked( )方法

  //AcitivityStackSupervisor.java

  void startSpecificActivityLocked(ActivityRecord r,

            boolean andResume, boolean checkConfig) {

        // Is this activity's application already running?

        ProcessRecord app = mService.getProcessRecordLocked(r.processName,

                r.info.applicationInfo.uid, true);

        if (app != null && app.thread != null) {

            try {

                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0

                        || !"android".equals(r.info.packageName)) {

                ......
                realStartActivityLocked(r, app, andResume, checkConfig);

                return;

            } catch (RemoteException e) {

                Slog.w(TAG, "Exception when starting activity "

                        + r.intent.getComponent().flattenToShortString(), e);

            }

        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,

                "activity", r.intent.getComponent(), false, false, true);

    }

这个 方法中判断了我们目标ActivityReocrd 所在进程是否存在,如果不存在,则需要ActivityManagerService创建进程,如果存在则调用 realStartActivityLocked(), 这里我们先假设应用进程已经存在,然后我们看下realStartActivityLocked() 方法。

  //AcitivityStackSupervisor.java

  final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,

            boolean andResume, boolean checkConfig) throws RemoteException {

                ......

                // Create activity launch transaction.

                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken);

                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),...));


               mService.getLifecycleManager().scheduleTransaction(clientTransaction);



               ......



        return true;

    }

这里我们创建了一个事务,可以看到创建事务的时候需要将目标应用的ApplicationThread 作为参数

//ClientTransaction.java
public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {

        ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);

        if (instance == null) {

            instance = new ClientTransaction();

        }

        instance.mClient = client;

        instance.mActivityToken = activityToken;



        return instance;

    }

并且给了事务一个LaunchActivtyItem 的回调callBack,然后调用AMS 中的ClientLifecycleManager 执行了该事务。

//ClientLifecyleManager.java
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {

        final IApplicationThread client = transaction.getClient();

        transaction.schedule();

        ……

    }

这里调用了事务的schedule()方法,

//ClientTransaction.java
public void schedule() throws RemoteException {

        mClient.scheduleTransaction(this);

    }

这里的mClient 是我们创建事务的时候传入的app.thread,它是目标进程的ApplicationThread对象, 这时候我们终于从ActivityManagerService 来到了目标进程的ApplicationThread。

//ApplicationThread.java

public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {

            ActivityThread.this.scheduleTransaction(transaction);
 }

ApplicationThread 是ActivityThread 的内部类,它直接调用了ActivityThread 的scheduleTransaction() 方法, 有没有在ActivityThread 找这个方法没有找到的童鞋呢,其实这个方法是定义在抽象类ClientTransactionHandler 中的,ActivityThread 继承了这个类,我们看下这个方法

//ActivityThread.java

void scheduleTransaction(ClientTransaction transaction) {

        transaction.preExecute(this);

        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

来到了ActivityThread 的sendMessage() 方法,通过ActivityThread 维护的Handler mH 发送了 标记 为ActivityThread.H.EXECUTE_TRANSACTION 的消息,接着我们来到Handler 的handleMessage方法中,


void handleMessage(Message msg){
  
case EXECUTE_TRANSACTION:

                    final ClientTransaction transaction = (ClientTransaction) msg.obj;

                    mTransactionExecutor.execute(transaction)

                    break;


}

可以看到这里用TransactionExecutor 对象调用了execute 执行了启动Activity的事务,TransactionExecutor 是ActivityThread 的一个属性,TransactionExecutor mTransactionExecutor = new TransactionExecutor(this), 可以看到它的构造函数中需要ClientTransactionHandler 作为参数,而ActivityThread 正好继承了ClientTransactionHandler. 接着往下走,

// TransactionExecutor.java

public void execute(ClientTransaction transaction) {
  ……

 executeCallbacks(transaction);

}

public void executeCallbacks(ClientTransaction transaction) {

        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();

        if (callbacks == null) {

            // No callbacks to execute, return early.

            return;

        }

        final int size = callbacks.size();

        for (int i = 0; i < size; ++i) {

            final ClientTransactionItem item = callbacks.get(i);

            ......

            item.execute(mTransactionHandler, token, mPendingActions);

            item.postExecute(mTransactionHandler, token, mPendingActions);

            ......

        }

    }

还记得我们创建事务的时候传入的callBack LaunchActivityItem 吗? 这里饶了一大圈就是调用了它的execute() 方法

//LaunchActivityItem.java

   public void execute(ClientTransactionHandler client, IBinder token,

            PendingTransactionActions pendingActions) {

        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,

                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,

                mPendingResults, mPendingNewIntents, mIsForward,

                mProfilerInfo, client);

        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
}

可以看到这里调用了ClientTransactionHandler 的handleLaunchActivity() 方法,我们上文提到过ActivityThead 继承了ClicentTransactionHandler,所以这里来到了ActivityThread 的handleLaunchActivity() 方法

//ActivityThread.java

public Activity handleLaunchActivity(ActivityClientRecord r,

            PendingTransactionActions pendingActions, Intent customIntent) {
        ......

        final Activity a = performLaunchActivity(r, customIntent);

        ......

        return a;
}

这里来到了performLauncherActivity()方法

//ActivityThread.java
   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //创建上下文

        ContextImpl appContext = createBaseContextForActivity(r);

        Activity activity = null;

        java.lang.ClassLoader cl = appContext.getClassLoader();

        //创建Activity
            activity = mInstrumentation.newActivity(

                    cl, component.getClassName(), r.intent);


        try {

            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

           //绑定上下文,创建PhoneWindow

                activity.attach(appContext, this, getInstrumentation(), r.token,

                        r.ident, app, r.intent, r.activityInfo, title, r.parent,

                        r.embeddedID, r.lastNonConfigurationInstances, config,

                        r.referrer, r.voiceInteractor, window, r.configCallback);
           //调用Activity onCreate()

                    mInstrumentation.callActivityOnCreate(activity, r.state);

                r.activity = activity;

            }

          ......

        return activity;

    }

这个方法中给目标Activity创建上下文Context, 通过Instrumentation.newActivity()创建目标Activity实例, 通过目标Activity attach() 方法和上下文绑定并且创建PhoneWindow,通过Instumentation.callActivityOnCreate() 调用目标Activity 的生命周期onCreate()方法,至此 一个Activity 的算是被启动了。

整理的启动流程图如下,以作参考 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.a2, PID: 18827 android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.a2/com.example.a2.jisuanqi}; have you declared this activity in your AndroidManifest.xml? at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2049) at android.app.Instrumentation.execStartActivity(Instrumentation.java:1709) at android.app.Activity.startActivityForResult(Activity.java:5192) at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:597) at android.app.Activity.startActivityForResult(Activity.java:5150) at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:583) at android.app.Activity.startActivity(Activity.java:5521) at android.app.Activity.startActivity(Activity.java:5489) at com.example.a2.MainActivity$1.onClick(MainActivity.java:37) at android.view.View.performClick(View.java:7125) at android.view.View.performClickInternal(View.java:7102) at android.view.View.access$3500(View.java:801) at android.view.View$PerformClick.run(View.java:27336) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
06-07

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值