Why ReferenceQueue always empty in activity? - java

I'm trying using ReferenceQueue to check Activity if the activity is destory,Whether this activity is recycled.
I just using intent to Main2Activity.class:
switch (v.getId()){
case R.id.btn_turn_two:
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
startActivity(intent);
break;
default:
break;
}
when I press to return MainActivity.class, the Main2Acitivity.class will be destroyed, so I using registerActivityLifecycleCallbacks watch activity in application,
public class MainApplication extends Application{
private watche watche;
#Override
public void onCreate() {
super.onCreate();
watche=new watche(this,getApplicationContext());
}
this class is check the Activity is destoryed.
public class watche {
KeyRefrence ref;
private Context context;
private final Set<String> retainedKeys = new CopyOnWriteArraySet<>();
private final ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
public watche(Application application, Context context){
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
this.context=context;
}
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) {
//weakrefrence to this activity
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyRefrence weakReference=new KeyRefrence(activity,key,activity.getPackageName(),referenceQueue);
final MyAyTask myAyTask=new MyAyTask(weakReference,activity,context);
myAyTask.doInBackground(null);
}
};
public class MyAyTask extends AsyncTask {
private Activity activity;
private KeyRefrence keyRefrence;
private Context context;
public MyAyTask(KeyRefrence keyRefrence, Activity activity,Context context){
this.activity=activity;
this.context=context;
this.keyRefrence=keyRefrence;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
#Override
protected Object doInBackground(Object[] objects) {
gc();
ref=(KeyRefrence)referenceQueue.poll();
if (ref==null){
Log.d(TAG, "onActivityDestroyed:Acitivty is not destory");
}
else if (ref!=null){
Log.d(TAG, "doInBackground: "+ref);
}
return null;
}
}
I have create KeyReference implement WeakReference,and I didn't do anything in Main2Activity.class, I just press the phone return,I check the gc,is working ,but the referencequeue always empty, I'm sure the Activity is destoryed.

Related

Update UI element from non-activity class

I have an Activity which creates a class that does some work. What is the typical Android method of having this class report back to the Activity in order to update the UI?
My activity, which creates the class:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyClass obj = new MyClass(this);
obj.DoWork();
}
}
The class that does the work, and wants to report back some
public class MyClass(Context context) {
private Context context;
public void DoWork() {
//Do some work with a countdown timer
//Report back some values
}
}
You can create your own interface like this:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyClass obj = new MyClass(this);
obj.setOnWorkDoneListener(new MyClass.OnWorkDoneListener(){
#Override
public void onDone(Values values) {
//Work done, use values
updateUI(values);
}
});
obj.DoWork();
}
}
public class MyClass(Context context) {
private Context context;
public interface OnWorkDoneListener{
void onDone(Values values);
}
private OnWorkDoneListener listener;
public void setOnWorkDoneListener(OnWorkDoneListener listener){
this.listener = listener;
}
public void DoWork() {
//Do some work with a countdown timer
when(workEnded) listener.onDone(backValues);
}
}

Android how to Check if language has changed

