搬运自本人博客:xge技术博客
原文:http://www.xgezhang.com/android_login_save_file.html
在本机或服务器上保存文件是比较简单的一件事,那么在安卓系统下我们该如何存储文件呢?这里我们借用登陆界面常见的“记住登陆用户名密码”的为例,来介绍一下如何把文件保存到手机内存,也综合复习和练习一下之前的内容:
首先我们还是先做界面:
对应的xml布局文件如下,采用的是线性布局加上相对布局来实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | < 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 = "fill_parent" android:orientation = "vertical" tools:context = "com.xgezhang.login.MainActivity" > < TextView android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "@string/please_input_user_name" /> < EditText android:id = "@+id/et_username" android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:singleLine = "true" /> < TextView android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "@string/please_input_password" /> < EditText android:id = "@+id/et_password" android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:singleLine = "true" android:password = "true" /> < RelativeLayout android:layout_width = "fill_parent" android:layout_height = "wrap_content" > < TextView android:id = "@+id/tv_remenber_user_info" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerVertical = "true" android:text = "@string/remenber_user_info" /> < CheckBox android:id = "@+id/cb" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_toRightOf = "@id/tv_remenber_user_info" android:checked = "true" /> < Button android:id = "@+id/bt_login" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_alignParentRight = "true" android:text = "@string/bt_login" /> </ RelativeLayout > </ LinearLayout > |
以上xml很容易理解,只是注意以下几点:
- Edittext要设置为singleLine=true;
- 密码内容要设置password=true,不然会是明文显示;
- Checkbox中可以通过指定checked参数的值来选择默认勾选情况;
接下来我们来看MainActivity代码,稍微理下思路:我们首先需要注册一个登陆按钮的事件,当点击登陆按钮之后,会读取et_username和et_password的值,判断是否为空,如果不为空,则验证是否是正确的用户名和密码。同时,如果我们选择了保存用户登陆信息,则需要把用户名密码通过文本的方式存储到手机中。
当然,每次程序运行的时候,会自动读取这个文本,如果文本中有内容,则自动填写到两个EditText里面,相当于保存内容的回显。
接下来我们来看看代码,因为这里只是模拟登陆,并不是真正的把数据发送到服务器再从database里面select并验证,所以就采用很基本的方式判断了。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package com.xgezhang.login; import java.util.HashMap; import java.util.Map; import com.xgezhang.login.service.loginService; import android.app.Activity; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener { private static final String TAG = "MainActivity" ; private EditText et_username; private EditText et_password; private CheckBox cb; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_username = (EditText) findViewById(R.id.et_username); et_password = (EditText) findViewById(R.id.et_password); cb = (CheckBox) findViewById(R.id.cb); Map <String,String> userInfoMap = loginService.getUserInfoMap( this ); //回显用户已经保存的登陆数据 if (userInfoMap!= null ){ et_username.setText(userInfoMap.get( "username" )); et_password.setText(userInfoMap.get( "password" )); } Button bt_login = (Button) findViewById(R.id.bt_login); bt_login.setOnClickListener( this ); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_login: // 登陆事件 String username = et_username.getText().toString().trim(); String password = et_password.getText().toString().trim(); if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) { Toast.makeText( this , "用户名或密码为空" , Toast.LENGTH_SHORT).show(); } else { if (cb.isChecked()) { boolean result = loginService.saveUserInfo( this , username, password); if (result){ Toast.makeText( this , "登陆信息已保存" , Toast.LENGTH_SHORT).show(); } else { Toast.makeText( this , "登陆信息保存异常" , Toast.LENGTH_SHORT).show(); } } if (username.equals( "zhangsan" ) && password.equals( "123456" )) { Toast.makeText( this , "登陆成功" , Toast.LENGTH_SHORT).show(); } else { Toast.makeText( this , "登陆失败,用户名或密码错误" , Toast.LENGTH_SHORT).show(); } } break ; default : break ; } } } |
其中有两个函数是需要我们进一步实现的,分别是saveUserInfo和getUserInfoMap。出于方便的目的,我们新建一个package命名为com.xgezhang.login.service,并新建一个class名为loginService,来存放登陆会用到的一些函数。
我们先介绍saveUserInfo函数,这里涉及到安卓系统数据保存的问题。我们需要知道的是,在Android系统下有一个/data/data路径,这里存放了所有安装的app的数据,在Eclipse中我们可以通过DDMS来查看:
每个app对应的文件夹里面默认有files和cache,其中cache用于存储缓存文件。
了解了存储的原理,我们就可以开始写代码了:
1 2 3 4 5 6 7 8 9 10 11 12 | static public boolean saveUserInfo(Context context, String username,String password) { try { File file = new File(context.getFilesDir(), "userinfo.txt" ); FileOutputStream fos = new FileOutputStream(file); fos.write((username + "###" + password).getBytes()); fos.close(); return true ; } catch (Exception e) { e.printStackTrace(); return false ; } } |
上述代码比较容易理解,和Java中文件操作没有两样,只是注意几个问题,一个是new File的路径不建议写成固定的路径,而是通过传递一个context,通过context.getFilesDir()函数来直接获取路径;另外,由于有文件的操作,这里需要加上try块来避免异常,并且如果出现异常将其catch掉,并返回false,供调用函数去判断。
这里的用户名密码是通过“###”符号来分割的,肯定是不太合理的,这里不用深究。
接下来是getUserInfoMap函数,和上述代码差不多,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static public Map<String,String> getUserInfoMap(Context context){ try { File file = new File(context.getFilesDir(), "userinfo.txt" ); FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader( new InputStreamReader(fis)); String str = br.readLine(); String[] userinfos = str.split( "###" ); Map<String, String> map = new HashMap<String, String>(); map.put( "username" , userinfos[ 0 ]); map.put( "password" ,userinfos[ 1 ]); return map; } catch (Exception e) { e.printStackTrace(); return null ; } } |
这里的异常是返回null,所以外部只用判断返回的Map是否为null即可。
另外,由于这两个函数显然都不需要将类实体化,所以我们可以直接写成static,在Android项目中推荐大家使用static函数,可以提高程序运行效率。
完整的loginService.java代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package com.xgezhang.login.service; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import android.R.string; import android.content.Context; public class loginService { /** * @param context 上下文 * @param username 用户名 * @param password 密码 * @return boolean true表示保存成功 false表示保存失败 */ static public boolean saveUserInfo(Context context, String username,String password) { try { File file = new File(context.getFilesDir(), "userinfo.txt" ); FileOutputStream fos = new FileOutputStream(file); fos.write((username + "###" + password).getBytes()); fos.close(); return true ; } catch (Exception e) { e.printStackTrace(); return false ; } } /** * 获取用户保存的登陆信息 * @param context * @return Map */ static public Map<String,String> getUserInfoMap(Context context){ try { File file = new File(context.getFilesDir(), "userinfo.txt" ); FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader( new InputStreamReader(fis)); String str = br.readLine(); String[] userinfos = str.split( "###" ); Map<String, String> map = new HashMap<String, String>(); map.put( "username" , userinfos[ 0 ]); map.put( "password" ,userinfos[ 1 ]); return map; } catch (Exception e) { e.printStackTrace(); return null ; } } } |
至此,程序结束,可以用虚拟机运行测试一下。
欢迎转载,请注明出处。