├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── mina-core-2.0.16.jar │ └── slf4j-android-1.6.1-RC1.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hhf │ │ └── activity │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hhf │ │ │ ├── activity │ │ │ └── MainActivity.java │ │ │ ├── client │ │ │ ├── ConnectUtils.java │ │ │ ├── FrameCodecFactory.java │ │ │ ├── FrameDecoder.java │ │ │ ├── FrameEncoder.java │ │ │ ├── HeartBeatHandler.java │ │ │ ├── HeartBeatListener.java │ │ │ └── HeartBeatMessageFactory.java │ │ │ ├── manager │ │ │ ├── ClientConnectManager.java │ │ │ └── SessionManager.java │ │ │ └── service │ │ │ ├── LongConnectService.java │ │ │ └── ServiceHandler.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hhf │ └── activity │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /build 5 | /*/build/ 6 | /captures 7 | .externalNativeBuild 8 | # User-specific configurations 9 | /.idea/ 10 | # OS-specific files 11 | .DS_Store 12 | .DS_Store? 13 | ._* 14 | .Spotlight-V100 15 | .Trashes 16 | ehthumbs.db 17 | Thumbs.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 本文档介绍: 2 |  1. 记录项目相关的资料,方便以后阅读查看 3 |  2. 本文档为markdown语法 4 | # MINA框架简介: 5 | 1. Mina是什么东西? 6 | Apache MINA 是一个网络应用框架,有助于用户非常方便地开发高性能、高伸缩性的网络应用。它通过Java NIO提供了一个抽象的、事件驱动的、异步的位于各种传输协议(如TCP/IP和UDP/IP)之上的API,Apache MINA 通常可被称之为: 7 | ``` 8 | NIO 框架库; 9 | 客户端/服务器框架库; 10 | 或者一个网络socket库。 11 | ``` 12 | 2. MINA框架的特点有: 13 | 基于java NIO类库开发;采用非阻塞方式的异步传输;事件驱动;支持批量数据传输;支持TCP、UDP协议;控制反转的设计模式(支持Spring);采用优雅的松耦合架构;可灵活的加载过滤器机制;单元测试更容易实现;可自定义线程的数量,以提高运行于多处理器上的性能;采用回调的方式完成调用,线程的使用更容易。 14 | 3. Mina的框架 15 | 当远程客户首次访问采用MINA编写的程序时,IoAcceptor作为线程运行,负责接受来自客户的请求。当有客户请求连接时,创建一个IoSession,该IoSession与IoProcessor、SocketChannel以及IOService联系起来。IoProcessor也作为另外一个线程运行,定时检查客户是否有数据到来,并对客户请求进行处理,依次调用在IOService注册的各个IoFilter,最后调用IoHandler进行最终的逻辑处理,再将处理后的结果Filter后返回给客户端。 16 | 4. Mina的现有应用 17 | MINA框架的应用比较广泛,应用的开源项目有Apache Directory、AsyncWeb、ApacheQpid、QuickFIX/J、Openfire、SubEthaSTMP、red5等。MINA框架当前稳定版本是1.1.6,最新的2.0版本目前已经发布了M1版本。 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.1" 6 | defaultConfig { 7 | applicationId "com.hhf.activity" 8 | minSdkVersion 15 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:26.0.0-alpha1' 28 | testCompile 'junit:junit:4.12' 29 | compile files('libs/mina-core-2.0.16.jar') 30 | compile files('libs/slf4j-android-1.6.1-RC1.jar') 31 | compile 'io.reactivex.rxjava2:rxjava:2.1.0' 32 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 33 | } 34 | -------------------------------------------------------------------------------- /app/libs/mina-core-2.0.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/libs/mina-core-2.0.16.jar -------------------------------------------------------------------------------- /app/libs/slf4j-android-1.6.1-RC1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/libs/slf4j-android-1.6.1-RC1.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/huanghongfa/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hhf/activity/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hhf.activity; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.hhf.activity", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hhf.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.util.Log; 8 | 9 | import com.hhf.client.ConnectUtils; 10 | import com.hhf.client.FrameCodecFactory; 11 | import com.hhf.client.HeartBeatHandler; 12 | import com.hhf.client.HeartBeatListener; 13 | import com.hhf.client.HeartBeatMessageFactory; 14 | import com.hhf.manager.ClientConnectManager; 15 | import com.hhf.manager.SessionManager; 16 | import com.hhf.service.LongConnectService; 17 | 18 | import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; 19 | import org.apache.mina.core.future.ConnectFuture; 20 | import org.apache.mina.core.session.IoSession; 21 | import org.apache.mina.filter.codec.ProtocolCodecFilter; 22 | import org.apache.mina.filter.keepalive.KeepAliveFilter; 23 | import org.apache.mina.transport.socket.nio.NioSocketConnector; 24 | 25 | import java.net.InetSocketAddress; 26 | 27 | import io.reactivex.Observable; 28 | import io.reactivex.ObservableEmitter; 29 | import io.reactivex.ObservableOnSubscribe; 30 | import io.reactivex.Observer; 31 | import io.reactivex.android.schedulers.AndroidSchedulers; 32 | import io.reactivex.annotations.NonNull; 33 | import io.reactivex.disposables.Disposable; 34 | import io.reactivex.schedulers.Schedulers; 35 | 36 | public class MainActivity extends AppCompatActivity implements LongConnectService.IStartConnectService { 37 | 38 | private final String TAG = "MainActivity"; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_main); 44 | ClientConnectManager.getInstance().init(this); 45 | LongConnectService.setIStartConnectService(this); 46 | Intent intent = new Intent(this, LongConnectService.class); 47 | startService(intent); 48 | } 49 | 50 | 51 | private void connect2(final Context context) { 52 | ObservableOnSubscribe subscribe = new ObservableOnSubscribe() { 53 | @Override 54 | public void subscribe(@NonNull ObservableEmitter e) throws Exception { 55 | NioSocketConnector mSocketConnector = new NioSocketConnector(); 56 | //设置协议封装解析处理 57 | mSocketConnector.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new FrameCodecFactory())); 58 | //设置心跳包 59 | KeepAliveFilter heartFilter = new KeepAliveFilter(new HeartBeatMessageFactory()); 60 | //每 5 分钟发送一个心跳包 61 | heartFilter.setRequestInterval(5 * 60); 62 | //心跳包超时时间 10s 63 | heartFilter.setRequestTimeout(10); 64 | // 获取过滤器链 65 | DefaultIoFilterChainBuilder filterChain = mSocketConnector.getFilterChain(); 66 | filterChain.addLast("encoder", new ProtocolCodecFilter(new FrameCodecFactory())); 67 | // 添加编码过滤器 处理乱码、编码问题 68 | filterChain.addLast("decoder", new ProtocolCodecFilter(new FrameCodecFactory())); 69 | mSocketConnector.getFilterChain().addLast("heartbeat", heartFilter); 70 | //设置 handler 处理业务逻辑 71 | mSocketConnector.setHandler(new HeartBeatHandler(context)); 72 | mSocketConnector.addListener(new HeartBeatListener(mSocketConnector)); 73 | //配置服务器地址 74 | InetSocketAddress mSocketAddress = new InetSocketAddress(ConnectUtils.HOST, ConnectUtils.PORT); 75 | //发起连接 76 | ConnectFuture mFuture = mSocketConnector.connect(mSocketAddress); 77 | mFuture.awaitUninterruptibly(); 78 | IoSession mSession = mFuture.getSession(); 79 | Log.d(TAG, "connect2======连接成功" + mSession.toString()); 80 | e.onNext(mSession); 81 | e.onComplete(); 82 | } 83 | }; 84 | Observable.create(subscribe).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { 85 | @Override 86 | public void onSubscribe(@NonNull Disposable d) { 87 | 88 | } 89 | 90 | @Override 91 | public void onNext(@NonNull Object o) { 92 | IoSession mSession = (IoSession) o; 93 | Log.d(TAG, "connect2======连接成功了吗====" + mSession.isConnected()); 94 | SessionManager.getInstance().setSeesion(mSession); 95 | SessionManager.getInstance().writeToServer("你看见了吗"); 96 | } 97 | 98 | @Override 99 | public void onError(@NonNull Throwable e) { 100 | Log.e(TAG, "Throwable e" + e); 101 | } 102 | 103 | @Override 104 | public void onComplete() { 105 | 106 | } 107 | }); 108 | 109 | } 110 | 111 | 112 | @Override 113 | public void startConnect() { 114 | ClientConnectManager.getInstance().connect(this); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/ConnectUtils.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * Created by huanghongfa on 2017/7/28. 8 | */ 9 | 10 | public class ConnectUtils { 11 | 12 | public static final int REPEAT_TIME = 5;//表示重连次数 13 | public static final String HOST = "127.0.0.1";//表示IP地址 14 | public static final int PORT = 3344;//表示端口号 15 | public static final int IDLE_TIME = 10;//客户端10s内没有向服务端发送数据 16 | public static final int TIMEOUT = 5;//设置连接超时时间,超过5s还没连接上便抛出异常 17 | 18 | /** 19 | * 获取当前时间 20 | * 21 | * @return 22 | */ 23 | public static String stringNowTime() { 24 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 25 | return format.format(new Date()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/FrameCodecFactory.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import org.apache.mina.core.session.IoSession; 4 | import org.apache.mina.filter.codec.ProtocolCodecFactory; 5 | import org.apache.mina.filter.codec.ProtocolDecoder; 6 | import org.apache.mina.filter.codec.ProtocolEncoder; 7 | 8 | /** 9 | * Created by huanghongfa on 2017/7/28. 10 | */ 11 | 12 | public class FrameCodecFactory implements ProtocolCodecFactory{ 13 | @Override 14 | public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception { 15 | return new FrameEncoder(); 16 | } 17 | 18 | @Override 19 | public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception { 20 | return new FrameDecoder(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/FrameDecoder.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import org.apache.mina.core.buffer.IoBuffer; 4 | import org.apache.mina.core.session.IoSession; 5 | import org.apache.mina.filter.codec.CumulativeProtocolDecoder; 6 | import org.apache.mina.filter.codec.ProtocolDecoderOutput; 7 | 8 | import java.nio.charset.Charset; 9 | 10 | /** 11 | * Created by huanghongfa on 2017/7/28. 12 | */ 13 | 14 | public class FrameDecoder extends CumulativeProtocolDecoder { 15 | 16 | 17 | private final static Charset charset = Charset.forName("UTF-8"); 18 | // 可变的IoBuffer数据缓冲区 19 | private IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true); 20 | 21 | @Override 22 | protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput protocolDecoderOutput) throws Exception { 23 | // 如果有消息 24 | while (ioBuffer.hasRemaining()) { 25 | // 判断消息是否是结束符,不同平台的结束符也不一样; 26 | // windows换行符(\r\n)就认为是一个完整消息的结束符了; UNIX 是\n;MAC 是\r 27 | byte b = ioBuffer.get(); 28 | if (b == '\n') { 29 | buff.flip(); 30 | byte[] bytes = new byte[buff.limit()]; 31 | buff.get(bytes); 32 | String message = new String(bytes, charset); 33 | buff = IoBuffer.allocate(100).setAutoExpand(true); 34 | // 如果结束了,就写入转码后的数据 35 | protocolDecoderOutput.write(message); 36 | } else { 37 | buff.put(b); 38 | } 39 | } 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/FrameEncoder.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import org.apache.mina.core.buffer.IoBuffer; 4 | import org.apache.mina.core.session.IoSession; 5 | import org.apache.mina.filter.codec.ProtocolEncoder; 6 | import org.apache.mina.filter.codec.ProtocolEncoderOutput; 7 | import org.apache.mina.filter.codec.textline.LineDelimiter; 8 | 9 | import java.nio.charset.Charset; 10 | 11 | /** 12 | * Created by huanghongfa on 2017/7/28. 13 | */ 14 | 15 | public class FrameEncoder implements ProtocolEncoder { 16 | private final static Charset charset = Charset.forName("UTF-8"); 17 | @Override 18 | public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput protocolEncoderOutput) throws Exception { 19 | IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true); 20 | buff.putString(message.toString(), charset.newEncoder()); 21 | // put 当前系统默认换行符 22 | buff.putString(LineDelimiter.DEFAULT.getValue(), charset.newEncoder()); 23 | // 为下一次读取数据做准备 24 | buff.flip(); 25 | 26 | protocolEncoderOutput.write(buff); 27 | // if (o instanceof String) { 28 | // String messageString = (String) o; 29 | // //封装为 Frame 协议 30 | // byte[] messageBytes = messageString.getBytes(Charset.forName("UTF-8")); 31 | // int totalSize = messageBytes.length + 4; 32 | // IoBuffer buffer = IoBuffer.allocate(totalSize); 33 | // buffer.putInt(totalSize); 34 | // buffer.put(messageBytes); 35 | // buffer.flip(); 36 | // protocolEncoderOutput.write(buffer); 37 | // } 38 | } 39 | 40 | @Override 41 | public void dispose(IoSession ioSession) throws Exception { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/HeartBeatHandler.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.support.v4.content.LocalBroadcastManager; 6 | import android.util.Log; 7 | 8 | import org.apache.mina.core.service.IoHandlerAdapter; 9 | import org.apache.mina.core.session.IdleStatus; 10 | import org.apache.mina.core.session.IoSession; 11 | 12 | /** 13 | * Created by huanghongfa on 2017/7/28. 14 | */ 15 | 16 | public class HeartBeatHandler extends IoHandlerAdapter { 17 | 18 | private final String TAG = "HeartBeatHandler"; 19 | 20 | public static final String BROADCAST_ACTION = "com.commonlibrary.mina.broadcast"; 21 | public static final String MESSAGE = "message"; 22 | private Context mContext; 23 | 24 | public HeartBeatHandler(Context context) { 25 | this.mContext = context; 26 | 27 | } 28 | 29 | @Override 30 | public void exceptionCaught(IoSession session, Throwable cause) 31 | throws Exception { 32 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用exceptionCaught"); 33 | } 34 | 35 | @Override 36 | public void messageReceived(IoSession session, Object message) 37 | throws Exception { 38 | Log.e(TAG, "接收到服务器端消息:" + message.toString()); 39 | if (mContext != null) { 40 | Intent intent = new Intent(BROADCAST_ACTION); 41 | intent.putExtra(MESSAGE, message.toString()); 42 | LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); 43 | } 44 | } 45 | 46 | @Override 47 | public void messageSent(IoSession session, Object message) throws Exception { 48 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用messageSent"); 49 | // session.close(true);//加上这句话实现短连接的效果,向客户端成功发送数据后断开连接 50 | } 51 | 52 | @Override 53 | public void sessionClosed(IoSession session) throws Exception { 54 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用sessionClosed"); 55 | 56 | } 57 | 58 | @Override 59 | public void sessionCreated(IoSession session) throws Exception { 60 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用sessionCreated"); 61 | 62 | } 63 | 64 | @Override 65 | public void sessionIdle(IoSession session, IdleStatus status) 66 | throws Exception { 67 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用sessionIdle"); 68 | } 69 | 70 | @Override 71 | public void sessionOpened(IoSession session) throws Exception { 72 | Log.d(TAG, ConnectUtils.stringNowTime() + " : 客户端调用sessionOpened"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/HeartBeatListener.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import android.util.Log; 4 | 5 | import com.hhf.manager.ClientConnectManager; 6 | 7 | import org.apache.mina.core.service.IoService; 8 | import org.apache.mina.core.service.IoServiceListener; 9 | import org.apache.mina.core.session.IdleStatus; 10 | import org.apache.mina.core.session.IoSession; 11 | import org.apache.mina.transport.socket.nio.NioSocketConnector; 12 | 13 | /** 14 | * Created by huanghongfa on 2017/7/28. 15 | * 监听服务器断线原因 16 | */ 17 | 18 | public class HeartBeatListener implements IoServiceListener { 19 | 20 | public NioSocketConnector connector; 21 | 22 | public HeartBeatListener(NioSocketConnector connector) { 23 | this.connector = connector; 24 | } 25 | 26 | @Override 27 | public void serviceActivated(IoService arg0) throws Exception { 28 | } 29 | 30 | @Override 31 | public void serviceDeactivated(IoService arg0) throws Exception { 32 | } 33 | 34 | @Override 35 | public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception { 36 | } 37 | 38 | @Override 39 | public void sessionClosed(IoSession arg0) throws Exception { 40 | Log.d("", "hahahaha"); 41 | } 42 | 43 | @Override 44 | public void sessionCreated(IoSession arg0) throws Exception { 45 | } 46 | 47 | @Override 48 | public void sessionDestroyed(IoSession arg0) { 49 | ClientConnectManager.getInstance().rePeatConnect(); 50 | } 51 | 52 | /* 53 | * 断线重连操作 54 | * @param content 55 | */ 56 | // public void repeatConnect(String content) { 57 | // // 执行到这里表示Session会话关闭了,需要进行重连,我们设置每隔3s重连一次,如果尝试重连5次都没成功的话,就认为服务器端出现问题,不再进行重连操作 58 | // int count = 0;// 记录尝试重连的次数 59 | // boolean isRepeat = false; 60 | // while (!isRepeat && count <= 10) { 61 | // try { 62 | // count++;// 重连次数加1 63 | // ConnectFuture future = connector.connect(new InetSocketAddress( 64 | // ConnectUtils.HOST, ConnectUtils.PORT)); 65 | // future.awaitUninterruptibly();// 一直阻塞住等待连接成功 66 | // IoSession session = future.getSession();// 获取Session对象 67 | // if (session.isConnected()) { 68 | // isRepeat = true; 69 | // // 表示重连成功 70 | // System.out.println(content + ConnectUtils.stringNowTime() + " : 断线重连" + count 71 | // + "次之后成功....."); 72 | // SessionManager.getInstance().setSeesion(session); 73 | // SessionManager.getInstance().writeToServer("重新连接的"); 74 | // break; 75 | // } 76 | // } catch (Exception e) { 77 | // if (count == ConnectUtils.REPEAT_TIME) { 78 | // System.out.println(content + ConnectUtils.stringNowTime() + " : 断线重连" 79 | // + ConnectUtils.REPEAT_TIME + "次之后仍然未成功,结束重连....."); 80 | // break; 81 | // } else { 82 | // System.out.println(content + ConnectUtils.stringNowTime() + " : 本次断线重连失败,3s后进行第" + (count + 1) + "次重连....."); 83 | // try { 84 | // Thread.sleep(3000); 85 | // System.out.println(content + ConnectUtils.stringNowTime() + " : 开始第" + (count + 1) + "次重连....."); 86 | // } catch (InterruptedException e1) { 87 | // e1.printStackTrace(); 88 | // } 89 | // } 90 | // } 91 | // } 92 | // } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/client/HeartBeatMessageFactory.java: -------------------------------------------------------------------------------- 1 | package com.hhf.client; 2 | 3 | import org.apache.mina.core.session.IoSession; 4 | import org.apache.mina.filter.keepalive.KeepAliveMessageFactory; 5 | 6 | /** 7 | * Created by huanghongfa on 2017/7/28. 8 | */ 9 | 10 | public class HeartBeatMessageFactory implements KeepAliveMessageFactory { 11 | 12 | @Override 13 | public boolean isRequest(IoSession ioSession, Object o) { 14 | //如果是客户端主动向服务器发起的心跳包, return true, 该框架会发送 getRequest() 方法返回的心跳包内容. 15 | return false; 16 | } 17 | 18 | @Override 19 | public boolean isResponse(IoSession ioSession, Object o) { 20 | //如果是服务器发送过来的心跳包, return true后会在 getResponse() 方法中处理心跳包. 21 | return false; 22 | } 23 | 24 | @Override 25 | public Object getRequest(IoSession ioSession) { 26 | //自定义向服务器发送的心跳包内容. 27 | return null; 28 | } 29 | 30 | @Override 31 | public Object getResponse(IoSession ioSession, Object o) { 32 | //自定义解析服务器发送过来的心跳包. 33 | return null; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/manager/ClientConnectManager.java: -------------------------------------------------------------------------------- 1 | package com.hhf.manager; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.hhf.client.ConnectUtils; 7 | import com.hhf.client.FrameCodecFactory; 8 | import com.hhf.client.HeartBeatHandler; 9 | import com.hhf.client.HeartBeatListener; 10 | import com.hhf.client.HeartBeatMessageFactory; 11 | 12 | import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; 13 | import org.apache.mina.core.future.ConnectFuture; 14 | import org.apache.mina.core.session.IoSession; 15 | import org.apache.mina.filter.codec.ProtocolCodecFilter; 16 | import org.apache.mina.filter.keepalive.KeepAliveFilter; 17 | import org.apache.mina.transport.socket.nio.NioSocketConnector; 18 | 19 | import java.net.InetSocketAddress; 20 | 21 | import io.reactivex.Observable; 22 | import io.reactivex.ObservableEmitter; 23 | import io.reactivex.ObservableOnSubscribe; 24 | import io.reactivex.Observer; 25 | import io.reactivex.android.schedulers.AndroidSchedulers; 26 | import io.reactivex.annotations.NonNull; 27 | import io.reactivex.disposables.Disposable; 28 | import io.reactivex.schedulers.Schedulers; 29 | 30 | /** 31 | * Created by huanghongfa on 2017/7/29. 32 | */ 33 | 34 | public class ClientConnectManager { 35 | 36 | private static ClientConnectManager instance; 37 | 38 | 39 | public static ClientConnectManager getInstance() { 40 | if (null == instance) { 41 | instance = new ClientConnectManager(); 42 | } 43 | return instance; 44 | } 45 | 46 | private ClientConnectManager() { 47 | 48 | } 49 | 50 | private Context context; 51 | 52 | public void init(Context context) { 53 | this.context = context; 54 | } 55 | 56 | public void connect(final Context context) { 57 | Observable.create(new ObservableOnSubscribe() { 58 | @Override 59 | public void subscribe(@NonNull ObservableEmitter e) throws Exception { 60 | NioSocketConnector mSocketConnector = new NioSocketConnector(); 61 | //设置协议封装解析处理 62 | mSocketConnector.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new FrameCodecFactory())); 63 | //设置心跳包 64 | KeepAliveFilter heartFilter = new KeepAliveFilter(new HeartBeatMessageFactory()); 65 | //每 5 分钟发送一个心跳包 66 | heartFilter.setRequestInterval(5 * 60); 67 | //心跳包超时时间 10s 68 | heartFilter.setRequestTimeout(10); 69 | // 获取过滤器链 70 | DefaultIoFilterChainBuilder filterChain = mSocketConnector.getFilterChain(); 71 | filterChain.addLast("encoder", new ProtocolCodecFilter(new FrameCodecFactory())); 72 | // 添加编码过滤器 处理乱码、编码问题 73 | filterChain.addLast("decoder", new ProtocolCodecFilter(new FrameCodecFactory())); 74 | mSocketConnector.getFilterChain().addLast("heartbeat", heartFilter); 75 | //设置 handler 处理业务逻辑 76 | mSocketConnector.setHandler(new HeartBeatHandler(context)); 77 | mSocketConnector.addListener(new HeartBeatListener(mSocketConnector)); 78 | //配置服务器地址 79 | InetSocketAddress mSocketAddress = new InetSocketAddress(ConnectUtils.HOST, ConnectUtils.PORT); 80 | //发起连接 81 | ConnectFuture mFuture = mSocketConnector.connect(mSocketAddress); 82 | mFuture.awaitUninterruptibly(); 83 | IoSession mSession = mFuture.getSession(); 84 | Log.d("", "======连接成功" + mSession.toString()); 85 | e.onNext(mSession); 86 | e.onComplete(); 87 | } 88 | }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { 89 | @Override 90 | public void onSubscribe(@NonNull Disposable d) { 91 | 92 | } 93 | 94 | @Override 95 | public void onNext(@NonNull Object o) { 96 | IoSession mSession = (IoSession) o; 97 | Log.d("MainActivity", "======连接成功了吗====" + mSession.isConnected()); 98 | SessionManager.getInstance().setSeesion(mSession); 99 | SessionManager.getInstance().writeToServer("你看见了吗\n"); 100 | } 101 | 102 | @Override 103 | public void onError(@NonNull Throwable e) { 104 | 105 | } 106 | 107 | @Override 108 | public void onComplete() { 109 | 110 | } 111 | }); 112 | } 113 | 114 | 115 | public void rePeatConnect() { 116 | final boolean[] isRepeat = {false}; 117 | Observable.create(new ObservableOnSubscribe() { 118 | @Override 119 | public void subscribe(@NonNull ObservableEmitter e) throws Exception { 120 | // 执行到这里表示Session会话关闭了,需要进行重连,我们设置每隔3s重连一次,如果尝试重连5次都没成功的话,就认为服务器端出现问题,不再进行重连操作 121 | int count = 0;// 记录尝试重连的次数 122 | NioSocketConnector mSocketConnector = null; 123 | while (!isRepeat[0] && count < 10) { 124 | try { 125 | count++; 126 | if (mSocketConnector == null) { 127 | mSocketConnector = new NioSocketConnector(); 128 | } 129 | //设置协议封装解析处理 130 | mSocketConnector.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new FrameCodecFactory())); 131 | //设置心跳包 132 | KeepAliveFilter heartFilter = new KeepAliveFilter(new HeartBeatMessageFactory()); 133 | //每 5 分钟发送一个心跳包 134 | heartFilter.setRequestInterval(5 * 60); 135 | //心跳包超时时间 10s 136 | heartFilter.setRequestTimeout(10); 137 | // 获取过滤器链 138 | DefaultIoFilterChainBuilder filterChain = mSocketConnector.getFilterChain(); 139 | filterChain.addLast("encoder", new ProtocolCodecFilter(new FrameCodecFactory())); 140 | // 添加编码过滤器 处理乱码、编码问题 141 | filterChain.addLast("decoder", new ProtocolCodecFilter(new FrameCodecFactory())); 142 | mSocketConnector.getFilterChain().addLast("heartbeat", heartFilter); 143 | //设置 handler 处理业务逻辑 144 | mSocketConnector.setHandler(new HeartBeatHandler(context)); 145 | mSocketConnector.addListener(new HeartBeatListener(mSocketConnector)); 146 | //配置服务器地址 147 | InetSocketAddress mSocketAddress = new InetSocketAddress(ConnectUtils.HOST, ConnectUtils.PORT); 148 | //发起连接 149 | ConnectFuture mFuture = mSocketConnector.connect(mSocketAddress); 150 | mFuture.awaitUninterruptibly(); 151 | IoSession mSession = mFuture.getSession(); 152 | if (mSession.isConnected()) { 153 | isRepeat[0] = true; 154 | Log.d("", "======连接成功" + mSession.toString()); 155 | e.onNext(mSession); 156 | e.onComplete(); 157 | break; 158 | } 159 | } catch (Exception e1) { 160 | if (count == ConnectUtils.REPEAT_TIME) { 161 | System.out.println(ConnectUtils.stringNowTime() + " : 断线重连" 162 | + ConnectUtils.REPEAT_TIME + "次之后仍然未成功,结束重连....."); 163 | break; 164 | } else { 165 | System.out.println(ConnectUtils.stringNowTime() + " : 本次断线重连失败,3s后进行第" + (count + 1) + "次重连....."); 166 | try { 167 | Thread.sleep(3000); 168 | System.out.println(ConnectUtils.stringNowTime() + " : 开始第" + (count + 1) + "次重连....."); 169 | } catch (InterruptedException e12) { 170 | Log.e("rePeatConnect ", "rePeatConnect e12" + e12); 171 | } 172 | } 173 | } 174 | 175 | } 176 | 177 | } 178 | }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { 179 | @Override 180 | public void onSubscribe(@NonNull Disposable d) { 181 | 182 | } 183 | 184 | @Override 185 | public void onNext(@NonNull Object o) { 186 | IoSession mSession = (IoSession) o; 187 | Log.d("MainActivity", "======连接成功了吗====" + mSession.isConnected()); 188 | SessionManager.getInstance().setSeesion(mSession); 189 | SessionManager.getInstance().writeToServer("重新连接发起的请求"); 190 | } 191 | 192 | @Override 193 | public void onError(@NonNull Throwable e) { 194 | 195 | } 196 | 197 | @Override 198 | public void onComplete() { 199 | 200 | } 201 | }); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/manager/SessionManager.java: -------------------------------------------------------------------------------- 1 | package com.hhf.manager; 2 | 3 | import android.util.Log; 4 | 5 | import org.apache.mina.core.session.IoSession; 6 | 7 | /** 8 | * Created by huanghongfa on 2017/7/28. 9 | */ 10 | 11 | public class SessionManager { 12 | 13 | private static SessionManager instance; 14 | 15 | private IoSession mSession; 16 | 17 | private volatile static Object bytes = new Object(); 18 | 19 | public static SessionManager getInstance() { 20 | if (null == instance) { 21 | instance = new SessionManager(); 22 | } 23 | return instance; 24 | } 25 | 26 | private SessionManager() { 27 | } 28 | 29 | public void setSeesion(IoSession session) { 30 | this.mSession = session; 31 | } 32 | 33 | public void writeToServer(Object msg) { 34 | if (mSession != null) { 35 | Log.e("tag", "客户端准备发送消息"); 36 | mSession.write(msg); 37 | } 38 | } 39 | 40 | public void closeSession() { 41 | if (mSession != null) { 42 | mSession.closeOnFlush(); 43 | } 44 | } 45 | 46 | public void removeSession() { 47 | this.mSession = null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/service/LongConnectService.java: -------------------------------------------------------------------------------- 1 | package com.hhf.service; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.support.annotation.Nullable; 7 | import android.util.Log; 8 | 9 | import com.hhf.client.FrameCodecFactory; 10 | 11 | import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; 12 | import org.apache.mina.core.service.IoAcceptor; 13 | import org.apache.mina.core.session.IdleStatus; 14 | import org.apache.mina.filter.codec.ProtocolCodecFilter; 15 | import org.apache.mina.transport.socket.nio.NioSocketAcceptor; 16 | 17 | import java.net.InetSocketAddress; 18 | 19 | /** 20 | * Created by huanghongfa on 2017/7/28. 21 | * 后台长连接服务 22 | */ 23 | 24 | public class LongConnectService extends Service { 25 | 26 | private final String TAG = "LongConnectService"; 27 | 28 | // 端口号,要求客户端与服务器端一致 29 | private static int PORT = 3344; 30 | static IStartConnectService mIStartConnectService; 31 | 32 | @Nullable 33 | @Override 34 | public IBinder onBind(Intent intent) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public int onStartCommand(Intent intent, int flags, int startId) { 40 | startService(); 41 | return super.onStartCommand(intent, flags, startId); 42 | } 43 | 44 | /** 45 | * 启动服务 46 | */ 47 | private void startService() { 48 | IoAcceptor acceptor; 49 | try { 50 | // 创建一个非阻塞的server端的Socket 51 | acceptor = new NioSocketAcceptor(); 52 | // 设置过滤器(使用mina提供的文本换行符编解码器) 53 | DefaultIoFilterChainBuilder filterChain = acceptor.getFilterChain(); 54 | // acceptor.getFilterChain().addLast("decoder", 55 | // new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"), 56 | // LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); 57 | // 为接收器设置管理服务 58 | acceptor.setHandler(new ServiceHandler()); 59 | acceptor.getFilterChain().addLast("encoder", new ProtocolCodecFilter(new FrameCodecFactory())); 60 | // 自定义的编解码器 61 | acceptor.getFilterChain().addLast("decoder", new ProtocolCodecFilter(new FrameCodecFactory())); 62 | // 设置读取数据的换从区大小 63 | acceptor.getSessionConfig().setReadBufferSize(2048); 64 | // 读写通道10秒内无操作进入空闲状态 65 | acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); 66 | // 绑定端口 67 | acceptor.bind(new InetSocketAddress(PORT)); 68 | Log.d(TAG, "服务器启动成功... 端口号未:" + PORT); 69 | mIStartConnectService.startConnect(); 70 | } catch (Exception e) { 71 | Log.d(TAG, "服务器启动异常..." + e); 72 | } 73 | } 74 | 75 | public static void setIStartConnectService(IStartConnectService list){ 76 | mIStartConnectService = list; 77 | } 78 | 79 | 80 | public interface IStartConnectService{ 81 | void startConnect(); 82 | } 83 | 84 | /** 85 | * 初始化客户端MINA 86 | * 87 | * @param host 88 | * @param port 89 | */ 90 | // public void initClientMina(String host, int port) { 91 | // NioSocketConnector connector = null; 92 | // try { 93 | // connector = new NioSocketConnector(); 94 | // HeartBeatHandler handler = new HeartBeatHandler();//创建handler对象,用于业务逻辑处理 95 | // connector.setHandler(handler); 96 | // connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));//添加Filter对象 97 | // } catch (Exception e2) { 98 | // e2.printStackTrace(); 99 | // System.out.println(e2.toString()); 100 | // } 101 | // connector.setConnectTimeout(ConnectUtils.TIMEOUT);//设置连接超时时间 102 | // int count = 0;//记录连接次数 103 | // while (true) { 104 | // try { 105 | // count++; 106 | // //执行到这里表示客户端刚刚启动需要连接服务器,第一次连接服务器的话是没有尝试次数限制的,但是随后的断线重连就有次数限制了 107 | // ConnectFuture future = connector.connect(new InetSocketAddress(ConnectUtils.HOST, ConnectUtils.PORT)); 108 | // future.awaitUninterruptibly();//一直阻塞,直到连接建立 109 | // IoSession session = future.getSession();//获取Session对象 110 | // if (session.isConnected()) { 111 | // //表示连接成功 112 | // System.out.println(ConnectUtils.stringNowTime() + " : 客户端连接服务器成功....."); 113 | // break; 114 | // } 115 | // } catch (RuntimeIoException e) { 116 | // System.out.println(ConnectUtils.stringNowTime() + " : 第" + count + "次客户端连接服务器失败,因为" + ConnectUtils.TIMEOUT + "s没有连接成功"); 117 | // try { 118 | // Thread.sleep(2000);//如果本次连接服务器失败,则间隔2s后进行重连操作 119 | // System.out.println(ConnectUtils.stringNowTime() + " : 开始第" + (count + 1) + "次连接服务器"); 120 | // } catch (InterruptedException e1) { 121 | // e1.printStackTrace(); 122 | // } 123 | // } 124 | // } 125 | // //为MINA客户端添加监听器,当Session会话关闭的时候,进行自动重连 126 | // connector.addListener(new HeartBeatListener(connector)); 127 | // } 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/com/hhf/service/ServiceHandler.java: -------------------------------------------------------------------------------- 1 | package com.hhf.service; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import com.hhf.manager.SessionManager; 7 | 8 | import org.apache.mina.core.service.IoHandlerAdapter; 9 | import org.apache.mina.core.session.IdleStatus; 10 | import org.apache.mina.core.session.IoSession; 11 | 12 | import java.util.Date; 13 | 14 | /** 15 | * Created by huanghongfa on 2017/7/28. 16 | */ 17 | 18 | public class ServiceHandler extends IoHandlerAdapter { 19 | 20 | private final String TAG = "ServiceHandler"; 21 | 22 | // 从端口接受消息,会响应此方法来对消息进行处理 23 | @Override 24 | public void messageReceived(IoSession session, Object message) throws Exception { 25 | super.messageReceived(session, message); 26 | Log.d(TAG, "服务器接受消息成功..."); 27 | String msg = message.toString(); 28 | if ("exit".equals(msg)) { 29 | // 如果客户端发来exit,则关闭该连接 30 | session.close(true); 31 | } 32 | // 向客户端发送消息 33 | Date date = new Date(); 34 | session.write(date); 35 | Log.d(TAG, "服务器接受消息成功..." + msg); 36 | } 37 | 38 | // 向客服端发送消息后会调用此方法 39 | @Override 40 | public void messageSent(IoSession session, Object message) throws Exception { 41 | super.messageSent(session, message); 42 | // session.close(true);//加上这句话实现短连接的效果,向客户端成功发送数据后断开连接 43 | Log.d(TAG, "服务器发送消息成功..."); 44 | } 45 | 46 | // 关闭与客户端的连接时会调用此方法 47 | @Override 48 | public void sessionClosed(IoSession session) throws Exception { 49 | super.sessionClosed(session); 50 | Log.d(TAG, "服务器与客户端断开连接..."); 51 | } 52 | 53 | // 服务器与客户端创建连接 54 | @Override 55 | public void sessionCreated(IoSession session) throws Exception { 56 | super.sessionCreated(session); 57 | Log.d(TAG, "服务器与客户端创建连接..."); 58 | } 59 | 60 | // 服务器与客户端连接打开 61 | @Override 62 | public void sessionOpened(IoSession session) throws Exception { 63 | Log.d(TAG, "服务器与客户端连接打开..."); 64 | super.sessionOpened(session); 65 | } 66 | 67 | @Override 68 | public void sessionIdle(IoSession session, IdleStatus status) throws Exception { 69 | super.sessionIdle(session, status); 70 | Log.d(TAG, "服务器进入空闲状态..."); 71 | SessionManager.getInstance().writeToServer("你看见了吗"); 72 | } 73 | 74 | @Override 75 | public void exceptionCaught(IoSession session, Throwable cause) throws Exception { 76 | super.exceptionCaught(session, cause); 77 | Log.d(TAG, "服务器发送异常..."); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MinaClient 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/hhf/activity/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.hhf.activity; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinStrange/MinaClient/9d8676bfbdc47ecfb538306219679946616b05ae/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------