Android(java)学习笔记155:中文乱码的问题处理(qq登录案例)
1. 我们在之前的笔记中LoginServlet.java中,我们Tomcat服务器回复给客户端的数据是英文的"Login Success","Login Failed".
现在我们改成如下的汉字:
1 package com.himi.web; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.annotation.WebServlet; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 /** 11 * Servlet implementation class LoginServlet 12 */ 13 @WebServlet("/LoginServlet") 14 public class LoginServlet extends HttpServlet { 15 private static final long serialVersionUID = 1L; 16 17 /** 18 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 19 */ 20 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 21 String qq = request.getParameter("qq"); 22 String password = request.getParameter("password"); 23 System.out.println("qq:"+qq); 24 System.out.println("password:"+password); 25 26 //模拟服务器操作,查询数据库,看qq和密码是否正确. response.getOutputStream()获得一个输出流,向浏览器写入数据(提示数据) 27 if("10086".equals(qq) && "123456".equals(password)) { 28 response.getOutputStream().write("登录成功".getBytes()); 29 }else { 30 response.getOutputStream().write("登录失败".getBytes()); 31 } 32 33 } 34 35 }
这时候,我们布署之前的Android手机端http程序到模拟器上,运行产生如下效果:
2.常见的乱码处理
(1)菱形里面有问号 : gbk的中文数据用utf-8表示的
Tomcat默认的编码是iso-8859-1的码表,没有中文,如果遇到了不认识的字符串就使用本地默认编码gbk;
当我们把这些gbk编码数据通过http协议传递给手机Android客户端的时候,Android客户端的默认编码是utf-8.所以会出现这样的问题。
这个乱码解决问题的方式:在客户端或者服务器端任意一端(不能同时修改两端).比如服务器端把回复数据编码改成utf-8,要么就是客户端把接收到数据指定以gb2312。
(2)中文用户名输入出现乱码
• 手机客户端
还是之前的qq登录的案例,这里输入中文:
点击登录之后,服务器端接收到数据,显示乱码,效果如下:出现?乱码
• PC端浏览器
这里也是输入中文用户名,如下:
结果出现乱码如下:出现的?乱码
(3)PC端到服务器端的乱码问题解决:
PC端到服务器端的乱码是由于之前客户端是以utf-8流的方式把中文用户名,传递给服务器端,服务端默认编码是ios-8859-1,它是不能识别中文的。
所以服务器端以ios-8859-1的方式显示数据,就会上面的问号乱码。
解决办法:修改服务器端代码,如下:
qq.getBytes("iso-8859-1"),这里必须指定iso-8859-1的方式,不然会默认是系统本地编码gb2312;
此时再次在浏览器提交数据,服务器端就不会出现乱码,如下:
(4)GET客户端到服务器端的乱码问题解决:
上面只是解决PC端到服务器端的乱码出现的问题,但是客户端到服务器端的乱码还是没有解决,如下:
这里我们知道get方式是组拼url路径的,提交的数据包含在这个路径下,提交的数据要是包含中文或者其他不合法字符就会转化,就如下:
解决方法:拼接url的路径时候,对中文进行转化:
(5)POST客户端到服务器端的乱码问题解决:
之前POST方式编写的QQ登录案例,直接布署程序到模拟器上会出现如下错误:
解决办法:
1 package com.himi.post; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.net.HttpURLConnection; 10 import java.net.MalformedURLException; 11 import java.net.URL; 12 13 import android.app.Activity; 14 import android.os.Bundle; 15 import android.text.TextUtils; 16 import android.util.Log; 17 import android.view.View; 18 import android.widget.CheckBox; 19 import android.widget.EditText; 20 import android.widget.Toast; 21 22 public class MainActivity extends Activity { 23 private static final String Tag = "MainActivity"; 24 private EditText et_qq; 25 private EditText et_pwd; 26 private CheckBox cb_remember; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 //查询关心的控件 33 et_qq = (EditText) findViewById(R.id.et_qq); 34 et_pwd = (EditText) findViewById(R.id.et_pwd); 35 cb_remember = (CheckBox) findViewById(R.id.cb_remember); 36 Log.i(Tag,"oncreate 被调用"); 37 //完成数据的回显。 38 readSavedData(); 39 } 40 //读取保存的数据 41 private void readSavedData() { 42 // getFilesDir() == /data/data/包名/files/ 获取文件的路径 一般系统是不会清理的。 用户手工清理,系统会有提示。 43 // getCacheDir()== /data/data/包名/cache/ 缓存文件的路径 当系统内存严重不足的时候 系统会自动的清除缓存 用户手工清理系统没有提示 44 File file = new File(getFilesDir(),"info.txt"); 45 if(file.exists()&&file.length()>0){ 46 try { 47 //FileInputStream fis = new FileInputStream(file); 48 FileInputStream fis =this.openFileInput("info.txt"); 49 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); 50 //214342###abcdef 51 String info = br.readLine(); 52 String qq = info.split("###")[0]; 53 String pwd = info.split("###")[1]; 54 et_qq.setText(qq); 55 et_pwd.setText(pwd); 56 fis.close(); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 } 60 } 61 } 62 /** 63 * 登陆按钮的点击事件,在点击事件里面获取数据 64 * @param view 65 */ 66 public void login(View view){ 67 final String qq = et_qq.getText().toString().trim(); 68 final String pwd = et_pwd.getText().toString().trim(); 69 if(TextUtils.isEmpty(qq)||TextUtils.isEmpty(pwd)){ 70 Toast.makeText(this, "qq号码或者密码不能为空", 0).show(); 71 return; 72 } 73 //判断用户是否勾选记住密码。 74 if(cb_remember.isChecked()){ 75 //保存密码 76 Log.i(Tag,"保存密码"); 77 try { 78 // File file = new File(getFilesDir(),"info.txt"); 79 // FileOutputStream fos = new FileOutputStream(file); 80 FileOutputStream fos = this.openFileOutput("info.txt", 0); 81 //214342###abcdef 82 fos.write((qq+"###"+pwd).getBytes()); 83 fos.close(); 84 Toast.makeText(this, "保存成功", 0).show(); 85 } catch (Exception e) { 86 e.printStackTrace(); 87 Toast.makeText(this, "保存失败", 0).show(); 88 } 89 }else{ 90 //无需保存密码 91 Log.i(Tag,"无需保存密码"); 92 } 93 94 //登录的操作,网络的请求 95 new Thread() { 96 public void run() { 97 //post请求提交数据 98 //String path = "http://localhost:8080/web/LoginServlet";这里不能写成localhost 99 try { 100 String path = getString(R.string.serverip); 101 URL url = new URL(path); 102 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 103 //重要,记得设置请求方式post 104 conn.setRequestMethod("POST"); 105 //重要,记得设置数据的类型 106 conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); 107 String data = "qq="+URLEncoder.encode(qq,"utf-8")+"&password="+URLEncoder.encode(pwd,"utf-8"); 108 //重要,记得设置数据的长度 109 conn.setRequestProperty("Content-Length", String.valueOf(data.length())); 110 111 //重要,记得给服务器写数据 112 conn.setDoOutput(true);//声明要给服务器写数据 113 //重要,把数据写给服务器 114 conn.getOutputStream().write(data.getBytes()); 115 116 int code = conn.getResponseCode(); 117 if(code == 200) { 118 InputStream is = conn.getInputStream(); 119 String result = StreamTools.readStream(is); 120 showToastInAnyThread(result); 121 }else { 122 showToastInAnyThread("请求失败"); 123 } 124 } catch (Exception e) { 125 e.printStackTrace(); 126 showToastInAnyThread("请求失败"); 127 } 128 }; 129 }.start(); 130 131 } 132 133 /** 134 * 显示土司 在主线程更新UI 135 * @param text 136 */ 137 public void showToastInAnyThread(final String text) { 138 runOnUiThread(new Runnable() { 139 140 public void run() { 141 Toast.makeText(MainActivity.this, text, 0).show(); 142 143 } 144 }); 145 } 146 }
这时候我们输入提交数据给服务器就不会出现乱码,如下:
(6)HttpClient-Get客户端到服务器端的乱码问题解决:
1 package com.himi.post; 2 import java.io.BufferedReader; 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import org.apache.http.HttpResponse; 9 import org.apache.http.client.HttpClient; 10 import org.apache.http.client.methods.HttpGet; 11 import org.apache.http.impl.client.DefaultHttpClient; 12 import android.app.Activity; 13 import android.os.Bundle; 14 import android.text.TextUtils; 15 import android.util.Log; 16 import android.view.View; 17 import android.widget.CheckBox; 18 import android.widget.EditText; 19 import android.widget.Toast; 20 public class MainActivity extends Activity { 21 private static final String Tag = "MainActivity"; 22 private EditText et_qq; 23 private EditText et_pwd; 24 private CheckBox cb_remember; 25 @Override 26 protected void onCreate(Bundle savedInstanceState) { 27 super.onCreate(savedInstanceState); 28 setContentView(R.layout.activity_main); 29 //查询关心的控件 30 et_qq = (EditText) findViewById(R.id.et_qq); 31 et_pwd = (EditText) findViewById(R.id.et_pwd); 32 cb_remember = (CheckBox) findViewById(R.id.cb_remember); 33 Log.i(Tag,"oncreate 被调用"); 34 //完成数据的回显。 35 readSavedData(); 36 } 37 //读取保存的数据 38 private void readSavedData() { 39 // getFilesDir() == /data/data/包名/files/ 获取文件的路径 一般系统是不会清理的。 用户手工清理,系统会有提示。 40 // getCacheDir()== /data/data/包名/cache/ 缓存文件的路径 当系统内存严重不足的时候 系统会自动的清除缓存 用户手工清理系统没有提示 41 File file = new File(getFilesDir(),"info.txt"); 42 if(file.exists()&&file.length()>0){ 43 try { 44 //FileInputStream fis = new FileInputStream(file); 45 FileInputStream fis =this.openFileInput("info.txt"); 46 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); 47 //214342###abcdef 48 String info = br.readLine(); 49 String qq = info.split("###")[0]; 50 String pwd = info.split("###")[1]; 51 et_qq.setText(qq); 52 et_pwd.setText(pwd); 53 fis.close(); 54 } catch (Exception e) { 55 e.printStackTrace(); 56 } 57 } 58 } 59 /** 60 * 登陆按钮的点击事件,在点击事件里面获取数据 61 * @param view 62 */ 63 public void login(View view){ 64 final String qq = et_qq.getText().toString().trim(); 65 final String pwd = et_pwd.getText().toString().trim(); 66 if(TextUtils.isEmpty(qq)||TextUtils.isEmpty(pwd)){ 67 Toast.makeText(this, "qq号码或者密码不能为空", 0).show(); 68 return; 69 } 70 //判断用户是否勾选记住密码。 71 if(cb_remember.isChecked()){ 72 //保存密码 73 Log.i(Tag,"保存密码"); 74 try { 75 // File file = new File(getFilesDir(),"info.txt"); 76 // FileOutputStream fos = new FileOutputStream(file); 77 FileOutputStream fos = this.openFileOutput("info.txt", 0); 78 //214342###abcdef 79 fos.write((qq+"###"+pwd).getBytes()); 80 fos.close(); 81 Toast.makeText(this, "保存成功", 0).show(); 82 } catch (Exception e) { 83 e.printStackTrace(); 84 Toast.makeText(this, "保存失败", 0).show(); 85 } 86 }else{ 87 //无需保存密码 88 Log.i(Tag,"无需保存密码"); 89 } 90 91 //登录的操作,网络的请求 92 new Thread() { 93 public void run() { 94 //String path = "http://localhost:8080/web/LoginServlet";这里不能写成localhost 95 try { 96 //httpclient get请求提交数据 97 String path = getString(R.string.serverip)+"?qq="+URLEncoder.encode(qq)+"&password="+URLEncoder.encoder(pwd); 98 //1.打开浏览器 99 HttpClient client = new DefaultHttpClient(); 100 //2.输入地址 101 HttpGet httpGet = new HttpGet(path); 102 //3.敲回车 103 HttpResponse response = client.execute(httpGet); 104 int code = response.getStatusLine().getStatusCode(); 105 if(code == 200) { 106 InputStream is = response.getEntity().getContent(); 107 String result = StreamTools.readStream(is); 108 showToastInAnyThread(result); 109 }else { 110 showToastInAnyThread("请求失败,返回码是:"+code); 111 } 112 113 114 } catch (Exception e) { 115 e.printStackTrace(); 116 showToastInAnyThread("请求失败"); 117 } 118 }; 119 }.start(); 120 121 } 122 123 /** 124 * 显示土司 在主线程更新UI 125 * @param text 126 */ 127 public void showToastInAnyThread(final String text) { 128 runOnUiThread(new Runnable() { 129 130 public void run() { 131 Toast.makeText(MainActivity.this, text, 0).show(); 132 133 } 134 }); 135 } 136 }
(7)HttpClient-Post客户端到服务器端的乱码问题解决:
1 package com.himi.post; 2 import java.io.BufferedReader; 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.util.ArrayList; 9 import java.util.List; 10 import org.apache.http.HttpResponse; 11 import org.apache.http.NameValuePair; 12 import org.apache.http.client.HttpClient; 13 import org.apache.http.client.entity.UrlEncodedFormEntity; 14 import org.apache.http.client.methods.HttpPost; 15 import org.apache.http.impl.client.DefaultHttpClient; 16 import org.apache.http.message.BasicNameValuePair; 17 import android.app.Activity; 18 import android.os.Bundle; 19 import android.text.TextUtils; 20 import android.util.Log; 21 import android.view.View; 22 import android.widget.CheckBox; 23 import android.widget.EditText; 24 import android.widget.Toast; 25 public class MainActivity extends Activity { 26 private static final String Tag = "MainActivity"; 27 private EditText et_qq; 28 private EditText et_pwd; 29 private CheckBox cb_remember; 30 @Override 31 protected void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.activity_main); 34 //查询关心的控件 35 et_qq = (EditText) findViewById(R.id.et_qq); 36 et_pwd = (EditText) findViewById(R.id.et_pwd); 37 cb_remember = (CheckBox) findViewById(R.id.cb_remember); 38 Log.i(Tag,"oncreate 被调用"); 39 //完成数据的回显。 40 readSavedData(); 41 } 42 //读取保存的数据 43 private void readSavedData() { 44 // getFilesDir() == /data/data/包名/files/ 获取文件的路径 一般系统是不会清理的。 用户手工清理,系统会有提示。 45 // getCacheDir()== /data/data/包名/cache/ 缓存文件的路径 当系统内存严重不足的时候 系统会自动的清除缓存 用户手工清理系统没有提示 46 File file = new File(getFilesDir(),"info.txt"); 47 if(file.exists()&&file.length()>0){ 48 try { 49 //FileInputStream fis = new FileInputStream(file); 50 FileInputStream fis =this.openFileInput("info.txt"); 51 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); 52 //214342###abcdef 53 String info = br.readLine(); 54 String qq = info.split("###")[0]; 55 String pwd = info.split("###")[1]; 56 et_qq.setText(qq); 57 et_pwd.setText(pwd); 58 fis.close(); 59 } catch (Exception e) { 60 e.printStackTrace(); 61 } 62 } 63 } 64 /** 65 * 登陆按钮的点击事件,在点击事件里面获取数据 66 * @param view 67 */ 68 public void login(View view){ 69 final String qq = et_qq.getText().toString().trim(); 70 final String pwd = et_pwd.getText().toString().trim(); 71 if(TextUtils.isEmpty(qq)||TextUtils.isEmpty(pwd)){ 72 Toast.makeText(this, "qq号码或者密码不能为空", 0).show(); 73 return; 74 } 75 //判断用户是否勾选记住密码。 76 if(cb_remember.isChecked()){ 77 //保存密码 78 Log.i(Tag,"保存密码"); 79 try { 80 // File file = new File(getFilesDir(),"info.txt"); 81 // FileOutputStream fos = new FileOutputStream(file); 82 FileOutputStream fos = this.openFileOutput("info.txt", 0); 83 //214342###abcdef 84 fos.write((qq+"###"+pwd).getBytes()); 85 fos.close(); 86 Toast.makeText(this, "保存成功", 0).show(); 87 } catch (Exception e) { 88 e.printStackTrace(); 89 Toast.makeText(this, "保存失败", 0).show(); 90 } 91 }else{ 92 //无需保存密码 93 Log.i(Tag,"无需保存密码"); 94 } 95 96 //登录的操作,网络的请求 97 new Thread() { 98 public void run() { 99 //post请求提交数据 100 //String path = "http://localhost:8080/web/LoginServlet";这里不能写成localhost 101 try { 102 String path = getString(R.string.serverip); 103 //1.打开浏览器 104 HttpClient client = new DefaultHttpClient(); 105 //2.输入地址 106 HttpPost httpPost = new HttpPost(path); 107 //设置一个url的表单的数据 108 List<NameValuePair> paramters = new ArrayList<NameValuePair>(); 109 paramters.add(new BasicNameValuePair("qq", qq)); 110 paramters.add(new BasicNameValuePair("password", pwd)); 111 httpPost.setEntity(new UrlEncodedFormEntity(paramters,"utf-8")); 112 //3.敲回车 113 HttpResponse response = client.execute(httpPost); 114 int code = response.getStatusLine().getStatusCode(); 115 if(code == 200) { 116 InputStream is = response.getEntity().getContent(); 117 String result = StreamTools.readStream(is); 118 showToastInAnyThread(result); 119 }else { 120 showToastInAnyThread("请求失败,返回码是:"+code); 121 } 122 123 } catch (Exception e) { 124 e.printStackTrace(); 125 showToastInAnyThread("请求失败"); 126 } 127 }; 128 }.start(); 129 130 } 131 132 /** 133 * 显示土司 在主线程更新UI 134 * @param text 135 */ 136 public void showToastInAnyThread(final String text) { 137 runOnUiThread(new Runnable() { 138 139 public void run() { 140 Toast.makeText(MainActivity.this, text, 0).show(); 141 142 } 143 }); 144 } 145 }