irpas技术客

Android版QQ的实现_cc睡醒了_android qq

网络投稿 6092

1、首先设置好基本的布局和页面设置;

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

2、建立SQLite数据库创建用户的信息表;完成用户的登录和注册功能; 首先创建一个数据库类继承SQLiteOpenHelper package com.example.myapplication.Database; import android.database.sqlite.SQLiteOpenHelper; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.util.Log; /** * ClassName:DatabaseHelper * Package:com.example.myapplication.Database * Description: meet 985&ta *创建一个数据库的类; * @Data:2021/11/08 16:47 * @Author:灿灿睡醒了 */ public class DatabaseHelper extends SQLiteOpenHelper { private static final int VERSION = 3;//数据库版本号 private static String DB_NAME="user";//数据库名称 private static String TAG="DatabaseHelper";//数据库名称 public DatabaseHelper(Context context) { super(context, DB_NAME, null, VERSION); } //当第一次建库的时候,调用该方法 @Override public void onCreate(SQLiteDatabase db) { //创建数据库的时候把用户表创建好 String sql = "create table user(id int,password varchar(20))"; db.execSQL(sql); Log.i(TAG, "创建数据库....."); } //当更新数据库版本号的时候就会执行该方法 @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { Log.i(TAG, "更新数据库....."); } } 创建UserDao.java,用来实现对数据库的增删改查; 可以在activity的里面调用这个类实现对用户的增删改查(一种简单而直接的方法) 利用SQliteDatabase? Android---实现SQLite数据库的增删改查_qiudonga的博客-CSDN博客用户类,用来实现账号和密码的获取以及设置 package com.example.myapplication.Shixian; import java.io.Serializable; /** * ClassName:User * Package:com.example.myapplication.Shixian * Description: meet 985&ta * 在User文件中声明要用到的表列名的变量,并对其添加get&&set方法: * 完成QQ的注册功能; * @Data:2021/11/07 20:18 * @Author:灿灿睡醒了 */ //用户表的设置; public class User implements Serializable { //用户的信息表只包括两个信息; private static String id;//用户的账号 private static String password;//用户密码 public User() { super(); // TODO Auto-generated constructor stub } public User(String id,String password) { super(); this.id = id; this.password = password; } public static String getId() { return id; } public void setId(String id) { this.id = id; } public static String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [id=" + id + ", password=" + password + "]"; } }

增删改查UserDao.java ?

package com.example.myapplication.Database; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import android.view.View; import com.example.myapplication.Shixian.User; /** * ClassName:Dao * Package:com.example.myapplication.Database * Description: meet 985&ta * 此类用来创建用户数据库以及增删改查; * @Data:2021/11/08 17:08 * @Author:灿灿睡醒了 */ public class UserDao { private final DatabaseHelper databaseHelper;//设置为一个静态变量; private SQLiteDatabase db;//外部全局变量; public UserDao(Context context) { //创建数据库; databaseHelper = new DatabaseHelper(context); } //往用户表添加数据 public void insert( ) { //打开该数据库; db = databaseHelper.getWritableDatabase(); //在这里引入表名;插入字段和数据; String sql = "insert into " + databaseHelper.TABLENAME + "(id,password)values(?,?)"; Object obj[]= new Object[]{User.getId(), User.getPassword()}; db.execSQL(sql, obj); db.close(); Log.i("RegisterActivity", "添加了一个用户记录"); } //往用户表删除数据 public void delete( ) { //打开该数据库; db = databaseHelper.getWritableDatabase(); String sql = "delete from " + databaseHelper.TABLENAME + "where id=?"; Object obj[]= new Object[]{User.getId()}; db.execSQL(sql,obj); db.close(); } //更改数据; public void update(String id,String password){ //打开该数据库; db = databaseHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("password", password); //参数1:表名 参数2:修改的值 参数三:查询条件 参数4:查询条件需要的参数 //根据id来修改password; db.update("user", contentValues, "id=?", new String[]{id}); db.close(); Log.i("ForgetActivity","修改密码成功"); } //判断数据库是否存在该账号 public boolean find(String id){ //打开该数据库; db = databaseHelper.getWritableDatabase(); //默认没有该数据 boolean flag=false; //查询所有号码(表名,查询账号,查询条件没有--null) Cursor cursor=db.query("users",new String[]{"id"},null,null,null,null,null); //如果游标能往下移动 while (cursor.moveToNext()){ //遍历Cursor对象,跟传入的id进行比较,如果相同就返回true,说明数据库存在该数据 if(id.equals(cursor.getString(0))){ flag=true; } } /* 一定要关闭游标,回收游标对象 */ cursor.close(); db.close(); return flag; } } DatabaseHelper.java类创建数据库实验对象: package com.example.myapplication.Database; import android.database.sqlite.SQLiteOpenHelper; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.util.Log; /** * ClassName:DatabaseHelper * Package:com.example.myapplication.Database * Description: meet 985&ta *创建一个数据库的类; * @Data:2021/11/08 16:47 * @Author:灿灿睡醒了 */ public class DatabaseHelper extends SQLiteOpenHelper { private static final int VERSION = 3;//数据库版本号 private static String DB_NAME="USER";//数据库名称 private static String TAG="DatabaseHelper";//数据库名称 public static String TABLENAME="users";//表名称 public DatabaseHelper(Context context) { super(context, DB_NAME, null, VERSION); } //当第一次建库的时候,调用该方法 @Override public void onCreate(SQLiteDatabase db) { //创建数据库的时候把用户表创建好 String sql = "create table "+TABLENAME+"(id varchar(20),password varchar(20))"; db.execSQL(sql); Log.i(TAG, "创建数据库....."); } //当更新数据库版本号的时候就会执行该方法 @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { Log.i(TAG, "更新数据库....."); //先不使用此功能; } } 测试数据库:可以自动生成测试类 右键-->go to-->Test 实现登录验证功能LoginActivity.java和UserDao.java:

添加手机验证码功能时:

注册页面:mob.com/mobService/sms

具体验证码步骤的实现

Android中实现手机获取验证码登录的功能_季风__的博客-CSDN博客_android手机验证码登录

Android proguard文件路径 - 简书? ?混淆文件

安卓 实现获取手机验证码登录功能_A赴野的博客-CSDN博客

遇到的一些问题: ①显示你的NDK找不到,打开File->Project Structure->SDK Location 在Android NDK location一栏点击下载,下载完成后clean project再build project就可以了。 解决方法:就是在settings里面找到AndroidSDK-->SDKTools自动下载相应的版本 ②在注册应用时找不到MD5密钥:

C:\WINDOWS\system32>L: L:\>cd L:\Androidexe\jre\bin L:\Androidexe\jre\bin>keytool -list -v -keystore debug.keystore keytool 错误: java.lang.Exception: 密钥库文件不存在: debug.keystore java.lang.Exception: 密钥库文件不存在: debug.keystore at sun.security.tools.keytool.Main.doCommands(Main.java:768) at sun.security.tools.keytool.Main.run(Main.java:366) at sun.security.tools.keytool.Main.main(Main.java:359) L:\Androidexe\jre\bin>cd L:\Androidexe\jre\jre\bin\ L:\Androidexe\jre\jre\bin>keytool -list -v -keystore debug.keystore keytool 错误: java.lang.Exception: 密钥库文件不存在: debug.keystore java.lang.Exception: 密钥库文件不存在: debug.keystore at sun.security.tools.keytool.Main.doCommands(Main.java:768) at sun.security.tools.keytool.Main.run(Main.java:366) at sun.security.tools.keytool.Main.main(Main.java:359)

我根据这篇文章注册的密钥:android中关于keytool 错误:java.lang.Exception:密钥库文件不存在: 解决步骤_ALazyPerson的博客-CSDN博客

别名: androiddebugkey 创建日期: 2021-11-11 条目类型: PrivateKeyEntry 证书链长度: 1 证书[1]: 所有者: CN=wu, OU=hnust, O=hnust, L=hunan, ST=hunan, C=china 发布者: CN=wu, OU=hnust, O=hnust, L=hunan, ST=hunan, C=china 序列号: 2d563662 有效期为 Thu Nov 11 09:32:14 CST 2021 至 Mon Mar 29 09:32:14 CST 2049 证书指纹: ? ? ? ? ?MD5: ?CC:C5:0C:B1:69:E2:0D:A1:4C:90:3F:02:2A:7B:05:74 ? ? ? ? ?SHA1: 8F:E7:F7:06:20:57:3D:EA:09:99:B3:35:6D:FC:8C:EB:5F:A3:27:CD ? ? ? ? ?SHA256: AC:8A:47:6A:B5:DC:14:AC:65:DD:86:26:7D:6B:9B:72:CB:F0:E7:D7:7F:82:35:D4:AB:83:24:E7:3E:CC:CF:83 签名算法名称: SHA256withRSA 主体公共密钥算法: 2048 位 RSA 密钥 版本: 3 扩展: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 01 76 DB 69 19 C6 33 22 ? D2 B5 B2 5E 08 A4 A1 99 ?.v.i..3"...^.... 0010: A1 4F 4B 9B ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.OK. ] ]

验证码功能代码段1?

package com.example.myapplication.widget; import android.content.Intent; import android.os.Handler; import android.os.Message; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.myapplication.R; import com.mob.MobSDK; import cn.smssdk.EventHandler; import cn.smssdk.SMSSDK; public class PhoneActivity extends AppCompatActivity implements View.OnClickListener { String APPKEY = "34afc2be232e8"; String APPSECRETE = "09dc75fb83ae9d913ac3b5b8156ffc7d"; // 手机号输入框 private EditText inputPhoneEt; // 验证码输入框 private EditText inputCodeEt; // 获取验证码按钮 private Button requestCodeBtn; // 验证按钮 private Button commitBtn; //倒计时 int i = 30; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_phone); init(); //初始化控件 } private void init() { inputPhoneEt = (EditText) findViewById(R.id.phone); inputCodeEt = (EditText) findViewById(R.id.et_yanzheng); requestCodeBtn = (Button) findViewById(R.id.but_yanzheng); commitBtn = (Button) findViewById(R.id.check); requestCodeBtn.setOnClickListener(this); commitBtn.setOnClickListener(this); // 启动短信验证sdk MobSDK.init(this, APPKEY, APPSECRETE); EventHandler eventHandler = new EventHandler() { @Override public void afterEvent(int event, int result, Object data) { Message msg = new Message(); msg.arg1 = event; msg.arg2 = result; msg.obj = data; handler.sendMessage(msg); } }; //注册回调监听接口 SMSSDK.registerEventHandler(eventHandler); } @Override public void onClick(View v) { String phone = inputPhoneEt.getText().toString(); switch (v.getId()) { case R.id.phone: // 1. 通过规则判断手机号 if (!judgePhoneNums(phone)) { return; } // 2. 通过sdk发送短信验证 SMSSDK.getVerificationCode("86", phone); // 3. 把按钮变成不可点击,并且显示倒计时(正在获取) requestCodeBtn.setClickable(false); requestCodeBtn.setText("重新发送(" + i + ")"); new Thread(new Runnable() { @Override public void run() { for (; i > 0; i--) { handler.sendEmptyMessage(-9); if (i <= 0) { break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } handler.sendEmptyMessage(-8); } }).start(); break; case R.id.check: //将收到的验证码和手机号提交再次核对 SMSSDK.submitVerificationCode("86", phone, inputCodeEt .getText().toString()); createProgressBar(); break; } } Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == -9) { requestCodeBtn.setText("重新发送(" + i + ")"); } else if (msg.what == -8) { requestCodeBtn.setText("获取验证码"); requestCodeBtn.setClickable(true); i = 30; } else { int event = msg.arg1; int result = msg.arg2; Object data = msg.obj; Log.e("event", "event=" + event); if (result == SMSSDK.RESULT_COMPLETE) { // 短信注册成功后,返回MainActivity,然后提示 if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {// 提交验证码成功 Toast.makeText(getApplicationContext(), "提交验证码成功", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(PhoneActivity.this, LoginActivity.class); startActivity(intent); } else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE) { Toast.makeText(getApplicationContext(), "正在获取验证码", Toast.LENGTH_SHORT).show(); } else { ((Throwable) data).printStackTrace(); } } } } }; private boolean judgePhoneNums(String phoneNums) { if (isMatchLength(phoneNums, 11) && isMobileNO(phoneNums)) { return true; } Toast.makeText(this, "手机号码输入有误!", Toast.LENGTH_SHORT).show(); return false; } public static boolean isMatchLength(String str, int length) { if (str.isEmpty()) { return false; } else { return str.length() == length ? true : false; } } public static boolean isMobileNO(String mobileNums) { /* * 移动:134、135、136、137、138、139、150、151、157(TD)、158、159、187、188 * 联通:130、131、132、152、155、156、185、186 电信:133、153、180、189、(1349卫通) * 总结起来就是第一位必定为1,第二位必定为3或5或8,其他位置的可以为0-9 */ String telRegex = "[1][356]\\d{9}";// "[1]"代表第1位为数字1,"[358]"代表第二位可以为3、5、8中的一个,"\\d{9}"代表后面是可以是0~9的数字,有9位。 if (TextUtils.isEmpty(mobileNums)) return false; else return mobileNums.matches(telRegex); } private void createProgressBar() { FrameLayout layout = (FrameLayout) findViewById(android.R.id.content); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER; ProgressBar mProBar = new ProgressBar(this); mProBar.setLayoutParams(layoutParams); mProBar.setVisibility(View.VISIBLE); layout.addView(mProBar); } @Override protected void onDestroy() { SMSSDK.unregisterAllEventHandler(); super.onDestroy(); } }

上面这串代码为啥作用不起来,奇怪了呢

验证码功能代码段2:显示有网络问题 ?

package com.example.myapplication.widget; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.myapplication.R; import com.example.myapplication.Shixian.Utils; import org.json.JSONException; import org.json.JSONObject; import cn.smssdk.EventHandler; import cn.smssdk.SMSSDK; public class PhoneActivity extends AppCompatActivity { private Button buttonCode,buttonLogin; private EditText editTextPhoneNum,editTextCode; private String phoneNum,code; private EventHandler eh; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_phone); buttonCode = findViewById(R.id.but_yanzheng); buttonLogin = findViewById(R.id.check); editTextCode = findViewById(R.id.et_yanzheng); editTextPhoneNum = findViewById(R.id.phone); eh = new EventHandler() { @Override public void afterEvent(int event, int result, Object data) { if (result == SMSSDK.RESULT_COMPLETE){ //回调完成 if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) { //提交验证码成功 runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(PhoneActivity.this,"登录成功",Toast.LENGTH_SHORT).show(); //如果需要跳转可在这里进行跳转页面 //登录成功跳转到软件首页页面 Intent intent=new Intent(PhoneActivity.this,RegisterActivity.class); startActivity(intent); } }); }else if (event == SMSSDK.EVENT_GET_VOICE_VERIFICATION_CODE){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(PhoneActivity.this,"语音验证发送",Toast.LENGTH_SHORT).show(); } }); } else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){ //获取验证码成功 runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(PhoneActivity.this,"验证码已发送",Toast.LENGTH_SHORT).show(); } }); }else if (event == SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){ Log.i("test","test"); } }else{ ((Throwable)data).printStackTrace(); Throwable throwable = (Throwable) data; throwable.printStackTrace(); Log.i("1234",throwable.toString()); try { JSONObject obj = new JSONObject(throwable.getMessage()); final String des = obj.optString("detail"); if (!TextUtils.isEmpty(des)){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(PhoneActivity.this,des,Toast.LENGTH_SHORT).show(); } }); } } catch (JSONException e) { e.printStackTrace(); } } } }; //注册一个事件回调监听,用于处理SMSSDK接口请求的结果 SMSSDK.registerEventHandler(eh); buttonCode.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { phoneNum = editTextPhoneNum.getText().toString(); if(!phoneNum.isEmpty()){ if(Utils.checkTel(phoneNum)){ //利用正则表达式获取检验手机号 // 获取验证码 SMSSDK.getVerificationCode("86", phoneNum); }else{ Toast.makeText(getApplicationContext(),"请输入有效的手机号",Toast.LENGTH_LONG).show(); } }else { Toast.makeText(getApplicationContext(),"请输入手机号",Toast.LENGTH_LONG).show(); return; } phoneNum = editTextPhoneNum.getText().toString(); } }); buttonLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { code = editTextCode.getText().toString(); if(!code.isEmpty()){ //提交验证码 SMSSDK.submitVerificationCode("86", phoneNum, code); }else{ Toast.makeText(getApplicationContext(),"请输入验证码",Toast.LENGTH_LONG).show(); return; } } }); } // 使用完EventHandler需注销,否则可能出现内存泄漏 @Override protected void onDestroy() { super.onDestroy(); SMSSDK.unregisterEventHandler(eh); } } package com.example.myapplication.Shixian; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ClassName:Utils * Package:com.example.myapplication.Shixian * Description: meet 985&ta * * @Data:2021/11/11 9:54 * @Author:灿灿睡醒了 */ public class Utils { /** * 正则匹配手机号码: */ public static boolean checkTel(String tel) { Pattern p = Pattern.compile("^[1][3,4,5,6,7,8,9][0-9]{9}$"); Matcher matcher = p.matcher(tel); return matcher.matches(); } }

不知道为啥。。。


学习SharedPreferences的用法:https://blog.csdn.net/baidu_31093133/article/details/51476726

使用步骤:

概述

SharedPreferences是一种轻量级的数据存储方式,采用键值对的存储方式。

SharedPreferences只能存储少量数据,大量数据不能使用该方式存储,支持存储的数据类型有booleans, floats, ints, longs, and strings。

SharedPreferences存储到一个XML文件中的在/data/data/<packagename>/shared_prefs/ ?

基本用法 1.获取SharedPreferences对象

mContextSp = this.getSharedPreferences( "testContextSp", Context.MODE_PRIVATE ); ---创建的文件名是,testContextSp.xml ---第一个参数指定了SharedPreferences存储的文件的文件名,第二个参数mode指定了操作的模式 mActivitySp = this.getPreferences( Context.MODE_PRIVATE ); ---创建的文件名是MainActivity.xml(该Activity叫MainActivity)

2.数据更新

SharedPreferences mContextSp = this.getSharedPreferences( "testContextSp", Context.MODE_PRIVATE ); SharedPreferences.Editor editor = mContextSp.edit(); editor.putInt( "age", 28); editor.putBoolean( "isStudent", false ); editor.putString( "job", "it" ); editor.commit(); //SharedPreferences添加或更新数据,操作文件数据,最后通过commit()或apply()提交修改。

3.操作后,在对应应用路径下有创建testContextSp.xml。具体手机里的数据如下 ?

<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="job">it</string> <int name="age" value="28" /> <boolean name="isStudent" value="false" /> </map>

数据获取 ?

//SharedPreferences提供的getInt(),getString()等获取文件中的数据,若数据不存在,则返回一个默认值 mContextSp = this.getSharedPreferences( "testContextSp", Context.MODE_PRIVATE ); String name = mContextSp.getString( "name", "bbb" ); int age = mContextSp.getInt( "age", 0 ); boolean isStudent = mContextSp.getBoolean( "isStudent", false );

实例(可以用于记住密码选项) ?

public class MainActivity extends Activity { SharedPreferences mContextSp; SharedPreferences mActivitySp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); mContextSp = this.getSharedPreferences( "testContextSp", Context.MODE_PRIVATE ); mActivitySp = this.getPreferences( Context.MODE_PRIVATE ); mActivitySp.edit().commit();//only create file //插入数据 Button addBtn = findViewById( R.id.add_data_btn ); addBtn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { SharedPreferences.Editor editor = mContextSp.edit(); editor.putString( "name", "aaa" ); editor.putInt( "age", 18); editor.putBoolean( "isStudent", true ); editor.commit(); } } ); //更新数据 Button updateBtn = findViewById( R.id.update_data_btn ); updateBtn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { SharedPreferences.Editor editor = mContextSp.edit(); editor.putInt( "age", 28); editor.putBoolean( "isStudent", false ); editor.putString( "job", "it" ); editor.commit(); } } ); //获取数据 Button getDataBtn = findViewById( R.id.get_data_btn ); getDataBtn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { String name = mContextSp.getString( "name", "bbb" ); int age = mContextSp.getInt( "age", 0 ); boolean isStudent = mContextSp.getBoolean( "isStudent", false ); Log.d( "sp_test", "name="+name+";age="+age+";isStudent="+isStudent); } } ); } }

但是这样的方法有一些弊端:

(1)在存放一些集合信息,存储ArrayList就不合适

(2)如果针对用户,新增加了很多熟悉,比如性别,头像等信息,那么需要一个一个的添加put和get方法,非常的繁琐。

序列化方法保存对象

import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StreamCorruptedException; import java.util.List; import android.util.Base64; public class SerializableUtil { public static <E> String list2String(List<E> list) throws IOException{ //实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //然后将得到的字符数据装载到ObjectOutputStream ObjectOutputStream oos = new ObjectOutputStream(baos); //writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它 oos.writeObject(list); //最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存 String listString = new String(Base64.encode(baos.toByteArray(),Base64.DEFAULT)); //关闭oos oos.close(); return listString; } public static String obj2Str(Object obj)throws IOException { if(obj == null) { return ""; } //实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //然后将得到的字符数据装载到ObjectOutputStream ObjectOutputStream oos = new ObjectOutputStream(baos); //writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它 oos.writeObject(obj); //最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存 String listString = new String(Base64.encode(baos.toByteArray(),Base64.DEFAULT)); //关闭oos oos.close(); return listString; } //将序列化的数据还原成Object public static Object str2Obj(String str) throws StreamCorruptedException,IOException{ byte[] mByte = Base64.decode(str.getBytes(),Base64.DEFAULT); ByteArrayInputStream bais = new ByteArrayInputStream(mByte); ObjectInputStream ois = new ObjectInputStream(bais); try { return ois.readObject(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } //将序列化的数据还原成List public static <E> List<E> string2List(String str) throws StreamCorruptedException,IOException{ byte[] mByte = Base64.decode(str.getBytes(),Base64.DEFAULT); ByteArrayInputStream bais = new ByteArrayInputStream(mByte); ObjectInputStream ois = new ObjectInputStream(bais); List<E> stringList = null; try { stringList = (List<E>) ois.readObject(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return stringList; } }

编写SharedPreUtil ,实现对对象的读取和保存

import java.io.IOException; import java.io.StreamCorruptedException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; public class SharedPreUtil { // 用户名key public final static String KEY_NAME = "KEY_NAME"; public final static String KEY_LEVEL = "KEY_LEVEL"; private static SharedPreUtil s_SharedPreUtil; private static UserEntity s_User = null; private SharedPreferences msp; // 初始化,一般在应用启动之后就要初始化 public static synchronized void initSharedPreference(Context context) { if (s_SharedPreUtil == null) { s_SharedPreUtil = new SharedPreUtil(context); } } /** * 获取唯一的instance * * @return */ public static synchronized SharedPreUtil getInstance() { return s_SharedPreUtil; } public SharedPreUtil(Context context) { msp = context.getSharedPreferences("SharedPreUtil", Context.MODE_PRIVATE | Context.MODE_APPEND); } public SharedPreferences getSharedPref() { return msp; } public synchronized void putUser(UserEntity user) { Editor editor = msp.edit(); String str=""; try { str = SerializableUtil.obj2Str(user); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } editor.putString(KEY_NAME,str); editor.commit(); s_User = user; } public synchronized UserEntity getUser() { if (s_User == null) { s_User = new UserEntity(); //获取序列化的数据 String str = msp.getString(SharedPreUtil.KEY_NAME, ""); try { Object obj = SerializableUtil.str2Obj(str); if(obj != null){ s_User = (UserEntity)obj; } } catch (StreamCorruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return s_User; } public synchronized void DeleteUser() { Editor editor = msp.edit(); editor.putString(KEY_NAME,""); editor.commit(); s_User = null; } }

?调用Activity代码

import android.app.Activity; import android.os.Bundle; import android.text.TextUtils; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class ActivityMain extends Activity { EditText edit_pwd; EditText edit_name; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SharedPreUtil.initSharedPreference(getApplicationContext()); edit_pwd = (EditText)findViewById(R.id.pwd); edit_name = (EditText)findViewById(R.id.name); button = (Button)findViewById(R.id.btn); //保存到本地 button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String name = edit_name.getText().toString(); String pwd = edit_pwd.getText().toString(); UserEntity user = new UserEntity(); user.setPassword(pwd); user.setUserName(name); //用户名,密码保存在SharedPreferences SharedPreUtil.getInstance().putUser(user); } }); Button delBtn = (Button)findViewById(R.id.btn_del); delBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SharedPreUtil.getInstance().DeleteUser(); edit_name.setText(""); edit_pwd.setText(""); } }); UserEntity user = SharedPreUtil.getInstance().getUser(); if(!TextUtils.isEmpty(user.getPassword()) && !TextUtils.isEmpty( user.getUserName() ) ){ edit_name.setText(user.getUserName()); edit_pwd.setText(user.getPassword()); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } }

找了半天问题出现在这里: Couldn't read row 0, col 1 from CursorWindow. ?Make sure the Cursor is initialized correctly before accessing data from it.


实现单聊、群聊、发语音以及语音聊天和视频聊天功能 根据MVC架构来构建项目: ? 控制层controller应用界面activityfragment适配器adapter ?环信创建的应用的key:1128211114111196#wcfapp
基于环信来设置单聊、群聊、发语音以及视频聊天的功能: 遇到一点点问题,不知道是直接使用通讯云,还是去搜索socket,socket聊天代码实现比较复杂,底层实现比较繁琐,但是通讯云的实现也是极其复杂的。和我之前所学习的页面等都不太一样。。socket没有找到合适的学习视频。。。(目前还在导入文件阶段)简单了解了一下socket的通信过程??????简单实现一个微信QQ聊天(客户端发送消息,服务端接收,bio)_HustleLeox的博客-CSDN博客

跟视频做的过程:

IMApplication文件的作用,集成环信SDK 又出现了一系列的问题:显示没有下载NDK....NDK Resolution Outcome: Project settings: Gradle model version=XXX, NDK version is UNKNOWN_宓雷的博客-CSDN博客 ????学习socket的使用方法:socket不是一种协议而是一种编程调用接口,socket的使用类型,主要有两种,使用的步骤主要有: // 步骤1:创建客户端 & 服务器的连接 // 创建Socket对象 & 指定服务端的IP及端口号 Socket socket = new Socket("192.168.1.32", 1989); // 判断客户端和服务器是否连接成功 socket.isConnected()); // 步骤2:客户端 & 服务器 通信 // 通信包括:客户端 接收服务器的数据 & 发送数据 到 服务器 <-- 操作1:接收服务器的数据 --> // 步骤1:创建输入流对象InputStream InputStream is = socket.getInputStream() // 步骤2:创建输入流读取器对象 并传入输入流对象 // 该对象作用:获取服务器返回的数据 InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); // 步骤3:通过输入流读取器对象 接收服务器发送过来的数据 br.readLine(); <-- 操作2:发送数据 到 服务器 --> // 步骤1:从Socket 获得输出流对象OutputStream // 该对象作用:发送数据 OutputStream outputStream = socket.getOutputStream(); // 步骤2:写入需要发送的数据到输出流对象中 outputStream.write(("Carson_Ho"+"\n").getBytes("utf-8")); // 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞 // 步骤3:发送数据到服务端 outputStream.flush(); // 步骤3:断开客户端 & 服务器 连接 os.close(); // 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStream br.close(); // 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReader socket.close(); // 最终关闭整个Socket连接 MainActivity.java 在主页面实现数据客户端和服务端的数据传递: package scut.carson_ho.socket_carson; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MainActivity extends AppCompatActivity { // 主线程Handler // 用于将从服务器获取的消息显示出来 private Handler mMainHandler; // Socket变量 private Socket socket; // 线程池 // 为了方便展示,此处直接采用线程池进行线程管理,而没有一个个开线程 private ExecutorService mThreadPool; /** * 接收服务器消息 变量 */ // 输入流对象 InputStream is; // 输入流读取器对象 InputStreamReader isr ; BufferedReader br ; // 接收服务器发送过来的消息 String response; /** * 发送消息到服务器 变量 */ // 输出流对象 OutputStream outputStream; /** * 按钮 变量 */ // 连接 断开连接 发送数据到服务器 的按钮变量 private Button btnConnect, btnDisconnect, btnSend; // 显示接收服务器消息 按钮 private TextView Receive,receive_message; // 输入需要发送的消息 输入框 private EditText mEdit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * 初始化操作 */ // 初始化所有按钮 btnConnect = (Button) findViewById(R.id.connect); btnDisconnect = (Button) findViewById(R.id.disconnect); btnSend = (Button) findViewById(R.id.send); mEdit = (EditText) findViewById(R.id.edit); receive_message = (TextView) findViewById(R.id.receive_message); Receive = (Button) findViewById(R.id.Receive); // 初始化线程池 mThreadPool = Executors.newCachedThreadPool(); // 实例化主线程,用于更新接收过来的消息 mMainHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: receive_message.setText(response); break; } } }; /** * 创建客户端 & 服务器的连接 */ btnConnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 利用线程池直接开启一个线程 & 执行该线程 mThreadPool.execute(new Runnable() { @Override public void run() { try { // 创建Socket对象 & 指定服务端的IP 及 端口号 socket = new Socket("192.168.1.172", 8989); // 判断客户端和服务器是否连接成功 System.out.println(socket.isConnected()); } catch (IOException e) { e.printStackTrace(); } } }); } }); /** * 接收 服务器消息 */ Receive.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 利用线程池直接开启一个线程 & 执行该线程 mThreadPool.execute(new Runnable() { @Override public void run() { try { // 步骤1:创建输入流对象InputStream is = socket.getInputStream(); // 步骤2:创建输入流读取器对象 并传入输入流对象 // 该对象作用:获取服务器返回的数据 isr = new InputStreamReader(is); br = new BufferedReader(isr); // 步骤3:通过输入流读取器对象 接收服务器发送过来的数据 response = br.readLine(); // 步骤4:通知主线程,将接收的消息显示到界面 Message msg = Message.obtain(); msg.what = 0; mMainHandler.sendMessage(msg); } catch (IOException e) { e.printStackTrace(); } } }); } }); /** * 发送消息 给 服务器 */ btnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 利用线程池直接开启一个线程 & 执行该线程 mThreadPool.execute(new Runnable() { @Override public void run() { try { // 步骤1:从Socket 获得输出流对象OutputStream // 该对象作用:发送数据 outputStream = socket.getOutputStream(); // 步骤2:写入需要发送的数据到输出流对象中 outputStream.write((mEdit.getText().toString()+"\n").getBytes("utf-8")); // 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞 // 步骤3:发送数据到服务端 outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } }); } }); /** * 断开客户端 & 服务器的连接 */ btnDisconnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { // 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStream outputStream.close(); // 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReader br.close(); // 最终关闭整个Socket连接 socket.close(); // 判断客户端和服务器是否已经断开连接 System.out.println(socket.isConnected()); } catch (IOException e) { e.printStackTrace(); } } }); } } 创建服务器的线程: package mina; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class TestServer { public static void main(String[] args) { NioSocketAcceptor acceptor = null; try { acceptor = new NioSocketAcceptor(); acceptor.setHandler(new TestHandler()); acceptor.getFilterChain().addLast("mFilter", new ProtocolCodecFilter(new TextLineCodecFactory())); acceptor.setReuseAddress(true); acceptor.bind(new InetSocketAddress(8989)); } catch (Exception e) { e.printStackTrace(); } } }


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Android #QQ