Logic of data transfer on an Android application uses Observer/Observable - android

Explanation:
I have an Android application which has developed by a colleague of me.
I need to chop off the application to make the logic/programmatic part to reside on a server as a Web Service and to make the remaining part to reside as a client on Android device.
The problem:
As you can see in the below code, Observer/Observable system is used.
1) First, i need to understand, if the Observer/Observable system is used only to establish a communication between Observer(Activity) and Observable.
2) If so, should i put the Manager's programmatic code into the Web Service, and communicate with the Activity using HTTP GET, POST protocols.
3) Or have i misunderstand everything?
public class MainActivity extends Activity implements Observer {
#Override
public void onCreate(Bundle savedInstanceState) {
view = new MainView(this);
ASensor aSensor;
aSensor = new ASensor(MainActivity.this);
aSensor.setListener(new Listener() {
#Override
public void onObjectDiscovered(List<AnObject> params) {
List<AnObject> myParams = new ArrayList<AnObject>();
TheManager.INSTANCE.feed(myParams);
}
});
}
#Override
public void update(Observable observable, Object data) {
if (observable instanceof TheManager) {
List<AnObject> objectList = (List<AnObject>) data;
String retValue = someProcessingMethod(objectList);
view.setRetValue(retValue);
}
}
}
public class TheManager extends Observable implements Serializable {
public static final TheManager INSTANCE = new TheManager();
public void feed(List<AnObject> params) {
List<AnObject> objectList = processParams(params);
notifyObservers(objectList);
}
}

In Google I/O 2010, Virgil Dobjanschi presented the talk Android REST Client Applications Design Pattern, which solves problems of keeping local data consistent to a remote database using SyncAdapters.
If you choose to store your data using a ContentProvider and synchronize it through a SyncAdapter, you may find a detailed description at Google Developers of how to:
Run the Sync Adapter When Server Data Changes
Run the Sync Adapter When Content Provider Data Changes
Run the Sync Adapter After a Network Message
Run the Sync Adapter Periodically
Run the Sync Adapter On Demand

Related

What process is the best way to send on API and save SQLiteDatabase?

I'm new on Android and working an big app which has sending data to API and saving it on SQlite. All of this process is on one class file . But it leaves me on an error. Sometimes the device hanged. other scenario is the data is incomplete . I have read about Intent Service and Services and I want to learn about the two, but I'm wondering how to get all of my data from UI and put it on services. May I know How?
It depends on the nature of the application. If this should happen in response to a user input...you could well use an AsyncTask. Otherwise, a background service could also do the job.
What you should NEVER do is run a network operation and/or database access on the main UI thread.
Services can receive data via intents. The way to send these intents depend on the type of service (Started, Bound or both). There are plenty of resources out there you can read...here's one from Android documentation...
https://developer.android.com/guide/components/services
An Example of an AsyncTask
The example below shows an implementation of AsyncTask that fetches a user's details from a network resource...
public class FetchUserTask extends AsyncTask<String,Void, UserDTO> {
private FetchUserTaskListener listener;
#Override
protected UserDTO doInBackground(String...params){
if(params == null || params.length == 0)
return null;
String userID = params[0];
UserDataProvider provider = new UserDataProvider(userID);
try {
return provider.get(userID);
}
catch(Exception ex){
//log the error
return null;
}
}
#Override
protected void onPostExecute(UserDTO user){
if(listener != null)
listener.onCompleted(user);
}
public void setListener(FetchUserTaskListener listener){
this.listener = listener;
}
public interface FetchUserTaskListener{
void onCompleted(boolean success);
}
}
How'd you use this AsyncTask?
For example, in an Activity, you would use it as below...
public class UserDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//instantiate activity...
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever_layout);
fetchUser(userId);
}
private void fetchUser(String userID){
FetchUserTask task = new FetchUserTask();
task.setListener(new FetchUserTaskListener<UserDTO>() {
#Override
public void onCompleted(UserDTO user) {
//CAUTION: make sure the activity hasn't been stopped before
//accessing any UI elements and/or context
}
}
task.execute(userID);
}
}
Note
You can (and will need to) make the example(s) above a bit more sophisticated. For example you can have the FetchUserTaskListener's onCompleted method return also an error message if an error occurred.
You will also need to check whether the activity has been paused or stopped before you access any context-bound data otherwise you might get an ILlegalStateException.
Make use of SQLiteOpenHelper class and it has methods to be overridden in your own class by extending SQLiteOpenHelper. Create Add, Update, Delete, Get methods as per your requirement in this class and keep this class as Singleton pattern. User Asynctasks to call those methids and you are done.
Hope that helps you visualise things in better way.

