Android开发--IM聊天项目(三)

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值