Can someone give me a working example of how to ignoring certain classes from LeakCanary?
I was looking at this example for ignoring certain classes from third party library in LeakCanary, but I couldn't figure out where to put this in my application. I put this in my Application class, but there are error from these variables and methods: isInAnalyzerProcess, enableDisplayLeakActivity, application, androidWatcher
public class DebugExampleApplication extends ExampleApplication {
protected RefWatcher installLeakCanary() {
if (isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
} else {
ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults().build();
enableDisplayLeakActivity(application);
ServiceHeapDumpListener heapDumpListener = new ServiceHeapDumpListener(application, DisplayLeakService.class);
final RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
public void onActivityDestroyed(Activity activity) {
if (activity instanceof ThirdPartyActivity) {
return;
}
refWatcher.watch(activity);
}
// ...
});
return refWatcher;
}
}
}
Thanks to CommonsWare, calling the methods and variables on LeakCanary works. Here is a complete example for ignoring certain References or Activities in LeakCanary. Look at the comments: IGNORE Rreferences and IGNORE Activities.
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import com.squareup.leakcanary.AndroidExcludedRefs;
import com.squareup.leakcanary.DisplayLeakService;
import com.squareup.leakcanary.ExcludedRefs;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import com.squareup.leakcanary.ServiceHeapDumpListener;
public class MyApplication extends Application {
// LeakCanary for memory leak detection
private RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) {
MyApplication application = (MyApplication) context.getApplicationContext();
return application.refWatcher;
}
#Override
public void onCreate() {
super.onCreate();
refWatcher = installLeakCanary();
}
/**
* Excluding known memory leaks from third party libraries
* #return
*/
protected RefWatcher installLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
} else {
// IGNORE References: Update or add reference class and context name in instanceField
ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults()
.instanceField("com.example.third.party.TheirClassOne", "context")
.instanceField("com.example.third.party.TheirClassTwo", "context")
.build();
LeakCanary.enableDisplayLeakActivity(this);
ServiceHeapDumpListener heapDumpListener = new ServiceHeapDumpListener(this, DisplayLeakService.class);
final RefWatcher refWatcher = LeakCanary.androidWatcher(this, heapDumpListener, excludedRefs);
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivityDestroyed(Activity activity) {
//IGNORE Activities: Update or add the class name here to ingore the memory leaks from those actvities
if (activity instanceof ThirdPartyOneActivity) return;
if (activity instanceof ThirdPartyTwoActivity) return;
if (activity instanceof ThirdPartyThreeActivity) return;
refWatcher.watch(activity);
}
#Override
public void onActivityResumed(Activity activity) {
}
});
return refWatcher;
}
}
}
In addtion to existing answer,
LeakCanary does watch(...) all Activity by default,
which you can disable like:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_watcher_auto_install">false</bool>
</resources>
See:
https://square.github.io/leakcanary/changelog/#configuring-retained-object-detection
Related
I am generating a plugin in Flutter that will integrate a native library *.aar and this library needs to be initialized in the Application class of android, since it will extend the Application class of the library. The problem I am having is that at no time does it seem that the Application class is used in the Flutter plugin, I have tried to create the application class and define it in the plugin manifest, but at no time does it seem to enter the onCreate. I do not see how to solve this situation, any indication is welcome.
My class Application:
public class App extends SDKApplication{
#Override
public void onCreate() {
super.onCreate();
Log.e("App", "onCreate");
}
}
My manifest
<application
android:name=".App">
</application>
and the plugin class:
public class SdkPlugin implements FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel channel;
private EMTingSDK sdk = EMTingSDK.getInstance();
private Context context;
public SdkPlugin(){
Log.e("PRUEBAS","onMethodCall");
new App();
}
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
context = flutterPluginBinding.getApplicationContext();
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), Constants.CHANNEL);
channel.setMethodCallHandler(this);
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull Result result) {
Log.e("PRUEBAS","onMethodCall");
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
}else {
result.notImplemented();
}
}
#Override
public void onDetachedFromEngine(#NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
P.D: I need to start it in the Application because the developers of the native library told us that it had to be spent in this way, then I will put a little more code regarding the application.
public class SDKApplication extends CompanyApplication implements ActivityLifecycleCallbacks {
public SDKApplication() {
}
public void onCreate() {
SDK.getInstance().setContext(this.getApplicationContext());
super.onCreate();
Log.i("InfoSDK", "SDKApplication - init from App ");
this.registerActivityLifecycleCallbacks(this);
}
public void onActivityCreated(Activity activity, Bundle bundle) {
SDK.getInstance().setHostActivity(activity);
}
public void onActivityStarted(Activity activity) {
}
public void onActivityResumed(Activity activity) {
SDK.getInstance().setHostActivity(activity);
}
public void onActivityPaused(Activity activity) {
}
public void onActivityStopped(Activity activity) {
}
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
public void onActivityDestroyed(Activity activity) {
}
}
And the Company Application is:
public class CompanyApplication extends Application {
public CompanyApplication () {
}
public void onCreate() {
super.onCreate();
RequestQueue mainQueue = null;
if (SDK.getInstance().isDevelopEnviroment()) {
mainQueue = Volley.newRequestQueue(this.getApplicationContext(), new HurlStack((UrlRewriter)null, this.newSslSocketFactory()));
} else {
mainQueue = Volley.newRequestQueue(this.getApplicationContext());
}
CompanyHttpClient.getInstance().setMainQueue(mainQueue);
}
}
I'm asking why after I placed the App Open Ads in the AppOpenManager and some other code added on MyApplication same article that Google Admob advices to use but I'm still confused, Why when I launch my app:
The Splash screen : Open First
Then the App Open Ads opens and stay running in the background while the MainActivity opens above the App Open Ads.
If you know how to solve this help me please...
Thanks in advance.
Here is the GIF about the app I was using for the test to show you App Open Ads
Try this:
gradle:
implementation 'com.google.android.gms:play-services-ads:20.1.0'
def lifecycle_version = "2.2.0"
def lifecycle_version1 = "2.3.1"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version1"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version1"
Manifests
android:name=".MyApplication"
AppOpenManager.java
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.appopen.AppOpenAd;
import java.util.Date;
import static androidx.lifecycle.Lifecycle.Event.ON_START;
public class AppOpenManager implements LifecycleObserver, Application.ActivityLifecycleCallbacks {
private long loadTime = 0;
private static final String LOG_TAG = "AppOpenManager";
private static String AD_UNIT_ID = "abc";
private AppOpenAd appOpenAd = null;
private Activity currentActivity;
private AppOpenAd.AppOpenAdLoadCallback loadCallback;
private static boolean isShowingAd = false;
private final MyApplication myApplication;
/** Constructor */
public AppOpenManager(MyApplication myApplication) {
this.myApplication = myApplication;
this.AD_UNIT_ID = myApplication.getString(R.string.app_open_id);
this.myApplication.registerActivityLifecycleCallbacks(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
/** LifecycleObserver methods */
#OnLifecycleEvent(ON_START)
public void onStart() {
showAdIfAvailable();
Log.d(LOG_TAG, "onStart");
}
/** Request an ad */
public void fetchAd() {
// Have unused ad, no need to fetch another.
if (isAdAvailable()) {
return;
}
loadCallback =
new AppOpenAd.AppOpenAdLoadCallback() {
/**
* Called when an app open ad has loaded.
*
* #param ad the loaded app open ad.
*/
#Override
public void onAdLoaded(AppOpenAd ad) {
AppOpenManager.this.appOpenAd = ad;
AppOpenManager.this.loadTime = (new Date()).getTime();
}
/**
* Called when an app open ad has failed to load.
*
* #param loadAdError the error.
*/
#Override
public void onAdFailedToLoad(LoadAdError loadAdError) {
// Handle the error.
}
};
AdRequest request = getAdRequest();
AppOpenAd.load(
myApplication, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, loadCallback);
}
/** Creates and returns ad request. */
private AdRequest getAdRequest() {
return new AdRequest.Builder().build();
}
/** Utility method to check if ad was loaded more than n hours ago. */
private boolean wasLoadTimeLessThanNHoursAgo(long numHours) {
long dateDifference = (new Date()).getTime() - this.loadTime;
long numMilliSecondsPerHour = 3600000;
return (dateDifference < (numMilliSecondsPerHour * numHours));
}
/** Utility method that checks if ad exists and can be shown. */
public boolean isAdAvailable() {
return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
}
/** ActivityLifecycleCallback methods */
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
#Override
public void onActivityStarted(Activity activity) {
currentActivity = activity;
}
#Override
public void onActivityResumed(Activity activity) {
currentActivity = activity;
}
#Override
public void onActivityStopped(Activity activity) {}
#Override
public void onActivityPaused(Activity activity) {}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}
#Override
public void onActivityDestroyed(Activity activity) {
currentActivity = null;
}
/** Shows the ad if one isn't already showing. */
public void showAdIfAvailable() {
// Only show ad if there is not already an app open ad currently showing
// and an ad is available.
if (!isShowingAd && isAdAvailable()) {
Log.d(LOG_TAG, "Will show ad.");
FullScreenContentCallback fullScreenContentCallback =
new FullScreenContentCallback() {
#Override
public void onAdDismissedFullScreenContent() {
// Set the reference to null so isAdAvailable() returns false.
AppOpenManager.this.appOpenAd = null;
isShowingAd = false;
fetchAd();
}
#Override
public void onAdFailedToShowFullScreenContent(AdError adError) {}
#Override
public void onAdShowedFullScreenContent() {
isShowingAd = true;
}
};
appOpenAd.show(currentActivity);
} else {
Log.d(LOG_TAG, "Can not show ad.");
fetchAd();
}
}
}
MyApplication.java
import android.app.Application;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
/** The Application class that manages AppOpenManager. */
public class MyApplication extends Application {
private static AppOpenManager appOpenManager;
#Override
public void onCreate() {
super.onCreate();
MobileAds.initialize(
this,
new OnInitializationCompleteListener() {
#Override
public void onInitializationComplete(InitializationStatus initializationStatus) {}
});
appOpenManager = new AppOpenManager(this);
}
}
As Google have mentioned in "Cold starts and loading screens" Section here, app open ads will show when users foreground the app when it's suspended in memory. "Cold starts" occur when the app is launched but was not previously suspended in memory.
What we should do is avoid using System.exit(0) in onBackPressed() for exiting the app since it will make the app in Cold starts mode when opened. Just use finishAffinity() for exiting the app.
replace this
#OnLifecycleEvent(ON_START)
public void onStart() {
showAdIfAvailable();
}
with this
#OnLifecycleEvent(ON_START)
public void onStart() {
if (!(currentActivity instanceof SplashActivity)) {
showAdIfAvailable();
}
}
I'm developing an .aar library and I really need to interact with the lifecycle of an activity (so the callback onCreate(), on onResume(), etc...) in a standard java class.
I tried a lot of things but nothing works.
Is there a way I can do that?
From my understanding you need some thing like this,
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
/**
* #Krish
*/
public class LifeCycleObserver {
private LifeCycleObserver() {}
private static LifeCycleObserver sLifeCycleObserver;
public static LifeCycleObserver getInstance()
{
if (sLifeCycleObserver == null)
{
sLifeCycleObserver = new LifeCycleObserver();
}
return sLifeCycleObserver;
}
public static void init(Application application)
{
application.registerActivityLifecycleCallbacks(sLifeCycleObserver.lifecycleCallbacks);
}
private Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
};
}
and use it like this in Application class,
import android.app.Application;
/**
* Created by krish
*/
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
LifeCycleObserver.init(this);
}
}
Whenever my android application goes into the background i always wants to open my password activity every time application comes from background, How can i implement this functionality in my code?
Please follow these steps:
Add New Class Global
public class Global extends Application
{
private static Global mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);
}
public static Global getInstance(){
return mInstance;
}
}
Add this line in your manifest in application tag like
<application
android:name=".Global"
</application>
Add this class and open your password intent when app come background to foreground like
public class ApplicationLifeCycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
public static Activity activity;
private static final String TAG = ApplicationLifeCycleHandler.class.getSimpleName();
public static boolean isInBackground = true;
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
this.activity = activity;
}
#Override
public void onActivityResumed(Activity activity) {
this.activity = activity;
if (isInBackground) {
Intent intent = new Intent(activity, PasswordActivity.class);//set your password activity
activity.startActivity(intent);
Log.d(TAG, "app went to foreground");
isInBackground = false;
}
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
#Override
public void onConfigurationChanged(Configuration configuration) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(int i) {
if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
Log.d(TAG, "app went to background");
isInBackground = true;
}
}
}
Hope it will help you and please let me know if you are facing any issue. Thanks
In Android Library, FragmentActivity extends Activity. I would like to add a few methods, and override some methods, of the original Activity.
import android.app.Activity
public class Activiti extends Activity {
public void myNewMethod() { ... }
}
Because of the original hierarchy, FragmentActivity extends Activity, myNewMethod() should also be present in my library's FragmentActiviti
import android.support.v4.app.FragmentActivity;
public abstract class FragmentActiviti extends FragmentActivity {
public void myNewMethod() { ... }
}
But this will lead to a duplication of code, which i do not want this happens. Is there a way to avoid this duplication?
Edit: Usage scenario
Activiti.java
public abstract class Activiti extends Activity {
private int current_orientation = Configuration.ORIENTATION_UNDEFINED; // ORIENTATION_UNDEFINED = 0
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
current_orientation = this.getResources().getConfiguration().orientation;
}
protected boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
}
FragmentActiviti.java
public abstract class FragmentActiviti extends FragmentActivity {
/* This onCreate() can be omitted. Just putting here explicitly. */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
protected void someUtilsForFragments() { /* not used yet */ }
}
E_fragtest_06.java
public class E_fragtest_06 extends FragmentActiviti {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.printf(isDevicePortrait()); // this NOT WORK for now
}
}
Edit 2: Try using Util class
i think using the Decorator Class would be the most nicest way to solve this problem (no duplication of code). But the Decorator Pattern is just a bit hard (or impossible) to apply on Android Activity scenario.
i try implementing #hazzik's approach, but i still experience some problems.
ActivityUtil.java
public abstract class ActivityUtil {
private int current_orientation = Configuration.ORIENTATION_UNDEFINED; // ORIENTATION_UNDEFINED = 0
public void onCreate(Activity activity, Bundle savedInstanceState) {
activity.onCreate(savedInstanceState);
current_orientation = activity.getResources().getConfiguration().orientation;
}
public boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
}
Activiti.java
public class Activiti extends Activity {
private ActivityUtil activityUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
activityUtil.onCreate(this, savedInstanceState);
}
protected boolean isDevicePortrait() { return activityUtil.isDevicePortrait(); }
}
FragmentActiviti.java
public abstract class FragmentActiviti extends FragmentActivity {
private ActivityUtil activityUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
activityUtil.onCreate(this, savedInstanceState);
}
protected boolean isDevicePortrait() { return activityUtil.isDevicePortrait(); }
}
In ActivityUtil.onCreate(), activity.onCreate(savedInstanceState); is causing this compile error:
The method onCreate(Bundle) from the type Activity is not visible.
If i change Activity to Activiti:
public abstract class ActivityUtil {
public void onCreate(Activiti activity, Bundle savedInstanceState) { ... }
...
}
It will lead to another compile error in FragmentActiviti.onCreate()'s activityUtil.onCreate():
The method onCreate(Activiti, Bundle) in the type ActivityUtil is not applicable for the arguments (FragmentActiviti, Bundle)
i understand why those errors occur. But i just don't know how to avoid them.
To thanks all the guys who have been contributing to this question, especially #flup for guiding me about the Decorator Pattern, #hazzik and #donramos for your extensive efforts, i m here posting
My enhanced Android's Activity and FragmentActivity classes.
If you are also developing Android applications, i hope my codes could help you guys in some ways :-)
ActivityCore.java
package xxx.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
public final class ActivityCore {
public interface ActivityCallbackInterface {
public void onCreateCallback(Bundle savedInstanceState);
public void onBeforeSaveInstanceState(Bundle outState);
public void onSaveInstanceStateCallback(Bundle outState);
}
private final Activity activity;
/**
* This current_orientation variable should be once set, never changed during the object life-cycle.
* But Activity.getResources() is not yet ready upon the object constructs.
* That's why THIS CLASS is wholly responsible to maintain THIS VARIABLE UNCHANGED.
*/
private int current_orientation = Configuration.ORIENTATION_UNDEFINED; // ORIENTATION_UNDEFINED = 0
public ActivityCore(Activity activity) { this.activity = activity; }
public void onCreate(Bundle savedInstanceState) {
((ActivityCallbackInterface) activity).onCreateCallback(savedInstanceState);
current_orientation = activity.getResources().getConfiguration().orientation;
}
public void onSaveInstanceState(Bundle outState) {
/**
* THIS is the best ever place i have found, to unload unwanted Fragments,
* thus prevent re-creating of un-needed Fragments in the next state of Activity.
* (state e.g. Portrait-to-Landscape, or Landscape-to-Portrait)
*
* The KEY is to do it BEFORE super.onSaveInstanceState()
* (my guess for this reason is, in super.onSaveInstanceState(),
* it saves the layout hierarchy, thus saved the Fragments into the Bundle also.
* Thus restored.
* Note that Fragments NOT IN LAYOUT, having ONLY TAGS, are also restored.)
*/
((ActivityCallbackInterface) activity).onBeforeSaveInstanceState(outState);
((ActivityCallbackInterface) activity).onSaveInstanceStateCallback(outState);
}
public int getCurrentOrientation() { return current_orientation; }
public boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
public boolean isDeviceLandscape() { return current_orientation == Configuration.ORIENTATION_LANDSCAPE; }
public boolean isNewDevicePortrait() { return activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; }
public boolean isNewDeviceLandscape() { return activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; }
public boolean isPortrait2Landscape() { return isDevicePortrait() && isNewDeviceLandscape(); }
public boolean isLandscape2Portrait() { return isDeviceLandscape() && isNewDevicePortrait(); }
public String describeCurrentOrientation() { return describeOrientation(current_orientation); }
public String getCurrentOrientationTag() { return getOrientationTag(current_orientation); }
public String describeNewOrientation() { return describeOrientation(activity.getResources().getConfiguration().orientation); }
public String getNewOrientationTag() { return getOrientationTag(activity.getResources().getConfiguration().orientation); }
private String describeOrientation(final int orientation) {
switch (orientation) {
case Configuration.ORIENTATION_UNDEFINED: return "ORIENTATION_UNDEFINED"; // 0
case Configuration.ORIENTATION_PORTRAIT: return "ORIENTATION_PORTRAIT"; // 1
case Configuration.ORIENTATION_LANDSCAPE: return "ORIENTATION_LANDSCAPE"; // 2
case Configuration.ORIENTATION_SQUARE: return "ORIENTATION_SQUARE"; // 3
default: return null;
}
}
#SuppressLint("DefaultLocale")
private String getOrientationTag(final int orientation) {
return String.format("[%d:%s]", orientation, describeOrientation(orientation).substring(12, 16).toLowerCase());
}
}
Activity.java
package xxx.android.app;
import xxx.android.ActivityCore;
import xxx.android.ActivityCore.ActivityCallbackInterface;
import android.os.Bundle;
public abstract class Activity extends android.app.Activity implements ActivityCallbackInterface {
private final ActivityCore activityCore;
public Activity() { super(); activityCore = new ActivityCore(this); }
#Override
protected void onCreate(Bundle savedInstanceState) { activityCore.onCreate(savedInstanceState); }
#Override public void onCreateCallback(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }
#Override
public void onBeforeSaveInstanceState(Bundle outState) {} // Optionally: let child class override
#Override
protected void onSaveInstanceState(Bundle outState) { activityCore.onSaveInstanceState(outState); }
#Override public void onSaveInstanceStateCallback(Bundle outState) { super.onSaveInstanceState(outState); }
public final int getCurrentOrientation() { return activityCore.getCurrentOrientation(); }
public final boolean isDevicePortrait() { return activityCore.isDevicePortrait(); }
public final boolean isDeviceLandscape() { return activityCore.isDeviceLandscape(); }
public final boolean isNewDevicePortrait() { return activityCore.isNewDevicePortrait(); }
public final boolean isNewDeviceLandscape() { return activityCore.isNewDeviceLandscape(); }
public final boolean isPortrait2Landscape() { return activityCore.isPortrait2Landscape(); }
public final boolean isLandscape2Portrait() { return activityCore.isLandscape2Portrait(); }
public final String describeCurrentOrientation() { return activityCore.describeCurrentOrientation(); }
public final String getCurrentOrientationTag() { return activityCore.getCurrentOrientationTag(); }
public final String describeNewOrientation() { return activityCore.describeNewOrientation(); }
public final String getNewOrientationTag() { return activityCore.getNewOrientationTag(); }
}
FragmentActivity.java
package xxx.android.support.v4.app;
import xxx.android.ActivityCore;
import xxx.android.ActivityCore.ActivityCallbackInterface;
import android.os.Bundle;
public abstract class FragmentActivity extends android.support.v4.app.FragmentActivity implements ActivityCallbackInterface {
private final ActivityCore activityCore;
public FragmentActivity() { super(); activityCore = new ActivityCore(this); }
#Override
protected void onCreate(Bundle savedInstanceState) { activityCore.onCreate(savedInstanceState); }
#Override public void onCreateCallback(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }
#Override
public void onBeforeSaveInstanceState(Bundle outState) {} // Optionally: let child class override
#Override
protected void onSaveInstanceState(Bundle outState) { activityCore.onSaveInstanceState(outState); }
#Override public void onSaveInstanceStateCallback(Bundle outState) { super.onSaveInstanceState(outState); }
public final int getCurrentOrientation() { return activityCore.getCurrentOrientation(); }
public final boolean isDevicePortrait() { return activityCore.isDevicePortrait(); }
public final boolean isDeviceLandscape() { return activityCore.isDeviceLandscape(); }
public final boolean isNewDevicePortrait() { return activityCore.isNewDevicePortrait(); }
public final boolean isNewDeviceLandscape() { return activityCore.isNewDeviceLandscape(); }
public final boolean isPortrait2Landscape() { return activityCore.isPortrait2Landscape(); }
public final boolean isLandscape2Portrait() { return activityCore.isLandscape2Portrait(); }
public final String describeCurrentOrientation() { return activityCore.describeCurrentOrientation(); }
public final String getCurrentOrientationTag() { return activityCore.getCurrentOrientationTag(); }
public final String describeNewOrientation() { return activityCore.describeNewOrientation(); }
public final String getNewOrientationTag() { return activityCore.getNewOrientationTag(); }
}
Lastly, i really have to thanks you guys are being so so so helpful and keep updating the solving progress with me! You all are the key persons who make stackoverflow a perfect site for programmers. Should you spot any problems in my codes, or any rooms for improvements, please do not hesitate to help me again :-)
Some improvements?
It is because onBeforeSaveInstanceState() is implemented upon usage, all the three classes need to keep abstract. This leads to a duplication of the member variable current_orientation. If current_orientation could be put into class ActivityBase, or grouping it into somewhere else, it would be a lot nicer!
stupid me. i have fixed it :-)
For my point of view the best solution here is to delegate logic to some class, let's call it CustomActivityLogic.
Also you need to create common interface (CustomActivity) for your activities if you want to access some data or methods of activity classes from your logic class.
To call protected virtual overridden methods there are two solutions:
call method of supper from overridden method
make a new method in subclass and call super method from this new method. Call new method from shared logic.
CustomActivity.java
public interface CustomActivity {
void someMethod();
}
Activiti.java
import android.app.Activity
public class Activiti
extends Activity
implements CustomActivity {
private CustomActivityLogic logic = new CustomActivityLogic();
public void someMethod() { /***/ }
public void myNewMethod() { logic.myNewMethod(this); }
#Override
protected void onCreate(Bundle savedInstanceState) {
logic.onCreate(this, savedInstanceState); // call shared logic
super.onCreate(savedInstanceState); // call super
}
}
FragmentActivitii.java
import android.support.v4.app.FragmentActivity;
public class FragmentActivitii
extends FragmentActivity
implements CustomActivity {
private CustomActivityLogic logic = new CustomActivityLogic();
public void someMethod() { /***/ }
public void myNewMethod() { logic.myNewMethod(this); }
#Override
protected void onCreate(Bundle savedInstanceState) {
logic.onCreate(this, savedInstanceState); // call shared logic
super.onCreate(savedInstanceState); // call super
}
}
CustomActivityLogic.java
public class CustomActivityLogic {
public void myNewMethod(CustomActivity activity) { /*...*/ }
public void onCreate(Activity activity, Bundle savedInstanceState) {
/* shared creation logic */
}
}
Approach with making onCreate available to call from outside via CustomActivity interface
CustomActivity.java
public interface CustomActivity {
void someMethod();
void onCreateSuper(Bundle savedInstanceState);
}
Activiti.java
import android.app.Activity
public class Activiti
extends Activity
implements CustomActivity {
private CustomActivityLogic logic = new CustomActivityLogic();
public void someMethod() { /***/ }
public void myNewMethod() { logic.myNewMethod(this); }
#Override
protected void onCreate(Bundle savedInstanceState) {
logic.onCreate(this, savedInstanceState); // call shared logic
}
public void onCreateSuper(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // call super
}
}
FragmentActivitii.java
import android.support.v4.app.FragmentActivity;
public class FragmentActivitii
extends FragmentActivity
implements CustomActivity {
private CustomActivityLogic logic = new CustomActivityLogic();
public void someMethod() { /***/ }
public void myNewMethod() { logic.myNewMethod(this); }
#Override
protected void onCreate(Bundle savedInstanceState) {
logic.onCreate(this, savedInstanceState); // call shared logic
}
public void onCreateSuper(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // call super
}
}
CustomActivityLogic.java
public class CustomActivityLogic {
public void myNewMethod(CustomActivity activity) { /*...*/ }
public void onCreate(CustomActivity activity, Bundle savedInstanceState) {
/* shared creation logic */
activity.onCreateSuper(savedInstanceState); // call-back super
}
}
You wish to add helper methods that help keep track of the orientation. I'd think this is not quite big enough to warrant the creation of a subclass.
Put them in a helper class instead:
public class OrientationHelper {
private Activity activity;
private int current_orientation;
public OrientationHelper(Activity activity){
this.activity = activity;
orientation = Configuration.ORIENTATION_UNDEFINED;
}
public int getNewOrientation() {
return activity.getResources().getConfiguration().orientation;
}
// call this when you wish to update current_orientation
public void updateOrientation() {
current_orientation = getNewOrientation();
}
public int getCurrentOrientation() {
return current_orientation;
}
public boolean isDevicePortrait() {
return current_orientation == Configuration.ORIENTATION_PORTRAIT;
}
public boolean isDeviceLandscape() {
return current_orientation == Configuration.ORIENTATION_LANDSCAPE;
}
public boolean isNewDevicePortrait() {
return getCurrentOrientation() == Configuration.ORIENTATION_PORTRAIT;
}
public boolean isNewDeviceLandscape() {
return getCurrentOrientation() == Configuration.ORIENTATION_LANDSCAPE;
}
public boolean isPortrait2Landscape() {
return isDevicePortrait() && isNewDeviceLandscape();
}
public boolean isLandscape2Portrait() {
return isDeviceLandscape() && isNewDevicePortrait();
}
public String describeCurrentOrientation() {
return describeOrientation(current_orientation);
}
public String describeNewOrientation() {
return describeOrientation(getNewOrientation());
}
private String describeOrientation(int current_orientation) {
switch (current_orientation) {
case Configuration.ORIENTATION_UNDEFINED:
return "ORIENTATION_UNDEFINED";
case Configuration.ORIENTATION_PORTRAIT:
return "ORIENTATION_PORTRAIT";
case Configuration.ORIENTATION_LANDSCAPE:
return "ORIENTATION_LANDSCAPE";
case Configuration.ORIENTATION_SQUARE:
return "ORIENTATION_SQUARE";
default: return null;
}
}
}
In those activities that work with orientation (and only those), you can instantiate the OrientationHelper and call updateOrientation() in select places.
The other bit of code, that organizes the saving of the instance state, I would not put in a different class just so that you can reuse it. Because this is not where one would expect modifications to state saving to occur and therefore it might get overlooked. (It took me a bit of scrolling around to figure out what it's supposed to do.)
I think the most readable way to go about that is to write it out explicitly in each Activity where you use it.
One last thing to consider is that the Sherlock Actionbar already extends Activity. And rightly so, I think. But this means that you'll occasionally run into trouble if you extend Activity too.
How about using the Decorator Pattern? Unfortunately, this will require you to delegate all of the existing methods, or whichever ones are necessary for your purpose.
public class ActivityDecorator extends Activity
{
private Activity RealActivity;
public ActivityDecorator(Activity _realActivity)
{
RealActivity = _realActivity;
}
public void myNewMethod() { ... } // this exposes the added/new functionality
// unfortunately for old functionality you need to delegate
public void oldMethod() { RealActivity.oldMethod(); }
}
However, once you've done this once for the ActivityProxy class, you can construct instances of ActivityDecorator with types that derive Activity such as FragmentActivity in your case. E.g.
ActivityDecorator decorator = new ActivityDecorator(new FragmentActivity());
Your design problem is one of the issues addressed by the upcoming Java 8 virtual extensions. See URL below for more details:
http://java.dzone.com/articles/java-8-virtual-extension
In the meantime, there is no easy way. A decorator class will not work, instead implement a utility class that will be called by both of your classes:
EDITED BASED ON NEW INFO:
/** NOTE: cannot be abstract class **/
public class ActivitiBase {
private int current_orientation = Configuration.ORIENTATION_UNDEFINED; // ORIENTATION_UNDEFINED = 0
private Activity activity;
public void ActivitiBase(Activity activity) {
this.activity = activity;
}
public void onCreate(Bundle savedInstanceState) {
current_orientation = activity.getResources().getConfiguration().orientation;
}
public boolean isDevicePortrait() { return current_orientation ==
Configuration.ORIENTATION_PORTRAIT; }
}
public void myNewMethod() { ... }
}
Activiti class:
public class Activiti extends Activity {
private ActiviBase activitiBase;
public Activiti() {
activitiBase = new ActiviBase(this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activitiBase.onCreate(savedInstanceState);
}
public void myNewMethod() {
activitiBase.myNewMethod();
}
}
FrameActiviti class:
public class FrameActiviti extends FrameActivity {
private ActiviBase activitiBase;
public FrameActiviti() {
activitiBase = new ActiviBase(this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activitiBase.onCreate(savedInstanceState);
}
public void myNewMethod() {
activitiBase.myNewMethod();
}
}