Android Question #AdditionalJar problem

wes58

Active Member
Licensed User
Longtime User
I wanted to add a ListView (or StackView) widget to my application. I was thinking about creating a library but with my Java knowledge it was too hard.
The other option that I thought about was to create an Eclipse project using compiled B4A java source code, and add to it ListView widget java files. But because I use quite a few libraries, after building the application in Eclipse it didn't run - coming with some errors of "class not found" etc.
So, I thought that maybe creating a jar with the ListView widget and including this jar in the B4A project using #AdditionalJar option would solve the problem.
I created a simple application in which I added an external Jar using #AdditionalJar option. I am not doing anything with this jar in main activity at this stage.
After building and running the application I get errors (sometimes) when I try to add the widget on the Home screen:
B4X:
java.lang.RuntimeException: Unable to instantiate receiver com.ListWidget.WidgetProvider: java.lang.ClassNotFoundException: Didn't find class "com.ListWidget.WidgetProvider" on path: DexPathList[[zip file "/data/app/my.test.List-2.apk"],nativeLibraryDirectories=[/data/app-lib/my.test.List-2, /system/lib]]
    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2388)
    at android.app.ActivityThread.access$1700(ActivityThread.java:135)
....
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.ListWidget.WidgetProvider" on path: DexPathList[[zip file "/data/app/my.test.List-2.apk"],nativeLibraryDirectories=[/data/app-lib/my.test.List-2, /system/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    ... 10 more
The problem is that I build an application again and it will be working OK. It happens randomly. I can build it 5 times and out of 5 times I can get one or two builds working. I don't change anything in the code before each build.
To find out what the problem was, I decompiled the working and not working application and noticed that in not working application the additional jar wasn't included in the built application (see attached photo).
I then tried the same thing with another application and including a jar file with StackView widget. And the same thing happens.

Any suggestions why this happens? Is it because I am not doing anything with this jar in main activity, so it is not included in the built application? But then again, why sometimes it is included?
 

Attachments

  • ListWidget.png
    ListWidget.png
    36.1 KB · Views: 336

wes58

Active Member
Licensed User
Longtime User
Try to do a Tools - Clean Project before you compile your app.
Also make sure to compile your app in Release mode.

Note that I never encountered such issue where an #AdditionalJar attribute is ignored.

Thanks Erel.
I don't know what happened on that day. If it didn't happen so many times, I would have thought that I was doing something wrong. But now it seems to be working fine. Maybe the computer needed an Easter break too!

It looks like this approach to collections widgets may work. Hopefully I will be able to get data from and to the widget from the application. I am not sure if I will be able to do it with JavaObject library. That's the next step.

The only issue I have is when I add the widget to the home screen and then update the application, the application is not responding.
If I remove the widget from the home screen and then update an application everything works fine.
I wonder, if it is because the application package is different from the jar package. Jar package is a (RemoteViewsFactory) RemoteViews service. Maybe during application update the service is not stopped?

Edit:
I did some more checking to find out what cause of the problem with application not responding and part of the log is below.
B4X:
Start proc com.ListWidget for broadcast com.ListWidget/.WidgetProvider: pid=1961 uid=10148 gids={50148}
onReceive Action android.appwidget.action.APPWIDGET_UPDATE
buildLayout appWidgetId 133
Exception Ljava/lang/NullPointerException; thrown while initializing Lcom/ListWidget/R$layout;

Calling main entry com.android.commands.am.Am
START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ListWidget/.main} from pid 1975
From the log, I think that the problem is the initializing widget layout resource (for widget service) which is started before main app.
The resources are generated in the R.java in jar using RGenerator2, for example:
B4X:
    public static final class layout {
        public static int widget_provider_layout = BA.applicationContext.getResources().getIdentifier("widget_provider_layout", "layout", BA.packageName);
        public static int widget_item = BA.applicationContext.getResources().getIdentifier("widget_item", "layout", BA.packageName);
    }
Do I get BA.applicationContext before starting main in application? Or is it because of that, that I got NullPointerException?
I can't move widget_provider.xml file (and widgetlayout.xml file) to the jar file because they are referenced in the Manifest which has to be in the B4A project.

