I have got an Activity class by:
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
String activityClassName = launchIntent.getComponent().getClassName();
Class<?> activityClazz = Class.forName(activityClassName);
Is it possible to create an instance of this Activity by using the activityClazz ? If so, how?
(My code is in a independent java class. Not in activity or service. )
Technically, you can create an instance of an Activity like this. However, this instance would be useless because its underlying Context would not have been set up.
The rule is that you should never ever create instances of Android components (Activity, Service, BroadcastReceiver, Provider) yourself (using the new keyword or other means). These classes should only ever be created by the Android framework, because Android sets up the underlying Context for these objects and also manages the lifecycle.
In short, your architecture is flawed if you need to create an instance of an Activity like this.
Class.forName() needs the fully qualified name - that is, the name of the package the class is contained in, plus the simple name of the class itself.
Assuming the package containing the class is called com.your.package, the code would have to be
String className = "com.your.package.Tab3"; // Change here
Object obj= null;
try {
obj= Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
yes. you can get the activity context by using below line of code
Activity activity = (Activity) getContext();
I have tried to trace through the code to see how addJavascriptInterface() on WebView is implemented, but it dives into native code, which basically cripples my ability to grok what is going on.
Specifically, I am trying to determine if the JNI(?) means by which addJavascriptInterface() arranges to call back into Java code relies upon getClass() as part of a reflection strategy, to map method references in JavaScript source to the implementations in Java. I would assume that it has to, and maybe I am searching in the wrong place, but I am not seeing it.
Can anyone point me to the code where the injected Java objects are used, so we can see how that is implemented?
Thanks!
UPDATE
To clarify, I mean using getClass() on the object passed to addJavascriptInterface().
The code that I think you're after is found in external/webkit/Source/WebCore/bridge/jni/. There are two main subdirectories there, jsc and v8 representing the two Javascript engines Android has used. Since V8 is the engine that's been used most recently and for some time, we'll stick with that.
I'm assuming you were able to successfully trace the Java side of the code to get from WebView.addJavascriptInterface() down to BrowserFrame.nativeAddJavaScriptInterface(), I'll leave those details out. The native side is picked up by AddJavaScriptInterface() in external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp, where the Java object passed in by the application is finally bound to the WebKit frame with bindToWindowObject().
I am trying to determine if the JNI means by which addJavascriptInterface() arranges to call back into Java code relies upon getClass() as part of a reflection strategy
The short answer is yes. They use a lot of wrappers around traditional JNI code, but if you look inside them the accessors on the JNIEnv for doing reflection are present. The wrappers they've created in V8 are:
external/webkit/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp
external/webkit/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp
external/webkit/Source/WebCore/bridge/jni/v8/JavaMethodJobjectV8.cpp
Going back to WebCoreFrameBridge.cpp, before that object the application passed in is bound, the jobject originally handed into the native code via JNI is wrapped in a JavaInstance class, and then converted to an NPObject, which is the final object bound to WebKit. The source for the V8 NPObject is at:
external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
We can see in the NPObject implementation that the calls always extract the JavaInstance back out and call methods there. If you look at examples like JavaNPObjectHasMethod() or JavaNPObjectInvoke, you'll notice the following line appear frequently:
instance->getClass()->methodsNamed(name)
This returns the JavaClass wrapper they've created, but if you look into the JavaClassJobjectV8 constructor and associated methods you'll see those familiar reflection calls to the Java object using the JNIEnv (including the actual JNI getClass() call into Dalvik).
So when a method is called by the bound WebKit frame, it finds the associated NPObject, which extracts its JavaInstance wrapper, which in turn uses JNI reflection to get access to the Java methods. The chain of custody here is a little harder to follow, so let me know if what's already provided is sufficient to answer your questions.
Here is what I got:
WebView wv = ...;
wv.addJavascriptInterface(object, name);
this goes to:
public void addJavascriptInterface(Object object, String name) {
checkThread();
mProvider.addJavascriptInterface(object, name);
}
mProvider is an interface of type WebViewProvider as it is declared in in WebView class:
//-------------------------------------------------------------------------
// Private internal stuff
//-------------------------------------------------------------------------
private WebViewProvider mProvider;
The only method I can see that instantiates it is ensureProviderCreated():
private void ensureProviderCreated() {
checkThread();
if (mProvider == null) {
// As this can get called during the base class constructor chain, pass the minimum
// number of dependencies here; the rest are deferred to init().
mProvider = getFactory().createWebView(this, new PrivateAccess());
}
}
getFactory() is implemented as:
private static synchronized WebViewFactoryProvider getFactory() {
return WebViewFactory.getProvider();
}
getProvider() is implemented as:
static synchronized WebViewFactoryProvider getProvider() {
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebViewClassic internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
sProviderInstance = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY);
if (sProviderInstance == null) {
if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage");
sProviderInstance = new WebViewClassic.Factory();
}
return sProviderInstance;
}
getFactoryByName() is implemented as:
private static WebViewFactoryProvider getFactoryByName(String providerName) {
try {
if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName);
Class<?> c = Class.forName(providerName);
if (DEBUG) Log.v(LOGTAG, "instantiating factory");
return (WebViewFactoryProvider) c.newInstance();
} catch (ClassNotFoundException e) {
Log.e(LOGTAG, "error loading " + providerName, e);
} catch (IllegalAccessException e) {
Log.e(LOGTAG, "error loading " + providerName, e);
} catch (InstantiationException e) {
Log.e(LOGTAG, "error loading " + providerName, e);
}
return null;
}
and here is where it uses Reflection. If an exception occurs during instantiating the custom class, WebViewClassic.Factory() will be used instead. Here is how it is implemented:
static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics {
#Override
public String findAddress(String addr) {
return WebViewClassic.findAddress(addr);
}
#Override
public void setPlatformNotificationsEnabled(boolean enable) {
if (enable) {
WebViewClassic.enablePlatformNotifications();
} else {
WebViewClassic.disablePlatformNotifications();
}
}
#Override
public Statics getStatics() { return this; }
#Override
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
return new WebViewClassic(webView, privateAccess);
}
#Override
public GeolocationPermissions getGeolocationPermissions() {
return GeolocationPermissionsClassic.getInstance();
}
#Override
public CookieManager getCookieManager() {
return CookieManagerClassic.getInstance();
}
#Override
public WebIconDatabase getWebIconDatabase() {
return WebIconDatabaseClassic.getInstance();
}
#Override
public WebStorage getWebStorage() {
return WebStorageClassic.getInstance();
}
#Override
public WebViewDatabase getWebViewDatabase(Context context) {
return WebViewDatabaseClassic.getInstance(context);
}
}
Now go back to mProvider = getFactory().createWebView(this, new PrivateAccess()); where getFactory() is either the custom class (by reflection) or WebViewClassic.Factory.
WebViewClassic.Factory#createWebView() returns WebViewClassic which is a sub-type of mProvider's type.
WebViewClassic#addJavascriptInterface is implemented as:
/**
* See {#link WebView#addJavascriptInterface(Object, String)}
*/
#Override
public void addJavascriptInterface(Object object, String name) {
if (object == null) {
return;
}
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mObject = object;
arg.mInterfaceName = name;
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
}
I think, this is what you are looking for :)
This is more of a comment than an answer, but I can't add a stacktrace in comments. So here it goes:
When setting a breakpoint in an Object that servers as a JavaScript Interface implementation, this is a sample stack-trace I get:
16> WebViewCoreThread#830034675584, prio=5, in group 'main', status: 'RUNNING'
at com.mediaarc.player.books.model.pagesource.service.EPubPageSourceService$JS.JSReady(EPubPageSourceService.java:1752)
at android.webkit.JWebCoreJavaBridge.nativeServiceFuncPtrQueue(JWebCoreJavaBridge.java:-1)
at android.webkit.JWebCoreJavaBridge.nativeServiceFuncPtrQueue(JWebCoreJavaBridge.java:-1)
at android.webkit.JWebCoreJavaBridge.handleMessage(JWebCoreJavaBridge.java:113)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:814)
at java.lang.Thread.run(Thread.java:841)
It starts in Java (Thread.run --> handleMessage). Then it disappears into Native code (nativeServiceFuncPtrQueue) and it comes out again in Java (nativeServiceFuncPtrQueue --> JSReady).
This stack is from a Nexus 10 running 4.3.
There is something going on in the Native Layer that moves the execution from within a call to nativeServiceFuncPtrQueue directly to the Java method of the JavaScriptInterface instance in Java.
Nowadays, the JavaScriptInterface need to annotate each method that it publishes to JavaScript (#JavaScriptInterface method annotation). Maybe this generates some JNI bridges on the fly calling from Native into Java.
I wonder how this stack-trace would have looked like on an older device where the #JavaScriptInterface annotations were not necessary.
from Understanding Android's webview addjavascriptinterface : "The method WebView.addJavascriptInterface sends a message to an instance of WebViewCore:
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); In WebViewCore.java there are a bunch of overloaded methods called sendMessage , but we don't really need to know which exactly is being called, since they do pretty much the same thing. There's even a nice comment to give us a hint that we're in the right place! All of them are delegating to an instance of EventHub which is some inner class. This method turns out to be synchronized, and is sending a message to an instance of Handler, which is a good indication that this is probably running in another thread, but for completeness sake, let's find out!
That Handler is instantiated in EventHub.transferMessages which is called from WebViewCore.initialize . There are a few more hops here, but eventually I found out that this is called from run in WebCoreThread (subclass of Runnable), which is instantiated along with a new Thread right here ." instantiated along with a new Thread right here ."
synchronized (WebViewCore.class) {
if (sWebCoreHandler == null) {
// Create a global thread and start it.
Thread t = new Thread(new WebCoreThread());
t.setName(THREAD_NAME);
t.start();
try {
WebViewCore.class.wait();
} catch (InterruptedException e) {
Log.e(LOGTAG, "Caught exception while waiting for thread " +
"creation.");
Log.e(LOGTAG, Log.getStackTraceString(e));
}
}
}
In other words, this could be the chain of calls in my opinion:
android.webkit.WebViewClassic
4159 #Override
4160 public void More ...addJavascriptInterface(Object object, String name) {
4161
4162 if (object == null) {
4163 return;
4164 }
4165 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
4166
4167 arg.mObject = object;
4168 arg.mInterfaceName = name;
4169
4170 // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
4171 // methods that are accessible from JS.
4172 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
4173 arg.mRequireAnnotation = true;
4174 } else {
4175 arg.mRequireAnnotation = false;
4176 }
4177 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
4178 }
android.webkit.WebViewCore
static class JSInterfaceData {
827 Object mObject;
828 String mInterfaceName;
829 boolean mRequireAnnotation;
830 }
java.lang.Object
37 public class Object {
38
39 private static native void registerNatives();
40 static {
41 registerNatives();
42 }
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class. The actual result type is Class where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
Returns: The Class object that represents the runtime class of this object. See also: The Java Language Specification, Third Edition (15.8.2 Class Literals)
64
65 public final native Class<?> getClass();
From a Dalvik's perspective I think you are just registering a JNI callback via findClass like this from JNIHelp.c :
/*
* Register native JNI-callable methods.
*
* "className" looks like "java/lang/String".
*/
int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
LOGV("Registering %s natives\n", className);
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s', aborting\n",
className);
abort();
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s', aborting\n", className);
abort();
}
(*env)->DeleteLocalRef(env, clazz);
return 0;
}
In conclusion my idea is derived from Native Libraries:
//Get jclass with env->FindClass
so maybe FindClass could be used instead of getClass...
is it safe to write such compatible code on Android?
if (Build.os.SDK_INT >= 11) {
newClass instance = new newClass();
....
}
else {
oldClass instance = new oldClass();
....
}
one of my colleagues argue with me that ClassNotFoundException might be thrown up when running the above code since ClassLoader is attempting to load newClass on an android os device which is below android 11. But I've tried couple times, and didn't see this happen.
After googling around for couple hours, I didn't find any information on how and when android default classLoader loads a specific class.
You should check the compatability like the following... It gives you more accurate than the above:
private static int currentApi = 0;
public static int getApiLevel() {
if (currentApi > 0) {
return currentApi;
}
if (android.os.Build.VERSION.SDK.equalsIgnoreCase("3")) {
currentApi = 3;
} else {
try {
Field f = android.os.Build.VERSION.class.getDeclaredField("SDK_INT");
currentApi = (Integer) f.get(null);
} catch (Exception e) {
return 0;
}
}
return currentApi;
}
you can alway use reflection to check if the class exists:
try {
Class.forName("yourclass")
} catch (ClassNotFoundExecption) {
oldClass instance = new oldClass();
}
Yes, this is safe to do on recent versions of Android. I want to say froyo and above, but it may have been even earlier than that. I don't recall for sure.
What happens is that dalvik performs a verification pass on the dex file at install time. For any classes/methods/fields that it can't resolve, it replaces those accesses with an instruction that throws a VerifyError.
In your example, if that code got loaded on, e.g. api 10, newClass instance = new newClass() would conceptually be replaced with throw new VerifYError(). So as long as that branch never gets executed at runtime, everything is good.
Short answer - don't do it.
Most VMs only load a class when it is absolutely needed. However a class loader is allowed to cache binary representation of classes beforehand[1].
Class loaders are allowed to cache binary representations of types,
load types early in anticipation of eventual use, or load types
together in related groups.
[1] - http://www.artima.com/insidejvm/ed2/lifetype2.html
[2] - http://developer.android.com/tools/extras/support-library.html
Edit - Have you checked if the class you need is available as part of the android support package ? [2]
I am on Android 2.2 SDK and could not get my static block inside MultiUserChat class to execute. I have tried to force load it as
try
{
String qual = MultiUserChat.class.getName();
ClassLoader.getSystemClassLoader().loadClass(qual);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
and it always hits the catch block. 'qual' gets the valid name of the class... what can it be?
Your app includes both framework classes like ArrayList and Activity, plus application classes like FlashlightActivity. The framework classes are loaded by the system class loader (and also the bootstrap class loadeR); the application classes are loaded by the application class loader.
The system class loader can only see the system classes. It doesn't know the application class path and it can't be used to load application classes. You need to use the application class loader to do that. The easiest way to get a reference to the application class loader is via an application class:
try {
String qual = MultiUserChat.class.getName();
MyActivity.class.getClassLoader().loadClass(qual);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
I am trying to do something similar to this stackoverflow posting. What I want to do is to read the definition of an activity or service from the SD card. To avoid manifest permission issues, I create a shell version of this activity in the .apk, but try to replace it with an activity of the same name residing on the SD card at run time. Unfortunately, I am able to load the activity class definition from the SD card using DexClassLoader, but the original class definition is the one that is executed. Is there a way to specify that the new class definition replaces the old one, or any suggestions on avoiding the manifest permission issues without actually providing the needed activity in the package? The code sample:
ClassLoader cl = new DexClassLoader("/sdcard/mypath/My.apk",
getFilesDir().getAbsolutePath(),
null,
MainActivity.class.getClassLoader());
try {
Class<?> c = cl.loadClass("com.android.my.path.to.a.loaded.activity");
Intent i = new Intent(getBaseContext(), c);
startActivity(i);
}
catch (Exception e) {
Intead of launching the com.android.my.path.to.a.loaded.activity specified in /sdcard/mypath/My.apk, it launches the activity statically loaded into the project.
Starting an Activity through DexClassLoader will be very tricky, because you didn't install the APK, and so there is nothing to handle your Intent.
You should have the same activity class in your platform APK and declare it in AndroidManifest.xml. Then, you should change the current ClassLoader to your desired DexClassLoader. As a result, it will start your plugin APK. (Note: "platform APK" refers to the app that is already installed in the phone, whereas "plugin APK" refers to the apk file saved in your SD card.)
The platform's application should look something like this:
public static ClassLoader ORIGINAL_LOADER;
public static ClassLoader CUSTOM_LOADER = null;
#Override
public void onCreate() {
super.onCreate();
try {
Context mBase = new Smith<Context>(this, "mBase").get();
Object mPackageInfo = new Smith<Object>(mBase, "mPackageInfo")
.get();
//get Application classLoader
Smith<ClassLoader> sClassLoader = new Smith<ClassLoader>(
mPackageInfo, "mClassLoader");
ClassLoader mClassLoader = sClassLoader.get();
ORIGINAL_LOADER = mClassLoader;
MyClassLoader cl = new MyClassLoader(mClassLoader);
//chage current classLoader to MyClassLoader
sClassLoader.set(cl);
} catch (Exception e) {
e.printStackTrace();
}
}
class MyClassLoader extends ClassLoader {
public MyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class<?> loadClass(String className)
throws ClassNotFoundException {
if (CUSTOM_LOADER != null) {
try {
Class<?> c = CUSTOM_LOADER.loadClass(className);
if (c != null)
return c;
} catch (ClassNotFoundException e) {
}
}
return super.loadClass(className);
}
}
For more code, you can visit https://github.com/Rookery/AndroidDynamicLoader
I'm reading the Android source code in order to find a more elegant method to implement this feature. If you have any idea, feel free to contact me.