Is it safe to write Android compatible code this way? - android

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]

Related

How to send data from Android to Flutter properly?

I'm trying to send data back and forth from Flutter to my native platform (in this case Android).
In order to keep some model consistency, I have generated the models for all platforms by using Protocol-Buffers.
When I try to pass data from Android to Flutter I'm not finding any way to do it without shenanigans like serializing to a handcrafted JSON.
There must be a way to use protobuf in order to do so, isn't it?
In order to give context, I have made a minimal app to try to solve this problem:
My Protocol Buffer
syntax = "proto3";
option java_package = "com.test.protobuf_test";
option java_outer_classname = "ProtoModel";
message SimplePerson {
int32 id= 1;
string name= 2;
}
From which I generate my model using:
protoc --java_out and protoc --dart_out
In Dart I get my class
class SimplePerson extends $pb.GeneratedMessage {...}
And in Java
public final class ProtoModel {
...
public static final class SimplePerson extends
com.google.protobuf.GeneratedMessageV3 implements
SimplePersonOrBuilder {...}
}
From Android inside my method channel, I am trying to pass one or many ProtoModel.SimplePerson objects back to Dart.
No success so far.
How would you actually do it?
I'd expect it to be something like
In Java:
ProtoModel.SimplePerson person = ProtoModel.SimplePerson.newBuilder().setId(3).setName("Person Name").build();
result(person);
And in Dart:
var result = await platform.invokeMethod("generatePerson");
if(result is SimplePerson) {
print("Success!");
} else {
print("Failure!");
}
So far I'm only getting Failures or Exceptions.
Thanks!
your very close your using result but i have it working with result.success
when (call.method) {
"getPlatformVersion" -> result.success(getPlatformVersion().toByteArray())
}
private fun getPlatformVersion(): Models.Version {
return Models.Version.newBuilder().setVersionName("Android ${android.os.Build.VERSION.RELEASE}").build()
}
great example here https://www.freecodecamp.org/news/flutter-platform-channels-with-protobuf-e895e533dfb7/
EDIT didnt see how old this post was
I have to use this as Pigeon is sill early access, and although pigeon was generally harder to set up i do prefer it

Extent Report Issue Parallel testing

I have the following Reporting code:
public class Reporting {
private ExtentHtmlReporter extentHtmlReporter;
private static ThreadLocal<ExtentReports> extentReports = new ThreadLocal<>();
private static ThreadLocal<ExtentTest> extentTest = new ThreadLocal<>();
public synchronized ExtentTest createInstanceReport(String testCaseName) {
System.out.println(extentReports.get());
new File(Constants.userDir + "/Reports/").mkdirs();
// To generate report with name
extentHtmlReporter = new ExtentHtmlReporter(
Constants.userDir + "/Reports/" +
"ExecutionReport_" + new SimpleDateFormat(
Constants.date).format(new Date()) + ".html");
// Setting Document Title
extentHtmlReporter.config().setDocumentTitle("Demo");
// Setting Report Name
extentHtmlReporter.config().setReportName("Demo Automation");
// Setting Theme
extentHtmlReporter.config().setTheme(Theme.STANDARD);
// Setting Chart location
extentHtmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
// Setting Chart visibility
extentHtmlReporter.config().setChartVisibilityOnOpen(false);
// Setting Time stamp
extentHtmlReporter.config().setTimeStampFormat("yyyy-MM-dd HH:mm:ss");
// Setting append exist as true
extentHtmlReporter.setAppendExisting(true);
ExtentReports extentReports = new ExtentReports();
extentReports.attachReporter(extentHtmlReporter);
// Setting system info
extentReports.setSystemInfo("Name",
BaseTest.prop.getProperty(Constants.testerName));
extentReports.setSystemInfo("Environment",
BaseTest.prop.getProperty(Constants.environment));
extentReports.setSystemInfo("Browser",
BaseTest.prop.getProperty(Constants.browser));
Reporting.extentReports.set(extentReports); // Instead of using here extentReport thread like this, Can anyone suggest to use it directly
// Add test case name in report
ExtentTest extentTest = Reporting.extentTest.get();
extentTest = Reporting.extentReports.get().createTest(testCaseName);
Reporting.extentTest.set(extentTest);
// Assigning categories
extentTest.assignCategory(MultiFunction.getProp()
.getProperty(Constants.browser));
System.out.println(Reporting.extentReports.get());
System.out.println(Reporting.extentTest.get());
return extentTest;
}
public synchronized ExtentTest getExtentTest() {
return extentTest.get();
}
public synchronized ExtentReports getInstanceReport() {
return extentReports.get();
}
public synchronized void remove() {
extentReports.remove();
extentTest.remove();
}
}
I was trying parallel testing using TestNG (and will have to use Selenium grid and sauce in future). I execute 2 test cases then only one test case result is added in the report.
I have isolated the extentTest, extentReporter and WebDriver instances using threadPool.
Tried below with extentHtmlReporter instance:
1) Tried to make it static(no luck)
2) Tried to make it local (the same behaviour, getting only 1 test case result)
3) Tried as a non-static global variable ( no luck)
Could you suggest how to solve the above issue?
Please note: Only one report is generated. But when I tried to run parallel test cases in debug mode reports are generated for both the test case. I think because one test case gets over its killing some instance (when running in non-debug mode)
Also, I want to redesign the following place in my code:
For extentRpeort, I am using:
Reporting.extentReports.set(extentReports);
To add extentReport instance to my extentReport Thread.
Instead of adding like this I want to use it directly so as to reduce line of code.
If I understand correctly you have to generate Report from all executed TestNG cases.
However, from code which you shared, it is very visible that you will have some trouble with it. You are making a few critical mistakes and result are obvious:
For generating reports with TestNG I will suggest grabbing information about test execution from TestNG listener. Something like:
public final class TestNGListener extends TestListenerAdapter implements IInvokedMethodListener, ISuiteListener {
#Override
public void onStart(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_STARTED, context.getName()));
}
#Override
public void onFinish(ITestContext context) {
Logger.info(buildMessage(Logger.PREFIX_TEST_FINISHED, context.getName()));
}
#Override
public void onTestStart(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_STARTED, getMethodName(result)));
}
#Override
public void onTestSuccess(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_SUCCESS, getMethodName(result)));
processTestResult(result);
}
#Override
public void onTestFailure(ITestResult result) {
Logger.info(buildMessage(Logger.PREFIX_METHOD_FAILED, getMethodName(result)));
}
You can't do everything in one method! You broke Single Responsibility Principle. Your createInstanceReport() is doing all jobs (setting report details, set system info, attach an executed test case to report) at one place. You have to redesign this logic to some logical separate operations. After redesigning your problem with the next line:
Reporting.extentReports.set(extentReports)
Could successfully disappear.
You have to consider a case, why you need to use exactly Extent, Reports Version 3. TestNG has test reports from the box. They are poor but they are presented out of the box. If you want just to improve it a little bit you could use ReportNG over TestNG.
It is quite easy to configure: Configuring ReportNG with TestNG for HTML Reports.
It isn't maintained, but it makes TestNG reports really eye candy and understandable.
Anyway, my suggestion is to use TestNGListener for getting info about test cases execution. And read more about good programming practice.
Work with TestNG/jUnit (or other runner framework that you are using) listener, here is a good example how to do it.
Do not put everything in a single class.
https://www.swtestacademy.com/extent-reports-version-3-reporting-testng/
The issue was with the flushing of extent report instance.
I was using ThreadLocal for storing extent report instance and was flushing the wrong instance.

