From 593d3fc0d8225fca25fed4bc6603084214dfaae1 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 5 Apr 2018 17:35:34 -0500 Subject: [PATCH] - Moved the NetworkService management logic to the newly introduced NetworkServiceManager class --- graphenej/build.gradle | 2 +- .../graphenej/api/android/NetworkService.java | 11 +- .../api/android/NetworkServiceManager.java | 117 ++++++++++++++++++ .../graphenej/api/bitshares/Nodes.java | 1 - .../agorise/graphenej/api/calls/GetBlock.java | 3 + .../graphenej/api/calls/GetRequiredFees.java | 3 + .../luminiasoft/labs/sample/MainActivity.java | 4 +- .../labs/sample/SampleApplication.java | 73 +---------- .../labs/sample/SecondActivity.java | 20 ++- 9 files changed, 151 insertions(+), 83 deletions(-) create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 7854e9b..8f3e666 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -10,7 +10,7 @@ android { buildToolsVersion "25.0.0" defaultConfig { - minSdkVersion 9 + minSdkVersion 14 targetSdkVersion 24 versionCode 12 versionName "0.4.7-alpha3" diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index e2b84c5..8e47e9c 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -99,8 +99,12 @@ public class NetworkService extends Service { return mCurrentId; } - public int sendMessage(ApiCallable apiCallable){ - ApiCall call = apiCallable.toApiCall(mApiIds.get(ApiAccess.API_DATABASE), mCurrentId); + public int sendMessage(ApiCallable apiCallable, int requiredApi){ + int apiId = 0; + if(requiredApi != -1){ + apiId = mApiIds.get(requiredApi); + } + ApiCall call = apiCallable.toApiCall(apiId, mCurrentId); if(mWebSocket.send(call.toJsonString())){ Log.v(TAG,"> "+call.toJsonString()); }else{ @@ -112,7 +116,8 @@ public class NetworkService extends Service { @Override public void onDestroy() { Log.d(TAG,"onDestroy"); - mWebSocket.close(NORMAL_CLOSURE_STATUS, null); + if(mWebSocket != null) + mWebSocket.close(NORMAL_CLOSURE_STATUS, null); } @Override diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java new file mode 100644 index 0000000..2f31c0d --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java @@ -0,0 +1,117 @@ +package cy.agorise.graphenej.api.android; + +import android.app.Activity; +import android.app.Application; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; + +import java.lang.ref.WeakReference; + +/** + * This class should be instantiated at the application level of the android app. + * + * It will monitor the interaction between the different activities of an app and help us decide + * when the connection to the full node should be interrupted. + */ + +public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks { + private final String TAG = this.getClass().getName(); + + /** + * Constant used to specify how long will the app wait for another activity to go through its starting life + * cycle events before running the teardownConnectionTask task. + * + * This is used as a means to detect whether or not the user has left the app. + */ + private final int DISCONNECT_DELAY = 1500; + + /** + * Handler instance used to schedule tasks back to the main thread + */ + private Handler mHandler = new Handler(); + + /** + * Weak reference to the application context + */ + private WeakReference mContextReference; + + // In case we want to interact directly with the service + private NetworkService mService; + + /** + * Runnable used to schedule a service disconnection once the app is not visible to the user for + * more than DISCONNECT_DELAY milliseconds. + */ + private final Runnable mDisconnectRunnable = new Runnable() { + @Override + public void run() { + Context context = mContextReference.get(); + if(mService != null){ + context.unbindService(mServiceConnection); + mService = null; + } + context.stopService(new Intent(context, NetworkService.class)); + } + }; + + public NetworkServiceManager(Context context){ + mContextReference = new WeakReference(context); + } + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + if(mService == null){ + // Starting a NetworkService instance + Context context = mContextReference.get(); + Intent intent = new Intent(context, NetworkService.class); + context.startService(intent); + } + } + + @Override + public void onActivityStarted(Activity activity) { + mHandler.removeCallbacks(mDisconnectRunnable); + if(mService == null){ + Context context = mContextReference.get(); + Intent intent = new Intent(context, NetworkService.class); + context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + } + + @Override + public void onActivityResumed(Activity activity) {} + + @Override + public void onActivityPaused(Activity activity) { + mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY); + } + + @Override + public void onActivityStopped(Activity activity) {} + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {} + + @Override + public void onActivityDestroyed(Activity activity) {} + + /** Defines callbacks for backend binding, passed to bindService() */ + private ServiceConnection mServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; + mService = binder.getService(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) {} + }; +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java b/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java index eabb0c5..46e61dc 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java @@ -6,7 +6,6 @@ package cy.agorise.graphenej.api.bitshares; public class Nodes { public static final String[] NODE_URLS = { - "wss://bitshares.nus/ws", "wss://dexnode.net/ws", // Dallas, USA "wss://bitshares.crypto.fans/ws", // Munich, Germany "wss://bitshares.openledger.info/ws", // Openledger node diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetBlock.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetBlock.java index f38c298..af4be02 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetBlock.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetBlock.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.ArrayList; import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.api.ApiAccess; import cy.agorise.graphenej.models.ApiCall; /** @@ -12,6 +13,8 @@ import cy.agorise.graphenej.models.ApiCall; public class GetBlock implements ApiCallable { + public static final int REQUIRED_API = ApiAccess.API_DATABASE; + private long blockNumber; public GetBlock(int blockNum){ diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetRequiredFees.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetRequiredFees.java index d9fd785..773a208 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetRequiredFees.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetRequiredFees.java @@ -9,6 +9,7 @@ import cy.agorise.graphenej.BaseOperation; import cy.agorise.graphenej.BlockData; import cy.agorise.graphenej.RPC; import cy.agorise.graphenej.Transaction; +import cy.agorise.graphenej.api.ApiAccess; import cy.agorise.graphenej.models.ApiCall; /** @@ -17,6 +18,8 @@ import cy.agorise.graphenej.models.ApiCall; public class GetRequiredFees implements ApiCallable { + public static final int REQUIRED_API = ApiAccess.API_DATABASE; + private Transaction mTransaction; private Asset mFeeAsset; diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java b/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java index 3cb2487..b2e280f 100644 --- a/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java +++ b/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java @@ -79,7 +79,7 @@ public class MainActivity extends AppCompatActivity { @OnClick(R.id.send_message) public void onSendMesage(View v){ GetBlock getBlock = new GetBlock(1000000); - mService.sendMessage(getBlock); + mService.sendMessage(getBlock, GetBlock.REQUIRED_API); } @OnClick(R.id.next_activity) @@ -93,8 +93,6 @@ public class MainActivity extends AppCompatActivity { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, NetworkService.class); - int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST; - intent.putExtra(NetworkService.KEY_REQUESTED_APIS, requestedApis); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java b/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java index 495e6ef..ba2b651 100644 --- a/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java +++ b/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java @@ -1,91 +1,24 @@ package com.luminiasoft.labs.sample; -import android.app.Activity; import android.app.Application; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; -import cy.agorise.graphenej.api.android.NetworkService; +import cy.agorise.graphenej.api.android.NetworkServiceManager; /** * Sample application class */ -public class SampleApplication extends Application implements Application.ActivityLifecycleCallbacks { +public class SampleApplication extends Application { private final String TAG = this.getClass().getName(); - /** - * Handler instance used to schedule tasks back to the main thread - */ - private Handler mHandler = new Handler(); - - /** - * Constant used to specify how long will the app wait for another activity to go through its starting life - * cycle events before running the teardownConnectionTask task. - * - * This is used as a means to detect whether or not the user has left the app. - */ - private final int DISCONNECT_DELAY = 1500; - - /** - * Runnable used to schedule a service disconnection once the app is not visible to the user for - * more than DISCONNECT_DELAY milliseconds. - */ - private final Runnable mDisconnectRunnable = new Runnable() { - @Override - public void run() { - Log.d(TAG,"Runing stopService"); - stopService(new Intent(getApplicationContext(), NetworkService.class)); - } - }; - @Override public void onCreate() { super.onCreate(); - Intent intent = new Intent(this, NetworkService.class); - startService(intent); /* * Registering this class as a listener to all activity's callback cycle events, in order to * better estimate when the user has left the app and it is safe to disconnect the websocket connection */ - registerActivityLifecycleCallbacks(this); - } - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - - } - - @Override - public void onActivityStarted(Activity activity) { - mHandler.removeCallbacks(mDisconnectRunnable); - } - - @Override - public void onActivityResumed(Activity activity) { - - } - - @Override - public void onActivityPaused(Activity activity) { - mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY); - } - - @Override - public void onActivityStopped(Activity activity) { - - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { - - } - - @Override - public void onActivityDestroyed(Activity activity) { - + registerActivityLifecycleCallbacks(new NetworkServiceManager(this)); } } diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java b/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java index 6da0e2f..fec16f1 100644 --- a/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java +++ b/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java @@ -32,6 +32,7 @@ import cy.agorise.graphenej.models.JsonRpcResponse; import cy.agorise.graphenej.operations.LimitOrderCreateOperation; import cy.agorise.graphenej.operations.TransferOperation; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; public class SecondActivity extends AppCompatActivity { @@ -46,14 +47,17 @@ public class SecondActivity extends AppCompatActivity { private Gson gson = new Gson(); + private Disposable mDisposable; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); + Log.d(TAG,"onCreate"); ButterKnife.bind(this); - RxBus.getBusInstance() + mDisposable = RxBus.getBusInstance() .asFlowable() .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer() { @@ -86,6 +90,12 @@ public class SecondActivity extends AppCompatActivity { unbindService(mConnection); } + @Override + protected void onDestroy() { + super.onDestroy(); + mDisposable.dispose(); + } + /** Defines callbacks for backend binding, passed to bindService() */ private ServiceConnection mConnection = new ServiceConnection() { @@ -107,25 +117,25 @@ public class SecondActivity extends AppCompatActivity { @OnClick(R.id.transfer_fee_usd) public void onTransferFeeUsdClicked(View v){ List operations = getTransferOperation(); - mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121"))); + mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API); } @OnClick(R.id.transfer_fee_bts) public void onTransferFeeBtsClicked(View v){ List operations = getTransferOperation(); - mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0"))); + mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0")), GetRequiredFees.REQUIRED_API); } @OnClick(R.id.exchange_fee_usd) public void onExchangeFeeUsdClicked(View v){ List operations = getExchangeOperation(); - mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121"))); + mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API); } @OnClick(R.id.exchange_fee_bts) public void onExchangeFeeBtsClicked(View v){ List operations = getExchangeOperation(); - mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0"))); + mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0")), GetRequiredFees.REQUIRED_API); } private List getTransferOperation(){