I am combining a static code analysis with a dynamic one. I basically create a new activity and set it up as the starting activity. During the run various methods in existing activities should be called.
I can call e.g. onCreate from outside, however, the super call to Activity will fail (or calls to SharedPreferences or other interesting classes) since Android does some initialization stuff when using the intents in order to call an activity (e.g. setting the context). But I need to somehow call methods like onCreate or onPause from outside while giving the target activity a valid context.
In my newly created activity I have got a valid context. I tried to pass it via calling ContextWrapper.attachBaseContext, but there is still a NullPointerException somewhere in Android due a the missing context. Is there some way to hack this somehow into a working state? Using reflection or other hacks would be no problem, since it is for analysis purposes only.
Thank you very much for any tips. I'd be able to modify the analyzed apps in any way to get this working.
However: Using an Intent is no option, since I cannot control which Activity-methods are being called, when and how often. I know that android has not been made for calling these methods directly, but it is not a common use case either :);
I have created a hack, which seems to help (I can get a valid context in the hacked activity). Let's see how far I get using this.
public static void hack(Activity hack, Activity main) {
try {
Field mActivityInfo = getField(Activity.class, "mActivityInfo");
mActivityInfo.set(hack, getClass("android.content.pm.ActivityInfo").newInstance());
Field mFragments = getField(Activity.class, "mFragments");
Field mContainer = getField(Activity.class, "mContainer");
Field mApplication = getField(Activity.class, "mApplication");
Field mWindow = getField(Activity.class, "mWindow");
Class FragmentManagerImpl = getClass("android.app.FragmentManagerImpl");
FragmentManager manager = (FragmentManager) mFragments.get(hack);
mApplication.set(hack, main.getApplication());
mWindow.set(hack, main.getWindow());
Class<?> FragmentContainer = getClass("android.app.FragmentContainer");
Method attachActivity = getMethod(FragmentManagerImpl, "attachActivity", Activity.class, FragmentContainer, Fragment.class);
attachActivity.invoke(manager, hack, mContainer.get(hack), null);
Method attachBaseContext = getMethod(ContextWrapper.class, "attachBaseContext", Context.class);
attachBaseContext.invoke(hack, new HackContext(main));
System.out.println("Hack performed");
} catch (Exception e) {
e.printStackTrace();
System.err.println("Hack failed :(");
}
}
Related
I have an activity that contains a number of fragments. Each fragment has a viewModel scoped to its lifetime with some logic inside. The host activity has a viewModel too, including some code to show a popup-style message.
I want my fragment viewModels to be able to post messages to this popup. However, how could I access the activity-viewModel from inside one of my fragment-viewModels?
I'll write some exemplary Kotlin code, but the question is not specific to Kotlin since it's more of an architectural issue.
class MyActivityViewModel {
...
popupMessage = MutableLiveData<String>("") // is observed by my activity
fun postMessage(text: String) {
popupMessage.value = text
}
}
class MyFragmentAViewModel {
...
fun someFunctionA() {
// want to call ActivityViewModel's postMessage from here
}
}
class MyFragmentBViewModel {
...
fun someFunctionB() {
// want to call ActivityViewModel's postMessage from here too
}
}
I can't easily call ViewModelProvider since I'd rather not keep a reference to an Activity in my viewModel. The only direct option I see is to pass the activity-viewModel to the fragment-viewModels through the constructor or an init() method. That should be safe since the parent viewModel's lifetime should exceed the fragment viewModels' lifetime. I think.
Still, that solution rubs me the wrong way.
Are there any other alternatives? Or perhaps an entirely different approach to the issue?
Here's the thought of a greenhorn:
Can't you tell the activity that your fragment wants to use its method?
If you
Make an interface with a method a la "fragmentAWantsToUsePostMessage" in your fragment
Implement the interface in the activity, so that every time fragmentAWantsToUsePostMessage is called, the activity calls postMessage
Get a reference to the implementation of the interface in your fragment
Use that reference when the fragment needs to call "post message"
Shouldn't that work? Or is that against your "not keeping a reference"?
As I said: I'm new to all of this, so I might be completely wrong.
I can see that there's a post on medium that might be relevant: How to Communicate between Fragment and Activity
I am testing an activity that starts another activity during its onCreate(). This second activity is started with startActivityForResult(), and the main activity then waits for onActivityResult().
I'm trying to use Espresso to test this, attempting to stub the second activity with intending(), and verify it occurred using intended().
It appears though that espresso-intents isn't designed to work with intents launched from within the onCreate() method (see the warning in the last paragraphs here).
Has anyone managed to stub an Intent started from within onCreate(), and if so, how?
I was able to get this working for myself by using the following Kotlin code:
#Rule #JvmField
val activityRule: IntentsTestRule<MainActivity> =
object : IntentsTestRule<MainActivity>(MainActivity::class.java, true, false) {
override fun beforeActivityLaunched() {
super.beforeActivityLaunched()
Intents.init()
intending(hasComponent(LaunchedFromOnCreateActivity::class.java.name)).respondWith(Instrumentation.ActivityResult(RESULT_OK, null))
}
override fun afterActivityLaunched() {
Intents.release()
super.afterActivityLaunched()
}
}
The general idea is that since the lifecycle stuff happens in between beforeActivityLaunched and afterActivityLaunched, you'll need to set up your intending there. That said, this doesn't make it possible to do intended testing.
I am trying to generate a notification from a class, Utilities.java, outside of the subclass of Context. I've thought about providing a SingletonContext class and have looked at posts ike this. I'd like to be able to return != null Context object since the notification can be generated at any given time because it is generated from a messageReceived() callback.
What are there downsides to doing something like this:
public static Context c;
public class MainActivity extends Activity{
#Override
public void onStart()
super.onStart()
c = this.getApplicationContext();
}
//other method somewhere outside this class
public Context getContext(){
return MainActivity.c
}
I don't think it would be any different than putting this on the onCreate(), however, it guarantees that the context is up to date when the activity starts.
The Context keeps a reference to this activity in memory, which you might not want. Perhaps use
this.getApplicationContext();
instead. This will still let you do file IO and most other things a context requires. Without a specific reference to this activity.
Maybe you should overwrite the onResume Method.
If you open a new activity, and switch back, the onStart method will not getting invoked.
Android Lifecycle: doc
BTW: I read about problems with ApplicationContext using a dialog or toast, so if you use the context to create on of these you should use your Activity as context.
I have Android application and own Application derived class holding some internal data.
Among other there are some string fields. The problem is that if I put the application in foreground, work on other application, switch back to my app again, the app may be restarted because it got killed by system. Unfortunatelly the Application object seems not to be created again because the onCreate method of application object doesn't get called and all fields are set to null. My Activity gets recreated but all Application's object fields are null. When is the Application.onCreate method called? How to handle it?
there is no onCreate that you can register to.in later API's there's a way to register to the Activity lifecycle functions. and then you can do what ever you want.
basically, what you should do is use SharedPrefrences for storing information.
what I would do is:
class MyApp extends Application {
private static String someResource = null;
public static String getSomeResource(Context context) {
if(someResource == null) {
SharedPrefrences prefs = (SharedPrefrences)
context.getSystemService(Context.SHARED_PREFRENCES);
someResource = prefs.getString(SOME_RESOURCE, null);
}
return someResource;
}
Application onCreate() will called only for one time during its life-cycle, i.e.. only when application is started.
As suggested by thepoosh below answer is valid ,if your application is killed,still the data is saved in shared preference.
I've created a few minor apps for Android while learning. Being a PHP developer, it's a challenge to get used to it.
I'm especially wondering how I could define a couple of "general" functions in a separate class. Eg I have a function that checks if network connection is available, and if not, shows a dialog saying that the user should enable it. Currently, that function exists in several of my activities. Of course that seems strange - I suppose it would be more logical to define it once and include it in the activites where needed.
I tried putting it in a new class, and included that class in the original activity. But that failed since eg getBaseContext() is not accepted anymore.
I'm wondering how to go ahead. What should I be Google-ing for ? What is this mechanism called?
You need to create class with static methods. Like this
public class HelperUtils {
public static void checkNetworkConnection(Context ctx) {...}
}
Then you can call it from any place like this:
HelperUtils.checkNetworkConnection(this.getContext());
Assuming current class has Context.
You should read books on general OOP concepts where different type of methods are explained.
You can for example create a class - let's call it NetworkUtils. In this class you can create static method boolean isNetworkConnectionAvailable() and return true if is available and false otherwise. In this class you can create another static method void showNoConnectionDialog(Activity activity) - and in this method you create dialog starting with
public static void showNoConnectionDialog(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//setting message, listener etc. and finally
builder.create().show();
}
In your activity, where you want to check and handle network connection you should call:
if (!NetworkUtils.isConnectionAvailable(getApplicationContext())) {
NetworkUtils.showNoConnectionDialog(YourActivityClassName.this)
}
I guess this should work.