详解android高仿微信后台接受消息,发送通知,监测网络连接

公司让我研究websocket,利用这个时间写了个仿微信的demo。

服务器端功能比较简单:转发给所有的client (类似与群聊)

android端功能:service后台接受消息;broadcastreceived监测网络连接断:断网时给出提示,重连时重新执行service中的websocket方法;接受到消息时如果activity没有在最前端发送notification通知用户。

要点:service将消息传递给activity使用的是activity中的全局handler

activity通service是否在屏幕最前时使用的binder对象,activity的onResum方法绑定service,onpause方法解绑service,来通知service是否需要保存消息并且发送notification。每次绑定后都查询一下service是否存着消息没有显示出来。

service中动态注册一个广播接收器来监听网络连接的状态


先看服务器端代码:

<pre name="code" class="java">/**
 * @author mace
 * @email [email protected]
 */

 
 
public class ChatServer extends WebSocketServer {

	public ChatServer(int port) throws UnknownHostException {
		super(new InetSocketAddress(port));
	}

	public ChatServer(InetSocketAddress address) {
		super(address);
	}

	@Override
	public void onOpen(WebSocket conn, ClientHandshake handshake) {

		sendToAll(conn.getRemoteSocketAddress().getAddress().getHostAddress()
				+ " 进入房间 !");

		System.out.println(conn.getRemoteSocketAddress().getAddress()
				.getHostAddress()
				+ " 进入房间 !");
	}

	@Override
	public void onClose(WebSocket conn, int code, String reason, boolean remote) {

		sendToAll(conn.getRemoteSocketAddress().getAddress().getHostAddress()
				+ " 离开房间 !");

		System.out.println(conn.getRemoteSocketAddress().getAddress()
				.getHostAddress()
				+ " 离开房间 !");
	}

	@Override
	public void onMessage(WebSocket conn, String message) {

		sendToAll("["
				+ conn.getRemoteSocketAddress().getAddress().getHostAddress()
				+ "]" + message);

		System.out.println("["
				+ conn.getRemoteSocketAddress().getAddress().getHostAddress()
				+ "]" + message);
	}

	@Override
	public void onError(WebSocket conn, Exception e) {
		e.printStackTrace();
		if (conn != null) {
			System.out.println( String.valueOf(e));
			conn.close();
			
		}
	}

	// 发送给所有的聊天者
	private void sendToAll(String text) {
		Collection<WebSocket> conns = connections();
		synchronized (conns) {
			for (WebSocket client : conns) {
				client.send(text);
			}
		}
	}

	// 测试
	public static void main(String[] args) throws InterruptedException,
			IOException {

		int port = 8887;

		ChatServer server = new ChatServer(port);
		server.start();

		System.out.println("房间已开启,等待客户端接入,端口号: " + server.getPort());

		BufferedReader webSocketIn = new BufferedReader(new InputStreamReader(
				System.in));

		while (true) {
			String stringIn = webSocketIn.readLine();
			server.sendToAll(stringIn);
		}
	}
}



下面详解android端的代码



service:service在Application中start,从程序一启动就开始运行

</pre><pre code_snippet_id="1727119" snippet_file_name="blog_20160623_4_78510" name="code" class="java"><pre name="code" class="java">/**
 * @author mace
 * @email [email protected]
 */

public class MyService extends Service {
    public static WebSocketClient client;
    private ConnectivityManager connectivityManager;
    private NetworkInfo info;
    private ArrayList<String> msgQueen = new ArrayList<String>();
    private boolean isSaveInSrevice = true;
    private MyBinder mBinder = new MyBinder();
    private boolean iscon = true;//用于在broadcast中判断是否是需要重新连接的
    private NotificationManager manager;
    private NotificationCompat.Builder notifyBuilder;
    private Vibrator vibrator;


