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、并发库知识
  • 为真正实现长连接打下基础
1 0
原创粉丝点击