I create an application with MVVM concept, there is fragment for viewpager in my Activity. some data changed when I change my language in my application, but the data that showed by webservices is not change. so I try to add android:configChanges="locale" in my every Activity and I already add this code on my Activity class :
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
recreate();
}
}
But its make my UI recreate every configuration change, including Screen Rotation while I just want to recreate if Language is changed.
this is my fragment code :
public class CatalogueFragment extends BaseFragment<FragmentCatalogueBinding, CatalogueViewModel>
implements CatalogueNavigator, CatalogueAdapter.CatalogueAdapterListener {
#Inject
CatalogueAdapter adapter;
#Inject
LinearLayoutManager mLayoutManager;
#Inject
ViewModelProvider.Factory factory;
FragmentCatalogueBinding fragmentCatalogueBinding;
private CatalogueViewModel catalogueViewModel;
public static CatalogueFragment newInstance(int Pos) {
Bundle args = new Bundle();
CatalogueFragment fragment = new CatalogueFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public int getBindingVariable() {
return BR.viewModel;
}
#Override
public int getLayoutId() {
return R.layout.fragment_catalogue;
}
#Override
public CatalogueViewModel getViewModel() {
catalogueViewModel = ViewModelProviders.of(this, factory).get(CatalogueViewModel.class);
return catalogueViewModel;
}
#Override
public void handleError(String error) {
// handle error
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
catalogueViewModel.setNavigator(this);
adapter.setListener(this);
}
#Override
public void onRetryClick() {
catalogueViewModel.fetchData();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentCatalogueBinding = getViewDataBinding();
setUp();
}
#Override
public void updateData(List<Movie> movieList) {
adapter.addItems(movieList);
}
private void setUp() {
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
fragmentCatalogueBinding.recyclerCatalogue.setLayoutManager(mLayoutManager);
fragmentCatalogueBinding.recyclerCatalogue.setItemAnimator(new DefaultItemAnimator());
fragmentCatalogueBinding.recyclerCatalogue.setAdapter(adapter);
}
}
and this is my ViewModel class
public class CatalogueViewModel extends BaseViewModel {
private final MutableLiveData<List<Movie>> movieListLiveData;
public CatalogueViewModel(DataManager dataManager, SchedulerProvider schedulerProvider) {
super(dataManager, schedulerProvider);
movieListLiveData = new MutableLiveData<>();
fetchData();
}
public void fetchData() {
setIsLoading(true);
getCompositeDisposable().add(getDataManager()
.getApiHelper().doMovieCall(URLConfig.API_KEY, getDataManager().getLanguage())
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(movieResponse -> {
if (movieResponse != null && movieResponse.getResults() != null) {
movieListLiveData.setValue(movieResponse.getResults());
}
setIsLoading(false);
}, throwable -> {
setIsLoading(false);
// getNavigator().handleError(throwable);
}));
}
public LiveData<List<Movie>> getMovieListLiveData() {
return movieListLiveData;
}
}
Can anybody show me where is my wrong? Thank you very much
You can use: ACTION_LOCALE_CHANGED
Here an example:
private BroadcastReceiver mLangReceiver = null;
protected BroadcastReceiver setupLangReceiver(){
if(mLangReceiver == null) {
mLangReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do what you want
}
};
registerReceiver(mLangReceiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
}
return mLangReceiver;
}

on click interface callback gives null in Android

I have Android module library which will create the Touch Keypad UI and set event listener for done and backbutton.
Also have main activity with implement eventCallback interface
MainActivity.java (Appication)
public class MainActivity extends AppCompatActivity implements eventCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View v = new touchkey(this);
setContentView(v);
}
#Override
public void onClick() {
Log.i("test","complete");
Toast.makeText(this, "this is my Toast message!!! =)",
Toast.LENGTH_LONG).show();
}
}
eventCallback.java (Android Module Library)
public interface eventCallback {
void onClick();
}
touchkey.java (Android Module Library)
public class touchkey extends RelativeLayout{
private static touchkey INSTANCE;
TextView bclear;
ImageView bdone;
eventCallback eventCall;
public touchkey(Context context) {
super(context);
initialize(context);
}
public touchkey(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public void test() {
Log.i("test","test");
}
private void initialize(Context context) {
inflate(context, R.layout.touchkey, this);
bclear = (TextView) findViewById(R.id.anti_theft_t9_key_clear);
bdone = (ImageView) findViewById(R.id.anti_theft_t9_key_done);
bdone.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (eventCall != null) {
eventCall.onClick();
}
}
});
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
but iam getting nullpointer exception on the touchkey.java
eventCall.onClick(); (eventCall is null)
I dont know where i am doing things wrong. can anybody help on this. Requirement: i need to handle the click event happening on the Library in Main Activity
You must create setter for eventCall (in touchkey.java):
public void setEventCall(eventCallback eventCall) {
this.eventCall = eventCall;
}
And, use it (in MainActivity):
View v = new touchkey(this);
setContentView(v);
((touchkey) v).setEventCall(this);

how to return result from asyn call

