ClassException casting Interface - android

I have a fragment, which is being used inside a MainActivity, actually it is used inside a ViewPager in a MainActivity.
public class Myfragment extends Fragment implements MySingleton.ResponseInterface{
public static Myfragment newInstance() {
final Myfragment mf = new Myfragment();
return mf;
}
public Myfragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
//.....
final MySingleton mysingleton = MySingleton.getInstance(getContext());
//I have a button in the fragment that I use like this
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mysingleton.getSomeResponse();
}
});
}
#Override
public void onResponseGiven(String response) {
Log.d("response", response);
}
}
I have a Singleton class to be used for different actions, the singleton includes an interface:
public class MySingleton{
private static MySingleton mInstance;
private static Context mContext;
public ResponseInterface responseInterface;
private MySingleton(Context context){
mContext = context;
this.responseInterface = (ResponseInterface) context;
}
public static synchronized MySingleton getInstance(Context context){
if(mInstance == null){
mInstance = new MySingleton(context);
}
return mInstance;
}
public void getSomeResponse(){
responseInterface.onResponseGiven("send response");
}
public interface ResponseInterface{
void onResponseGiven(String response);
}
}
Why do I get ClassCastException telling that MainActivity cannot be casted to MySingleton.ResponseInterface??

In your MySingleton class
public class MySingleton{
private static MySingleton mInstance;
private static Context mContext;
public ResponseInterface responseInterface;
private MySingleton(Context context){
mContext = context;
}
public static synchronized MySingleton getInstance(Context context){
if(mInstance == null){
mInstance = new MySingleton(context);
}
return mInstance;
}
public void getSomeResponse(ResponseInterface responseInterface){
this.responseInterface = responseInterface;
responseInterface.onResponseGiven("send response");
}
public interface ResponseInterface{
void onResponseGiven(String response);
}
}
And in your Fragment
final MySingleton mysingleton = MySingleton.getInstance(getActivity());
// edited here
mysingleton.getSomeResponse(Myfragment.this);

You should pass this to the getInstance method:
MySingleton.getInstance(this);
And try to cast it to MySingleton.ResponseInterface instead of MySingleton.