How do I return a String Response to my Fragment form an API and update my fragment

I am relatively new to developing Android application. I tried searching for the answer everywhere even used OkHttp library but nothing seemed to work.
I have an application which displays data retrieved from an API in a fragment. I have an app drawer which is used to change what you want to see and switches to a new fragment.
What I want to achieve is connect to the internet and retrieve all the data as it is in a string format and assign it to a APIResponse String in my Fragment.
The code in which I have trouble is :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_newfragment, container, false);
unbinder = ButterKnife.bind(this, view);
listdata = new ArrayList<>();
DataReceivingClass dataReceivingClass;
String APIResponse ="";
APIResponse = /*Need to pass 2 String Parameters namely source and sort
which is required for generating the url as I want to reuse this code
over other fragments.
Connect to the internet using the url and get the data in string format
and assign the returned string to APIResponse */
Gson gson = new Gson();
dataReceivingClass = gson.fromJson(APIResponse, DataReceivingClass.class);
listdata = dataReceivingClass.getData();
I used all sorts of solutions I found. Created new myAsync class which inherited from AsyncTask and tried but still couldn't get it done.
My Application always used to crash.
and one time when it didn't crash it just worked according to the values I had passed to APIResponse initially and didn't update it later.
runOnUiThread function also seemed confusing and didn't seem to work. I was unable to use it.
Please tell me the code to make it run. A code which I can reuse in other fragments by just changing parameters passed as mentioned in my code will be really appreciated.
If possible (and the response is a Json or XML), consider using a 3rd Party library (like Retrofit or Jackson) for handling your API calls, they are not very difficult to implement and save you a lot of time (and problems).
For handling the response, you can use Callbacks to "respond" when your request has ended.
public interface RequestCallback<T> {
void onSuccess(T data);
void onError(Object error);
}
Implement your Callback in your Fragment
public class YourFragment extends Fragment implements RequestCallback<String> {
... your code ...
#Override
public void onSuccess(String data) {
// update UI
}
#Override
public void onError(Object error) {
// show error
}
}
Then you can use the Callback methods (passing the Fragment instance) when the request has finished. You can implement that Callback in every Fragment/Class that needs to "respond" your requests.
Also, there are other alternatives.
About runOnUiThread, every network request needs to be done in an separated Thread. But you can't interact with the UI on that Thread (I guess that's the reason your Application is crashing). So you use runOnUiThread() for execute your code on UI Thread to interact with the UI.
runOnUiThread(new Runnable() {
#Override
public void run() {
// update UI
}
};
Alternatively you can use the Annotation #UiThread in a method to make sure that method is executed in the UI Thread.
#UiThread
public void updateUI() {
// update UI
}
Also, if you're using AsyncTask you can update UI in onPostExecute(), which runs on UI Thread
class YourAsyncTask extends AsyncTask<String, Void, String> {
.. your code ..
#Override
protected void onPostExecute(String result) {
// update UI
}
}
I hope I have helped.

How do I make my activity use testing data?

I have an application which displays data (posts) from a web API.
A background service syncs this data at some unknown time and saves it.
When visiting my main activity it loads this data and displays it in a RecyclerView
The loading is handled via a singleton class
I currently test the main activity as follows
#Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void testDataLoad() {
int postsTotal = DataSingleton.getInstance().getPostsCount();
ViewInteraction empty = onView(withId(R.id.empty_view));
ViewInteraction recycler = onView(withId(R.id.recycler_view));
if (postsTotal == 0) {
empty.check(matches(isDisplayed()));
recycler.check(matches(not(isDisplayed())));
} else {
empty.check(matches(not(isDisplayed())));
recycler.check(matches(isDisplayed()));
recycler.check(new RecyclerViewItemCountAssertion(greaterThan(postsTotal)));
}
}
I know that this can't be the right way to write tests. I want to be able to test both with an empty data set and a non-empty set so that the if-else is two separate tests. The only way I think I can achieve it is to mock the data.
Is there another way?
Can I use Mockito to make the MainActivity use mock data without modifying the production code? Is my only choice to make it inject either real or mocked data providers in place of my singleton?
Is it better to just uninstall and reinstall my app each time so there is no data to start with and then continue with real data testing?
Android Activity are heavyweight and hard to test. Because we don't have control over the constructor, it is hard to swap in test doubles.
The first thing to do is to make sure you are depending on an abstraction of the data-source rather than a concretion. So if you are using a singleton with a getPostsCount() method then extract an interface:
interface DataSourceAbstraction {
int getPostsCount();
}
Make a wrapper class that implements your interface:
class ConcreteDataSource implements DataSourceAbstraction {
#Override
int getPostsCount() {
return DataSingleton.getInstance().getPostsCount();
}
}
And make the Activity depend on that rather than the concrete DataSingleton
DataSourceAbstraction dataSourceAbstraction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super(savedInstanceState);
injectMembers();
}
#VisibleForTesting
void injectMembers() {
dataSourceAbstraction = new ConcreteDataSource();
}
You can now swap in a test double by subclassing and overriding injectMembers that has relaxed visibility. It's a bad idea do this in enterprise development, but there are less options in Android Activities where you don't control the constructor of the class.
You can now write:
DataSourceAbstraction dataSource;
//system under test
MainActivity mainActivity
#Before
public void setUp() {
mockDataSource = Mockito.mock(DataSourceAbstraction.class);
mainActivity = new MainActivity() {
#Override
void injectMembers() {
dataSourceAbstraction = mockDataSource;
}
};
}