i have all my async calls in their own classes and so i dont want to have global vars being set aync'ly. To do this i want to return objects eg a string from my asunc postProcess methods.
can this be done?
Below is my general structure to my classes, i want to return a String for example from onPostExecute(). I see delegates are mentioned in other places but this seems very messy, sure there is a way to have a return type to the class or methods?
class GetStuffAsyncly extends AsyncTask<String, String, String>
{
// my vars....
public myconstructor(String dialogMessage, Context con)
{
this.qDialog = new ProgressDialog(con);
this.dialogString = dialogMessage;
this.context = con;
}
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute()
{
super.onPreExecute();
do stuff like fire dialog
}
#Override
protected String doInBackground(String... args)
{
// do stuff in background...
return data;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String jsonString)
{
// dismiss the dialog after getting all data
dialog.dismiss();
}
}
Some thing like below
class GetStuffAsyncly extends AsyncTask<String, String, String> {
String dialogString;
ProgressDialog dialog;
Context context;
AsyncListener listener;
// my vars....
public GetStuffAsyncly(String dialogMessage, Context con, AsyncListener listener) {
this.dialog = new ProgressDialog(con);
this.dialogString = dialogMessage;
this.context = con;
this.listener = listener;
}
/**
* Before starting background thread Show Progress Dialog
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
listener.onTaskStarted();
}
#Override
protected String doInBackground(String... args) {
// do stuff in background...
return data;
}
/**
* After completing background task Dismiss the progress dialog
**/
protected void onPostExecute(String jsonString) {
// dismiss the dialog after getting all data
dialog.dismiss();
listener.onTaskFinished(jsonString);
}
}
And the listener class
public interface AsyncListener {
void onTaskStarted();
void onTaskFinished(String data);
}
and you can call like this
new GetStuffAsyncly(message, this, new AsyncListener() {
#Override
public void onTaskStarted() {
//do your stuff
}
#Override
public void onTaskFinished(String data) {
//Do your stuff;
}
}).execute(parameter);
Another option is to use AsyncTaskLoader. You derive your class not from AsyncTask, but from AsyncTaskLoader. In your Activity you need to implement LoaderCallbacks interface. The args you want to use in Loader, you put in Bundle. All information you want to get from Loader will be passed in method onLoadFinished(). Here's an example
public class BaseInitLoader extends AsyncTaskLoader<Employee[]> {
Context mContext;
boolean firstrun;
public BaseInitLoader(Context context, Bundle args) {
super(context);
mContext = context;
firstrun = args.getBoolean("firstrun");
}
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad();
}
#Override
public Employee[] loadInBackground() {
MainActivity activity =(MainActivity) mContext;
Cursor cursor = new DatabaseFiller(activity.getDb(), mContext, firstrun).fillTable();
ArrayList<Employee> list = new ArrayList<>();
QueryResultIterable<Employee> itr = null;
try {
itr = cupboard().withCursor(cursor).iterate(Employee.class);
for(Employee employee: itr){
list.add(employee);
}
} finally {
// close the cursor
if (itr != null) {
itr.close();
}
}
Employee[] employees = new Employee[list.size()];
employees = list.toArray(employees);
return employees;
}
}
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks, View.OnClickListener {
/**
* ATTENTION: This was auto-generated to implement the App Indexing API.
* See https://g.co/AppIndexing/AndroidStudio for more information.
*/
TextView priority, name, innerPhone, mobilePhone, position;
Button cityBtn;
CharSequence[] cities;
SQLiteDatabase db;
Context mContext;
private Cursor cursor;
private SQLiteDatabase database;
private ListView listView;
private TextView nameTxt;
private EmployeeAdapter adapter;
public static final String LOG_TAG = "Database";
SharedPreferences prefs;
private boolean firstrun;
private ViewPager viewPager;
private TabLayout tabLayout;
private final int INITIAL = 1;
private final int SORT_NAME = 2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
Bundle args = new Bundle();
prefs = getSharedPreferences("ua.lanet.PipBoy", MODE_PRIVATE);
if(prefs.getBoolean("firstrun", true)){
args.putBoolean("firstrun", true);
prefs.edit().putBoolean("firstrun", false).apply();
}
else{
args.putBoolean("firstrun", false);
}
getLoaderManager().initLoader(INITIAL, args, this);
PipBoyDataHelper helper = new PipBoyDataHelper(this);
db = helper.getWritableDatabase();
}
public SQLiteDatabase getDb() {
return db;
}
#Override
public Loader onCreateLoader(int id, Bundle args) {
return new BaseInitLoader(mContext, args);
}
#Override
public void onLoadFinished(Loader loader, Object data) {
//do something with the data. Cast Object to your return type of loader
}
#Override
public void onLoaderReset(Loader loader) {
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.text:
getLoaderManager().initLoader(SORT_NAME, null, this);
break;
}
}
private class ViewPagerAdapter extends FragmentPagerAdapter{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
}
}