If you are casting MainActivity to MySingleton.ResponseInterface, you have to be sure that is implementing it.
In you actual code, MyFragment is implementing MySingleton.ResponseInterface.
I guess MainActivity doesn't
When you are calling MySingleton.getResponse(), you are sending the message to the responseInterface in your singleton. (I guess your activity, given it is a context when you create your singleton's instance).
public void getSomeResponse(){
responseInterface.onResponseGiven("send response");
}
If you want to get the answer to your fragment, you have to ensure tha the singleton is calling to your fragment instance.
Anyway, there is a very serieus design issue. attaching a fragment/activity instance to your singleton can lead to memeory leaks if you don't free when them are destroyed. read this
Also your singleton isn't really a singleton, you create a new instance after you call getIntance every time.

You are getting class cast exception because in the line final MySingleton mysingleton = MySingleton.getInstance(getContext());, you are passing activity context and you are trying to cast that context to ResponseInterface. ResponseInterface is implemented by Myfragment.
To solve it: You can change MySingeton class constructor as below
private MySingleton(Context context, ResponseInterface responseInterface){
mContext = context;
this.responseInterface = responseInterface;
}
and the calling part (in MyFragment OnCreateView) as
final MySingleton mysingleton = MySingleton.getInstance(getContext(), this);
and in getInstance method as
public static synchronized MySingleton getInstance(Context context, ResponseInterface responseInterface){
if(mInstance == null){
mInstance = new MySingleton(context, responseInterface);
}
return mInstance;
}

Related

How to I set interface which id contained in normal Class(not activity)

I'm using an app which contain viewPager and TapLayout. And a fragment which showed on ViewPager has Bluetooth connecting function. When I tab a button on a fragment, Ble Started.
But in that process, I got an
interface ClassCastException Error
A fragment implement interface which in BluetoothController. BluetoothController need context, so I passed context(getContext) which got in Fragment.
When I implement interface and pass context in MainActivity, it works well. But I do that in fragment, Android studio occurs 'interface ClassCastException Error'.
I should implement interface in fragment, because interface pass Bluetooth state like Connecting, DisConnecting, Start etc.
How can I set interface in fragment?
When MainActivity implement interface, Bluetooth interface works well. But that case, Bluetooth state is not passed in fragment. It pass state in MainActivity. I think when I got state in MainActivity, pass the state to fragment is one of the solution for this problem. But I think it is not good method.
here is BluetoothContoller code
public class BluetoothController extends BluetoothPacketController
{
public static final String TAG = BluetoothController.class.getSimpleName() +"_Debug";
private static Context context;
private static final int SCAN_PERIOD = 1000*10;
private int BLE_STATE = BLUETOOTH_STATE.IDLE;
private BleInterface mListener;
public class BLUETOOTH_STATE {
public static final int IDLE = 0;
public static final int SCANNING = 1;
public static final int CONNECTION_TRY = 2;
public static final int CONNECTION_ERR = 3;
public static final int CONNECTION_SUCC = 4;
public static final int DISCONNECTION = 5;
}
public interface BleInterface{
void bleScanTimeOut();
void bleScanStart();
void bleScanStop();
void bleConnectTry();
void bleDisConnectTry();
void bleErrDisConnectTry();
void bleDisConnected();
void bleResult(boolean ret);
void bleResult(String ret);
}
private static BluetoothController instance = null;
#Override
public void setContext(Context context) {
super.setContext(context);
this.context = context;
}
public static BluetoothController getInstance() {
if (instance == null) {
instance = new BluetoothController();
}
return instance;
}
#Override
public void init(Context context) {
super.init(context);
setContext(context);
mListener = (BluetoothController.BleInterface) context;//listener;//(BluetoothController.BleInterface) context;
}
...
}
And here is fragment code
public class RegistrationFragment extends Fragment implements BluetoothController.BleInterface{
private View view;
public static final String TAG = RegistrationFragment.class.getSimpleName() + "_Debug";
private static RegistrationFragment instance = null;
public static RegistrationFragment getInstance() {
if (instance == null) {
instance = new RegistrationFragment();
}
return instance;
}
public void setContext(Context context) {
this.context = context;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.register_view, container, false);
BluetoothScanUpperAPI21.getInstance().init(getContext());
BluetoothScanUnderAPI21.getInstance().init(getContext());
BluetoothController.getInstance().init(getContext());
startUi_init();
return view;
}
...
}
I want to get Bluetooth state through BleInterface in fragment.
Why are you casting context in BleInterface? Can't you pass this from fragment to BluetoothController where this referes BleInterface which is implemented in fragment?
In Fragment
BluetoothController.getInstance().init(this);
BleController's Init:
#Override
public void init(BluetoothController.BleInterface ble) {
super.init(context);
setContext(context);
mListener = ble;
}
Reason for the behaviour that you are facing:
Fragment doesn't have their own context. They always refers to activity context. So, when you pass getContext(), it refers to activity context and compiler tries to find BleInterface in activity which does not exists. That's why you are facing ClassCastException.

Memory leaks in context

