Android开发--IM聊天项目(三)
来源:互联网 发布:依祈怎么淘宝不能买了 编辑:程序博客网 时间:2024/06/10 16:39
1.先解决了一下上次的小米5闪退问题。
闪退是每次按back键时候发生的。
back之后,程序的回调应该是onPause,onStop,onDestroy,而我重写了onDestroy来释放资源如下
@Override public void onDestroy(){ super.onDestroy(); Log.e(TAG, "onDestroy: " ); socket.disconnect(); thread.stop(); }
这里问题就处在thread.stop()上,这是很不安全的一种做法,但是在5.1上就没有出现闪退的问题,应该是6.0的安全机制更强了,这里并没有去翻看源码考证,只是笔者的猜测。下面我们来分析一下Thread
Thread.stop()方法时,会发生下面两件事:
1. 即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。
2. 释放该线程所持有的所有的锁
那么如何正确的停止呢?
关于如何正确停止线程,这篇文章(how to stop thread)给出了一个很好的答案, 总结起来就下面3点(在停止线程时):
1. 使用violate boolean变量来标识线程是否停止
2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性
3. 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO
2.thread–>service
由于我们要慢慢的来实现长连接,首先就得要用service啊,不能简单只是单开一个线程。
说来也是惭愧,虽然学过这些方面的知识不过还是真的第一次做service的项目开发,之前的水平也只是停留在做一个startservice,stopservice的demo上。
public class ChatService extends Service { private static final String TAG = "ChatService"; private MyBinder binder=new MyBinder(); private Socket socket=null; private boolean result=true; @Override public void onCreate(){ super.onCreate(); /* *在这里初始化socket链接 */ try { socket= IO.socket("http://115.159.38.75:3000"); socket.connect(); }catch (URISyntaxException e){ Log.e(TAG, "run: "+"error" ); e.printStackTrace(); } while (!socket.connected()){} } @Override public int onStartCommand(Intent i,int flags,int startId){ Log.e(TAG, "onStartCommand: " ); return super.onStartCommand(i,flags,startId); } @Override public void onDestroy(){ /* *关闭连接,停止监听 */ super.onDestroy(); while (socket.connected()){ socket.disconnect(); } socket.off(); Log.e(TAG, "onDestroy: "+socket.connected()); } @Override public IBinder onBind(Intent intent){ return binder; } /* *在获取信息和获取登陆结果用了两个interface来回调结果 */ public class MyBinder extends Binder{ /* *用于获取新信息 */ public void getMsg(final I_onMessageGet i_onMessageGet){ socket.on("newMsg", new Emitter.Listener() { @Override public void call(Object... args) { String newcontent=args[1].toString(); i_onMessageGet.newMsg(newcontent); } }); } public void sendMSg(String msg){ socket.emit("postMsg",msg); } public void login(String name){ socket.emit("login",name); } /* *获取登陆结果 */ public void getLoginResult(final I_loginResult i_loginResult){ socket.on("nickExisted", new Emitter.Listener() { @Override public void call(Object... args) { Log.e(TAG, "nickExisted" ); i_loginResult.loginFailed(); result=false; } }); socket.on("loginSuccess",new Emitter.Listener() { @Override public void call(Object... args) { Log.e(TAG, "loginSuccess:"+Thread.currentThread().getId()); i_loginResult.loginSuccess(); } }); } }}
代码不长也都写了功能注释。
3.新增登陆Activity
写个登陆界面用于取个名字而已。后面想配上对应的密码,不过还要在服务器端部署很多东西,正在努力补相关知识。与service的绑定也在这个activity中实现。
public class LoginActivity extends Activity { @BindView(R.id.btn_login)Button button; @BindView(R.id.edt_username)EditText editText; private static final String TAG = "LoginActivity"; private ChatService.MyBinder myBinder; private ServiceConnection serviceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { LogUtil.log(TAG,"onServiceConnected"); myBinder=(ChatService.MyBinder)service; } @Override public void onServiceDisconnected(ComponentName name) { LogUtil.log(TAG,"onServiceDisconnected"); } }; @Override protected void onCreate(Bundle s){ super.onCreate(s); setContentView(R.layout.loginactivity); ButterKnife.bind(this); Intent service=new Intent(this,ChatService.class); bindService(service,serviceConnection,BIND_AUTO_CREATE); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String name=editText.getText().toString(); myBinder.login(name); myBinder.getLoginResult(new I_loginResult() { @Override public void loginSuccess() { Intent intent=new Intent(LoginActivity.this,ChatActivity.class); startActivity(intent); finish(); } @Override public void loginFailed() { handler.obtainMessage().sendToTarget(); } }); } }); } Handler handler=new Handler(){ @Override public void handleMessage(Message message){ editText.setText(""); Toast.makeText(LoginActivity.this,"用户名重复",Toast.LENGTH_SHORT).show(); } }; @Override protected void onStart(){ LogUtil.log(TAG,"onStart"); super.onStart(); } @Override protected void onResume(){ LogUtil.log(TAG,"onResume"); super.onResume(); }}
另外在写这个activity的界面的时候用了constraintlayout,参考一下郭神的文章。http://blog.csdn.net/guolin_blog/article/details/53122387 调界面真的是相当方便啊。不过一些负责的界面还是要自己动手啊,简单的用这个constraintlayout真的是再合适不过了。
再附ChatActivity的相关改动。
public class ChatActivity extends AppCompatActivity { private static final String TAG = "ChatActivity"; @BindView(R.id.text_list)RecyclerView recyclerView; @BindView(R.id.send_btn)Button button; @BindView(R.id.chat_edit)EditText editText; private static String IPAddress="115.159.38.75"; private static int PORT=4000; private Socket socket=null; private ChatRecyclerAdpter adpter; private ArrayList<Msg> msg_list=new ArrayList<>(); private DBHelper dbHelper; private Cursor cursor; private Thread thread; private NotificationManager notificationManager; private ChatService.MyBinder binder; private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { LogUtil.log("from util","onServiceConnected: "+name.toString()); binder=(ChatService.MyBinder)service; binder.getMsg(new I_onMessageGet() { @Override public void newMsg(String s) { Log.e(TAG, "newMsg: "+s ); SendNotification(s); Msg msg=new Msg(); msg.setContent(s); msg.setType(0); msg_list.add(msg); dbHelper.insert(msg); cursor.requery(); Message.obtain(handler).sendToTarget(); } }); } @Override public void onServiceDisconnected(ComponentName name) { LogUtil.log(TAG, "onServiceDisconnected: "+name.toString()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.chatactivity); ButterKnife.bind(this); notificationManager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); dbHelper=new DBHelper(this); cursor=dbHelper.select(); for (int i=0;i<cursor.getCount();i++){ cursor.moveToPosition(i); Msg msg=new Msg(); msg.setContent(cursor.getString(1)); msg.setType(cursor.getInt(2)); msg_list.add(msg); } adpter=new ChatRecyclerAdpter(this,msg_list); LinearLayoutManager manager=new LinearLayoutManager(this); recyclerView.setLayoutManager(manager); recyclerView.setAdapter(adpter); //绑定service Intent startservice=new Intent(this,ChatService.class); bindService(startservice,connection,BIND_AUTO_CREATE); recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { recyclerView.scrollToPosition(msg_list.size()-1); } }); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String msg_send=editText.getText().toString(); binder.sendMSg(msg_send); Msg msg=new Msg(); msg.setType(1); msg.setContent(msg_send); msg_list.add(msg); dbHelper.insert(msg); cursor.requery(); Message.obtain(handler).sendToTarget(); editText.setText(""); } }); } Handler handler=new Handler(){ @Override public void handleMessage(Message message){ adpter.notifyDataSetChanged(); recyclerView.scrollToPosition(msg_list.size()-1); } }; private void SendNotification(String content){ LogUtil.log(TAG, "SendNotification: "+content ); /*这里要注意一个细节。 *如果当前Activity存在的话,不应该再create一个新的活动,应该是回到当前活动。 * 所以要给Intent添加对应的flag */ Intent i=new Intent(Intent.ACTION_MAIN); i.addCategory(Intent.CATEGORY_LAUNCHER); i.setComponent(new ComponentName(this,ChatActivity.class)); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PendingIntent p=PendingIntent.getActivity(this,0,i,0); Notification notification=new Notification.Builder(this) .setAutoCancel(true) .setContentText(content) .setContentTitle("新消息") .setTicker("Ticker") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setDefaults(Notification.DEFAULT_SOUND) .setContentIntent(p) .build(); notificationManager.notify(0,notification); } @Override public void onDestroy(){ unbindService(connection); super.onDestroy(); Log.e(TAG, "onDestroy"); }}
目前问题:
1.在魅族手机上提示了该app耗电过高??
2.掉线问题
下一部任务目标:
- 检测掉线,掉线重连
- 深入学习java io、nio、socket、并发库知识
- 为真正实现长连接打下基础
- Android开发--IM聊天项目(三)
- Android开发--IM聊天项目(一)
- Android开发--IM聊天项目(二)
- Android开发--IM聊天项目(四)--IPC实战
- Android开发--IM聊天项目(五)--进程保活踩坑
- android的开源电话/通讯/IM聊天项目全集
- Android 的开源电话/通讯/IM聊天项目全集
- Android 的开源电话/通讯/IM聊天项目全集
- Android 的开源电话/通讯/IM聊天项目全集
- Android 的开源电话/通讯/IM聊天项目全集
- Android基于环信SDK开发IM即时聊天(一)
- #征文再续#Android基于XMPP Smack Openfire开发IM即时聊天(二)
- Android基于环信SDK开发IM即时聊天(二)
- Android基于环信SDK开发IM即时聊天
- Android-IM从零开始开发一个即时通讯项目
- android 【IM】 聊天布局问题
- 移动应用(手机应用)开发IM聊天程序解决方案
- 移动应用(手机应用)开发IM聊天程序解决方案
- SPRING学习第一次错误
- Java集合框架汇总
- 如何快速转载CSDN中的博客
- Java面试题集及解答
- css样式
- Android开发--IM聊天项目(三)
- Java之自定义异常
- POJ3122_Pie_二分
- linux修改profile文件出错后所有命令用不了
- 面向对象—轮播图
- Android 中播放内存中视频
- 串口与并口的区别
- OpenGL ES 平台搭建教程(在包含Visual Studio 的Microsoft Windows 上构建)
- Android 事件的分发