当我使用 Kotlin 在 Android Studio 中更改为另一个活动时如何停止崩溃

Posted

技术标签:

【中文标题】当我使用 Kotlin 在 Android Studio 中更改为另一个活动时如何停止崩溃【英文标题】:How stop crashes when I change to another activity in Android Studio using Kotlin 【发布时间】:2019-04-21 08:21:54 【问题描述】:

每次我尝试从 LoginActivity 切换到 RegisterActivity 时它都会崩溃,我不知道为什么

这是我的 MainActivity.kt

package com.test.kotlinfirstapp

import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity;
import android.widget.Button
import com.test.kotlinfirstapp.R.layout.activity_main
import kotlinx.android.synthetic.main.activity_login.*
import kotlinx.android.synthetic.main.activity_register.*
import kotlinx.android.synthetic.main.content_main.*

class MainActivity : AppCompatActivity() 


    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button2 = findViewById<Button>(R.id.button2) as Button
        button2.setOnClickListener
        val intent = Intent(this, LoginActivity::class.java)
        startActivity(intent)
    


    


这是我的 LoginActivity.kt

package com.test.kotlinfirstapp

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.TargetApi
import android.content.pm.PackageManager
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.app.LoaderManager.LoaderCallbacks
import android.content.CursorLoader
import android.content.Loader
import android.database.Cursor
import android.net.Uri
import android.os.AsyncTask
import android.os.Build
import android.os.Bundle
import android.provider.ContactsContract
import android.text.TextUtils
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import android.widget.TextView

import java.util.ArrayList
import android.Manifest.permission.READ_CONTACTS
import android.content.Intent
import android.widget.Button

import kotlinx.android.synthetic.main.activity_login.*

/**
 * A login screen that offers login via email/password.
 */
