花了两天重写学习了一下IPC、binder等相关知识,于是决定把这个聊天项目也重构一下,让聊天的service功能单独在一个进程,一个是为了更加稳定,对内存的控制也能更加合理,使应用常驻后台,防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。 Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。 典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。
下面放代码
// IChatManager.aidl
package com.sp.chattingroom;
// Declare any non-default types here with import statements
import com.sp.chattingroom.Model.Msg;
import com.sp.chattingroom.I_NewMessageArrived;
import com.sp.chattingroom.I_GetLoginResult;
interface IChatManager {
void registerNewMsgListener(I_NewMessageArrived listener);
void registerLoginResultListener(I_GetLoginResult listener);
void Login(in String name,I_GetLoginResult listener);
void SendMsg(in Msg msg);
}
// I_NewMessageArrived.aidl
package com.sp.chattingroom;
// Declare any non-default types here with import statements
import com.sp.chattingroom.Model.Msg;
interface I_NewMessageArrived {
void newMessageArrive(in Msg msg);
}
// I_GetLoginResult.aidl
package com.sp.chattingroom;
// Declare any non-default types here with import statements
interface I_GetLoginResult {
void loginSucceed();
void loginFailed();
}
注释上写了文件名字。
然后build之后就会生成了三个IInterface的类,就是进程通信的关键了,接下来就会利用他们的Binder来实现IPC。
然后是Service的改造
package com.sp.chattingroom.Service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import com.sp.chattingroom.IChatManager;
import com.sp.chattingroom.I_GetLoginResult;
import com.sp.chattingroom.I_NewMessageArrived;
import com.sp.chattingroom.Model.Msg;
import java.net.URISyntaxException;
/**
* Created by Administrator on 2017/2/20.
*/
public class ChatService extends Service {
private static final String TAG = "ChatService";
private Socket socket=null;
private I_GetLoginResult i_getLoginResult;
private I_NewMessageArrived i_newMessageArrived;
private Binder mBinder=new IChatManager.Stub(){
@Override
public void SendMsg(Msg msg) throws RemoteException {
socket.emit("postMsg",msg.getContent());
}
@Override
public void Login(String name, final I_GetLoginResult listener) throws RemoteException {
i_getLoginResult=listener;
socket.emit("login",name);
socket.on("nickExisted", new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.e(TAG, "nickExisted" );
try {
listener.loginFailed();
}catch (RemoteException e){
e.printStackTrace();
}
}
});
socket.on("loginSuccess",new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.e(TAG, "loginSuccess:"+Thread.currentThread().getId());
try {
listener.loginSucceed();
}catch (RemoteException e){
e.printStackTrace();
}
}
});
}
@Override
public void registerNewMsgListener(I_NewMessageArrived listener) throws RemoteException {
i_newMessageArrived=listener;
}
@Override
public void registerLoginResultListener(I_GetLoginResult listener) throws RemoteException {
i_getLoginResult=listener;
}
};
@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()){}
socket.on("newMsg", new Emitter.Listener() {
@Override
public void call(Object... args) {
String newcontent=args[1].toString();
try {
i_newMessageArrived.newMessageArrive(new Msg(0,newcontent));
}catch (RemoteException e){
e.printStackTrace();
}
}
});
}
@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 mBinder;
}
}
然后是LoginActivity
package com.sp.chattingroom;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.sp.chattingroom.base.LogUtil;
import com.sp.chattingroom.Service.ChatService;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by Administrator on 2017/2/21.
*/
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 IChatManager mRemoteManager;
private I_GetLoginResult mResultListener=new I_GetLoginResult.Stub(){
@Override
public void loginSucceed() throws RemoteException {
LogUtil.log(TAG,"log suc");
Intent intent=new Intent(LoginActivity.this,ChatActivity.class);
startActivity(intent);
finish();
}
@Override
public void loginFailed() throws RemoteException {
handler.obtainMessage().sendToTarget();
}
};
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtil.log(TAG,"onServiceConnected");
mRemoteManager=IChatManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteManager=null;
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();
try {
mRemoteManager.Login(name, mResultListener);
}
catch (RemoteException e){
e.printStackTrace();
}
}
});
}
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();
}
@Override
protected void onPause(){
LogUtil.log(TAG,"onPause");
super.onPause();
}
@Override
protected void onStop(){
LogUtil.log(TAG,"onStop");
super.onStop();
}
@Override
protected void onRestart(){
LogUtil.log(TAG,"onRestart");
super.onRestart();
}
@Override
protected void onDestroy(){
LogUtil.log(TAG,"onDestroy");
super.onDestroy();
unbindService(serviceConnection);
}
@Override
protected void onSaveInstanceState(Bundle outState){
LogUtil.log(TAG,"onSaveInstance");
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
LogUtil.log(TAG,"onRestoreInstance");
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onConfigurationChanged(Configuration configuration){
super.onConfigurationChanged(configuration);
LogUtil.log(TAG,"onConfigurationChanged:"+configuration.orientation);
}
}
ChatActivity
package com.sp.chattingroom;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.github.nkzawa.socketio.client.Socket;
import com.sp.chattingroom.Adapter.ChatRecyclerAdpter;
import com.sp.chattingroom.Adapter.DBHelper;
import com.sp.chattingroom.base.LogUtil;
import com.sp.chattingroom.Model.Msg;
import com.sp.chattingroom.Service.ChatService;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
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 IChatManager iChatManager;
private I_NewMessageArrived i_newMessageArrived=new I_NewMessageArrived.Stub(){
@Override
public void newMessageArrive(Msg msg) throws RemoteException {
Log.e(TAG, "newMsg: "+msg.getContent());
SendNotification(msg.getContent());
msg_list.add(msg);
dbHelper.insert(msg);
cursor.requery();
Message.obtain(handler).sendToTarget();
}
};
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtil.log("from util","onServiceConnected: "+name.toString());
iChatManager=IChatManager.Stub.asInterface(service);
try {
iChatManager.registerNewMsgListener(i_newMessageArrived);
}catch (RemoteException e){
e.printStackTrace();
}
}
@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(cursor.getInt(2),cursor.getString(1));
//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(1,msg_send);
try {
iChatManager.SendMsg(msg);
}catch (RemoteException e){
e.printStackTrace();
}
//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");
}
@Override
public void onBackPressed(){
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
}
下一阶段目标:
- 继续实现断线重连。。。
- 同时搭建推送平台
- 进程保活,互相拉活的学习。