I'm having a problem starting an activity in a downloaded feature module when it's published to the play store. It always crashes on setContentView() in the downloaded modules activity.
java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx/xxxActivity}: android.content.res.Resources$NotFoundException: Resource ID #0x7e080000
Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x7e080000
at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:227)
at android.content.res.Resources.loadXmlResourceParser(Resources.java:2149)
at android.content.res.Resources.getLayout(Resources.java:1158)
at android.view.LayoutInflater.inflate(LayoutInflater.java:421)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
The really strange part is that if I publish a new version of the app (only change is versionCode) to play store and update the app everything works perfectly.
When I uninstall the app and install it again the crash returns.
my Application is inheriting SplitCompatApplication() and just to be sure I've since tried to add:
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
SplitCompat.install(this)
}
to the activty in the feature module and disabled proguard to make sure nothing is removed during minify
My SplitInstallStateUpdatedListener
private val listener = SplitInstallStateUpdatedListener { state ->
val multiInstall = state.moduleNames().size > 1
state.moduleNames().forEach { name ->
// Handle changes in state.
when (state.status()) {
SplitInstallSessionStatus.DOWNLOADING -> {
// In order to see this, the application has to be uploaded to the Play Store.
displayLoadingState(state, "Laddar ner $name")
}
SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> {
/*
This may occur when attempting to download a sufficiently large module.
In order to see this, the application has to be uploaded to the Play Store.
Then features can be requested until the confirmation path is triggered.
*/
startIntentSender(state.resolutionIntent()?.intentSender, null, 0, 0, 0)
}
SplitInstallSessionStatus.INSTALLED -> {
if(toInstall.isNotEmpty() && toInstall.contains(name)) {
toInstall.remove(name)
}
if(toInstall.isEmpty()) {
// Updates the app’s context with the code and resources of the
// installed module. (should only be for instant apps but tried it anyway, no change)
SplitInstallHelper.updateAppInfo(applicationContext)
Handler().post {
viewModel.goToOverview()
}
}
}
SplitInstallSessionStatus.INSTALLING -> displayLoadingState(state, "Installerar $name")
SplitInstallSessionStatus.FAILED -> {
toastAndLog("Error: ${state.errorCode()} for module ${state.moduleNames()}")
}
}
}
}
This code downloads modules depending on user claims and starts an activity in the base app
The downloaded modules activity is then started from a BottomSheetDialogFragment like this:
xxx.setOnClickListener(view -> {
Intent intent = new Intent();
String packageName = Constants.MODULE_BASEPACKAGE + "." + Constants.MODULE_XXXXX;
intent.setClassName(getActivity().getPackageName(),packageName + ".XxxxxActivity" );
ParcelUuid parcelUuid = new ParcelUuid(UUID.randomUUID());
intent.putExtra("uuid", parcelUuid);
startActivity(intent);
dismiss();
});
I'm all out of ideas about what to try next. It seems like it's something that doesn't update the resource list until an update is installed and a restart of the app is not enough, or am I just missing something simple?
You can always access the resources from the main project inside the dynamic module, so you could just put your resources for the dynamic module in the main app, and then use the R.java from the main App.
However, the proper way to open these resources is to use SplitCompat.install(this) inside the dynamic delivered activity
This seems to have been a bug in com.android.tools.build:gradle:3.2.1
When I upgraded to 3.3.0 the problem resolved itself.
Hopefully it might help someone else who has this problem...
I had an exactly same problem; fresh install crashes with Resources$NotFoundException, but subsequent upgrade works OK (the dynamic module is not downloaded again). But my case was slightly different, because instead of starting an Activity in the dynamic module, I wanted to load a Fragment through Navigation. In that case, I should have just navigated and let Navigation do its thing without manually checking the module was loaded or not (refer to https://developer.android.com/guide/navigation/navigation-dynamic for more info).
// Just navigate without calling splitInstallManager.installedModules.contains()
findNavController().navigate(DynamicDeliveryDirections.actionDynamicFragment())
If you want to start an Activity, you do need to check whether the module is loaded or not, as you are already doing. I suggest you take a look at Google's example, which does exactly what you are trying to do.
https://codelabs.developers.google.com/codelabs/on-demand-dynamic-delivery/index.html?index=..%2F..index#1
As for my case, I had to make sure the package names were correct. For example, if the main module's package name is com.example.foo and dynamic module is com.example.foo.dynamic_activity, then starting the Activity in the dynamic module would look like the following.
Intent().setClassName(
"com.example.foo",
"com.example.foo.dynamic_activity.DynamicActivity"
).also {
startActivity(it)
}
I don't know why it works, but for me using AppCompatActivity solves this problem
Related
I have small code which makes sure a url is always opened in browser. It is working so far.
Recently, I got message from Google Play Console stating:
If your app requires the QUERY_ALL_PACKAGES permission, you need to
submit the declaration form in Play Console by July 12
I have checked and verified, we don't use QUERY_ALL_PACKAGES in my app. However, we use Explicit Intents and resolveActivity in few places. One of the code (I took it from this place) is:
override fun openInBrowser(url: String) {
val dummyIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://"))
val resolveInfo = context.packageManager.resolveActivity(dummyIntent, PackageManager.MATCH_DEFAULT_ONLY)
val browserPackageName = resolveInfo?.activityInfo?.packageName
val realIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (browserPackageName != null && browserPackageName.isNotBlank() && browserPackageName != "android") {
realIntent.setPackage(browserPackageName)
}
startActivity(context, realIntent, null)
}
I am not sure if it falls under the category of querying a package. I have gone through this link and as per my understanding I don't need to add either package under queries tag or QUERY_ALL_PACKAGES. I just want to validate my understanding.
The limited app visibility affects the return results of methods that give information about other apps, such as queryIntentActivities(), getPackageInfo(), and getInstalledApplications(). The limited visibility also affects explicit interactions with other apps, such as starting another app's service.
Some packages are still visible automatically. Your app can always see these packages in its queries for other installed apps. To view other packages, declare your app's need for increased package visibility using the element. The use cases page provides examples for common app interaction scenarios.
I have received this project from the previous developer, and he said everything is working fine on his end. But on my end, when I try to sync the project I get a lot of errors, and I don't know where to even start. One of them is this:
Here's that code:
private fun setupScrollDirection() {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID
excluded_vertical_fastscroller.isHorizontal = false
excluded_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
excluded_vertical_fastscroller.allowBubbleDisplay = config.showInfoBubble
excluded_vertical_fastscroller.setViews(manage_folders_list) {
excluded_vertical_fastscroller.updateBubbleText(getBubbleTextItem(it))
}
}
I have a lot more errors of the same type, like this one for example:
Please have a call with the other developer and ask him/her to share his screen on call.
When the screen is being shared, ask the developer to Ctrl+ Click on beGoneIf variable/function and then check where the beGoneIf is defined in the project. This way you can check whether the required library/dependencies used by other developer is present in your local project or not.
How can I go to recent apps menu and How to select a particular app from the recents by using Espresso Android Instrumentation Test
Since selecting app from recent apps menu, it need controller over device. I think it can't be done with Espresso alone.
But you can achieve this by using android uiautomator.
fun selectAppFromRecentApps(appTitle:String){
mDevice.pressHome()
mDevice.pressRecentApps()
var uiSelector = UiSelector().className("android.widget.ScrollView") //scroll view listing all recent apps
var count = mDevice.findObject(uiSelector).childCount
for (i in 0 until count step 1) {
val child = UiScrollable(uiSelector.childSelector(UiSelector().resourceId("com.android.systemui:id/task_view_bar").instance(i))).getChild(UiSelector().resourceId("com.android.systemui:id/title")) // app framelayout
val text = child.text
if (text == appTitle) {
child.click()
break
}
if(i==count-1){
throw RuntimeException("App : "+ appTitle +" not found in
recent apps")
}
}
}
Unfortunately VIGNESHs answer did not work for me. I expect different devices to have different implementations of the overview view, or probably it's the Android version.
Anyway the following worked for me on different devices:
getInstrumentation().waitForIdleSync();
mDevice.pressHome();
mDevice.pressRecentApps();
mDevice.waitForIdle();
// If the application is listed, there needs to be an element with a content description
// containing the package name
if (!mDevice.wait(Until.hasObject(By.descStartsWith(getTargetContext().getPackageName())), 1000))
{
fail("Overview did not open");
}
// As the app to be tested was the last one opened, we can simply press the button again.
mDevice.pressRecentApps();
// Wait until the activity under test is back.
// If you skip that, you might be to early, doing further tests.
mDevice.wait(Until.hasObject(By.pkg(getTargetContext().getPackageName())), 1000);
getInstrumentation().waitForIdleSync();
I am working on an app with Appcelerator.
Appcelerator uses nearly the same kind of require.js like node.js does.
Now I want to implement a feature that logges out the current user and does not leave any trace.
The most simple way would be to restart the app, but Appcelerator and especially Apple does not support this.
So i have to open the login window and clean all the data that leaves a trace to the old user.
The easiest way would be to dereference one of the main nodes in the require chain leaving all the data dereferenced and garbage collected.
I know there is a way (as mentioned here) to do that in node:
/**
* Removes a module from the cache
*/
function purgeCache(moduleName) {
// Traverse the cache looking for the files
// loaded by the specified module name
searchCache(moduleName, function (mod) {
delete require.cache[mod.id];
});
// Remove cached paths to the module.
// Thanks to #bentael for pointing this out.
Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
if (cacheKey.indexOf(moduleName)>0) {
delete module.constructor._pathCache[cacheKey];
}
});
};
/**
* Traverses the cache to search for all the cached
* files of the specified module name
*/
function searchCache(moduleName, callback) {
// Resolve the module identified by the specified name
var mod = require.resolve(moduleName);
// Check if the module has been resolved and found within
// the cache
if (mod && ((mod = require.cache[mod]) !== undefined)) {
// Recursively go over the results
(function traverse(mod) {
// Go over each of the module's children and
// traverse them
mod.children.forEach(function (child) {
traverse(child);
});
// Call the specified callback providing the
// found cached module
callback(mod);
}(mod));
}
};
So I tried to read out the require-cache in Appcelerator with:console.log(require, "-" ,require.cache); with an output like: <KrollCallback: 0x79f6fe50> - <null>
So now my questions:
Is there a way to reach the require-cache in Appcelerator?
Do you know a way to clean up a big Appcelerator-App?
Since it is possible to wirte native Modules for Appcelerator:
Do you know a way to clean up a big Android App?
Do you know a way to clean up a big iOS App?
Thank you very much
In my application I make use of two build flavours / build variants. After switching to two build flavours, a bug was introduced in my application. I have now discovered the reason for this bug, but I am unable to find a solution.
The situation:
In my MainActivity class, I have a function that checks if a file exists - it is very straightforward;
public boolean fileExists(String filename) {
File file = null;
file = this.getApplicationContext().getFileStreamPath(filename);
return file.exists();
}
Using the debugger, location of the file is reported as: /data/data/foo.bar.appname.buildflavour/files/filename
In another class, I try to write to this same location;
outputStream = getActivity().getApplicationContext().openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write("test");
outputStream.close();
However, when I print the following line in front of the outputStream getActivity().getApplicationContext() - context is reported as; com.foo.bar.appname#14fcdd18. Therefore, I believe that these two classes are trying to save / retrieve a file in different locations. Any ideas on how I can make sure that the application is writing the file in the correct build flavour location? Thank you in advance!
You need to use different application id.
Form official site Configuring Gradle Builds
When using build variants, the build system enables you to uniquely identify different packages for each product flavors and build types.
productFlavors {
pro {
applicationId = "com.example.my.pkg.pro"
}
free {
applicationId = "com.example.my.pkg.free"
}
}
Eventually I was able to find the answer to my question. Initially I was calling a function in MainActivity onCreate, but this function was no longer being called because the buildFlavors redirected directly to one of my application fragments.
To anyone coming here with a similar problem: verify that your Flavors call the necessary functions and classes.