class LoginActivity : AppCompatActivity(), LoaderCallbacks<Cursor> 
    /**
     * Keep track of the login task to ensure we can cancel it if requested.
     */
    private var mAuthTask: UserLoginTask? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        // Set up the login form.
        populateAutoComplete()
        password.setOnEditorActionListener(TextView.OnEditorActionListener  _, id, _ ->
            if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) 
                attemptLogin()
                return@OnEditorActionListener true
            
            false


        )
        val button = findViewById<Button>(R.id.button4) as Button
        button4.setOnClickListener
            val intent = Intent(this, RegisterActivity::class.java)
            startActivity(intent)

        button3.setOnClickListener  attemptLogin() 

    


    private fun populateAutoComplete() 
        if (!mayRequestContacts()) 
            return
        

        loaderManager.initLoader(0, null, this)
    

    private fun mayRequestContacts(): Boolean 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) 
            return true
        
        if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) 
            return true
        
        if (shouldShowRequestPermissionRationale(READ_CONTACTS)) 
            Snackbar.make(email, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                .setAction(android.R.string.ok,
                     requestPermissions(arrayOf(READ_CONTACTS), REQUEST_READ_CONTACTS) )
         else 
            requestPermissions(arrayOf(READ_CONTACTS), REQUEST_READ_CONTACTS)
        
        return false
    

    /**
     * Callback received when a permissions request has been completed.
     */
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>,
        grantResults: IntArray
    ) 
        if (requestCode == REQUEST_READ_CONTACTS) 
            if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                populateAutoComplete()
            
        
    


    /**
     * Attempts to sign in or register the account specified by the login form.
     * If there are form errors (invalid email, missing fields, etc.), the
     * errors are presented and no actual login attempt is made.
     */
    private fun attemptLogin() 
        if (mAuthTask != null) 
            return
        

        // Reset errors.
        email.error = null
        password.error = null

        // Store values at the time of the login attempt.
        val emailStr = email.text.toString()
        val passwordStr = password.text.toString()

        var cancel = false
        var focusView: View? = null

        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(passwordStr) && !isPasswordValid(passwordStr)) 
            password.error = getString(R.string.error_invalid_password)
            focusView = password
            cancel = true
        

        // Check for a valid email address.
        if (TextUtils.isEmpty(emailStr)) 
            email.error = getString(R.string.error_field_required)
            focusView = email
            cancel = true
         else if (!isEmailValid(emailStr)) 
            email.error = getString(R.string.error_invalid_email)
            focusView = email
            cancel = true
        

        if (cancel) 
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView?.requestFocus()
         else 
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true)
            mAuthTask = UserLoginTask(emailStr, passwordStr)
            mAuthTask!!.execute(null as Void?)
        
    

    private fun isEmailValid(email: String): Boolean 
        //TODO: Replace this with your own logic
        return email.contains("@")
    

    private fun isPasswordValid(password: String): Boolean 
        //TODO: Replace this with your own logic
        return password.length > 4
    

    /**
     * Shows the progress UI and hides the login form.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private fun showProgress(show: Boolean) 
        // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
        // for very easy animations. If available, use these APIs to fade-in
        // the progress spinner.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) 
            val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()

            login_form.visibility = if (show) View.GONE else View.VISIBLE
            login_form.animate()
                .setDuration(shortAnimTime)
                .alpha((if (show) 0 else 1).toFloat())
                .setListener(object : AnimatorListenerAdapter() 
                    override fun onAnimationEnd(animation: Animator) 
                        login_form.visibility = if (show) View.GONE else View.VISIBLE
                    
                )

            login_progress.visibility = if (show) View.VISIBLE else View.GONE
            login_progress.animate()
                .setDuration(shortAnimTime)
                .alpha((if (show) 1 else 0).toFloat())
                .setListener(object : AnimatorListenerAdapter() 
                    override fun onAnimationEnd(animation: Animator) 
                        login_progress.visibility = if (show) View.VISIBLE else View.GONE
                    
                )
         else 
            // The ViewPropertyAnimator APIs are not available, so simply show
            // and hide the relevant UI components.
            login_progress.visibility = if (show) View.VISIBLE else View.GONE
            login_form.visibility = if (show) View.GONE else View.VISIBLE
        
    

    override fun onCreateLoader(i: Int, bundle: Bundle?): Loader<Cursor> 
        return CursorLoader(
            this,
            // Retrieve data rows for the device user's 'profile' contact.
            Uri.withAppendedPath(
                ContactsContract.Profile.CONTENT_URI,
                ContactsContract.Contacts.Data.CONTENT_DIRECTORY
            ), ProfileQuery.PROJECTION,

            // Select only email addresses.
            ContactsContract.Contacts.Data.MIMETYPE + " = ?", arrayOf(
                ContactsContract.CommonDataKinds.Email
                    .CONTENT_ITEM_TYPE
            ),

            // Show primary email addresses first. Note that there won't be
            // a primary email address if the user hasn't specified one.
            ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"
        )
    

    override fun onLoadFinished(cursorLoader: Loader<Cursor>, cursor: Cursor) 
        val emails = ArrayList<String>()
        cursor.moveToFirst()
        while (!cursor.isAfterLast) 
            emails.add(cursor.getString(ProfileQuery.ADDRESS))
            cursor.moveToNext()
        

        addEmailsToAutoComplete(emails)
    

    override fun onLoaderReset(cursorLoader: Loader<Cursor>) 

    

    private fun addEmailsToAutoComplete(emailAddressCollection: List<String>) 
        //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
        val adapter = ArrayAdapter(
            this@LoginActivity,
            android.R.layout.simple_dropdown_item_1line, emailAddressCollection
        )

        email.setAdapter(adapter)
    

    object ProfileQuery 
        val PROJECTION = arrayOf(
            ContactsContract.CommonDataKinds.Email.ADDRESS,
            ContactsContract.CommonDataKinds.Email.IS_PRIMARY
        )
        val ADDRESS = 0
        val IS_PRIMARY = 1
    

    /**
     * Represents an asynchronous login/registration task used to authenticate
     * the user.
     */
    inner class UserLoginTask internal constructor(private val mEmail: String, private val mPassword: String) :
        AsyncTask<Void, Void, Boolean>() 

        override fun doInBackground(vararg params: Void): Boolean? 
            // TODO: attempt authentication against a network service.

            try 
                // Simulate network access.
                Thread.sleep(2000)
             catch (e: InterruptedException) 
                return false
            

            return DUMMY_CREDENTIALS
                .map  it.split(":") 
                .firstOrNull  it[0] == mEmail 
                ?.let 
                    // Account exists, return true if the password matches.
                    it[1] == mPassword
                
                ?: true
        

        override fun onPostExecute(success: Boolean?) 
            mAuthTask = null
            showProgress(false)

            if (success!!) 
                finish()
             else 
                password.error = getString(R.string.error_incorrect_password)
                password.requestFocus()
            
        

        override fun onCancelled() 
            mAuthTask = null
            showProgress(false)
        
    

    companion object 

        /**
         * Id to identity READ_CONTACTS permission request.
         */
        private val REQUEST_READ_CONTACTS = 0

        /**
         * A dummy authentication store containing known user names and passwords.
         * TODO: remove after connecting to a real authentication system.
         */
        private val DUMMY_CREDENTIALS = arrayOf("foo@example.com:hello", "bar@example.com:world")
    

