I created a library for the recurrent screen in android and when I tried to implement it in my activity I got this error message.
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.app.Activity.getApplicationContext()' on a null object reference
at com.expert.recur.ScreenReco.<init>(ScreenReco.java:15)
at com.expert.recurringscreen.MainActivity.onCreate(MainActivity.java:18)
My code.
MainActivity.java:
public class MainActivity extends AppCompatActivity {
ScreenReco screenReco;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenReco=new ScreenReco(MainActivity.this);//line 18
screenReco.value = 1000;
screenReco.runnable.run();
}
}
My library:
public class ScreenReco {
Activity activity;
public ScreenReco(Activity activity) {
this.activity = activity;
}
public Context context = activity.getApplicationContext();//line 15
public int value;
public Handler handler = new Handler();
public Runnable runnable = new Runnable() {
#Override
public void run() {
Intent i = new Intent(context, context.getClass());
handler.postDelayed((Runnable) context,value);
context.startActivity(i);
}
};
}
You are supposed to create an object of ScreenReco class before assigning values to its variables ...
public class MainActivity extends AppCompatActivity {
ScreenReco screenReco;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenReco=new ScreenReco(); // you are missing this
screenReco.context = this;
screenReco.value = 1000;
screenReco.runnable.run();
}
}
But I strongly recommend you to use constructors for this ... it's the good practice
java.lang.NullPointerException: Attempt to invoke virtual method
'android.content.Context android.app.Activity.getApplicationContext()'
on a null object reference
You can create a Constructor.
A constructor is a special method that is called whenever an object is
created using the new keyword.
public class ScreenReco {
Activity activity;
ScreenReco(Activity ctx)
{
this.activity=ctx
}
}
Then
ScreenReco screenReco=new ScreenReco(MainActivity.this);
Or
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenReco=new ScreenReco(MainActivity.this);
Your MainActivity is almost fine. But, it is not recommended to call run(); method of Runnable object ourself. Instead, pass the Runnable object in a constructor of a Thread object and call start(); method on the Thread object and let the system call the run(); method itself when it is appropriate. Your improved MainActivity may look like this :
public class MainActivity extends AppCompatActivity {
ScreenReco screenReco;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenReco=new ScreenReco(MainActivity.this, 1000);//line 18
// screenReco.value = 1000;
// if you want to run `runnable` in a new Thread
// new Thread(screenReco.runnable).start();
//or if you want to run it in the same thread
//new Handler().post(screenReco.runnable);
//screenReco.runnable.run();
}
}
Now moving into your library file i.e. ScreenReco.java, it looks like activity variable hasn't been initialized while you are trying to call getApplicationContext() method in the class as #PraveenSP's answer has already said. A better practice would be declare all required class variable at once and then initialize them all in the class constructor as:
public class ScreenReco {
// if you need this activity variable to just use as context
// then do not use this variable here just use context only
Activity activity;
// the better practice here is to declare these variable as private
// and pass values for these variables in constructor and initialize them there
public Context context;
public int value;
public Handler handler;
public Runnable runnable;
// constructor improved from your code
public ScreenReco(Activity activity, int value) {
this.activity = activity;// this only if you are using activity
// object to something otherwise get rid of this variable.
this.context = activity.getApplicationContext();
this.handler = new Handler();
this.value = value;
this.runnable = new Runnable() {
#Override
public void run() {
Intent i = new Intent(context, context.getClass());
// handler.postDelayed((Runnable)context, value);
context.startActivity(i);
}
// this is the edit you need
this.handler.postDelayed(runnable, value);
};
}
// this version of constructor is what exactly I would have done by declaring class variable as `private` up there
// public ScreenReco(Context context, int value) {
// this.activity = activity;
// this.context = context;
// this.handler = new Handler();
// also initialize `value` and `runnable` object here
// this.value = value;
// this.runnable = new Runnable() {
// #Override
// public void run() {
// Intent i = new Intent(context, context.getClass());
// handler.postDelayed((Runnable) context,value);
// context.startActivity(i);
// }
// };
// }
}
Also, I'm so confused by the code you wrote inside the runnable's run() method. It looks like you are trying to start the MainActivity from MainActivity. And, in the line below, you tried to cast context variable to Runnable which is very error-prone.
Related
I have two classes corresponding to two activities in my code.
What I would like, is that the functions in the class of the activity B is launched in the activity A, but without showing the activity : I would just like the code to be executed...
How can I launch the functions of the activity B in background from the activity A?
I read to use Services, but I don't know at all how to use them, I don't see so many reading about it. I don't know if it is the good way.
I tried this with no success: startService(new Intent(this, HereMap.class));
Activity A:
public class LoginActivity extends AppCompatActivity {
#Override
protected void onResume(){
...
//Code to execute the activity B in background
Intent activityB= new Intent(this, HereMap.class);
activityB.onDownloadButtonClicked(); //=> here is the execution but doesn't work...
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
Activity B:
public class HereMap extends AppCompatActivity {
...
private MapLoader.Listener mapLoaderHandler = new MapLoader.Listener() {
#Override
public void onProgress(int progress) {
Log.i(TAG, "Progress " + progress + "%");
downloadProgressBar.setProgress(progress);
}
#Override
public void onInstallationSize(long diskSize, long networkSize) {
Log.i(TAG, "Map data require " + diskSize);
}
#Override
public void onGetMapPackagesComplete(MapPackage rootMapPackage,
MapLoader.ResultCode resultCode) {
if (resultCode == MapLoader.ResultCode.OPERATION_SUCCESSFUL) {
Log.i(TAG, "Map packages received successful: " + rootMapPackage.getTitle());
currentInstalledMaps = new ArrayList<>(1);
populateInstalledMaps(rootMapPackage);
} else {
Log.e(TAG, "Can't retrieve map packages: " + resultCode.name());
Toast.makeText(HereMap.this,
"Error: " + resultCode.name(), Toast.LENGTH_SHORT).show();
return;
}
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Init map
setContentView(R.layout.activity_here_map);
mapFragment = (AndroidXMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapfragment);
...
public void onDownloadButtonClicked() {
Log.d(TAG, "Downloading new map data...");
List<Integer> downloadList = new ArrayList<>(1);
downloadList.add(120214); //Id:120002 Berlin Id:120214, Name: Andalucia, Size:231504 Cherche l'id avec l'application iOS map-downloader-ios-swift
downloadProgressBar.setProgress(0);
downloadProgressBar.setVisibility(View.VISIBLE);
MapLoader.getInstance().installMapPackages(downloadList);
}
If you have some function you want to use in multiple Activities, you can put it in its own class and instantiate that class in whatever place you need to call its methods. The created class should not extend Activity or Fragment if it isn't actually an Activity or Fragment (this will cause all sorts of bugs). If it needs access to a Context or other Android component, you could pass those in at construction or to the methods that need it.
If you need your helper class to interact with its hosting activity (e.g. show or hide views, send data to views, etc) you can define interfaces on the helper or pass the views in directly.
If you define interfaces on your helper class you can use those to "call back" to whatever activity is hosting it (e.g. to show a progress bar). If you don't want to use an interface you could just pass the view to the helper class too, but the interface approach is sometimes more flexible.
Here are examples of both approaches:
Using an Interface
public class MapHelper {
public interface ProgressBarHolder {
void showBar();
}
private ProgressBarHolder progress;
MapHelper(ProgressBarHolder prog) {
progress = prog;
}
public void onDownloadButtonClicked() {
List<Integer> downloadList = new ArrayList<>(1);
downloadList.add(120214);
progress.showBar();
MapLoader.getInstance().installMapPackages(downloadList);
}
}
then you can call it from any Activity, like this
public class MyActivity extends AppCompatActivity
implements MapHelper.ProgressBarHolder
{
private MapHelper helper = new MapHelper(this);
private ProgressBar bar;
#Override
public void showBar() {
// show a progress bar, MapHelper will call this
// when it needs the current activity to show a progress bar
bar.setProgress(0);
bar.setVisibility(View.VISIBLE);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set up stuff
bar = findViewById(R.id.progress);
bar.setVisibility(View.GONE);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
helper.onDownloadButtonClicked();
}
});
}
}
Passing in Views
public class MapHelper {
private ProgressBar progress;
MapHelper(ProgressBar prog) {
progress = prog;
}
public void onDownloadButtonClicked() {
List<Integer> downloadList = new ArrayList<>(1);
downloadList.add(120214);
progress.setProgress(0);
progress.setVisibility(View.VISIBLE);
MapLoader.getInstance().installMapPackages(downloadList);
}
}
then you can call it from any Activity, like this
public class MyActivity extends AppCompatActivity {
private MapHelper helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set up stuff
ProgressBar bar = findViewByid(R.id.progress);
bar.setVisibility(View.GONE);
// Wait to create the helper until you have the views
helper = new MapHelper(bar);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
helper.onDownloadButtonClicked();
}
});
}
}
I'm trying ways to call my MainActivity method into my doWork in java class in android studio. But I'm not getting it. Is there any way to call this method ?
Class in the MainActivity
public class MainActivity extends AppCompatActivity implements LocationListener {
protected double latitude, longitude;
TextView txtLat, txtLng;
LocationManager locationManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void LocalizarUsuario(){
txtLat = (TextView) findViewById(R.id.txtLat);
txtLng = (TextView) findViewById(R.id.txtLng);
locationManager =(LocationManager)
getSystemService(Context.LOCATION_SERVICE);
if(ActivityCompat.checkSelfPermission(MainActivity .this,Manifest.permission.ACCESS_FINE_LOCATION)!=PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 100);
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,this);
}
}
Java class with the doWork
public class WorkerClass extends Worker {
public WorkerClass(
#NonNull Context context,
#NonNull WorkerParameters params) {
super(context, params);
}
#Override
public Result doWork() {
LocalizarUsuario();
// Indicate whether the work finished successfully with the Result
return Result.success();
}
}
Depending on what exactly you want to do, you could place the WorkerClass into the MainActivity (inner class) or if you want to use the WorkerClass only from the MainActivity, you could pass the instance of
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);
}
}
I am developing an Android app and thus, I have a MainActivity class. Inside of that MainActivity class, I have a method, let's call it doSomething():
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void doSomething(){
// bla bla bla
}
}
I also have a different class (with different layout) that is called OtherActivity. I want to use the doSomething method inside it:
public class OtherActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other_activity_layout);
// Let's use doSomething()
}
}
I tried this:
public class OtherActivity extends AppCompatActivity {
MainActivity main;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other_activity_layout);
// Let's use doSomething()
MainActivity main = new MainActivity();
main.doSomething();
}
}
But it does not work. I also tried to make OtherActivity to extend the MainActivity, doing the following:
public class OtherActivity extends MainActivity{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other_activity_layout);
// Let's use doSomething()
super.doSomething();
}
}
But it does not allow me to initialize the layout...
How can I do?
Thanks in advance.
To communicate between to Activity Broadcast is the best way, and for the same application, we can use local broadcast using LocalBroadcastManager.
First, we should register one broadcast in MainActivity,
public class MainActivity1 extends AppCompatActivity {
public static final String INTENT_FILTER = "do_some_action";
public static final String INTENT_BUNDLE_VALUE = "value1";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
LocalBroadcastManager.getInstance(this).registerReceiver(
mChangeListener, new IntentFilter(INTENT_FILTER));
}
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mChangeListener);
}
private BroadcastReceiver mChangeListener = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intentData) {
// you can do anything here
if (intentData != null && intentData.hasExtra(INTENT_BUNDLE_VALUE)) {
String value = intentData.getStringExtra(INTENT_BUNDLE_VALUE);
doSomeAction(value);
}
}
};
private void doSomeAction(String value) {
}
}
Then to do some action in MainActivity from OtherActivity, we can send Local broadcast from OtherActivity it will reach the receiver of Which we register in MainActivity,
public class OtherActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
// You can call MainActivity to do some actions
Intent intent = new Intent(MainActivity1.INTENT_FILTER);
intent.putExtra(MainActivity1.INTENT_BUNDLE_VALUE, "Any string or any value");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
Done!!!.
Something like this should do the trick, I'm going to make a static navigator to handle your navigation logic. If you are opposed to static methods you could also make them on your Application object to make it easier to manage dependencies, I'm just making it static for simplicity.
//Making this fully static for simplicity, this is fine for a small app
//you can make it a singleton on the application class for more flexibility
public class Navigator {
//static member vars that determine navigation
// pass in Context if needed for navigation purposes
public static void doSomething(Context context){
// bla bla bla
}
}
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity_layout);
}
private void doSomething() {
Navigator.doSomething(this);
}
}
public class OtherActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other_activity_layout);
}
private void doSomething() {
Navigator.doSomething(this);
}
}
I am reading about how to interact between UI and background thread here.
This article has following note:
The AsyncTask does not handle configuration changes automatically,
i.e. if the activity is recreated. The programmer has to handle that
in his coding. A common solution to this is to declare the AsyncTask
in a retained headless fragment.
I dont understand what is retained headless fragment.
For example, in this way I can add fragment:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.frame, new MyFragment());
transaction.commit();
And in fragment I can execute AsyncTask like this:
MyTask myTask = new MyTask();
String[] args = {"Hello"};
myTask.execute(args);
Is this called "to declare the AsyncTask in a retained headless fragment"?
Headless fragment is nothing but a fragment which does not have a view. In onCreate() of the fragment lifeCycle, use setRetainInstance(true);. This will not destroy the fragment even if the activity recreates. So if an AsyncTask is running in fragment, on recreation of the activity, you wont lose the AsyncTask.
In onCreate of the activity, you have to add the fragment with a tag. Before adding, check if the fragment exist using getFragmentManager().findFragmentByTag(TAG), if the fragment is null then create a new instance of the fragment and add it.
In Fragment there will not be any view inflated, so no need to override onCreateView().
An example of headlessFragment :
public class HeadlessProgressFragment extends Fragment {
private ProgressListener mProgressListener;
private AsyncTask<Void, Integer, Void> mProgressTask;
public interface ProgressListener {
void updateProgress(int progress);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setProgressListener(Context context) {
mProgressListener = (ProgressListener) context;
}
public void startProgress(final int size) {
if (mProgressTask == null || mProgressTask.getStatus() != AsyncTask.Status.RUNNING || mProgressTask.getStatus() == AsyncTask.Status.FINISHED) {
mProgressTask = new AsyncTask<Void, Integer, Void>() {
#Override
protected Void doInBackground(Void... params) {
for (int index = 0; index < size; index++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
publishProgress(index + 1);
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mProgressListener != null) {
mProgressListener.updateProgress(values[0]);
}
}
};
mProgressTask.execute();
}
}
}
In Activity Something like this :
public class MainActivity extends FragmentActivity implements HeadlessProgressFragment.ProgressListener {
private static final String TAG = "progress_fragment";
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dummy_view);
mHeadlessProgressFragment = (HeadlessProgressFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if (mHeadlessProgressFragment == null) {
mHeadlessProgressFragment = new HeadlessProgressFragment();
getSupportFragmentManager().beginTransaction().add(mHeadlessProgressFragment,TAG).commit();
}
mHeadlessProgressFragment.setProgressListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
final Button startFillBtn = (Button) findViewById(R.id.btn_start_filling);
startFillBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHeadlessProgressFragment.startProgress(100);
}
});
}
#Override
public void updateProgress(int progress) {
mProgressBar.setProgress(progress);
}
}
As i simplified the complexity in my case by Just update your UI (if you have to) by checking the calling fragment or activity is present or not. Start the asynctask by assigning the weakreference of calling entity.