Using internal android classes

I want to use an android internal class, com.android.internal.widget.LockPatternUtils .
I searched for examples and I got this:
LockPatternUtils lpu = new LockPatternUtils(this.getContentResolver());
However, i cant import, autimport does not appear and manually doing
import com.android.internal.widget.LockPatternUtils
doesnt work.
How is this handled? I think there was something about creating an interface but not sure about it
EDIT: I got this:
boolean patternLocked = android.provider.Settings.System.getInt(
getContentResolver(),Settings.System.LOCK_PATTERN_ENABLED, 0)==1;
That works for the pattern lock, but i cant know about the pin or password lock
Use reflection to access the internal Android method getActivePasswordQuality (line 350 of LockPatternUtils.java) and compare the int it returns to the int for any of the DevicePolicyManager Constants:
protected boolean isSecure() {
String LOCK_PATTERN_UTILS = "com.android.internal.widget.LockPatternUtils";
try {
Class<?> lockPatternUtilsClass = Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils = lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
Method method = lockPatternUtilsClass.getMethod("getActivePasswordQuality");
int lockProtectionLevel = Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils)));
// Then check if lockProtectionLevel == DevicePolicyManager.TheConstantForWhicheverLevelOfProtectionYouWantToEnforce, and return true if the check passes, false if it fails
}
catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
How is this handled?
Those classes are only available as part of full firmware builds, not from Android SDK apps.
You can not use Android internal classes, they do not come with the public SDK.
You can build your own SDK exposing them more or less as they do here How do I build the Android SDK with hidden and internal APIs available?
This is not recommended because you will have no guarantees.

Android static variable different in service than from activity

