Monitoring android lifecycle events from a library - android

I'm still fairly new to the programming world in general, so I hope this isn't an obvious/abstract question.
I'm developing an android library that needs to monitor the lifecycle events of the activity that's using it. How do I accomplish this while creating the least amount of work for the developer using my library? Preferably, I like to use something that is already built into Android.
I've seen similar questions, such as: Automatically log Android lifecycle events using ActivityLifecycleCallbacks?
But it doesn't really apply to a library project.
Am I missing something? Any insight would be greatly appreciated.

I think that your best (long term) option is to have a setup along with the integration of the library (i.e. pass the Application in an entry point of your library).
That said, there is an undocumented way to get the current Application. as described here: https://stackoverflow.com/a/12495865/458365
try {
final Class<?> activityThreadClass =
Class.forName("android.app.ActivityThread");
final Method method =
activityThreadClass.getMethod("currentApplication");
return (Application) method.invoke(null, (Object[]) null);
} catch (final Exception e) {
// handle exception
}
Once you have that you can call Application.registerActivityLifecycleCallbacks() to register your own ActivityLifecycleCallbacks
Update:
Another alternative to get the application (Context) that I've come across is to use a Content provider. I think libraries like Firebase use this method because it has zero set up. However, it requires the consumer of the library to have an application object (which actually is the same as with the manual method) but it does look a lot cleaner:
Inside the onCreate of the CP we can cast getContext as Application and go from there with the Callbacks process.
Source: https://medium.com/#andretietz/auto-initialize-your-android-library-2349daf06920

Related

What do the numbers in Android Studio debugger window mean?

What do the highlighted numbers example, 4580, 4581 etc., mean? They are not PIDs, this was crossed checked with the ps command in adb shell.
This number is the Register number of the register where the Object's reference is stored.
What is register number?
Something completely useless from an app developer point of view! I am sure you know about the Dalvik VM on which android applications run. So, the frames in a Dalvik byte code are made up of registers. And these registers store the object references. Check this link to know more. Not sure why android studio shows them in debugger. I don't see any use of it.
In short: The number may not necessarily be the register number, it could be the ID from ObjectReferenceImpl, which is an implementation of ObjectReference interface from Java Debug Interface (JDI).
In length: From analysis of Idea Community code base, ThreadDescriptorImpl.java (ThreadDescriptorImpl), was found to be the class responsible for providing the thread description to be displayed in the debug window (please refer above image presented with the question). The ID is referred as thread.uniqueID(). The thread here is of ThreadReferenceProxyImpl type which extends ObjectReferenceProxyImpl, where the uniqueID method is implemented. This method in turn returns a uniqueID from an object of ObjectReference type. Upon cursory search the ObjectReference definition with satisfying criteria was not found in Idea code base. It was later found to be hidden in the definition of JDI interface. From the JDI implementation jar found in the Idea setup, ObjectReferenceImpl was found to provide the final implementation of uniqueID method. The code snippet is listed below -
private long myID;
private static synchronized long nextID()
{
return nextID++;
}
ObjectReferenceImpl(VirtualMachine aVm, Oop oRef)
{
super(aVm);
this.saObject = oRef;
this.myID = nextID();
}
public long uniqueID()
{
return this.myID;
}
However in saying so and answering the question, words like 'probably' and 'may be' were used because, the references for ObjectReference implementations were not found immediately in the Idea Community edition source code. And, the inferences were from the jar implementations. If direct references were to be provided in the future by someone looking at this question and answer, the answer can be modified to reflect certainty.

Handling dependencies in Library projects