I have a class App which extends Application and has a static method which returns context(which is static).
public class App extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = this.getApplicationContext();
DBHelper dbHelper = new DBHelper();
DatabaseManager.initializeInstance(dbHelper);
}
public static Context getContext() {
return context;
}
}
Now in the DBHelper class which extends SQLiteAssetHelper in the constructor, i have this:
public DBHelper() {
super(App.getContext(), DATABASE_NAME, null, DATABASE_VERSION);
}
This is the DataBaseManager class:
public class DatabaseManager {
private Integer mOpenCounter = 0;
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase() {
mOpenCounter+=1;
if(mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
}
Everything works fine, but there is a design problem because context fields should not be static. How do I use a context in DBHelper while keeping the code working and the field non-static?
You can pass ApplicationContext inside the DBHelper constructor like below:
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
and then
DBHelper dbHelper = new DBHelper(getApplicationContext());

Mortar and Flow libraries vs. ViewPager

I'm trying to adapt Mortar&Flow in my app and faced with an issue, that I can't make PageAdapter works with Screens, instead of Fragments.
Anyone managed to get it right?
I didn't succeed but, probably someone can guide me from this point:
The initial Dagger Registration:
#Module(
injects = {
MainActivity.class,
},
library = true,
complete = false
)
public class DaggerConfig {
#SuppressWarnings("unused")
#Provides #Singleton Gson provideGson() {
return new GsonBuilder().create();
}
}
MainScreen, whose View is hosting ViewPager:
#Layout(R.layout.screen_main) #WithModule(MainScreen.Module.class)
public class MainScreen extends Path {
#dagger.Module(injects = MainView.class, addsTo = DaggerConfig.class)
public static class Module {}
#Singleton
public static class Presenter extends ViewPresenter<MainView> {
#Inject
public Presenter() {}
}
}
MainView:
...........
#Inject
MainScreen.Presenter presenter;
...........
#Override protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.inject(this);
final Path[] screens = {
new SubScreen("1"),
new SubScreen("2"),
new SubScreen("3"),
};
CustomPagerAdapter customPagerAdapter = new CustomPagerAdapter(getContext(), screens );
customPagerAdapter .setAdapter(firstRunPagerAdapter);
}
.....
Now, the main part, SubScreen (3 similar screens, that differs only by the parameters we are passing into them => they should adjust views according these parameters)
#Layout(R.layout.screen_subscreen) #WithModule(SubScreen.Module.class)
public class SubScreen extends Path {
private final String title;
public SubScreen(String titleParam) {
title = titleParam;
}
#dagger.Module(injects = SubView.class, addsTo = DaggerConfig.class)
public class Module {
#Provides
SubViewMetadata provideSubViewMetadata() {
return new SubViewMetadata(backgroundColor, title);
}
}
#Singleton
public static class Presenter extends ViewPresenter<SubView> {
private String title;
#Inject
public Presenter(String title) {
this.title= title;
}
#Override
protected void onLoad(Bundle savedInstanceState) {
super.onLoad(savedInstanceState);
if (!hasView()) {
return;
}
getView().setTitle(subViewMetadata.title);
}
}
}
and it's view
public class SubView extends FrameLayout {
#InjectView(R.id.subViewTitleTextView)
TextView subViewTitleTextView;
#Inject
SubScreen.Presenter presenter;
public SubView(Context context, AttributeSet attrs) {
super(context, attrs);
ObjectGraphService.inject(context, this);
}
public void setTitle(String title) {
subViewTitleTextView.setText(title);
}
#Override protected void onAttachedToWindow() {....}
#Override protected void onDetachedFromWindow() {....}
......
}
Custom Pager adapter:
public class CustomPagerAdapter extends PagerAdapter {
private final Context context;
private final Path[] screens;
public CustomPagerAdapter(Context context, Path[] screens) {
this.context = context;
this.screens = screens;
}
#Override
public int getCount() {
return (screens == null)? 0 : screens.length;
}
#Override
public boolean isViewFromObject(View view, Object o) {
return view.equals(o);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Path screen = screens[position];
MortarScope originalScope = MortarScope.getScope(context);
MortarScope newChildScope = originalScope.buildChild().build("tutorialpage" + position);
Context childContext = newChildScope.createContext(context);
View newChild = Layouts.createView(childContext, screen);
container.addView(newChild);
return newChild;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = ((View) object);
container.removeView(view);
MortarScope.getScope(view.getContext()).destroy();
}
}
The problem statement: it's crashing, as SubView class hasn't been added into list of Injections at the "Layouts.createView(childContext, screen);" moment in the Adapter, and I can't add it by default, because I want to have a #provider of data from SubScreen to SubScreen.Presenter. (I'm using local variable.
If I add SubView.class into list of injections and convert local Screen's variables into static, then I'll have 3 identical pages inside the ViewPager (which is logical, as every next call of the constructor - overrides old static variables).
Any help/ideas?
Thanks for your help,
Konstantin
Ok, I figured out.
First of all, adding SubView into list of globally injected classes
Then modifying SubScreen class:
#Layout(R.layout.screen_subscreen)
public class SubScreen extends Path {
private static String titleStatic; // Introducing static variable
private final String title;
public SubScreen(String titleParam) {
title = titleParam;
}
public void refreshPresenter() {
titleStatic = title;
}
#Singleton
public static class Presenter extends ViewPresenter<SubView> {
private String title;
#Inject
public Presenter() {
}
#Override
protected void onLoad(Bundle savedInstanceState) {
super.onLoad(savedInstanceState);
if (!hasView()) {
return;
}
getView().setTitle(titleStatic);
}
}
}
and then in Custom adapter do this changes:
public class CustomPagerAdapter extends PagerAdapter {
private final Context context;
private final SubScreen[] screens;
public CustomPagerAdapter(Context context, SubScreen[] screens) {
this.context = context;
this.screens = screens;
}
......
#Override
public Object instantiateItem(ViewGroup container, int position) {
SubScreen screen = screens[position];
MortarScope originalScope = MortarScope.getScope(context);
MortarScope newChildScope = originalScope.buildChild().build("tutorialpage" + position);
Context childContext = newChildScope.createContext(context);
screen.refreshPresenter(); // updating the static var with local one!
View newChild = Layouts.createView(childContext, screen);
container.addView(newChild);
return newChild;
}
....
}
I.e. the solution is to keep the local AND static variables in the Screen, if the same screen is going to be reused. And when we inflate the view it - just setting the right value to the static one (that would be used in the Presenter).
I am not sure, that it is the best possible solution, but it works. It would be nice to hear, if it can be improved.

