Android聊天软件的开发(一)--预备知识

一,通信结构

   对于软件的注册,登录,用户信息管理,通讯录等功能模块,客户端与服务器的通信结构为C/S结构,使用HTTP协议进行数据交互。
Android聊天软件的开发(一)--预备知识_第1张图片

   而对于聊天模块,客户端与服务器的通信结构为类P2P结构(其实还是C/S,不过服务器作为中转站而已),使用Socket(套接字)实现服务器对客户端的消息推送功能。
Android聊天软件的开发(一)--预备知识_第2张图片


二,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;
	}

	/**将加密后的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解密
*
* 用于将客户端上传的,经过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; } }



详情可以看:http://blog.csdn.net/chaijunkun/article/details/7275632

三,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标签中添加主题。
	android:name=".MainActivity"
	android:theme="@style/Theme.AppCompat.Light" 
	android:windowSoftInputMode="adjustPan"
	android:screenOrientation="portrait">

   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.NAVIGATION_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聊天软件的开发(一)--预备知识_第3张图片



首页   Android聊天软件的开发








你可能感兴趣的:(Android软件开发)