A token is sent to the client when I login, so I'm going to use the retrofit library to take the token and include it in the header of the request and send the data to the server.
When I login, I want to save data through the SharedPreferences library to store tokens delivered to the client locally.
But there is error :
FATAL EXCEPTION: main
Process: com.example.todo_android, PID: 7323
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:217)
at com.example.todo_android.MainActivity.saveData(MainActivity.kt:30)
at com.example.todo_android.Screen.LoginScreenKt$sendLogin$1.onResponse(LoginScreen.kt:64)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1$1.run(DefaultCallAdapterFactory.java:83)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Actually When I login only with body json without Header and SharedPreferences, its working and it gives me token and resultCode.
resultCode : 200
token : e91389ca537f481d1937a43c49da0d5b827e5cfd
There is code:
Login Model
package com.example.todo_android.Data.Profile
data class Login(
val email: String,
val password: String
)
LoginRequest
package com.example.todo_android.Request.ProfileRequest
import com.example.todo_android.Data.Profile.Login
import com.example.todo_android.Response.ProfileResponse.LoginResponse
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface LoginRequest {
@POST("/account/login/")
fun requestLogin(
// @Header("Authorization") token: String,
@Body loginRequest: Login
) : Call<LoginResponse>
}
LoginResponse
package com.example.todo_android.Response.ProfileResponse
import com.google.gson.annotations.SerializedName
data class LoginResponse(
@SerializedName("resultCode")
val resultCode: String,
@SerializedName("token")
val token: String
)
**LoginScreen**
package com.example.todo_android.Screen
import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import com.example.todo_android.Data.Profile.Login
import com.example.todo_android.MainActivity
import com.example.todo_android.Navigation.Action.RouteAction
import com.example.todo_android.Navigation.NAV_ROUTE
import com.example.todo_android.R
import com.example.todo_android.Request.ProfileRequest.LoginRequest
import com.example.todo_android.Response.ProfileResponse.LoginResponse
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
fun goCalendar(route: NAV_ROUTE, routeAction: RouteAction) {
routeAction.navTo(route)
}
@ExperimentalMaterial3Api
fun sendLogin(email: String, password: String, routeAction: RouteAction) {
var loginResponse: LoginResponse? = null
var retrofit = Retrofit.Builder()
.baseUrl("https://plotustodo-ctzhc.run.goorm.io/")
.addConverterFactory(GsonConverterFactory.create())
.build()
var loginRequest: LoginRequest = retrofit.create(LoginRequest::class.java)
loginRequest.requestLogin(Login(email, password)).enqueue(object : Callback<LoginResponse> {
//실패할 경우
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
Log.e("LOGIN", t.message.toString())
}
//성공할 경우
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
loginResponse = response.body()
when (loginResponse?.resultCode) {
"200" -> {
goCalendar(NAV_ROUTE.CALENDAR, routeAction)
Log.d("LOGIN", "resultCode : " loginResponse?.resultCode)
Log.d("LOGIN", "token : " loginResponse?.token)
Log.d("LOGIN", "메인 화면으로 갑니다.")
MainActivity().saveData(loginResponse?.token.toString())
}
"500" -> {
Log.d("LOGIN", "non_field_errors:[Check Your Email or Password]")
}
}
}
})
}
@ExperimentalMaterial3Api
@Composable
fun LoginScreen(routeAction: RouteAction) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
)
{
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
TextField(
modifier = Modifier.width(300.dp),
value = email,
colors = TextFieldDefaults.textFieldColors(
Color(0xff9E9E9E),
disabledLabelColor = Color(0xff9E9E9E),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
shape = RoundedCornerShape(20.dp),
onValueChange = {
email = it
})
Spacer(modifier = Modifier.height(30.dp))
TextField(
modifier = Modifier.width(300.dp),
value = password,
colors = TextFieldDefaults.textFieldColors(
Color(0xff9E9E9E),
disabledLabelColor = Color(0xffE9E9E9),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
singleLine = true,
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
shape = RoundedCornerShape(20.dp),
onValueChange = {
password = it
})
Spacer(modifier = Modifier.height(60.dp))
Button(
modifier = Modifier
.width(300.dp)
.height(50.dp),
colors = ButtonDefaults.buttonColors(Color(0xffFFBE3C7)),
onClick = { sendLogin(email, password, routeAction) }
) {
Text(text = stringResource(id = R.string.login), color = Color.Black)
}
}
}
MainActivity
package com.example.todo_android
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.ExperimentalMaterial3Api
import com.example.todo_android.Navigation.NavigationGraph
import com.example.todo_android.ui.theme.TodoandroidTheme
@ExperimentalMaterial3Api
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TodoandroidTheme {
NavigationGraph()
}
}
loadData()
}
fun loadData() {
val pref = getSharedPreferences("UserTokenKey", Context.MODE_PRIVATE) // UserTokenKey: 파일명
val token = pref.getString("Token", "")
}
fun saveData(token: String) {
val pref = getSharedPreferences("UserTokenKey", Context.MODE_PRIVATE) // UserTokenKey: 파일명
val edit = pref.edit()
edit.putString("Token", token) // "Token"라는 이름을 사용하여 token값을 입력한다.
edit.commit()
}
}
I would appreciate it if you could tell me which part was implemented incorrectly and how to correct it.
CodePudding user response:
As stated in the log, the exact problem is:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:217)
at com.example.todo_android.MainActivity.saveData(MainActivity.kt:30)
at com.example.todo_android.Screen.LoginScreenKt$sendLogin$1.onResponse(LoginScreen.kt:64)
which points to this line
MainActivity().saveData(loginResponse?.token.toString())
You cannot instantiate a new MainActivity
class in order to call the function saveData()
. The Context
used to call getSharedPreferences()
will be null
if your MainActivity
is created like that.
So you can try if the following options address the problem:
- Make your
saveData()
as acallback
function, pass it to the necessary class to call - Pass
Context
to the class you need to callgetSharedPreferences()
, and call it likeContext.getSharedPreferences()