Android - Alternative to Using Context Inside of Parcelable

I have a class I use for managing bitmaps in my application.
It needs to have a context passed to it to be able to use getResources().
Problem is I believe you can't use Context inside of a Parcelable class.
Anyone have any ideas for a solution?
Below is my code:
public class ImageManager implements Parcelable {
private static final long serialVersionUID = 66;
private HashMap<Integer, Bitmap> mBitmaps;
private HashMap<Integer, Drawable> mDrawables;
//private Context mContext;
private boolean mActive = true;
public ImageManager(Context c) {
mBitmaps = new HashMap<Integer, Bitmap>();
mDrawables = new HashMap<Integer, Drawable>();
//mContext = c;
}
public ImageManager(Parcel in) {
// TODO Auto-generated constructor stub
}
public Bitmap getBitmap(int resource) {
if (mActive) {
if (!mBitmaps.containsKey(resource)) {
mBitmaps.put(resource,
BitmapFactory.decodeResource(mContext.getResources(), resource));
}
return mBitmaps.get(resource);
}
return null;
}
public Drawable getDrawable(int resource) {
if (mActive) {
if (!mDrawables.containsKey(resource)) {
mDrawables.put(resource, mContext.getResources().getDrawable(resource));
}
return mDrawables.get(resource);
}
return null;
}
public void recycleBitmaps() {
Iterator itr = mBitmaps.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry e = (Map.Entry)itr.next();
((Bitmap) e.getValue()).recycle();
}
mBitmaps.clear();
}
public ImageManager setActive(boolean b) {
mActive = b;
return this;
}
public boolean isActive() {
return mActive;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeValue(mBitmaps);
dest.writeValue(mDrawables);
//dest.writeValue(mContext);
dest.writeByte((byte) (mActive ? 0x01 : 0x00));
}
public static final Parcelable.Creator<ImageManager> CREATOR = new Parcelable.Creator<ImageManager>() {
public ImageManager createFromParcel(Parcel in) {
return new ImageManager(in);
}
public ImageManager[] newArray(int size) {
return new ImageManager[size];
}
};
}
A "manager" normally can't be Parcelable or Serializable.
Parcelable or Serializable objects are kind of objects that just hold some data and don't perform any operations on it and don't need to reference Context.
When you pass objects as a Parcelable, the object received will be recreated so this way you can't preserve the same instance.
To solve your task, make ImageManager a singleton.
public final class ImageManager {
public static synchronized ImageManager getInstance(final Context context) {
if (sInstance == null) {
sInstance = new ImageManager(context.getApplicationContext());
}
return sInstance;
}
private static ImageManager sInstance;
private final Context mContext;
private ImageManager(Context c) {
mBitmaps = new HashMap<Integer, Bitmap>();
mDrawables = new HashMap<Integer, Drawable>();
}
And whenever you need it in Activity, call
ImageManager imageManager = ImageManager.getInstance(this);
This way the same ImageManager will be accessible in all Activities.
You can create your own Application implementation and add static getter.
public class Application extends android.app.Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = this;
}
public static Context getContext(){
return context;
}
}
And use it everywhere you need:
Application.getContext();
But in general I thing it's not the best idea.
I suggest turning your class into a helper class by making the methods you need static and then passing Context as a parameter as well, like so:
public Bitmap getBitmap(int resource, Context context) { ... }
Don't store that reference to Context in your helper class, if you do you will most likely be leaking it.

How to pass params to Android AsnycTaskLoader

I'm trying to pass params into a AsyncTaskLoader. How do I do that?
Currently, I'm putting what I need to pass in in a separate static class. Anyway around this?
public class NewsAsyncTaskLoader extends AsyncTaskLoader<List<Content>> {
private static final DbHelper dbHelper = DbHelperFactory.getDbHelper();
public FeedAsyncTaskLoader(Context context) {
super(context);
}
#Override
public List<Content> loadInBackground() {
List<Content> contents = DbHelper.getStream(FeedSections.getInstance().getCurrentSection());
feed.setContents(contents);
return feed;
}
}
Pass additional parameters into your constructor:
public FeedAsyncTaskLoader(Context context, String moreInfo) {
super(context);
// Do something with moreInfo
}

Categories

Resources