How do i mock REST backend in an android app

My android app communicate with backend service through REST API . I want to mock out this API to quickly develop the front end.
I am using android volley as client side networking library.
You can use the dependency injection design pattern for this.
Basically you specify an interface that defines a set of methods corresponding to the queries you have in your REST backend, e.g.:
interface DataSupplier {
// Lookup user by ID
User getUser(int id);
// Get all blog posts posted by a specific user.
List<BlogPost> getUsersBlogPosts(int userId);
}
Now in the class where you need to query the backend, you specify an injector. This can be done in multiple ways (e.g. constructor injection, setter injection - see the wiki article for more details). An injector lets you inject an implementation of the dependency into the class that depends on it. Let us assume you use constructor injection. Your class that uses the backend would look like this:
public class DependentClass {
private final DataSupplier mSupplier;
public DependentClass(DataSupplier dataSupplier) {
mSupplier = dataSupplier;
}
// Now you simply call mSupplier whenever you need to query the mock
// (or - later in development - the real) REST service, e.g.:
public void printUserName() {
System.out.println("User name: " + mSupplier.getUser(42).getName());
}
}
Then you create a mock implementation of DataSupplier:
public class MockRestService implements DataSupplier {
#Override
public User getUser(int id) {
// Return a dummy user that matches the given ID
// with 'Alice' as the username.
return new User(id, "Alice");
}
#Override
public List<BlogPost> getUsersBlogPosts(int userId) {
List<BlogPost> result = new ArrayList<BlogPost>();
result.add(new BlogPost("Some Title", "Some body text"));
result.add(new BlogPost("Another Title", "Another body text"));
result.add(new BlogPost("A Third Title", "A third body text"));
return result;
}
}
and use that to instantiate your dependent class:
DepedentClass restClient = new DepedentClass(new MockRestService());
Now you can use restClient as if it was connected to your actual backend. It will simply return dummy objects that you can use to develop your front end.
When you are done with your front end and ready to implement your backend, you do so by creating another implementation of DataSupplier that sets up a connection to your REST backend and queries it for real objects. Let us say you name this implementation RestService. Now you can simply replace the constructor creating the MockRestService with your RestService constructor like so:
DepedentClass restClient = new DepedentClass(new RestService());
And there you have it: by swapping a single constructor call, you can change your front end code from using dummy objects to using real REST-delivered objects.
You could even have a debug flag and create the restClient according to the state of your application (debug or release):
boolean debug = true;
DependentClass restClient = null;
if (debug) {
restClient = new DepedentClass(new MockRestService());
} else {
restClient = new DepedentClass(new RestService());
}
I've recently created RESTMock. It is a library for Mocking REST API's in android tests. It can be used during development though. You would need to set it up following the README on github and create a basic Android Instrumentation test that would start your app and do nothing. This way the app is started with the Mock Server in background.
Example test:
public class SmokeTest {
#Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<MainActivity>(
SplashActivity.class,
true,
false);
#Test
public void smokeTest() throws InterruptedException {
rule.launchActivity(null);
Thread.sleep(10000000);
}
}

