【发布时间】:2017-02-03 17:20:40
【问题描述】:
我被这个问题困住了。 在我的应用程序中,我使用 SharedPrefManager 作为单例类,以保留一些“会话”信息。在这个简单的示例应用程序中,我只保存用户名和一个布尔值,指示用户是否已登录,如果已登录则跳过登录屏幕,否则显示它。 启动应用程序时,在启动画面之后,LoginActivity 会启动。 此时,如果用户之前已经登录过(以后没有注销,即isuseradded SharedPreferences 变量的布尔值为true),用户将被重定向到MainActivity,否则他会看到登录表单,他可以在其中插入用户名和密码并向远程服务器发出 REST API 请求。 如果登录正确,MainActivity 将启动并简单地显示用户名,取自远程登录 REST API 调用获得的响应 JSON。 现在,这是我的问题:我想实现一个简单的注销功能,该功能删除(或更改,如在此测试应用程序中)由 SharedPreferences 存储的所有信息并将用户带回 LoginActivity。 但是当我单击注销时,SharedPreferences 的值仅在 MainActivity 中更新,而在 LoginActivity 中它们保持与以前相同,因此当用户被重定向到 LoginActivity 时,isusedadded 共享首选项仍然是 true,并且用户被重定向回 MainActivity。 但是 SharedPrefManager 类是单例的,所以它的值在应用程序的每个部分都应该是相同的,因为它只有一个实例存在,那我为什么会有这种行为呢?
您可以通过下载完整的应用代码here 并使用这些凭据登录来测试应用:
用户名: 52346
密码: 32fjueM1(区分大小写)
下面是我的代码。
App.java:
package mypackage.sharedprefapp;
import android.app.Application;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
public class App extends Application
{
private RequestQueue mRequestQueue;
private static App mInstance;
@Override
public void onCreate()
{
super.onCreate();
mInstance = this;
}
public static synchronized App getInstance()
{
return mInstance;
}
// This method returns the queue containing GET/POST requests
public RequestQueue getRequestQueue()
{
if(mRequestQueue == null)
{
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
// This method adds a GET/POST request to the queue of requests
public <T> void addToRequestQueue(Request<T> req)
{
getRequestQueue().add(req);
}
}
SplashActivity.java
package mypackage.sharedprefapp;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class SplashActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
}
}
LoginActivity.java
package mypackage.sharedprefapp;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
public class LoginActivity extends AppCompatActivity
{
private static final String TAG = "LoginActivity";
Button loginButton;
EditText userIDInput, passwordInput;
TextView loginErrorMsg, title;
LinearLayout bgLayout;
SharedPrefManager sharedPrefManager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
sharedPrefManager = SharedPrefManager.getInstance(this);
Log.i("HERE", "LoginActivity onCreate: "+sharedPrefManager.isUserAdded());
}
@Override
protected void onResume()
{
super.onResume();
sharedPrefManager = SharedPrefManager.getInstance(this);
Log.i("HERE", "LoginActivity onResume: "+sharedPrefManager.getUserName());
// if the user is already logged in
if(sharedPrefManager.isUserAdded())
{
Log.i(TAG, "User is logged in");
// if the device is connected to the internet
if(Utils.isDeviceOnline(this))
{
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
// if the device is offline
else
{
Log.i(TAG, "Internet connection is not available");
}
}
// if the user is not logged in
else
{
Log.i(TAG, "User is NOT logged in");
setContentView(R.layout.activity_login);
title = (TextView) findViewById(R.id.title);
loginErrorMsg = (TextView) findViewById(R.id.login_errorText);
userIDInput = (EditText) findViewById(R.id.login_id_paziente);
passwordInput = (EditText) findViewById(R.id.login_password);
loginButton = (Button) findViewById(R.id.login_submitButton);
bgLayout = (LinearLayout) findViewById(R.id.login_parentLayout);
// Bind a custom click listener to the login button
loginButton.setOnClickListener(new LoginButtonClickListener(this));
// Bind a custom click listener to the background layout
// so that the soft keyboard is dismissed when clicking on the background
bgLayout.setOnClickListener(new LoginBackgroundClickListener());
}
}
}
LoginButtonClickListener.java
package mypackage.sharedprefapp;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class LoginButtonClickListener implements View.OnClickListener
{
private Context context;
private SharedPrefManager sharedPrefManager;
public LoginButtonClickListener(Context c)
{
this.context = c;
}
@Override
public void onClick(View v)
{
sharedPrefManager = SharedPrefManager.getInstance(context);
// if internet connection is available
if(Utils.isDeviceOnline(context))
{
// Verify login credentials from DB
doLogin();
}
else {}
}
// check login data with REST API
private void doLogin()
{
final String paziente_id = ((EditText)((Activity)context).findViewById(R.id.login_id_paziente)).getText().toString();
final String paziente_password = ((EditText)((Activity)context).findViewById(R.id.login_password)).getText().toString();
StringRequest stringRequest = new StringRequest
(
Request.Method.POST,
"http://www.stefanopace.net/iCAREServer/api/v1/index.php/login",
new Response.Listener<String>()
{
@Override
public void onResponse(String s)
{
try
{
JSONObject obj = new JSONObject(s);
if(!obj.getBoolean("error"))
{
String name = obj.getString("nome");
sharedPrefManager.addUser(name);
Intent intent = new Intent(context, MainActivity.class);
context.startActivity(intent);
}
else
{
Toast.makeText(context, "Error on JSON response", Toast.LENGTH_LONG).show();
}
}
catch(JSONException e)
{
e.printStackTrace();
}
}
},
new Response.ErrorListener()
{
@Override
public void onErrorResponse(VolleyError volleyError)
{
Toast.makeText(context, volleyError.getMessage(), Toast.LENGTH_LONG).show();
}
})
{
@Override
protected Map<String, String> getParams() throws AuthFailureError
{
Map<String, String> params = new HashMap<>();
params.put("id_paziente", paziente_id);
params.put("password", paziente_password);
return params;
}
};
App.getInstance().addToRequestQueue(stringRequest);
}
}
LoginBackgroundClickListener.java
package mypackage.sharedprefapp;
import android.view.View;
public class LoginBackgroundClickListener implements View.OnClickListener
{
@Override
public void onClick(View v)
{
Utils.hideSoftKeyboard(v);
}
}
MainActivity.java
package mypackage.sharedprefapp;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity
{
private static final String TAG = "MainActivity";
TextView personName;
SharedPrefManager sharedPrefManager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
sharedPrefManager = SharedPrefManager.getInstance(this);
setContentView(R.layout.activity_main);
personName = (TextView) findViewById(R.id.person_name);
Log.i(TAG, "Value: "+sharedPrefManager.isUserAdded());
personName.setText(sharedPrefManager.getUserName());
}
@Override
protected void onResume()
{
super.onResume();
sharedPrefManager = SharedPrefManager.getInstance(this);
Log.w("MainActivity", "onResume");
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
// Logout button has been pressed
case R.id.logout_action:
{
Log.i("Logout pressed", "Logout button has been pressed!");
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setMessage("Are you sure you want to logout?");
alertDialogBuilder.setPositiveButton
(
"Yes",
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface arg0, int arg1)
{
boolean success = sharedPrefManager.removeDataFromSharedPreference();
// if the operation is OK
if(success)
{
// go to the login activity
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
else
{
Toast.makeText(getApplicationContext(), "Error during logout", Toast.LENGTH_LONG).show();
}
}
}
);
alertDialogBuilder.setNegativeButton
(
"No",
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface arg0, int arg1)
{}
}
);
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
return true;
}
}
return super.onOptionsItemSelected(item);
}
}
SharedPrefManager.java
package mypackage.sharedprefapp;
import android.content.Context;
import android.content.SharedPreferences;
public final class SharedPrefManager
{
private static final String TAG = "SharedPrefManager";
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
private static SharedPrefManager mInstance;
private static final String SHARED_PREF = "sharedprefs";
private static final String KEY_IS_USER_ADDED = "isuseradded";
public static final String KEY_USER_NAME = "username";
private SharedPrefManager(Context context)
{
sharedPreferences = context.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
sharedPreferences.registerOnSharedPreferenceChangeListener(new LocalSharedPreferencesChangeListener());
}
public static SharedPrefManager getInstance(Context context)
{
if(mInstance == null)
{
mInstance = new SharedPrefManager(context);
}
return mInstance;
}
// add an user to the shared preferences
public boolean addUser(String name)
{
editor.putString(KEY_USER_NAME, name);
editor.putBoolean(KEY_IS_USER_ADDED, true);
return editor.commit();
}
public boolean removeDataFromSharedPreference()
{
editor.putString(KEY_USER_NAME, "HELLO");
editor.putBoolean(KEY_IS_USER_ADDED, false);
return editor.commit();
}
public String getUserName()
{
return sharedPreferences.getString(KEY_USER_NAME, "");
}
public boolean isUserAdded()
{
return sharedPreferences.getBoolean(KEY_IS_USER_ADDED, false);
}
}
Utils.java
package mypackage.sharedprefapp;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
public class Utils
{
public static void hideSoftKeyboard(View view)
{
InputMethodManager inputMethodManager = (InputMethodManager) view.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
// This method checks if the device is connected to the Internet
public static boolean isDeviceOnline(final Context context)
{
final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivityManager != null)
{
final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
boolean isOnline = (networkInfo != null && networkInfo.isConnectedOrConnecting());
if(!isOnline)
{
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
alertDialog.setCancelable(false);
alertDialog.setTitle("Error");
alertDialog.setMessage("Internet is not available");
alertDialog.setPositiveButton("Try again", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
// Reload the Activity
Intent intent = ((Activity) context).getIntent();
((Activity) context).finish();
context.startActivity(intent);
}
});
alertDialog.show();
}
return isOnline;
}
return false;
}
}
LocalSharedPreferencesChangeListener
package mypackage.sharedprefapp;
import android.content.SharedPreferences;
import android.util.Log;
public class LocalSharedPreferencesChangeListener implements SharedPreferences.OnSharedPreferenceChangeListener
{
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
Log.i("HERE", key);
}
}
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".LoginActivity"
android:padding="0dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/scrollView"
android:layout_gravity="center_horizontal">
<LinearLayout
android:id="@+id/login_parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clickable="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:layout_gravity="center_horizontal"
android:layout_marginTop="50dp"
android:layout_marginBottom="30dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_marginBottom="30dp"
android:singleLine="false"
android:padding="5dp"
android:textSize="30sp" />
<EditText
android:id="@+id/login_id_paziente"
android:layout_width="match_parent"
android:layout_height="40dp"
android:inputType="number"
android:ems="10"
android:hint="Username"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:singleLine="true"
android:minLines="1"
android:gravity="center_horizontal"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:focusableInTouchMode="true"
android:focusable="true" />
<EditText
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="40dp"
android:hint="Password"
android:textColorHint="@android:color/darker_gray"
android:inputType="textPassword"
android:ems="10"
android:textColor="@android:color/black"
android:maxLines="1"
android:singleLine="true"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:focusableInTouchMode="true"
android:focusable="true" />
<TextView
android:id="@+id/login_errorText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login error"
android:textColor="@android:color/holo_red_dark"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:singleLine="false"
android:padding="5dp"
android:textSize="20sp"
android:layout_margin="10dp"
android:visibility="invisible" />
<Button
android:id="@+id/login_submitButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
android:textSize="20sp"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="mypackage.sharedprefapp.MainActivity">
<TextView
android:id="@+id/person_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="25sp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
splashscreen.xml(在可绘制资源文件夹中)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/holo_green_dark"/>
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/ic_launcher"/>
</item>
</layer-list>
mainmenu.xml(在 res/menu 资源文件夹中)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/logout_action"
android:title="Logout"
app:showAsAction="always" />
</menu>
styles.xml
<resources>
...
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/splashscreen</item>
</style>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mypackage.sharedprefapp">
<!-- Needs internet to connect to Google Services -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Permission to check internet connection state -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme" />
<activity
android:name=".LoginActivity"
android:label="@string/app_name"
android:process=":other_process"
android:screenOrientation="portrait"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.LOGIN_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
build.gradle(模块:app)
dependencies {
...
compile 'com.android.volley:volley:1.0.0'
}
谢谢!
【问题讨论】:
-
在 SharedPrefManager.java 中创建一个方法公共类型并添加此代码 SharedPreferences.Editor.clear().commit();并在注销时调用。
-
您可以在注销操作中添加它 editor.putBoolean("KEY_IS_USER_ADDED", false);
-
在我的 SharedPrefManager.java 中的 removeDataFromSharedPreference 方法中,我有这个在注销时调用的代码:
editor.clear(); return editor.commit();其中编辑器定义为 SharedPreferences.Editor .是一样的吗?如果是,它对我不起作用:(或者更好,它起作用,但前提是我重新启动应用程序。 -
@ashish 我也试过了,不适合我:(
-
当您注销时,应用程序现在的行为是什么?
标签: android login sharedpreferences logout