I'm hoping someone can clear up whats going on in my code for me. I have a block of code reading and writing to my database which is run both from a service and from my activities. I need this block of code to be threadsafe regardless of if it is called from the service or the activities. As such, I took the class the block of code was in and made a static locking object to keep it threadsafe as you can see in the block of code below:
synchronized(AnalyticsMessages.sync_lock){
Log.v("mixpanel", "locking off of: "+AnalyticsMessages.sync_lock.toString());
String[] eventsData = mDbAdapter.generateDataString(table);
if (eventsData != null) {
String lastId = eventsData[0];
String rawMessage = eventsData[1];
HttpPoster poster = getPoster(mEndpointHost, mFallbackHost);
HttpPoster.PostResult eventsPosted = poster.postData(rawMessage, endpointUrl);
if (eventsPosted == HttpPoster.PostResult.SUCCEEDED) {
logAboutMessageToMixpanel("Posted to " + endpointUrl);
logAboutMessageToMixpanel("Sent Message\n" + rawMessage);
//Log.v("mixpanel", "Sent Message\n" + rawMessage);
mDbAdapter.cleanupEvents(lastId, table);
Log.v("mixpanel", "removing id: "+lastId);
}
else if (eventsPosted == HttpPoster.PostResult.FAILED_RECOVERABLE) {
// Try again later
if (!hasMessages(FLUSH_QUEUE)) {
sendEmptyMessageDelayed(FLUSH_QUEUE, mFlushInterval);
}
}
else { // give up, we have an unrecoverable failure.
mDbAdapter.cleanupEvents(lastId, table);
}
}
}
sync_lock is defined in AnalyticsMessages like this:
private static final Object sync_lock = new Object();
It was clearly not locking properly, hence the log with the toString. The log tells me that I somehow seem to have 2 different sync_lock variables (since it prints an address in memory). I should mention that AnalyticsMessages is instantiated like this:
public static AnalyticsMessages getInstance(Context messageContext) {
synchronized (sInstances) {
Context appContext = messageContext.getApplicationContext();
AnalyticsMessages ret;
if (! sInstances.containsKey(appContext)) {
if (MPConfig.DEBUG) Log.d(LOGTAG, "Constructing new AnalyticsMessages for Context " + appContext);
ret = new AnalyticsMessages(appContext);
sInstances.put(appContext, ret);
}
else {
if (MPConfig.DEBUG) Log.d(LOGTAG, "AnalyticsMessages for Context " + appContext + " already exists- returning");
ret = sInstances.get(appContext);
}
return ret;
}
}
What seems to be going on is that when the service passes itself to AnalyticsMessages.getInstance as the context, I end up with one sync_lock variable, but when one of my Activities passes itself as the context here, I end up with a different one? Does this imply that an android service uses a different class loader than the main process? Is there a workaround? I would think that this would actually be a relatively common problem? Am I totally off track in what's going on here? Thanks in advance!
it does seem that android:process was used for my background service
That's a common source of this specific difficulty. While, from a Java source standpoint, it's all one app, the fact that you are running two separate processes means that you have two separate object heaps, and so a global (static data member) in one heap will be distinct from the same-named global in the other heap.
Is there a good reason to have the service running in a different process than the rest of the app?
Usually not. It causes your app to consume more memory and more CPU time, and limits the ability of the various components (running in different processes) to interoperate, as you saw. I generally counsel against multiple processes. That being said, changing it will warrant thorough testing, particularly if you don't know why that technique was used in the first place.

Android In-app billing general questions

I know In-App billing is new in Android and I would like to use it, but the version requirements make me think twice whether it's worth the effort. I would appreciate any input from those who have implemented or worked with In App Billing in detail.
I still have 10% 1.5 users. In app billing requires at least 1.6 to work. Does that mean 1.5 users will crash immediately? If not, at what point does it fail? I don't want to write a bunch of hacky code to stay compatible with 1.5 users.
If user reinstalls the app, are their app purchases remembered?
At what point does it fail if you don't have the required Market version?
Thanks.
Regarding version support, you'll have write some extra code to detect the device OS version (see android.os.Build.VERSION) so make sure it will run on 1.5 devices. I strongly suggest isolating that code in its own class, and only instantiate that class after your version check. That way your code stays clean (not "hacky") and you don't accidentally reference a 1.6+ class from a class field. In my code, I have version test classes that look like this:
public class Android8 {
private static final String TAG = "Android8";
// public test variables
public static final boolean IS_V8;
public static final boolean AT_LEAST_V8;
private static final Object pimpl;
static {
int sdk_int = 0;
try {
Field field = Build.VERSION.class.getField( "SDK" );
String sdk_str = (String)field.get( null );
sdk_int = Integer.parseInt( sdk_str );
} catch( Throwable e ) {
}
IS_V8 = (sdk_int==8);
AT_LEAST_V8 = (sdk_int>=8);
if( AT_LEAST_V8 ) {
pimpl = new Implementation();
} else {
pimpl = null;
}
}
// Version safe interface
public static void Camera_setDisplayOrientation( Camera camera, int degrees ) {
if( AT_LEAST_V8 )
((Implementation)pimpl).Camera_setDisplayOrientation( camera, degrees );
}
// Will cause a verify error if loaded in a pre Android8 environment
private static final class Implementation {
public static void Camera_setDisplayOrientation( Camera camera, int degrees ) {
camera.setDisplayOrientation( degrees );
}
}
}
Question 2: NO, if items are UNMANAGED. Yes if they are.
That's the point with managed items, let's the Google's servers manage (remenber) the purchased items for this sort of cases.
(The "manage by user account" purchase type is useful if you are selling items such as game levels or application features. These items are not transient and usually need to be restored whenever a user reinstalls your application, wipes the data on their device, or installs your application on a new device.)
from: http://developer.android.com/guide/market/billing/billing_admin.html#billing-purchase-type

Categories

Resources