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 acallbackfunction, pass it to the necessary class to call - Pass
Contextto the class you need to callgetSharedPreferences(), and call it likeContext.getSharedPreferences()
