看过包建强的《App研发录》之后对其中的基础Activity类封装感到惊讶,一直想找一种方式去解决关于app中使用socket长连接问题,如何实现简易的封装来达到主活动中涉及socket相关的代码量少的效果。正常的实现socket的基本方式都是新建一个Socket服务,在活动中绑定服务和注册全局广播,通过绑定服务中的函数去发送数据,通过全局广播去接收数据。如果每个活动中都去写绑定服务和注册全局广播等内容就会造成一大堆的代码冗余且主活动中的其他业务不是很突出,故我借用包建强书中的相关封装方式,新建了一个SocketBaseActivity类,去实现绑定服务与注册全局广播的内容,其中将全局广播接收器中的接收函数做抽象,在主活动继承SocketBaseActivity,在初始化时,实例化接收器中的接收函数,如果不实例化则不会进行绑定服务和注册广播操作,如果需要发送则掉父类中相关变量的发送函数即可。
下面是具体实现细节:
SocketService类
package com.splxtech.powermanagor.engine; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.splxtech.powermanagor.IBackService; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.net.Socket; import java.net.UnknownHostException; import java.util.Arrays; /** * Created by li300 on 2016/10/7 0007. */ public class SocketService extends Service { private static final String TAG = "BackService"; //心跳包频率 private static final long HEART_BEAT_RATE = 30 * 1000; public static final String HOST = "192.168.0.102";// // public static final int PORT = 51001; public static final String MESSAGE_ACTION="com.splxtech.powermanagor.engine.socket"; public static final String HEART_BEAT_ACTION="com.splxtech.powermanagor.engine.socket.heart"; public static final String HEART_BEAT_STRING="00";//心跳包内容 private ReadThread mReadThread; private LocalBroadcastManager mLocalBroadcastManager; private WeakReference<Socket> mSocket; // For heart Beat private Handler mHandler = new Handler(); private Runnable heartBeatRunnable = new Runnable() { @Override public void run() { if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) { boolean isSuccess = sendMsg(HEART_BEAT_STRING);//就发送一个HEART_BEAT_STRING过去 如果发送失败,就重新初始化一个socket if (!isSuccess) { mHandler.removeCallbacks(heartBeatRunnable); mReadThread.release(); releaseLastSocket(mSocket); new InitSocketThread().start(); } } mHandler.postDelayed(this, HEART_BEAT_RATE); } }; private long sendTime = 0L; private IBackService.Stub iBackService = new IBackService.Stub() { @Override public boolean sendMessage(String message) throws RemoteException { return sendMsg(message); } }; @Override public IBinder onBind(Intent arg0) { return iBackService; } @Override public void onCreate() { super.onCreate(); new InitSocketThread().start(); mLocalBroadcastManager=LocalBroadcastManager.getInstance(this); } public boolean sendMsg(String msg) { if (null == mSocket || null == mSocket.get()) { return false; } Socket soc = mSocket.get(); try { if (!soc.isClosed() && !soc.isOutputShutdown()) { OutputStream os = soc.getOutputStream(); String message = msg; os.write(message.getBytes()); os.flush(); sendTime = System.currentTimeMillis();//每次发送成数据,就改一下最后成功发送的时间,节省心跳间隔时间 } else { return false; } } catch (IOException e) { e.printStackTrace(); return false; } return true; } private void initSocket() {//初始化Socket try { Socket so = new Socket(HOST, PORT); mSocket = new WeakReference<Socket>(so); mReadThread = new ReadThread(so); mReadThread.start(); mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//初始化成功后,就准备发送心跳包 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void releaseLastSocket(WeakReference<Socket> mSocket) { try { if (null != mSocket) { Socket sk = mSocket.get(); if (!sk.isClosed()) { sk.close(); } sk = null; mSocket = null; } } catch (IOException e) { e.printStackTrace(); } } class InitSocketThread extends Thread { @Override public void run() { super.run(); initSocket(); } } // Thread to read content from Socket class ReadThread extends Thread { private WeakReference<Socket> mWeakSocket; private boolean isStart = true; public ReadThread(Socket socket) { mWeakSocket = new WeakReference<Socket>(socket); } public void release() { isStart = false; releaseLastSocket(mWeakSocket); } @Override public void run() { super.run(); Socket socket = mWeakSocket.get(); if (null != socket) { try { InputStream is = socket.getInputStream(); byte[] buffer = new byte[1024 * 4]; int length = 0; while (!socket.isClosed() && !socket.isInputShutdown() && isStart && ((length = is.read(buffer)) != -1)) { if (length > 0) { String message = new String(Arrays.copyOf(buffer, length)).trim(); Log.e(TAG, message); //收到服务器过来的消息,就通过Broadcast发送出去 if(message.equals(HEART_BEAT_STRING)){//处理心跳回复 Intent intent=new Intent(HEART_BEAT_ACTION); mLocalBroadcastManager.sendBroadcast(intent); }else{ //其他消息回复 Intent intent=new Intent(MESSAGE_ACTION); intent.putExtra("message", message); mLocalBroadcastManager.sendBroadcast(intent); } } } } catch (IOException e) { e.printStackTrace(); } } } } }
上面有一个IBackService.aidl文件:
// IBackService.aidl package com.splxtech.powermanagor; // Declare any non-default types here with import statements interface IBackService { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ boolean sendMessage(String message); }
下面是SocketBaseActivity类中的内容:
package com.splxtech.powermanagor.Base; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.IBinder; import android.support.v4.content.LocalBroadcastManager; import com.splxtech.powermanagor.IBackService; import com.splxtech.powermanagor.engine.SocketService; import com.splxtech.splxapplib.activity.BaseActivity; /** * Created by li300 on 2016/10/7 0007. */ public abstract class SocketBaseActivity extends BaseActivity { //子类中完成抽象函数赋值 //实体中通过实现该全局接收器方法来处理接收到消息 public MessageBackReciver mReciver; private IntentFilter mIntentFilter; private Intent mServiceIntent; private LocalBroadcastManager localBroadcastManager; //通过调用该接口中的方法来实现数据发送 public IBackService iBackService; //标记是否已经进行了服务绑定与全局消息注册 private boolean flag; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { iBackService = IBackService.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { iBackService = null; } }; @Override public void onStart() { flag = false; if(mReciver!=null) { flag = true; initSocket(); localBroadcastManager.registerReceiver(mReciver,mIntentFilter); bindService(mServiceIntent,conn,BIND_ABOVE_CLIENT); } super.onStart(); } @Override public void onDestroy() { if(flag==true) { unbindService(conn); localBroadcastManager.unregisterReceiver(mReciver); } super.onDestroy(); } public void initSocket() { localBroadcastManager = LocalBroadcastManager.getInstance(this); //mReciver = new MessageBackReciver(); mServiceIntent = new Intent(this,SocketService.class); mIntentFilter = new IntentFilter(); mIntentFilter.addAction(SocketService.HEART_BEAT_ACTION); mIntentFilter.addAction(SocketService.MESSAGE_ACTION); } public abstract class MessageBackReciver extends BroadcastReceiver { @Override public abstract void onReceive(Context context, Intent intent); } }
在主activity中如何使用:
package com.splxtech.powermanagor.activity.dqjc; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ListView; import com.splxtech.powermanagor.Base.SocketBaseActivity; import com.splxtech.powermanagor.R; import com.splxtech.powermanagor.adapter.DqItemAdapter; import com.splxtech.powermanagor.engine.SocketService; import com.splxtech.powermanagor.entity.Appliance; import com.splxtech.powermanagor.utils.Utils; import com.splxtech.splxapplib.activity.BaseActivity; import java.util.ArrayList; /** * Created by li300 on 2016/10/6 0006. */ public class DqActivity extends SocketBaseActivity { @Override protected void initVariables() { //给全局消息接收器赋值,并进行消息处理 mReciver = new MessageBackReciver(){ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(action.equals(SocketService.HEART_BEAT_ACTION)) { Utils.toastShow(baseActivity,"Get a heart heat"); } else { String message = intent.getStringExtra("message"); } } }; } @Override protected void initViews(Bundle savedInstanceState) { } @Override public void loadData() { } }