这是我的 RegisterActivity.kt

    package com.test.kotlinfirstapp

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.TargetApi
import android.content.pm.PackageManager
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.app.LoaderManager.LoaderCallbacks

import android.content.CursorLoader
import android.content.Loader
import android.database.Cursor
import android.net.Uri
import android.os.AsyncTask

import android.os.Build
import android.os.Bundle
import android.provider.ContactsContract
import android.text.TextUtils
import android.view.KeyEvent
import android.view.View
import android.view.View.OnClickListener
import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Button
import android.widget.EditText
import android.widget.TextView

import java.util.ArrayList

import android.Manifest.permission.READ_CONTACTS

/**
 * A login screen that offers login via email/password.
 */
class RegisterActivity : AppCompatActivity(), LoaderCallbacks<Cursor> 
    /**
     * Keep track of the login task to ensure we can cancel it if requested.
     */
    private var mAuthTask: UserLoginTask? = null

    // UI references.
    private var mEmailView: AutoCompleteTextView? = null
    private var mPasswordView: EditText? = null
    private var mProgressView: View? = null
    private var mLoginFormView: View? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)
        // Set up the login form.
        mEmailView = findViewById<View>(R.id.email) as AutoCompleteTextView
        populateAutoComplete()

        mPasswordView = findViewById<View>(R.id.password) as EditText
        mPasswordView!!.setOnEditorActionListener(TextView.OnEditorActionListener  textView, id, keyEvent ->
            if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) 
                attemptLogin()
                return@OnEditorActionListener true
            
            false
        )

        val mEmailSignInButton = findViewById<View>(R.id.button4) as Button
        mEmailSignInButton.setOnClickListener  attemptLogin() 

        mLoginFormView = findViewById(R.id.login_form)
        mProgressView = findViewById(R.id.login_progress)
    

    private fun populateAutoComplete() 
        if (!mayRequestContacts()) 
            return
        

        loaderManager.initLoader(0, null, this)
    

    private fun mayRequestContacts(): Boolean 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) 
            return true
        
        if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) 
            return true
        
        if (shouldShowRequestPermissionRationale(READ_CONTACTS)) 
            Snackbar.make(mEmailView!!, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                .setAction(android.R.string.ok)  requestPermissions(arrayOf(READ_CONTACTS), REQUEST_READ_CONTACTS) 
         else 
            requestPermissions(arrayOf(READ_CONTACTS), REQUEST_READ_CONTACTS)
        
        return false
    

    /**
     * Callback received when a permissions request has been completed.
     */
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>,
        grantResults: IntArray
    ) 
        if (requestCode == REQUEST_READ_CONTACTS) 
            if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                populateAutoComplete()
            
        
    


    /**
     * Attempts to sign in or register the account specified by the login form.
     * If there are form errors (invalid email, missing fields, etc.), the
     * errors are presented and no actual login attempt is made.
     */
    private fun attemptLogin() 
        if (mAuthTask != null) 
            return
        

        // Reset errors.
        mEmailView!!.error = null
        mPasswordView!!.error = null

        // Store values at the time of the login attempt.
        val email = mEmailView!!.text.toString()
        val password = mPasswordView!!.text.toString()

        var cancel = false
        var focusView: View? = null

        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) 
            mPasswordView!!.error = getString(R.string.error_invalid_password)
            focusView = mPasswordView
            cancel = true
        

        // Check for a valid email address.
        if (TextUtils.isEmpty(email)) 
            mEmailView!!.error = getString(R.string.error_field_required)
            focusView = mEmailView
            cancel = true
         else if (!isEmailValid(email)) 
            mEmailView!!.error = getString(R.string.error_invalid_email)
            focusView = mEmailView
            cancel = true
        

        if (cancel) 
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView!!.requestFocus()
         else 
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true)
            mAuthTask = UserLoginTask(email, password)
            mAuthTask!!.execute(null as Void?)
        
    

    private fun isEmailValid(email: String): Boolean 
        //TODO: Replace this with your own logic
        return email.contains("@")
    

    private fun isPasswordValid(password: String): Boolean 
        //TODO: Replace this with your own logic
        return password.length > 4
    

    /**
     * Shows the progress UI and hides the login form.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private fun showProgress(show: Boolean) 
        // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
        // for very easy animations. If available, use these APIs to fade-in
        // the progress spinner.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) 
            val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime)

            mLoginFormView!!.visibility = if (show) View.GONE else View.VISIBLE
            mLoginFormView!!.animate().setDuration(shortAnimTime.toLong()).alpha(
                (if (show) 0 else 1).toFloat()
            ).setListener(object : AnimatorListenerAdapter() 
                override fun onAnimationEnd(animation: Animator) 
                    mLoginFormView!!.visibility = if (show) View.GONE else View.VISIBLE
                
            )

            mProgressView!!.visibility = if (show) View.VISIBLE else View.GONE
            mProgressView!!.animate().setDuration(shortAnimTime.toLong()).alpha(
                (if (show) 1 else 0).toFloat()
            ).setListener(object : AnimatorListenerAdapter() 
                override fun onAnimationEnd(animation: Animator) 
                    mProgressView!!.visibility = if (show) View.VISIBLE else View.GONE
                
            )
         else 
            // The ViewPropertyAnimator APIs are not available, so simply show
            // and hide the relevant UI components.
            mProgressView!!.visibility = if (show) View.VISIBLE else View.GONE
            mLoginFormView!!.visibility = if (show) View.GONE else View.VISIBLE
        
    

    override fun onCreateLoader(i: Int, bundle: Bundle): Loader<Cursor> 
        return CursorLoader(
            this,
            // Retrieve data rows for the device user's 'profile' contact.
            Uri.withAppendedPath(
                ContactsContract.Profile.CONTENT_URI,
                ContactsContract.Contacts.Data.CONTENT_DIRECTORY
            ), ProfileQuery.PROJECTION,

            // Select only email addresses.
            ContactsContract.Contacts.Data.MIMETYPE + " = ?", arrayOf(
                ContactsContract.CommonDataKinds.Email
                    .CONTENT_ITEM_TYPE
            ),

            // Show primary email addresses first. Note that there won't be
            // a primary email address if the user hasn't specified one.
            ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"
        )
    

    override fun onLoadFinished(cursorLoader: Loader<Cursor>, cursor: Cursor) 
        val emails = ArrayList<String>()
        cursor.moveToFirst()
        while (!cursor.isAfterLast) 
            emails.add(cursor.getString(ProfileQuery.ADDRESS))
            cursor.moveToNext()
        

        addEmailsToAutoComplete(emails)
    

    override fun onLoaderReset(cursorLoader: Loader<Cursor>) 

    

    private fun addEmailsToAutoComplete(emailAddressCollection: List<String>) 
        //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
        val adapter = ArrayAdapter(
            this@RegisterActivity,
            android.R.layout.simple_dropdown_item_1line, emailAddressCollection
        )

        mEmailView!!.setAdapter(adapter)
    


    private interface ProfileQuery 
        companion object 
            val PROJECTION = arrayOf(
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.IS_PRIMARY
            )

            val ADDRESS = 0
            val IS_PRIMARY = 1
        
    

    /**
     * Represents an asynchronous login/registration task used to authenticate
     * the user.
     */
    inner class UserLoginTask internal constructor(private val mEmail: String, private val mPassword: String) :
        AsyncTask<Void, Void, Boolean>() 

        override fun doInBackground(vararg params: Void): Boolean? 
            // TODO: attempt authentication against a network service.

            try 
                // Simulate network access.
                Thread.sleep(2000)
             catch (e: InterruptedException) 
                return false
            

            for (credential in DUMMY_CREDENTIALS) 
                val pieces = credential.split(":".toRegex()).dropLastWhile  it.isEmpty() .toTypedArray()
                if (pieces[0] == mEmail) 
                    // Account exists, return true if the password matches.
                    return pieces[1] == mPassword
                
            

            // TODO: register the new account here.
            return true
        

        override fun onPostExecute(success: Boolean?) 
            mAuthTask = null
            showProgress(false)

            if (success!!) 
                finish()
             else 
                mPasswordView!!.error = getString(R.string.error_incorrect_password)
                mPasswordView!!.requestFocus()
            
        

        override fun onCancelled() 
            mAuthTask = null
            showProgress(false)
        
    

    companion object 

        /**
         * Id to identity READ_CONTACTS permission request.
         */
        private val REQUEST_READ_CONTACTS = 0

        /**
         * A dummy authentication store containing known user names and passwords.
         * TODO: remove after connecting to a real authentication system.
         */
        private val DUMMY_CREDENTIALS = arrayOf("foo@example.com:hello", "bar@example.com:world")
    

