I need to pass the activity context to my service as soon as the activity is being constructed. Here is my code:
public class myService extends Service
{
private AppCompatActivity activity;
public void setActivity(AppCompatActivity activity)
{
this.activity = activity;
}
}
public class myActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
// ... some things are being executed and myService is being bound
mService.setActivity(this);
}
}
I get NullPointerException as - I suppose - the myActivity class is still being constructed and the reference cannot be passed. How can I make Android run this method after onCreate? I found some solutions for Java involving factory pattern but I'm not sure how can I use it, if I can use it in my case at all.
Service is a Context by itself. So if you need the Context only, you can just call this in your Serviceclass.
Alternatively, you should pass the Activity to the service before starting it. Make sure you pass the Activity after calling super.onCreate(bundle);
However, you should not manipulate your Activity or it's views from a Service. A better way is notifying your Activity from your Service.
Notify activity from service
Edit: Observer pattern
Create a new class called NotificationCenter.java
public class NotificationCenter {
private static int totalEvents = 1;
public static final int updateActivity = totalEvents++;
// you can add more events
// public static final int anotherEvent = totalEvents++;
private final SparseArray<ArrayList<Object>> observers = new SparseArray<>();
private final SparseArray<ArrayList<Object>> removeAfterBroadcast = new SparseArray<>();
private final SparseArray<ArrayList<Object>> addAfterBroadcast = new SparseArray<>();
private int broadcasting = 0;
public interface NotificationCenterDelegate {
void didReceivedNotification(int id, Object... args);
}
private static volatile NotificationCenter Instance = null;
public static NotificationCenter getInstance() {
NotificationCenter localInstance = Instance;
if (localInstance == null) {
synchronized (NotificationCenter.class) {
localInstance = Instance;
if (localInstance == null) {
Instance = localInstance = new NotificationCenter();
}
}
}
return localInstance;
}
public void postNotificationName(int id, Object... args) {
broadcasting++;
ArrayList<Object> objects = observers.get(id);
if (objects != null && !objects.isEmpty()) {
for (int a = 0; a < objects.size(); a++) {
Object obj = objects.get(a);
((NotificationCenterDelegate) obj).didReceivedNotification(id, args);
}
}
broadcasting--;
if (broadcasting == 0) {
if (removeAfterBroadcast.size() != 0) {
for (int a = 0; a < removeAfterBroadcast.size(); a++) {
int key = removeAfterBroadcast.keyAt(a);
ArrayList<Object> arrayList = removeAfterBroadcast.get(key);
for (int b = 0; b < arrayList.size(); b++) {
removeObserver(arrayList.get(b), key);
}
}
removeAfterBroadcast.clear();
}
if (addAfterBroadcast.size() != 0) {
for (int a = 0; a < addAfterBroadcast.size(); a++) {
int key = addAfterBroadcast.keyAt(a);
ArrayList<Object> arrayList = addAfterBroadcast.get(key);
for (int b = 0; b < arrayList.size(); b++) {
addObserver(arrayList.get(b), key);
}
}
addAfterBroadcast.clear();
}
}
}
public void addObserver(Object observer, int id) {
if (broadcasting != 0) {
ArrayList<Object> arrayList = addAfterBroadcast.get(id);
if (arrayList == null) {
arrayList = new ArrayList<>();
addAfterBroadcast.put(id, arrayList);
}
arrayList.add(observer);
return;
}
ArrayList<Object> objects = observers.get(id);
if (objects == null) {
observers.put(id, (objects = new ArrayList<>()));
}
if (objects.contains(observer)) {
return;
}
objects.add(observer);
}
public void removeObserver(Object observer, int id) {
if (broadcasting != 0) {
ArrayList<Object> arrayList = removeAfterBroadcast.get(id);
if (arrayList == null) {
arrayList = new ArrayList<>();
removeAfterBroadcast.put(id, arrayList);
}
arrayList.add(observer);
return;
}
ArrayList<Object> objects = observers.get(id);
if (objects != null) {
objects.remove(observer);
}
}
}
Then make your Activities look like this, you receive messages from the Service in didReceivedNotification()
public class YourActivity implements NotificationCenter.NotificationCenterDelegate {
#Override
public void onPause() {
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.updateActivity);
super.onPause();
}
#Override
public void onResume() {
NotificationCenter.getInstance().addObserver(this, NotificationCenter.updateActivity);
super.onResume();
}
#Override
public void didReceivedNotification(int id, Object... args) {
if (id == NotificationCenter.updateActivity) {
// do something with your activity, your service called this
}
}
}
Finally send messages in your Service to all the Activities which are listening:
NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateActivity, optionalData);
Which is very nice, you don't have to pass Activity instances.
NotificationCenter source is from Telegram.
public class myService extends Service
{
public static myActivity activity;
public static void setActivity(myActivity activity)
{
this.activity = activity;
}
public void useActivityExample()
{
myService.myActivity.update();
}
}
public class myActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
// ... some things are being executed and myService is being bound
mService.setActivity(getActivity());
}
}
Related
I am trying to test my code by using Mockito
static class SongAdapterPresenter implements SortedSongSelectionContract.SongAdapterContract.Presenter {
private List<Song> songs;
private final Presenter sortedSongSelectionPresenter;
private final SortedSongSelectionContract.SongAdapterContract.Adapter adapter;
private SortedSongSelectionContract.SongAdapterContract.SongView selectedSongView;
private Song selectedSong;
SongAdapterPresenter(SortedSongSelectionContract.SongAdapterContract.Adapter adapter, SortedSongSelectionContract.Presenter sortedSongSelectionPresenter) {
this.adapter = adapter;
this.sortedSongSelectionPresenter = sortedSongSelectionPresenter;
}
#Override
public int getItemCount() {
return songs != null ? songs.size() : 0;
}
#Override
public void onBindView(SortedSongSelectionContract.SongAdapterContract.SongView songView, int position) {
Song song = songs.get(position);
songView.setTitle(song.getName());
songView.setArtists(song.getArtists());
List<Genre> genres = song.getGenres();
int size = genres.size();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
builder.append(genres.get(i).getName()).append(",");
}
int length = builder.length();
if (length > 0) {
builder.deleteCharAt(length - 1);
}
songView.setGenres(builder.toString());
songView.showPlayIcon(true);
boolean select = isSongEqual(song);
if (select) {
selectedSongView = songView;
}
songView.showSelectionUi(select);
}
#Override
public void onItemClicked(SortedSongSelectionContract.SongAdapterContract.SongView songView, int position) {
Song song = songs.get(position);
if (isSongEqual(song)) {
return;
}
deSelect(song);
selectedSongView = songView;
selectedSongView.showSelectionUi(true);
selectedSong = songs.get(position);
sortedSongSelectionPresenter.getBus().post(new BusEvents.SongSelected(selectedSong));
}
#Override
public void setSongs(List<Song> songs) {
this.songs = songs;
adapter.refresh();
}
#Override
public void deSelect(Song song) {
if (!isSongEqual(song)) {
if (selectedSongView != null) {
selectedSongView.showSelectionUi(false);
}
selectedSong = null;
selectedSongView = null;
}
}
private boolean isSongEqual(Song song) {
return !(song == null || selectedSong == null) && (song == selectedSong || selectedSong.getId().equals(song.getId()));
}
}
//endregion
//region Instance methods
private void processEvent(Object event) {
if (event instanceof BusEvents.SongSelected) {
deSelect(((BusEvents.SongSelected) event).getSong());
}
}
//endregion
}
I want to write test for onBindView
Following is my Test Class
RunWith(PowerMockRunner.class)
#PrepareForTest(Log.class)
public class SongAdapterPresenterTest {
private SortedSongSelectionPresenter.SongAdapterPresenter songAdapterPresenter;
#Mock
private SortedSongSelectionContract.SongAdapterContract.Adapter adapter;
#Mock
private SortedSongSelectionContract.Presenter presenter;
#Mock
private SortedSongSelectionContract.SongAdapterContract.SongView songView;
private Song song;
private List<Song> songList;
#Before
public void setUp() {
song = new Song("1", "A", "B");
songList = new ArrayList<>(1);
songList.add(song);
songAdapterPresenter = new SortedSongSelectionPresenter.SongAdapterPresenter(adapter, presenter);
}
#Test
public void getItemCountWithSongListNotNull_returnSongListSize() {
songAdapterPresenter.setSongs(songList);
Assert.assertEquals(songList.size(), songAdapterPresenter.getItemCount());
}
#Test
public void getItemCountWithSongListNull_returnIsZero(){
songAdapterPresenter.setSongs(null);
Assert.assertEquals(0, songAdapterPresenter.getItemCount());
}
#Test
public void testonBindView() {
songAdapterPresenter.onBindView(songView, 1);
verify(songView).showSelectionUi(true);
}
#Test
public void deSelect_SongRemoved(){
songAdapterPresenter.deSelect(song);
verify(songView).showSelectionUi(false);
}
}
I have created Mock Object of View and object of my PresenterAdapter class. I am not getting what causes the error.
I keep getting NullPointer when i execute my onBindView Test
Any help would be greatly appreciated.
You never call setSongs and that's why you get a NullPointerException.
Also note that your songList has only one element.
Change your code to:
#Test
public void testonBindView() {
songAdapterPresenter.setSongs(songList);
songAdapterPresenter.onBindView(songView, 0);
verify(songView).showSelectionUi(true);
}
I have a splash screen using AsyncTask, it will download some data from database and store the data in ArrayList. This ArrayList will be used for RecyclerView in fragments of MainActivity.class.
The problem is when I run the app from Android Studio to my phone, everything works perfectly. But, when I destroy the app and run it manually from my phone it will display blank white screen and then it will crash. And if I run once again after it crashed, the app will work. So, this app will always work only if I run it from Android Studio or after it crashed.
The error says that it is caused by the empty list. If I'm not mistaken, I think the AsyncTask doesn't seem to work properly after the activity is destroyed. But I don't know how to fix it. Please help me to solve this problem.
SplashScreen.java
public class SplashScreenActivity extends AppCompatActivity {
public static Event event;
private static List<Feed> feedList;
private static List<Event> eventList;
private static List<Event> todayList;
private static List<Event> upcomingList;
private static List<Partner> partnerList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splashscreen);
Time today = new Time(Time.getCurrentTimezone());
today.setToNow();
Config.TODAY_DATE = String.valueOf(today.monthDay) + "-" + String.valueOf(today.month) + "-" + String.valueOf(today.year);
new DownloadData().execute("");
}
class DownloadData extends AsyncTask<String, Integer, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
startActivity(new Intent(getBaseContext(), WelcomeActivity.class));
finish();
}
#Override
protected String doInBackground(String... params) {
RequestHandler rh = new RequestHandler();
String JSON_STRING = rh.sendGetRequest(Config.URL_GET_ALL_DATA);
JSONObject jsonObject;
eventList = new ArrayList<>();
todayList = new ArrayList<>();
upcomingList = new ArrayList<>();
partnerList = new ArrayList<>();
feedList = new ArrayList<>();
try {
jsonObject = new JSONObject(JSON_STRING);
JSONArray getEvent = jsonObject.getJSONArray(Config.TAG_JSON_EVENT);
for (int i = 0; i < getEvent.length(); i++) {
int id = getEvent.getJSONObject(i).getInt(Config.TAG_ID);
int eoId = getEvent.getJSONObject(i).getInt(Config.TAG_EO_ID);
String eoName = getEvent.getJSONObject(i).getString(Config.TAG_EO_NAME);
String title = getEvent.getJSONObject(i).getString(Config.TAG_TITLE);
String day = getEvent.getJSONObject(i).getString(Config.TAG_DAY);
String date = getEvent.getJSONObject(i).getString(Config.TAG_DATE);
int price = getEvent.getJSONObject(i).getInt(Config.TAG_PRICE);
event = new Event(id, eoId, eoName, title, day, date, price);
eventList.add(event);
if(Config.TODAY_DATE.equals(event.getDate())){
todayList.add(event);
} else {
upcomingList.add(event);
}
}
JSONArray getPartner = jsonObject.getJSONArray(Config.TAG_JSON_PARTNER);
for (int i = 0; i < getPartner.length(); i++) {
int pId = getPartner.getJSONObject(i).getInt(Config.TAG_ID);
String pName = getPartner.getJSONObject(i).getString(Config.TAG_NAME);
String pEmail = getPartner.getJSONObject(i).getString(Config.TAG_EMAIL);
String pPhone = getPartner.getJSONObject(i).getString(Config.TAG_PHONE);
String pPhoto = getPartner.getJSONObject(i).getString(Config.TAG_PHOTO_URL);
Partner partner = new Partner(pId, pName, pEmail, pPhone, pPhoto);
partnerList.add(partner);
}
JSONArray getArticle = jsonObject.getJSONArray(Config.TAG_JSON_ARTICLE);
for (int i = 0; i < getArticle.length(); i++) {
int feedId = getArticle.getJSONObject(i).getInt(Config.TAG_ID);
String feedAuthor = getArticle.getJSONObject(i).getString(Config.TAG_FEED_AUTHOR);
String feedTitle = getArticle.getJSONObject(i).getString(Config.TAG_FEED_TITLE);
String feedContent = getArticle.getJSONObject(i).getString(Config.TAG_FEED_CONTENT);
String feedDate = getArticle.getJSONObject(i).getString(Config.TAG_FEED_DATE);
String feedThumbnail = getArticle.getJSONObject(i).getString(Config.TAG_FEED_THUMBNAIL);
Feed feed = new Feed(feedId, feedAuthor, feedTitle, feedContent, feedDate, feedThumbnail);
feedList.add(feed);
}
} catch (JSONException e) {
e.printStackTrace();
}
return JSON_STRING;
}
}
public static List<Feed> getFeedList(){ return feedList;}
public static List<Event> getEventList() {return eventList;}
public static List<Event> getTodayList() { return todayList;}
public static List<Event> getUpcomingList() { return upcomingList;}
public static List<Partner> getPartnerList() {return partnerList;}
}
DiscoverFragment.java
public class DiscoverFragment extends Fragment implements ViewPager.OnPageChangeListener, View.OnClickListener {
protected View view;
private LinearLayout pager_indicator;
private int dotsCount;
private ImageView[] dots;
private List<Feed> feedList;
private List<Event> eventList;
private List<Partner> partnerList;
public DiscoverFragment() {}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_discover, container, false);
RecyclerView recyclerViewEvent = (RecyclerView) view.findViewById(R.id.discover_event_recycler_view);
RecyclerView recyclerViewPartner = (RecyclerView) view.findViewById(R.id.discover_partner_recycler_view);
ClickableViewPager intro_images = (ClickableViewPager) view.findViewById(R.id.pager_introduction);
pager_indicator = (LinearLayout) view.findViewById(R.id.viewPagerCountDots);
eventList = SplashScreenActivity.getEventList();
partnerList = SplashScreenActivity.getPartnerList();
feedList = SplashScreenActivity.getFeedList();
EventAdapter eventAdapter = new EventAdapter(getContext(), eventList);
DiscoverPartnerAdapter discoverPartnerAdapter = new DiscoverPartnerAdapter(getContext(), partnerList);
DiscoverFeedAdapter mAdapter = new DiscoverFeedAdapter(getContext(), feedList);
final LinearLayoutManager layoutManagerEvent = new LinearLayoutManager(getContext());
final LinearLayoutManager layoutManagerPartner = new LinearLayoutManager(getContext());
layoutManagerEvent.setOrientation(LinearLayoutManager.HORIZONTAL);
layoutManagerPartner.setOrientation(LinearLayoutManager.HORIZONTAL);
addBottomDots(0);
intro_images.setAdapter(mAdapter);
intro_images.setCurrentItem(0);
intro_images.addOnPageChangeListener(this);
intro_images.setOnItemClickListener(new ClickableViewPager.OnItemClickListener() {
#Override
public void onItemClick(int position) {
Config.FEED_ID = position;
startActivity(new Intent(getContext(), ArticleActivity.class));
}
});
return view;
}
private void addBottomDots(int currentPage) {
dots = new ImageView[feedList.size()]; //the problem
pager_indicator.removeAllViews();
for (int i = 0; i < dots.length; i++) {
dots[i] = new ImageView(getContext());
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
pager_indicator.addView(dots[i], params);
}
if (dots.length > 0)
dots[currentPage].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
}
#Override
public void onClick(View v) {
switch (v.getId()) {
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
addBottomDots(position);
for (int i = 0; i < dotsCount; i++) {
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
}
dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
}
}
LogCat
01-29 00:40:57.565 32535-32535/com.irmaelita.esodiaapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.irmaelita.esodiaapp, PID: 32535
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
at com.irmaelita.esodiaapp.fragment.DiscoverFragment.addBottomDots(DiscoverFragment.java:181)
at com.irmaelita.esodiaapp.fragment.DiscoverFragment.onCreateView(DiscoverFragment.java:158)
feedList is null. You create feedList instance when DownloadData task is executed. But you call feedList.size() in addBottomDots when fragment view should be created. So, most probably addBottomDots is called before DownloadData task is executed. You need to fix it.
The feedlist in your discover fragment is going empty while initializing. Please set a null check before doing so.It not about running from android studio.If I have understood it correctly you are trying to access a list from splasScreen activity after finishing it. ie in post execute you finish the current activity and the fragment is in main activity,so the list is going null.So if this is the case (and please correct me if not) then either download the data somewhere centrally or best way send it to main activity with intent and use it there. Also when running from android studio kill the app manually and run it again,while the phone is connected and see if it crashes in current scenario.
Send your data from doInBackground to MainActivity with sendBroadcast
Add broadcast method in DownloadData class
private void broadcast(SplashParcel parcel) {
Intent i = new Intent("splash_parcel");
i.putExtra("values", parcel);
sendBroadcast(i);
}
#Override
protected String doInBackground(String... params) {
// your code
// ..
try {
// your code
// ..
// send splashParcel to MainActivity
SplashParcel splashParcel = new SplashParcel(feedList, eventList, todayList, upcomingList, partnerList);
broadcast (splashParcel);
} catch (JSONException e) {
e.printStackTrace();
}
return JSON_STRING;
}
Add new class SplashParcel.java
public class SplashParcel implements Parcelable {
public static final Creator<SplashParcel> CREATOR = new Creator<SplashParcel>() {
#Override
public SplashParcel createFromParcel(Parcel in) {
return new SplashParcel(in);
}
#Override
public SplashParcel[] newArray(int size) {
return new SplashParcel[size];
}
};
private static List<Feed> _feedList;
private static List<Event> _eventList;
private static List<Event> _todayList;
private static List<Event> _upcomingList;
private static List<Partner> _partnerList;
protected SplashParcel(Parcel in) {
_feedList = new ArrayList<Feed>();
in.readList(_feedList, null);
_eventList = new ArrayList<Event>();
in.readList(_eventList, null);
_todayList = new ArrayList<Event>();
in.readList(_todayList, null);
_upcomingList = new ArrayList<Event>();
in.readList(_upcomingList, null);
_partnerList = new ArrayList<Partner>();
in.readList(_partnerList, null);
}
public SplashParcel(List<Feed> feedList, List<Event> eventList, List<Event> todayList, List<Event> upcomingList, List<Partner> partnerList) {
_feedList = feedList;
_eventList = eventList;
_todayList = todayList;
_upcomingList = upcomingList;
_partnerList = partnerList;
}
public SplashParcel() {
}
public List<Feed> getFeedList() {
return _feedList;
}
public void setFeedList(List<Feed> feedList) {
_feedList = feedList;
}
public List<Event> getEventList() {
return _eventList;
}
public void setEventList(List<Event> eventList) {
_eventList = eventList;
}
public List<Event> getTodayList() {
return _todayList;
}
public void setTodayList(List<Event> todayList) {
_todayList = todayList;
}
public List<Event> getUpcomingList() {
return _upcomingList;
}
public void setUpcomingList(List<Event> upcomingList) {
_upcomingList = upcomingList;
}
public List<Partner> getPartnerList() {
return _partnerList;
}
public void setPartnerList(List<Partner> partnerList) {
_partnerList = partnerList;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeList(_feedList);
parcel.writeList(_eventList);
parcel.writeList(_todayList);
parcel.writeList(_upcomingList);
parcel.writeList(_partnerList);
}
}
MainActivity.java
// member variable
private BroadcastReceiver _splashReceiver;
private Bundle _bundle = new Bundle();
#Override
protected void onResume() {
super.onResume();
splashReceiver();
}
// receive splashParcel from SplashScreenActivity
private void splashReceiver() {
if (_splashReceiver == null) {
_splashReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
SplashParcel splashParcel = intent.getParcelableExtra("values");
if (splashParcel != null) {
// save splashParcel into _budle
_bundle.putParcelable("splash_parcel", splashParcel);
}
}
};
registerReceiver(_splashReceiver, new IntentFilter("splash_parcel"));
}
}
//Send _bundle to DiscoverFragment
private void showDiscoverFragment(){
if(_bundle != null) {
// create instance of discoverFragment with passing _bundle as arguments
DiscoverFragment discoverFragment = new DiscoverFragment();
discoverFragment.setArguments(_bundle);
// replace activity_main.xml with discoverFragment
getSupportFragmentManager().beginTransaction().replace(R.id.main_container, discoverFragment).addBackStack(null).commit();
}
}
In onCreateView of DiscoverFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
SplashParcel splashParcel = getArguments().getParcelable("splash_parcel");
if(splashParcel != null) {
// your splashParcel ready in here
List<Feed> feedList = splashParcel.getFeedList()
List<Event> eventList = splashParcel.getEventList()
List<Event> todayList = splashParcel.getTodayList();
List<Event> upcommingList = splashParcel.getUpcomingList();
List<Partner> partnerList = splashParcel.getPartnerList();
}
}
1.. If i run two methods create2YearsDatabse(); at one anpplication run session
then kill the program completely and then in the next run run the method :
updateAutoGeneratedCalendar();
then the result is as expected it takes about
2-3 MB of memory
2. But if i run create2YearsDatabse() and onSuccess() callback of async Task it is using then the memory it takes in internal memory suddenly goes to more than 400 MB.
// The methods are managed in this way:
public void create2YearsDatabase() {
new BGAsyncTasks(context, new ThreadCallBack() {
#Override
public void onSuccess() {
final SettingAndStatusDTO settingDto = realm.where(SettingAndStatusDTO.class).findFirst();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
//because two years has more or equal to 730 days
if (realm.where(CalendarDto.class).findAll().size() >= 730) {
settingDto.setIs2YearsFullDBCreated(true);
context.getSharedPreferences(context.getString(R.string.shared_pref_name),Context.MODE_PRIVATE).edit().putBoolean(SplashActivity.FIRST_TIME_RUN,false).apply();
}
}
});
SplashActivity.freshRun = false;
startDashBoard();
}
#Override
public void onFailure() {
}
}, BGAsyncTasks.CREATE_INITIALIZE_2_YEARS_CALENDAR).execute();
}
public void updateAutoGeneratedCalendar() {
new BGAsyncTasks(context, new ThreadCallBack() {
#Override
public void onSuccess() {
Log.i("datatest", "full data size" + realm.where(CalendarDto.class).findAll().size());
final SettingAndStatusDTO settingDto = realm.where(SettingAndStatusDTO.class).findFirst();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
if (realm.where(CalendarDto.class).findAll().size() >= 32000)
settingDto.setIs90YearsDBCreated(true);
}
});
}
#Override
public void onFailure() {
}
}, BGAsyncTasks.CREATE_AUTO_GENERATE_CALENDAR).execute();
}
And My Async Task Looks like this :
public class BGAsyncTasks extends AsyncTask<Void, Void, Void> {
// Intention variables
public static final int CREATE_INITIALIZE_2_YEARS_CALENDAR = 0;
public static final int CREATE_AUTO_GENERATE_CALENDAR = 1;
// Message from the Activity/Fragment.
ThreadCallBack callBack;
Context context;
int intention;
IParseData parser ;
//Constructor for bg processes.
public BGAsyncTasks(Context c, ThreadCallBack callBack, int DATA_TYPE_FROM_RES) {
this.callBack = callBack;
this.context = c;
this.intention = DATA_TYPE_FROM_RES;
this.parser = new ParseData(context);
}
#Override
protected Void doInBackground(Void... params) {
switch (intention) {
case CREATE_AUTO_GENERATE_CALENDAR: {
final Realm asyncRealm = Realm.getDefaultInstance();
for (int i = 2000; i <= 2090; i++) {
if (i < 2072 || i > 2073) {
for (int j = 0; j < 12; j++) {
parser.setOnemonthData(i, j);
}
}
Log.i("datatest", "year:" + i);
}
}
case CREATE_INITIALIZE_2_YEARS_CALENDAR: {
String thisMonthString;
for (int i = 2072; i <=2073; i++) {
for (int j = 0; j < 12; j++) {
thisMonthString = getStringByMonth(i, j);// returns the json string
parser.parseOneMonthData(fixFormatting(thisMonthString));
}
}
}
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
callBack.onSuccess();
}
}
And my Parse and saving to Database methods look like this:
public class ParseData implements IParseData {
Context context;
Realm realm;
CalendarDto mCalendarDto;
public ParseData(Context c) {
context = c;
mCalendarDto = new CalendarDto();
}
public void parseOneMonthData(String monthData) {
//parse json data of one month and return as DTO of size equal
//to no of days in that month
try {
JSONArray oneMonthJsonData = new JSONArray(monthData);
int length = oneMonthJsonData.length();
for (int i = 0; i < length; i++) {
saveOneDayData(oneMonthJsonData.optJSONObject(i));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public void saveOneDayData(final JSONObject singleTouple) {
realm = Realm.getDefaultInstance();
// parsing the data of one day so that it can be used everywhere.
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
mCalendarDto.setDayInfo(singleTouple.optString(DataItems.DAY_INFO));
mCalendarDto.setMahina(singleTouple.optString(DataItems.MAHINA));
realm.copyToRealmOrUpdate(mCalendarDto);
}
});
}
// saving data by month and year
public void setOnemonthData(final int yr, final int mnt) {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
mCalendarDto.setMonthEnId(currentEngMonth);
mCalendarDto.setMonthNpId(month);
realm.copyToRealmOrUpdate(mCalendarDto);
}
}
});
}
}
Here is my Calendar Realm Object :
public class CalendarDto extends RealmObject {
public CalendarDto() {
}
#PrimaryKey
private int primaryDayId
private String sakey;
private String raja;
private String mantri;
private String nepalSambat;
// more variables and ...........
//// autogenerated getters and settetrs
////////
}
#Override
protected Void doInBackground(Void... params) {
switch (intention) {
case CREATE_AUTO_GENERATE_CALENDAR: {
final Realm asyncRealm = Realm.getDefaultInstance(); // <--- this line
and
public void saveOneDayData(final JSONObject singleTouple) {
realm = Realm.getDefaultInstance(); // <--- this line
and
public void setOnemonthData(final int yr, final int mnt) {
realm = Realm.getDefaultInstance(); // <--- this line
On background threads, you should close the Realm instance after using them in a finally block.
In this case, you are opening a new Realm instance for every single day twice, and you're not closing any of them.
On a background thread, best practice is to open only one Realm instance, and then close it after the execution is complete.
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
//do things
} finally {
if(realm != null) {
realm.close();
}
}
I'm experimenting a bit with Dagger on Android which seems to be a nice tool to isolate dependencies. In the first place I copied the android-activity-graphs example from GitHub: https://github.com/square/dagger/tree/master/examples/android-activity-graphs
I then added a couple of classes to the ActivityModule
#Module(
injects = {
HomeActivity.class,
HomeFragment.class
},
addsTo = AndroidModule.class,
library = true
)
public class ActivityModule {
private static final String TAG = "Activity_Module";
private final DemoBaseActivity activity;
public ActivityModule(DemoBaseActivity activity) {
this.activity = activity;
}
/**
* Allow the activity context to be injected but require that it be annotated with
* {#link ForActivity #ForActivity} to explicitly differentiate it from application context.
*/
#Provides
#Singleton
#ForActivity
Context provideActivityContext() {
return activity;
}
#Provides
#Singleton
ActivityTitleController provideTitleController() {
return new ActivityTitleController(activity);
}
//My addition from here
#Provides
#Singleton
Player providePlayer() {
Log.i(TAG, "in providePlayer()");
return new MyAndroidTestPlayer(activity);
}
#Provides
RandomNumberGenerator provideRandomNumberGenerator() {
Log.i(TAG, "in provideRandomNumberGenerator()");
return new RealRandomNumberGenerator();
}
}
The rest of the graph initialization is identical to the example from github.
The thing that puzzles me is the fact that the injected object are null after the construction of the class they are injected into (HomeFragment)... for a while.
Again HomeFragment is more or less identical to HomeFragment from the examples in github, with a few additions of my own.
If I call whatever on either of the injected Player or RandomNumberGenerator objects in the onCreateView() of the HomeFragment I get an error saying they are null.
However if I call them inside the inner OnClickListener - onClick() they work as expected.
Can anyone point me to the piece of knowledge I am missing to understand what is going on here?
public class HomeFragment extends DemoBaseFragment {
public static final String TAG = "HOME_FRAGMENT";
public static HomeFragment newInstance() {
return new HomeFragment();
}
#Inject
ActivityTitleController titleController;
#Inject
Player player;
#Inject
RandomNumberGenerator randomNumberGenerator;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
if (randomNumberGenerator != null) {
Log.i(TAG, "randomNumberGenerator is NOT null");
} else {
Log.e(TAG, "randomNumberGenerator is NULL!");
}
if (player != null) {
Log.i(TAG, "player is NOT null");
} else {
Log.e(TAG, "player is NULL!");
}
//int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
//player.playTestNote();
tv.setGravity(CENTER);
tv.setText("Play test note");
tv.setTextSize(40);
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
player.playTestNote();
int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
Log.i(TAG, "Text view clicked, random number is: " + randomNumber);
}
});
return tv;
}
The classes I'm using to test with are pretty trivial (RandomNumberGenerator more so than the Player class). I'll skip the RandomNumberGenerator. Here is the MyAndroidTestPlayer which implements Player (just one playTestNote() method).
public class MyAndroidTestPlayer implements Player {
SoundPool soundPool;
private static final int MAX_STREAMS = 10;
private static final int DEFAULT_SRC_QUALITY = 0;
private static final int HARDCODED_SOUND_RESOURCE_C3 = R.raw.midi_48_c3;
private static final int DEFAULT_PRIORITY = 1;
private static final String TAG = "MyAndroidTestPlayer";
private Context context;
private boolean isLoaded = false;
private int streamId;
private int soundId;
public MyAndroidTestPlayer(Context context) {
this.context = context;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
createNewSoundPool();
} else {
createOldSoundPool();
}
}
protected void createOldSoundPool() {
soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, DEFAULT_SRC_QUALITY);
Log.i(TAG, "created old sound pool");
loadSoundPool();
}
protected void createNewSoundPool() {
AudioAttributes attributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();
soundPool = new SoundPool.Builder().setAudioAttributes(attributes).build();
Log.i(TAG, "created new sound pool");
loadSoundPool();
}
private void loadSoundPool() {
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
isLoaded = true;
Log.i(TAG, "Loaded");
Log.i(TAG, "Status: " + status);
}
});
soundId = soundPool.load(context, HARDCODED_SOUND_RESOURCE_C3, DEFAULT_PRIORITY);
}
#Override
public void playTestNote() {
Log.i(TAG, "before loaded check");
if (isLoaded) {
streamId = soundPool.play(soundId, 1, 1, 1, 0, 1f);
Log.i(TAG, "Played Sound");
Log.i(TAG, "streamId: " + streamId);
}
}
}
Thank you in advance.
I think I got it. HomeFragment had to #Override onCreate and inject itself with the following line
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoBaseActivity) getActivity()).inject(this);
}
Then it works for me.
I hope this will help other users on my level of understanding.
observer.update();
Above method makes the visibility of observers "GONE", but i want to change all other observers except sender observer. How can i control this ?
My all actions are observer and register themself in their constructor like below,
public class ParentAction extends AbstractAction implements IActionObserver{
private ArrayList<IAction> lSubItems;
private View subView;
public ParentAction( String ItemText,int drawable,ArrayList<IAction> SubItems) {
super(ItemText,drawable);
lSubItems = SubItems;
ActionHolder.getInstance().registerObserver(this);
}
#Override
public void update() {
getSubView().setVisibility(View.GONE);
} ...
ActionHolder
public class ActionHolder implements IActionSubject {
private static ActionHolder uniqueActionHolder;
private ArrayList observers;
private ActionHolder() {
observers = new ArrayList();
}
public static synchronized ActionHolder getInstance() {
if (uniqueActionHolder == null) {
uniqueActionHolder = new ActionHolder();
}
return uniqueActionHolder;
}
public void registerObserver(IActionObserver o) {
observers.add(o);
}
public void removeObserver(IActionObserver o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
observer.update();
}
}
public void actionClicked(View view) {
notifyObserver();
}
}
Is this your own implementation of the observer pattern? If so, you can modify the notify method, for instance:
public void notifyObserver(IAction sender) {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
if (observer != sender)
observer.update();
}
}
and call this as
ActionHolder.getInstance().notifyObserver(this);
Alternatively, you could add a flag in your action class:
private bool sender = false;
set the flag before notifying:
sender = true;
ActionHolder.getInstance().notifyObserver();
and use this flag in the update:
#Override
public void update() {
if (!sender) {
getSubView().setVisibility(View.GONE);
}
sender = false;
}
You raising event in actionClicked method and then notifying all observers. Just pass a reference to your sender observer to skip its refreshing later.
If i got your code correctly, you can achieve that by controlling sender with your view
public void actionClicked(View view) {
notifyObserver(view);
}
public void notifyObserver(View view) {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
observer.update(view);
}
}
And update method skips current view
#Override
public void update(View view) {
if (!getSubView().equals(view)) {
getSubView().setVisibility(View.GONE);
}
}