I have one Android open source library project in which I have been using a separate library for logging (Which is also developed by me).
The logging library is not that important, but it makes the development and debugging easier (Both for me and the user of the main library).
I am using gradle (and Jitpack) to use both the libraries. Now the Logging library is actually having few extra permissions in manifest (For writing logs to file, not necessary for the main library).
Now one of the user asked me to remove the extra permissions. And I don't know how can I do that without removing the logging library (or changing the functionality in the logging library itself).
I even realised that few people might not need the logging library at all, so is there a way I can make it optional, like if the user didn't include the Logging library in their build.gradle, it won't get imported which I can detect and not call the logging functions?
I know it sounds confusing, but I'd like to know how to decouple both the libraries. In fact please let me know if you know about any such example from any popular library too.
Yes you can probably do both with some clever tricks.
part 1. PERMISSIONS
About the manifest permissions. It's pretty simple, do not add the permission to the logging library and instead, check it on runtime. Like this:
if (context.checkPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.os.Process.myPid(),
Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
carry on with your write to disk operation ...
}
and then on your documentation you write that IF developers using the library wants to have a local logging, they have to declare WRITE_EXTERNAL_STORAGE on the manifest.
part 2. (not) IMPORTING THE LIB
All I'm gonna write here is by heart and 100% NOT tested. There might (will) be some errors that you'll have to shift around, but hopefully I'll pass a solid idea.
First on the NoobCameraFlash lib you'll define LumberJack in the build.gradle file with provided instead of compile. This will make the compiler known about the LumberJack so compilation can pass, but it won't include it on the actual build.
Then create in your NoobCameraFlash library a class that is a mirror of the library funcionalities. Which means the methods d(String, String), e(String, String) etc.
Then you in this mirror class you will something like the following to check if lumberjack is actually available.
private static boolean lumberJackAvailable = false;
private static boolean lumberJackTested = false;
private static boolean isLumberJackAvailable() {
if(lumberJackTested) return lumberJackAvailable;
lumberJackTested = true;
try {
if(Class.forName("") != null) {
lumberJackAvailable = true;
}
} catch(Throwable e){
// ClassNotFoundException, LinkageError, ExceptionInInitializerError
}
return lumberJackAvailable;
}
public static LumberJackMirror create() {
// could also be a singleton
if(isLumberJackAvailable() return new LumberJackMirror();
else return null;
}
then of course you have to check if(lumberJackMirror != null). So as you can see it's not the most straight forward way of doing things.
Another way that simplifies this a little bit is to create an interface in a different library, that both the mirror and the actual LumberJack implements and use the factory can return an empty implementation of the interface instead of having to null check all the time.
As well, include on the documentation, that if developers want to have the logging functionality they have to add it to the build.gradle. Something like compile 'your_groud_id:lumberjack:version'
edit
another common way of doing that is to make it explicit on the NoobCameraFlash initialisation code. Something like:
NoobCameraFlash.config()
.setLogger(new LumberJack());
so that forces developers to know about LumberJack instead of checking via Class. But that would mean you need some version of LumberJack that is not just static methods.
end_edit
But hopefully just the permission removal will be enough and you don't have to do this part.2 =]
happy coding.

Avoid a block of code to run during unit test using mokito

I am using Mokitio in android to run unit test cases.
.
What i am trying to do: There is a block of code in onCreate event
of the activity
I am trying not to run this block of code during Running Unit test
cases and run it during app regularly.
Is it possible to do something like that using mokito because mokito synchronizes for activity life cycle
The proper solution here is to change your design a bit. You should not think in terms of code blocks, but in terms of functionality.
The way of preventing that some x lines of code are run in a certain environment, but are not in some other context ... is by using proper OO means.
Meaning: first create an interface that describes the functionality of those lines of code we are talking about:
public interface DoTheFoo {
public void foo(Bar bar);
}
Then you create a "production" implementation DoTheFooImpl of that interface (which as a side effect: you might be able to write proper unit tests for as well).
Finally: within your class that needs that functionality, use dependency injection to acquire an object providing the DoTheFoo interface. In your production environment, that would be a DoTheFooImpl object; but for your unit testing, you would simply create an mock for it - configured to do nothing upen calls to foo().
Of course that sounds like a bit of work; but the point is: currently, your design is somehow deficient. And instead of trying to go for dirty hacks/workarounds, consider looking at your design to identify a more elegant way to resolve your problem.

Unittesting AsyncTaskLoader with getLoaderResultSynchronously

I am trying to create unit tests for a REST client that does some API calls. The client works fine in the live application, but I can't get it to run in the test case.
Apparantly, LoaderTestCase.getLoaderResultSynchronously() could be used here (at least according to Android reference, but it will not accept my loader. The code:
public void testGetEventInfo() {
// Init some vars
...
// Create & execute loader
RESTLoader loader = new RESTLoader(getContext(),
RESTLoader.HTTPVerb.GET, action, params, LOADER_GET_NEWS);
getLoaderResultSynchronously(loader);
}
This yields the error getLoaderResultSynchronously(Loader) in the type LoaderTestCase is not applicable for the arguments (RESTLoader).
RESTLoader extends AsyncLoader. Note that I'm using the supportlibrary, maybe the Loader in there is incompatible? The documentation gives no information on this.
I've tried to solve this in several ways, though none seem to work:
Registered a listener to loader. However, the callback never triggers
Using CountdownLatch (also with a listener). Again, no trigger/countdown timeout.
Playing around with the type template (), without success.
Similar solutions on SO, though again failing to reach the listener.
Does anybody know why getLoaderResultSynchronously will not accept the loader? Or another clean way of testing the Loader, including a way to test return data? I can test handling the return data in a separate case, but I would also like to test the actual data.
Sincerely,
Have you taken a look at the source code? You'll find the following import statements:
import android.content.Loader;
import android.content.Loader.OnLoadCompleteListener;
It doesn't look like Android offers a support version for LoaderTestCase. The easiest solution would be to temporarily change to the non-support LoaderManager (that is, have your class make use of the android.content.Loader instead), test your app, and then switch back to the support implementation. You might also consider copying the testing source code into your project, import the support LoaderManager, and execute it directly. I'm not familiar with the test libraries for Loaders but it doesn't seem outwardly obvious that this would cause any major issues.
You can get sources from LoaderTestCase here, create SupportLoaderTestCase class from that sources in your test project and modify all namespaces to support library namespaces (e.g. change android.content.Loader with android.support.v4.content.Loader). Than you can extend your test case from SupportLoaderTestCase (not from LoaderTestCase) and use it without problems
The method you are trying to call (getLoaderResultSynchronously) accepts an object of type android.content.Loader. If your RESTLoader class is not of that EXACT type then you will get this error. I suspect your class directly or indirectly extends android.support.v4.content.Loader, which would explain the error.
I am not aware of a back-port of LoaderTestCase that would support testing of this type of class.

How do you test an Android application across multiple Activities?

We are building a complex Android application consisting of many screens and workflows spread across many Activities. Our workflows are similar to what you might see on a Bank's ATM machine, for example, there is an Activity to login in that transitions to a main menu Activity which can transition to other activities based on the user's choices.
Since we have so many workflows we need to create automated tests that span multiple activities so we can test a workflow from end to end. For example, using the ATM example, we would want to enter a valid PIN, verify that sends us to the main menu, choose withdraw cash, verify that we are on the withdraw cash screen, etc., etc., and eventually find ourselves back on the main menu or "logged" out.
We've toyed with the test APIs that come with Android (e.g. ActivityInstrumentationTestCase2) and also with Positron, but neither seem capable of testing beyond the bounds of a single Activity, and while we can find some utility in these tools for some unit testing, they won't meet our needs for testing scenarios that cut across multiple Activities.
We are open to an xUnit framework, scripting, GUI recorders/playbacks, etc. and would appreciate any advice.
I feel a bit awkward about answering my own bounty question, but here it is...
I've searched high and low on this and can't believe there is no answer published anywhere. I have come very close. I can definitely run tests that span activities now, but my implementation seems to have some timing issues where the tests don't always pass reliably. This is the only example that I know of that tests across multiple activities successfully. Hopefully my extraction and anonymizing of it did not introduce errors. This is a simplistic test where I type a username and password into a login activity, and then observe a proper welcome message is shown on a different "welcome" activity:
package com.mycompany;
import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;
public class LoginTests extends InstrumentationTestCase {
#MediumTest
public void testAValidUserCanLogIn() {
Instrumentation instrumentation = getInstrumentation();
// Register we are interested in the authentication activiry...
Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);
// Start the authentication activity as the first activity...
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
instrumentation.startActivitySync(intent);
// Wait for it to start...
Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
assertThat(currentActivity, is(notNullValue()));
// Type into the username field...
View currentView = currentActivity.findViewById(username_field);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(EditText.class));
TouchUtils.clickView(this, currentView);
instrumentation.sendStringSync("MyUsername");
// Type into the password field...
currentView = currentActivity.findViewById(password_field);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(EditText.class));
TouchUtils.clickView(this, currentView);
instrumentation.sendStringSync("MyPassword");
// Register we are interested in the welcome activity...
// this has to be done before we do something that will send us to that
// activity...
instrumentation.removeMonitor(monitor);
monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);
// Click the login button...
currentView = currentActivity.findViewById(login_button;
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(Button.class));
TouchUtils.clickView(this, currentView);
// Wait for the welcome page to start...
currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
assertThat(currentActivity, is(notNullValue()));
// Make sure we are logged in...
currentView = currentActivity.findViewById(welcome_message);
assertThat(currentView, is(notNullValue()));
assertThat(currentView, instanceOf(TextView.class));
assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
}
}
This code is obviously not very readable. I have actually extracted it into a simple library with an English-like API so I can just say things like this:
type("myUsername").intoThe(username_field);
click(login_button);
I've tested to a depth of about 4 activities and am satisfied that the approach works though as I said, there appears to be an occasional timing issue I have not completely figured out. I am still interested in hearing of any other ways of testing across activities.
Take a look at Robotium
'a open-source test framework created to make automatic black-box testing of Android applications significantly faster and easier than what is possible with Android instrumentation tests out-of-the-box.'
Homepage:
http://www.robotium.org/
Source:
http://github.com/jayway/robotium
Please note that the Robotium project is maintained by the company I work for
You could always use Robotium. It supports blackbox testing just like Selenium but for Android. You will find it at Robotium.org
I'm surprised no one has mentioned some of the leading automated functional testing tools. Compared with Robotium, these don't require writing Java code.
MonkeyTalk: an open-source tool backed by the company Gorilla Logic. Pros: provides recording as well as a higher-level scripting language easier for non-technical users, and is cross-platform (includes iOS). Given those benefits as requirements, we've found this to be the best solution. It also allows customization beyond what can be done in their scripting language using Javascript.
Calabash-Android: an open-source tool for Cucumber-style features. Pros: write features in the Gherkin language which is Business Readable, Domain Specific Language that lets you describe software’s behavior without detailing how that behavior is implemented. Similar but not exact support is available for iOS in cucumber-ios. Recording capabilities are not as good, as they produce a binary output.
A couple of other references:
Here are some additional comparisons between Robotium,
Monkeytalk and Calabash. It mentions TestDroid as another
possibility.
This blog mentions the above plus NativeDriver and Bot-bot.
I created a record-and-playback tool for Android and made it available on GitHub. It's easy to configure and use, requires no programming, runs against real devices (which do not have to be rooted) and automatically saves screenshots as it plays tests.
First of all, use 'ActivityInstrumentationTestCase2', not 'InstrumentationTestCase', as your base class. I use Robotium and routinely test across multiple Activities. I found that I have to specify the login activity as the generic type (and class argument to the constructor).
The 'ActivityInstrumentationTestCase2' constructor ignores the package argument and does not require it. The constructor that takes the package is deprecated.
From the Javadocs:
"ActivityInstrumentationTestCase2(String pkg, Class activityClass)
This constructor is deprecated. use ActivityInstrumentationTestCase2(Class) instead"
Using the recommended base class allows the framework to handle certain boilerplate, like starting your activity. That's done by the call to 'getActivity()', if necessary.
Found this useful with a couple of modifications.
Firstly getInstrumentation().waitForIdleSync() will cure the flakiness SingleShot speaks of
and also InstrumentationTestCase has a lauchActivity function that can replace the start activity lines.
you can do it like this to avoid the flake waiting times out of sync :
final Button btnLogin = (Button) getActivity().findViewById(R.id.button);
Instrumentation instrumentation = getInstrumentation();
// Register we are interested in the authentication activity...
Instrumentation.ActivityMonitor aMonitor =
instrumentation.addMonitor(mynextActivity.class.getName(), null, false);
getInstrumentation().runOnMainSync(new Runnable() {
public void run() {
btnLogin.performClick();
}
});
getInstrumentation().waitForIdleSync();
//check if we got at least one hit on the new activity
assertTrue(getInstrumentation().checkMonitorHit(aMonitor, 1));
I'm working on pretty much the same thing, and I'll probably go with a variation on the accepted answer to this question, but I did come across Calculuon (gitHub) during my searches for a solution.
I haven't personally used it, but ApplicationTestCase looks like it might be what you're looking for.
Will accepted approach work with different Activities from different Applications, signed by different certificates? If not, Robotium the best way to test activities within same application.
There is another way to do the multiple activity using ActivityInstrumentation Class..
Its a normal automation scenario...
First get the focus of what ever object you want and then send a key
Simple as that
sample code
button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);
Only thing is understanding the every API calls will help us.
This answer is based on the accepted answer but modified to solve the timing issue which for me became consistent after adding about a half dozen tests. #pajato1 gets the credit for solving the timing issue, as cited in the accepted answer comments.
/**
* Creates a test Activity for a given fully qualified test class name.
*
* #param fullyQualifiedClassName The fully qualified name of test activity class.
*
* #return The test activity object or null if it could not be located.
*/
protected AbstractTestActivity getTestActivity(final String fullyQualifiedClassName) {
AbstractTestActivity result = null;
// Register our interest in the given activity and start it.
Log.d(TAG, String.format("Running test (%s) with main class: %s.", getName(), fullyQualifiedClassName));
instrumentation = getInstrumentation();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(instrumentation.getTargetContext(), fullyQualifiedClassName);
// Wait for the activity to finish starting
Activity activity = instrumentation.startActivitySync(intent);
// Perform basic sanity checks.
assertTrue("The activity is null! Aborting.", activity != null);
String format = "The test activity is of the wrong type (%s).";
assertTrue(String.format(format, activity.getClass().getName()), activity.getClass().getName().equals(fullyQualifiedClassName));
result = (AbstractTestActivity) activity;
return result;
}
Try the Monkey tool testing
Step 1:
open the android studio terminal(Tools-> open terminal)
Step 2:
In order to use monkey , open up a command prompt and just naviagte to the following directory.
export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools
Step 3:
add this monkey command into terminal and press enter..
see the magic in your emulator.
adb shell monkey -p com.example.yourpackage -v 500
500- it is the frequency count or the number of events to be sent for testing.
you can change this count..
More reference,
http://www.tutorialspoint.com/android/android_testing.htm
http://androidtesting.blogspot.in/2012/04/android-testing-with-monkey-tool.html

Categories

Resources