Android聊天软件的开发(一)--预备知识
2014-06-06 13:33
507 查看
一,通信结构
对于软件的注册,登录,用户信息管理,通讯录等功能模块,客户端与服务器的通信结构为C/S结构,使用HTTP协议进行数据交互。而对于聊天模块,客户端与服务器的通信结构为类P2P结构(其实还是C/S,不过服务器作为中转站而已),使用Socket(套接字)实现服务器对客户端的消息推送功能。
二,RSA加解密
为了提高数据传输的安全性,针对HTTP协议的通信方式,将客户端提交的参数通过1024位长的公钥进行加密,服务器接收到数据后,使用256位长的私钥进行解密。公钥和私钥都是通过OpenSSL预先生成,保存在字符串中的。密钥生成步骤:
1. 安装OpenSSL。可以在 http://www.openssl.org/source/ 下载,直接安装。
2. 打开密令提示符,进入到你想存放密钥文件的文件夹。比如F:\key目录
3.生成密钥。在命令提示符输入下面的命令:
openssl genrsa -out private_key.pem 1024 (生成1024位长度的ASCII编码的私钥)
openssl rsa -in private_key.pem -out public_key.pem -pubout (通过私钥来生成对应的公钥)
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt (将私钥进行PKCS#8编码,这样才能使用。-nocrypt,不采用二次加密)
4.打开F:\key目录的public_key.pem和pkcs8_private_key.pem,提取-----BEGIN PUBLIC KEY-----和-----END
PUBLIC KEY-----之间的字符串,就可以分别得到公私钥。
Java中使用RSA加密(需要添加bcprov-jdk15on-150.jar和sun.misc.BASE64Decoder.jar)
客户端加密
public class EncryptUtils { /** RSA加密的公钥 ,与服务端的私钥对应*/ private static final String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0dLTXOCN7VMM4MbbxkVIwIRmEjzlKJSbd97uoWiwt3z/Q1u4DQNfTVdeLtDIEFeYUz1/mJPltMdlUDB8/YO2MfHnvipk4DC+C7mZ5DgP/5Qtglvl6alPTL2yhZNpJ5MCJQNvYk7l5A1lDwSwFKkFmBl2vHeGY76C/Y62ofeZYRwIDAQAB"; private static char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * RSA加密 */ public static String GetRsaEncrypt(String src) { if (src == null) return ""; String enData = null; try { Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider()); RSAPublicKey pubKey = GetPublicKey(publicKeyStr); cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] output = cipher.doFinal(src.getBytes("UTF-8")); enData = ByteToString(output); } catch (Exception e) { e.printStackTrace(); } return enData; } <span style="white-space:pre"> </span>/**将加密后的byte数组转换为String*/ private static String ByteToString(byte[] data) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < data.length; i++) { // 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移 stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]); // 取出字节的低四位 作为索引得到相应的十六进制标识符 stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]); } return stringBuilder.toString(); } /** * 将String型私钥转换为RSAPublicKey * * @param publicKeyStr * 公钥数据字符串 */ private static RSAPublicKey GetPublicKey(String publicKeyStr) { RSAPublicKey pubKey = null; try { BASE64Decoder base64Decoder = new BASE64Decoder(); byte[] buffer = base64Decoder.decodeBuffer(publicKeyStr); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (Exception e) { e.printStackTrace(); } return pubKey; } }
服务器解密
public class EncryptUtils { /** 服务器端RSA解密的私钥 ,与客户端的公钥对应*/ private static final String privateKeyStr = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALR0tNc4I3tUwzgxtvGRUjAhGYSPOUolJt33u6haLC3fP9DW7gNA19NV14u0MgQV5hTPX+Yk+W0x2VQMHz9g7Yx8ee+KmTgML4LuZnkOA//lC2CW+XpqU9MvbKFk2knkwIlA29iTuXkDWUPBLAUqQWYGXa8d4ZjvoL9jrah95lhHAgMBAAECgYEAqpnjFc0HDmP2I7wsXniqoMHKJB5bZRN2iUbZ7LFDLyLua/umDQFSiYOQQY1b86zYVjgvS58NCASml+TV7c8vA5care9HqpQX+1YdBUB1Hw+zloIwJD5894123wumRpbHBK1vkgvX3cKz0K+lNHfnXoov49DLvDeTCtBb9GQQX2ECQQDv1avuF4DlbVavIuTNZFw5YntZM13E2M+JMx84XGODvncuDl8C/TpW+ZsaaKqU7caXtWl2p5ip4UZPYLwyRh65AkEAwJ51buJ5qZhA7kD6is4RC4P7F2LwGkhKrpXxlzHsPeW8m9if62E7AH3B8MGicIVeQjJwxSrsiI3wYEHDlIyu/wJAN7ZzEgPztVgI4vZAIFZH9iyiar478hZLX5u4jOcpVtlP5isAdzlL7Bhfp2rY9W+mymch8KZOGGh0ZMwb67HOQQJAcOw04mXpd3CoGEWF3FxEh+C/Eo3RP0dEaSfEs6Pz4LHPqfoMfvzIj1gqm8+ZQKgfg2V40U6BzuiPlI7Zbzwu1wJAMbq668GPCzMgc0LLImkGTaOPcmjPYUbAYXa4k/90M3sX0t6s9u0kl9NfotSpF9M3AdbFSdKWXoY8XScOkhnpQQ=="; /** * RSA解密<BR/> * <BR/> * 用于将客户端上传的,经过RSA加密的参数进行解密 * * @param src * @return 解密后的数据 */ public static String GetRsaDecrypt(String src) { String deData = null; try { Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider()); RSAPrivateKey priKey = GetPrivateKey(privateKeyStr); cipher.init(Cipher.DECRYPT_MODE, priKey); byte[] output = cipher.doFinal(StringToByte(src)); deData = new String(output, "utf-8");// 此处如果不指定编码格式,则会产生中文乱码 } catch (Exception e) { e.printStackTrace(); } return deData; } /** * 将String型私钥转换为RSAPrivateKey * * @param privateKeyStr * @return */ private static RSAPrivateKey GetPrivateKey(String privateKeyStr) { RSAPrivateKey priKey = null; try { BASE64Decoder base64Decoder = new BASE64Decoder(); byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); priKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (Exception e) { e.printStackTrace(); } return priKey; } /**将解密数据转换为byte数组*/ private static byte[] StringToByte(String str) { int len = str.length(); byte[] data = new byte[len / 2]; char[] ch = str.toCharArray(); String tmp; int i = 0, j = 0; while (i < len) { //两个char组合成一个byte tmp = String.valueOf(ch[i++]); //高位 int high = Integer.parseInt(tmp, 16); tmp = String.valueOf(ch[i++]); //低位 int low = Integer.parseInt(tmp, 16); data[j] = (byte) ((high << 4) + low); j++; } return data; } }
详情可以看:/article/2129082.html
三,Log4j日志输出
Log4j用于在服务器端输出日志。首先需要添加commons-logging.jar和log4j.jar,然后在src根目录下创建一个log4j.properties文件。
使用时,只需要Logger logger = Logger.getLogger(String name); 就可以通过logger输出debug,info,error等级别的日志。不过,如果你在log4j.properties文件中设置的日志输出的最低级别为info,则不会输出debug级别的日志。
详情可以看:http://kdboy.iteye.com/blog/208851
四,低版本支持ActionBar
Android3.0版本后,才支持ActionBar。如果要在低版本中支持ActionBar,需要用到android-support-v7-appcompat项目。这个项目在Android官方SDK的sdk\extras\android\support\v7目录也可以找到,不过有一个很奇怪的地方,我用官方的appcompat项目实现下拉菜单时,其背景图片的边框是透明的,如下面左图。所以我将这个图片替换成下面的右图(将appcompat项目drawable-hdpi,drawable-mdpi,drawable-xhdpi三个目录下的abc_menu_dropdown_panel_holo_light.9.png替换即可)。在上一篇文章中讲到了如何导入和使用appcompat这个项目,接下来是以MainActivity为例,讲述在项目中如何使用兼容版本的ActionBar。
1.在AndroidManifest.xml文件中,需要在MainActivity的activity标签中添加主题。
<activity <span style="white-space:pre"> </span>android:name=".MainActivity" <span style="white-space:pre"> </span><span style="color:#cc0000;">android:theme="@style/Theme.AppCompat.Light"</span> <span style="white-space:pre"> </span>android:windowSoftInputMode="adjustPan" <span style="white-space:pre"> </span>android:screenOrientation="portrait"> </activity>
2.让MainActivity继承ActionBarActivity,通过getSupportActionBar()获得ActionBar对象。
3.通过ActionBar的addTab为其添加选项卡。
4.通过onCreateOptionsMenu添加Menu项,onOptionsItemSelected为Menu项增加下拉子菜单,onMenuItemClick监听每个子菜单的点击。由于android-support-v7-appcompat在Android3.0以下不支持Overflow按钮,如果要使用Overflow按钮,可以通过PopupMenu来实现。
在实现代码的过程中,有一点要特别注意!import ActionBar相关类的时候,要看清楚,导入的必须是appcompat项目中的类,如android.support.v7.app.ActionBar,而不是android.app.ActionBar。这是因为我们工程的target
platform是3.0以上版本,因此Android sdk包含有ActionBar相关的类。但是我们需要支持的最低版本是低于3.0的,比如部署到2.3的真机上,这时Android sdk并不含有ActionBar相关的类,需要appcompat的支持。(这里还需要区分target
platform和minSdkVersion的含义。target platform:编译项目的Android版本。minSdkVersion:支持的最低版本)
具体代码如下,
import android.support.v4.app.FragmentTransaction; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar.Tab; import android.support.v7.app.ActionBar.TabListener; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.PopupMenu.OnMenuItemClickListener; public class MainActivity extends ActionBarActivity implements OnMenuItemClickListener{ private Context mContext; private PopupMenu mAddMenu; private PopupMenu mManageMenu; private ActionBar mActionBar; private ChatFragment mChatFragment; private ContactsFragment mContactsFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mContext = this; mChatFragment = new ChatFragment(); mContactsFragment = new ContactsFragment(); setActionBar(); } private void setActionBar() { mActionBar = getSupportActionBar(); mActionBar.setNavigationMode(ActionBar.N***IGATION_MODE_TABS); // mActionBar.setDisplayShowTitleEnabled(false);//隐藏ActionBar的title TabListener listener = new MyTabListener(); mActionBar.addTab(mActionBar.newTab().setIcon(R.drawable.main_tab_chat).setText(R.string.fragment_chat).setTabListener(listener)); mActionBar.addTab(mActionBar.newTab().setIcon(R.drawable.main_tab_contacts).setText (R.string.fragment_contacts).setTabListener(listener)); } /**添加ActionBar的Menu*/ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return super.onCreateOptionsMenu(menu); } /**添加Menu的下拉子菜单*/ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_manage: if(mManageMenu == null) { mManageMenu = new PopupMenu(mContext, findViewById(R.id.action_manage)); mManageMenu.inflate(R.menu.main_item_manage); mManageMenu.setOnMenuItemClickListener(this); } mManageMenu.show(); return true; default: return super.onOptionsItemSelected(item); } } /**监听子菜单的点击事件*/ @Override public boolean onMenuItemClick(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.menu_userInfo: //TODO ... break; case R.id.menu_setting: //TODO ... break; } return false; } /**TabListener实现类*/ class MyTabListener implements TabListener{ public void onTabSelected(Tab tab, FragmentTransaction arg1) { switch (tab.getPosition()) { case 0://聊天界面 getSupportFragmentManager().beginTransaction() .replace(R.id.main_container, mChatFragment).commit(); break; case 1://通讯录界面 getSupportFragmentManager().beginTransaction() .replace(R.id.main_container, mContactsFragment).commit(); break; } } public void onTabReselected(Tab arg0, FragmentTransaction arg1) { } public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { } } }
最后实现的MainActivity界面
首页 Android聊天软件的开发
相关文章推荐
- Android聊天软件的开发(三)--网络连接
- android软电话开发预备知识
- android聊天软件开发,实战篇(1)
- Android聊天软件的开发(五)--头像设置
- Java Android程序员软件开发知识:枚举的介绍,以及代码的编写教程。
- Android聊天软件的开发--聊天通信
- Android聊天软件的开发
- Android聊天软件的开发(七)--聊天通信
- 智渔课堂官方免费教程一:Java软件开发预备知识
- Android聊天软件界面开发
- android聊天软件开发,实战篇(2)
- 适用于Android开发的简单聊天软件
- Android聊天软件的开发
- I学霸官方免费教程一:Java软件开发预备知识
- 嵌入式软件开发培训笔记——s5pc100裸机编程预备知识
- Android聊天软件的开发(六)--表情
- Android聊天软件的开发(二)--数据库
- Android聊天软件的开发(四)--通讯录
- 从事手机软件开发需要掌握什么知识