I have an Activity with a Recycler view handling cards. I have a SwipeRefreshLayout so when I swipe it updates the RecyclerView with new content. So far, so good.
Howerver, I want to update the RecyclerView every X seconds so if, for instance, I leave the activity with the recylcerview opened and I forgot to swipe it, it would as well update by itself.
To do that, I thought something like this:
( I ommitted necessary code ).
My main Activity which contains the recyclerview schedules a Job like this:
private void scheduleJob() {
ComponentName serviceName = new ComponentName(this, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresDeviceIdle(false)
.setRequiresCharging(false)
.setPeriodic(3000)
//.setOverrideDeadline(400) // Remove comment for faster testing.
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
Toast.makeText(this,"Start", Toast.LENGTH_LONG).show();
} } }
Inside MyJobService I through a Broadcast to my Activity, to let it know it has to update the content of the RecylcerView.
The responsable of receiving the broadcast is an inner class like this:
public static class MyReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context,"event",Toast.LENGTH_SHORT).show();
}
}
Problem is on my main Activity I hold two private variable called items (which is the ArrayList ) and adapter ( which is the adapter of the recylcerview) I pass items to the adapter to update the recyclerView content.However, as I am in a static inner class I can’t access outter class variables.
Whats the correct way to do something like this? I think I am messing too much and I guess there must be an easiest and more straight forward way to accomplish what I want.
Thank you very much
Well, turns out I was complicating things too much. It can be done with a simple Handler like this:
// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
// Do something here on the main thread
Toast.makeText(getApplicationContext(),"Update",Toast.LENGTH_SHORT).show();
ItemsDS itemsDS = new ItempsDS(getApplicationContext());
items = itemsDS.getItems();
adapter.clear();
adapter.addAll(items);
handler.postDelayed(runnableCode, 1000);
}
};
There are Two ways to do this thing:
First is as mentioned by #akshayBhat, you can do it by removing static keyword from BroadcastReceiver class:
ArrayList<String> items;
Adapter adapter;
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}
Second is to pass the reference of current activity state to the BroadCast Receiver and access ClassVariables there using that refernce;
ArrayList<String> items;
Adapter adapter;
public static class MyReceiver extends BroadcastReceiver {
AboutUs mContext;
#Override
public void onReceive(Context context, Intent intent) {
mContext = (AboutUs)context;
DataSource dataSource = new DataSource(context);
mContext.items = dataSource.getItems(); // can't do this due to outter class variable
mContext.adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}
Related
I have a SplashScreen Activity which call Asynctask Class to get information in internet.
I want to wait while my Asynctask is not finish (time during on internet speed connection)
My activity:
public static boolean test = true;
[...]
final Liste en_ce_moment = new Liste("En ce moment au cinéma", nowMovie);
mesListes.add(en_ce_moment);
//call my Asynctask file
fetchNowMovie process = new fetchNowMovie();
process.execute();
while(test)
{
}
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
My Asynctask:
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
SplashScreenActivity.test = false;
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Logically, the boolean became false in onPostExecute so the while loop stop and the intent have to start but the while loop never stop...
Let's do what you want to do in a safe way by using simple interface logic:
So we added our simple interface and we re-define your MyAsnycTask class's constructor like so:
public class MyAsnycTask extends AsyncTask
{
OnTaskFinished listener;
// Our simple interface
public interface OnTaskFinished {
void TimeToNextActivity();
}
// Your MyAsnycTask class constructor
public MyAsnycTask(OnTaskFinished l) {
listener = l;
}
. . .
As a last line of code in onPostExecute(), we're done whatever we're doing. So tell this via our listener:
listener.TimeToNextActivity();
To use our interface that we added earlier, your Activity must implements it. So we implements it. And in implemented method, we go to next Activity with Intent:
public class MyActivity extends Activity
implements MyAsnycTask.OnTaskFinished
{
#Override
public void TimeToNextActivity()
{
// Here go to next activity
Intent i = new Intent(this, MainActivity.class);
startActivity(i);
}
As we modified our MyAsnycTask class's constructor, we must initialize it like this:
MyAsnycTask process = new MyAsnycTask(this);
process.execute();
This looks like a problem hiding inside another problem, but to get through the first-level issue you could try using CountDownLatch instead of a static boolean:
CountDownLatch latch = new CountDownLatch(1);
fetchNowMovie process = new fetchNowMovie(latch);
process.execute();
latch.await();
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
You'll have to accept the latch as part of your AsyncTask's constructor:
private final CountDownLatch latch;
public fetchNowMovie(CountDownLatch latch) {
this.latch = latch;
}
// ...
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
latch.countDown();
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Try to use very simple library:
https://github.com/Arasthel/AsyncJobLibrary
Just start your Splash activity:
Intent i = new Intent(context, SplashScreenActivity.class);
startActivity(i);
In "onCreate" method of Splash activity do, what you need in background and after mission complete, do on main thread (update list):
AsyncJob.doInBackground(new AsyncJob.OnBackgroundJob() {
#Override
public void doOnBackground() {
//load from local DB or http-request:
List<Movie> movieList = fetchNowMovie();
//You can convert movieList to Json string, for example and save in SharedPreferences. Or you can use local DB for saving new movies.
// Send the result to the UI thread and show it
AsyncJob.doOnMainThread(new AsyncJob.OnMainThreadJob() {
#Override
public void doInUIThread() {
Intent i = new Intent(splashScreenContext, MainActivity.class);
startActivity(i);
//load movies from shared preferences or local Database in MainActivity (onCreate)
}
});
}
});
Connect library (build.gradle in app-project directory):
dependencies {
...
implementation 'com.arasthel:asyncjob-library:1.0.3'
...
}
I want to make an app lister application which fetches the application list via the packagemanager through an AsyncTask<Void, Void, List<PackageSummary>> nested in a singleton class. However, if and only if I implement the async task, the RecyclerView will not populate on the first OnCreate.
I am sure I am doing a silly mistake and/or do not understand AsyncTask and RecyclerView well enough, but for the love of me I cannot find the root of the issue.
In my toy app repository I have prepared two, relatively cleaner branches for illustration purposes:
One in which the packages are fetched in the main thread, and the recyclerview populates on first Oncreate (git_UI_thread).
One in which an AsyncTask<Void, Void, List<PackageSummary>> class is called. The application persistence is not set yet (on purpose), and the RecyclerView will only populate after the application is rotated (git_background_thread).
For those who are not inclined to click on the bitbucket link above, the code snippet of the inside of my AsyncTask looks like this:
#Override
protected void onPostExecute(List<SingletonPackageSummarySupplier.PackageSummary> packageSummaries) {
super.onPostExecute(packageSummaries);
isQueryingInProgress = false;
packageSummaryList = packageSummaries;
}
#Override
protected List<SingletonPackageSummarySupplier.PackageSummary> doInBackground(Void... voids) {
List<PackageSummary> installedPackages = new ArrayList<>();
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
List<ResolveInfo> resolveInfoList = context.getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfoList) {
ActivityInfo activityInfo = resolveInfo.activityInfo;
installedPackages.add(new PackageSummary(resolveInfo.activityInfo));
}
return installedPackages;
}
And this is my Main activity OnCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
psList = SingletonPackageSummarySupplier.getInstance(context).getPackageSummaryListReadOnly();
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerViewLayoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(recyclerViewLayoutManager);
adapter = new AdapterApplist(context, psList);
recyclerView.setAdapter(adapter);
}
And this is how the singleton is fetched:
static SingletonPackageSummarySupplier instance;
public static SingletonPackageSummarySupplier getInstance(Context context) {
if (instance == null) {
instance = new SingletonPackageSummarySupplier(context);
} else{
instance.updateInstance(context);
}
return instance;
}
P.S.: I think (but not sure) the singleton pattern is justified in order to diminish the changes of memory leaks.
P.S.2: I have read a couple questions about this, but none had an accepted / working solution.
There are two steps: Update your data to AdapterApplist and notify it. Hence you should create a new method like:
public void setData(List<SingletonPackageSummarySupplier.PackageSummary> list)
{ //reset your data ere
}
inside AdapterApplist class. Then, update your post:
#Override
protected void onPostExecute(List<SingletonPackageSummarySupplier.PackageSummary> packageSummaries) {
super.onPostExecute(packageSummaries);
isQueryingInProgress = false;
packageSummaryList = packageSummaries;
adapter.setData(packageSummaryList);
adapter.notifyDataSetChanged();
}
Just add adapter.notifyDataSetChanged(); in your postexecute method of AsyncTask
Either I was unable to implement the observer pattern (note that the asynctask was in a singleton class separate from the recyclerview and activity), or it is just not working well in this case.
Anyway, I ended up fixing the issue with the relatively new lifecycle-aware components: MutableLiveData, LiveData and AndroidViewModel (instead of viewmodel, which does not get context as constructor parameter). It is simple and elegant.
The key part was this, in the activity:
PackageSummarySupplier model = ViewModelProviders.of(this).get(PackageSummarySupplier.class);
model.getPackageSummaryList().observe(this, packageSummaryList -> {
adapter = new AdapterApplist(context, packageSummaryList);
recyclerView.setAdapter(adapter);
});
I have an Http call class I made to send some images. I want these requests to be done in the background so the user can continue doing whatever they want in the app.
When the call has finished executing, I want to remove the Item from the listview and update it.
Here is my code.
public HttpCall(ArrayList<Jobs> jobs, ArrayList<GridImages> images, ArrayList<GridImages> signatures, Context context,TwoColumnListAdapter customadapter) {
this.jobs = jobs;
this.images = images;
this.signatures = signatures;
this.context = context;
listView = (SwipeMenuListView) ((Activity) context).findViewById(R.id.listView);
adapter = customadapter;
mydb = new Database_Helper(context);
}
public void SendALL(){
//Calls the http requests and then executes the update for the listview.
listView.post(new Runnable() {
#Override
public void run() {
// Intent intent = new Intent(context,AddJob.class);
// context.startActivity(intent);
listView.setAdapter(adapter);
adapter.remove(jobs.get(0));
adapter.notifyDataSetChanged();
}
});
}
I could start a new Intent and then it will show that it updated the listview as the AddJob class makes a call to the database and updates the listview.
I wrote a collection widget to show calllogs in home screen.
So there may have 2 receivers exist: one is widget provider the other is call log content observer. Im not sure whether content observer is a receiver or not.
CallLogAppWidgetProvider is declared in manifest.xml and I need to store some data in this receiver object (for using later in observer).
CallLogContentObserver is constructed and registered when WidgetProvider triggered. Observer is triggered when calls are made.
The problem is, when I first place my widget, widgetprovide is constructed, triggered and onUpdate is called, I can see CallLogAppWidgetProvider.mContext and CallLogAppWidgetProvider.mWidgetIds is set in app's main thread(tid=8148,pid=8148).
but when calls are made, CallLogContentObserver.onChange is called in the same thread(tid=8148,pid=8148). Inside onChange, mContext has the value that set previously but mWidgetIds is null, so mywidget could not reflect calllog's changes.
According to this post: Save data in Broadcast receiver , my widgetprovider may be destroyed when it returned, so it could explain why mWidgetIds is null, but conflict with mContext stored old value. And that post did not explain why we need persist our data in receivers.
If widgetprovider is destroyed, then when observer triggered how did android start to run observer's onChange? Is it the same mechanism like closures in lua? Why mContext can store a value?
If widgetprovider is not destroyed, then why mWidgetIds is null when observer runs? I dont know exactly how java passes primitive array, according to this post: Is an array a primitive type or an object (or something else entirely)? , arrays are runtime classes created by jvm, so mWidgetIds is a strong reference, the integer array mWidgetIds points to can not be gc after provider returned, because at least mWidgetIds points to it. What happend when provider returned?
Could you please tell me what on earth happend behind thses scenes? Any information will be precitated, thank you.
code is below:
public class CallLogAppWidgetProvider extends AppWidgetProvider {
private static final String TAG = "CallLogAppWidgetProvider";
private ContentObserver mContentObserver = null;
private Handler mHandler = new Handler();
private Context mContext = null; //data to store
private int[] mWidgetIds; //data to store
protected class CallLogContentObserver extends ContentObserver {
public CallLogContentObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
#Override
public void onChange(boolean selfChange) {
AppWidgetManager.getInstance(mContext).notifyAppWidgetViewDataChanged(mWidgetIds, R.id.calllog_list);
}
}
#Override
public void onEnabled(Context context) {
mContext = context; //store data when first place in home screen
mContentObserver = new CallLogContentObserver(mHandler);
context.getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, mContentObserver);
}
#Override
public void onDisabled(Context context) {
context.getContentResolver().unregisterContentObserver(mContentObserver);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
mWidgetIds = appWidgetIds; //store data when first place in home screen
for (int i = 0; i < appWidgetIds.length; i++) {
Intent intent = new Intent(context, CallLogRemoteViewsService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calllog_appwidget);
rv.setRemoteAdapter(R.id.calllog_list, intent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
Hi I'm trying to run a code which has one activity and a BroadcastReceiver which runs when new message comes and yes it's run clearly but I have a problem with BroadcastReceiver object !
It's part of MainActivity CLASS :
public class MainActivity extends FragmentActivity {
private IncomingSms checkAndDo; //=> OBJECT
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkAndDo= new IncomingSms();
checkAndDo.setProgramState(210); // program state is a variable
checkAndDo.getProgramState(this); // Toast output : " >>>210 "
....
But problem starts when a new message comes and onReceived() called ! IncommingSms class :
public class IncomingSms extends BroadcastReceiver {
private int programState=110; // Which state we are ? 110=> white / 111=> off / 210=>black ...
public void onReceive(Context context, Intent intent) {
final Bundle bundle = intent.getExtras();
this.getProgramState(context);
// This method called again but toast output is : ">>>110"
// which is initial value !?
AND ....
}
public void setProgramState(int status) {
this.programState=status;
}
public void getProgramState( Context context) {
Toast.makeText(context, ">>>"+this.programState , Toast.LENGTH_LONG).show();
}
question : I'm not sure why it happens but onReceive() uses only the initial value which is so bad . ANY Idea?
this.getProgramState(context);
// This method called again but toast output is : ">>>110"
// which is initial value !?
You met that problem because you registered your BroadcastReceiver in the manifest. So Android will create a new BroadcastReceiver and pass the sms broadcast to it.
If you would like to get the state you set in your program, you must register your BroadcastReceiver in your Activity.
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(checkAndDo, filter);
using Intent data instead bundle
Intent(ContextHolder.applicationContext!!, NotificationDeleteReceiver::class.java).setData(conversation.conversationId.toUri())