如果需要,这是我的 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.test.kotlinfirstapp">

    <!-- To auto-complete the email text field in the login form with the user's emails -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.READ_PROFILE"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
                android:name=".LoginActivity"
                android:label="@string/title_activity_login">
        </activity>
        <activity
                android:name=".RegisterActivity"
                android:label="@string/title_activity_register">
        </activity>
    </application>

</manifest>

我要做的就是让我在 LoginActivity 上的一个按钮转到 RegisterActivity,然后从那里返回到 LoginActivity。

【问题讨论】:

请将 logcat 中的堆栈跟踪和异常添加到您的帖子中 从详细或错误日志添加工作室中显示的错误是什么 【参考方案1】:

我在您的 LoginActivity 中没有看到任何启动 RegisterActivity 的实现,因此您将无法到达那里。除此之外,如果您不想在登录后返回 MainActivity,则必须将 UserLoginTask:onPostExecute() 中的 success 逻辑从 finish() 替换为您想要执行的操作。

【讨论】:

每次我尝试实现一些东西来启动它时,它会在单击按钮以将我带到应用程序中的 registerActivity 屏幕时崩溃我的程序 在更新 SDK 之前我没有遇到这个问题。但是现在每次我使用意图时它都会崩溃。你是怎么解决你的问题的,我完全被困住了。

以上是关于当我使用 Kotlin 在 Android Studio 中更改为另一个活动时如何停止崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Firebase Android -- 在 Kotlin 中使用电子邮件和密码创建用户

使用 Kotlin Android Studio 拨打电话

如何使用 kotlin 在 android studio 中引用 timerPicker

用 kotlin 学习 Android MVVM 架构组件

Android Studio 3.0 Kotlin 更改未反映在构建中

RecyclerView 动画问题 Android Kotlin