    @Override
    public void onCreate() {
        super.onCreate();
        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(mReceiver, mFilter);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String address = "ws://192.168.31.184:8887";
        try {
            WebSocketImpl.DEBUG = true;
            System.setProperty("java.net.preferIPv6Addresses", "false");
            System.setProperty("java.net.preferIPv4Stack", "true");
            client = new WebSocketClient(new URI(address), new Draft_17()) {
                @Override
                public void onOpen(final ServerHandshake serverHandshakeData) {
                    iscon = true;
                    if (!isSaveInSrevice) {
                        Message msg = new Message();
                        msg.what = ChatClientActivity.ONOPEN;
                        ChatClientActivity.uihandler.sendMessage(msg);
                    } else {
                        Log.e("Mace", "没有打开activity是的Onopen+1");
                    }
                }

                @Override
                public void onMessage(final String message) {
                    //接受到消息就发送震动
                    openVibrator();
                    //判断activity是否是在最前端,如果是就将消息直接传给activity,如果不是就保存在service中
                    //并且发送
                    if (!isSaveInSrevice) {
                        Message msg = new Message();
                        msg.what = ChatClientActivity.ONMESSEGE;
                        Bundle b = new Bundle();
                        b.putString("msg", message);
                        msg.setData(b);
                        ChatClientActivity.uihandler.sendMessage(msg);
                    } else {
                        msgQueen.add(message);
                        //发送通知
                        sendNotification();
                        Log.e("Mace", message);
                    }
                }

                @Override
                public void onClose(final int code, final String reason, final boolean remote) {
                    iscon = false;
                    if (!isSaveInSrevice) {
                        Message msg = new Message();
                        msg.what = ChatClientActivity.ONCLOSE;
                        ChatClientActivity.uihandler.sendMessage(msg);
                    } else {
                        Log.e("Mace", "没有activity时的" + " onClose");
                    }

                }

                @Override
                public void onError(final Exception e) {
                    iscon = false;
                    client.close();
                    Log.e("Mace", String.valueOf(e));
                }
            };
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        client.connect();
        return super.onStartCommand(intent, flags, startId);

    }

    private void openVibrator() {
        vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        long[] pattern = {100, 400, 100, 400};   // 停止 开启 停止 开启
        vibrator.vibrate(pattern, -1);           //重复两次上面的pattern 如果只想震动一次,index设为-1
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mBinder.setIsSaveInSer();
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("Mace", "+++++++++++Srevice Destroy++++++++++");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /**
     * 接受到网络重连的广播时重新执行startcommoned方法让client重连
     */
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                connectivityManager = (ConnectivityManager)
                        getSystemService(Context.CONNECTIVITY_SERVICE);
                info = connectivityManager.getActiveNetworkInfo();
                if (info != null && info.isAvailable() && iscon == false) {
                    //断网的时候client会被close (调用了onclose方法)
                    Intent serviceIntent = new Intent(context, MyService.class);
                    context.startService(serviceIntent);
//                    client.connect(); //不能用client直接connect,具体原因期待有人指出,可能是classnotfind?
                    Log.e("Mace", "StartService");
                }
            }
        }
    };

    /**
     * 实现1.act到屏幕最前方时将保存在service中的msg向UI发送
     * 2.act不再屏幕最前方时通知service保存消息
     */
    class MyBinder extends Binder {

        public void sendToUI() {
            Message msg = new Message();
            msg.what = ChatClientActivity.ONMESSEGELIST;
            Bundle b = new Bundle();
            ArrayList l = new ArrayList();
            l.addAll(msgQueen);
            b.putParcelableArrayList("list", l);
            msg.setData(b);
            ChatClientActivity.uihandler.sendMessage(msg);
            isSaveInSrevice = false;
            msgQueen.clear();
            manager.cancel(121);
        }

        public void setIsSaveInSer() {
            isSaveInSrevice = true;
        }
    }

    //发送notification
    private void sendNotification() {
        //点击的意图ACTION是跳转到Intent
        Log.e("Mace", "sendNotification");
        Intent resultIntent = new Intent(this, ChatClientActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.a);
        notifyBuilder = new NotificationCompat.Builder(this)
            /*设置large icon*/
                .setLargeIcon(bitmap)
             /*设置small icon*/
                .setSmallIcon(R.mipmap.a)
            /*设置title*/
                .setContentTitle("您收到了" + String.valueOf(msgQueen.size()) + "条消息")
            /*设置详细文本*/
                .setContentText(msgQueen.get(msgQueen.size() - 1))
             /*设置发出通知的时间为发出通知时的系统时间*/
                .setWhen(System.currentTimeMillis())
             /*设置发出通知时在status bar进行提醒*/
                .setTicker("收到新消息")
            /*setOngoing(boolean)设为true,notification将无法通过左右滑动的方式清除 * 可用于添加常驻通知,必须调用cancle方法来清除 */
                .setOngoing(false)
             /*设置点击后通知消失*/
                .setAutoCancel(true)
             /*设置通知数量的显示类似于QQ那种,用于同志的合并*/
//                .setNumber(3)
             /*点击跳转到MainActivity*/
                .setContentIntent(pendingIntent);

        manager.notify(121, notifyBuilder.build());
    }

}

 activity中代码: 
 