Android service causes a singleton's instance variable to be null

I have class called ClientManager where i load the dictionary from the server. Once the dictionary is loaded a Service is started (which runs in its own process) that will use the data from this dictionary. However, when i access the dictionary through the class singleton it is null. Dictionary object resides in class DataManager. Please see the code below:
ClientManager.java
DataManager mDataManager = DataManager.getInstance();
public void apiCompleted(ApiResult apiResult){
....
DataDictionary dataDict = (DataDictionary) apiResult.valueObject;
//dictionary loads OK from server since i can see the contents by iterating through it
mDataManager.addDictionary(dataDict);
if (!serviceRunning(MyService.class)){
Intent intent = new Intent(mContext, MyService.class);
mContext.startService(intent);
}
MyService.java
public class MyService extends Service {
...
DataManager mDataManager;
#Override
public void onCreate(){
mDataManager = DataManager.getInstance();
if(!mDataManager.containsDataDictionary()){
//toast dictionary is null
}
}
DataManager.java
public class DataManager {
private static DataManager instance = null;
private DataDictionary mDataDictionary = null;
public static DataManager getInstance(){
if (instance == null){
instance = new DataManager();
}
return instance;
}
public boolean containsDataDictionary() {
if ( m_dataDictionary == null ){
return false;
}
return true;
}
public DataDictionary getDataDictionary() {
return mDataDictionary;
}
public void addDataDictionary(DataDictionary p_dataDictionary) {
mDataDictionary = p_dataDictionary;
}
}
You said it yourself: "which runs in its own process"
Each process has its own VM. You cannot access variables etc from one process to another.
The obvious question to follow up this would be, do you absolutely need to run the service in its own process?
Data sharing between processes are done through Intents or ContentProviders.
If the data is only shared within your application package you may also use SQLite or SharedPreferences but both these uses non-volatile memory and are significantly slower than data sharing through IPC.
The Singleton Pattern is allowed to share data within the application not between applications. Any instances of an app its lifespan is restricted within scope of the app life. It seems that you are trying to extend the scope of singleton object outside the app, which is not possible at all..
Note : Each process runs in own VM, so target processes doesn't have singleton instance of source processes.
There are two ways of communication for an app...
1) Active Communication (IPC) : When both source and target app is running (i.e.. processes) you need source/sender should act as a server and target/receiver should act as a client and both should communicate with Remote Object which both side has same remote object signature.Example : AIDL implementation....
2) Passive Communication (Accessing Source's Resource) : When Source is not alive and target is trying to access the data of source which is stored in any kind of storage can be accessed via Intents/Content Provider.
If you want to share data between process then you go with AIDL implementation only...

Categories

Resources