How could I resolve this issue? Any suggestions?
 
Last edited:
Upvote 0

wes58

Active Member
Licensed User
Longtime User
It depends on your library architecture. Where is the intent receiver that handles the APPWIDGET_UPDATE? It should be in a B4A service.
Unfortunately it is not a library. It is an additional jar which has a (RemoteViewsFactory) RemoteViews.Service for collections widget in the jar. I don't know how to wrap this service to create a library. I posted it in the library development forum but I didn't get any help/suggestions how to do it. That's why I decided to use additional jar option.
It is probably not worth spending time on development of the library because the layout of the widget would depend on each personal design. Unless, we used a custom layout?
I found out that the issue I had was not only during update of the application but also after reboot of the phone.
One other option that I tried with resources, was to add R.java from the compiled B4A application to the Eclipse project for additional jar. Then create a jar. Delete a R.class from the jar and recompile the B4A application with this jar. If both application and the jar have the same package name it works fine (i.e. the location of the R.class is the same). For example widget package is com.myapp.listwidget, b4a app is com.myapp. and the location of the R.class is in com.myapp everything works fine.
It's a bit of a mess but it works.

The question I have for you is: If I don't add (or change resources) in the application, will the resource id's stay the same between several re-building of the application in B4A?
 
Upvote 0

wes58

Active Member
Licensed User
Longtime User
The ids are created by aapt tool. They are not guaranteed to stay the same.
Thanks Erel. It doesn't really matter if they change because they are stored in R.classes and accessed during the runtime.
 
Upvote 0

wes58

Active Member
Licensed User
Longtime User
Now, I got stackWidget working with its Service inside a jar (included as a additional Jar to the app) but I am not able to receive intents in my Application.
I have created a service in my application and I would like to receive the intents from the StackWidget service.
In the Manifest for the receiver for StackWidget I have the following:
B4X:
<receiver android:name="com.StackWidget.StackWidgetProvider">
            <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                 android:resource="@xml/stackwidgetinfo" />
          </receiver>
           <service android:name="com.StackWidget.StackWidgetService"
                android:permission="android.permission.BIND_REMOTEVIEWS"
                android:exported="true" />
This works fine for the widget.

Now, I would like to get the intents in another service in the same application (this service is B4A service). I tried different options but I can't receive intents from the widget.
In the log I have the following broadcasts sent to the StackWidget Service from StackWidgetProvider (AppWidgetProvider):
B4X:
On Receive Intent { act=android.appwidget.action.APPWIDGET_UPDATE flg=0x10 cmp=com.StackWidget/.StackWidgetProvider (has extras) }
On Receive (Click) Intent { act=com.StackWidget.TOAST_ACTION flg=0x10000010 cmp=com.StackWidget/.StackWidgetProvider bnds=[120,1021][442,1417] (has extras) }
I tried to add to the Manifest for the second (B4A) service:
B4X:
<service android:name=".testservice" android:permission="android.permission.BIND_REMOTEVIEWS">
         </service>
         <receiver android:name=".testservice$testservice_BR" >
              <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                 <action android:name="com.StackWidget.TOAST_ACTION" />
              </intent-filter>
        </receiver>
But this doesn't work.
Do I have to create a broadcast receiver in the service code?
Any idea what I am doing wrong?
 
Upvote 0

wes58

Active Member
Licensed User
Longtime User
I couldn't receive broadcasts in B4A service from RemoteViewsService.RemoteViewsFactory so the only way to get broadcasts in B4A service, was to create a broadcast intent in onReceive sub in StackWidgetProvider (AppWidgetProvider).
B4X:
    public void onReceive(Context context, Intent intent) {
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        if (intent.getAction().equals(TOAST_ACTION)) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
            int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);

            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("com.StackWidget.action.ACTION_TEST");
            broadcastIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
            broadcastIntent.putExtra("ViewTouched", viewIndex);
            context.sendBroadcast(broadcastIntent);
        ...
        ...
        }
    }
I thought I will post it, just in case that some else has similar problem.
 
Upvote 0
Top