/**
 * @author mace
 * @email [email protected]
 */
public class ChatClientActivity extends AppCompatActivity implements OnClickListener {
    public static int ONMESSEGE = 1;
    public static int ONMESSEGELIST = 2;
    public static int ONCLOSE = -1;
    public static int ONOPEN = 0;
    private ScrollView svChat;
    private EditText etDetails;
    private EditText etName;
    private EditText etMessage;
    private Button btnSend;
    private TextView closetv;
    private long exitTime = 0;
    private MyService.MyBinder myBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.sendToUI();
            Log.e("Mace", "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

    };

    public static MyHandler uihandler;

    //activity回到最前端时连接service进行通信
    @Override
    protected void onResume() {
        super.onResume();
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, connection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        uihandler = new MyHandler();
        setContentView(R.layout.activity_chat_client);
        svChat = (ScrollView) findViewById(R.id.svChat);
        etDetails = (EditText) findViewById(R.id.etDetails);
        etName = (EditText) findViewById(R.id.etName);
        etMessage = (EditText) findViewById(R.id.etMessage);
        btnSend = (Button) findViewById(R.id.btnSend);
        btnSend.setOnClickListener(this);
        closetv = (TextView) findViewById(R.id.onClose);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnSend:
                try {
                    if (MyService.client != null) {
                        Log.e("Mace", "btnSend" + String.valueOf(Thread.currentThread().getId()));
                        MyService.client.send(etName.getText().toString().trim() + "说:" + etMessage.getText().toString().trim());
                        svChat.post(new Runnable() {
                            @Override
                            public void run() {
                                svChat.fullScroll(View.FOCUS_DOWN);
                                etMessage.setText("");
                                etMessage.requestFocus();
                            }
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        unbindService(connection);
    }

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            exit();
            return false;
        }
        return super.onKeyDown(keyCode, event);
    }

    public void exit() {
        if ((System.currentTimeMillis() - exitTime) > 2000) {
            Toast.makeText(getApplicationContext(), "再按一次退出程序",
                    Toast.LENGTH_SHORT).show();
            exitTime = System.currentTimeMillis();
        } else {
            finish();
        }
    }

    //接受service传来的内容,并相应的修改UI
    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == ONMESSEGE) {
                addChatMessage(String.valueOf(msg.getData().get("msg")));
            } else if (msg.what == ONCLOSE) {
                setConClose();
            } else if (msg.what == ONOPEN) {
                setConOpen();
            } else if (msg.what == ONMESSEGELIST) {
                ArrayList msgList = msg.getData().getParcelableArrayList("list");
                String s = "";
                for (int i = 0; i < msgList.size(); i++) {
                    s = (String) msgList.get(i);
                    addChatMessage(s);
                }
            }
        }
    }

    //添加message进入对话框
    public void addChatMessage(String msg) {
        etDetails.append(msg + "\n");
    }

    //连接关闭时的界面
    public void setConClose() {
        btnSend.setEnabled(false);
        closetv.setVisibility(View.VISIBLE);
    }

    //连接可用时的界面
    public void setConOpen() {
        btnSend.setEnabled(true);
        closetv.setVisibility(View.GONE);
    }
}

manifest文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.wlf.websocket">

    <!-- 网络权限 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ChatClientActivity"
            android:windowSoftInputMode="stateHidden|adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:persistent="true"></service>
    </application>

</manifest>
欢迎各路大神指出不足,大家共同提高\(^o^)/

demo实例:

http://download.csdn.net/detail/mace_android/9558242

 
 

猜你喜欢

转载自blog.csdn.net/mace_android/article/details/51744884