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);}//当第一次建库的时候,调用该方法@Overridepublic void onCreate(SQLiteDatabase db) {//创建数据库的时候把用户表创建好String sql = "create table user(id int,password varchar(20))";db.execSQL(sql);Log.i(TAG, "创建数据库.....");}//当更新数据库版本号的时候就会执行该方法@Overridepublic 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;}@Overridepublic 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);}//当第一次建库的时候,调用该方法@Overridepublic void onCreate(SQLiteDatabase db) {//创建数据库的时候把用户表创建好String sql = "create table "+TABLENAME+"(id varchar(20),password varchar(20))";db.execSQL(sql);Log.i(TAG, "创建数据库.....");}//当更新数据库版本号的时候就会执行该方法@Overridepublic 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\binL:\Androidexe\jre\bin>keytool -list -v -keystore debug.keystore
    keytool 错误: java.lang.Exception: 密钥库文件不存在: debug.keystore
    java.lang.Exception: 密钥库文件不存在: debug.keystoreat 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.keystoreat 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:74SHA1: 8F:E7:F7:06:20:57:3D:EA:09:99:B3:35:6D:FC:8C:EB:5F:A3:27:CDSHA256: 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;@Overrideprotected 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);// 启动短信验证sdkMobSDK.init(this, APPKEY, APPSECRETE);EventHandler eventHandler = new EventHandler() {@Overridepublic 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);}@Overridepublic 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() {@Overridepublic 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;elsereturn 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);}@Overrideprotected 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;@Overrideprotected 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() {@Overridepublic void afterEvent(int event, int result, Object data) {if (result == SMSSDK.RESULT_COMPLETE){//回调完成if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {//提交验证码成功runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic void run() {Toast.makeText(PhoneActivity.this,"语音验证发送",Toast.LENGTH_SHORT).show();}});}else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){//获取验证码成功runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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需注销,否则可能出现内存泄漏@Overrideprotected 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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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();//然后将得到的字符数据装载到ObjectOutputStreamObjectOutputStream oos = new ObjectOutputStream(baos);//writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它oos.writeObject(list);//最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存String listString = new String(Base64.encode(baos.toByteArray(),Base64.DEFAULT));//关闭oosoos.close();return listString;}public static  String obj2Str(Object obj)throws IOException{if(obj == null) {return "";}//实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件ByteArrayOutputStream baos = new ByteArrayOutputStream();//然后将得到的字符数据装载到ObjectOutputStreamObjectOutputStream oos = new ObjectOutputStream(baos);//writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它oos.writeObject(obj);//最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存String listString = new String(Base64.encode(baos.toByteArray(),Base64.DEFAULT));//关闭oosoos.close();return listString;}//将序列化的数据还原成Objectpublic 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 blocke.printStackTrace();}return null;}//将序列化的数据还原成Listpublic 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 blocke.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
    {// 用户名keypublic 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 blocke.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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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;@Overrideprotected 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(){@Overridepublic 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);//用户名,密码保存在SharedPreferencesSharedPreUtil.getInstance().putUser(user);            }});Button delBtn = (Button)findViewById(R.id.btn_del);delBtn.setOnClickListener(new OnClickListener(){@Overridepublic 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());}}@Overridepublic 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
  • 应用界面activity
  • fragment
  • 适配器adapter
  • 环信创建的应用的key:1128211114111196#wcfapp

    基于环信来设置单聊、群聊、发语音以及视频聊天的功能:

  • 遇到一点点问题,不知道是直接使用通讯云,还是去搜索socket,socket聊天代码实现比较复杂,底层实现比较繁琐,但是通讯云的实现也是极其复杂的。和我之前所学习的页面等都不太一样。。socket没有找到合适的学习视频。。。(目前还在导入文件阶段)
  • 简单了解了一下socket的通信过程​​​​​​简单实现一个微信QQ聊天(客户端发送消息,服务端接收,bio)_HustleLeox的博客-CSDN博客

跟视频做的过程:

  1. IMApplication文件的作用,集成环信SDK
    又出现了一系列的问题:显示没有下载NDK....
    NDK Resolution Outcome: Project settings: Gradle model version=XXX, NDK version is UNKNOWN_宓雷的博客-CSDN博客

    ​​​​学习socket的使用方法:

  2. socket不是一种协议而是一种编程调用接口,socket的使用类型,主要有两种,使用的步骤主要有:
    
    // 步骤1:创建客户端 & 服务器的连接// 创建Socket对象 & 指定服务端的IP及端口号 Socket socket = new Socket("192.168.1.32", 1989);  // 判断客户端和服务器是否连接成功  socket.isConnected());// 步骤2:客户端 & 服务器 通信
    // 通信包括:客户端 接收服务器的数据 & 发送数据 到 服务器<-- 操作1:接收服务器的数据 -->// 步骤1:创建输入流对象InputStreamInputStream 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();// 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStreambr.close();// 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReadersocket.close();// 最终关闭整个Socket连接
  3. 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;@Overrideprotected 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() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 0:receive_message.setText(response);break;}}};/*** 创建客户端 & 服务器的连接*/btnConnect.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 利用线程池直接开启一个线程 & 执行该线程mThreadPool.execute(new Runnable() {@Overridepublic 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() {@Overridepublic void onClick(View v) {// 利用线程池直接开启一个线程 & 执行该线程mThreadPool.execute(new Runnable() {@Overridepublic void run() {try {// 步骤1:创建输入流对象InputStreamis = 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() {@Overridepublic void onClick(View v) {// 利用线程池直接开启一个线程 & 执行该线程mThreadPool.execute(new Runnable() {@Overridepublic 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() {@Overridepublic void onClick(View v) {try {// 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStreamoutputStream.close();// 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReaderbr.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();}}
    }
    

Android版QQ的实现相关推荐

  1. android版qq通讯录备份,手机备份首选Android版QQ同步助手的三大理由

    目前,腾讯已发布Android版QQ同步助手,这意味着QQ同步助手已经能够满足S60 V2.V3.V5.iPhone和Android等五大平台手机用户的数据备份需求. 同步助手 本站先以一部S60平台 ...

  2. 电脑版Android版Mac版iPad版,从苹果iPad上运行Android版QQ谈移动应用跨平台

    随着移动设备的发展和普及,人们生活已离不开移动设备:手机,而各种系统的手机,甚至平板电脑也如雨后春笋般出现,这对移动设备应用开发者产生一个难题:如何开发出尽可能适应多平台的手机应用,提高对平台的兼容性 ...

  3. qq农场android版,qq农场手机版

    QQ农场华丽登场,它与PC端QQ农场数据同步,延续了PC端QQ农场大部分的功能,并在此基础上有着更多可以适应Android终端产品的特性,操作更便捷.体验更流畅.游戏趣味性的模拟了作物的成长过程,所以 ...

  4. android版qq通讯录备份通讯录备份,安卓手机怎么备份通讯录 QQ同步助手备份通讯录方法-站长资讯中心...

    随着智能手机的普及,智能手机带来的一些问题也日益增多,比如容易出现故障导致手机数据丢失,或者有时候换了手机导致原来手机上的数据没有导出,特别是通讯录/联系人资料.这时候,养成备份习惯的重要性就十分突出 ...

  5. 没有Iphone也能装逼:让Android版QQ显示成Iphone6

    在智能手机市场上苹果的iPhone一直都有着很高的关注度,不过其高昂的价格却让人望而却步.有些年轻人为了拥有一部iPhone 不惜出租胸部来做广告位,更有甚者还卖身卖肾.其实这又何苦呢.其实只要小小地 ...

  6. android 仿QQ音乐歌单效果

    最新的项目里面,有一个需求比较好玩,就是要仿造下QQ音乐里面的歌单上下切换效果,如下 先做一个类似的效果,测试效果如下: 而为了快速开发,不花时间在制造轮子上面,我选用是的zhy大神的一个自定义lay ...

  7. android人脸识显示头像自定义,Android 仿QQ头像自定义截取功能

    看了Android版QQ的自定义头像功能,决定自己实现,随便熟悉下android绘制和图片处理这一块的知识. 先看看效果: 思路分析: 这个效果可以用两个View来完成,上层View是一个遮盖物,绘制 ...

  8. android用qq浏览器打开微信网页版,QQ浏览器怎么打开微信小程序?

    继微信.QQ 之后,QQ 浏览器上也可以使用小程序了. 现已实现与微信小程序打通.QQ 浏览器 Android 版现已上线小程序,在搜索的场景下,小程序嵌入 QQ 浏览器「搜索直达」,作为直接的内容承 ...

  9. pc使用qq for android,腾讯QQ for Pad Android版

    QQ HD(平板专用手机QQ,Pad也能视频通话.语音对讲!)QQHD是腾讯专为Android Pad设计开发的一款即时通信软件,让您在平板电脑上获得功能丰富的畅聊体验.支持多人视频.视频美颜.文件多 ...

最新文章

  1. 腾讯优图13篇论文入选ICCV2019,涉及2D图像多视图生成等研究
  2. web开发工程师出路
  3. [javaweb] servlet-session 会话跟踪技术 与 session保存作用域 (三)
  4. spring bean的初始化和销毁
  5. Go 语言学习笔记(三):类型系统
  6. php中n12br,PHP常见字符串操作函数与用法总结
  7. mysql操作json优点和缺点_详解Mysql中的JSON系列操作函数
  8. 基于PCA进行多光谱和全色图像融合
  9. android 查看cad方案,android 加载dwg 图纸解决方案
  10. 无根树任意根深度加强版
  11. 对称、群论与魔术(五)——真实扑克牌图案的对称性探索
  12. illegal offset type php,PHP warning Illegal offset type错误解决办法
  13. RabbitMQ 归纳总结
  14. c和c++中 typedef struct与struct,以及 typedef struct LNode *list
  15. 基于Python实现Jacobi迭代法、Gauss-Seidel迭代法和SOR法方程组(二)
  16. HUAWEI WeAutomate RPA初级教程之PPT自动化
  17. C# 中where关键字详解
  18. 转发(Forward)和重定向(Redirect)的区别
  19. 虚拟主机和物理服务器有什么不同
  20. qq令牌码怎么提取_QQ空间怎么引流?

热门文章

  1. 【OCR识别验证码】--基于tesseract
  2. i5 1135g7和i7 1165g7区别 i51135g7和i71165g7对比差多少
  3. MySQL免安装版安装与环境变量配置
  4. SpatialEmbeddings-master
  5. 报错 AssertionError: Torch not compiled with CUDA enabled
  6. 仿写小米网站首页 顶部导航栏部分
  7. 前端实现小米官网导航栏的实现 主要通过HTML+CSS完成效果有下拉列表和一个固定定位,中间色块用来辨识固定定位,效果更加明显
  8. 一位华为码农的讲述:我来自农村,我看到的真实现状(虐心)
  9. 基于STM32的土壤温湿度、PH和氮磷钾检测系统【蓝牙显示五插针传感器MAX3485芯片协议转换】
  10. 小米加密兔和百度莱茨狗怎么玩?莱茨狗和加密兔怎么注册