How to use interface to communicate between two activities

I am trying to make listener interface between two activities Act1 and Act2. Act1 will start Act2. If there is some event occurred in Act2, it will inform it to Act1. Problem is that I am starting new activity using Intent, so how Act1 will assign itself as listener to Act2's interface?
Act1.java
public class Act1 extends ActionBarActivity implements
ActionBar.OnNavigationListener {
ActionBar actionbar;
Intent pizzaIntent;
boolean visibleFirstTime = true;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menutab);
//set actionbar here
}
#Override
public boolean onNavigationItemSelected(int arg0, long arg1)// item pos,
// itemid
{
switch (arg0) {
case 0:
if(this.visibleFirstTime == false)
{
if(pizzaIntent == null)
{
pizzaIntent = new Intent(this,Act2.class);
//how to call setChangeListener?
}
startActivity(pizzaIntent);
}
else
this.visibleFirstTime = false;
break;
case 1:
System.out.println("selected: " + arg0);
break;
case 2:
System.out.println("selected: " + arg0);
break;
case 3:
System.out.println("selected: " + arg0);
break;
default:
break;
}
return true;
}
}
Act2.java
public class Act2 extends Activity {
selectionChangeListener listener;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pizza_slice_selection);
}
public void setChangeListener(selectionChangeListener listener)
{
this.listener = listener;
}
private interface selectionChangeListener
{
public void selectionMadeAtIndex(int index);
}
}
Note: Please don't suggest me to use fragments. I want to use activities currently.
I would suggest to create a model class. Let me give you an example:
Model class:
public class CustomModel {
public interface OnCustomStateListener {
void stateChanged();
}
private static CustomModel mInstance;
private OnCustomStateListener mListener;
private boolean mState;
private CustomModel() {}
public static CustomModel getInstance() {
if(mInstance == null) {
mInstance = new CustomModel();
}
return mInstance;
}
public void setListener(OnCustomStateListener listener) {
mListener = listener;
}
public void changeState(boolean state) {
if(mListener != null) {
mState = state;
notifyStateChange();
}
}
public boolean getState() {
return mState;
}
private void notifyStateChange() {
mListener.stateChanged();
}
}
And here's how you would use this:
// Imports
public class MainActivity extends Activity implements OnCustomStateListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomModel.getInstance().setListener(this);
boolean modelState = CustomModel.getInstance().getState();
Log.d(TAG, "Current state: " + String.valueOf(modelState));
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
#Override
public void stateChanged() {
boolean modelState = CustomModel.getInstance().getState();
Log.d(TAG, "MainActivity says: Model state changed: " +
String.valueOf(modelState));
}
}
Changing the member state in second activity:
// Imports
public class SecondActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CustomModel.getInstance().changeState(true);
}
}
LogCat output:
Current state: false
MainActivity says: Model state changed: true
Have you considered using LocalBroadcastManager?
In Act1's onCreate:
act2InitReceiver= new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
// do your listener event stuff
}
};
LocalBroadcastManager.getInstance(this).registerReceiver(act2InitReceiver, new IntentFilter("activity-2-initialized"));
In Act1's onDestroy:
LocalBroadcastManager.getInstance(this).unregisterReceiver(act2InitReceiver);
In Act2's onCreate:
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("activity-2-initialized"));
Give me a comment if the code doesn't compile, I'm writing it by hand.
The best, shortest and the easiest way to do this is to use static variables, like this:
class Main extends Activity {
static String message = "Hi";
}
class Another extends Activity {
public onCreate() {
Log.i(Main.message); // implementation of the message, 'Hi'
}
}

Categories